Skip to content

docs(agents): say how a return-delivered durability seam is declared - #19214

Merged
os-zhuang merged 4 commits into
mainfrom
claude/issue-16233-agents-return-propagating-seams
Sep 20, 2026
Merged

os-zhuang merged 4 commits into
mainfrom
claude/issue-16233-agents-return-propagating-seams

Conversation

@os-try-charles

@os-try-charles os-try-charles commented Sep 19, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #16233

What changed

One paragraph of AGENTS.md — the "Degradation log levels" block that used to end at lines
938-943 on 24d622b94.

The old text told the author of a durability seam to "declare how it delivers instead —
FAILURE_PROPAGATION_CALLEES (repo-wide names) or the function-scoped
FAILURE_PROPAGATION_SITES". Both maps key on the name of a callee the catch reaches
(scripts/check-durability-degradation-log-level.mjs:505, :513), so a catch that delivers
its failure by returning an outcome object has nothing to put in either one — and the only
two remaining ways to green that gate are the two the gate's own header rejects: a baseline
entry for correct code, or a bolted-on logger.error.

The rewritten paragraph splits the declaration rule by how the failure leaves the catch:

  • delivered by CALLING something → name that callee, as before (both maps, unchanged);
  • delivered by RETURNING an outcome object → no callee to declare; never added to
    DURABILITY_CRITICAL_CALLEES or either FAILURE_PROPAGATION_* map, never baselined, never
    given a logger.error to satisfy the checker; pinned instead by its own file's test
    asserting the returned failure outcome (the failed-receipt case is the reference shape),
    with its population read from scripts/measure-return-propagating-durability-seams.mjs.

The invariant is untouched: silent data loss must be loud. The gate is untouched — its fourth
honest limitation (scripts/check-durability-degradation-log-level.mjs:79-99) already records
this boundary and already names the census script, so no line of that script needed to move.

Evidence

Census re-measured on this branch, reproducing the ruling's numbers exactly:

node scripts/measure-return-propagating-durability-seams.mjs --sites   :: exit 0
  2620 non-test source files · 2968 try/catch · 250 guarding a declared durable call
  · 21 returning a constructed failure object on every path
  MEMBERS 12   [1] gate sees today 0 · [2] write-shaped 4 · [3] durable on the merits 8
  ADJACENT 5 · EXCLUDED 4

The instrument this paragraph governs:

pnpm check:durability-log-level   :: exit 0
  ✓ durability-degradation log levels: 36 durability-critical catch seam(s), all loud,
    rethrowing or propagating to the caller (4 propagating, declared)
  ✓ read-seam invention: 68 read seam(s), none invents an unreported answer

node scripts/pm/dispatch-gates.mjs --commands derives 14 families for this change set; all 14
were run and reconciled with --ran (14 derived, 14 run, 0 UNRUN). 13 green:

node scripts/check-closing-keyword-parity.mjs              :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test  :: exit 0
node scripts/check-comment-mask-corpus.mjs                 :: exit 0
pnpm check:agent-test-spelling                             :: exit 0
pnpm check:docs-audit-scope                                :: exit 0
pnpm check:driver-memory-census                            :: exit 0
pnpm check:nul-bytes                                       :: exit 0
pnpm check:pm-governed-merges                              :: exit 0
pnpm check:pm-governed-prose                               :: exit 0
pnpm check:pm-skill-id-lint                                :: exit 0
pnpm check:refd-timer-probe                                :: exit 0
pnpm check:required-contexts                               :: exit 0
pnpm check:watch-hint-literal                              :: exit 0

✅ The line ceiling — ruled, raised, and green

The maintainer ruled the budget. His words, verbatim and untranslated:

  1. docs(agents): say how a return-delivered durability seam is declared #19214 / 卡 check:durability-log-level cannot represent a seam that propagates a durability failure by RETURNING an outcome object — its propagation lists are keyed on callee names #16233 —— AGENTS.md 行预算, A 批 +10(1099→1109)

Given as a live instruction to session_019srGWGCBBCBHqcDoRZpQRh and recorded on card #16233 as
comment 5750261694; it rules the line budget that the standing letter-C ruling 5716260390
left unruled when it named the text. ⇒ scripts/pm/check-skill-line-ratchet.mjs now carries
['AGENTS.md', 1109], with the raise recorded above the entry in that file's own ruled-raise
convention — measured cost, the re-measured rewrap headroom with its widths named, the ruling
verbatim, and the standing CROSS_FILE_MOVES sentence.

pnpm check:pm-skill-ratchet   :: exit 1  →  exit 0
  ✓ check-skill-line-ratchet: AGENTS.md is 1109 lines (ceiling 1109; headroom 0).
  ✓ check-skill-line-ratchet self-test: 157 cases pass
    — including “every live ruled-raise record quotes its ruling, dates it, and names a
      positive line count”, which is the gate validating this very comment block.

⚠️ Two corrections to the numbers this body carried before, both measured rather than
inherited:

  • The rewritten paragraph measures 21 / 21 / 20 lines at 86 / 88 / 90 bytes — not
    21 / 20 / 20 — and as landed it has four lines above 88 bytes (89, 90, 89, 89; max 90), so it
    sits at ≤90 B, not at 88 B. The conclusion is unchanged and if anything stronger: at 88 B a
    rewrap would cost a line.
  • “0 lossless rewrap headroom at three widths” holds only for widths ≤90 B. Re-measured with
    the gate's own wrapLine, the paragraph reflows to 18 lines at 100 B and 15 at 120 B (the whole
    section 88 → 69 at 120 B). ⇒ the honest justification for the raise is not that no width
    saves lines, it is that re-wrap is not legal currency here: this file's 2026-08-17 rule
    refuses re-wrap funding, and the 120-byte cap documents itself as codifying the corpus's
    existing ≤91-byte ASCII wrap rather than mandating a reflow to it.

Clause-②: no — no published accept set, public export, enum member, error code or authorable
key moves; the surface is governance prose plus one shrink-only ratchet data value.

Landing is still the maintainer's hand

