Skip to content

Four surfaces state the gate set as three commands; CI now enforces four — one PR must move all four, and three of them are governed #73

Description

@zhuangjianguo

The gap

PR #72 (#67) added pnpm lint:i18n-gate to .github/workflows/ci.yml, so CI now enforces four gates. Four places in this repository still tell a contributor there are three:

where what it says governed?
README.md 47–49 the three-command block under 「Every metadata change is gated」 no
CLAUDE.md 8 ⛔ rule: pnpm validate && pnpm lint && pnpm typecheck must all pass yes
AGENTS.md 30–37 ## Verify your work — a three-command block, then 「Never report a change as done, and never open a PR, until all three pass. Paste the three green tails into the PR body.」 yes
AGENTS.md 129 Delivery process table: | Gates | pnpm validate && pnpm lint && pnpm typecheck. | yes

package.json's verify script has composed all four for some time —

"verify": "pnpm validate && pnpm lint && pnpm typecheck && pnpm lint:i18n-gate"

— so the documents are now the last thing in the repository still saying three.

Why this is one PR and not two

README.md is the only one of the four outside the governed range, so it could be fixed on the normal path today. ⛔ Do not. Fixing the editable surface alone would leave README.md saying four while CLAUDE.md and AGENTS.md say three — trading a uniform understatement, which merely omits, for a genuine contradiction, which misleads. All four move together or none do.

That makes this a governed-surface PR: AGENTS.md leaves CLAUDE.md and AGENTS.md to the maintainer's merge, the same path PR #40 took. The dev authors it, CI runs, the seat reviews and records an ACCEPT, and then it waits — ⛔ the seat does not merge it.

Why it is not a defect

A contributor who copies those three commands is led into an omission, not an error: the three still pass and still mean what they say, and pnpm verify — which the same documents also mention — runs all four anyway. Nothing breaks. This is documentation catching up with an enforcement surface that moved, which is the same shape as #44, #58 and #63 and is worth the same care.

What to change

Make all four state the same four-gate set. Notes per surface:

  • AGENTS.md 30–37 is a rewrite, not a line insert. The count is welded into the prose twice — 「all three pass」 and 「the three green tails」 — so adding a fourth command without touching those two phrases leaves the section contradicting itself in the space of three lines. ⚠️ That is exactly the defect class this card is fixing; do not reproduce it while fixing it.
  • AGENTS.md 129 is a table cell; keep the cell's existing shape.
  • CLAUDE.md 8 is inside a ⛔ rule that is deliberately terse and mirrors a Prime Directive; match its register and ⛔ do not expand it into a paragraph.
  • README.md 47–49 is a fenced block with a trailing comment per line; follow that convention for the fourth.
  • Consider whether any of the four should simply say pnpm verify instead of enumerating — state your reading, do not act on it unasked. Enumeration is what drifted; a single composed command cannot. But README.md's block exists to show a reader what runs, and collapsing it may cost more than it saves. If you recommend it, recommend it in the PR body and leave the enumerations in place.

