diff --git a/AGENTS.md b/AGENTS.md index 9692463816..bf3305d7d2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,6 +18,16 @@ When writing new code: - After editing a file, run `pnpm --dir src exec eslint --prune-suppressions --max-warnings=0 ` and confirm the count for that file did not increase. - If a suppression is truly unavoidable (e.g. `vi.spyOn(Cls.prototype as any, "privateMethod")` where no typed alternative exists), document why in a comment next to the cast. +### Typed test files + +New or substantially changed test files must not add `@typescript-eslint/no-explicit-any` suppression entries to `src/eslint-suppressions.json`: + +- Prefer typed structural doubles: type mock context/proxy classes against the real public types they stand in for (e.g. `vscode.ExtensionContext`), and use `unknown` for storage values and mock callback parameters instead of `any`. +- Access private members with bracket notation (`provider["viewLocalState"]`) and call public members without casts so their generic signatures keep checking arguments. +- When a partial double cannot be assigned to the real type, cast it once at the construction site (`as unknown as T`) with a comment naming the members the double provides; do not repeat `as any` at every use site. +- Validate with `pnpm --dir src run check-types`, the narrowest affected Vitest suite, and `pnpm --dir src exec eslint --prune-suppressions --max-warnings=0 `; the run must not add a new entry or raise an existing count. +- An exception requires a comment next to the cast explaining why no typed alternative exists and maintainer approval in review. + ## Persisted Setting Checklist When adding or changing a user setting, trace the complete round trip. A setting is not complete merely because its control renders or its value reaches storage.