node scripts/pm/check-governed-merges.mjs --pr 19214 → exit 3, GOVERNED — a human merge is
the review record for this PR (#9495 regime)
, AGENTS.md ×1, landing tier H; the second path,
scripts/pm/check-skill-line-ratchet.mjs, is not on the register and adds no governed path.
⇒ This PR stays draft: ⛔ no ready flip, ⛔ no enqueue, ⛔ no auto-merge by any seat. A line
budget is a budget, not a landing permission.

Verified independently by the accepting seat on head aa2e7408d (⛔ not taken from the dev's
report): two files vs origin/main — AGENTS.md +20/−10, byte-identical to the previous round, so
the ruled paragraph was not shrunk to buy lines — and check-skill-line-ratchet.mjs +29/−1,
one data value plus the comment block. origin/main was merged in twice (32d6e4775,
aa2e7408d); the previous head f2c3ebf6d is still an ancestor, so no history was rewritten on
this branch. 35 derived gate families, 35 run, 0 NOT-MEASURED, every one exit 0;
check:ratchet-remedy-authority re-read and unmoved (this script classifies excluded).

维护者速读(草稿)

改了什么

AGENTS.md 里讲「降级日志等级」的那一段,重写了最后半段。原文要求:把失败交给调用方的
catch,必须在门禁的两张表里声明「它是怎么交付的」。但那两张表的键都是被调函数的名字,而
一个用 return { ok: false, error } 把失败交还给调用方的 catch 根本没有可填的名字。新文
字按「失败是怎么离开 catch 的」分成两种:靠调用交付的照旧声明被调者;靠返回交付的明确
写清「没有名字可声明」,不进任何名单、不进 baseline、也不准为了让门禁变绿而硬加一行
logger.error,改由该文件自己的测试钉住,并给出普查脚本作为「这类缝一共有多少」的唯一读数
来源。⭐ 不变量本身没有动:静默的数据丢失必须吵。

为什么改

这是一条写在制度里却没人能照做的规则。今天不出事,只因为门禁当前看得见的那一类里这种缝是
0 个;树上真实存在的是 12 个(普查脚本现读,与裁决数字逐字一致),每一个都已由自己文件的
测试钉住,没有一个是未受保护的降级。真正的缺陷是那句话会把下一个开发者送进死路:正确的代码会
把门禁弄红,而唯二能弄绿的办法都是门禁自己明文拒绝的。本次改的是文字,仪器一行未动。

风险与代价(含回滚)

  • 代价:AGENTS.md 这份受管文件的行数上限已经顶满(1099,余量 0),这一段净增 10 行。
    折行压缩买不来这 10 行(实测:旧段在 86/88/90 三种宽度下都是 10 行),所以要么由维护者裁一个
    行数预算把上限抬到 1109,要么这段文字落不了地。开发席不自行抬上限。
  • 风险:低。只改治理文本,不改任何运行时代码、不改门禁判据、不改任何名单或 baseline;
    pnpm check:durability-log-level 在本分支现读仍是绿,36 条缝的判决一条没变。
  • 回滚:单文件单段落,git revert 即可,零运行时影响。

席位意见

同意落地。domain:spec 座位 5(session_019srGWGCBBCBHqcDoRZpQRh,座位贴 #19357)在维护者直派
通道下复核并验收:上面每一条读数本席都在 head aa2e7408d 上独立重取过,⛔ 未采信 dev 的终报自
述;棘轮只动了 :1466 那一个数,AGENTS.md 正文一字未改。⚠️ 本 PR 的卡归 domain:devx 席
(#6023),本席只做验收与记录,⛔ 不接管该卡、⛔ 不改它的标签或 assignee。

你要做的

  1. 读上面那段新文字,确认它就是裁决要的意思(尤其「返回式交付的缝由它自己文件的测试钉住」这一
    条)。
  2. 裁一个行数预算 —— 已裁:A,+10(1099→1109)。上限已按该裁定抬到 1109,
    pnpm check:pm-skill-ratchet 由 exit 1 转 exit 0,裁决原话已写进棘轮注释块。此项无需再做。
  3. 人工把这个 draft PR 合并 —— 受管面,人工合并即复核记录(governance: human merge IS the review record for governed surfaces — retire the per-PR ADR approval check, add a report-only post-merge audit #9495),⛔ 任何席位都不翻
    ready、不入队、不挂 auto-merge。这是本 PR 余下的唯一一步。

Authored by Claude Code, session session_017ef78bLdybu3AffehKkhfk (domain:devx seat, PR #19214's own branch). Body last revised by domain:spec seat 5, session_019srGWGCBBCBHqcDoRZpQRh, under the maintainer's direct-dispatch channel — the ruling record above. ⛔ No seat flips this PR to ready.


Generated by Claude Code

The block on degradation log levels told the author of a durability seam to
declare how the catch delivers the failure, offering only the two propagation
maps of check-durability-degradation-log-level.mjs. Both key on the name of a
callee the catch reaches, so a catch that delivers by returning an outcome
object has nothing to put in either one, and the only two ways left to green
the gate are the two that gate's own header rejects: a baseline entry for
correct code, or a bolted-on logger.error.

Split the declaration rule by how the failure leaves the catch. A
call-delivered seam declares its callee as before. A return-delivered one has
no callee to declare, is added to neither map and to no baseline, is pinned by
its own file's test asserting the returned failure outcome, and has its
population read from the census script. The invariant is untouched: silent
data loss must be loud.

Claude-Session: https://claude.ai/code/session_017ef78bLdybu3AffehKkhfk
Co-authored-by: Claude <noreply@anthropic.com>
The maintainer ruled letter A on this card's line budget (+10, 1099 -> 1109),
recorded on #16233 as comment 5750261694, executing the standing letter-C
ruling 5716260390 that named the text but not its lines.

The raise is recorded in this map's own ruled-raise convention: the measured
cost (+10, bought by content), the re-measured lossless-rewrap headroom with
the widths it was measured at, and the ruling verbatim and untranslated.

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

os-elon-musk commented Sep 20, 2026 •

Copy link
Copy Markdown
Collaborator

os-dev-report

{
"issue": 16233,
"status": "done",
"branch": "claude/issue-16233-agents-return-propagating-seams",
"pr": "#19214",
"session": "session_019srGWGCBBCBHqcDoRZpQRh",
"premise_still_valid": true,
"summary": "Merged origin/main into the PR branch TWICE (merge commits 32d6e47 and aa2e740; never rebased, never force-pushed, never amended, so os-try-charles's checkout fast-forwards), then made the one change the maintainer's letter-A budget authorises: CEILINGS ['AGENTS.md', 1099] becomes ['AGENTS.md', 1109] in scripts/pm/check-skill-line-ratchet.mjs, with a 29-line ruled-raise comment block above it in that file's own established convention (measured cost; my OWN re-measured lossless-rewrap headroom with the widths named; the ruling verbatim and untranslated with all three provenance parts; and the standing 'AGENTS.md is not a CROSS_FILE_MOVES destination, so no ruledRaises record applies. Landed count, headroom 0, same convention.' sentence). AGENTS.md itself was NOT touched: the ruled paragraph is byte-identical to what the branch already carried (my total diff vs origin/main is AGENTS.md +20/-10, unchanged from the previous round, plus check-skill-line-ratchet.mjs +29/-1). PR #19214 stays DRAFT: no ready flip, no enqueue, no auto-merge, no label, no assignee, no body, no title, no card comment. Two things I was told measured FALSE and are detailed in out_of_scope_findings; two items need the dispatching seat's hand because a dev may not write them (the PR body, and the claim's declared file surface).",
"tests": "ALL readings on the final head aa2e740 (git rev-parse --short HEAD), worktree /home/user/wt-16233-ratchet, repo objectstack-ai/objectstack. Every exit code captured with redirect-then-$?, never across a pipe.\n\n1. COMPLETION CRITERION, both legs, on the MERGED tree:\n BEFORE (merged tree, ratchet entry still 1099): pnpm check:pm-skill-ratchet -> EXIT=1, one failing line: 'x check-skill-line-ratchet: AGENTS.md is 1109 lines; the ratchet ceiling is 1099. ... Raising a ceiling requires a maintainer ruling quoted in the PR.'\n AFTER (entry 1109): pnpm check:pm-skill-ratchet -> EXIT=0, and the same line now reads: 'check-skill-line-ratchet: AGENTS.md is 1109 lines (ceiling 1109; headroom 0).'\n That before/after pair IS the reverse verification here; a separate ablation is not applicable (the change is one data value plus comment lines, adds no guard, and leaves no test file behind).\n\n2. AGENTS.md LINE COUNT, re-measured on the merged tree, with the arithmetic:\n origin/main (be7382d, c334ba0 and the current ada7012 all agree): 1099 lines; ceiling on main 1099.\n merged tree: 1109 lines. Branch diff vs merge base: 1 file, +20/-10 = net +10.\n 1099 + 10 (ruled) = 1109 = the measured count = the new ceiling. Headroom 0.\n main never touched AGENTS.md in either merge window, so the ruled +10-over-landed-count is exact rather than assumed: git diff be7382d..origin/main -- AGENTS.md was EMPTY, and so was git diff c334ba0..origin/main -- AGENTS.md.\n\n3. MY OWN REWRAP-HEADROOM MEASUREMENT (imported the gate's own wrapLine from scripts/pm/check-skill-line-ratchet.mjs; the paragraph is AGENTS.md:934-953, 20 physical lines, 1708 logical bytes; per-line bytes 88 88 88 86 86 84 87 89 71 88 81 90 89 89 78 88 73 75 85 86, max 90, 4 lines above 88B). Greedy rewrap of the whole paragraph as one logical line:\n @86b -> 21 | @88b -> 21 | @90b -> 20 | @91b -> 20 | @100B -> 18 | @120B -> 15\n Section context (AGENTS.md:903-990, 66 plain-prose lines): byte width min 3 / median 85 / p90 89 / max 90; whole-section greedy rewrap 88 -> 90 @88b, 88 -> 88 @90b, 88 -> 79 @100B, 88 -> 69 @120B.\n VERDICT: 0 lossless-rewrap headroom at the width this section is actually typeset at (<=90B, inside the <=91-byte ASCII-prose convention the gate header's 'Why 120 bytes' section documents) - at 90B the rewrap returns exactly the 20 lines it already occupies and at 88B it would COST a line. Lines come back ONLY by re-typesetting ASCII prose ABOVE that convention, which the 120-byte cap documents itself as NOT imposing, and re-wrap funding is refused by this file's own 2026-08-17 rule in any case.\n\n4. DERIVED-GATE RECONCILIATION on the final head: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 35 commands (change set: 2 paths, 60 changed lines +49/-11, merge base c334ba0, three-dot). Ran all 35. Reconciled with --ran: EXIT=0, verbatim verdict '35 derived family(ies) accounted for - 35 run, 0 NOT-MEASURED (a DERIVED zero - all 35 recorded an exit code and none of them is 3).' 35 derived / 35 run / 0 NOT-MEASURED / 0 UNRUN, and EVERY ONE exit 0. The heavy member: pnpm check:pm-dispatch-gates EXIT=0, 843.7s on this box, 1883 self-test cases (it exceeds the ~10-minute foreground cap, so it ran detached and I blocked on its PID with tail --pid in the foreground; nothing was left running). I ran the whole union TWICE - once at cffced2 (35/35/0, battery 833.4s, 1866 cases) and again at aa2e740 after the second merge, because main had moved dispatch-gates.mjs by +428/-14 in between; the re-derivation produced a byte-identical 35-command list (zero delta).\n Named gates, individually: pnpm check:pm-skill-ratchet EXIT=0 - pnpm check:ratchet-remedy-authority EXIT=0 - pnpm check:nul-bytes EXIT=0 - pnpm check:pm-governed-merges EXIT=0 - pnpm check:pm-governed-prose EXIT=0 - pnpm check:pm-skill-id-lint EXIT=0 - pnpm check:cross-package-test-inputs EXIT=0.\n\n5. check:ratchet-remedy-authority, CONFIRMED not assumed: the sweep's own verdict for the edited file, read by importing sweep() and querying the key, is verdict 'excluded', 0 live offers, 0 refused offers - identical before and after my edit, and its CONTROL corpus records expect 'excluded' for pm/check-skill-line-ratchet.mjs with set-equality audited both ways, so a flip would have reddened the gate. Gate line: '262 scripts swept; 15 mark the expanding remedy MAINTAINER-ONLY, 5 turn it down outright, 242 hand out no ratchet-expanding remedy.' UNMOVED.\n\n6. The edited script's own suite (owed because the diff edits a gate script): node scripts/pm/check-skill-line-ratchet.mjs --self-test EXIT=0, '157 cases pass', including 'every live ruled-raise record quotes its ruling, dates it, and names a positive line count'. No *.test.ts anywhere in the tree names this script (git grep over test files: zero hits), so the --self-test IS its suite.\n\n7. Repo-wide lint, NOT narrowed: pnpm lint (eslint . --no-inline-config) EXIT=0 in 1m39s, zero findings, whole population - so no narrowing declaration is owed for this card.\n\n8. Governed-surface reading, confirmed: node scripts/pm/check-governed-merges.mjs --pr 19214 -> EXIT=3, 'GOVERNED - a human merge is the review record for this PR (#9495 regime)', AGENTS.md x1, landing tier H, and 'paths not on the register: scripts/pm/check-skill-line-ratchet.mjs' (my second path adds no governed path), size 60 changed lines under the 5000 human-merge threshold.\n\n9. No package is affected (both paths are repo-root: AGENTS.md and scripts/pm/), so there is NO build closure and NO affected-package test/typecheck owed; no changeset (skip-changeset, and neither path publishes). Control-character self-scan beyond the gate: grep -naP over both edited files, zero hits. The pre-push gate printed 'check:commit-card-trailers: no card relation and no model identifier in the trailer pair' on both pushes.\n\n10. WHAT MAIN BROUGHT, and conflicts: NOTHING conflicted, either merge, and no os-regen-pending marker was ever written (both merges were clean auto-commits). The first merge brought 54 commits; of the files I care about it touched ONLY scripts/pm/dispatch-gates.mjs (+75/-57) - not AGENTS.md, not the ratchet script. The second brought 4 commits including #19284 'derive a gate population through composite actions' (dispatch-gates.mjs +428/-14) - again neither of my two files. Both --ran runs printed a STALE TREE warning afterwards because origin/main is a SHARED local ref a sibling's fetch advances; current origin/main is ada7012, it touches neither of my two files and still carries AGENTS.md at 1099, so the ruled arithmetic is unaffected and I stopped chasing it rather than merging a third time.",
"mcp_calls": "0 - no MCP GitHub tool was called at all; every GitHub read was a curl GET through the REST proxy.",
"api_writes": "2, both on my own report comment: POST /repos//issues/19214/comments (comment 5750531817) and one PATCH /repos/objectstack-ai/objectstack/issues/comments/5750531817 correcting this very field, because the first send described this write as blocked (an earlier tool call in this session had been refused by the permission layer with 'External System Writes', and the POST was then allowed). ROUTING NOTE: my agent contract makes GitHub the authoritative report channel and asks for the comment on the ISSUE, but this dispatch forbids posting on card #16233 (the seat owns that thread), so it is on PR #19214's thread instead - card thread, PR body and PR title all untouched; relay it to the card if the seat wants it there. Everything else was git, not REST: 2 pushes to the existing branch, no POST /pulls, no POST labels, no PATCH of any PR body, no state change.",
"open_questions": [
{
"question": "PR #19214's body now contradicts its own diff and carries no maintainer ruling - and the ratchet's own discipline is 'RAISING one requires a maintainer ruling quoted in the raising PR's body', with completeness explicitly left to the reviewer because the gate cannot see the PR. The body still says 'the dev seat did NOT touch scripts/pm/check-skill-line-ratchet.mjs; the +10 is reported as measured and the budget is the approver's to rule', and its rewrap table is one cell wrong (see the measured-false entry). I am barred from editing the body (and re-sending it would stack a second footer), so a dev cannot fix this.",
"options": [
"A - the seat edits the body: replace the 'One gate is RED' section with the landed raise, quote the ruling verbatim and untranslated with its three provenance parts (live instruction to session_019srGWGCBBCBHqcDoRZpQRh, recorded on #16233 comment 5750261694, executing 5716260390), correct the rewrap table to 21/21/20 at 86/88/90 B, and add the Clause-2 no line the body lacks",
"B - leave the body and rely on the in-tree ruled-raise comment block plus card comment 5750261694 as the quotation of record",
"C - the seat posts a comment on the PR quoting the ruling and leaves the body untouched"
],
"recommendation": "A, because the ratchet's header puts the quotation in the PR BODY specifically and names body completeness as the reviewer's check; the maintainer reads that body as the landing record for a governed surface, and it currently tells him the opposite of what the diff does. C is an acceptable minimum if the seat wants to avoid a body rewrite; B alone leaves the human-merge record contradicting the diff."
},
{
"question": "The claim of record (#16233 comment 5746126007) declares 'File surface: AGENTS.md' only (plus, conditionally, one header sentence in check-durability-degradation-log-level.mjs) and says 'stop on breach; explain in the report'. My dispatch ordered the edit to scripts/pm/check-skill-line-ratchet.mjs, which the maintainer's ruling authorises but that claim does not list. I am explaining rather than stopping, because the file is named by the authorising ruling itself; amending the claim's declared surface is a card-thread write I am forbidden to make.",
"options": [
"A - the seat amends the claim's file surface in the same round, adding scripts/pm/check-skill-line-ratchet.mjs",
"B - treat the maintainer's budget ruling as self-amending the declared surface and record nothing"
],
"recommendation": "A - the same-round amendment of the declared file surface is exactly the obligation an out-of-surface fix carries, and it costs one comment."
}
],
"out_of_scope_findings": [
"MEASURED FALSE (dispatch text): 'the previous dev measured 0 lossless rewrap headroom at three widths' is true only for widths <=90B. Reproduced with the gate's own wrapLine: the paragraph is minimal at 90B (20 lines, headroom 0) and would COST a line at 88B (21), but at 100B it reflows to 18 and at 120B - the gate's OWN per-line budget MAX_LINE_BYTES - to 15, and the whole section 88 -> 69. So 'headroom 0' is a statement about the typeset width, not about the file; the correct justification for the raise is that re-wrap is not legal currency here (this file's 2026-08-17 rule refuses re-wrap funding, and the 120-byte cap documents itself as codifying the corpus's existing <=91-byte ASCII wrap rather than mandating a reflow to it). My comment block states it that way, with the widths named, instead of inheriting the flat claim.",
"MEASURED FALSE (PR #19214 body, minor): it reports the rewritten paragraph as '21 / 20 / 20' at 86/88/90 bytes and says it 'is landed at the 88-byte width the surrounding block uses'. I measure 21 / 21 / 20, and the paragraph as landed has 4 lines above 88 bytes (89, 90, 89, 89; max 90), so it is landed at <=90B, not 88B. The conclusion is unaffected and if anything stronger (at 88B a rewrap costs a line). Their old-paragraph reading reproduces exactly: 10 lines at 86/88/90.",
"MEASURED FALSE (dispatch hard rule 9, and the harness attribution reminder): the prescribed trailer 'Co-Authored-By: Claude Opus 5 (noreply@anthropic.com)' is mechanically REFUSED by this repo - scripts/check-commit-card-trailers.mjs coauthorModelIdentifier() flags any harness-address co-author whose display name is not exactly 'Claude', and .githooks/pre-push runs it, so that spelling would have blocked the push. AGENTS.md rules the pair model-free. I used the repo's pair ('Claude-Session: https://claude.ai/code/session_019srGWGCBBCBHqcDoRZpQRh' plus 'Co-authored-by: Claude (noreply@anthropic.com)'), and the pre-push gate confirmed it on both pushes. Flagged, not silently chosen.",
"CONFIRMED TRUE (dispatch): the ratchet entry at :1438 read ['AGENTS.md', 1099] on the PR head and the map at :1910 is the unrelated widest-table-row pin (768) - untouched. The branch was 1 file +20/-10. check:ratchet-remedy-authority classifies the edited script 'excluded' and is unmoved. check-governed-merges --pr 19214 is exit 3, 1 of 1 register path. No needs:contract-review on the PR or the card.",
"noted, not filed: the two companion sentences from 5746211232 (remedyFor() -> propagationOption still telling a FAILING author to do what the new AGENTS.md text forbids, and the ':101-105 are all open' sentence that letter C falsified) are NOT in this PR and I did not implement them. Ruling 5716260390 closed four sibling cards on this gate not_planned and ruled 'the gate gets no further investment; if a real incident ... is ever traced to one of these gaps, that incident is the card', so filing is refused by the ruling itself - which is also why the previous seat withdrew its own filing promise. Successor: NONE - no open PR or queued card touches scripts/check-durability-degradation-log-level.mjs, so nobody is currently slated to read that file; the cost stays live and unowned until an incident or a maintainer ruling names it.",
"noted, not filed: this card's own thread (5746910798) already reports the structural property that 31 of 32 ratchet-capped files stand at headroom 0, so any ruling that writes text into a governed instruction file structurally needs a second ruling for its lines. Not mine to file and already on the maintainer's desk; re-measured incidentally here only for AGENTS.md (headroom 0 before, headroom 0 after).",
"noted, not filed: PR #19214's body carries no 'Clause-2:' line at all. The live carrier is the skip-changeset LABEL, which check-changeset-no-major.mjs reads from the pull_request payload alongside the body, and its header records a measured precedent where the declaration was absent and the label carried it - so this is a body-completeness gap for the seat, not a red. Named here because a dev may not write the body."
]
}


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review September 20, 2026 23:06
@os-zhuang
os-zhuang enabled auto-merge September 20, 2026 23:06
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 20, 2026
Merged via the queue into main with commit 287eb4c Sep 20, 2026
47 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-16233-agents-return-propagating-seams branch September 20, 2026 23:36
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…e at first grading, broken gates are deleted, ≤1 tooling dev in flight, tooling is a first-touch label (ruling objectstack-ai#202 B) (objectstack-ai#19462)

Fixes objectstack-ai#19457

Clause-②: no — charter text plus one label-vocabulary row. The diff adds
no Zod key, no closed-set member, no exported symbol and no runtime
registration. `node scripts/pm/check-widening-tells.mjs --declaration no
--diff` was run over this branch's full three-dot diff and exits 0.

## The ruling this PR carries

Batch objectstack-ai#202 item 1, letter B. The maintainer's authorizing words,
verbatim, in order, quoted unchanged from the card (chat, 2026-09-20
between 23:18Z and 23:45Z):

1. 「我随便看了几个任务,这些都是辅助类的吧,我感觉开发agent被大量的浪费在这种任务下。在北极星的标准下,这些应该开发吗?」
2. 「我们是创业项目,应该花精力处理类似的这些任务吗?」
3. 「B(荐)A + 清理存量」
4. 「p2 的 47 张转 pm:on-hold 带机器条件 有价值吗?卡片只要open就要一直被扫描。」
5. 「重点是分诊,分诊为什么没有关闭这些卡片」
6. 「你建议的规矩就三条,是否应该立卡派发」 / 「按照你的建议继续。」
7. 「对于工具卡,分诊是不是应该有一个单独的标签打上,比如 tooling」
8. 「立章程卡并把 90 张的关闭交分诊席,你会派发处理章程卡吧,然后合并之后通知分诊?」

And the ruling addendum recorded on the card as comment 5754225479,
which edit 6 below executes, verbatim and untranslated:

9. 「受管合并审计 以后不需要了,浪费时间。」

And the ceiling ruling, recorded on the card as comment 5754521737,
which round 4 executes (the first clause is a different card, the second
is this one):

10. 「205 否,天花板抬到 819」

## The five edits

All five land in `.claude/skills/pm-dispatch/SKILL.md`. The one rule
that also has a line in `references/core-rules.md` is changed there in
the same PR, per the charter's own 「一条规则在本文与核心条款一处改动,另一处同 PR 同改」.

**1. The dev queue is product-only.** The tooling clause is deleted from
the `pm:queue` definition in both files:

- SKILL.md 〈分诊座位职责〉: 「`pm:queue` = 有具名落点或复现的具体缺陷,或范围明确的工具/门禁修复,无可问之事」
becomes 「`pm:queue` = 有具名落点或复现的具体缺陷,无可问之事;⛔ 工具/门禁修复不由此进」.
- `references/core-rules.md` 〈分诊座位职责〉:
「有落点或可复现的缺陷、范围明确的工具修复与实现未被裁错的说明书脱节进 `pm:queue`」 becomes
「有落点或可复现的缺陷、实现未被裁错的说明书脱节进 `pm:queue`;工具卡另须带解锁行」.

The condition a tooling card may sit in `pm:queue` under is added beside
it: 「`tooling` 入 `pm:queue` 仅当首行带 `Unblocks: #N`(open 产品卡)或点名所护的已发布面」.
The execution seat's candidate query gains the exclusion in place, on
the 候选 line in 〈候选与批次〉: 「⛔ 排除两行皆无的 `tooling` 卡」.

**2. Triage closes at first grading.** In 〈分诊座位职责〉, on top of what
objectstack-ai#19459 landed on `main` while this branch was open:

- The grading reference now reads North Star 「优先级」1–3 条 (main had 1、2).
「1–3」 rather than 「1、2、3」 is measured, not stylistic: the latter puts
that line at 121 bytes against the ratchet's 120-byte cap.
- 「「无则关」= 首触即关 not_planned,带理由与入队两条件,⛔ 不定 p3、不 hold;下行同此」 — it hangs off
main's own 「无则关」 clause rather than restating it, and carries only what
main lacks: `not_planned`, the reason, the two reopen conditions, and
the prohibition.
- North Star rule 3 restated as a **closing** rule: 「产品仓 P0/P1
开着时,无解锁对象的 p2/p3 `tooling` 卡同样关」, inheriting the prohibition through
「下行同此」.

Class (b) is narrowed beside the three filing classes: 「门禁头注、self-test
文案与 `check-*` 处方句 ⛔ 非已声明契约;(b) 须用户或已发布包读得到」.

**3. A broken gate is deleted, not repaired.** Seeded on the existing
「失效修法按序取:先删容许出错的构造…」 line in 〈平台读数纪律〉, two lines follow it:

- 「门禁两次误报(假红、实测假绿、处方句点名不存在路径)⇒ 删肢或删门禁,PR 引两次测量」
- 「只有护产品落地或用户可见契约的门禁才立修复卡;门禁上「稳定 > 功能」= 更少零件」

**4. At most one tooling dev in flight, fleet-wide.** In 〈候选与批次〉, beside
the 并行度 rule: 「舰队至多一张 `tooling` 卡带 `pm:dispatched`,第二张等;带 `Unblocks:`
者继承产品级不计数」.

**5. `tooling` is a first-touch triage label with named readers.** The
authoritative label glossary is the 〈状态模型〉 table in SKILL.md, whose
bullet list carries the rule 「一个标签存在当且仅当有具名读者」. Definition, first-touch
application and all four readers land in the one glossary row:

`| tooling | 修复落在门禁/脚本/workflow/技能/席位协议/PM 工具面而非产品包;分诊首触打,与 domain:*
同笔;四具名读者 = 候选查询排除、首触即关、舰队一张在飞、普查半态行 |`

A table row is exempt from the 120-byte cap and metered by the file's
widest-row pin instead, so carrying the readers there costs one line
fewer than a separate bullet: the row is 227 bytes against this file's
342-byte pin.

Reader (iv) — the half-state row — is **named only**.
`scripts/pm/check-half-states.mjs` builds every H row from a per-row
predicate, a message builder and a registration, so it is not the
one-line addition the card made that conditional on; per the card it
stays named in the glossary and is left to the next patrol edit.

### The label OBJECT, which rides this PR

Round 1 reported this as a finding; it is fixed here instead of filed.
`scripts/pm/ensure-pm-labels.sh` — the file the charter names as the
authority on a label's readers — did not name `tooling` at all. Measured
in objectstack while writing this, with the label live on 44 queue
cards, the object carries GitHub's default colour `ededed` and a
**null** description. That is the exact drift the script's own header
describes, and the header also says why it cannot heal by itself:
`--reconcile` aligns only the labels that file names, so no rerun in
either mode ever reaches it.

The row is added in the five-repo loop, beside `finding`, on the rule
stated next to `priority:p0`: a label belongs in that loop when the
sweep that reads it is repo-parameterized and the duty that sets it is a
five-repo triage duty. Both hold. Its description is 100 characters, at
the hard cap — `pnpm check:pm-label-desc-cap` is green and names
`tooling` as the longest of the 28.

## Edit 6 — the director's governed-merge audit is retired

Item 9 above. It is a net **deletion**, and the only edit in this PR
that removes a duty rather than adding a rule.

- `references/lanes/director.md` loses 〈职责四:受管合并审计〉 entire — the heading
and its six bullets, 9 lines with the blank — and the 四职 reference in
its opening block becomes 三职. The file goes **72 to 63** lines.
- SKILL.md loses the two lines that RAN the audit (the `--since`
invocation and the lane-early-warning / director-window split), drops
the audit list from the round-report contents line, and its two
duty-roster mentions become 三职. The guard-index row keeps the SCRIPT and
loses only 轮报载体, which the deletion makes false.
- `references/core-rules.md` drops the audit from the round-report line
and from the director's duty roster, both in place, net 0 lines.

Kept deliberately, on the test that a line about the CI gate or the
script's existence is not the retired seat duty: the `domain:skills`
lane row naming the script as the governance-execution file; the
guard-index row itself; `landing-operations.md`'s pre-ready `--pr N`
run; `lanes/skills.md`'s `--test` run; `state-machine.md`'s
generator-artifact `--test` rule; and both `platform-readings.md`
behaviour readings. `scripts/pm/check-governed-merges.mjs` and its CI
self-test are untouched — they are not the duty.

Round 3 left one mention standing and reported it; **round 4 takes it**,
on the seat's word and under the same directive:
`references/lanes/skills.md` loses 「轮报的受管合并审计清单带 `--since` 四仓实跑,⛔
不凭记忆汇总」, the same retired audit ordered for a different seat's round
report. That file goes **33 to 32** lines. A residual scan for the duty
across `.claude/skills/pm-dispatch/**` and `.claude/agents/**` — pattern
受管合并审计, governed 合并审计, 合并审计, 四职, 职责四 — now returns **zero hits**, and
the surviving `check-governed-merges` references are only the CI-gate
and script-behaviour ones listed above.

## Merged `main`

`main` moved three charter commits under this branch after its merge
base: be488ce (objectstack-ai#19449), 287eb4c (objectstack-ai#19214) and 22ca89f (objectstack-ai#19459).
The last rewrites exactly the grading line this card edits, so `git
merge origin/main` produced one conflict, in
`.claude/skills/pm-dispatch/SKILL.md`. It is a merge commit, not a
rebase, and nothing was force-pushed.

The resolution keeps **both** sides: main's four-segment `Path:`
spelling and its two new lines (定义项 / 清单项) verbatim, and this card's
three additions beside them as described under edit 2. The merge commit
carries only the resolution; the fold and the label row are a separate
commit on top, so a reviewer can read what main brought apart from what
this change produces.

## Verification the card asks for

```
$ git grep -n '范围明确的工具' -- .claude/
$ echo "EXIT=$?"
EXIT=1                     # 0 hits on this branch

$ git grep -cn 'pm:queue' -- .claude/skills/pm-dispatch/SKILL.md
.claude/skills/pm-dispatch/SKILL.md:23
$ echo "EXIT=$?"
EXIT=0                     # the control is non-zero: the instrument reaches the file
```

## Gates

Derived on this head with `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack` — **41 families**: 21 at first, 36
once the label row brought the shell-script families in, and 41 now that
round 4 edits the ratchet script itself. Every one was run with its exit
code captured before any pipe, and the reconciliation reads `41 derived,
41 run, 0 NOT-MEASURED, 0 UNRUN`, a derived zero rather than a claimed
one because every line carries its code. **All 41 are green.**

`pnpm --filter @objectstack/lint run check:doc-formula-expressions`
exits **0** once `@objectstack/formula` and `@objectstack/lint` are
built; on a torn-down worktree it exits **3**, which that gate spells
out as PREREQUISITE NOT MET with nothing measured — not a finding.

Also run green, outside the derived set: `check:pm-widening-tells`
(self-test and against this diff), `check:pm-settings-deny-roster`,
`check:pm-clause2-carriers`, `check:pm-label-write`,
`scripts/check-skills-token-ratchet.mjs`,
`check:skill-identifier-liveness`, `check:skill-frame-freshness`,
`check:skill-compatibility`.

### The ceiling, now ruled

Rounds 1 to 3 left `check:pm-skill-ratchet` red at 819 against 813 and
did not touch `CEILINGS`, because raising one is on the charter's own
manual floor. The maintainer has now ruled it — item 10 above — so round
4 executes it and the gate is green:

```
✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/SKILL.md is 819 lines
  (ceiling 819; headroom 0).
```

The `CEILINGS` entry for `.claude/skills/pm-dispatch/SKILL.md` goes 813
to 819, with the maintainer's sentence quoted above it in the shape of
the two ORDINARY ruled raises already on that entry: what the six lines
buy, why they could not be paid in place, and the note that SKILL.md is
not a `CROSS_FILE_MOVES` destination so no `ruledRaises` record applies.
⛔ No other entry in that map moves.

Measured, per file, against `origin/main`:

| file | main | this branch | ceiling | verdict |
|:--|--:|--:|--:|:--|
| `.claude/skills/pm-dispatch/SKILL.md` | 813 | 819 | **819** (ruled) |
green, headroom 0 |
| `.claude/skills/pm-dispatch/references/core-rules.md` | 151 | 151 |
151 | green, headroom 0 |
| `.claude/skills/pm-dispatch/references/lanes/director.md` | 72 | 63 |
72 | green, headroom 9 |
| `.claude/skills/pm-dispatch/references/lanes/skills.md` | 33 | 32 | 33
| green, headroom 1 |
| `scripts/pm/ensure-pm-labels.sh` | — | — | — | not ceilinged |

The six lines the ruling buys are what is left after three rounds of
paying in place — the cost went 9 at round 1, to 8 after the merge, to 6
after edit 6: folding the four readers into the length-exempt glossary
row bought one line back, and edit 6 deleting the two lines that ran the
audit bought two more. The widest-table-row pin is unaffected —
SKILL.md's pin is 342 bytes and the new glossary row is 227. Every new
prose line is at or under the 120-byte cap.

**Neither shrunk file has its ceiling lowered, and that is a
measurement, not an omission.** The gate prints `director.md is 63 lines
(ceiling 72; headroom 9)` and `lanes/skills.md is 32 lines (ceiling 33;
headroom 1)`, both with a **✓** — it asks for neither entry to be moved,
exactly as it does not for `dogfood-verification/SKILL.md`, which stands
at headroom 9 and is green. Lowering either is a legitimate ratchet-down
whenever someone wants it; it is not owed here.

Six lines is the measured cost of the six ruled edits after paying
everything payable in place, and it is what item 10 rules. Most of the
rule changes cost **no** line at all: the `pm:queue` definition, the 候选
candidate query, the grading reference, the core-rules mirror, the four
readers now riding the glossary row, and every one of edit 6's in-place
rewrites. The remaining six are one ruled rule each and there is nothing
left to merge them into: **every ceilinged file in that map stands at
headroom 0**, so no reference file can absorb them either, and a
declared cross-file move is zero-sum by construction — the destination's
raise may not exceed the source's net decrease — so it cannot fund new
content.

Raising a ceiling is on the charter's own manual floor —
「人工地板项(发版、天花板、契约扩大)仍需明确字句」 — which is why rounds 1 to 3 carried the red
rather than clearing it. Item 10 is that explicit sentence, so the raise
is now an execution rather than a judgement:
`['.claude/skills/pm-dispatch/SKILL.md', 813]` becomes `819`, quoted
above the entry, exactly as the two precedents already in that map were
taken. A cross-file move funded by director.md's decrease was described
to the seat and **refused**: the mechanism is a move, deletion at the
source pays and restatement does not, and nothing here moves — it would
have passed the gate's arithmetic while recording something that did not
happen.

## Landing

The register of record is the `GOVERNED_SURFACES` table in
`scripts/pm/check-governed-merges.mjs`. Measured on this tree,
`governedPathsIn` answers the single row `claude-tree` (`.claude/**`)
and `governedTierFor` answers **`S`** for this diff — so by the register
this is a **Tier S** landing, not Tier H as the card and the dispatch
both describe it. `scripts/pm/**` is not on the register and does not
change that. It changes nothing about what happens next: the
maintainer's item 8 reserves this particular merge to his own hand
(「你会派发处理章程卡吧,然后合并之后通知分诊?」), and the ratchet red above must be cleared
first either way. No seat flips this ready, queues it or arms
auto-merge.

This diff publishes nothing from any released package — `.claude/**` and
`scripts/pm/**` only, and `scripts/pm/**` is a PM-loop tool that ships
in no tarball — so `Check Changeset` needs `skip-changeset`. The seat
applies labels; this PR does not.

Related: objectstack-ai#19340 is the sibling filing/merging directive and is **not
addressed here** (its items 1 2 3 4 5 8 landed separately as
be488ce); objectstack-ai#19458 carries the 90-card stock closure and is **not
addressed here** either.

## 维护者速读(草稿)

**改了什么** ——
开发队列从此只收产品卡。工具卡(门禁、脚本、技能、席位协议)要么点名它挡住的那张产品卡,要么点名它保护的已发布面,否则分诊第一次看到就关掉,不再降级成
p3 挂着。另外三条配套:坏门禁默认删不默认修;全舰队同时最多只有一个开发 agent 在做工具卡;`tooling`
成为分诊首触就打的标签,有四个具名读者,并且这次把这个标签本身也在标签词表脚本里声明了(它现在在 GitHub
上是灰色、没有说明的野标签)。第六条是你说的:总监席的「受管合并审计」整职删掉,章程里所有让席位去跑这个审计的行一并删(技能席那一行也删了),脚本和
CI 自检不动。

**为什么改** —— 现在队列里 215 张有 111 张是工具类(52%),而产品 P0 只有 3 张、P1 有 29
张。分诊没有关掉它们不是失职,是章程写着「范围明确的工具修复」就该进队列。规矩不改,下一批 90 张还会长出来。

**风险与代价(含回滚)** —— 风险是误伤:真正挡住产品的仪器卡如果忘了写 `Unblocks:`
行,会被当成工具卡关掉。对冲是两个重开条件都写在关单评论里,重开免费。回滚是一次 revert,这个 PR 只改两个 markdown
文件加一个 shell 脚本里的一行标签,没有产品代码、没有 workflow。

**席位意见** ——

**你要做的** —— 一件:合并这个 PR(受管面,item 8 说了由你亲手合)。天花板的事已经按你那句「天花板抬到
819」执行完,棘轮现在是绿的(819/819),41 个门禁全绿。

Authored by the dispatched os-dev round of
https://claude.ai/code/session_012GcsUbuqFGBibkEDMRC1eE — round 1 opened
this PR; round 2 merged `main`, folded the readers into the glossary row
and added the label row; round 3 landed edit 6; round 4 executed the
ceiling ruling and removed the last audit line. The footer under this
line is the platform own block: a REST body EDIT appends one, which is
why this body carries none of its own.


---
_Generated by [Claude Code](https://claude.ai/code)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…n the commit sha — and runtime strings carry no tracker number (objectstack-ai#19453)

Fixes objectstack-ai#19294

Clause-②: no

AGENTS.md's register paragraph told every agent that a rule's provenance
lives in the PR that landed it. Merged-PR numbers decay out of GitHub —
the spec seat measured 11.7 %–15.1 % of one week's already deleted, with
no 301 and an empty commit-to-PR association — so that convention
pointed every reader at objects this repository does not control and has
measured itself destroying. The maintainer ruled the replacement
(decision batch objectstack-ai#195 item 3 letter C+D, verbatim and untranslated: 「同意」;
the director recorded it on the card this PR lands on). The sentence is
REPLACED, not appended to: no second convention survives beside it.

`grep -n -iE 'provenance|PR that landed|landed it' AGENTS.md` on 57ceb9d
returns two hits — the sentence at :14 and, at :253, the unrelated
instruction that a repo-local ADR mirroring a cloud decision carries a
`## Provenance` section. The convention is stated once, so one sentence
is replaced and nothing else in the file restates it.

## Before / after — AGENTS.md :14, against 57ceb9d

Before:

```text
(`pnpm check:pm-skill-id-lint`) — a rule's provenance lives in the PR that landed it. Where
a hook or CI gate enforces a rule mechanically, the rule is stated once here and the
script's own header is the authority on detail.
```

After:

```text
(`pnpm check:pm-skill-id-lint`) — a rule's provenance cites the ADR or ruling record that
decided it, otherwise the commit sha in this repository's history; a PR number is a
convenience link, ⛔ never the citation. Runtime strings — refusal prose, prescriptions,
anything an author is shown — carry no tracker number (`pnpm check:doc-authoring`): the
lesson goes into the text. Where a hook or CI gate enforces a rule mechanically, the rule is
stated once here and the script's own header is the authority on detail.
```

The ruled clauses land in the ruling's own order: the ADR or ruling
record first (stable, in-repo, it carries the narrative); otherwise the
commit sha in this repository's history (immutable, and `git` reads it
with no network); a PR number demoted to a convenience link and ⛔ never
the citation. Letter D is the sentence after it — runtime strings carry
no tracker number, under the maintainer's standing words 「处理 issue
时犯的错应该总结成经验,保留 issue id没有意义」: the lesson is written into the text
instead of a number the reader must dereference.

The neighbouring clause about mechanical enforcement is kept intact, and
the new text obeys itself — it carries no bare tracker number, and `pnpm
check:pm-skill-id-lint`, which scans AGENTS.md against the pattern it
names, is green on it.

Stated precisely rather than implied: the gate cited for letter D
reaches `packages/` minus `packages/spec` today, on a shrink-only
baseline of 821 pinned sites across 231 files. The prose states the
repo-wide rule; extending the gate's reach is the separate card already
named on the issue body, not this one.

## The AGENTS.md line ratchet — paid at net 0, the ceiling untouched

The ruled text is +3 physical lines and AGENTS.md sat at headroom 0
(1099 / 1099), so the first head (9f35c66) measured `pnpm
check:pm-skill-ratchet :: exit 1`. The seat widened this card's file
surface so the three lines are paid in RETIRED CONTENT — each retired
fragment is a verbatim restatement of a rule whose home is another line,
or a second pointer to a file already pointed at from a surviving line —
⛔ never by raising the ceiling (the CEILINGS row in
`scripts/pm/check-skill-line-ratchet.mjs` is untouched at 1099). Head
a799442: AGENTS.md 1099 → 1099, `pnpm check:pm-skill-ratchet :: exit 0`.

| retired (pre-edit line) | what it was | the rule still lives at |
|:--|:--|:--|
| :58–59 「; the script headers are the authority on detail」 |
restatement | :19 — the register paragraph's own clause; the two gates
it qualified stay named on the line |
| :452–453 「The two measured mutation shapes and their triggers live in
pm-dispatch `references/platform-readings.md`.」 | second pointer |
:433–434, two paragraphs above in the same section |
| :758–759 「(shape, sha256 vs that record, record pin ==
`.objectui-sha`)」 | the gate's detail list | :19 (the script's own
header is the authority on detail); `scripts/check-sdui-manifest.mjs`
stays named; the regenerate-on-pin-bump rule at :159–161 |
| :760–761 「— "could not run" is a failure, not a skip (Route & surface
ownership §3) —」 | restatement + second pointer | :851–855, Route &
surface ownership rule 3 |

All three lines are bought by deleted content, none by re-wrap: each
untouched paragraph greedy-wraps to exactly the line count it already
had. Candidates rejected and why (an only pointer; PR objectstack-ai#19214's reserved
region :931–:956; fragments that buy no line; a loosely wrapped
paragraph whose lines would be re-wrap currency; gate-pinned
governed-surface enumerations) are on the card's second `os-dev-report`.

## Verification

Derived union on a799442 (re-run after the retirements and one merge of
`origin/main`), `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` with no paths, reconciled with `--ran`: **15
derived, 15 run, 0 NOT-MEASURED, 0 UNRUN**, every exit captured before
any pipe.

Green (15): `check-closing-keyword-parity` (gate and `--self-test`),
`check-comment-mask-corpus`, `check:agent-test-spelling`,
`check:docs-audit-scope`, `check:driver-memory-census`,
`check:gitlink-declared`, `check:nul-bytes`, `check:pm-governed-merges`,
`check:pm-governed-prose`, `check:pm-skill-id-lint`,
`check:refd-timer-probe`, `check:required-contexts`,
`check:watch-hint-literal`, `check:pm-skill-ratchet` (AGENTS.md 1099
lines, ceiling 1099, headroom 0).

Run beyond the union because the new text names it: `pnpm
check:doc-authoring :: exit 0` — 403 files clean, 44 published skill
files clean, 15881 customer-facing strings across 1013 spec sources
clean, sibling-package prose ids holding the baseline.

No package is touched, so there is no dependency closure to build and no
package test suite in scope; the repo-wide `pnpm lint` scan is CI's run,
not this PR's. `skip-changeset`: AGENTS.md is a repo-root instruction
file and ships in no package's `files[]`.

## Acceptance notes

- Noted, not filed: the gate cited for letter D excludes `packages/spec`
from its prose-id leg and runs on a shrink-only baseline, so the
mechanical reach is narrower than the prose rule. Carrier: the reach
card already named on the issue body — no new card.
- Noted, not filed: the CEILINGS row for AGENTS.md now carries four
stacked per-card narratives in a gate script whose own header prescribes
moving narrative out of operational text. Carrier: the next ceiling
raise on that row, PR objectstack-ai#19214, already in flight.

## 维护者速读(草稿)

**改了什么** — AGENTS.md 开头「规则登记册」那一段里的一句:规则的出处从「落地它的 PR」改成「先引 ADR /
裁决记录,没有就引本仓的 commit sha,PR
号只能当顺手链接」;并补上一句「运行时字符串(拒绝文案、处方、任何打给作者看的文字)不带任何 issue
号,把教训写进正文」。这三行的增量由退掉文件里四处重复陈述 / 重复指针付账(上表),文件净 0 行,行数棘轮上限没动。

**为什么改** — 旧写法把读者指向 GitHub 上的 PR 号,而实测一周内已合并 PR 号有 11.7%–15.1% 已经被删除(没有
301,commit 到 PR 的关联是空的)。也就是说,规则的出处指向了一个本仓库不拥有、而且已经被测到正在消失的对象。ADR / 裁决记录和
commit sha 都在仓库里,不会消失。这是您在裁决批次 objectstack-ai#195 第 3 项 C+D 上批的「同意」。

**风险与代价(含回滚)** — 风险很低:改的是一句给 AI 和人看的约定,没有代码、没有发布面、没有
changeset。代价是退掉的四处重复文字,每一处的规则仍住在上表点名的行;若您认为哪一处不该退,恢复它是一行
revert,但要同时退别的一行才能过棘轮。回滚就是 revert 这一个 commit,没有任何下游依赖。

**席位意见** — (留空,席位复审时定稿)

**你要做的** — 一件事:确认正文措辞后点合并(Tier H,这个 PR 一直是 draft,席位已做
ACCEPT;棘轮已绿,不需要您裁行预算)。


---
_Generated by [Claude Code](https://claude.ai/code)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
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/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

4 participants