Constraints

  • ⛔ Documentation only. No .github/, no package.json, no src/.
  • ⛔ Do not change what the gates are or their order — validate → lint → typecheck → lint:i18n-gate, matching verify and ci.yml as merged.
  • Grep for every occurrence before declaring done, not just the four above. This card exists because one statement drifted in four places at once, and the four were found by a grep, not by a list — so run the grep yourself and report what it returns, including "nothing else".
  • ⛔ Do not touch DESIGN.md, LICENSE, CHANGELOG.md or docs/design/**.

Acceptance

Provenance

Found by the dev on #67 while checking whether CLAUDE.md's three-gate rule should become four — it reported all four surfaces, correctly declined to touch the three governed ones, and recommended one PR covering all four rather than a card for the README half alone. The PM seat adopted that recommendation; the reasoning above is the dev's.

Related

#67 / PR #72 (19a8a4c, which made CI enforce four) · PR #40 (the governed-surface path this card follows) · #44 · #58 · #63 (documentation catching up with the tree)

Activity

  1. zhuangjianguo commented on Sep 10, 2026

    @zhuangjianguo
    CollaboratorAuthor

    Claim: session session_01R3n3GGzobdegM4HUzah1iR · branch claude/issue-73-four-gates-everywhere

    PM dispatch. The assignee and this claim are set by the PM seat on behalf of the dev that will work this card; the dev inherits both, checks that this is the newest Claim: on the thread and that it names its branch, and ⛔ posts no second claim and never writes the assignee field.

    Branch off current origin/main (19a8a4c) — which already carries PR #72, so .github/workflows/ci.yml genuinely runs four gates and the documents are genuinely the last thing saying three.

    #71 is dispatched in parallel and edits src/dashboards/legal.dashboard.ts and src/datasets/cycle-time.dataset.ts. You edit README.md, CLAUDE.md and AGENTS.md. Disjoint file surfaces. ⛔ Do not touch src/.

    ⚠️ This is a governed-surface PR and it will NOT be merged by the seat. CLAUDE.md and AGENTS.md are on AGENTS.md's own governed list, so this follows PR #40's path: you author it, CI runs, the seat reviews and records an ACCEPT on this issue, and then it waits open for the maintainer. Write the PR body knowing a human will read it as the primary audience — include the 「维护者速读」 section the card asks for.

    Dispatch notes on top of the card body:

    • AGENTS.md 30–37 is the one that can go wrong quietly. The count is welded into the prose twice — 「all three pass」 and 「the three green tails」. Adding a fourth command without rewriting both leaves the section contradicting itself in three lines, which is precisely the defect class this card fixes. ⚠️ Do not reproduce the bug while fixing it.
    • The one judgement call is pnpm verify vs enumeration — whether any surface should stop listing commands and just name the composed script. Enumeration is what drifted four times; a composed command cannot. But README.md's block exists to show a reader what runs. Recommend in the PR body, ⛔ do not act on it unasked.
    • Run the grep yourself. The card's four rows came from a grep, not from a list; report what yours returns, including "nothing else". A fifth occurrence is exactly the shape of thing this card exists to have missed.
    • ⚠️ Read the tree, not my table. Every line I quoted was read on 30fd863; 19a8a4c has landed since.

    Acceptance


    Generated by Claude Code

  2. zhuangjianguo commented on Sep 10, 2026

    @zhuangjianguo
    CollaboratorAuthor

    os-dev-report

    {
      "issue": 73,
      "status": "done",
      "branch": "claude/issue-73-four-gates-everywhere",
      "pr": "https://github.com/objectstack-ai/hotclm/pull/77",
      "premise_still_valid": true,
      "summary": "Every documentary statement of the gate set now reads validate -> lint -> typecheck -> lint:i18n-gate, matching package.json's verify and ci.yml as merged. My own repo-wide grep returned FIVE such statements, not the card's four: the fifth is docs/backlog/README.md 29, the acceptance floor every dispatched card inherits. I moved it with the other four rather than filing it, because leaving it would have produced exactly the trade the card forbids - a uniform understatement swapped for a genuine contradiction between the backlog and AGENTS.md. It is ungoverned, off every exclusion list in the card, mechanically identical to the other four, and disjoint from #71's file surface (src/dashboards, src/datasets); declared in the PR body and the commit message. AGENTS.md 'Verify your work' was rewritten rather than appended to - the count was welded into the prose twice ('All three exit non-zero', 'the three green tails') - and the replacement prose is deliberately count-free, so a fifth gate cannot reopen the defect there. The pnpm-verify-vs-enumeration judgement is recommended in the PR body and NOT acted on: my reading is that AGENTS.md 130 and docs/backlog/README.md 29 should eventually collapse to pnpm verify (pure restatements, no per-command information) while README.md and AGENTS.md's blocks should keep enumerating (their job is to show a reader what runs). Governed surface: PR #77 is a DRAFT left OPEN for the maintainer, carrying a 维护者速读(草稿) section for the seat to finalize.",
      "tests": "All four gates on 4560611, each written to its own log with $? read directly, never through a pipe: validate EXIT=0 ('✓ Validation passed (1031ms) · Data: 11 Objects 170 Fields'), lint EXIT=0 ('21 warning(s), 5 suggestion(s) (913ms)' - pre-existing on main, diff is markdown only), typecheck EXIT=0 (tsc --noEmit, no output), lint:i18n-gate EXIT=0 ('✓ i18n gate · COVERAGE: 0 missing keys across 2 locale(s)'). Both gate runs went through the container's shared verify lock (OS_VERIFY_LOCK_SLOT=hotclm-issue-73; VERDICT command-exit 0 both times, waited 0s). ABLATION, one solid demonstration meeting the #63/#67 bar: all four touched documents reduced to two lines of garbage AT ONCE (README.md 12136->59 bytes, CLAUDE.md 985->59, AGENTS.md 11816->59, docs/backlog/README.md 2747->59; mutation proved on disk by injected-marker counts 0->1 per file plus git diff --stat HEAD showing 4 files changed, 419 deletions, never by an editor exit code), all four gates re-run against the corrupted tree, ALL FOUR STILL EXIT 0 (ANY_GATE_NONZERO=0) - so no gate reads any of these files. Restored under trap ... EXIT INT TERM using absolute paths from git rev-parse --show-toplevel and git checkout HEAD -- (not bare checkout), then verified BY STATE: git hash-object matched git rev-parse HEAD:path for all four (f90ea10.../0e60115.../85fa26f.../b8a73b6...), injected marker count back to 0 in each, git diff HEAD --quiet EXIT=0, git status --porcelain empty. Second independent line of evidence: tsconfig.json 14 is include: [objectstack.config.ts, src/**/*]. Edits verified on disk by before/after spelling counts: old composed three-gate spelling 1->0 in CLAUDE.md, AGENTS.md and docs/backlog/README.md; lint:i18n-gate 0->1 (README, CLAUDE, backlog) and 0->2 (AGENTS.md); AGENTS.md gate-count word 'three' 3->0; repo-wide grep for the old composed spelling now returns nothing. PR body read back in full after posting: 0 lines removed by the sanitizer, the only delta is the platform's own appended attribution footer. CI on the head: check run 'Validate' completed, conclusion success.",
      "mcp_calls": "2 — create_pull_request and this add_issue_comment. All reads (issue body, comments, PR read-back, check runs) went through unauthenticated REST after a channel probe returned 200, so they cost no MCP quota.",
      "open_questions": [
        {
          "question": "Should any surface stop enumerating the gate set and simply name `pnpm verify`? Recommended in the PR body, deliberately not acted on.",
          "options": [
            "A. Collapse only the two restatement surfaces (AGENTS.md 130 Gates cell, docs/backlog/README.md 29 acceptance floor) to `pnpm verify`; keep README.md's and AGENTS.md's fenced blocks enumerating.",
            "B. Collapse all five to `pnpm verify`.",
            "C. Leave all five enumerating, as this PR does."
          ],
          "recommendation": "A. Those two name the set only to say 'this is the bar' — they carry no per-command information and are pure restatements of package.json's verify, which is precisely the mechanism that drifted five copies at once. `pnpm verify` cannot drift; it is the definition. The two fenced blocks are different: their job is to SHOW a reader what runs, with a per-gate comment explaining why lint is not a second spelling of validate, and collapsing those costs more than it saves. Non-blocking; a two-line follow-up card."
        },
        {
          "question": "DESIGN.md 354 (the M1 milestone acceptance cell) still spells the gate set as three: 「`validate`/`lint`/`typecheck` 绿」. This card excludes DESIGN.md and AGENTS.md 128/131/132 makes it governed.",
          "options": [
            "A. Leave it — it records what a delivered milestone was accepted against, a historical record rather than an instruction to a contributor today.",
            "B. File a `needs-user-decision` card to update it to four."
          ],
          "recommendation": "A, with the seat's confirmation. It reads differently from the other five occurrences, and filing a governed-surface decision is the PM seat's act rather than mine — so it is reported here rather than filed."
        }
      ],
      "out_of_scope_findings": [
        "fixed in this PR, not filed: docs/backlog/README.md 29 — a FIFTH occurrence of the gate set the card's table did not list. Same defect class, ungoverned, off every exclusion list, mechanically identical to the other four, no other claim on the file (#71 is on src/ only), no new verification surface. Declared in the PR body and commit message.",
        "noted, not filed: DESIGN.md 354 — M1 milestone acceptance cell still spells three gates. Governed surface excluded by this card; see open_questions. Successor: the PM seat, if it decides a needs-user-decision card is warranted.",
        "noted, not filed: docs/backlog/14-release.md 14 already says `pnpm verify` green — the composed form this PR recommends for the two restatement surfaces. Correct today, needs no change; recorded because it is the one place in the tree that already does the thing recommended above.",
        "noted, not filed: pre-existing validate/lint warnings on main (21 warnings, 5 suggestions — carrier-only field verdicts on clm_review/clm_signature, an approval node that may resolve to an empty approver slate). Untouched by this markdown-only diff, and unchanged between the clean and ablated runs. Successor: none identified; they predate this branch and belong to whoever next works those objects."
      ]
    }

    Generated by Claude Code

  3. zhuangjianguo commented on Sep 10, 2026

    @zhuangjianguo
    CollaboratorAuthor

    ACCEPT — PR #77 · ⛔ NOT merged, and will not be by this seat

    CI green on head 4560611. Four files, markdown only. CLAUDE.md and AGENTS.md are governed surfaces, so this follows PR #40's path: ACCEPTed here and left open for the maintainer. The 「维护者速读」 finalisation is posted on the PR itself.

    ⚠️ Also: I waited for your completion report before reviewing this, which I failed to do on PR #72 and PR #74 earlier today. The precondition I adopted after that — ListAgents before touching any PR — held you as still running at 11:45Z and stopped me. First application, and it worked. Recorded because the two failures are recorded.

    The fifth occurrence, and why taking it was right

    The card gave you four and told you to grep rather than trust the list. You found five — docs/backlog/README.md:29, the acceptance floor every dispatched card inherits — and moved it with the others rather than filing it. Your reason is the one that decides it:

    leaving it would have produced exactly the trade the card forbids — a uniform understatement swapped for a genuine contradiction between the backlog and AGENTS.md.

    That is the card's own argument applied to a case the card did not know about, which is the difference between following a card and understanding it. I re-ran the grep independently on main and confirm the composed three-gate spelling sits at AGENTS.md:129, CLAUDE.md:8 and docs/backlog/README.md:29, with package.json:21 already carrying all four.

    Making the section count-free instead of correcting its count

    This is the part I want on the record:

    the count was welded into the prose twice … and the replacement prose is deliberately count-free, so a fifth gate cannot reopen the defect there.

    I asked you not to reproduce the bug while fixing it. You did better: you removed the class. 「all of them」 and 「one green tail per gate」 cannot go stale when a sixth gate arrives, and a statement that cannot drift is worth more than a statement that is currently correct. That is the same principle PR #74 applied when it replaced a copied list with a pointer, arrived at independently.

    The ablation

    All four documents reduced to garbage at once, mutation proved on disk per file (marker counts plus git diff --stat showing 419 deletions), all four gates re-run, all four still exit 0, restored under a trap with per-file git hash-object matched against git rev-parse HEAD:<path>. One demonstration covering the whole file surface rather than four separate ones — cleaner than the bar I set, and the tsconfig include line as an independent second reading behind it.

    Your two open questions — both answered

    1. pnpm verify vs enumeration → A, and it is filed

    Your split is right and the reasoning is the useful part:

    Those two name the set only to say "this is the bar" — they carry no per-command information and are pure restatements of package.json's verify, which is precisely the mechanism that drifted five copies at once. pnpm verify cannot drift; it is the definition. The two fenced blocks are different: their job is to SHOW a reader what runs.

    A restatement of a definition should be a reference to the definition; a demonstration should demonstrate. Filed as its own card, ⛔ queued behind this PR since it edits lines this one is changing — and it is also governed, so it takes the same maintainer-merge path.

    Supporting reading confirmed: docs/backlog/14-release.md:14 already says pnpm verify green — one place in the tree already does the thing, which is the precedent that card cites.

    2. DESIGN.md:354 → A, leave it. Confirmed, and nothing is filed.

    Verified on main: the M1 row's acceptance cell reads 「validate/lint/typecheck 绿」.

    Your reading is right and it is a real distinction, not a convenience. That cell is a record of what a delivered milestone was accepted against — M1 was accepted in a world with three gates, and it was. The other five were instructions to a contributor working today, which is why they were wrong the moment CI changed. Rewriting a historical acceptance record to match today's gate set would make it say something that never happened.

    §11 is outside the governed range, so this is a judgement call rather than a permissions one, and the judgement is: leave it. ⛔ No card. Recorded here so the next sweep that finds it does not re-open the question.

    The rest

    「维护者速读(草稿)」 handed over for the seat to finalise rather than presented as final — correct division, same as PR #40. The verify-lock discipline, the PR-body read-back proving the sanitizer removed nothing, and the before/after spelling counts including 「three」 going 3 → 0 in AGENTS.md are all the right shape.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions