Skip to content

Commit f7e5231

Browse files
os-justinclaude
andauthored
docs(agents): a claim pre-check hit is a hint, not a claim (#18827)
Fixes #18811 Clause-②: no ## The defect `AGENTS.md` rule 2 hands the dev `git ls-remote --heads origin | grep issue-N` as 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.md` points at `AGENTS.md` eight times and carries the strings `ls-remote` and `Claim:` 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 -l` at **2026-09-17T22:52Z**, exit 0: | reading | value | |:---|:---| | `claude/issue-*` heads on `origin` | **290** — and that INCLUDES this PR's own branch, so **289** pre-existing | | seat census 2026-09-17T21:45Z | 291 | | the #18774 dev's re-take 2026-09-17T21:48Z | 291 | ⭐ 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**: | | re-derived (290 heads) | seat's reading (291 heads) | |:---|---:|---:| | head with an OPEN PR | 26 | 26 | | head with a closed / merged PR, branch never deleted | 8 | 8 | | head with NO PR | **256** | 257 | 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.md` On `a7bafc29af`: | command | result | exit | |:---|:---|---:| | `git grep -c 'AGENTS\.md' -- .claude/agents/os-dev.md` | `.claude/agents/os-dev.md:8` | 0 | | `git grep -n 'ls-remote\|Claim:' -- .claude/agents/os-dev.md` | no output | 1 | | **lit control** `git grep -c 'worktree' -- .claude/agents/os-dev.md` | `.claude/agents/os-dev.md:9` | 0 | | `git grep -n 'platform-readings' -- .claude/agents/os-dev.md` | one hit, `:54`, and only for the single-card read spelling | 0 | The 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: > 仓库根的 AGENTS.md 有约束力,第一次编辑前先读它。 ## 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: ``` A hit is a hint, not a claim — most heads carry no PR: ⛔ never skip a card on one, the `Claim:` comment decides. ``` **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, which `AGENTS.md` :393–:395 already defines ("the identity record is the `Claim:` comment — first line beginning `Claim:`"). The clause therefore adds a reading, not a mechanism. ### Two declared deviations from the dispatch 1. **The ratio is stated qualitatively, not dated.** The dispatch asked for the measured ratio, dated. This file's own preamble governs its authoring: *"A rule states what to do and what never to do, in one executable sentence; it carries no incident narrative, no ruling date or quotation, and no issue-number citation"*. A dated census in the rules layer is exactly the incident narrative that preamble excludes, and `pnpm check:pm-skill-id-lint` scans 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. 2. **No third pointer to the fact layer.** `AGENTS.md` already points at pm-dispatch `references/platform-readings.md` twice, 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.md` was 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: ``` ⛔ **Both roots are governed surfaces** — human-merge only, or queued under **Prime Directive #14**'s pinned-approval path; no per-PR check holds it: the queue guard refuses an unpinned governed diff at queue time. ``` Now — one line: ``` ⛔ **Both roots are governed surfaces** — human-merge only, or **Prime Directive #14**'s pinned-approval path. ``` Where the retired content survives, **in this same file**: | retired fragment | survives at | |:---|:---| | "the queue guard refuses an unpinned governed diff at queue time" | Prime Directive #14, :284 — *"Behind it: the queue guard refuses an unpinned governed diff"* | | "queued under ... pinned-approval path" as a mechanism | Prime Directive #14, :268–:274, which defines the approval, who may give it, and that the owning seat then lands | | "Both roots are governed" | Prime Directive #14, :257–:258, which enumerates `.claude/**` and `skills/**` in the governed-surface list | The 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. | ratchet | before | after | |:---|---:|---:| | `AGENTS.md` lines | 1099 (ceiling 1099, headroom 0) | **1099** (ceiling 1099, headroom 0) | | `AGENTS.md` widest table row | 768 bytes (pin 768) | **768 bytes (pin 768)** | | `node scripts/pm/check-skill-line-ratchet.mjs` | exit 0 | exit 0 | `git 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 **unbounded** `pulls?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 predicate `CLAIM_COMMENT_MARKER` = the regex at `scripts/pm/check-half-states.mjs` :1138, i.e. `Claim:` or `Claimed:` 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.** | card | labels at read time | stray head | `ahead_by` | claim in ANY spelling? | |:---|:---|:---|---:|:---| | #8006 | `pm:on-hold` `domain:engine` | `claude/issue-8006-put-roundtrip-census` | 0 | none | | #9613 | `pm:on-hold` `domain:engine` | `claude/issue-9613-publish-gate-remeasure` | 0 | none | | #11633 | `tracking` `domain:services` `pm:blocking` | `claude/issue-11633-cross-request-cache-design` | 0 | yes — `## Claim + dispatch` heading | | #11663 | `priority:p0` `security` `tracking` `domain:services` `pm:epic` | `claude/issue-11663-platform-admin-reanchor-design` | 0 | yes — `## Claim + dispatch` heading | | #12799 | `tooling` `domain:devx` `pm:on-hold` | `claude/issue-12799-surplus-threshold` | 0 | none | | #14512 | `pm:queue` `domain:spec` `priority:p3` | `claude/issue-14512-multi-package-artifact-single-copy` | 0 | yes — prose, "Claiming the producer half" | | #15638 | `bug` `pm:queue` `domain:cli` `priority:p3` | `claude/issue-15638-ui-plugin-legacy-arm` | 0 | yes — `## Claim` heading, "Claimed by the os-dev seat" | | #16184 | `bug` `priority:p2` `pm:blocked` `domain:engine` | `claude/issue-16184-schema-drift-media-column-ruled-end-state` | 0 | none (only a triage seat's ⛔ 不认领 disclaimer) | | #17676 | `bug` `priority:p2` `pm:blocked` `domain:engine` | `claude/issue-17676-writable-package-durability` | 0 | none | Three readings that sharpen what this list is and is not: 1. ⭐ **Every one of the nine stray heads is `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. 2. **Five are unclaimed in every spelling**: #8006, #9613, #12799, #16184, #17676. The other four carry a claim the machine predicate cannot see (a `## Claim` heading or prose) — see the finding below. 3. ⛔ **This is not proof anyone was harmed.** Seven of the nine are parked for reasons of their own (`pm:on-hold`, `pm:blocked`, `pm:blocking`, `pm:epic`, `tracking`). Only **#14512** and **#15638** carry `pm: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. | # | command | exit | |---:|:---|---:| | 1 | `node scripts/check-closing-keyword-parity.mjs` | 0 | | 2 | `node scripts/check-closing-keyword-parity.mjs --self-test` | 0 | | 3 | `node scripts/check-comment-mask-corpus.mjs` | 0 | | 4 | `pnpm check:agent-test-spelling` | 0 | | 5 | `pnpm check:docs-audit-scope` | 0 | | 6 | `pnpm check:driver-memory-census` | 0 | | 7 | `pnpm check:nul-bytes` | 0 | | 8 | `pnpm check:pm-governed-merges` | 0 | | 9 | `pnpm check:pm-governed-prose` | 0 | | 10 | `pnpm check:pm-skill-id-lint` | 0 | | 11 | `pnpm check:pm-skill-ratchet` | 0 | | 12 | `pnpm check:refd-timer-probe` | 0 | | 13 | `pnpm check:required-contexts` | 0 | | 14 | `pnpm check:watch-hint-literal` | 0 | Reconciliation, `dispatch-gates.mjs --repo objectstack-ai/objectstack --ran`, exit 0: > ✓ dispatch-gates --ran: 14 derived famil(ies) accounted for — 14 run, 0 NOT-MEASURED (a DERIVED zero — all 14 recorded an exit code and none of them is 3). Named by the dispatch, outside the derived union — all exit 0: | command | exit | |:---|---:| | `node scripts/pm/check-skill-line-ratchet.mjs` | 0 | | `pnpm check:skill-frame-sync` | 0 | | `pnpm check:doc-authoring` | 0 | **Repo-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 json` output). ⭐ Of those 6,843, **zero are `.md`** — `eslint.config.mjs` names neither `.md` nor `markdown`, so this diff's single file is provably outside eslint's configured surface altogether. Control characters: `grep -naP` over the control-byte class on `AGENTS.md` → no match (exit 1), beyond `pnpm check:nul-bytes` above. **`skip-changeset`**: nothing published moves. Measured, not assumed — no package in the workspace lists `AGENTS.md` in its `files[]` (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.com/claude-code) https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu --- _Generated by [Claude Code](https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent a09d797 commit f7e5231

1 file changed

Lines changed: 3 additions & 3 deletions

File tree

‎AGENTS.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,8 @@ Even inside your own worktree, operate defensively:
460460
straight to `main`.** Name the branch after the issue it fixes: `claude/issue-<n>-<slug>`.
461461
The issue number is what makes in-flight work *discoverable* — `git ls-remote --heads
462462
origin | grep issue-<n>` is a one-command pre-check, and the Duplicate Fix Guard
463-
workflow warns on fix PRs whose branch names no declared issue.
463+
workflow warns on fix PRs whose branch names no declared issue. A hit is a hint, not a
464+
claim — most heads carry no PR: ⛔ never skip a card on one, the `Claim:` comment decides.
464465

465466
⛔ **Off `main` is literal — a stacked series, each PR branched off the one below, is NOT a supported form.** No
466467
tooling represents it: squash landing destroys the ancestry link, so every descendant rewinds behind what landed and
@@ -796,8 +797,7 @@ working in its domain — browse the directory, never a hand-written list here:
796797
- `.claude/skills/` — repo-internal agent playbooks; every entry must carry
797798
`metadata.internal: true`.
798799

799-
⛔ **Both roots are governed surfaces** — human-merge only, or queued under **Prime Directive #14**'s pinned-approval
800-
path; no per-PR check holds it: the queue guard refuses an unpinned governed diff at queue time.
800+
⛔ **Both roots are governed surfaces** — human-merge only, or **Prime Directive #14**'s pinned-approval path.
801801

802802
---
803803

0 commit comments

Comments
 (0)