diff --git a/AGENTS.md b/AGENTS.md index 799fa4f9f59..59950ad22d5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -145,6 +145,13 @@ - Formatting/linting via Oxlint and Oxfmt. - Never add `@ts-nocheck` and do not add inline lint suppressions by default. Fix root causes first; only keep a suppression when the code is intentionally correct, the rule cannot express that safely, and the comment explains why. - Do not disable `no-explicit-any`; prefer real types, `unknown`, or a narrow adapter/helper instead. Update Oxlint/Oxfmt config only when required. +- Prefer `zod` or existing schema helpers at external boundaries such as config, webhook payloads, CLI/JSON output, persisted JSON, and third-party API responses. +- Prefer discriminated unions when parameter shape changes runtime behavior. +- Prefer `Result`-style outcomes and closed error-code unions for recoverable runtime decisions. +- Keep human-readable strings for logs, CLI output, and UI; do not use freeform strings as the source of truth for internal branching. +- Avoid `?? 0`, empty-string, empty-object, or magic-string sentinels when they can change runtime meaning silently. +- If introducing a new optional field or nullable semantic in core logic, prefer an explicit union or dedicated type when the value changes behavior. +- New runtime control-flow code should not branch on `error: string` or `reason: string` when a closed code union would be reasonable. - Dynamic import guardrail: do not mix `await import("x")` and static `import ... from "x"` for the same module in production code paths. If you need lazy loading, create a dedicated `*.runtime.ts` boundary (that re-exports from `x`) and dynamically import that boundary from lazy callers only. - Dynamic import verification: after refactors that touch lazy-loading/module boundaries, run `pnpm build` and check for `[INEFFECTIVE_DYNAMIC_IMPORT]` warnings before submitting. - Extension SDK self-import guardrail: inside an extension package, do not import that same extension via `openclaw/plugin-sdk/` from production files. Route internal imports through a local barrel such as `./api.ts` or `./runtime-api.ts`, and keep the `plugin-sdk/` path as the external contract only.