|
488 | 488 | * second half of the contradiction is a FIELD — which is how 17 cards |
489 | 489 | * across three repos (2026-08-23 census: 6 objectstack, 10 objectui, 1 |
490 | 490 | * cloud) sat in it with nothing reporting them. The measured origin is a |
491 | | - * state ROLLBACK that swaps the label and leaves the field: of the three |
492 | | - * rollback paths, only dead-claim reclamation ever named the assignee |
493 | | - * drop, so H8's and H19's remedy sentences now name it too (「同笔摘 |
494 | | - * assignee」). ⚠️ The field carries two meanings — dead agent claims and |
| 491 | + * state ROLLBACK that swaps the label and leaves the field. What every |
| 492 | + * rollback path owes instead is the release ACT — ONE act with TWO |
| 493 | + * halves: |
| 494 | + * 「释放是显式动作:让卡离手者同笔清 assignee + `Release:` 行(会话/因/去向);下一任重新认领。」 |
| 495 | + * — clear the assignee AND post the `Release:` line, in one write. H8's |
| 496 | + * full-delivery remedy, H19's release text and H37's dispatched-residue |
| 497 | + * remedy all say exactly that, from one constant (`RELEASE_ACT_RULE`), |
| 498 | + * because a remedy naming only the FIELD half prescribes the other |
| 499 | + * half-state — the field cleared with no record of who let the card go — |
| 500 | + * which is the shape H47 reports. |
| 501 | + * ⚠️ The field carries two meanings — dead agent claims and |
495 | 502 | * genuine human ownership — and the row deliberately does NOT try to tell |
496 | 503 | * them apart: the ruling of 2026-08-23 puts the rule FIRST and any |
497 | 504 | * true-ownership exemption in an explicit marker LATER, never the other |
@@ -1091,6 +1098,35 @@ export const CLAIM_COMMENT_MARKER = /^\s*>?\s*Claim(?:ed)?\s*:/mi; |
1091 | 1098 | */ |
1092 | 1099 | export const RELEASE_COMMENT_MARKER = /^\s*>?\s*Release\s*:/mi; |
1093 | 1100 |
|
| 1101 | +/** |
| 1102 | + * The release ACT, quoted from `.claude/skills/pm-dispatch/SKILL.md` VERBATIM |
| 1103 | + * and kept UNBROKEN on one line so it stays greppable against its source: |
| 1104 | + * |
| 1105 | + * 「释放是显式动作:让卡离手者同笔清 assignee + `Release:` 行(会话/因/去向);下一任重新认领。」 |
| 1106 | + * |
| 1107 | + * ONE constant, because every remedy sentence that tells a seat how to let a |
| 1108 | + * card go has to say the SAME thing. Three of them — H8's full-delivery |
| 1109 | + * remedy, H19's release text on both branches, H37's dispatched-residue |
| 1110 | + * remedy — carried a shorthand of this file's own coining instead: a phrase |
| 1111 | + * wearing quotation brackets that appeared in NO SKILL.md, ever (`git log -S` |
| 1112 | + * over that file's whole history came back empty). In this file's register the |
| 1113 | + * brackets promise that these are the protocol's OWN words, so the shorthand |
| 1114 | + * was not a spelling nit — it made a promise the source could not answer. |
| 1115 | + * |
| 1116 | + * ⚠️ It is ONE act with TWO halves — clear the assignee AND post the |
| 1117 | + * `Release:` line (session, cause, destination) — and a remedy naming only the |
| 1118 | + * field half PRESCRIBES a half-state: the field cleared, no record of who let |
| 1119 | + * the card go or why, which is the exact shape H47 exists to report. ⛔ So do |
| 1120 | + * not paraphrase this line, and do not coin a shorthand for it again. |
| 1121 | + * |
| 1122 | + * `selfTest` keeps its OWN transcription of the same line (`RELEASE_RULE_LINE`) |
| 1123 | + * rather than reading this constant: two independent copies compared against |
| 1124 | + * each other is a real check, while a pin that reads the value it pins asserts |
| 1125 | + * nothing at all. |
| 1126 | + */ |
| 1127 | +const RELEASE_ACT_RULE = |
| 1128 | + '释放是显式动作:让卡离手者同笔清 assignee + `Release:` 行(会话/因/去向);下一任重新认领。'; |
| 1129 | + |
1094 | 1130 | export function h2AssigneeNoClaimComment(issue, commentBodies) { |
1095 | 1131 | const labels = labelNames(issue); |
1096 | 1132 | const pmTracked = labels.some((l) => l === 'pm:queue' || l === 'pm:dispatched'); |
@@ -1757,7 +1793,10 @@ export function h8MergedPrStillDispatched(issue, mergedPrs, openPrs) { |
1757 | 1793 | `delivering PR ${list} is MERGED but the card still carries \`pm:dispatched\` — ` + |
1758 | 1794 | `the merge's paired write never landed. Drop \`pm:dispatched\` and re-grade the ` + |
1759 | 1795 | `remainder (re-queue, close, or block the un-delivered half) in the same stroke, ` + |
1760 | | - `and 「同笔摘 assignee」 — the landing re-label owes the ASSIGNEE DROP too. A ` + |
| 1796 | + `and let the card GO in the landed shape — 「${RELEASE_ACT_RULE}」 — ONE act with TWO ` + |
| 1797 | + `halves: clear the assignee AND post the \`Release:\` line (session, cause, destination). ` + |
| 1798 | + `Dropping the field alone leaves no record of who let the card go, which is the half-state ` + |
| 1799 | + `H47 reports. A ` + |
1761 | 1800 | `re-graded card that keeps the finished dev's assignee lands straight in H24's ` + |
1762 | 1801 | `two-views contradiction (\`pm:queue\` + assigned = dispatchable to the queue view, ` + |
1763 | 1802 | `taken to the claim rule), which is how 17 cards across three repos got stuck where ` + |
@@ -3468,7 +3507,10 @@ export function h19BlockOutlivedBlocker(issue, resolutions) { |
3468 | 3507 | 'to release when the card carries a MERGED PR newer than that conversion comment (the card moved on ' + |
3469 | 3508 | 'after the condition was written, so the cited fact can be true and no longer current). This row ' + |
3470 | 3509 | 'surfaces the candidate; the unlock sweep releases it — ⛔ never a label written from this script. ' + |
3471 | | - 'When that release does happen, its paired write includes 「同笔摘 assignee」: a card returned to ' + |
| 3510 | + `When that release does happen it is the landed ACT — 「${RELEASE_ACT_RULE}」 — ONE write with ` + |
| 3511 | + 'TWO halves: it clears the assignee AND carries the `Release:` line (session, cause, destination). ' + |
| 3512 | + 'Dropping the field alone leaves no record of who let the card go, the half-state H47 reports. ' + |
| 3513 | + 'A card returned to ' + |
3472 | 3514 | '`pm:queue` still carrying the assignee of the seat that parked it is dispatchable to the queue ' + |
3473 | 3515 | 'view and taken to the claim rule at the same time (H24), which is the state the unlock scan was ' + |
3474 | 3516 | 'measured leaving behind — ⚠️ agent identity only, a HUMAN assignment is ⛔ never cleared by an agent.'; |
@@ -6948,8 +6990,11 @@ export function h37FamilyMemberDrift(folds, openByNumber, closedByNumber) { |
6948 | 6990 | 'That is the same non-atomic family write failing in the MIRROR direction, and it reads to ' + |
6949 | 6991 | 'every per-card predicate as an ordinary in-flight card. Patrol input, NOT a verdict — this ' + |
6950 | 6992 | 'member may still have work of its own outstanding. Remedy: clear the residue in the write ' + |
6951 | | - 'that cleared the head (drop `pm:dispatched` and the assignee together — H24\'s ' + |
6952 | | - '「同笔摘 assignee」), or re-dispatch it on its own card and its own branch if work remains. ' + |
| 6993 | + 'that cleared the head, in the landed shape — letting a card go is ONE act with TWO ' + |
| 6994 | + `halves, 「${RELEASE_ACT_RULE}」 — so drop \`pm:dispatched\`, clear the assignee AND ` + |
| 6995 | + 'post the `Release:` line (session, cause, destination) together; a kept assignee lands ' + |
| 6996 | + 'in H24, a cleared field with no line in H47. Or re-dispatch it on its own card and its ' + |
| 6997 | + 'own branch if work remains. ' + |
6953 | 6998 | `Claimants seen on that branch: ${namedMembers(entry?.claimants ?? [])}.`, |
6954 | 6999 | ]); |
6955 | 7000 | } |
@@ -13712,6 +13757,12 @@ function selfTest() { |
13712 | 13757 | // holds is exactly the drift a shared constant cannot express. |
13713 | 13758 | const RELEASE_RULE_LINE = |
13714 | 13759 | '释放是显式动作:让卡离手者同笔清 assignee + `Release:` 行(会话/因/去向);下一任重新认领。'; |
| 13760 | + // The retired coinage, ASSEMBLED rather than spelled. The measure of this |
| 13761 | + // repair is a `git grep -c` for that phrase over this file reading ZERO, so a |
| 13762 | + // literal needle in the negative cases below would be the single hit that |
| 13763 | + // defeats the check it exists to protect. Joining the halves keeps both the |
| 13764 | + // case and the grep honest. |
| 13765 | + const RETIRED_ASSIGNEE_COINAGE = ['同笔摘', 'assignee'].join(' '); |
13715 | 13766 |
|
13716 | 13767 | // -- Row-text wrappers: every message assertion goes through one ----------- |
13717 | 13768 | // |
@@ -17043,19 +17094,36 @@ function selfTest() { |
17043 | 17094 | t('H24 adjacency: H3 is silent (only ONE label is present)', h3QueueAndDispatched(queued(['pm:queue'], ['os-elon'])), false); |
17044 | 17095 | t('H24 adjacency: H2 is silent when the claim comment is complete', h2AssigneeNoClaimComment(queued(['pm:queue'], ['os-elon']), ['Claim: PM loop round 1\nSession: session_x']), false); |
17045 | 17096 |
|
17046 | | - // -- The paired-write remedy texts (#11196 fix 2) --------------------------- |
| 17097 | + // -- The paired-write remedy texts (#11196 fix 2, re-pointed) --------------- |
17047 | 17098 | // H8's landing re-label and H19's unlock scan are the two rollback paths that |
17048 | | - // never named the assignee drop; both sentences name it now, and the |
17049 | | - // half-delivered branch must NOT (there the label and the claim are CORRECT). |
| 17099 | + // never named the release act; both sentences name it now — and they name it |
| 17100 | + // in the spelling that LANDED, quoting the rule line UNBROKEN so the quote |
| 17101 | + // stays checkable against SKILL.md, and naming the SECOND half (the |
| 17102 | + // `Release:` line) that a bare field drop omits. The half-delivered branch |
| 17103 | + // must NOT prescribe it (there the label and the claim are CORRECT). |
| 17104 | + // |
| 17105 | + // Every re-pointed sentence also carries a negative case for the retired |
| 17106 | + // coinage: a re-pointing a later edit can silently undo is not a repair, and |
| 17107 | + // the whole-file grep that measures this one runs nowhere but in a reviewer's |
| 17108 | + // shell. |
17050 | 17109 | const pairedMerged = [{ number: 900, merged_at: '2026-08-22T10:00:00Z', body: 'Fixes #10638', head: { ref: 'x' } }]; |
17051 | 17110 | const pairedOpenHalf = [{ number: 901, merged_at: null, draft: true, body: 'Part of #10638', head: { ref: 'y' } }]; |
17052 | | - t('H8: the full-delivery remedy names 同笔摘 assignee', h8row(queued(['pm:dispatched'], ['os-elon']), pairedMerged, []).includes('同笔摘 assignee'), true); |
| 17111 | + t('H8: the full-delivery remedy names the release act in the spelling that LANDED', h8row(queued(['pm:dispatched'], ['os-elon']), pairedMerged, []).includes('同笔清 assignee'), true); |
| 17112 | + t('H8: …quoting the rule line unbroken, so the quote stays checkable against SKILL.md', h8row(queued(['pm:dispatched'], ['os-elon']), pairedMerged, []).includes(RELEASE_RULE_LINE), true); |
| 17113 | + t('H8: …and names the SECOND half, the `Release:` line a bare field drop omits', h8row(queued(['pm:dispatched'], ['os-elon']), pairedMerged, []).includes('`Release:` line'), true); |
| 17114 | + t('H8: …and the retired coinage does not come back', h8row(queued(['pm:dispatched'], ['os-elon']), pairedMerged, []).includes(RETIRED_ASSIGNEE_COINAGE), false); |
17053 | 17115 | t('H8: …and points at H24 as the state it prevents', h8row(queued(['pm:dispatched'], ['os-elon']), pairedMerged, []).includes('H24'), true); |
17054 | 17116 | t('H8: …and keeps the human-assignment refusal', h8row(queued(['pm:dispatched'], ['os-elon']), pairedMerged, []).includes('never cleared by an agent'), true); |
17055 | | - t('H8: the HALF-delivered branch prescribes no assignee drop', h8row(queued(['pm:dispatched'], ['os-elon']), pairedMerged, pairedOpenHalf).includes('同笔摘 assignee'), false); |
| 17117 | + t('H8: the HALF-delivered branch prescribes no assignee drop', h8row(queued(['pm:dispatched'], ['os-elon']), pairedMerged, pairedOpenHalf).includes('同笔清 assignee'), false); |
| 17118 | + t('H8: …nor the release rule line — nothing is being let go there', h8row(queued(['pm:dispatched'], ['os-elon']), pairedMerged, pairedOpenHalf).includes(RELEASE_RULE_LINE), false); |
17056 | 17119 | t('H8: …and still says the label is correct there', h8row(queued(['pm:dispatched'], ['os-elon']), pairedMerged, pairedOpenHalf).includes('must NOT be dropped'), true); |
17057 | | - t('H19: the release text names 同笔摘 assignee', h19row(queued(['pm:blocked']), [{ key: 'objectstack-ai/objectstack#2', number: 2, local: true, state: 'closed' }]).includes('同笔摘 assignee'), true); |
17058 | | - t('H19: …on the unresolved branch too (one release contract, one sentence)', h19row(queued(['pm:blocked']), [{ key: 'objectstack-ai/cloud#2', number: 2, local: false, state: 'unresolved', detail: 'HTTP 404' }]).includes('同笔摘 assignee'), true); |
| 17120 | + t('H19: the release text names the release act in the spelling that LANDED', h19row(queued(['pm:blocked']), [{ key: 'objectstack-ai/objectstack#2', number: 2, local: true, state: 'closed' }]).includes('同笔清 assignee'), true); |
| 17121 | + t('H19: …quoting the rule line unbroken', h19row(queued(['pm:blocked']), [{ key: 'objectstack-ai/objectstack#2', number: 2, local: true, state: 'closed' }]).includes(RELEASE_RULE_LINE), true); |
| 17122 | + t('H19: …and names the SECOND half, the `Release:` line', h19row(queued(['pm:blocked']), [{ key: 'objectstack-ai/objectstack#2', number: 2, local: true, state: 'closed' }]).includes('`Release:` line'), true); |
| 17123 | + t('H19: …and the retired coinage does not come back', h19row(queued(['pm:blocked']), [{ key: 'objectstack-ai/objectstack#2', number: 2, local: true, state: 'closed' }]).includes(RETIRED_ASSIGNEE_COINAGE), false); |
| 17124 | + t('H19: …on the unresolved branch too (one release contract, one sentence)', h19row(queued(['pm:blocked']), [{ key: 'objectstack-ai/cloud#2', number: 2, local: false, state: 'unresolved', detail: 'HTTP 404' }]).includes('同笔清 assignee'), true); |
| 17125 | + t('H19: …with the rule line unbroken there too', h19row(queued(['pm:blocked']), [{ key: 'objectstack-ai/cloud#2', number: 2, local: false, state: 'unresolved', detail: 'HTTP 404' }]).includes(RELEASE_RULE_LINE), true); |
| 17126 | + t('H19: …and the retired coinage gone from that branch as well', h19row(queued(['pm:blocked']), [{ key: 'objectstack-ai/cloud#2', number: 2, local: false, state: 'unresolved', detail: 'HTTP 404' }]).includes(RETIRED_ASSIGNEE_COINAGE), false); |
17059 | 17127 |
|
17060 | 17128 | // -- H25 + the `pm:awaiting-maintainer` vocabulary (#11196 fix 5) ----------- |
17061 | 17129 | t('the ruled spelling is pm:-prefixed', AWAITING_MAINTAINER_LABEL, 'pm:awaiting-maintainer'); |
@@ -18547,6 +18615,12 @@ function selfTest() { |
18547 | 18615 | t('H37 mirror: …keyed to the member carrying the residue', h37key1(...residue37), 11679); |
18548 | 18616 | t('H37 mirror: …and it says the head CLOSED', h37row1(...residue37).includes('CLOSED'), true); |
18549 | 18617 | t('H37 mirror: …naming it as the same write failing the other way', h37row1(...residue37).includes('MIRROR direction'), true); |
| 18618 | + // The third re-pointed remedy sentence: this row prescribes letting a card |
| 18619 | + // go, so it owes the same landed act, both halves, as H8's and H19's do. |
| 18620 | + t('H37 mirror: …and the remedy names the release act in the spelling that LANDED', h37row1(...residue37).includes('同笔清 assignee'), true); |
| 18621 | + t('H37 mirror: …quoting the rule line unbroken', h37row1(...residue37).includes(RELEASE_RULE_LINE), true); |
| 18622 | + t('H37 mirror: …and names the SECOND half, the `Release:` line', h37row1(...residue37).includes('`Release:` line'), true); |
| 18623 | + t('H37 mirror: …and the retired coinage does not come back', h37row1(...residue37).includes(RETIRED_ASSIGNEE_COINAGE), false); |
18550 | 18624 | const released37 = [ |
18551 | 18625 | h37FoldBranches([h37claim(11679, FOLD_37)]), |
18552 | 18626 | h37map(h37card(11679, ['pm:dispatched']), h37card(11678, ['pm:queue'])), |
|
0 commit comments