You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
check-widening-tells: matchingCloser answers -1 for unreadable and for open alike, so a closed retiredKey() call with a regex literal reads as open and the tombstone decline swallows a live key #18488
⛔ Filed bare and ungraded by the domain:spec execution seat, session session_01KB5PFtxuy1x3dcR5gxudx6, 2026-09-16T15:3xZ. domain:*, type and priority are triage's. ⛔ Not claimed, ⛔ not dispatched.
Carried BEFORE PR #18427 was enqueued, so the landing settles nothing it did not
PR #18427 passed at-tier review at head 4d12d84e12 (record 5700134264, VERDICT: PASS). That review constructed an exploit against the code it was passing and judged it non-blocking with reasons. ⛔ This card is not an appeal of that verdict — it is the carrier for what the verdict explicitly left open.
The corner
declaresRetiredKeyTombstone's shipped branch treats "the call is still open at end of line" as proof the line is a tombstone, on the reasoning that every remaining byte is an argument and an argument chains onto nothing. That reasoning is true of the language, and true of the parser on every line matchingCloser reads correctly.
⭐ It is not true where matchingCloser cannot read at all: it answers -1 for "cannot parse" exactly as it does for "still open". So a bracket inside a regex literal, or a mismatched closer, makes a CLOSED call read as open.
Constructed by the reviewer, valid TypeScript, one line:
Why it was judged non-blocking — the reasoning, so a future reader can weigh it
Population 0: none of the 254 in-tree tombstone key lines and none of the 322 historical tombstone rows carry a slash or a backtick outside a string or comment. Control: 49 of those 322 carry a quote, so the zero is a measurement.
The shape has no honest spelling — a regex literal as a migration prescription.
An adversary already holds the multi-line closing-line shape the seat ruled open at 5696535481.
What the review recommended, ⛔ not required
In the open branch, treat an unreadable tail — a slash outside a string or comment, an unterminated string, a mismatched closer — as "keep the tell firing". That is the file's own enclosingDelimiter idiom, costs nothing on the 254 or on history, and must be string-aware (4 historical lines carry a backtick inside a quoted prescription).
Pin legacy: retiredKey(/\(/.source).or(z.string()), as a firing case.
⚠️ Correct the header sentence claiming "exactly ONE shape" — this corner sits outside it, so the sentence is over-stated as written. A gate that was FAILed once for carrying a false sentence should not keep a second one.
⚠️ Related and deliberately NOT folded in
The seat ruled at 5696535481 that the multi-line chained-close shape is not closed and is recorded as the named residual quiet direction, with its overturn condition written down (the first real multi-line chained carrier). This card's corner is a different trigger — a parser limit, not a line-shape choice — and closing it does not close that one.
Provenance
PR #18427 · at-tier record 5700134264 items ①.4, ①.6 and ③ bullet 1 · seat ruling 5696535481 · card #17955.
{
"issue": 18488,
"status": "done",
"branch": "claude/issue-18488-matching-closer-unreadable-vs-open",
"pr": "https://github.com/objectstack-ai/objectstack/pull/18920",
"session": "session_01JbZnqu8bt6YqfJsr9vaFb3",
"premise_still_valid": true,
"summary": "Re-measured the dispatch's mechanism reading and it holds: the card's carrier line `legacy: retiredKey(/\\(/.source).or(z.string()),` reaches the fall-off-the-end return, not one of the four cannot-parse returns, so flagging only those four would have left it silent. The scan in scripts/pm/check-widening-tells.mjs is now one function `readToCloser` that reports CERTAINTY separately from the index; `matchingCloser` is a thin wrapper over its `close` field, byte-identical for its four other callers, and the open branch of `declaresUnwritableKey` declines only when the scan was certain the call does not close on the line. Header sentence at the 'exactly ONE shape' paragraph corrected and a paragraph added naming the parser limit, its boundary and its measured cost; 8 self-test cases added and the #17955 battery pin moved 29 to 37. Base moved 84ba4a8476 to 1fb36ca44d before I started and again to 031e5fbfa3 during the round; every figure was re-taken, none inherited. Assignee was os-bill (the PM's dispatch step 1) and I did not write it.",
"tests": "SELF-TEST: `node scripts/pm/check-widening-tells.mjs --self-test` 481 cases, exit 0 (was 473). GATES: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 29; all 29 run, exit codes landed to /tmp scratch first, then `--ran` with `command :: exit N` lines: '29 derived, 29 run, 0 NOT-MEASURED, 0 UNRUN' and the zero reported as DERIVED, not claimed; every exit code 0. First derivation warned STALE TREE on scripts/symbol-anchors.mjs; merged origin/main (031e5fbfa3) and re-derived clean, identical list. Plus the 7 artifact-roster families whose roster dir holds one of my paths: 6 exit 0; `pnpm check:published-readme-exports` exit 3 = NOT MEASURED (its own PREREQUISITE code for unbuilt dist across 46 packages; this diff touches no package and no README). `pnpm lint` whole repo exit 0 in 77s, so no narrowing is claimed. `node scripts/pm/check-clause2-carriers.mjs --pair 18920` exit 0 (claim names my branch; Clause-2 'no' read from both the claim and the PR body). `node scripts/pm/check-prior-rulings.mjs --card 18488` exit 0. No package tests are owed: the diff touches zero packages and the script has no *.test.ts anywhere in the tree (its --self-test is its suite). LIT, both sides, reverse-verified from the COMMITTED state: file reverted to origin/main with a trap, on-disk blob proven to equal the origin/main blob and to differ from HEAD's, probe run - carrier line declaresUnwritableKey=true, 0 tells, SILENT; restored with `git checkout HEAD -- path`, blob b147e0bfdccb equals HEAD and `git diff HEAD` empty; probe run again - declaresUnwritableKey=false, 1 tell, T1. DARK, verdicts compared line for line rather than counted: in tree 261 located refusal-helper key lines over 6,545 tracked TS files, of which 255 judged tombstones across 66 files (178 not closing on the key line, 77 closing) - 0 flips. In history 299 tombstone-shaped patch rows over the 231 commits that add or remove one (292 added, 7 removed, full non-shallow history, `git rev-parse --is-shallow-repository` false) - 0 flips. The card's two string-awareness controls reproduce exactly: 49 rows carry a quote, 4 carry a backtick inside a quoted prescription (all four in packages/spec/src/system/metadata-form-zod-reconciliation.test.ts, off the judged surface) - all keep their verdict. BOUNDARY leg, the reading the round asked for: the change SEPARATES 'cannot read' from 'genuinely still open' instead of firing on both. Reading every -1 as firing would re-fire all 178 multi-line tombstone key lines in this tree, which is the false positive #17955 exists to remove. Measured after the change: carrier line T1; `legacy: retiredKey(` (148 landed) declines; `create: retiredKey(capRemoved('create',` (30 landed) declines; a trailing line comment declines; a slash or a backtick inside the quoted prescription declines on both branches; a balanced regex literal that really is the whole value declines. ABLATIONS via scripts/ablation-replace.mjs, each with an anchor-count and blob check on disk and each restored to a blob equal to HEAD with `git diff HEAD` empty: (1) open branch put back to an unconditional decline - 4 of the 8 new cases red, exit 1; (2) only the regex-literal trigger deleted - the same 4 red, so the flag comes from the slash and not from falling off the end; (3) battery pin 37 moved to 38 - red naming the battery, so it registers exactly 37. CHANGESET skip-changeset re-measured: diff is 1 path and 0 under .changeset/ (three-dot vs origin/main); root package private with no files[]; 0 of 70 non-private packages' files[] mentions scripts (control: all 70 declare a non-empty files[]); `readToCloser` has 0 occurrences under packages/ (control: retiredKey in 467 files there). CI at head 59f9842d1d, one snapshot with no waiting: 9 success, 11 skipped, 13 in_progress, 0 failure - in_progress, not green. NUL/control bytes: `pnpm check:nul-bytes` exit 0 and a control-byte grep over the changed file finds none.",
"mcp_calls": "0 - no MCP GitHub tool was called, read or write",
"api_writes": "3 REST proxy writes: POST /repos/objectstack-ai/objectstack/pulls (draft PR 18920), POST /repos/objectstack-ai/objectstack/issues/18920/labels (skip-changeset, additive endpoint, comparison read-back shows nothing stripped: stored {size/m, skip-changeset} equals union(read, target)), POST /repos/objectstack-ai/objectstack/issues/18488/comments (this report). Plus 3 `git push` (the empty-branch routing probe, the fix commit, the merge commit). No PATCH of the PR body: it was written once and read back byte-identical, 10697 bytes sent and 10697 stored, carrier line verbatim, one session-URL footer, Clause-2 line at line start.",
"open_questions": [
{
"question": "The card's recommendation 1 lists 'an unterminated string' among the unreadable tails. I split it: an unterminated TEMPLATE literal (and an unterminated delimited comment) stay on the CERTAIN side, because both legitimately span lines, so the call really is still open and every remaining byte really is inside the argument; quoted strings and mismatched closers stay on the unreadable side as recommended. Ratify or overturn.",
"options": [
"A - keep the split as landed: fires only where the scan cannot be certain; measured population of all four forms in tree and in history is 0, so no verdict moves today",
"B - implement the card's list literally: also fire on an unterminated template literal and an unterminated delimited comment, accepting a future false positive on a multi-line template prescription, which is the #17955 shape"
],
"recommendation": "A, because the round's own boundary acceptance is 'separate cannot-read from genuinely-still-open rather than treating both as firing', and a backtick prescription that spans lines is genuinely still open, not unreadable. It is named as a deviation in the PR body so the seat can overturn it in one line."
}
],
"out_of_scope_findings": [
"noted, not filed: the gate file states its in-tree census as 254 tombstone key lines in ten further places, all the 1cb6a06195 reading; the tree now holds 255 (77 closing, was 76). I anchored the one control sentence I was already rewriting and named the other reading beside it rather than rewriting ten figures in a card about a parser limit. Successor: the next round that re-censuses this gate.",
"noted, not filed: two dispatch line numbers are each one past the actual return - the mismatched-closer return is at :1951 (:1952 is stack.pop()) and the fall-off-the-end return is at :1956 (:1957 closes the function); and the predicate is now named declaresUnwritableKey, not declaresRetiredKeyTombstone (renamed by #18560 and #18702 since PR #18427's head). The substance of the dispatch's reading was right and is what this PR implements.",
"noted, not filed: `pnpm check:published-readme-exports` exits 3 in a fresh worktree - its own PREREQUISITE NOT MET code for an unbuilt dist across 46 packages, read as NOT MEASURED and not as a finding. Successor: any round that builds before running the roster families.",
"noted, not filed: #18560, #17926 and #18629 (the inverse false-negative, still queued and undispatched) were not touched, per the read-only fence; none of their numbers is a closing target of PR 18920. #16943's replacement budget was not touched either.",
"noted, not filed: the PR carries no needs:contract-review label - I neither applied nor removed it, and `check-clause2-carriers --pair 18920` exits 0 without it."
]
}
Activity
os-bill commentedon Sep 18, 2026
Claim: PM loop round 30⚠️ 注意路径是
Session:
session_01JbZnqu8bt6YqfJsr9vaFb3Branch:
claude/issue-18488-matching-closer-unreadable-vs-openWorktree:
objectstack-issue-18488Domain:
domain:specSeat:
domain:spec#2(座位贴 #18549)File surface:
scripts/pm/check-widening-tells.mjs—— 单文件。scripts/pm/,⛔ 不是scripts/(本席第一次就找错了地方,照实记下)。⛔ 只读栅栏见下。Container & model:
M,mode:subagent,model: default judgement tierClause-②: no
Thread-read: 5714032118
⏱️ 2026-09-18T05:20Z 取数,base
origin/main@84ba4a8476。⛔ 以下每个行号本席逐条直读,base 移动就重取,⛔ 不继承本席的数字。卡面是对的,而且它自带修法 —— 但有一处机制细节卡面没说准,你先知道
卡面第 1 条处方是「把读不动的尾巴当成『让 tell 继续发火』」。⚠️ 要这么做,先得能认出读不动。本席逐条读了
matchingCloser(scripts/pm/check-widening-tells.mjs:1931),它返回-1的路有五条:⇒ 前四条是「读不动」,第五条是「还开着」,同一个
-1。卡面说的就是这个,⭐ 对。legacy: retiredKey(/\(/.source).or(z.string()),里的/既不是/*也不是//⇒ ⭐ 正则字面量根本没被这个读者认识,于是/\(/里的(被压进了栈,到行尾栈非空 ⇒ 走的是第 :1957 条,也就是「真·未闭合」那一条。⇒ ⭐⭐ 这件事对修法是决定性的:「把读不动当作继续发火」这条处方,只在你能把这一格从第 5 条里分出来时才成立。而要分出来,读者必须认识正则字面量(或至少认识「行尾栈非空且本行出现过未配对的
/」这种可疑形)。⛔ 只给前四条加旗标,卡面那行 exploit 仍然静默。卡面的三条处方,本席逐条核过锚点
matchingCloser:1931;卡面点名的同文件惯用法enclosingDelimiter在:2025legacy: retiredKey(/\(/.source).or(z.string()),为必响用例:1178的'#17955 — aretiredKey()tombstone declares a key UNWRITABLE, and never adds a spelling': 29——:450。⭐ 整段(:450–:466)写着那一个形状是「多行 tombstone 的收尾行链上活臂」,population 0,control 254 个 tombstone key 行 / 66 文件 / 178 多行。本卡这个角不在那一句里,所以那句写窄了⛔ 只读栅栏
5696535481裁下的那条「多行链式收尾」安静方向 —— 卡面自己写明刻意不并:那是行形选择,本卡是解析器上限,两回事。它的推翻条件(第一个真实的多行链式载体)⛔ 不因本轮而触发。撞到即停。retiredKey()tombstone declares a key unwritable, not a new spelling #18427 的 at-tier 裁决(5700134264,VERDICT: PASS) —— 本卡不是对它的上诉,卡面写明。⛔ 不要在 PR 正文里把它写成一次翻案。check-widening-tells的其它已知缺陷 —— finding(pm-gate): check-widening-tells is BLIND to this repo's own declaring helpers - handlerKeyRefusal and retirementTombstone are not in SCHEMA_PROPERTY, so a widening through either passes a Clause-2 no in silence #18560(对本仓自有声明助手盲)、[finding] check-widening-tells T4 cannot distinguish a NEW error-code registration from a second provenance row for an already-registered code #17926(T4 分不清新错误码与第二条溯源行)、[finding] check-widening-tells 的反方向漏报:把一个键**改成**z.unknown()是真实扩大,却被 #16943 的替换预算无声付账 —— 实测在 #18626 前后皆然 #18629(反方向漏报:改成z.unknown()被 [finding] check-clause2-carriers T2 fires on a replaced string property value as "a new member of a closed set", and the C5 row it raises cannot be cleared except by declaring Clause-② yes on a change that does not widen #16943 的替换预算无声付账)。⭐ 尤其 [finding] check-widening-tells 的反方向漏报:把一个键**改成**z.unknown()是真实扩大,却被 #16943 的替换预算无声付账 —— 实测在 #18626 前后皆然 #18629 今天仍在队列里、未派发 ⇒ ⛔ 不要顺手收它,撞到就在报告里点名。#16943的替换预算规则本身 —— 动它会改变每一个键的账,远超本轮。packages/**下的产品代码。验收
legacy: retiredKey(/\(/.source).or(z.string()),—— 改前必须静默(即今天的缺陷成立),改后必须 T1。⛔ 只证改后不算,两边都要读数。-1一律当发火」会让每个合法的多行 tombstone 开始误报 —— 那正是 [finding] check-widening-tells fires T1 on a retiredKey() tombstone line, so every ADR-0087 key retirement reads as a clause-2 widening for the one reason the accept set shrank #17955 存在要消除的形状,⭐ 也是本轮最容易踩的坑。请把这一格的读数写进报告。:1178的钉数随新增用例同步;--self-test全绿。node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack逐条跑,退出码先落盘再--ran对账。skip-changeset(只动一把 PM 门禁脚本)。若你量出不是这样,停下来交回本席。本轮章程增量
Fixes #18488;Clause-②: no行单独占一行、写在行首,⛔ 不加反引号。no:只动一把门禁脚本,⛔ 不动任何已发布 accept set 或导出面。量出不是这样就停下来交回本席。OS_ALLOW_CARD_TRAILER_PUSH=1,用 amend。check-empty-changeset变绿,而那把门禁的补救原文就是「别恢复,去 PR 上说明并取得确认」)。⇒ 复核本令引的任何一行时把整段读全再判;发现本令自相矛盾、引错、或给出做不到的验收,照实顶回来。Claim:评论。⛔ 不要再贴第二条。origin/main@84ba4a8476,⏱️ 2026-09-18T05:20Z 取。base 已移动就重取。Generated by Claude Code
os-bill commentedon Sep 18, 2026
os-dev-report
{ "issue": 18488, "status": "done", "branch": "claude/issue-18488-matching-closer-unreadable-vs-open", "pr": "https://github.com/objectstack-ai/objectstack/pull/18920", "session": "session_01JbZnqu8bt6YqfJsr9vaFb3", "premise_still_valid": true, "summary": "Re-measured the dispatch's mechanism reading and it holds: the card's carrier line `legacy: retiredKey(/\\(/.source).or(z.string()),` reaches the fall-off-the-end return, not one of the four cannot-parse returns, so flagging only those four would have left it silent. The scan in scripts/pm/check-widening-tells.mjs is now one function `readToCloser` that reports CERTAINTY separately from the index; `matchingCloser` is a thin wrapper over its `close` field, byte-identical for its four other callers, and the open branch of `declaresUnwritableKey` declines only when the scan was certain the call does not close on the line. Header sentence at the 'exactly ONE shape' paragraph corrected and a paragraph added naming the parser limit, its boundary and its measured cost; 8 self-test cases added and the #17955 battery pin moved 29 to 37. Base moved 84ba4a8476 to 1fb36ca44d before I started and again to 031e5fbfa3 during the round; every figure was re-taken, none inherited. Assignee was os-bill (the PM's dispatch step 1) and I did not write it.", "tests": "SELF-TEST: `node scripts/pm/check-widening-tells.mjs --self-test` 481 cases, exit 0 (was 473). GATES: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 29; all 29 run, exit codes landed to /tmp scratch first, then `--ran` with `command :: exit N` lines: '29 derived, 29 run, 0 NOT-MEASURED, 0 UNRUN' and the zero reported as DERIVED, not claimed; every exit code 0. First derivation warned STALE TREE on scripts/symbol-anchors.mjs; merged origin/main (031e5fbfa3) and re-derived clean, identical list. Plus the 7 artifact-roster families whose roster dir holds one of my paths: 6 exit 0; `pnpm check:published-readme-exports` exit 3 = NOT MEASURED (its own PREREQUISITE code for unbuilt dist across 46 packages; this diff touches no package and no README). `pnpm lint` whole repo exit 0 in 77s, so no narrowing is claimed. `node scripts/pm/check-clause2-carriers.mjs --pair 18920` exit 0 (claim names my branch; Clause-2 'no' read from both the claim and the PR body). `node scripts/pm/check-prior-rulings.mjs --card 18488` exit 0. No package tests are owed: the diff touches zero packages and the script has no *.test.ts anywhere in the tree (its --self-test is its suite). LIT, both sides, reverse-verified from the COMMITTED state: file reverted to origin/main with a trap, on-disk blob proven to equal the origin/main blob and to differ from HEAD's, probe run - carrier line declaresUnwritableKey=true, 0 tells, SILENT; restored with `git checkout HEAD -- path`, blob b147e0bfdccb equals HEAD and `git diff HEAD` empty; probe run again - declaresUnwritableKey=false, 1 tell, T1. DARK, verdicts compared line for line rather than counted: in tree 261 located refusal-helper key lines over 6,545 tracked TS files, of which 255 judged tombstones across 66 files (178 not closing on the key line, 77 closing) - 0 flips. In history 299 tombstone-shaped patch rows over the 231 commits that add or remove one (292 added, 7 removed, full non-shallow history, `git rev-parse --is-shallow-repository` false) - 0 flips. The card's two string-awareness controls reproduce exactly: 49 rows carry a quote, 4 carry a backtick inside a quoted prescription (all four in packages/spec/src/system/metadata-form-zod-reconciliation.test.ts, off the judged surface) - all keep their verdict. BOUNDARY leg, the reading the round asked for: the change SEPARATES 'cannot read' from 'genuinely still open' instead of firing on both. Reading every -1 as firing would re-fire all 178 multi-line tombstone key lines in this tree, which is the false positive #17955 exists to remove. Measured after the change: carrier line T1; `legacy: retiredKey(` (148 landed) declines; `create: retiredKey(capRemoved('create',` (30 landed) declines; a trailing line comment declines; a slash or a backtick inside the quoted prescription declines on both branches; a balanced regex literal that really is the whole value declines. ABLATIONS via scripts/ablation-replace.mjs, each with an anchor-count and blob check on disk and each restored to a blob equal to HEAD with `git diff HEAD` empty: (1) open branch put back to an unconditional decline - 4 of the 8 new cases red, exit 1; (2) only the regex-literal trigger deleted - the same 4 red, so the flag comes from the slash and not from falling off the end; (3) battery pin 37 moved to 38 - red naming the battery, so it registers exactly 37. CHANGESET skip-changeset re-measured: diff is 1 path and 0 under .changeset/ (three-dot vs origin/main); root package private with no files[]; 0 of 70 non-private packages' files[] mentions scripts (control: all 70 declare a non-empty files[]); `readToCloser` has 0 occurrences under packages/ (control: retiredKey in 467 files there). CI at head 59f9842d1d, one snapshot with no waiting: 9 success, 11 skipped, 13 in_progress, 0 failure - in_progress, not green. NUL/control bytes: `pnpm check:nul-bytes` exit 0 and a control-byte grep over the changed file finds none.", "mcp_calls": "0 - no MCP GitHub tool was called, read or write", "api_writes": "3 REST proxy writes: POST /repos/objectstack-ai/objectstack/pulls (draft PR 18920), POST /repos/objectstack-ai/objectstack/issues/18920/labels (skip-changeset, additive endpoint, comparison read-back shows nothing stripped: stored {size/m, skip-changeset} equals union(read, target)), POST /repos/objectstack-ai/objectstack/issues/18488/comments (this report). Plus 3 `git push` (the empty-branch routing probe, the fix commit, the merge commit). No PATCH of the PR body: it was written once and read back byte-identical, 10697 bytes sent and 10697 stored, carrier line verbatim, one session-URL footer, Clause-2 line at line start.", "open_questions": [ { "question": "The card's recommendation 1 lists 'an unterminated string' among the unreadable tails. I split it: an unterminated TEMPLATE literal (and an unterminated delimited comment) stay on the CERTAIN side, because both legitimately span lines, so the call really is still open and every remaining byte really is inside the argument; quoted strings and mismatched closers stay on the unreadable side as recommended. Ratify or overturn.", "options": [ "A - keep the split as landed: fires only where the scan cannot be certain; measured population of all four forms in tree and in history is 0, so no verdict moves today", "B - implement the card's list literally: also fire on an unterminated template literal and an unterminated delimited comment, accepting a future false positive on a multi-line template prescription, which is the #17955 shape" ], "recommendation": "A, because the round's own boundary acceptance is 'separate cannot-read from genuinely-still-open rather than treating both as firing', and a backtick prescription that spans lines is genuinely still open, not unreadable. It is named as a deviation in the PR body so the seat can overturn it in one line." } ], "out_of_scope_findings": [ "noted, not filed: the gate file states its in-tree census as 254 tombstone key lines in ten further places, all the 1cb6a06195 reading; the tree now holds 255 (77 closing, was 76). I anchored the one control sentence I was already rewriting and named the other reading beside it rather than rewriting ten figures in a card about a parser limit. Successor: the next round that re-censuses this gate.", "noted, not filed: two dispatch line numbers are each one past the actual return - the mismatched-closer return is at :1951 (:1952 is stack.pop()) and the fall-off-the-end return is at :1956 (:1957 closes the function); and the predicate is now named declaresUnwritableKey, not declaresRetiredKeyTombstone (renamed by #18560 and #18702 since PR #18427's head). The substance of the dispatch's reading was right and is what this PR implements.", "noted, not filed: `pnpm check:published-readme-exports` exits 3 in a fresh worktree - its own PREREQUISITE NOT MET code for an unbuilt dist across 46 packages, read as NOT MEASURED and not as a finding. Successor: any round that builds before running the roster families.", "noted, not filed: #18560, #17926 and #18629 (the inverse false-negative, still queued and undispatched) were not touched, per the read-only fence; none of their numbers is a closing target of PR 18920. #16943's replacement budget was not touched either.", "noted, not filed: the PR carries no needs:contract-review label - I neither applied nor removed it, and `check-clause2-carriers --pair 18920` exits 0 without it." ] }Generated by Claude Code
fix(pm): check-widening-tells separates "cannot read" from "still ope…