Skip to content

fix(pm): a pure-regeneration head move keeps the contract-review record, decided on the committed trees - #19634

Merged
huangyiirene merged 6 commits into
mainfrom
claude/issue-19244-pure-regen-keeps-review-record
Sep 22, 2026
Merged

huangyiirene merged 6 commits into
mainfrom
claude/issue-19244-pure-regen-keeps-review-record

Conversation

@huangyiirene

@huangyiirene huangyiirene commented Sep 22, 2026

Copy link
Copy Markdown
Collaborator

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 textreferences/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
60c99d8180ce09 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)
744a0a3180ce09 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)
0b4022b744a0a3 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, 5dd391125ee1ae025756) and parses it.

Ablation, one-shot through scripts/ablation-replace.mjs on the committed tree: removing the hop de-duplication (blob 9203b8a3c2584369e387fbc9, 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 fix(pm): repair two carriers still spelling the superseded references-tier boundary #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

The maintainer ruled 2026-09-20 that a pure regeneration commit does not
re-open an at-tier review record. The criterion is machine-read on the
COMMITTED trees — the paths that moved between the recorded head and the
new one, restricted to those carrying no `merge=os-regen` attribute,
must be empty — and never a seat's own statement. The `Regen-provenance:`
line is a pointer a later reader re-runs, not the evidence.

Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc
Co-authored-by: Claude <noreply@anthropic.com>
Seventeen cases in `check-clause2-carriers.mjs` and seven at the queue:
the chain read off either thread, the multi-hop walk, the ambiguity that
carries nothing, the empty tree that carries, the hand-written path that
does not, the record id the line must name, and the unreachable tree that
is UNJUDGED rather than clean in both readers.

Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc
Co-authored-by: Claude <noreply@anthropic.com>
@huangyiirene huangyiirene added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 22, 2026 — with Claude
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 22, 2026
…and edit

The ruled criterion reads 'every touched path is a generated artefact OR
the merge commit's own carry-over from main'. Round 1 implemented only the
first arm, so a head-to-head diff read every path main carried over as
hand-written and the exception never fired on the loop the card measured.

The second arm, machine-read on committed trees: a path this pull request
never touched at EITHER head moved only because the base moved. The PR's
own delta is read once per head (merge-base + one name-only diff), which
is the same verdict as a per-path diff comparison at four calls instead
of two per path. A run naming no base ref is UNJUDGED, never clean.

The reference text returns to its ceiling: the exception compresses to one
line, and the duplicated-and-drifted --pair exit table retires to the
checker's own header, which is the authority on that detail.

Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc
Co-authored-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: dcffe20eee8194cb5bdd4a64a520f57befd233aa

① Derived judgments

(a) Criterion — faithful, and the second arm is the ruling's own, not an invention. unexplainedPathsBetween (carriers :3204-3216) takes git diff -z --name-only OLD NEW (:3205-3206), drops every path whose check-attr --source NEW value is os-regen (:3208-3210), then drops every path absent from BOTH merge-base BASE X..X deltas (:3212-3214). Arm ② is licensed by the ruling's own gloss 「every touched path is a generated artefact or the merge commit's own carry-over from main」; it does contradict the ruling's parenthetical 「every non-generated path is byte-identical between the two heads」, and the dev is right that without it the exception never fires (round 1 refused this branch's own merge-forward). Judged RIGHT. The equivalence to the specified per-path byte comparison is sound: if neither delta names p then OLD:p == MB-OLD:p and NEW:p == MB-NEW:p, so both diffs compare the same blobs; if either delta names p the left sides differ and identical content would mean an identical blob. Case walk, in code, not report: merge-forward of another PR's hand-written path → own empty → hand.filter empty → carried (:3214, pinned :8161); hand edit beside a regeneration → path in the NEW delta → refused, named (:3270-3272, pinned :8156); hand-resolved merge → resolution sits in the NEW delta → refused (:8163). ⭐ The .gitattributes-widening attack closes itself: a PR that marks a hand path os-regen has .gitattributes in its own delta and is refused.
(b) Committed trees only — yes. The four runGit shapes are diff A B, check-attr --source TO, merge-base, rev-parse (:3205, :3208, :3212, :3280). No git status, no one-rev diff, no index read; --source is present as the ruling's committed half requires. Residual, inherent to the ruled instrument: check-attr still honours .git/info/attributes, which outranks the in-tree file — out of the ruling's scope, named not charged.
(c) Pointer, never evidence — yes, all three states. regenCarry re-runs on every call (:3239-3288). Cannot fetch a commit → unreadable with a fetch remedy (:3265-3269) → gateBindingState :3347 and locateReviewOfRecord :4557 both return unreadable, and pairUnjudged :5334-5336 makes it UNJUDGED, ⛔ never clean (pinned :8165-8166). Cannot resolve the base → unreadable (:3258-3261, pinned :8169). Chain naming two records → refused (:3248-3250, pinned :8158). No git reader at all → unreadable (:3252-3256, pinned :8167). C3's remedy states the line certifies nothing by being present (:3395-3402, pinned :8157).
(d) Chain walk — one real defect. Multi-hop works (:3172-3178, pinned :8143); abbreviation via shaMeets (:3159) is correct against contractReviewHeadMatch's prefix test, and the rev-parse expansion at :3277-3283 is what makes a 40-char carried head match a 12-char record span (its catch falls back to the abbreviation, which then simply fails to match — conservative). The record-id pin is right: the recursion is depth-1 (carriedHead === null guard, :4557) and only returns when back.id === carry.record (:4562), so a line naming a record the thread does not carry leaves the record ABSENT (pinned :8159). ⛔ DEFECT: :3174-3175 breaks on step.length !== 1 without de-duplicating identical hops. Two byte-identical Regen-provenance: lines — the same record, same OLD, same NEW — posted on both carriers are not ambiguity, yet they end the walk and kill the carry. The reader deliberately reads both threads and pin :8141 advertises exactly that ("READ off either thread"), and :40 trains the dual-carrier habit, so this is a trap the code invites. Fail-closed, but it defeats the exception in the most likely shape.
(e) Queue guard — correct, and nothing is re-implemented. loadRecordRecognisers :759 binds locate to the imported locateReviewOfRecord; the guard adds only the reader (:1533, :1626) and the base (:2475-2476, context.baseSha = group.base_sha at :912 / pull.base.sha at :926 — the merge group's OWN base, as asked). git() gained an input channel and stdio[0]: 'pipe' for check-attr --stdin (:2100-2101). unreadable reaches EXIT_REFUSED_UNREADABLE via :1112 → :1147, pinned at :4469 and :4471 (no reader) and :4479 (no base). The no-regression control is there and is the important one: a record on the CURRENT head clears with a runGit that throws on any call (:4481), i.e. no tree is read when none is owed. os-regen-merge.sh:20 forbids rebase/force-push, so the OLD head stays reachable in a fetch-depth: 0 checkout.
(f) Reference file — one required change. The file reads 60 lines and the added :21 is 115 bytes, both under the ratchet's 60-line ceiling and 120-byte cap. The retired line's two homes check out: the checker's header section ## Exit codes — the refusal to read as clean, in one table (carriers :602-665, covering 0, 2, 3 and --pair's 4 at length) and SKILL.md:657 for 「只确定性行红才挡请审」; AGENTS.md:19 does make the script header the authority on detail; the retired line was also drifted (it said 3 = 环境答不了, the table says 3 = PREREQUISITE NOT MET and 2 = INCOMPLETE), so retiring drops no rule. ⛔ DEFECT: the ruling's execution paragraph orders :21 to carry 「the exception and the provenance-line format」. :21 carries the exception but names neither the format nor the literal token Regen-provenance: — it says only 「PR 落 provenance 行」, which collides with the pre-existing :40 「清标同笔落 PR provenance 评论」, a different artefact. A seat reading the governed text alone cannot write a line the machine will recognise. The dev's report frames the format move as a compression route and does not name it as a departure from the ruling's instruction. This is avoidable inside the cap: - 例外:纯重生成 head 后移原记录继续管;判据机读已提交树;PR 落 \Regen-provenance:` 行。is 110 bytes, still one line, ceiling untouched — it trades 「⛔ 非自述」 (already carried by 机读, and spelled at length in the C3 remedy) for the token the reader actually matches. (g) **Narrowing / widening.** The OLD-head arm is right:ownis the UNION of both deltas (:3213), so a path the PR touched at OLD but not at NEW is REFUSED, not passed — correct, because the PR's own contribution to that path changed and the record never judged that.baseRef: 'origin/main'is hard-coded ingather(:6172) rather than read frompr.base.ref; for a non-main base the merge-base is earlier, the delta larger and the verdict more refusing — conservative, named not charged. One accepted asymmetry: when the chain names a record that does not exist, gateBindingStatestill answerscompleted(:3348-3350) while C6 answers absent — the pair is still refused, but the two rows now disagree about why. (h) **Batteries.** Carriers: 23 declared (:866), 23t(calls (:8141-8169), floor 36 → 37 (:882). Guard: 10 declared (:557), 10 asserts (:4465-4481), floor 23 → 24 (:562). Floors raised consistently. All four case classes are exercised in the carriers reader: (i) :8161, (ii) :8156 and :8160, (iii) :8163 with the same-path cross-pin at :8164, (iv) :8169, plus the once-per-head pin at :8165. In the guard only (i)/(iii)/(iv) are claimed and landed — and :4463'sgitHandis byte-identical to :4476'sqgit(qHand)`, so asserts :4467 and :4477 pin the same specimen with the same expectation. One of the two is dead weight; class (ii) is not separately exercised at the queue.

② Semver level

No release. .claude/** and scripts/pm/** ship in no package's files[]; nothing published changes, no consumer-visible surface moves. skip-changeset is on the PR and Check Changeset reads skipped at this head — the repo-correct form per review-checklist 〈范围与 changeset〉, ⛔ not an empty changeset. Correct.

③ Boundary flags

  • +331 net on the two scripts against a +250 budget — accept the number, minus one item. The added mechanism is ~168 lines (carriers :3120-3288) of which roughly half is the rule quote and the four-state contract, plus 64 test lines; that density is this corpus's own house style (the file's own header runs 700 lines) and a budget would not refuse it. The one thing a budget should refuse is the guard's duplicate specimen at :4463/:4476 noted in ①(h). Reporting rather than absorbing the overage is the right act.
  • The set-membership shape instead of the per-path byte comparison — sound. Equivalence argued in ①(a); it is exact, not approximate, and it costs four git calls per hop instead of two per path. Accepted.
  • PR body not patched — correct deviation, and it is now the seat's act. .claude/agents/os-dev.md forbids the dev a PATCH /pulls; the body is stale in three places (it still describes round 1's TWO reference lines, and still carries ## One gate is RED, which this head contradicts — 22 success / 8 skipped / 1 in_progress). The seat must land pr_body_replacement in the same stroke as any re-dispatch.
  • landing-operations.md untouched, so the ruling's fourth bullet (arm-time re-measurement becomes standing, ⛔ no reuse of an earlier zero) lands nowhere. ESCALATE. The ruling's execution paragraph assigns this card only the reference edit plus the reader fix, so it is not this PR's region — but the PR says Fixes #19244, and closing the card leaves bullet 4 with no carrier at all. Before ACCEPT the seat must either file a card for it against references/landing-operations.md (PR fix(pm): repair two carriers still spelling the superseded references-tier boundary #19379's open region, single-writer) or hand it to that PR, per review-checklist 「只落地了可实施的一半 ⇒ 必须 Part of #<n>」.
  • Class-(b) finding on check-governed-merges.mjs's 「CURRENT head」 prose — valid, correctly out of scope. The seam is real (the audit refuses nothing, so the cost is prose a reader acts on). File it; ⛔ do not widen this PR.
  • Checks at this head: Lint & Repo Gates is in_progress — an honest reading for a draft (review-checklist 〈测试与门禁证据〉); it is not a ground for this verdict, but it is not yet green and precondition ③ cannot be read as met until it is.
  • Must change before this record can read PASS: (1) :21 must name Regen-provenance: — the 110-byte spelling in ①(f) fits the cap, the ceiling, and the ruling's instruction; (2) regenChainToHead :3174 must de-duplicate hops on record + from + to before the step.length !== 1 test, so two identical lines on the two carriers do not end the walk. Both are small and local; nothing in the mechanism's safety needs rework — I found no path on which an unreviewed head reads clean.

Implemented-by: claude/issue-19244-pure-regen-keeps-review-record
Reviewed-by: session_01Wnstp2kTth7sGXfr8fXypc

VERDICT: FAIL


Generated by Claude Code

…two carriers is one hop

Three things the at-tier review named. (1) The rule line now carries the
literal `Regen-provenance:` token, so a seat reading the governed text
alone can write a line the reader matches; it stays one line at 110 bytes
and the file stays at its ceiling. (2) regenChainToHead de-duplicates hops
on record + from + to BEFORE the ambiguity test: the reader searches both
carriers and the text trains the dual-carrier habit, so one hop posted on
the PR and on its card arrived twice and ended the walk. Two DIFFERENT
hops into one head still do. (3) The queue battery's duplicate specimen
becomes a real slipped-in-edit case, and the path it names is asserted at
the reader that owns the reason.

Claude-Session: https://claude.ai/code/session_01Wnstp2kTth7sGXfr8fXypc
Co-authored-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: d362803eb436b5e6332042d94fb0a8a5b97b245a

① Derived judgments

(a) Criterion — faithful, both arms re-walked in code. unexplainedPathsBetween (carriers :3212-3223): git diff -z --name-only OLD NEW (:3213-3214), drop every path whose check-attr --source NEW value is os-regen (:3216-3218), drop every path absent from BOTH merge-base BASE X..X deltas (:3220-3222). Arm ② is the ruling's own gloss 「the merge commit's own carry-over from main」, and the set-membership shape is exactly equivalent to the specified per-path byte comparison (neither delta names p ⇒ both sides compare the same blob; either delta names p ⇒ the left sides differ and equal content would mean an identical blob). Case walk, in code: merge-forward of another PR's hand-written path ⇒ own empty ⇒ hand.filter empty ⇒ carried (:3222; pinned carriers :8174, guard :4482); a hand edit slipped beside a regeneration ⇒ the path is in the NEW delta ⇒ refused and NAMED (:3281-3283; pinned :8161, :8169, guard :4471, :4475); a hand-resolved merge of a path the PR itself touches ⇒ the resolution sits in the NEW delta ⇒ refused (:8176, guard :4484), with the same-path cross-pin at :8177 proving the DELTA and not the path name decides. ⭐ Correcting the previous record's overclaim: the .gitattributes-widening attack closes only when .gitattributes moves inside OLD..NEW; a PR that widened merge=os-regen at or before OLD keeps that widening unchecked afterwards. Residue of the ruling's own chosen instrument (it names merge=os-regen), and the reviewer of OLD owns that widening — named, not charged.
(b) Committed trees only — yes. The four runGit shapes are diff A B, check-attr --source TO, merge-base, rev-parse (:3213, :3216, :3220, :3288). No git status, no one-rev diff, no index or worktree read; -z on both the diff and --stdin, and the triplet parse (:3218) is bounded correctly. Named residue, unchanged: check-attr still honours $GIT_DIR/info/attributes, outside the ruling's scope.
(c) Pointer, never evidence — verified in all four states. computeRegenCarry :3253-3293 re-runs on every pair: no git reader ⇒ unreadable (:3260-3263, pinned :8168, guard :4478); no base ref ⇒ unreadable (:3266-3269, pinned :8179, guard :4485); a commit this checkout cannot reach ⇒ unreadable with the fetch remedy (:3276-3279, pinned :8166-8167, guard :4477 at EXIT_REFUSED_UNREADABLE); hops naming two records ⇒ refused (:3256-3259, pinned :8163). gateBindingState :3355-3357 and locateReviewOfRecord :4566-4567 both return unreadable on it, and pairUnjudged makes the pair UNJUDGED, ⛔ never clean (:8160 pins the clean case separately). The C3 remedy states the line certifies nothing by being present (:3409-3411, pinned :8162).
(d) Chain walk — the de-duplication is correct and the pins are non-vacuous. The key is record + "\u0000" + from + "\u0000" + to, written as the escape in source (:3175; zero raw NUL bytes in either file, so check:nul-bytes is honest). It is built OUTSIDE the loop, at :3173-3176, and the step.length !== 1 ambiguity test is at :3183 — de-dup runs BEFORE it, as required. The existing ambiguity pin still holds: two hops with different from are two keys, so step.length is 2 and the walk ends (:8152, null) — unchanged by the de-dup, a true regression pin. New pin :8153 (same record/from/to on the PR and on the card, different comment ids) yields exactly one entry ⇒ chain length 1; without the Map it would be 2 ⇒ break ⇒ null ⇒ the pin fails. New pin :8154 drives that chain end to end (completed + found + zero rows) and fails the same way. So the dev's 2-of-1140 ablation is corroborated FROM THE CODE, and nothing else in the battery routes a dual-carrier hop through regenChainToHead. Named residue, accepted: the key is the literal triple, so one hop spelled 7-hex on one carrier and 40-hex on the other is still two hops and still ends the walk — the refusing direction, and a shaMeets-based fold would need a longest-wins rule the round-1 finding did not ask for. Multi-hop walks pin at :8151; the loop is bounded because seen grows over a finite set of from values. shaMeets (:3159) is the right prefix test against contractReviewHeadMatch, and rev-parse at :3288-3291 expands the carried head so a 40-char head matches a 12-char record span (its catch falls back to the abbreviation, which then simply fails to match — conservative). The record-id pin holds: recursion is depth-1 (carriedHead === null guard :4566) and returns only when back.id === carry.record (:4570), so a line naming a record the thread does not carry leaves the record ABSENT (:8164).
(e) Queue guard — one mechanism, reached through the import. Nothing is re-implemented: loadRecordRecognisers binds the imported locateReviewOfRecord; the leg adds only the reader and the base (:1533, :1626, :2475-2476). baseRef is context.baseSha, which is group.base_sha on merge_group (:912) and pull.base.sha otherwise (:926) — the merge group's OWN base, as asked. git() gained the input channel and stdio[0]: 'pipe' for check-attr --stdin (:2100-2101). EXIT_REFUSED_UNREADABLE on an unreadable tree is pinned (:4477, :4478, :4485); the control that a record on the CURRENT head clears with a runGit that throws on any call (:4487) proves no tree is read when none is owed. qSlipped (:4464) is a genuine class-(ii) case and no longer a copy: a merge=os-regen path moves BESIDE scripts/pm/x.mjs, which own[REF_HEAD] names, so hand.filter keeps exactly the slipped path — distinct from qHand (:4460), which moves one hand path and no generated one. Assert :4471 reads EXIT_REFUSED_UNAPPROVED + record.state === 'absent'; assert :4475 imports unexplainedPathsBetween through RECOGNISER_SOURCES.tier (:4474 — the same lazy path the leg itself takes, pinned to ./check-clause2-carriers.mjs at :4431) with the right arguments and the right expectation. Asserting the path at the source is an acceptable resting place because the queue's rendering genuinely has no field for it, and the assert title says "the reader", not "the queue" — honest.
(f) Reference file — the round-1 must-change is landed. :21 reads exactly 「- 例外:纯重生成 head 后移原记录继续管;判据机读已提交树;PR 落 Regen-provenance: 行。」 at 110 bytes, and the file is 60 lines with a maximum line width of 120 bytes (line 56) — at the ratchet's ceiling 60 / headroom 0 (check-skill-line-ratchet.mjs :516, :1941) and inside the 120-byte cap. The collision with :40's 「清标同笔落 PR provenance 评论」 is gone: :21 now names a token, :40 a comment. Trading 「⛔ 非自述」 loses nothing the ruling requires: 判据机读 carries it on the line itself, and the C3 remedy spells it at length — 「⛔ The line certifies NOTHING by being present」 (:3409-3410) — with AGENTS.md:19 making the script header the authority on such detail. The retired 0/4/3 line's two homes both check out: the checker's ## Exit codes — the refusal to read as clean, in one table (:602-668, covering 0, 2, 3 and --pair's 4 at length) and SKILL.md 〈入队与落地〉:657. The retired line was also drifted (it said 3 = 环境答不了; the table has 3 = PREREQUISITE NOT MET and 2 = the cannot-answer verdict), so retiring it drops no rule and removes a wrong one.
(g) No form narrowed or widened past the ruling. own is the UNION of both deltas (:3221), so a path the PR touched at OLD but not at NEW is REFUSED — correct, the record never judged that contribution. baseRef: 'origin/main' is hard-coded in gather (:6180): for a non-main base the merge-base is earlier and the verdict strictly more refusing — conservative, named. REPO_GIT (:3231-3232) carries no cwd, so a sweep run with PM_SWEEP_REPO pointed at a repository other than the local checkout reads shas that do not exist and answers unreadable — fail-closed, named. One accepted asymmetry survives: a chain naming a record that does not exist leaves gateBindingState at completed (:3356-3357) while C6 answers absent — the pair is still refused, only the two rows disagree about why.
(h) Batteries — declared equals run, floors raised, pins non-vacuous. Carriers: 25 declared (:866) and 25 t( calls (:8149-8179), floor 36 → 37 (:882). Guard: 11 declared (:557) and 11 asserts (:4468-4487), floor 23 → 24 (:562). The two new de-dup pins are shown non-vacuous above from the code, not from the report. The retitled class-(ii) assert (:4471) exercises what it claims — a slipped-in edit beside a regeneration, not the class-(iii) specimen it duplicated last round. Guard classes (i)/(ii)/(iii)/(iv) plus the two controls are now all distinct; carriers cover (i) :8174-8175, (ii) :8161/:8169, (iii) :8176-8177, (iv) :8179, once-per-head :8178.

② Semver level

None — no release. .claude/** and scripts/pm/** appear in no package's files[]; nothing published moves, no consumer-visible surface changes. skip-changeset is on the PR and Check Changeset reads skipped at this head — the repo-correct form, ⛔ not an empty changeset. Correct.

③ Boundary flags

  • +348 net on the two scripts against a +250 dispatch budget — coverage, not padding; report it, do not absorb it. The +17 over round 1's +331 buys: the de-dup Map plus its rationale block (:3167-3176), two pins that the code shows are non-vacuous, and the replacement of a dead duplicate specimen with a real class-(ii) one. Round 1's only charged item (the duplicate at the queue) is gone, so the number is now +348 of live mechanism. Accepted as the overage a budget should be told about rather than one it should refuse.
  • The merge of origin/main carries nothing of its own. git diff 49d5069 d362803 --stat is exactly the PR's three files (+363/-15); git diff 62c76fe d362803 --stat is exactly main's 17 files. No hand resolution rode in.
  • Checks at this head: all green. 31 runs, latest per name: 21 success, 10 skipped, zero red, zero in_progress. Lint & Repo Gates, Governed Surface Queue Guard, Test Core 1-6 and the four Type Check legs all succeed. Precondition ③ reads met; Check PR Size is skipped, so the budget above is a dispatch fact, not a CI one.
  • PR body patched by the seat — resolved. The first line is now Part of #19244, ## One gate is RED is gone, and the Regen-provenance: spelling at :21 is described correctly. Part-of PR must not also close its card succeeds.
  • references/landing-operations.md untouched — round 1's ESCALATE now closes. The ruling's fourth bullet (arm-time re-measurement becomes standing, ⛔ no reuse of an earlier zero) has a carrier again: the body's Part of #19244 keeps the card open for it and for PR fix(pm): repair two carriers still spelling the superseded references-tier boundary #19379's region. Nothing further is owed by this PR.
  • Class-(b) finding on check-governed-merges.mjs's 「CURRENT head」 prose — valid and correctly out of scope. The seam is real at :329, :1027 and :1584 (a carried record also satisfies the Tier S landing), the audit refuses nothing, and the file is untouched. File it on the card; ⛔ do not widen this PR.
  • The queue not printing WHICH path refused — accepted as named, not taken. ~9 lines (carry the reason on the absent return, render it). The refusal is unaffected; only the operator's legwork is. File it with the above.
  • Nothing found on which an unreviewed head reads clean. Both round-1 must-change items are landed and independently re-verified; every judgment of the previous record was re-walked on this head.

Implemented-by: claude/issue-19244-pure-regen-keeps-review-record
Reviewed-by: session_01Wnstp2kTth7sGXfr8fXypc

VERDICT: PASS


Generated by Claude Code

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

Labels

documentation Improvements or additions to documentation size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants