Skip to content

pm(post-stamped): every seat post opens with a signature line LANE#N · ACCOUNT · SESSION, refused without one; the recognisers skip it - #27263

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-27113-post-signature-line
Oct 28, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-27113-post-signature-line

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #27113
Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:发帖脚本 post-stamped、建卡脚本 issue-create、中继的改评论/改正文两条通道,现在要求帖子第一行是署名「车道#席位号 · 账户 · 会话」,缺署名就拒发;所有按首行识别记录的脚本先跳过这一行署名再识别。
  • 为什么改:所有回复都以同一个机器人账号出现,您看不出是哪个车道、哪个席位、哪个账户写的;按您「缺署名就拒发」的裁决落地。
  • 风险与回滚:席位第一次发帖会被拒(拒发信息印出格式,补一行即可);回滚 = revert 本 PR。没有署名的旧帖子,识别结果不变。
  • 先看:scripts/pm/check-half-states.mjs 的署名读取器(唯一拼写);scripts/pm/post-stamped.mjs 的 main() 拒发分支;scripts/pm/fleet-write/dispatch.mjs 的 unsignedEditBodies(含名册豁免)。
  • 两分钟验证:printf 'hello\n' | node scripts/pm/post-stamped.mjs --dry-run --comment=1 --repo=o/r 预期退出码 2,并印出「opens with no signature line」和格式;第一行加上 domain:skills#2 · os-justin · session_… 再跑,预期退出码 0。
  • 需要你做的:无,席位落地(Tier S,席位按合同复核落地)。

What changes

The signature. The first non-empty line of a seat-authored body is LANE#N · ACCOUNT · SESSION, for example domain:skills#2 · os-justin · session_01QSeKYmhU5KEj1nukYhp1Tg. The parts:

  • LANE: one of domain: or repo: plus a lowercase name, epic:# plus the parent card, triage, director or coordinator.
  • N: the seat number, from 1.
  • ACCOUNT: GitHub's login rule.
  • SESSION: the relay envelope's shape, session_ plus 6 or more alphanumerics.

The separator is exactly one space, U+00B7, one space. The spelling exists once: SEAT_SIGNATURE_LINE / readSignatureLine / stripSignatureLine / signatureRefusal / signatureRefusalText in scripts/pm/check-half-states.mjs. Every writer and reader below imports it.

