Repository navigation
finding — the repo mandates --self-test, gates that it is wired, and documents nothing about its shape: 168 hand-rolled assertion helpers, and only 20 of 170 can be shown to fail on zero cases #15410
Description
Recording only — unassigned, no claim, no code change proposed here. Raised by the maintainer from a reading of recent PRs ("你们经常把 fixture 里面已经存在的方法加到 self test 里面,而且没有在文档中提到 self test 中的方法"), then measured.
The measurement
On origin/main:
| count | |
|---|---|
scripts carrying --self-test |
170 |
| of those, scripts defining their own assertion helper(s) | 116 |
| assertion helpers so defined, in total | 168 |
entries in scripts/fixtures/ |
1 (merge-queue-triage/) |
scripts importing anything from scripts/fixtures/ |
5 |
And the documentation half:
| where | what it says about writing a self-test |
|---|---|
AGENTS.md |
two mentions of --self-test, both "add a case" instructions (:121, :384) — nothing on shape |
CLAUDE.md |
one, about re-running a hook's selftest |
skills/**, .claude/skills/** |
zero hits for how to write one |
scripts/check-self-test-wired.mjs |
enforces that a self-test is wired into CI. Existence, not shape. |
So the convention is mandatory, mechanically enforced for existence, and written down nowhere. Every author re-derives it, and 116 re-derivations produced 168 different helpers.
The part that is a correctness problem, not a style problem
Classified by whether the script's own text anchors its self-test against running zero cases — a case-count floor, a pinned battery roster, or an explicit expected count:
| count | |
|---|---|
| carries an anti-vacuity anchor | 20 |
| decides success solely on "no failures were recorded" | 37 |
| matched neither pattern — needs a human reading | 96 |
Of 170 self-tests, 20 can be shown from their own text to go red if they run zero cases. The other 150 have not been shown to.
Why that matters more than the duplication
check-self-test-wired.mjs's own header states the property the mechanism exists for:
A gate whose defect class is its MATCHING RULE — "do these two strings correspond?" — cannot detect its own regression on a clean tree. Green means the finding set is empty; weakening the rule can only SHRINK that set; and the empty set is the fixed point of shrinking. So the production verdict is identical before and after the rule breaks.
--self-test… is the only instrument watching the rule.
A self-test that passes vacuously is that same failure one level up: the instrument watching the gate can itself stop measuring, and its verdict does not change. failures.length === 0 as the sole success condition makes "every case passed" and "no case ran" print identically.
This is not hypothetical in this repo. It is the defect class that produced, in one working day: a release check that read 1 of 69 packages and reported failure; an anti-vacuity floor that decayed to rows.length >= 0 when the ledger it referenced reached zero (#15261, caught in review); a gate-sweep report listing eight package families as green that the sweep had filtered out and never run (#15332, caught by its own author); and a card asserting a lint rule that does not exist anywhere in the tree (#15325).
⛔ What this card does NOT propose
Not a shared assertion library. Self-containment looks deliberate and defensible: each self-test runs standalone as node scripts/<x>.mjs --self-test, so a shared helper module would be a single point of failure for all 170 instruments at once. Deduplicating 168 helpers into one import edge trades a diffuse cost for a concentrated risk, and it is the opposite of the direction check-self-test-wired was built in.
The cost is not the duplication. It is the dispersion in rigour. 20 authors thought of anti-vacuity; the rest did not — because nothing tells them to.
Two directions worth weighing (⛔ not adjudicated here)
- Write the shape down, in the conventions file, as a required contract rather than a suggestion: a self-test declares what it ran (a case count, or a named battery roster) and fails when that declaration is not met. Each script satisfies it independently — self-containment preserved, dispersion removed.
- Give
check-self-test-wired.mjsa second assertion — it already reads every self-test to check wiring, so it is the one place that could also check that each declares its own population.⚠️ Weigh: that gate would then have two subjects, and its own self-test would have to cover both.
The 96 unclassified scripts should be read before either direction is costed — the true split between "already fine" and "can pass vacuously" is the number that sizes this, and this card does not have it.
Provenance
Measured on origin/main at a56baa2bd, 2026-09-04. Related, same defect class: #15373 (no gate reads the CLI transcripts in content/docs, four published pages carried a stale number), #15357 (a gating rule shipped claiming "0 findings over the corpus" against a corpus that is not the app it names).
Activity
docs(audits): the self-test shape census — 179 instruments classified…
baozhoutao commentedon Sep 12, 2026
串行条件复核:旧的解除了,新的换了一个 ⇒ 本轮仍不派,⛔ 无状态变更
domain:devx执行 PM 席(#6023),session_012GKcPZbMoGq7WPzKLfRBTU。⛔ 本评论不是 claim:标签、assignee、pm:*一律未动,本卡留在pm:queue。上一任席位(
session_012zGPuVVX3deAx9LdjK8jCk)在5548955061里留了交接条件,并写明「any other seat picking it up should read the ruling comment above first」。本席读了那两条,然后量了那个条件当刻是否成立。上一任点名的两个前序 —— 都已离开
state=closed,merged=false⇒ 被放弃,不再占用AGENTS.mdmerged=true⇒ 已落地⇒ 上一任写下的那个串行条件已经解除。
AGENTS.md现在被另一个 PR 占着 —— 换了个前序本席扫了全部 22 个开着的 PR 的文件列表,
AGENTS.md命中恰好一个:docs(agents): Protocol Domains table names marketplace/ …,关闭的是 AGENTS.md Protocol Domains table still listsCloud | cloud/after #16325 relocates the directory tomarketplace/#17370,车道domain:skills。⇒ 上一任那条纪律的理由没变(两个席位同时改
AGENTS.md,第二个必然要在治理面上解冲突,而治理面是人工合并的终点站,冲突的代价比等待高得多)。⇒ 本卡剩下那半仍然串行排队,只是前序换成了 #17838。本轮的处置
⛔ 不派。 这不是「被跳过」,是那条纪律本身在起作用:
AGENTS.md里的治理契约(裁决5548566694的 Q1:「B, then C」,C 就是这一半)。GOVERNED_APPROVERS。⛔ 本席无论如何都不会武装它,也不会合它。AGENTS.md的 draft,等于给一个人工合并的队列制造一次冲突解决 —— 代价不对称:等待几乎免费,冲突要在治理面上重解一遍。下一个读到这张卡的人(包括本席)需要的三条
AGENTS.md上还有哪些开着的 PR —— ⛔ 不要引本评论当证据,这个读数会腐烂(⭐ 它是一个现在时的裸状态,和本班 [finding] the docs-audit union-read prose hard-codes the anchor lag as "532 keys" in two places — measured 604 four days later #16200 处理的那一族是同一种句子;本评论因此给它盖了时间:2026-09-12,22 个开着的 PR 之上)。5548566694,先读它 —— 特别是 Q1 的「option A 已被拒(为四个已知文件每个 PR 花掉数分钟去守)」与 Q2 的「一个没人读的 baseline 和一个 CI 从不求值的断言,正是本卡要治的那个缺陷类」。⛔ 不要重新发明方向。devx@objectstack车道。本席据此复核了本车道全部开着的 PR 与pm:dispatched卡,确认车道内不存在外来Claim:。⇒ 本卡的旧 claim(上一任席位、分支claude/issue-15410-self-test-shape-census,该分支在 origin 上已不存在)已了结,不构成占用;本席不派它的理由只有上面那条串行,⛔ 与认领归属无关。Generated by Claude Code
claude commentedon Sep 14, 2026
Claim: PM loop round 8
Session:
session_017ef78bLdybu3AffehKkhfkBranch:
claude/issue-15410-self-test-shape-contractWorktree:
objectstack-issue-15410Domain:
domain:devxFile surface:
AGENTS.md(the self-test shape contract — direction 1 / ruling5548566694's "C" half). ⛔ Nothing else without reporting first (stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opus(dispatch-gates --tier: no path-derived mandate; default tier is the review of record for this seat)Clause-②: no
Thread-read: 5646680868
Serial constraints cleared: ⭐ RE-MEASURED, ⛔ not carried over from comment
5646680868, which said in as many words that its own reading would rot. That comment (2026-09-12, over 22 open PRs) named PR #17838 as the standing occupant ofAGENTS.md. Measured again at 2026-09-14T19:13Z over 7 open PRs, reading each PR's own file list: 0 of 7 touchAGENTS.md— firing control, 4 of 7 touchpackages/**, so the instrument moves. ⇒ the occupant has left and the serial queue this card was parked in is empty. The two older predecessors that comment retired (#15290 abandoned, #15427 merged) stay retired. ⛔ Do not re-describe the serial as #15290/#15427.AGENTS.mdis the RULES layer. The PR stays draft, review is requested from both authorized approvers, and ⛔ this seat will NOT arm auto-merge and will NOT merge it under any circumstance. It lands by human approval or not at all.domain:devx执行席 · 座位贴 #6023 · claim written at 2026-09-14T19:13ZGenerated by Claude Code
claude commentedon Sep 14, 2026
os-dev-report
{ "issue": 15410, "status": "blocked", "branch": "claude/issue-15410-self-test-shape-contract", "pr": "https://github.com/objectstack-ai/objectstack/pull/18227", "premise_still_valid": true, "summary": "Direction 1 / ruling 5548566694's C half is WRITTEN and pushed: one new AGENTS.md section (+41 lines, no deletions, diff is exactly one file) stating the self-test shape as a judgeable contract — the two orthogonal holes (no assertion floor, no verdict handshake), what a floor must pin (battery NAMES with per-battery minimums, never one total, because a printed case count is evidence not proof), what a handshake must read (a module-level flag the verdict set) with the two ACCIDENT non-handshakes named (process.exit(selfTest()) and selfTest() === 0), the landed exemplar to copy (scripts/check-agent-model-declared.mjs, carrying SELF_TEST_BATTERIES + SELF_TEST_BATTERY_FLOOR + selfTestReachedVerdict), the explicit refusal of a shared assertion library, and the instrument to run. The first segment's sizing read is cited as a reading of that tree on 2026-09-05, never as a present-tense claim. BLOCKED ON ONE THING ONLY, and it is yours to rule: AGENTS.md stood at 1075 lines against a ceiling of 1075 — headroom 0 — so this is a ceiling raise 1075 -> 1116. I did NOT touch CEILINGS, did NOT delete any rule line, and did NOT re-wrap to buy lines; the ratchet is RED by design and the measured number is reported rather than manufactured green. Draft PR #18227 is open with review requested from both authorized approvers (os-zhuang, hotlong); ⛔ not armed, not enqueued, not merged, and it cannot merge until the raise is ruled. A ruling quoted in the PR body plus the one-line CEILINGS edit ['AGENTS.md', 1075] -> 1116 turns it green with no other change to the diff.", "tests": "Gate list derived IN the worktree at the real diff: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack -> exit 0, 14 commands, header line 'gate list derived from the tree of objectstack-ai/objectstack at commit 1bdbf82cb', --repo assertion held against this checkout's origin. All 14 run, exit code captured BEFORE any pipe (cmd > file 2>&1; EXIT=$?): 13 x exit 0, 1 x exit 1. GREEN: check-closing-keyword-parity (and its --self-test), check-comment-mask-corpus, check:agent-test-spelling, check:docs-audit-scope, check:driver-memory-census, 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. RED (the only finding in the whole run, verdict line quoted verbatim): 'check-skill-line-ratchet: AGENTS.md is 1116 lines; the ratchet ceiling is 1075. ... Raising a ceiling requires a maintainer ruling quoted in the PR.' Baseline reading on the clean tree before the edit, same gate: 'AGENTS.md is 1075 lines (ceiling 1075; headroom 0)'. Max-line-length rule checked independently over the inserted range 1029-1070 with a byte-length scan: 0 lines exceed 120 bytes. Control-byte self-scan outside the gate: grep -naP over AGENTS.md, exit 1 = no hits (exit captured without a pipe). Commit trailers: node scripts/check-commit-card-trailers.mjs --range origin/main..HEAD -> exit 0, '1 commit message(s) ... carry no card relation and no model identifier in the trailer pair'; the pre-push hook re-ran it on push and printed the same. skip-changeset judged by the sole criterion: no non-private package in packages/, services/ or apps/ ships AGENTS.md in files[] — zero hits, enumerated programmatically. Claim verification before writing: 'pnpm check:self-test-wired' does NOT exist as a root alias (CI invokes node scripts/check-self-test-wired.mjs directly in lint.yml:1571-1572) — caught and corrected before commit, since a fabricated command in AGENTS.md is exactly this card's defect class. --probe --only PATH confirmed present in the instrument's argv parser. PR body read back after creation and byte-compared against what was sent: identical, 6366 bytes, footer block whole, first line 'Fixes #15410', 'Clause-②: no' matching byte-exact on its own line. Label read back: skip-changeset present. NOT MEASURED: the 11 wide-population families, the 51 artifact-roster families and the 14 pending-changeset families dispatch-gates prints as outside its runnable total; CI runs them. No ablation and no reverse verification — nothing executable ships in this diff.", "mcp_calls": "0 — every GitHub read and write went through the REST proxy with curl. No MCP GitHub tool was called.", "api_writes": "5 — POST /repos/.../git push (branch, twice: the empty routing probe and the commit), POST /repos/objectstack-ai/objectstack/pulls (draft PR #18227), POST /repos/.../pulls/18227/requested_reviewers (os-zhuang + hotlong), POST /repos/.../issues/18227/labels (skip-changeset), POST /repos/.../issues/15410/comments (this report). Reads (issue, comments, 7 open PRs, per-PR file lists, PR read-back, label read-back) are not writes and are not counted.", "open_questions": [ { "question": "AGENTS.md was at headroom 0 (1075/1075) before this change and the contract is +41 lines. The dispatch said to stop and report rather than pay for it myself, so the ratchet is red and CEILINGS is untouched. Which way?", "options": [ "A — rule the raise: quote a ruling in PR #18227's body and edit ['AGENTS.md', 1075] -> 1116 in scripts/pm/check-skill-line-ratchet.mjs (one line). The gate goes green with no other change to the diff, and the PR is then a normal governed draft awaiting the two approvals it already requested.", "B — name a target line count and I compress to it, then re-report the measured number. ⚠️ Cost, stated rather than buried: the section is already at the shape's minimum — the floor rule, the handshake rule and the two ACCIDENT non-handshakes are each one paragraph and each is a separate judgeable clause. Below roughly 30 lines it stops answering 'how do I write one' and becomes the suggestion prose the card says is the problem.", "C — move it out of AGENTS.md into a referenced file (.claude/skills/ or docs/) and leave a one-line pointer behind. ⚠️ This changes what the card asked for: the card's own framing is that the convention is mandatory and mechanically enforced while the shape is written NOWHERE an author is required to read. A pointer into on-demand detail is a weaker instrument than a rule in the rules layer, and it would also need its own ceiling since the .claude/ files are ratcheted too.", "D — pay by cross-file move (the one raise an author may take alone: destination raise at most the net source decrease, ruling cited). ⛔ NOT taken, and not recommended: it requires editing a second governed file in this PR, which the dispatch forbids, and every note in the ratchet's own map records AGENTS.md as not a CROSS_FILE_MOVES destination." ], "recommendation": "A. The ratchet's own failure text names exactly one legal exit for growth — 'Raising a ceiling requires a maintainer ruling quoted in the PR' — and this is the case that exit exists for: a rule the repo mandates and mechanically enforces, whose shape was written down nowhere, landing in the rules layer. 41 lines is the second-smallest section in the file's back half and is one-time; the alternative the ratchet suggests (push detail out to references/) is option C, whose cost is that the contract stops being where the author is required to look." } ], "out_of_scope_findings": [ "to file (class (a), reproducible defect; dedupe words: measure-self-test-floor, classifyHandshake, LANDED_HANDSHAKE_NAMES, handshake census incomplete, #14968): scripts/measure-self-test-floor.mjs REFUSES on today's origin/main (base 1bdbf82cb) and prints no census at all, on BOTH entry paths — the bare invocation and --probe --only PATH. Its own words: 'THE HANDSHAKE CENSUS IS NOT COMPLETE -- no census printed. 2 file(s) carry a landed handshake spelling the recogniser reads as `none`: scripts/check-workflow-step-name-quoting.mjs, scripts/pm/check-prior-rulings.mjs ... Widen classifyHandshake and LANDED_HANDSHAKE_NAMES together, with a control on each.' exit 1 both times. ⚠️ This is the tool refusing CORRECTLY rather than lying, and it is the tool AGENTS.md now points authors at — so the reference is still right, but the instrument is unusable for a census until the recogniser is widened. ⛔ Not repaired here: out of the dispatch's declared file surface (AGENTS.md only), and it is a code change in a different file. NOT deduped by me — the message names #14968 as the card about this coupling, so triage should grade it against that before filing.", "noted, not filed: AGENTS.md's two existing --self-test mentions (the cross-package-test-inputs paragraph and the stash-hook paragraph) both say 'add a --self-test case' and neither points at the new section. A cross-reference would cost zero lines (the host line has byte headroom under the 120-byte rule), but the first sits immediately after a block that check-published-list-mirrors holds byte-equal to a gate's failure text, so the edit carries mirror risk for a discoverability gain the new section's own heading already provides (it contains the literal `--self-test`). 承接者: the next PR that edits either paragraph for its own reasons.", "noted, not filed: the card body's own three classes do not partition its stated population (20 + 37 + 96 = 153 against 170). Already recorded by the first segment in comment 5548544061 and in docs/audits/2026-09-self-test-shape-census.md; re-reported here only so this round is not read as having missed it. 承接者: 无 — the card's numbers were superseded by the behavioural census and nothing reads them now." ] }Generated by Claude Code
claude commentedon Sep 14, 2026
⛔ 进决策箱 —— 这是人工地板项,
pm:dispatched→needs-user-decisiondomain:devx执行席 · 座位贴 #6023 · 取数时刻 2026-09-14T19:34Z方向 1 的契约已经写好并推上去了(draft PR #18227,
AGENTS.md+41 / −0,一个文件),但它落不下来,卡在一件本席无权裁的事上。事实(本席对着 GitHub 与树逐条核过,⛔ 不采信报告自述)
门禁的失败原文点名了唯一一条合法出路:
⛔ 为什么本席不裁
章程的人工地板逐字列着「门禁削弱:降阈值、删必查项、抬 ratchet 上限、跳过测试」。抬上限就在名单上。 ⇒ 这不是席位的判断题,⛔ 本席不代裁、也不绕开。
⭐ 同样要说清:dev 没有制造一个假绿。它没动
CEILINGS、没删任何规则行、没有为了买行数去重排版,而是让 ratchet 红着并把实测数字报上来。这是对的处置。四个选项(dev 给的,本席核过代价)
check-skill-line-ratchet.mjs里那一行。门禁转绿,diff 其余部分不动,PR 随即变成一个等两个批准的普通受管 draft。AGENTS.md,放进被引用的文件,原地留一行指针。.claude/那些文件同样有 ratchet,还得再抬一次。AGENTS.md记为不是CROSS_FILE_MOVES的目的地。dev 推荐 A,本席同意并给出席位意见:ratchet 的失败文本自己写明抬上限就是留给这种情形的出口,而这正是那种情形 —— 一条仓库强制执行、却从未写下形状的规则,落进规则层。41 行一次性,是该文件后半部第二小的节。⛔ ratchet 建议的另一条路(把细节推到
references/)就是选项 C,其代价是契约不再待在作者被要求看的地方。现状
os-zhuang与hotlong两个授权批准账户 request review;正文带## 维护者速读(草稿)。AGENTS.md是规则层,靠人工批准落地或不落地。needs-user-decision,受理人保留。裁定回来后:A ⇒ 改那一行 + 在 PR 正文引裁决 ⇒ 转回等批准;B/C ⇒ 退回 dev 重做。Generated by Claude Code
os-elon-musk commentedon Sep 15, 2026
Ruling: batch #135 item 4 · letter B (target ≤ 24 lines; ceiling raised by the measured count) · maintainer 「135 同意」 2026-09-15T15:02Z
Director seat, session
session_01WCEaPsmKY4UyoivKkkaUHt. Presented on 2026-09-15 with this seat's own recommendation B — compress to the judgeable clauses, target ≤ 24 lines, and raise the ratchet ceiling by exactly the measured count; the maintainer's one-line reply adopts it. The ratchet raise (a human-floor item) is thereby ruled for that amount;os-zhuang's review question on PR #18227 (「这个关键文件没有长度限制吗?」) is answered by this ruling.What is ruled
AGENTS.mdsection is compressed to its judgeable clauses: the assertion-floor rule (a self-test pins battery NAMES with per-battery minimums, never one total), the verdict-handshake rule (the verdict reads a module-level flag the batteries set), and one line pointing at where the two ACCIDENT non-handshakes and the how-to live (docs/audits/2026-09-self-test-shape-census.md; thescripts/measure-self-test-floor.mjsdocblock). Target ≤ 24 lines including the heading. The two ACCIDENT examples move to the referenced audit document, ⛔ not deleted.scripts/pm/check-skill-line-ratchet.mjs['AGENTS.md', 1075]is raised by exactly the measured line count of the compressed section, and PR docs(agents): write down the shape a --self-test must have to be capable of failing #18227's body quotes this ruling verbatim: 「135 同意」 on batch 🔗 Broken links detected in documentation #135 item 4, presented as 「B,目标 ≤ 24 行,并按实测行数抬上限」. ⛔ No other ceiling moves; ⛔ no cross-file move (D).Execution
needs-user-decision→pm:queue,domain:devx; assignee cleared (queue cards carry none — the presence bit returns with the next claim). The lane seat re-dispatches on the existing branchclaude/issue-15410-self-test-shape-contract/ PR #18227 (currently ready-for-review withCHANGES_REQUESTED); the PR lands only on the two authorized approvals under the governed path, ⛔ never armed or merged by a seat.Prior rulings read: self-test,repo,mandates,gates,wired,documents,nothing,shape,hand-rolled,assertion,helpers,shown (+2 more) → 101 hits; ADR-0076 D3, ADR-0087 D2, ADR-0119 D3, ADR-0120 D2/D5 — none governs this card; ruling 5548566694 (Q1 「B, then C」) and the ratchet's own failure text 「Raising a ceiling requires a maintainer ruling quoted in the PR」 are the governing lines.
Generated by Claude Code
os-try-charles commentedon Sep 15, 2026
Claim: PM loop round 10 — 按裁定重派(⛔ 不是新一轮,是同一条分支上的返工)
Session:
session_017ef78bLdybu3AffehKkhfkBranch:
claude/issue-15410-self-test-shape-contract(既有分支,headbb16e496f)Worktree:
objectstack-issue-15410Domain:
domain:devxFile surface:
AGENTS.md(压缩)·docs/audits/2026-09-self-test-shape-census.md(接收搬过去的两个 ACCIDENT 例子)·scripts/pm/check-skill-line-ratchet.mjs(只抬['AGENTS.md', …]那一个数)(stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opusClause-②: no
Thread-read: 5682595374
Serial constraints cleared: ⭐ 维护者已裁,本轮是执行裁定,⛔ 不是重新提问。 裁定在评论
5682595374(总监席呈报,维护者逐字「135 同意」,batch #135 item 4,2026-09-15T15:02Z):取 letter B —— 压到可判子句,目标 ≤ 24 行,并按实测行数抬上限。该裁定同时回答了os-zhuang在 PR #18227 上的 CHANGES_REQUESTED 提问「这个关键文件没有长度限制吗?」。AGENTS.md是规则层,PR 只靠两个授权批准落地,⛔ 本席永不武装、永不合并。5669326134)的 dev 交付后卡在人工地板上,已由裁定解开;那条认领了结,⛔ 不构成占用。分支与 PR 都还在,本轮在其上返工。domain:devx执行席 · 座位贴 #6023 · claim written at 2026-09-15T22:14ZGenerated by Claude Code
os-try-charles commentedon Sep 15, 2026
os-dev-report
{ "issue": 15410, "status": "done", "branch": "claude/issue-15410-self-test-shape-contract", "pr": "https://github.com/objectstack-ai/objectstack/pull/18227", "premise_still_valid": true, "summary": "Rework on the existing branch executing maintainer ruling 135 同意 (decision batch #135 item 4, comment 5682595374), letter B. The AGENTS.md --self-test section is compressed 41 -> 24 lines including the heading, keeping exactly the three ruled clauses: the assertion floor pins battery NAMES with a per-battery minimum (never one total); the verdict sets a module-level flag the dispatch refuses on when unset; and one pointer line at docs/audits/2026-09-self-test-shape-census.md and the scripts/measure-self-test-floor.mjs docblock. The two worked non-handshake shapes were MOVED into that audit document (new subsection under '### The ACCIDENT'), not deleted. scripts/pm/check-skill-line-ratchet.mjs CEILINGS ['AGENTS.md', 1075] -> 1099, exactly the measured count; ['AGENTS.md', 768] and every other entry untouched; no existing rule line deleted, no re-wrap used as currency, no cross-file move (option D). The branch was merged with origin/main so the gate derivation is not taken from a stale tree. PR body now quotes the ruling verbatim, keeps line 0 'Fixes #15410' and the standalone 'Clause-②: no', and 维护者速读(草稿) is rewritten to the new shape. os-zhuang re-requested for review (both authorized approvers now requested). PR left ready-for-review as another actor set it; not readied, not enqueued, not merged, auto-merge not armed.", "measurement": { "compressed_section_lines_including_heading": 24, "agents_md_lines_origin_main": 1075, "agents_md_lines_this_branch": 1099, "ceiling_before": 1075, "ceiling_after": 1099, "raise": 24, "raise_equals_measured_count": true, "independent_check": "git diff origin/main...HEAD --numstat reads '24\t0\tAGENTS.md'", "note": "The ruling's single pointer line is TWO physical lines: as one line it measured 170B and the same gate's second axis fired — '✗ check-skill-line-ratchet: AGENTS.md has 1 line(s) over the 120-byte budget: L1048 (170B)'. Wrapping it at a legal break point cost the 24th line. 24 is the measured minimum that keeps all three clauses judgeable AND satisfies the 120-byte per-line budget; it is within the ruled target of ≤ 24, so no re-presentation is owed." }, "accident_examples_moved": { "destination": "docs/audits/2026-09-self-test-shape-census.md", "section": "### The ACCIDENT (line 123) -> new subsection '#### The two non-handshake shapes — the worked examples, held here' at line 130", "defeated_shape_line": 138, "accident_shape_line": 140, "readings": { "positive_control '### The ACCIDENT'": 1, "'process.exit(selfTest());' in the new block": 1, "'selfTest() === 0'": 1, "fire_control 'selfTest() !== 0' (must be absent)": 0 } }, "tests": "GATE DERIVATION: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, run on the MERGED tree (the first derivation refused quietly-wrong output: '⚠️ STALE TREE — ... 47 commit(s) behind origin/main', so origin/main was merged in first). Changeset derived by the tool itself, 3 paths: AGENTS.md, docs/audits/2026-09-self-test-shape-census.md, scripts/pm/check-skill-line-ratchet.mjs. 45 commands derived. ALL 45 RUN, ALL EXIT 0. Reconciliation with --ran (exit codes recorded, ' :: exit N' format): 'Run reconciliation — 45 derived, 45 run, 0 NOT-MEASURED, 0 UNRUN.' / '✓ dispatch-gates --ran: 45 derived famil(ies) accounted for — 45 run, 0 NOT-MEASURED (a DERIVED zero — all 45 recorded an exit code and none of them is 3).' Every exit code captured as 'cmd > log 2>&1; EXIT=$?', never across a pipe. RATCHET RED -> GREEN, both verdicts quoted. BEFORE (at bb16e496f): '✗ check-skill-line-ratchet: AGENTS.md is 1116 lines; the ratchet ceiling is 1075. ... Raising a ceiling requires a maintainer ruling quoted in the PR.' INTERMEDIATE (after compression, before ceiling edit): '✗ check-skill-line-ratchet: AGENTS.md has 1 line(s) over the 120-byte budget: L1048 (170B).' AFTER: '✓ check-skill-line-ratchet: AGENTS.md is 1099 lines (ceiling 1099; headroom 0).' + '✓ check-skill-line-ratchet: AGENTS.md: widest table row is 768 bytes (pin 768; headroom 0).' + '✓ check-skill-line-ratchet self-test: 157 cases pass.' Gate exit 0. PREREQUISITE NOT MET, handled not reported as red: pnpm --filter @objectstack/lint run check:doc-formula-expressions first exited 3 ('Nothing was measured: this gate exited before running a single check'). Built its two prerequisites under the shared verify lock — OS_VERIFY_LOCK_SLOT=issue-15410-r2, 'VERDICT command-exit 0 · held the lock 196s · waited 0s' (shared-box seconds) — then re-ran: exit 0. LINT, narrowed with the three required readings: (1) population read from eslint.config.mjs itself — base block is files: ['**/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs}'] and no markdown/MDX plugin is configured, so AGENTS.md and the audit document are outside ESLint's population entirely; exactly one changed file is in it. (2) file count from --format json: 1 file linted, 0 errors, 0 warnings, exit 0 (eslint --no-inline-config scripts/pm/check-skill-line-ratchet.mjs). (3) immutability: this repo runs one eslint.config.mjs which 'never enables type-aware linting (no parserOptions.project, no typed @typescript-eslint rules) for ANY file' (its own QUERY_OPTIONS_TEST_GLOBS header), so this diff cannot move the verdict on any untouched file. Repo-wide pnpm lint declared to CI. ROSTER FAMILIES the tool marked '⛔ roster under scripts/ or scripts/pm, which one of your paths is in' (silence is evidence in neither direction) — run anyway: check-published-list-mirrors 0, check-skills-token-ratchet 0, check:console-injection 0, check:dts-closure 0, check:engine-double-contract 0, check:i18n-stale-fill 0, check:pm-label-desc-cap 0. check:published-readme-exports exit 3 = PREREQUISITE NOT MET (44 packages not built): NOT MEASURED locally, reason = it reads built type entries and a whole-workspace build is CI's run; this diff touches no package source. CONTROL BYTES: grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]' over all three changed files — no match; check-nul-bytes OK (8662 text files). NO ABLATION: this card lands prose plus one ratchet integer. There is no new guard to mutate, so no ablation is owed; the ratchet's own red->green transition across the ceiling edit is the falsifiability evidence, and it is recorded above with the pre-edit red and post-edit green from the same gate. TYPECHECK/PACKAGE TESTS: no package source touched; no package is in the diff's dependency closure. scripts/pm/check-skill-line-ratchet.mjs has no dedicated test suite (git grep over *.test.* finds none); its coverage is its own --self-test, run green above (157 cases), plus check:ratchet-remedy-authority (exit 0), check-published-list-mirrors (exit 0) and check-skills-token-ratchet (exit 0), which all read it. MEASURED ON: final commit 1c74d5785 (git rev-parse --short HEAD after the merge), which is the pushed head.", "mcp_calls": "0 — no MCP GitHub tool was called at any point in this run", "api_writes": "4 REST proxy writes + 1 git push. git push origin claude/issue-15410-self-test-shape-contract (2 commits: 2fca7cbb4 the compression, 1c74d5785 the origin/main merge). PATCH /repos/objectstack-ai/objectstack/pulls/18227 (body) — HTTP 200. POST /repos/objectstack-ai/objectstack/pulls/18227/requested_reviewers — first attempt HTTP 415 (missing Content-Type: application/json) wrote NOTHING; retried with the header, HTTP 201, requested now [hotlong, os-zhuang]. POST /repos/objectstack-ai/objectstack/issues/15410/comments (this report). NO label write this round: read-back shows [documentation, size/s, skip-changeset] already present and still correct (union(read, target) has nothing the read-back lacks), and skip-changeset still holds — AGENTS.md, docs/audits/** and scripts/pm/** are shipped by no package's files[]. ⚠️ PR body read-back: the stored body is byte-identical to what was sent EXCEPT that the platform appended a second, bare '_Generated by [Claude Code](https://claude.ai/code)_' block after the session-URL footer on the REST update. Both blocks survive; per the AGENTS.md footer clause the footer was NOT re-posted in a loop. Line 0 is 'Fixes #15410', 'Clause-②: no' is on its own line, and both ruling quotations verify present in the stored body.", "open_questions": [], "out_of_scope_findings": [ "noted, not filed: scripts/measure-self-test-floor.mjs REFUSES on today's origin/main and prints no census — 'THE HANDSHAKE CENSUS IS NOT COMPLETE -- no census printed', naming 2 files whose landed handshake its recogniser reads as `none` (scripts/check-workflow-step-name-quoting.mjs, scripts/pm/check-prior-rulings.mjs) and pointing at #14968. Reproduced this run: exit 1. It refuses rather than publishing a flattering number, which is correct behaviour, but the pointer line AGENTS.md now carries resolves today to a readable how-to (the docblock + the audit document) and NOT to a runnable census. Not filed because the instrument already names its own tracking card: 承接者 = #14968. The PR body states this limitation truthfully rather than claiming the tool prints a census.", "noted, not filed: pnpm check:published-readme-exports exits 3 (PREREQUISITE NOT MET — 44 packages whose built type entry it would read are not built) on a fresh worktree install. This is the gate's own declared refusal shape, not a defect, and clearing it needs a whole-workspace build that belongs to CI. 承接者: CI's Lint & Repo Gates job, which builds first." ] }Generated by Claude Code
os-try-charles commentedon Sep 15, 2026
复核裁决:ACCEPT —— 裁定逐条执行到位;⚠️ 有一处超出裁定枚举的内容,我不替维护者放行,原样呈上
domain:devx执行席 · 座位贴 #6023 · PR head1c74d5785· 读数取自origin/main与 PR,⛔ 不取报告自述 · 取数时刻 2026-09-15T22:53Z一、算术是对的,而且是我自己量的
裁定要求「按实测行数抬上限,不多不少」—— 对上了。
二、那第 24 行不是凑数,是同一道门禁的第二条轴逼出来的
裁定要「一行指针」。dev 报称把它写成一行时是 170B、撞上 120 字节/行的预算。我自己量:把那两行接起来是 169 字节(dev 报 170,差 1,应是换行符;⛔ 结论不变),而
check-skill-line-ratchet确实带一条 120 字节的逐行预算(该脚本:185起有一整段「Why 120 bytes」的论证)。⇒ 一行装不下是门禁自己规定的,不是作者偷懒。 24 落在裁定的 ≤ 24 上,⛔ 不超,所以不欠重新呈报。
三、「搬走而不是删掉」—— 两个方向都量了
一边少、另一边多、暗对照为零 ⇒ 这是搬运,⛔ 不是删除。裁定这一条成立。
四、⚠️ 我不放行、也不遮掩的一处:交付里有第四块,裁定只枚举了三块
裁定逐字枚举的是:断言下限规则 · verdict handshake 规则 · 一行指针。
交付的 24 行里有四块:
席位意见(⛔ 不是裁定):第 3 块带着两样卡片自己点名过的东西 —— 可抄的范例(
check-agent-model-declared.mjs),以及 ⛔ 不要做共享断言库那条拒绝(卡面正文明写「Not a shared assertion library」,理由是 170 个仪器的单点故障)。砍掉它,契约就只剩「钉名字、置 flag」,没有范例可抄,也没有拦住作者最容易伸手去拿的那个错解法。但代价要说清:它占了 24 行里的 4 行,也就是永久天花板里多出的 4 行;没有它大约落在 20 行 / 抬 20。抬 ratchet 上限是人工地板项,所以「没超预算」⛔ 不自动等于「在授权范围内」。
⇒ 本席不替维护者裁这 4 行。 它会随批准一起到你面前(你是两个授权批准账户之一,本 PR 不经你批不可能落地),要砍在评审里说一句即可,dev 再压一版、天花板同步降到实测值。
五、入队前检(⛔ 但本 PR 本席永不入队)
Fixes #15410第 0 行 · 逐字节Clause-②: no恰一行 · 裁定逐字引文在案(「135 同意」×1、「B,目标 ≤ 24 行,并按实测行数抬上限」×1、「batch #135」×1)—— 这是 ratchet 失败文本自己要求的,少了它门禁仍红 ·维护者速读在案 · 两个授权批准账户(hotlong、os-zhuang)都已 request review ·draft=false、auto_merge=false✔。绿判据按 check 名去重取最新:39 次运行 → 32 名 → 仍有 1 个 in_progress(
Lint & Repo Gates,正是跑 ratchet 的那个)。AGENTS.md是规则层,它只靠两个授权批准落地,或者不落地。六、两条我认可的处置
origin/main并进分支,因为第一次dispatch-gates拒绝对一棵落后 47 个提交的树安静作答(「a well-formed answer about a tree nobody is on」),而它取 family 的 15 个文件在那段区间里变过。⭐ 这是对的:在陈旧树上推出来的门禁清单,green 得毫无意义。measure-self-test-floor.mjs今天跑不出 census 这件事,dev 在 PR 正文里如实写了,没有把「指针指向一个能跑的工具」说成既成事实。那个工具自陈识别器不完整并点名 No canonical way to ask whether a self-test has a verdict handshake — the repair landed in three spellings and measure-self-test-floor reports none of them #14968 —— 它拒绝而不是给一个好看的数字,是对的;但新契约的指针今天解析到的是一份可读的 how-to,⛔ 不是一次可跑的普查。Generated by Claude Code
os-try-charles commentedon Sep 16, 2026
✅ PR #18227 已落地 —— 同笔摘
pm:dispatched+ 清 assigneedomain:devx执行席 · 座位贴 #6023 · 落地两读,⛔ 不看merged布尔、⛔ 不看 head-sha 祖先性 · 取数时刻 2026-09-16T07:13Z读数一 · 队列分支:
gh-readonly-queue/*现为空,本 PR 的队列分支已消失。读数二 · 重新 fetch 的
origin/main(8b81ab60d)内容探针scripts/pm/check-skill-line-ratchet.mjs:1383现为['AGENTS.md', 1099],而AGENTS.md实测 1099 行 —— 上限由 1075 抬到 1099,余量归零处正好贴合(第二根轴['AGENTS.md', 768]未动)闭合:PR 正文首行⚠️ 但标签与受理人没有跟着走 —— 本笔补上,这正是
Fixes #15410⇒ GitHub 已把本卡关为closed completed。pm:dispatched半状态的形状。⭐ 本 PR 是治理面,由
os-zhuang批准后人工合并,⛔ 本席全程未武装、未入队。状态
pm:dispatched摘除,assignee 清空,其余标签保留。Generated by Claude Code
docs(pm): a lit control drawn with the same instrument is not a contr…
fix(pm): bind H31's carrier pair through the closing keyword (objects…
docs(agents): write down the shape a --self-test must have to be capa…
chore(gates): retire check-type-source-resolution (maintainer ruling …