Skip to content

Commit 62c76fe

Browse files
committed
fix(pm): name the Regen-provenance token in the rule, and one hop on 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>
1 parent dcffe20 commit 62c76fe

3 files changed

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

2323
## 复核归属与资格(按面)
2424

‎scripts/pm/check-clause2-carriers.mjs‎

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -863,7 +863,7 @@ const SELF_TEST_BATTERIES = Object.freeze({
863863
'#18862: cross-author LIVE claims with no `Release:` between — the hand-over the protocol never wrote, named; judged only after its effective instant': 52,
864864
'#16770: the exit-0 line says which carriers agreed — LABEL carriers — and that the PR body was not read': 14,
865865
'#18892: the claim comment\'s EDIT reading — taken from the two stamps already in hand, reported and never failed': 10,
866-
'⭐ the 2026-09-20 ruling: a pure-regeneration head move KEEPS the record, decided on the COMMITTED trees': 23,
866+
'⭐ the 2026-09-20 ruling: a pure-regeneration head move KEEPS the record, decided on the COMMITTED trees': 25,
867867
});
868868

869869
// DELETING an entry silences that battery's floor exactly as effectively as
@@ -3165,7 +3165,15 @@ const shaMeets = (a, b) => a.startsWith(b) || b.startsWith(a);
31653165
* thread carries no chain — the only direction this may err in.
31663166
*/
31673167
export function regenChainToHead(pair) {
3168-
const hops = regenProvenanceHops(pair);
3168+
// ⭐ DE-DUPLICATED FIRST, on record + from + to. The reader searches BOTH
3169+
// carriers and the governed text trains the dual-carrier habit, so ONE hop
3170+
// posted on the PR and on its card arrives here twice — identical bytes, not
3171+
// ambiguity. Judged by the ambiguity test below it would END the walk and
3172+
// kill the carry in its most likely shape; two DIFFERENT hops into one head
3173+
// still do, which is the fact that test exists for.
3174+
const hops = [
3175+
...new Map(regenProvenanceHops(pair).map((h) => [`${h.record}\u0000${h.from}\u0000${h.to}`, h])).values(),
3176+
];
31693177
let target = String(pair?.headSha ?? '').toLowerCase();
31703178
if (target.length < H51_SHA_MIN_HEX) return null;
31713179
const chain = [];
@@ -8142,6 +8150,8 @@ export async function selfTest() {
81428150
t('⛔ a line naming only one sha is not a hop — the tail after it is the seat\'s transcript and is unread', regenProvenanceHops({ prComments: [{ id: 9, body: `Regen-provenance: 3301 · \`${HEAD_9AF9}\`` }] }).length === 0);
81438151
t('the chain walks BACK over several hops, oldest first', regenChainToHead({ headSha: 'cccccccc', prComments: [PROV(HEAD_9AF9, 'bbbbbbbb'), PROV('bbbbbbbb', 'cccccccc')] })?.map((h) => h.from).join() === `${HEAD_9AF9},bbbbbbbb`);
81448152
t('⛔ two hops arriving at ONE head carry no chain — ambiguity is never ranked', regenChainToHead({ headSha: NEW_HEAD, prComments: [PROV(HEAD_9AF9), PROV('bbbbbbbb')] }) === null);
8153+
t('⭐ the SAME hop on BOTH carriers is ONE hop, ⛔ not ambiguity — the dual-carrier habit this file trains must not kill the carry', regenChainToHead({ headSha: NEW_HEAD, prComments: [PROV()], cardComments: [PROV(HEAD_9AF9, NEW_HEAD, 3301, 3351)] })?.length === 1);
8154+
t('…and it CARRIES end to end from there, so the pair is CLEAN rather than 重挂', (() => { const dual = moved([RECORD_ON_9AF9, PROV()], GIT_EMPTY, { cardComments: [CLAIM('Clause-②: yes'), PROV(HEAD_9AF9, NEW_HEAD, 3301, 3351)] }); return gateBindingState(dual).state === 'completed' && locateReviewOfRecord(dual).state === 'found' && pairRows(dual).length === 0; })());
81458155
t('⛔ a thread with no line carries none, so today\'s rule is untouched where nobody claims the exception', regenChainToHead(moved([RECORD_ON_9AF9])) === null && gateBindingState(moved([RECORD_ON_9AF9])).state === 'moved-after-clear');
81468156
// the tree test — and it reads COMMITTED trees, never the working one
81478157
t('⭐ an EMPTY non-`merge=os-regen` diff CARRIES the record: the pair reads COMPLETED, ⛔ not 重挂', gateBindingState(CARRIED()).state === 'completed');

‎scripts/pm/check-governed-queue-guard.mjs‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ const SELF_TEST_BATTERIES = Object.freeze({
554554
'⭐ #18701: the record lives on the PR or its card, and BOTH are read': 14,
555555
'⛔ #19036: the SIZE line at the queue — imported, per queued PR, fail-closed': 30,
556556
'⭐ #19344: the remedy names a path the ruleset actually offers': 5,
557-
'⭐ the 2026-09-20 ruling: a certified PURE REGENERATION carries the record to the queued head': 10,
557+
'⭐ the 2026-09-20 ruling: a certified PURE REGENERATION carries the record to the queued head': 11,
558558
});
559559

560560
// DELETING an entry silences that battery's floor exactly as effectively as
@@ -4459,13 +4459,20 @@ export async function selfTest() {
44594459
const qRegen = { moved: 'packages/spec/api-surface/ui.txt\0', attrs: 'packages/spec/api-surface/ui.txt\0merge\0os-regen\0' };
44604460
const qCarry = { moved: 'packages/spec/api-surface/ui.txt\0AGENTS.md\0', attrs: 'packages/spec/api-surface/ui.txt\0merge\0os-regen\0AGENTS.md\0merge\0unspecified\0', own: {} };
44614461
const qHand = { moved: 'AGENTS.md\0', attrs: 'AGENTS.md\0merge\0unspecified\0', own: { [REF_HEAD]: 'AGENTS.md\0' } };
4462+
// (ii) an edit SLIPPED IN beside the regeneration: a generated path moved, and
4463+
// so did one of this pull request's own, which its delta at the NEW head names.
4464+
const qSlipped = { moved: 'packages/spec/api-surface/ui.txt\0scripts/pm/x.mjs\0', attrs: 'packages/spec/api-surface/ui.txt\0merge\0os-regen\0scripts/pm/x.mjs\0merge\0unspecified\0', own: { [REF_HEAD]: 'scripts/pm/x.mjs\0' } };
44624465
const gitPure = qgit(qRegen);
4463-
const gitHand = qgit({ ...qHand, own: { [REF_HEAD]: 'AGENTS.md\0' } });
4466+
const gitHand = qgit(qSlipped);
44644467
const carried = await tierRun({ comments: onOldHead, runGit: gitPure, baseRef: QBASE });
44654468
assert('⭐ a-record-on-an-OLDER-head-CARRIES-when-the-move-is-a-certified-pure-regeneration', carried.exitCode === EXIT_CLEAR && carried.entries[0].record.state === 'stands' && carried.entries[0].record.carriedHops === 1, JSON.stringify(carried.entries[0].record));
44664469
assert('and-the-CLEAR-names-the-head-actually-reviewed-and-says-it-re-ran-on-the-committed-trees', /carried forward over 1 certified PURE-REGENERATION hop/.test(renderGuardVerdict(carried)) && /COMMITTED trees/.test(renderGuardVerdict(carried)) && /never on the `Regen-provenance:` line being present/.test(renderGuardVerdict(carried)), renderGuardVerdict(carried));
44674470
const handMoved = await tierRun({ comments: onOldHead, runGit: gitHand, baseRef: QBASE });
4468-
assert('⛔ a-HAND-WRITTEN-path-in-the-range-refuses-exactly-as-an-uncarried-old-head-does', handMoved.exitCode === EXIT_REFUSED_UNAPPROVED && handMoved.entries[0].record.state === 'absent');
4471+
assert('⛔ (ii) an-EDIT-SLIPPED-IN-beside-the-regeneration-refuses-exactly-as-an-uncarried-old-head-does', handMoved.exitCode === EXIT_REFUSED_UNAPPROVED && handMoved.entries[0].record.state === 'absent');
4472+
// ⭐ WHICH path is named by the reader that owns the reason; at the queue the
4473+
// refusal surfaces as an absent record, so the name is asserted at its source.
4474+
const { unexplainedPathsBetween } = await import(RECOGNISER_SOURCES.tier);
4475+
assert('…and-the-reader-NAMES-the-slipped-in-path-rather-than-the-generated-one-beside-it', unexplainedPathsBetween(gitHand, { from: REF_OLD, to: REF_HEAD, base: QBASE }).join() === 'scripts/pm/x.mjs', unexplainedPathsBetween(gitHand, { from: REF_OLD, to: REF_HEAD, base: QBASE }).join());
44694476
const blindTree = await tierRun({ comments: onOldHead, runGit: () => { throw new Error('fatal: bad object'); }, baseRef: QBASE });
44704477
assert('⛔ a-tree-this-build-cannot-reach-is-UNREADABLE-exit-4-never-clean', blindTree.exitCode === EXIT_REFUSED_UNREADABLE && blindTree.entries[0].record.state === 'unreadable');
44714478
assert('⛔ and-a-run-with-NO-git-reader-refuses-too-the-line-alone-certifies-nothing', (await tierRun({ comments: onOldHead, baseRef: QBASE })).exitCode === EXIT_REFUSED_UNREADABLE);

0 commit comments

Comments
 (0)