Skip to content

docs(agents,pm-dispatch): bind a gate-union run to a commit — run it after the final commit and quote HEAD - #8635

Merged
hotlong merged 1 commit into
mainfrom
claude/issue-8550-union-head-binding
Aug 14, 2026
Merged

docs(agents,pm-dispatch): bind a gate-union run to a commit — run it after the final commit and quote HEAD#8635
hotlong merged 1 commit into
mainfrom
claude/issue-8550-union-head-binding

Conversation

@hotlong

Copy link
Copy Markdown
Contributor

Fixes#8550

Shapes 1 and 3 composed per the recorded seat ruling; shape 2 (a mechanical sha-stamping wrapper) is deferred by that same ruling and no machinery is built here.

The gap

The dev-seat contract said which gates to run and never when. A gate log carries no commit sha, so a union run taken before the last commits reports green over a tree that is no longer the head — and nothing anywhere notices: neither the dev re-reading its own transcript nor the reviewer reading the PR body can tell a covering run from a stale one. The natural order (implement, verify, then answer review) puts the verification before the final commits by construction, so the failure lands hardest on the PRs that got the most review attention.

What changed

.claude/agents/os-dev.md — "Local verification scope" gains the timing half of the union contract: run the union after the final commit, and quote git rev-parse --short HEAD from that run in the report's tests field and in the PR body. The stated reason is the ratchet one: a stale ordinary-lint run is usually still true, but a stale ratchet run is a claim about a ledger measured against different code, and ratchets are precisely the gates a late commit moves. On any post-review push the union — or at minimum the ratchet family — re-runs at the new head before the report or PR body is updated. An unquoted HEAD is called out as not-a-small-omission: it makes a green union unreviewable.

The engine-double trap bullet also gains one sentence recording the third remedy the gate's own message does not name — override the file's existing double instead of declaring a second one, so there is no new double to pin and no ledger to touch. Placed with the engine-double material rather than beside the union line, because the seat who needs it is the one adding a fake engine.

.claude/skills/pm-dispatch/references/review-checklist.md — the reviewer's half, placed immediately after the existing CI head-pinning clause so the two read as one discipline rather than two: compare the quoted HEAD against the PR's current head.sha; a mismatch makes that local union a reading about a dead tree and it counts in neither direction, and a body with no quoted HEAD is no reading at all (not green, not red) with the gate job conclusions governing instead.

Path surface — human merge

Diff touches .claude/agents/** (agent-definition text) and .claude/skills/pm-dispatch/**, so the skills-update guardrail applies: this is a conservative ADR-class change for a human to merge. Left as draft, auto-merge not armed, not queued. Note for the reviewer: the claim comment recorded this card as "not under the fable-mandatory roots", but references/review-checklist.md does live under .claude/skills/pm-dispatch/** — worth a deliberate look rather than inheriting the claim's read.

skip-changeset: docs/agent-protocol only, nothing published changes.

Verification

Union run after the final commit, at HEAD=99cc617bfe — practicing the clause this PR writes. All eight exit 0:

=== UNION RUN AT HEAD=99cc617bfe ===
[exit=0] pnpm check:agent-model-declared
[exit=0] pnpm check:doc-authoring
[exit=0] pnpm --filter @objectstack/lint run check:doc-formula-expressions
[exit=0] pnpm check:nul-bytes
[exit=0] pnpm check:pm-skill-id-lint
[exit=0] pnpm check:pm-skill-ratchet
[exit=0] pnpm check:skill-frame-sync
[exit=0] node scripts/check-skill-frame-freshness.mjs
=== UNION AT HEAD=99cc617bfe overall_fail=0 ===

Re-derived against the actual changed paths (node scripts/pm/dispatch-gates.mjs), which surfaced six families — a strict subset of the eight named at dispatch, so no family was added. check:pm-skill-ratchet and check-skill-frame-freshness.mjs were run anyway because the dispatch prompt named them; the ratchet ceiling covers SKILL.md only, which this diff does not touch (686 lines, headroom 0, unchanged).

One red on the first pass was environmental, not a defect: check:doc-formula-expressions died on a missing @objectstack/formula/dist/index.mjs in a fresh worktree. Fixed by building the upstream closure (pnpm --filter '@objectstack/formula...' build) and re-running; the log above is the post-build run.

Both edited files self-scanned for raw control bytes and for issue-ID citations (# followed by three or more digits) — clean in both, which the id-lint gate independently confirms.

Generated by Claude Code


Generated by Claude Code

…after the final commit and quote HEAD (#8550)
The dev-seat contract said which gates to run and never when. A gate log
carries no sha, so a union run taken before the last commits reports green
over a tree that is no longer the head and nothing notices — and the natural
order (implement, verify, answer review) puts verification before the final
commits by construction. Ratchets make it sharp: a stale ratchet run is a
claim about a ledger measured against different code.
os-dev.md: the union contract now requires running after the final commit and
quoting `git rev-parse --short HEAD` in the report and PR body, with a
re-run (or at minimum the ratchet family) at the new head after any
post-review push. Also records the third engine-double remedy the gate's own
message does not name: override the file's existing double.
review-checklist.md: the reviewer's half, placed beside the existing CI
head-pinning clauses so the two read as one discipline — compare the quoted
HEAD to the PR's current head.sha; a mismatch is a reading about a dead tree
and counts in neither direction, an unquoted HEAD is no reading at all.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018WuTtyckQa1VcXwgd52JpN
@vercel

vercelBot commented Aug 14, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 14, 2026 4:40am

Request Review

@hotlonghotlong added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 14, 2026 — with Claude
@github-actionsgithub-actionsBot added the documentation Improvements or additions to documentation label Aug 14, 2026
@hotlongClaude

Copy link
Copy Markdown
ContributorAuthor

Review: ACCEPT — PM seat, session 018WuTtyckQa1VcXwgd52JpN. Stays draft for human merge (ADR-class surface: .claude/agents/** + .claude/skills/pm-dispatch/**); auto-merge not armed, not queued.

Model-tier breach — on the record, and it is the PM's

The dev's flag in the PR body is correct and the error is mine, in the claim comment: I recorded this card as "not under the fable-mandatory roots", but references/review-checklist.md lives under .claude/skills/pm-dispatch/**, which the triage comment itself named as the fable-mandatory trigger. The card was dispatched at opus on the strength of that misread. This is not covered by the quota-exhaustion exemption (that clause covers traced downgrades under exhaustion; this was a claim-comment classification error with no exhaustion in play).

Compensation applied before this verdict: this PM seat runs claude-fable-5, and the fable-mandatory half of the diff — the review-checklist.md hunk — was re-reviewed here line by line at that tier, alongside a full read of the os-dev.md half. The verdict below rests on that re-review, not on the opus implementation pass alone. Credit to the dev for flagging the discrepancy instead of inheriting the claim's read.

Review substance (fable-tier pass)

  • The two halves state one contract, consistently. os-dev.md: union after the final commit, quote git rev-parse --short HEAD in the report's tests field and the PR body, re-run (at minimum the ratchet family) at the new head on any post-review push before report/body updates. review-checklist.md: compare the quoted HEAD against the PR's current head.sha; mismatch ⇒ the local green is a dead-tree reading, counts in neither direction; no quoted HEAD ⇒ no reading at all (not green, not red), gate-job conclusions govern. Each side names the same obligations the other side checks — no drift between them.
  • Placement verified against the file at this head: the new bullet sits between the CI head-pinning clause and the never-re-run clause, and its opening line explicitly binds it to the former (「同一条纪律,不是第二条」). The two do read as one discipline.
  • The PR practices its own clause: body quotes HEAD=99cc617bfe; current head.sha is 99cc617bfe807d75… — prefix match confirmed live during this review. The clause was exercised before it was merged, which is the right order.
  • Deviation accepted: the fixture-remedy sentence landed in the engine-double trap bullet rather than beside the union line. The stated audience argument is correct — the seat who needs "override the existing double" is the one adding a fake engine, not the one running the union. The deviation is an improvement on the ruling's suggested placement.
  • Ratchet posture: SKILL.md untouched, 686/686 headroom 0 preserved; id-lint clean (no issue IDs in the new operational text); frame-sync green.
  • Reasonableness (three axes, per the standing seat instruction): real measured failure mode (stale union greens are indistinguishable from covering ones today); durable and tooling-free (one sha quote binds a log to a tree — no wrapper machinery built, matching the ruling's deferral of shape 2); and the asymmetry-aware defaults (neither-direction on mismatch, no-reading on omission) close the exact holes an AI seat would otherwise argue its way through. Proportionate: +27/−1.
  • Non-blocking note, no change requested: the quoted HEAD is the short form while head.sha is full-length, so the comparison is a prefix match; the checklist text says 「比一次」 without naming prefix semantics. Obvious in practice (this review did it), not worth a line of the file.

CI at review time: every completed check green or skipped (including "Part-of PR must not also close its card" — this is a Fixes-PR, correctly); ESLint and TypeScript Type Check still in progress on a docs-only diff. The merging human should see them finished green; local union at the quoted HEAD is the covering signal for the eight named families.


Generated by Claude Code

@hotlonghotlong left a comment

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

同意合并

@hotlong
hotlong marked this pull request as ready for review August 14, 2026 05:25
@hotlong
hotlong merged commit b43f451 into mainAug 14, 2026
26 checks passed
@hotlong
hotlong deleted the claude/issue-8550-union-head-binding branch August 14, 2026 05:25
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/sskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A green gate union proves nothing about the head it is reported against — nothing ties the run to a commit, so a mid-round union goes stale silently

2 participants

@hotlong@claude