Skip to content

Commit eff0a96

Browse files
huangyiireneclaude
andauthored
fix(pm): a pure-regeneration head move keeps the contract-review record, decided on the committed trees (#19634)
Part of #19244 (the card stays open for the ruling's fourth bullet — arm-time drift re-measurement in `references/landing-operations.md`, PR #19379's region — and for the `check-governed-merges.mjs` CURRENT-head prose; the seat returns it to the queue at this PR's landing) Clause-②: no A review record binds to a head, so any push re-owes the review. On a generated-artefact-dense surface that is a loop the reviewed seat cannot exit: somebody else lands, baselines drift, the seat regenerates, the head moves, the record is owed again — measured four times in one round on this card, with two PASSed pull requests left unlanded. The maintainer ruled it (2026-09-20, director seat batch #193 item 2, letter B′, comment 5749024878), verbatim and untranslated: > 纯重生成提交不需要开达档复核记录 ## What lands **The rule text** — `references/contract-review.md` gains ONE line under the head-binding sentence at :20 and retires one, so the file stays at its ceiling of 60 (headroom 0), the new line at 110 bytes — it names the literal `Regen-provenance:` token the reader matches, which is what the ruling's execution paragraph orders into this line: - 例外:纯重生成 head 后移原记录继续管;判据机读已提交树;PR 落 `Regen-provenance:` 行。 Retired as a provable duplicate: 「0 = 确定性行全清;4 = 任一不成立,只确定性行红才挡落地;3 = 环境答不了 ⛔ 不作干净。」 — its surviving homes are the checker's own header section `## Exit codes — the refusal to read as clean, in one table` (which AGENTS.md makes the authority on that detail, and which shows the retired line had also drifted: 3 is PREREQUISITE NOT MET there, and 2 is the cannot-answer verdict) and `SKILL.md` 〈入队与落地〉 :657 for 「只确定性行红才挡」. The `Regen-provenance:` exact format now lives in the checker's C3 `moved-after-clear` remedy, the row a seat whose head moved lands on. **The criterion, both arms, on committed trees**: of the paths `git diff -z --name-only OLD NEW` lists, drop every one carrying `merge=os-regen` (`git check-attr --source NEW -z merge --stdin`), then drop every one this pull request never touched at either head — a path absent from both `git diff --name-only MERGE-BASE-OLD OLD` and `git diff --name-only MERGE-BASE-NEW NEW` moved only because the base moved, which is the ruling's own 「the merge commit's own carry-over from main」. Empty is the whole criterion. The second arm is what makes the exception fire at all: without it a merge-forward lists every path main carried over and reads them as hand-written. **The mechanism**, once, in `scripts/pm/check-clause2-carriers.mjs`, and reached by the queue guard through the lazy import it already takes (⛔ no second parser): - `REGEN_PROVENANCE_LINE` reads one hop off either thread — the PR's or its card's — in the shape `Regen-provenance: RECORD-ID · OLD-HEAD → NEW-HEAD · COMMAND → (empty)`. Everything after the second sha is the seat's own transcript and is deliberately unread. - `regenChainToHead` walks back from the pull request's current head over as many hops as the thread carries. Hops are de-duplicated on record + from + to BEFORE the ambiguity test — the reader searches both carriers and the governed text trains the dual-carrier habit, so one hop posted on the PR and on its card is one hop — while two DIFFERENT hops arriving at one head still end the walk rather than being ranked. - `unexplainedPathsBetween` runs the ruled test on the two **committed** trees: the two-dot name-only diff, the attribute read with `--source NEW` (so `.gitattributes` itself is read out of a commit, never out of the working tree), and the PR's own delta at each head against `merge-base BASE head` — the base is `origin/main` in the carriers reader and the merge group's own base sha in the queue guard. - `regenCarry` answers four states that are never folded: `none` (no line — today's rule, untouched), `carried`, `refused` (a line that does not certify), `unreadable` (the environment could not answer — a commit or the base ref this reader cannot reach). - `gateBindingState` no longer reports `moved-after-clear` for a carried move, and `locateReviewOfRecord` re-reads the record at the carried head — pinned to the record id the chain names, so a line pointing at a comment the thread does not carry certifies nothing. **The line is a pointer, never the evidence.** Every reader re-runs the test itself. A reader that cannot reach both commits or the base answers with a gap: the pair is UNJUDGED in `--pair` and the queue guard refuses on `EXIT_REFUSED_UNREADABLE` — ⛔ never clean, in either reader. A seat that writes the line and nothing else has certified nothing. The committed-trees half is not stylistic: it is comment 5748085403's reading, where one un-added regeneration answered `git status`, `git diff --cached` and `git diff` three different ways and the `--cached` reading was main's side, which looks exactly like the answer. ## Measured, on real committed trees Measured with the installed git (2.43.0), on this branch's OWN history rather than a fixture: | range | what it is | moved paths | verdict | |---|---|---|---| | `60c99d8` → `180ce09` | the merge-forward this PR made in round 1 | 2 hand-written `.changeset/*.md` that main brought; unexplained = 0 | **CARRIED** (round 1's one-arm test refused exactly this commit) | | `744a0a3` → `180ce09` | this PR's own base to its head — the lit control, same reader, same run | unexplained = 3, this PR's own three files | refused (correct) | | `0b4022b` → `744a0a3` | a source change on main | 28 moved, 10 dropped by the attribute, 18 kept | refused (correct) | Both directions demonstrated on real trees. The line reader was also run against the real specimen this card recorded (record 5746847791, `5dd391125e` → `e1ae025756`) and parses it. Ablation, one-shot through `scripts/ablation-replace.mjs` on the committed tree: removing the hop de-duplication (blob `9203b8a3c258` → `4369e387fbc9`, anchor 1 → 0, proved on disk) fails exactly the two pins that cover it — 2 of 1140, by name — and the restore is byte-identical to the HEAD blob with a clean `git diff HEAD`. ## Gates — every one, with the exit code captured before any pipe `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` derived 44 families for these three paths at the final head; all 44 ran and `--ran` reconciles 44/44 with 0 NOT-MEASURED (a derived zero: every row recorded an exit code and none is 3). - 44 of 44 exit 0, including `check:pm-clause2-carriers` (1140 cases; 1115 on main), `node scripts/pm/check-governed-queue-guard.mjs --self-test` (312 cases; 301 on main), `check:pm-skill-ratchet`, `check:pm-skill-id-lint`, `check:skill-frame-sync`, `check:pm-dispatch-gates`, `check:pm-governed-merges`, `check:nul-bytes`, `check:doc-authoring`, `check:cross-package-test-inputs`. - Also run, outside the derivation: `check:pm-governed-prose` exit 0, `node scripts/check-skills-token-ratchet.mjs` exit 0. - `pnpm --filter @objectstack/lint run check:doc-formula-expressions` first exited **3** (PREREQUISITE NOT MET — nothing measured). Re-run after `turbo run build --filter=@objectstack/formula --filter=@objectstack/lint`: **exit 0**. - `git merge origin/main` at the final head brought `49d5069` (17 files: `packages/spec` field-scale and `scripts/pm/post-stamped.mjs`) — no conflict, no `merge=os-regen` path moved, no deferral (`node scripts/check-regen-pending.mjs` exit 0), `pnpm-lock.yaml` unmoved; every gate above was run AFTER that merge, on the final head. ## Line ratchet — green, paid in the file's own currency `pnpm check:pm-skill-ratchet :: exit 0` — 「contract-review.md is 60 lines (ceiling 60; headroom 0)」. The ceiling was NOT raised and `scripts/pm/check-skill-line-ratchet.mjs` is untouched: round 1's two lines compress to one and one provably duplicated line retires, the only currency a line ratchet takes. Two other duplicate candidates were examined and left in place because each carries a residue with no surviving home (a scheduling clause at :26; the ③ of a numbered list at :47). ## Concurrency PR #19379 (draft, `claude/pm-superseded-references-tier`) is open on `scripts/pm/check-governed-queue-guard.mjs`. Its diff was read first: it rewrites the THIRD-leg header prose around the superseded references-tier wording (lines ~279–310) and one self-test summary string. This PR touches neither — its hunks are `git()`'s stdin, `runGuard`'s reader parameter, the pair it builds, `recordVerdict`'s carried fields, the CLEAR rendering and a new self-test battery. The later lander merges once. `origin/main` was merged into this branch before this PR opened. `#19068` — the `exports`-map sentence in the same reference file — is a different card and a different region; it is not addressed here and remains open. ## Acceptance notes - `check-half-states.mjs` H51 was measured, as the ruling's execution stroke asks: it does **not** refuse a moved head. It fires only on a verdict for the **current** head while the label is still hung, and says so itself — "A review naming an OLDER head is NOT this row". It is report-only patrol input and writes nothing, so nothing there needed changing. - `check-governed-merges.mjs` is the post-merge audit and refuses nothing; its Tier S prose says a record "for the CURRENT head". That sentence is now narrower than the rule, but it is prose in a report-only tool and outside this card's file surface. - The ruling's fourth bullet — arm-time re-measurement of drift becoming standing practice — is about landing operations, not carrier discipline, and `references/landing-operations.md` is PR #19379's region. It is not landed here. - The queue battery's former duplicate specimen is now a real class-(ii) case — a generated path moved BESIDE one of this pull request's own. The queue renders a refused carry as an absent record and does not print the path, so the battery asserts the name at `unexplainedPathsBetween`, the reader that owns the reason. Making the queue print it is a ~9-line change, named and not taken. - The de-duplication key is the exact record + from + to triple: the same hop spelled with DIFFERENT sha abbreviations on the two carriers is still two hops and still ends the walk — the refusing direction. - `skip-changeset`: `.claude/**` and `scripts/pm/**` are in no package's `files[]`; this diff publishes nothing. --- _Generated by [Claude Code](https://claude.ai/code)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 80ca0b1 commit eff0a96

3 files changed

Lines changed: 363 additions & 15 deletions

File tree

‎.claude/skills/pm-dispatch/references/contract-review.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
- FAIL 同 PASS 剥双载体:同笔留卡上交接评论(引复审、独立性对、欠改);卡态与 assignee 不动。
1919
- 重挂前先查裁决:闸门标签缺失 ⇒ 先 grep 卡评论找复审结论;`get_reviews` 读空 ≠ 未复审。
2020
- PASS + 无标 + head 未动 = 已清标不是被剥;head 后移或无结论才重挂;清标缺引记录即半态。
21+
- 例外:纯重生成 head 后移原记录继续管;判据机读已提交树;PR 落 `Regen-provenance:` 行。
2122

2223
## 复核归属与资格(按面)
2324

@@ -40,7 +41,6 @@
4041
- 轮次报告设复审清单专节,形状与代裁清单同为强制审计。
4142
- 落地前检三条,过则 Tier S 入队、Tier H 等人批:① 达档条款②复核 PASS 在案(同形记录)。
4243
- ② 双载体已清,逐对机读 `PM_SWEEP_REPO=仓 node scripts/pm/check-clause2-carriers.mjs --pair N`。
43-
- 0 = 确定性行全清;4 = 任一不成立,只确定性行红才挡落地;3 = 环境答不了 ⛔ 不作干净。
4444
- 确定性行 = 记录在案、`Served-tier:`、双载体一致、认领形;C5 放宽 tell 只报告,归复核裁。
4545
- ③ PR check 全绿,⛔ 非 required 子集;例外:merge-base 同签名的红不计、按设计而红见 SKILL.md。
4646
- 签名 = 失败步 + 首错行,读 base check runs 的 API ⛔ 不凭口述;主干红止血立单不变。

0 commit comments

Comments
 (0)