post-stamped: every post's first line is a signature <lane>#<n> · <account> · <session>, and a post without one is refused #27113
Copy link
Copy link
Closed
Labels
area:devpathThe road — create, dev, verify, publish/install, connect an agent, iterateThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:skillspriority:p1High: required for production / M2High: required for production / M2tooling
Description
Filing gate ③ (maintainer-directed). Filed by the triage seat (session_01AavokzJ5DndAwitDXvKy4U), 2026-10-28 ~06:2xZ. ⛔ Not a claim.
The maintainer's words (verbatim, in order)
- 「还有一个问题是我看到回复,但是不知道是哪个 账户回复的,哪个车道回复的。」
- On the triage seat's proposal: 「同意 让发帖脚本 post-stamped 强制每条帖子第一行带署名:车道#席位号 · 账户 · 会话,缺署名就拒发。」
Why
Every seat posts through the fleet relay, so GitHub shows every comment as objectstack-fleet[bot]. Headers are not uniform. Some name a seat and session (· triage seat · session_…), some name neither, and almost none name the account. So the maintainer reading a thread cannot tell which lane, which seat or which Claude account wrote a reply.
Wanted
- The signature line. The first non-empty line of every body that
scripts/pm/post-stamped.mjswrites is:
<lane>#<n> · <account> · <session><lane>#<n>: the seat, as the seat roster names it. Examples:domain:ui#1,domain:server#2,domain:skills#1,repo:cloud#2,repo:hotcrm#1,triage#1,director#1.<account>: the account the seat runs under, the same value a claim'sAccount:line carries (for exampleos-zhuang,os-justin,huangyiirene).<session>: the fullsession_…id.- Example:
domain:ui#1 · os-zhuang · session_01JQwxbZeBv2y91towxasSUT
- Refuse without it.
post-stampedrefuses (non-zero exit, nothing sent) when the first non-empty line does not match the shape, naming the expected shape in the refusal. ⛔ No silent auto-insert. It may offer--sign-from <seat-post>to fill the line from the seat's own roster entry, but a body without a signature is never posted. - The same rule on the other writers. The other seat-authored writers through the relay apply it too:
scripts/pm/issue-create.mjsbodies, andcomment_edit/issue_patchinscripts/pm/fleet-write/dispatch.mjswhen they write a seat-authored body. Generated workflow cards (the full-run card, the queue-flake anchor) are out of scope: they are authored by a workflow, and their marker line says so. - Machine-shaped records keep working. Every recogniser that reads a seat artefact by its first line skips one leading signature line before matching. That covers
## Contract review,os-dev-report,Contract-review-claim:,Contract-review-handoff:,Brief-claimed-by:, claim and release lines, round records, and## 席位核对. Pin each with a test that the same body is recognised with and without the signature line. - The skill says it once. One line in
.claude/skills/pm-dispatch/references/seat-post-protocol.md(or wherever the posting rule lives) states the signature and points to the refusal. Edit within existing lines where possible; the files are near their line ceilings.
Surface
scripts/pm/post-stamped.mjs,scripts/pm/issue-create.mjs,scripts/pm/fleet-write/dispatch.mjs,scripts/pm/record-recognisers.mjs, and every other parser that keys on a first line, plus their tests.- One line in the pm-dispatch skill. Tier S (
.claude/**) plus non-gatescripts/pm/**. Clause-②: no·Metadata-protocol: none.
Acceptance
post-stampedwith a body whose first line is not a signature exits non-zero and sends nothing. With a signature, it posts as today.- Every recogniser's existing fixtures pass with a signature line prepended.
- The next round record, claim and review record from any seat carry the signature line, and the maintainer can read lane, seat, account and session off the first line of any reply.
Dedupe: open domain:skills issues read for post-stamped, signature, 署名, account: none. New.
Generated by Claude Code
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
area:devpathThe road — create, dev, verify, publish/install, connect an agent, iterateThe road — create, dev, verify, publish/install, connect an agent, iteratedomain:skillspriority:p1High: required for production / M2High: required for production / M2tooling
Activity
objectstack-fleet commentedon Oct 28, 2026
Claim: PM loop round 1 (takeover shift, wave 6) · 2026-10-28T12:44Z
Session:
session_01QSeKYmhU5KEj1nukYhp1TgAccount:
os-justinBranch:
claude/issue-27113-post-signature-lineWorktree:
objectstack-issue-27113Domain:
domain:skillsSeat:
domain:skills#2File surface:
scripts/pm/post-stamped.mjs(the signature refusal and its self-test battery),scripts/pm/issue-create.mjs(same rule on card bodies),scripts/pm/fleet-write/dispatch.mjs+fleet-write/ops.mjsonly where a seat-authored body passes throughcomment_edit/issue_patch,scripts/pm/record-recognisers.mjs(every first-line recogniser skips one leading signature line; fixtures with and without it) and any otherscripts/pm/**parser that keys on a body's first line (named by grep, not recalled); ONE line in.claude/skills/pm-dispatch/references/seat-post-protocol.md(88/88, headroom 0 — in place); generated workflow cards (full-run card, queue-flake anchor) out of scope (stop on breach; explain in the report)Container & model:
M,mode:subagent,model: opus — default tier (dispatch-gates --tier on scripts/pm/post-stamped.mjs, issue-create.mjs, fleet-write/dispatch.mjs, record-recognisers.mjs and references/seat-post-protocol.md, this act: no path-derived mandate),effort: highClause-②: no
Metadata-protocol: none
Responsibility:
the fleet's own posting tools (scripts/pm/post-stamped.mjs and siblings) produce the gap — every post lands as objectstack-fleet[bot] with no uniform header | no platform path covers it (the relay strips the actor) | reached by the maintainer on every reply they read; used today by every seat, so every post is affectedThread-read: none
Premise: holds — read in this act on
origin/maind63f24a7+:scripts/pm/post-stamped.mjsandscripts/pm/issue-create.mjscarry nosignature/署名text (0 hits each);seat-post-protocol.mdcarries none (0 hits);scripts/pm/record-recognisers.mjskeys seven first-line shapes plus the newMAILBOX_KEYSbattery (PR #27143 merged 2026-10-28T12:33Z) — the file this card serializes behind is on main. Triage filed the card with its grade in the same stroke (p1,pm:queue,domain:skills,area:devpath); no thread.Serial constraints cleared: PR #27143 (the only in-flight PR on
record-recognisers.mjs) is MERGED; no open PR touches any file above (open-PR title/branch scan in this act: none); #27062 (queued,record-recognisers.mjs+contract-review.md) stays serialized BEHIND this PR; #27047 (dispatched this wave) touchesSKILL.md,execution-duties.md,landing-operations.md,triage-duties.md,scripts/ci/scheduled-full-run*.mjs— disjoint. Tier S (.claude/**+scripts/pm/**): one draft PR; contract review atCONTRACT_REVIEW_TIER; the seat lands it (the maintainer's landing word of this shift is on record: 「你应该按照 pm-dispatch 技能审核并落地pr」).objectstack-fleet commentedon Oct 28, 2026
os-dev-report
{ "issue": 27113, "status": "done", "branch": "claude/issue-27113-post-signature-line", "pr": "https://github.com/objectstack-ai/objectstack/pull/27263", "head": "bce074a0", "session": "session_01QSeKYmhU5KEj1nukYhp1Tg", "premise_still_valid": true, "summary": "Every seat-authored body now has to open with the signature LANE#N · ACCOUNT · SESSION, spelled once in scripts/pm/check-half-states.mjs (readSignatureLine / stripSignatureLine / signatureRefusal / signatureRefusalText). Three writers refuse an unsigned body before any request, a dry run included: post-stamped.mjs (both --comment and --body, listed beside the stamp contract's refusals under the one EXIT_REFUSED); issue-create.mjs (a plan error, exit 2, beside the token rule); fleet-write/dispatch.mjs (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). Nothing inserts the line, and --sign-from is not built. Every first-line recogniser found by grep (nine readers in five files) now skips one leading signature line, and each named recogniser is pinned unsigned and signed. One line in seat-post-protocol.md states the rule. pr_create is out of scope (its line 1 is the closing keyword), and the PR body says why. The card assignee was already os-justin; I did not write it, and the PR assignee mirrors it.", "tests": "Self-tests, base 9ff8154c then head bce074a0, each exit 0: post-stamped 724/25 then 735/26 batteries (new 'the signature', 11 cases); issue-create 48/7 then 56/8 (new, 8); record-recognisers 254/14 then 276/15 (new 'the signature line', 22: the reader's shape including six lane forms and nine near misses, the session half held equal to fleet-write/ops.mjs SESSION_SHAPE, and with/without pins for ## 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); fleet-write/dispatch 247/22 then 260/23 (new 'the signature at the seat's door', 13); label-write 134 then 136 (release battery pin 30 to 32); unchanged and green: check-half-states 5034, check-prior-rulings 155, with-fleet 32 (its issue-create dry-run fixture signed), check-governed-queue-guard 401, fleet-roster 78, mailbox-open 45, check-handover 46. CLI dry runs: an unsigned comment exits 2 with 'the body opens with no signature line'; a signed one exits 0; under a signature, a bare stamp on the claim line still gets the positional refusal. Ablation: not run, because the diff is scripts plus one doc line, which os-dev.md excludes from ablation.", "gates": "Derived at bce074a0 with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (exit 0): 65 commands, 9 paths against merge base 9ff8154cf. All 65 ran locally, each exit captured to a file before any read: 64 in a foreground loop (63 exit 0 on the first run). pnpm --filter @objectstack/lint run check:doc-formula-expressions exited 3 (PREREQUISITE NOT MET: formula and lint unbuilt) and exited 0 after turbo run build of the two under os-verify-lock (VERDICT command-exit 0, 87s held). pnpm check:pm-dispatch-gates ran under nohup with exit 0 (fast tier, 1935 cases, 344.7s). --ran with the 'COMMAND :: exit N' record (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)'. Also run, not derived: check:pm-governed-prose exit 0; check-governed-queue-guard --self-test exit 0. Local-only families check:dev-prereqs and check:skill-frame-freshness were not run: the diff touches neither packages/spec nor a four-axis frame file. PR check-runs on bce074a03eeb at one read, no polling: 46 total, 9 success (Governed Surface Queue Guard, Check Changeset, Check PR Size, PR-body guards, Test UI, Live required-set sweep, Release section-coverage sweep, Snapshot the board), 20 skipped, 17 in_progress (Lint · CI, release & engine gates; Lint · ESLint; Lint · PM dispatch-gates; Lint · docs & code guards; Lint · repo & tooling gates; three Lint · scoped lanes; Plan Test Core shards; Dogfood Regression Gate; Live half-state sweep; Platform checklist gate sweep; Auto Label). Required contexts other than the Queue Guard were in_progress or skipped at the read.", "dogfood_battery": "n/a — no `!` in the PR title and no (narrowing) arm (Clause-②: no)", "mcp_calls": "0 — no MCP GitHub tool, read or write", "api_writes": "2 REST writes through the fleet-write relay (each one POST /repos/objectstack-ai/objectstack/dispatches): (1) pr_create, whose relay run 40696448941 did POST /pulls (draft) + POST /issues/27263/assignees + POST /issues/27263/labels (skip-changeset), body read back identical, 11619 bytes; (2) this os-dev-report comment, via node scripts/pm/post-stamped.mjs --comment=27113 from origin/main's tool (the comparison worktree at 9ff8154c; origin/main had moved 2 commits, neither touching scripts/pm/**), whose relay POSTs /issues/27113/comments. Plus 3 git pushes of the branch (the empty-branch probe and two checkpoints); git push is not a REST write. Reads: GET issue and comments for #27113, GET pulls/27263, GET check-runs.", "files_changed": [ ".claude/skills/pm-dispatch/references/seat-post-protocol.md", "scripts/pm/check-half-states.mjs", "scripts/pm/check-prior-rulings.mjs", "scripts/pm/fleet-write/dispatch.mjs", "scripts/pm/issue-create.mjs", "scripts/pm/label-write.mjs", "scripts/pm/post-stamped.mjs", "scripts/pm/record-recognisers.mjs", "scripts/pm/with-fleet.sh" ], "line_budget": "seat-post-protocol.md: 88 lines before, 88 after (ceiling 88, headroom 0). One line added after the post-stamped stamp line: '- 帖与卡正文首行恒为署名 `LANE#N · ACCOUNT · SESSION`;缺即写工具拒发(`post-stamped.mjs` 头注)。' (118 bytes). Paid by deleting '- 常设半边是总监席摘要的座位贴活性巡查,同一判据,细则见 `dispatch-runbook.md`。', whose content lives at references/dispatch-runbook.md line 41 (and seat-lifecycle.md line 37). No re-wrap.", "first_line_readers": { "changed_to_skip_one_signature_line": [ "check-half-states OS_DEV_REPORT_MARKER (composed from SEAT_SIGNATURE_SOURCE, still anchored, no m)", "check-half-states yieldedClaimIndexes (yield line)", "check-half-states isTriageRulingComment", "check-half-states h56StampedReadings / h56AmbiguousLines (opening-line stamp; post-stamped's positional refusal imports it)", "check-half-states h65Headline (round records)", "record-recognisers readMailboxLine (MAILBOX_KEYS)", "check-prior-rulings firstContentLine / classifyRulingComment", "label-write firstLineKeyText / isRecheckComment and releaseTextRefusal", "post-stamped decisionAnalysisSignals (opening-line frame)" ], "need_nothing": [ "m-flag or per-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, check-handover STAY_BEHIND_LINE", "H64 seatSignature head window (a signature carries a session id)", "post-stamped commentRow (display only)", "generated artefacts: fleet-roster writeRefusal, sweep-closed-cards COMMENT_MARKER", "Contract-review-claim: and Brief-claimed-by: have no machine reader in scripts/** (skill prose only)" ] }, "deviations": [ "PR title: the order's title spelled the shape with angle-bracket placeholders. os-dev.md requires placeholder words in GitHub text and binds over the order, so the title reads LANE#N · ACCOUNT · SESSION.", "PM hypothesis 1, measured: the reader lives in check-half-states.mjs, not record-recognisers.mjs. record-recognisers imports check-half-states, which owns the first-line markers, so the reverse edge would close a top-level-await cycle. Lane class [a-z][a-z0-9-]* instead of [a-z]+ (domain:engine-core). The session half mirrors the relay SESSION_SHAPE (session_ plus 6 or more alphanumerics, so cse_-derived sessions read), pinned equal in the self-test.", "PM hypothesis 3, refined: the signature is judged in post-stamped main() beside renderBody under the one EXIT_REFUSED, as a second block. renderBody has three refusal texts and is shared with label-write's release comment, so the signature is not a row in the stamp-contract numbered list.", "PM hypothesis 4, refined: per-recogniser with/without pins (22 cases) instead of doubling every existing fixture. Hypotheses 2 and 5 held, except that check:pm-governed-prose and the queue-guard self-test were not derived; I ran both anyway.", "A comparison worktree (/home/user/objectstack-issue-27113-cmp, detached at BASE 9ff8154c, node_modules symlinked) was used for the before-counts and as the current-main tool for both relay writes. It is removed at the end of the round.", "Commit trailers use AGENTS.md's model-free pair (Claude-Session + Co-authored-by: Claude). The harness reminder's model-named trailer was not used, because AGENTS.md and os-dev.md take precedence." ], "open_questions": [], "out_of_scope_findings": [ "carrier: none · noted, not filed (boundary of the card's Wanted 3; not a class a/b/c defect): writers outside the card's named set still accept an unsigned body. These are a raw `comment` or `issue_create` op through the dispatch door (for example mailbox-open.mjs's mailbox-close comment) and label-write's `Release:` comment. Their readers already skip a signature. Widening the requirement is the seat's call. Dedupe words: signature, unsigned, post-stamped, dispatch comment, Release comment", "carrier: none · noted, not filed: OS_DEV_REPORT_MARKER keeps its i flag (the OS-DEV-REPORT dialect), so the composed signature is read case-insensitively in that one marker. Read side only; every writer refuses a mis-cased signature. Dedupe words: os-dev-report marker, case, signature" ] }Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
ACCEPT → PR #27263 comment 6470626164 ·
domain:skills#2· 2026-10-28T13:43Z — ⛔ not a claim. Contract review (CONTRACT_REVIEW_TIER) PASS → PR comment 6470403263 on headbce074a0: the signature lineLANE#N · ACCOUNT · SESSIONspelled once incheck-half-states.mjs, refused bypost-stamped(comment and body modes),issue-createand the relay'scomment_edit/issue_patch, skipped by nine first-line readers with with/without pins; one rule line inseat-post-protocol.md(88/88). The claim's file surface is amended by the review to includewith-fleet.shandcheck-prior-rulings.mjs(mechanical consequences). 51 check-runs green or expected-skip,mergeable_state: clean; the seat lands it through the relay (pr_ready+automerge_enable) in the next act on the maintainer's standing word of this shift. After MERGED: strippm:dispatched; every post this seat writes from then on opens withdomain:skills#2 · os-justin · session_01QSeKYmhU5KEj1nukYhp1Tg. Acceptance notes carried: two writers outside the card's named set still accept an unsigned body (the relay's rawcomment/issue_createops,label-write'sRelease:comment) — widening is the maintainer's call.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
domain:skills#2 · os-justin · session_01QSeKYmhU5KEj1nukYhp1Tg
Landed — PR #27263 merged; card closed by
Fixes #27113domain:skills#2)git merge-base --is-ancestor 39ea1d67 origin/mainaftergit fetch origin main39ea1d67pm(post-stamped): every seat post opens with a signature line LANE#N · ACCOUNT · SESSION …is onorigin/main;GET pulls/27263→merged: true,merge_commit_sha 39ea1d67, merged by the queue entry the seat made on the maintainer's standing word (2026-10-28T14:07Z);origin/main:seat-post-protocol.mdcarries the 署名 rule line andorigin/main:scripts/pm/post-stamped.mjsthe signature refusal; main's push CI on39ea1d67at this reading: 32 success · 23 skipped · 24 in progress · 0 failureGET issues/27113state: closed,state_reason: completed(2026-10-28T14:07Z), closed by the PR body'sFixes #27113(the only closing keyword in the body — no other card was closed by it)State change:
pm:dispatchedremoved from this card in the same act (label-write, read back:tooling,priority:p1,domain:skills,area:devpathremain as ownership). This comment is the seat's first post under the rule it lands: line 1 is the signature. The two writers the review left outside the card (the relay's rawcomment/issue_createops,label-write'sRelease:comment) still accept an unsigned body — carried as an Acceptance note, the maintainer's call to widen.Generated by Claude Code