docs(agents): a claim pre-check hit is a hint, not a claim - #18827
Conversation
AGENTS.md rule 2 handed the dev `git ls-remote --heads origin | grep issue-<n>` as a one-command pre-check and said only what the command IS, never what a HIT means. The reading rule stood only in the fact layer (pm-dispatch references), which the dev definition never points at, so the layer holding the rule had no reader and the layer with the reader had no rule. The failure direction is the silent one: a stale head reads as "already claimed" and nothing goes red. One clause on the existing sentence states the reading. Paid in-file: the Skills section's restatement of the governed-surface queue mechanism retires, since Prime Directive #14 already carries it. Co-authored-by: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu
维护者速读(终稿)改了什么: 为什么改:这条读法规则本来就在( 风险与代价(含回滚):纯文本、单文件、净零行;不加门禁、不改那条命令本身;14 个派生门禁加 3 个点名门禁加全仓 lint 均为 0。回滚 = revert 这一个 commit。 席位意见:通过,按 dev 交付的原样。 你要做的:批准本 PR( Generated by Claude Code |
Fixes #18811
Clause-②: no
The defect
AGENTS.mdrule 2 hands the devgit ls-remote --heads origin | grep issue-Nas a one-command claim pre-check and states only what the command IS — never what a HIT MEANS. The reading rule was not missing from the repo; it was in a layer this reader is never sent to. It stands today in the fact layer,.claude/skills/pm-dispatch/references/platform-readings.md:378, while the dev agent definition.claude/agents/os-dev.mdpoints atAGENTS.mdeight times and carries the stringsls-remoteandClaim:zero times. So the layer holding the rule has no reader, and the layer with the reader has no rule. The failure direction is the silent one: a stale head reads as "already claimed", a live card is skipped, and nothing goes red — and most heads are stale, because a dev container can create a remote branch but cannot delete one.Re-derived readings
Taken in this worktree, not carried from the card or the triage.
Head census — re-taken
git ls-remote --heads origin 'refs/heads/claude/issue-*' | wc -lat 2026-09-17T22:52Z, exit 0:claude/issue-*heads onorigin⭐ The population moved DOWN by two between 21:45Z and 22:52Z (291 pre-existing, then 289 pre-existing plus this branch). Both vanished heads were headless ones. The census is a moving reading, which is itself the argument for a clause that states a direction rather than a number.
The split — re-taken on the same window the seat used
Newest 1,200 PRs,
state=all, back to 2026-09-05T14:45Z; read at 2026-09-17T22:54Z:The two readings are the same population less the two deleted heads, plus this PR's own (PR-less at census time). Roughly 88% of hits are stale in both.
The two pointer readings on
.claude/agents/os-dev.mdOn
a7bafc29af:git grep -c 'AGENTS\.md' -- .claude/agents/os-dev.md.claude/agents/os-dev.md:8git grep -n 'ls-remote|Claim:' -- .claude/agents/os-dev.mdgit grep -c 'worktree' -- .claude/agents/os-dev.md.claude/agents/os-dev.md:9git grep -n 'platform-readings' -- .claude/agents/os-dev.md:54, and only for the single-card read spellingThe lit control is what makes the empty reading a real absence rather than a mis-typed pattern: the same grep over the same file finds 9 hits for a word that IS there. And
.claude/agents/os-dev.md:19 is the pointer itself, verbatim and untranslated:The clause
One sentence, appended to the existing sentence in the same bullet — not a new paragraph, and no second read added to the one-command pre-check. Verbatim:
112 characters / 116 bytes. It lands on rule 2 at what is now
AGENTS.md:463–:464, and costs one line.It does two things the pre-check could not do by itself: it names the wrong action (skipping a card on a hit) rather than only describing the instrument, and it names the record that DOES decide — the
Claim:comment, whichAGENTS.md:393–:395 already defines ("the identity record is theClaim:comment — first line beginningClaim:"). The clause therefore adds a reading, not a mechanism.Two declared deviations from the dispatch
pnpm check:pm-skill-id-lintscans this file for the issue-number half of it. So the clause says "most heads carry no PR" and the dated census lives here, in the PR body, and in the fact layer. This also keeps the clause from rotting — the census moved twice today.AGENTS.mdalready points at pm-dispatchreferences/platform-readings.mdtwice, at :431 and :450, both times for exactly "the readings". A third pointer measured 2 added lines instead of 1 (and wrapped the code span across a line break), i.e. it would have cost a second retired line to restate a pointer the file already carries. The defect the card names is that the RULE was not in the reader's layer; the rule is now there.The payment
AGENTS.mdwas at 1099 / 1099 — no headroom. The clause adds one line, so one line retires. No re-wrap is used as currency: the line is bought by deleting ~109 characters of content, and the surviving sentence is shorter than either line it replaces.Retired —
AGENTS.md:799–:800, two lines:Now — one line:
Where the retired content survives, in this same file:
.claude/**andskills/**in the governed-surface listThe surviving sentence keeps the pointer (a reader in the Skills section still learns both roots are governed and where the rule is); what retires is the restatement of the mechanism.
⛔ The ratchet's widest-row pin is untouched — no table row is edited.
AGENTS.md: widest table row 768 bytes (pin 768) before and after.AGENTS.mdlinesAGENTS.mdwidest table rownode scripts/pm/check-skill-line-ratchet.mjsgit diff --stat:1 file changed, 3 insertions(+), 3 deletions(-)— net zero lines.The victim search the triage left
Report-only. Zero state writes — no card touched, no label, no branch deleted.
Method. All 290
claude/issue-*heads mapped to card numbers (281 distinct, 0 unparseable); the 256 with no PR in the 1,200-PR window re-checked per branch against the unboundedpulls?state=all&head=...endpoint (so the window is not a source of false "headless"); their card numbers intersected with the 518 open non-PR issues; each survivor's full comment list tested against the machine predicateCLAIM_COMMENT_MARKER= the regex atscripts/pm/check-half-states.mjs:1138, i.e.Claim:orClaimed:at line start, case-insensitive, blockquote allowed.Population: 27 open cards carry a confirmed same-number headless head. Result: the list is NOT empty — 9 rows. Read at 2026-09-17T22:55Z–22:56Z.⚠️ Every row may have left this state since.
ahead_bypm:on-holddomain:engineclaude/issue-8006-put-roundtrip-censuspm:on-holddomain:engineclaude/issue-9613-publish-gate-remeasuretrackingdomain:servicespm:blockingclaude/issue-11633-cross-request-cache-design## Claim + dispatchheadingpriority:p0securitytrackingdomain:servicespm:epicclaude/issue-11663-platform-admin-reanchor-design## Claim + dispatchheadingtoolingdomain:devxpm:on-holdclaude/issue-12799-surplus-thresholdpm:queuedomain:specpriority:p3claude/issue-14512-multi-package-artifact-single-copybugpm:queuedomain:clipriority:p3claude/issue-15638-ui-plugin-legacy-arm## Claimheading, "Claimed by the os-dev seat"bugpriority:p2pm:blockeddomain:engineclaude/issue-16184-schema-drift-media-column-ruled-end-statebugpriority:p2pm:blockeddomain:engineclaude/issue-17676-writable-package-durabilityThree readings that sharpen what this list is and is not:
ahead_by: 0— a zero-commit probe branch. By the fact layer's own two-read criterion (platform-readings.md:377: zero commits ahead AND no open PR), not one of them is in-flight work. On all nine, a pre-check hit today is false in the exact silent direction.put()— the option-1 tightening deferred by #7856 #8006, finding: the shipped runtime publish gate already costs linearly in tenant object count — 506 ms per flow publish at 420 objects, before any widening #9613, [finding] The type-check debt ledger banked 118 raw errors of surplus across 4 of 31 entries — the measurement #6376 lacked when it decided a surplus must not go red #12799, driver-sql(schema-drift): during #15989's dual-encoding window a staticJSON_COLUMN_FIELD_TYPEScannot serve both moved and unmoved deployments — ⚠️ the body's "reports the ruled end-state" framing is MEASURED FALSE, see comment 5588614136 #16184, A writable package created viaPOST /api/v1/packagesis registered in memory only — it does not survive a restart, and leaves its published metadata orphaned #17676. The other four carry a claim the machine predicate cannot see (a## Claimheading or prose) — see the finding below.pm:on-hold,pm:blocked,pm:blocking,pm:epic,tracking). Only A multi-package artifact serializes its metadata twice — the flattened top level and everypackages[i]body carry the same definitions #14512 and [finding]plugin-hono-serverstill accepts the legacyui-plugintype thatPluginSchemarefuses — an unreachable arm under ADR-0049 #15638 carrypm:queue, i.e. sit in the dispatch pool — and both of those carry a non-marker claim. So the honest verdict is what the triage predicted: the instrument is wrong by default on ~88% of hits, and nine open cards sit in precisely the state where that default fires; no card is demonstrably a victim because of its stray head.Gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, derived in this worktree with no hand-fed path list (change set: 1 path,AGENTS.md). Every exit code captured redirect-then-$?, never across a pipe.node scripts/check-closing-keyword-parity.mjsnode scripts/check-closing-keyword-parity.mjs --self-testnode scripts/check-comment-mask-corpus.mjspnpm check:agent-test-spellingpnpm check:docs-audit-scopepnpm check:driver-memory-censuspnpm check:nul-bytespnpm check:pm-governed-mergespnpm check:pm-governed-prosepnpm check:pm-skill-id-lintpnpm check:pm-skill-ratchetpnpm check:refd-timer-probepnpm check:required-contextspnpm check:watch-hint-literalReconciliation,
dispatch-gates.mjs --repo objectstack-ai/objectstack --ran, exit 0:Named by the dispatch, outside the derived union — all exit 0:
node scripts/pm/check-skill-line-ratchet.mjspnpm check:skill-frame-syncpnpm check:doc-authoringRepo-wide
pnpm lint(eslint . --no-inline-config) — exit 0, the FULL population, not a narrowing: 6,843 files linted, 0 errors, 0 warnings (count read from eslint's own--format jsonoutput). ⭐ Of those 6,843, zero are.md—eslint.config.mjsnames neither.mdnormarkdown, so this diff's single file is provably outside eslint's configured surface altogether.Control characters:
grep -naPover the control-byte class onAGENTS.md→ no match (exit 1), beyondpnpm check:nul-bytesabove.skip-changeset: nothing published moves. Measured, not assumed — no package in the workspace listsAGENTS.mdin itsfiles[](0 of all package manifests), and a repo-root instruction file ships in nothing.维护者速读(草稿)
改了什么 ——
AGENTS.md规则 2 里,给 dev 的认领前置检查命令后面加了一句话:一次命中只是线索,不是认领;大多数分支头根本没有 PR;⛔ 不要因为一次命中就跳过一张卡,真正作数的是卡上的Claim:评论。全文件只动这一句,外加一处为它付账的删减。为什么改 —— 这条读法规则本来就存在,但写在 dev 从来不被指去读的那一层(pm-dispatch 的事实层)。dev 被指去读的是
AGENTS.md(dev 定义里点名它 8 次,ls-remote与Claim:一次都没有)。结果是:有规则的那层没有读者,有读者的那层没有规则。而默认读法恰好是危险的那一侧 —— 实测 290 个分支头里 256 个没有任何 PR(约 88%),一个听话的 dev 在近九成命中上会把一张活卡读成「已被认领」而跳过,全程没有任何红色信号。本次另外跑了分诊席留下的受害者检索:9 张 open 卡此刻正处在这个状态,且它们的残留分支无一例外是零提交的探针分支。风险与代价(含回滚) —— 代价极低:纯文本、单文件、净零行(加一行、退一行),不动任何机制、不加门禁、不改那条命令本身。付账方式是删掉 Skills 一节里对「受管面排队机制」的重复叙述 —— 那段话在 Prime Directive #14 里已经原样存在(:257、:268–:274、:284),删的是重复,不是信息。棘轮 1099 / 1099 前后不变,最宽表格行 768 字节的 pin 未被触碰。全部 14 个派生门禁 + 3 个点名门禁 + 全仓
pnpm lint均为 0。回滚 = revert 这一个 commit,无迁移、无下游依赖。席位意见 ——
你要做的 —— 这是受管面(
AGENTS.md在GOVERNED_SURFACES内),需要您本人合并。请重点看两处:① 那句话的措辞是否就是您要 dev 读到的意思(尤其「⛔ never skip a card on one」这半句是不是过强);② 付账是否可接受 —— 即 Skills 一节保留指路、删掉机制复述这个取舍。另外报告里有两条本轮顺带测出、未在本 PR 修的发现(那条 grep 会匹配错卡号;Claim:拼写已经漂移成## Claim),按协议交由席位立卡。🤖 Generated with Claude Code
https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu
Generated by Claude Code