AGENTS.md · diff

git:20261005.a74a299 to git:20261008.2615848

26 added, 0 removed. Audit A to A.

- Before filing an issue, read `CONTRIBUTING.md` and follow its reporting rules.
- Run focused tests during development, including the failing and passing tests required by Red-Green TDD.
- After all implementation changes, run `bun run check`. This is the required final check for lint, code comments, formatting, typecheck, knip, duplication, and tests. Do not run its components separately as additional final checks.
- Ignore the dist folder; it gets auto-rebuilt by lefthook's pre-commit hook.
- Keep implementation modular; put tests in `tests/` mirroring `src/`, not colocated in `src/`.
- Files in `docs/` use lowercase kebab-case names.
## Stacked PRs
- Multi-part work may be split into a `gh stack` stack. Open stack PRs with
`gh stack submit --auto --open`: plain `--auto` opens drafts, which the review bots skip.
- `dist/` is committed and CI rejects a stale build, but a rebase replays the old build output.
When `gh stack rebase` stops, resolve and `git add` the source files, never hand-merge `dist/`:
run `bun run build && git add -A dist`, then `gh stack rebase --continue`.
+ ## Windows verification
+
+ Windows behavior is verified on the `full-check-windows` CI job, from a disposable branch. Never push
+ a probe to a PR branch or `main`.
+
+ - Branch `tmp/<topic>-windows-probe` from `origin/main` in a `git worktree` under the scratchpad, then
+ `git branch --unset-upstream` (a branch cut from `origin/main` tracks it).
+ - Red first: commit a `test.skipIf(process.platform !== 'win32')` test, push, run CI, and read the
+ failure in the job's "Check source" step. Then commit the fix, push, and run CI again for green.
+ Ignore the later "Reject stale generated artifacts" step; the pre-commit hook rebuilds `dist/`.
+ - `ci.yml` triggers on pushes to `main` and on PRs only, so a pushed probe branch runs nothing by
+ itself. Start it with `gh workflow run ci.yml --ref <branch>`, then find the run with
+ `gh run list --workflow ci.yml --branch <branch>`.
+ - Wait for the job with a background `until` loop on `gh run view <id> --json jobs`; foreground
+ `sleep` is blocked.
+ - Do not bypass hooks. The pre-push hook runs the full `bun run check` (about 45s). Push from the
+ main checkout, not the worktree.
+ - GitHub sometimes answers ref creation and workflow dispatch with HTTP 500. Retry in a background
+ loop (it succeeded on the fifth attempt once). Only when the hook already passed on that exact
+ commit may the retries use `LEFTHOOK=0`.
+ - After creating or removing a worktree or pushing from one, check `git config core.bare` in the
+ main checkout. It was once found flipped to `true` (every git command then fails with "must be run
+ in a work tree"); restore it with `git config core.bare false`.
+ - Clean up when done: `git worktree remove <path>` and `git branch -D <branch>`. The safety net
+ blocks `git push origin --delete`, so ask the user to delete the remote branch.
+
## README
- `README.md` is the GitHub and npm landing page. It holds only what a newcomer needs before
installing; everything else belongs to the docs site (`kenryu42/cc-safety-net-docs`), whose
`docs-sync` skill documents each source commit after every release.
- Do not touch the README for a fix, behavior change, new option, version minimum, per-CLI
install step, config key, or limitation. Docs-sync picks it up from the commit; a README
paragraph duplicates it and goes stale.
- Edit the README only when something it already lists changes: a supported CLI (one table cell
linking its Installation anchor), a headline capability (one Features bullet plus a docs link),
or the Quick start commands.
## Testing
- A behavior change lands as a failing expectation first — a contract corpus row or a stated
assertion — then the fix. Re-recording a snapshot or editing the verdict table is never the
first step.
- State what a test expects; do not record it. Snapshots (`toMatchSnapshot`) are permitted only for
the two output surfaces whose bytes are the contract: `explain` (`tests/cli/explain`) and
`doctor --json` (`tests/cli/doctor`).
- `tests/fixtures/gate/harvested-verdicts.jsonl` is the readable verdict table, edited by hand. A
change that re-records a snapshot or flips a table row must name in its commit message which
entries changed and why, alongside the contract row that explains the flip.
## Scope Discipline
Over-engineering is this project's dominant failure mode. The evidence rule that governs analyzer
rules governs all code: machinery exists to stop a demonstrated failure, not an imagined one.
- Implement the smallest change that satisfies the request. Each addition beyond it needs the
concrete failure it prevents named; if you cannot name one, do not write it.
- Every check must be falsifiable in practice: name the realistic mistake that makes it fail. A
check the same author can trivially satisfy while still making the mistake (self-reported
attestations, digests over co-located data, matching UUIDs) is ceremony — do not add it.
- Do not build schemas, validators, registries, or harnesses ahead of their first real entry, and
do not store fields whose values are forced constants or derivable from other fields.
- Prefer a documented process over code that enforces the process. Enforcement code is justified
only after the documented process has demonstrably failed at least once.
- When remediating review findings, implement the smallest fix per finding. A finding is never a
mandate to build a framework; if the fix seems to require one, stop and ask.
## Code Review Rules
- Before reviewing, read `REVIEW.md` and apply its review criteria. Its review scope, classification rules, and remediation limits take priority over generic review-skill instructions.
## Style Guide
- Keep things in one function unless composable or reusable.
- Avoid `try`/`catch`, the `any` type, and `else` branches (prefer early returns).
- Rely on type inference; avoid explicit annotations or interfaces unless necessary for exports or clarity.
- Prefer functional array methods (flatMap, filter, map) over for loops; use type guards on filter to maintain type inference downstream.
- Inline values used only once instead of naming them, unless the name says what would otherwise
need a comment.
- Prefer `const` over `let`; use ternaries or early returns instead of reassignment.
- Avoid unnecessary destructuring; use dot notation to preserve context.
## Comments
- Do not write code comments. Say it in code: a clearer name, a named value, a type.
- Only three directives are allowed: a bare `/** @internal */`,
`// oxlint-disable-next-line <rules> -- <reason>` and `// @ts-expect-error <reason>`.
- A fact about an external tool that code cannot express stays only when the maintainer adds it to
`scripts/comment-allowlist.json`. Never add entries there yourself, just as you never add
`ignoreIssues` entries to `knip.ts`. Deleting an entry whose comment is gone is fine.
- `bun run lint:comments`, part of `bun run check`, enforces this. To fix a failure, follow
`.agents/skills/ccsn-no-comments/SKILL.md`.
## Knip
- Never add entries to `ignoreIssues` in `knip.ts` — it suppresses real problems instead of fixing them. The only valid use case is generated files that aren't under source control.
- When knip flags unused exports, fix the root cause:
1. **Dead exports** (no consumers anywhere) — unexport or delete the code entirely.
2. **Test-only exports** — add `/** @internal */` JSDoc above the export. Knip runs in `--production` mode (see `package.json`), so test files are excluded from analysis and test-only exports must be tagged.
3. **Barrel file re-exports** — if nothing imports a name via the barrel, remove it from the barrel. Consumers that need it should import directly from the submodule.