diff --git a/CLAUDE.md b/CLAUDE.md index a89858f38..eb373b523 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -51,10 +51,16 @@ pnpm run token:audit # audit-tokens.mjs — design-token usage gate (CI b - **OSV vulnerabilities**: Run `pnpm audit` or check the security CI job. Add `pnpm.overrides` with pinned exact versions. - Correction loop: fix → commit → verify CI → fix until all jobs green. -**PR review-comment policy — the CodeAnt Correction Loop (proactive, automatic, every PR):** Fix ALL inline comments (CodeAnt AI + any bot/human) on every PR, **without being asked**. Validate findings against the *current* code (anchors may be stale); implement real **root-cause** fixes (code **+ tests + i18n + docs** in lockstep) or reply with evidence if a false positive. **Never add a new `biome-ignore`** — the suppression ratchet (`scripts/check-suppressions.mjs`) fails the quality gate; refactor so the rule passes honestly. Reply to each thread citing the resolving commit (`POST .../comments//replies`), resolve it (GraphQL `resolveReviewThread`), leave **0 unresolved**. Then commit, push, and **re-trigger** (`gh pr comment --body "@codeant-ai review"`). **Iron rule — loop until quiescent:** a push triggers a fresh review that often raises NEW findings (a "wave"); repeat the full cycle until **BOTH** a fresh review yields **0 new comments** AND **0 threads unresolved**. Only then merge (auto-squash; admin-squash only after CI is green + loop quiescent). Full canonical procedure: [`docs/CODEANT-REVIEW-LOOP.md`](docs/CODEANT-REVIEW-LOOP.md). +**PR review-comment policy — the CodeAnt Correction Loop (proactive, automatic, every PR):** Fix ALL inline comments (CodeAnt AI + any bot/human) on every PR, **without being asked** — this explicitly includes CodeRabbit's collapsed **nitpick** sections and **outside-diff-range** findings (both easy to miss since they're collapsed by default in the review UI), not just its top-level actionable comments. Validate findings against the *current* code (anchors may be stale); implement real **root-cause** fixes (code **+ tests + i18n + docs** in lockstep) or reply with evidence if a false positive. **Never add a new `biome-ignore`** — the suppression ratchet (`scripts/check-suppressions.mjs`) fails the quality gate; refactor so the rule passes honestly. Reply to each thread citing the resolving commit (`POST .../comments//replies`), resolve it (GraphQL `resolveReviewThread`), leave **0 unresolved**. Then commit, push, and **re-trigger** (`gh pr comment --body "@codeant-ai review"`). **Iron rule — loop until quiescent:** a push triggers a fresh review that often raises NEW findings (a "wave"); repeat the full cycle until **BOTH** a fresh review yields **0 new comments** AND **0 threads unresolved**. Only then merge (auto-squash; admin-squash only after CI is green + loop quiescent). Full canonical procedure: [`docs/CODEANT-REVIEW-LOOP.md`](docs/CODEANT-REVIEW-LOOP.md). **PR-size limit — keep every PR under ~100 changed files so CodeAnt actually reviews it.** CodeAnt does **not** post inline review comments on PRs that exceed ~100 changed files (the >100-file check hangs/skips). Since any i18n-touching change fans out across 19 locale source files + 19 rebuilt `bundle.json` per module, a multi-feature branch crosses 100 fast. **Before pushing, run `git diff --name-only ...HEAD | wc -l`.** If it is over ~100, split the work into the **fewest** stacked PRs that each stay clearly under the limit — group by which locale module-files they touch so the per-PR fan-out stays small (e.g. P0 batch touching `writer.json`; P1/P2 batch touching `common.json`/`dashboard.json`). Stack them (PR2 base = PR1's branch) so each PR's incremental diff — what CodeAnt sees — is small; when PR1 merges, PR2 auto-retargets to `main`. **Do not** make more PRs than needed: if everything fits under ~100 in one (or two) PRs, use that. Keep commits atomic per concern regardless of how they are bundled into PRs. +**Branching & merge discipline (every change, no exceptions):** Never commit directly to `main` — always create a feature branch, push, and open a PR, even for a single-file doc/config/chore edit. Before merging, wait for the **full CI suite to go green, including non-required/advisory jobs** (`E2E Tests`, `E2E Deep Coverage`, `Storybook`, `Lighthouse`, `Visual Regression`) — not just the branch-protection-required checks (Security Audit, Build, Quality Gate ×2). When doing a structured multi-step sprint (an audit, a migration broken into workstreams), group related small workstreams into the fewest PRs that stay reviewable — by natural/documented boundaries, not one PR per tiny item — while keeping one commit per logical concern inside each PR. + +**Known merge-gate quirks (GitHub, this repo):** +- **Mergeable-state cache lag vs. this repo's own wait-for-everything policy — two different things:** (1) GitHub itself blocks the merge button while *any* check is still `pending`, required or not (a real, observed technical constraint — it clears on its own once every check concludes, pass or fail); separately, once all checks have actually concluded, GitHub's branch protection only re-blocks on a *failing required* check. (2) This repo's own policy above is stricter than that floor: wait for the advisory jobs to *pass*, not just stop being `pending`, before merging. If `gh pr merge` still fails with "base branch policy prohibits the merge" after every job (required and advisory) shows a concluded `success`, and `mergeable: MERGEABLE`, and 0 review threads are unresolved, that's the mergeable-state *cache* lagging behind reality, not either policy above. Re-poll a few times at ~60s spacing. Never use `--admin` to route around any of this without a maintainer's fresh, explicit authorization for that specific merge. +- **Stacked-PR auto-close on squash-merge:** squash-merging a PR with `--delete-branch` can cause GitHub to **auto-close (not retarget)** a downstream PR whose base was the just-deleted branch, instead of the usual automatic retarget-to-`main`. Recovery: `git push origin :refs/heads/` to temporarily restore the ref, `gh pr reopen `, `gh pr edit --base main`, then delete the temp branch once `gh pr list --state open --json baseRefName` shows nothing still depends on it. + **E2E notes:** Do NOT use `networkidle` waits (HMR keeps WebSocket open). Scope sidebar navigation via `#sidebar`. Shared helpers: `tests/e2e/helpers.ts`. Mobile E2E: set `RUN_MOBILE_E2E=1` locally (off by default). **Feature-flag E2E coverage (anti-pattern guard):** Every test that relies on a specific flag state MUST use `setFeatureFlags(page, {...})` from `helpers.ts` to make that dependency explicit and guard against future default changes. Call it BEFORE `page.goto()` — it uses `addInitScript` so it runs before app JS.