docs(agents): the os-dev terminal report carries its own session id - #18273
Conversation
The `os-dev-report` JSON gains a `session` field: the id the harness stamps on the run that writes the report, read from the transcript's Claude-Session line (a subagent's is its parent's). The added line is paid in place: the two `premise_still_valid: false` notes compress to one line, so the file stays at its 403-line ceiling with every edited line at or under 120 bytes. Claude-Session: https://claude.ai/code/session_01HZfg2AwVX191qCizp88gQr Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Reviewing seat: ① Derived judgments
② Semver levelNot applicable — nothing published moves; ③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
维护者速读(终稿)改了什么: 为什么改: 风险与代价(含回滚):纯规则文本,不碰发布包、不改 CI、行数不变; 席位意见:建议批准。本席在 head 上亲核:两处改动都在认领区域 :319–:398 内、PR #18250 的 :287/:301 字节不变;新行 117 字节、付费行 119 字节且三条规则都在;棘轮 403/403、id-lint / governed-prose 绿;H64 带字段/不带字段两向复现;本单的 dev 报告本身已是新契约的首个实例。CI 在 head 上跑中( 你要做的(一个动作):approve 本 PR。approve 后本席翻 ready + 入队。 Generated by Claude Code |
|
Ruling-C landing provenance — skills seat, session Generated by Claude Code |
Fixes #18264
The
os-dev-reportcontract in.claude/agents/os-dev.md(:319 the comment; :360–:377 the JSON shape) gains ONE field,session, so every dev report carries in its TEXT thesession_id that the same file's :51 makes the attribution — 「归属 = 文本里的 session ID,非user.login」 — and that the re-keyed H64 inscripts/pm/check-half-states.mjsreads. One governed file, two hunks, both inside the claimed REGION :319–:398 (PR #18250's :287 / :301 are untouched).check-half-states.mjsis not in the diff: P2 below is the measurement that says it does not need to be.Landing:
.claude/**is the rules layer of the governed surface — this PR stays a DRAFT; the seat hangs the four-piece after the report; an authorized approval lands it.skip-changeset:.claude/**publishes nothing (os-dev.md :297, the fast lane).What changed — 403/403, every edited line at or under 120 bytes
Line numbers are on this branch at
f60aba4b; B = bytes of the line as stored (LC_ALL=C awk '{print length($0)}'). The file spells the placeholder inside angle brackets, as the template always has; it is written here as ID.Added, :366 (117 B) — the fifth identity field, right after
pr:Paid, :379 + :380 → :380 (106 B + 66 B → 119 B) — a tighter rewrite of the adjacent note pair under the JSON block:
Why nothing operative was lost: the words that left are 你的 / issue 的 / 写 / 那时 / 为 / 的 and the clause 「PM 重新分诊」 — the rule-6 cross-reference, the evidence-in-
summaryinstruction and thepr-null-or-surviving-part rule all survive; 「PM 重新分诊」 is the PM's act, not a dev instruction, and SKILL.md 〈报告契约〉 already states it (「当再分诊输入复核」).wc -l= 403 before and after.pnpm check:pm-skill-ratcheton the head:✓ check-skill-line-ratchet: .claude/agents/os-dev.md is 403 lines (ceiling 403; headroom 0).The payment deviates from the suggested route, and says so
The ruling allowed 「a semantic no-op fold or a tighter rewrite of adjacent lines」. The fold was measured —
open_questions:372–:374 collapses to one 111 B line, JSON-identical — and NOT taken: os-dev.md :292–:293 (「付行数棘轮的唯一合法货币是删内容:⛔ 不拿 re-wrap(折行合并)当筹行,新增以删减付账」「分界只问折行有没有为新增内容买行」) and the ratchet ledger (「re-wrap funding is refused per the 2026-08-17 rule」) refuse a fold that buys a line for new content, and the file's own conflict clause makes the file win over the dispatch prompt. The tighter rewrite is the route's second option and the file's currency at once.Adjacent-pair measurement of the region, the way the SKILL.md precedent did it: no two adjacent prose lines concatenate under 120 B — :319+:320 = 223, :320+:321 = 200, :321+:322 = 207, :322+:323 = 202, :323+:324 = 210, :324+:325 = 178, :378+:379 = 198, :379+:380 = 172, :380+:381 = 160, :381+:382 = 203, :386+:387 = 231, :387+:388 = 225, :388+:389 = 183, :389+:390 = 171, :390+:391 = 210, :391+:392 = 221, :392+:393 = 195 (pre-edit numbering). So every 2→1 merge deletes words, and :379+:380 is the one pair whose deletable words carry no dev rule. Candidate prose lines for a SECOND added line (the no-MCP route and the subagent case, in Chinese) measured 134–141 B in every spelling tried, over the cap — which is why the whole instruction lives in the one JSON line.
P1 — how a dev actually obtains its id (measured in this run,
mode:subagent)Claude-Session:line)session_01HZfg2AwVX191qCizp88gQrget_sessionwith no argument (the Claude Code Remote MCP — not a GitHub tool)session_01HZfg2AwVX191qCizp88gQr, titlePM dispatch skillsCLAUDE_CODE_REMOTE_SESSION_IDcse_01HZfg2AwVX191qCizp88gQr— the same 24-character tail under acse_prefixCLAUDE_CODE_SESSION_IDd13ed524-f222-55f1-9c04-ff395efe6ed7— a UUID, not asession_01form; H64 cannot read itCLAUDE_CODE_CHILD_SESSION1P1 is half-falsified. A subagent has no
session_01…id of its own: every route the harness offers stamps the PARENT session's id — the same id it already puts on the dev's commit trailer and PR-body footer (os-dev.md :286 / :398). So inmode:subagentthe only value that can be filled honestly equals the dispatching PM's id by VALUE; what the ruling's ⛔ protects — provenance: read your own stamp, never copy the prompt — still holds, and is what the line says (「this run's harness-stamped id」). The line is followable with no MCP (the transcript'sClaude-Session:line, the source the footer already draws on) and names the subagent case so the field is fillable in both modes;get_sessionis the MCP-only second route and stays out of the rule line (byte budget; not universal). Inmode:cloudthe same routes return the dev session's own id. This PR's own report carries"session": "session_01HZfg2AwVX191qCizp88gQr"— the first live instance of the contract, and the first instance of the subagent case.Premise correction, small: the ruling's 「the way the file already prescribes for the harness-stamped
modelfield」 — os-dev.md has no such field; that discipline lives inreferences/contract-review.md:57 andreferences/platform-readings.md:369 (get_session→last_served_model). The analog inside os-dev.md is the PR-body session-URL footer, which is what the new line is anchored to.P2 — the id in a JSON value clears H64; the reader is untouched
Replayed through the UNTOUCHED module at
c053f748(git diff e592b4b6 HEAD -- scripts/pm/check-half-states.mjsis empty): anos-dev-reportcomment, fenced JSON, only the platform's bare footer,created_atafter the pin.seatSignatureseatSessionIdPresenth64SpeaksAbouth64UnattributedSeatContent"session": "session_01HZfg2AwVX191qCizp88gQr"reporttruefalsenullreportfalsetrue1on this card carries anos-dev-reportmarker — a seat/dev artefact — and NO session id appears anywhere in its text…」H52's
devReportOpenQuestionsreads{"marked":true,"parsed":true,"questions":[]}for BOTH bodies: the reader parses the object and readsopen_questionsalone, so an extra key is neither seen nor judged.seatSessionIdPresenttests the WHOLE body withSEAT_SESSION_ID, so a JSON value carrying the id attributes the report with no reader change —check-half-states.mjsstays out of the surface, as the ruling's measure-first clause asked.P3 — one JSON, two copies
:319 is unchanged: the comment is 「同一段 JSON」 as the terminal message, so the field reaches both copies through the one payload. No second mechanism.
Gates — all on head
f60aba4bRoster derived with
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack, NO paths (stderr: change set 1 path vs merge basec053f748b; committed 1, working tree 0, untracked 0): 17 commands. Every one ran in the foreground with the exit captured by redirect before any pipe and recorded ascommand :: exit N:pnpm --filter @objectstack/lint run check:doc-formula-expressionsexit 3 (PREREQUISITE NOT MET —@objectstack/lintnot built), re-run exit 0 afterpnpm exec turbo run build --filter=@objectstack/formula --filter=@objectstack/lint --concurrency=2underscripts/pm/os-verify-lock.sh(VERDICT command-exit 0). No exit-3 reading is reported here as a measurement.--ranverdict:✓ dispatch-gates --ran: 17 derived famil(ies) accounted for — 17 run, 0 NOT-MEASURED (a DERIVED zero — all 17 recorded an exit code and none of them is 3).pnpm check:pm-governed-proseexit 0 (2 instruction surface(s) name all 5 registered governed surfaces … and claim no others).check:pm-dispatch-gateswas not derived for this change set, so it was not run.check:pm-skill-id-lint:27 file(s) clean (pattern /#[0-9]{3,}/g);check:agent-model-declared:os-dev.md → opus;check:nul-bytesexit 0; the pre-push hook'scheck:commit-card-trailers:1 commit message(s) on this push carry no card relation and no model identifier in the trailer pair.Reverse verification of the ratchet (one-off, not landed): with the fix committed, a 404th line was appended under a
trap … EXIT INT TERMwith an absolute repo root — marker count 0 → 1, mutated blob6f16705b…against HEAD blob7a914c94…— and the gate went RED (✗ … os-dev.md is 404 lines; the ratchet ceiling is 403, exit 1);git checkout HEAD -- .claude/agents/os-dev.mdrestored it: marker count 0,git diff HEADempty,git status --porcelainempty, restored blob equal to HEAD's.Acceptance notes
mode:subagentthe report'ssessionequals the claim's id by value, so H64 clears but a reader cannot tell a subagent's report from its seat's own post by id alone; a per-subagentsession_01…id would be a harness feature, not a contract line. 承接者:无.model-field premise (above) — a wording slip in the dispatch prompt; no rule is affected.维护者速读(草稿)
改了什么:开发 agent 的终报契约(
.claude/agents/os-dev.md的 JSON 模板)新增一个字段session,要求每份报告的正文自带写它的会话 id;同一节的两行说明合并为一行付账,文件仍是 403 行,改动只落在 :319–:398。为什么改:同一文件 :51 规定「归属 = 文本里的 session ID」,而报告模板里没有任何一行能承载这个 id,平台附加的页脚也不带它;刚落地的 H64 巡查按此把每一份 dev 报告都记成「无归属」。补上字段后报告自证归属,巡查行自然消失(P2 实测)。
风险与代价(含回滚):零运行时影响,不发布任何包;唯一代价是每份报告多一行。子代理模式下该 id 与派发席位相同(P1 实测),这是事实而非漏洞。回滚 = revert 这一个 commit。
席位意见:(留空)
你要做的:审阅并批准这张受管面 PR;落地由席位执行。
Generated by Claude Code