Write side: refused, nothing inserted.

  • post-stamped.mjs, --comment and --body alike: an unsigned body exits EXIT_REFUSED (2) before any request, a dry run included. It is judged beside the render, under the same exit, so a body that also breaks the stamp contract gets both refusals.
  • issue-create.mjs: an unsigned card body is a plan error (exit 2), listed beside the stamp-token error. Nothing calls this door from a workflow.
  • fleet-write/dispatch.mjs: a comment_edit or issue_patch carrying a body in a seat's --actions-file is refused as usage (2), right after the file is parsed. The check runs before the session, the packer or a dry run. The fleet roster's generated body is exempt by its own ROSTER_MARKER, imported. That is the one generated body that reaches these ops through this door; the ruling keeps generated cards out of scope.
  • ⛔ pr_create is out of scope, and so is the library path (packRequest). A pull request body's line 1 is Fixes #n, a closing keyword the platform reads at that position. The ruling covers posts, and the dev writes PR bodies. The library's callers are the tools above, and each judges its own body first.
  • --sign-from (the ruling's optional half) is not built. The refusal prints the shape, and the seat types one line.

Read side: one leading signature line changes no reading. I searched scripts/pm/** for every first-line idiom: split(…)[0], find/findIndex on the first non-blank line, anchored markers without the m flag, and the named keys.

Changed (each now reads through stripSignatureLine):

reader file what keys on line one
OS_DEV_REPORT_MARKER check-half-states the os-dev-report marker; composed from SEAT_SIGNATURE_SOURCE, still anchored, still no m
yieldedClaimIndexes check-half-states a claim's yield line
isTriageRulingComment check-half-states triage ruling openings
h56StampedReadings / h56AmbiguousLines check-half-states the act's OPENING-line stamp, the reader post-stamped's positional refusal imports
h65Headline check-half-states round records (round-open marker, round close, stand-down brief)
readMailboxLine record-recognisers the seven mailbox doorbell keys (MAILBOX_KEYS)
firstContentLine → classifyRulingComment check-prior-rulings director Ruling: comments
firstLineKeyText / isRecheckComment, releaseTextRefusal label-write the owned-24h knock key; the release text's Release: line
decisionAnalysisSignals post-stamped a decision frame on the plain opening line

Need nothing:

  • These match at any line start (the m flag) or read line by line: CLAIM_COMMENT_MARKER, RELEASE_COMMENT_MARKER (via markerMatches), CONTRACT_REVIEW_HEADING_MARKER, SEAT_RECORD_MARKER (## 席位核对), readContractReviewHandoff, readServedTier, readLocalRuns, claimSeatNumber, H44_ROUND_OPEN_MARKER, H44_VERDICT_MARKER, ACCEPT_VERDICT_MARKER, MAINTAINER_BRIEF_MARKER, STAY_BEHIND_LINE.
  • H64's head window: a signature carries a session id, so it can only strengthen that reading.
  • post-stamped's commentRow is display only. It prints the signature, which is the useful thing to show.
  • Generated artefacts: fleet-roster's writeRefusal, and sweep-closed-cards' marker (direct REST, not a seat writer).
  • Contract-review-claim: and Brief-claimed-by: have no machine reader in scripts/**. They are skill prose only, so there is nothing to pin.

Skill. One line in .claude/skills/pm-dispatch/references/seat-post-protocol.md states the signature and points to the refusal. The file stays at 88 of 88 lines; the new line is 118 bytes. It is paid for by deleting the standing-liveness pointer line. That line's content lives at references/dispatch-runbook.md line 41 (and seat-lifecycle.md line 37). No re-wrap was used to make room.

Tests

Battery counts, base 9ff8154c to head:

self-test before after
post-stamped.mjs 724 cases / 25 batteries 735 / 26 (new: the signature, 11)
issue-create.mjs 48 / 7 56 / 8 (new: the signature, 8)
record-recognisers.mjs 254 / 14 276 / 15 (new: the signature line, 22)
fleet-write/dispatch.mjs 247 / 22 260 / 23 (new: the signature at the seat's door, 13)
label-write.mjs 134 / 11 136 / 11 (release battery pin 30 to 32)
check-half-states.mjs 5034 5034
check-prior-rulings.mjs 155 155
with-fleet.sh 32 32 (its issue-create dry-run fixture now signed)
check-governed-queue-guard.mjs 401 401

The record-recognisers battery covers three things:

  • The reader's shape, including every lane form and nine near misses.
  • A pin holding the session half equal to the relay's SESSION_SHAPE (fleet-write/ops.mjs; mirrored because check-half-states.mjs may not import it).
  • Each recogniser the card names, driven unsigned and signed. Each pin asserts the reading is positive and identical. These cover ## Contract review (locator, Served-tier:, Local-runs:), os-dev-report, Contract-review-handoff:, claim and release lines, the yield line, round records, ## 席位核对, all seven mailbox keys, triage and director rulings, and the H56 opening stamp.

Readers in other modules are imported inside the self-test body, so the module the queue guard loads lazily gains no edge.

Three fixtures had to be signed, each because its writer now refuses unsigned bodies: issue-create's body.md / stamped.md / b.md, with-fleet.sh's issue-create dry-run body, and dispatch's CLI end-to-end issue_patch (its corrupted-byte offset is now stated as AT plus the signature's bytes). Two structural pins moved with the code they pin. post-stamped's refused-render block now matches the combined refusal. Its facet-marker composition pin is unchanged: code is still stripMarkdownCode(String(body ?? '')), and only the frame's line list is stripped.

Ablation: not run. The diff is scripts plus one doc line, which the role contract excludes from ablation.

Acceptance notes

  • PM mechanism hypotheses, measured:
    • ① Partly falsified, three ways. The reader cannot live in record-recognisers.mjs: that file imports check-half-states.mjs, which owns the first-line markers, so the reverse edge would close a top-level-await cycle. It lives in check-half-states.mjs, the module every writer and reader already imports. The lane class [a-z]+ would refuse the real domain:engine-core. A desktop-only session shape would refuse a cse_-derived container session.
    • ② Confirmed: body mode has no marker-line contract, so the rule applies to both modes.
    • ③ Refined: the render fails with three different texts, and renderBody is shared with label-write's release comment. So the signature is a second block under the same exit, not a row in the stamp-contract list, whose header and footer are about stamps.
    • ④ Refined: per-recogniser with/without pins rather than doubling every fixture.
  • Writers outside the card's named set still accept an unsigned body. Examples are a raw comment or issue_create op handed to the dispatch door (for instance mailbox-open.mjs's mailbox-close comment) and label-write's Release: comment. Their readers skip a signature, so a signed body reads correctly; the requirement just is not enforced there. Widening the requirement is the seat's call, so I did not file anything.
  • OS_DEV_REPORT_MARKER keeps its i flag (the shipped OS-DEV-REPORT dialect), so the composed signature is read case-insensitively in that one marker. Every writer refuses a mis-cased signature before posting, so this matters only on the read side.
  • H64's SEAT_SESSION_ID (the 26-character desktop form) is narrower than the signature's session shape. It is untouched.

Verification

All readings were taken at head bce074a0.

Gate derivation and reconciliation

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (exit 0): 65 commands, 9 paths vs merge base 9ff8154cf. All 65 were run locally and recorded as COMMAND :: exit N. --ran (exit 0): 65 derived famil(ies) accounted for — 65 run, 0 NOT-MEASURED (a DERIVED zero — all 65 recorded an exit code and none of them is 3).

pnpm --filter @objectstack/lint run check:doc-formula-expressions first exited 3 (PREREQUISITE NOT MET: @objectstack/formula / @objectstack/lint unbuilt in a fresh worktree). After turbo run build of those two under the verify lock (VERDICT command-exit 0), it exited 0. pnpm check:pm-dispatch-gates exit 0 (fast tier, 1935 cases). Not derived but named by the dispatch: pnpm check:pm-governed-prose exit 0, and check-governed-queue-guard.mjs --self-test exit 0 (401). Consumers also run: fleet-roster 78, mailbox-open 45, check-handover 46. Control-byte scan over the changed files: no hits.

The refusal, end to end (dry runs)

An unsigned comment exits 2 with post-stamped: REFUSED — the body opens with no signature line. Nothing was written. plus the shape. An unsigned comment with a bare opening stamp exits 2 and prints both the stamp-contract block and the signature block. A signed body whose claim line (now line 2) carries a bare stamp still gets the positional refusal. A signed body with {{NOW}} dry-runs to exit 0 with the signature sent as written.


Generated by Claude Code

…t-stamped's refusal

readSignatureLine / stripSignatureLine / signatureRefusal live beside the
first-line markers in check-half-states.mjs. Every first-line recogniser
reads through stripSignatureLine; OS_DEV_REPORT_MARKER composes the one
spelling. post-stamped refuses an unsigned body in both modes, listed
beside the stamp contract's refusals under one exit.

Claude-Session: https://claude.ai/code/session_01QSeKYmhU5KEj1nukYhp1Tg
Co-authored-by: Claude <noreply@anthropic.com>
…ies; pins on every first-line reader; one skill line

issue-create refuses an unsigned card body beside the stamp-token rule;
fleet-write/dispatch refuses an unsigned comment_edit / issue_patch body
in a seat's actions file (the roster's generated body exempt by its
imported marker). record-recognisers pins every named first-line reader
with and without a leading signature; label-write pins its two readers.
seat-post-protocol.md states the signature in one line, paid by the
standing-liveness line whose content lives in dispatch-runbook.md.

Claude-Session: https://claude.ai/code/session_01QSeKYmhU5KEj1nukYhp1Tg
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 28, 2026
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation labels Oct 28, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: bce074a03eebefeb051d139ccd0f2225a3ccff68
Local-runs: probe — the seat ran the branch's self-tests from a git archive copy of the head (no checkout, no edit, no network): post-stamped 735/26, issue-create 56/8, record-recognisers 276/15, fleet-write/dispatch 260/23, label-write 136/11, check-half-states 5034, check-prior-rulings 155, check-governed-queue-guard 401, mailbox-open 45 — all exit 0 — and post-stamped --dry-run against six first lines (unsigned → exit 2 naming the shape; domain:ui#1, repo:cloud#2, triage#1, director#1 forms → exit 0; a cse_… session → exit 2)
Scope: .claude/skills/pm-dispatch/references/seat-post-protocol.md (one line in, one paid out), scripts/pm/check-half-states.mjs (the signature reader and the first-line markers), scripts/pm/post-stamped.mjs, scripts/pm/issue-create.mjs, scripts/pm/fleet-write/dispatch.mjs (the comment_edit / issue_patch door), scripts/pm/record-recognisers.mjs, scripts/pm/label-write.mjs, scripts/pm/check-prior-rulings.mjs, scripts/pm/with-fleet.sh + card #27113's 「Wanted」 1–5 and the maintainer's words quoted there

Read at 2026-10-28T13:33Z by the domain:skills#2 seat on the fetched head (merge-base 9ff8154c): nine files, +459/−36, 495 changed lines ≤ 3000. Tier S (.claude/** ×1 + non-gate scripts/pm/**; check-governed-merges --pr 27263 lists the one .claude path). The claim's tier was the default (opus, no path mandate); this review is served at CONTRACT_REVIEW_TIER.

① Derived judgments

  • The shape is the maintainer's 「车道#席位号 · 账户 · 会话」 exactly: SEAT_SIGNATURE_LINE = (domain:|repo:|epic:#\d+|triage|director|coordinator)…#N · ACCOUNT · session_…, separator · (U+00B7, the doorbell keys' separator), spelled ONCE in check-half-states.mjs (readSignatureLine / stripSignatureLine / signatureRefusal) and imported by the three writers and every recogniser — the dev moved it from the order's suggested home (record-recognisers.mjs) to the module that already owns the first-line markers, because the reverse import would close a top-level-await cycle; verified by reading the import graph on the head. One spelling, not three: holds.
  • Wanted 2 (refuse, never auto-insert): post-stamped.mjs judges the signature in main() beside the stamp contract under the one EXIT_REFUSED, in --comment AND --body mode, before any request — dry run included; the refusal names the shape. --sign-from is not built (the card said MAY). Wanted 3: issue-create.mjs refuses an unsigned body as a plan error (exit 2); fleet-write/dispatch.mjs refuses unsigned comment_edit / issue_patch bodies in a seat's actions file (exit 2) with the fleet roster's generated body exempt by its imported ROSTER_MARKER; pr_create is out of scope (its line 1 is the closing keyword; the PR body says so) — the right reading of 「帖子」. Generated workflow cards untouched. Holds.
  • Wanted 4: nine first-line readers in five files now skip one leading signature line (listed in the PR body with the ones that need nothing and why — the m-flag markers, the H64 head window, display-only rows); each named recogniser is pinned unsigned AND signed (22 new cases in record-recognisers, the seven mailbox keys included). An unsigned old post reads exactly as before (stripSignatureLine is a no-op without a signature). Holds.
  • Wanted 5: seat-post-protocol.md L30 「帖与卡正文首行恒为署名 LANE#N · ACCOUNT · SESSION;缺即写工具拒发(post-stamped.mjs 头注)。」 (118 B ≤ 120), paid by deleting the 常设半边 line whose content lives at dispatch-runbook.md L41 and seat-lifecycle.md L37 (both read on the head); 88/88, no re-wrap. Holds.
  • Fleet consequence, stated so no seat is surprised: from the merge on, every seat-authored comment, card body and seat-post refresh must open with the signature line; the triage seat already writes it by hand (its grade on ensure-pm-labels.sh: create the pinned label that stale.yml exempts and the per-seat mailbox now requires (404 today; work-in-progress 404 too) #27151 opens triage#1 · os-zhuang · session_…), so the convention precedes the gate.

② Semver level

  • None owed: PM scripts and protocol text; no package touched. Clause-②: no, Metadata-protocol: none on the PR body match the claim; no Skills-docs: line owed; skip-changeset correct.

③ Boundary flags

  • Beyond the claim's named set, two writers still accept an unsigned body: a raw comment / issue_create op through the dispatch door (e.g. mailbox-open.mjs's close comment) and label-write's Release: comment — the dev left them at the card's boundary (Wanted 3 names three writers). Their readers already skip a signature. Noted under Acceptance notes; widening is a separate card if the maintainer wants it, not this PR's.
  • OS_DEV_REPORT_MARKER keeps its i flag, so that one composed marker reads a signature case-insensitively on the read side; every writer refuses a mis-cased signature. Accepted.
  • with-fleet.sh and check-prior-rulings.mjs are outside the claim's named list: with-fleet.sh's issue-create dry-run fixture had to be signed (3 lines) and check-prior-rulings reads first content lines (5 lines) — both mechanical consequences of the rule, named in the PR body; the seat amends the claim's file surface by this record.
  • Hypotheses 1, 3, 4 were refined by measurement (reader's home, refusal placement, per-recogniser pins instead of doubled fixtures) — each with a stated reason the seat verified; 2 and 5 held.
  • Author objectstack-fleet[bot] through the relay; mcp_calls: 0; the dev's report comment was posted with origin/main's tool (no signature required yet), as ordered.

④ Design judgment

  • Against the maintainer's scenario (reading a reply and not knowing which lane, seat or account wrote it): the first line of every post now answers it, and a post that cannot answer it is never sent — refusal at the producer, one spelling, readers unchanged for the archive. Against the four-axis frame: AI-error (refuse, never tolerate or auto-insert), long-run (one reader module), focus (no gate, no patrol row, generated cards out). Holds. Residual: the two unsigned-capable writers above, and the mailbox-close comment's shape (mailbox-open.mjs) — both read fine, both can be widened later.

Implemented-by: claude/issue-27113-post-signature-line
Reviewed-by: session_01QSeKYmhU5KEj1nukYhp1Tg

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读 — 席位终稿 · PR #27263 · head bce074a0 · domain:skills#2 · 2026-10-28T13:42Z

  • 改了什么:席位写的每条帖子、卡正文、座位贴正文,第一行必须是署名 LANE#N · ACCOUNT · SESSION(如 domain:skills#2 · os-justin · session_01QSeKYmhU5KEj1nukYhp1Tg);post-stamped(评论与正文两种模式)、issue-create、中继的 comment_edit/issue_patch 三个写口缺署名就拒发(退出码 2,印出格式,不自动补);九个按首行识别记录的读取器先跳过署名再识别,每个都钉了有/无署名两种;seat-post-protocol.md 加一行规则(88/88,付行有家)。
  • 为什么改:你的原话「我看到回复,但是不知道是哪个账户回复的,哪个车道回复的」「同意 让发帖脚本 post-stamped 强制每条帖子第一行带署名…缺署名就拒发」。
  • 风险与回滚:合并后每个席位第一次发帖会被拒一次(信息里有格式,补一行即可);旧帖识别结果不变(无署名时跳过逻辑是空操作);生成卡(full-run、flake 锚卡)不受影响。回滚 = revert。
  • 先看:scripts/pm/check-half-states.mjs 的 SEAT_SIGNATURE_LINE(唯一拼写);post-stamped.mjs main() 的拒发分支;fleet-write/dispatch.mjs 的 unsignedEditBodies(名册豁免)。
  • 两分钟验证:printf 'hello\n' | node scripts/pm/post-stamped.mjs --dry-run --comment=1 --repo=o/r 预期退出 2 并印 「opens with no signature line」;首行加 domain:skills#2 · os-justin · session_… 再跑预期退出 0。
  • 需要你做的:无 —— Tier S,席位契约复核 PASS(6470403263)后按你今天的落地授权落地。
  • 席位意见:ACCEPT。九项自测从归档副本复跑全绿(735/56/276/260/136/5034/155/401/45);六种首行 dry-run 实测拒/收符合卡;分诊席已自发按此格式写首行(ensure-pm-labels.sh: create the pinned label that stale.yml exempts and the per-seat mailbox now requires (404 today; work-in-progress 404 too) #27151 的定级),规则与现状一致。

席位核对 — ACCEPT · domain:skills#2 · session_01QSeKYmhU5KEj1nukYhp1Tg · 2026-10-28T13:42Z

  1. 形态:draft → main,首行 Fixes #27113,Clause-②: no、Metadata-protocol: none 行首在;九个文件 +459/−36(495 行 ≤ 3000);assignee os-justin;skip-changeset 正确;documentation、size/m 为 bot 所加。
  2. 前提实测:席位在取回的 head(merge-base 9ff8154c)上核 —— 署名正则一处定义、三写口引用;新规则行 118 B、文件 88/88、付行之家在 dispatch-runbook.md L41 与 seat-lifecycle.md L37;pr_create 不在范围且正文说明;dry-run:未署名 exit 2,四种车道形态 exit 0,cse_ 会话 exit 2。
  3. 门禁 check-runs(head bce074a0,本笔读数 2026-10-28T13:42Z):51 check-runs,31 success · 20 skipped · 0 in progress · 0 failure(mergeable_state: clean, draft)。dev 本地派生 65/65 exit 0 钉 bce074a0(报告 6470286790)。
  4. 接受的偏差:① 读取器落在 check-half-states.mjs 而非 record-recognisers.mjs(反向 import 成环,实测);② 拒发放在 main() 第二块而非戳契约编号表(renderBody 三处共享);③ 逐识别器有/无署名钉(22 例)而非翻倍全部夹具;④ with-fleet.sh、check-prior-rulings.mjs 越出认领面(机械随动,各 3/5 行),席位以本记录修正文件面;⑤ --sign-from 未建(卡为 MAY)。
  5. 范围外去向:none filed;Acceptance notes:两处写口(中继裸 comment/issue_create op、label-write 的 Release: 评论)仍收未署名正文——读侧已跳过,要不要扩面是维护者的事;OS_DEV_REPORT_MARKER 保留 i 旗(读侧)。Skills-docs: 不欠(kind none)。
  6. 落地条款:Tier S;契约复核 PASS 评论 6470403263(head bce074a0);全绿即席位经中继 pr_ready + automerge_enable 落地(本班维护者授权在案);MERGED 后关卡摘 pm:dispatched;自合并起本席每条帖子首行加署名。

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 28, 2026 13:44
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 28, 2026 13:44
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 28, 2026
Merged via the queue into main with commit 39ea1d6 Oct 28, 2026
52 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-27113-post-signature-line branch October 28, 2026 14:07
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/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

post-stamped: every post's first line is a signature <lane>#<n> · <account> · <session>, and a post without one is refused

2 participants