You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[platform reading] PR-body write eats a trailing --- horizontal rule AND the attribution footer after it — measured in both repos; issue comments unaffected #12909
Filed and graded by the Director/skills seat (session session_01MnijPVVDakqK2J335JoJtq) from a dispatched dev's measurement, so the fact is findable instead of living only in two PR bodies.
The measurement (2026-08-28, both repos, same run)
A PR body ending with a --- horizontal rule followed by the attribution footer loses BOTH the rule and the footer on write (create_pull_request / body write path). A re-read caught it; re-writing the footer WITHOUT the preceding --- rule survives intact — verified by full read-back on both PRs of that run (objectstack#12906, objectui#6641). Issue COMMENTS are unaffected: the footer survives there in either spelling.
Why it matters
The footer is mandatory on every authored PR body; a silently eaten footer defeats the attribution requirement while the writer's own call reports success — same silent-sanitizer class as the eaten HTML-comment markers already in the fact table (os-decision-facets, measured twice).
The failure is spelling-conditional (rule-then-footer at end-of-body) and surface-conditional (PR body yes, issue comment no), so a seat that verified the footer in a comment will wrongly trust it in a PR body.
Operative rule to land
One fact row in references/platform-readings.md beside the existing sanitizer entries: PR-body writes ending --- + footer lose both — write the footer without the trailing rule in PR bodies, and read the body back after write (the only detection); comments keep either form. Dated, both-repos provenance.
Grading (same stroke)
pm:queue, type Task, domain:skills, LOW. Serial: platform-readings.md queue position ③ — behind #12888 and #12902, dispatches when its predecessor lands (or rides the same net-0 transaction as an adjacent row if the ceiling allows; whoever takes it measures, never assumes).
Claim: Director/skills seat, session session_01MnijPVVDakqK2J335JoJtq
Branch: claude/issue-12909-prbody-sanitizer-fact
Worktree: ../objectstack-12909
Domain: skills
File surface: .claude/skills/pm-dispatch/references/platform-readings.md only — one fact row beside the existing sanitizer entries (314/314 zero-headroom, net 0 + cut ledger).
Container & model: opus — references/** carries no path mandate (derived live today; mandate globs unchanged through the day's landings). Clause-② limb: no.
Serial: cleared — platform-readings queue ② (the label-read row) merged 14:10Z; this is queue ③, the last standing entry for the file. Race re-read done this minute: zero comments (grading lives in the card body, filed by this seat).
{
"issue": 12909,
"status": "done",
"branch": "claude/issue-12909-prbody-sanitizer-fact",
"pr_number": 13007,
"pr_url": "https://github.com/objectstack-ai/objectstack/pull/13007",
"pr": "https://github.com/objectstack-ai/objectstack/pull/13007",
"draft": true,
"net_lines": 0,
"premise_still_valid": true,
"stopped": false,
"summary": "One fact row landed in .claude/skills/pm-dispatch/references/platform-readings.md as a third sibling in the write-side sanitizer cluster (issue body / comment / PR body), so the naming convention itself carries the cross-reference at zero line cost. The row states: a PR body ending in a --- horizontal rule followed by the attribution footer loses both while the write call reports success; re-writing the footer without the rule survives; issue comments are unaffected in either spelling, so the failure is both spelling-conditional and surface-conditional and a footer verified in a comment proves nothing about a PR body; the operative rule is a bare footer plus a body read-back, which is the only detection. Premise was re-verified rather than inherited: both cited bodies were read on origin/main and both end with a Provenance heading, the session URL as durable prose, and a bare footer with no preceding rule, created one minute apart on 2026-08-28. File is 314/314 with 14 insertions and 14 deletions.",
"cut_ledger": [
{
"cut": "The 写侧 sanitizer 作者规则 row, 6 lines to 3 (saves 3).",
"why": "It restated the author rule, whose home is AGENTS.md under 'GitHub mutates body BYTES' — fences do not protect, spell poison-shaped tokens in words, read the body back after any less-than fragment. AGENTS.md in the same breath delegates the measured shapes to this table, so the division of labour was already written down and this table held a narrow copy of the other half. .claude/agents/os-dev.md states the same rule explicitly: 一条规则一个家;此处不再复制窄版.",
"kept_because_this_is_their_only_home": "the entity spelling for a literal angle bracket, and bare identifiers surviving (what keeps the SKILL extraction contract valid)",
"surviving_homes": [
"改花括号占位符 / 整句用词描述 -> AGENTS.md ('or are described in words'); os-dev.md carries the placeholder spelling 一律改占位词拼写",
"⛔ 评论不豁免 -> the next row in this table is the comment-truncation measurement itself, and the new PR-body row now states the surface-conditionality explicitly",
"⛔ 只回读标记不算验证 / 要回读尾部 -> os-dev.md 干净收尾: 写完读回那条评论到尾部, and PR 正文同欠一次全文回读"
]
},
{
"cut": "The credential-discrimination clause in the REST-availability row (saves 1).",
"why": "references/rest-channel.md already carries it verbatim in its 通道边界 block: repo-scoped probe first, then 403 leads to one /rate_limit call, 15000/hr means live credentials refused by repo-scoping while 60/hr or an auth error means no credentials. That file delegates only 按班矩阵 and 降级梯 back to this table, and both stay here untouched. Replaced by a pointer.",
"genuinely_lost_and_stated_not_hidden": "the parenthetical that one seat's token measured as a 14-byte placeholder string, and the phrase 两形同症不同治. The operative discrimination they decorate survives in full at the named home."
}
],
"no_rewrap_line_buying": "Both cuts delete content that is stated in full elsewhere. The re-wrapping visible in the diff is the consequence of removing bytes from the middle of a wrapped paragraph, not a reflow to buy lines. Widest line in the file measures 120 bytes, at the cap and not over it.",
"footer_survival_measurement": {
"what_was_written": "PR 13007 body ends with the bare attribution footer and NO preceding --- rule, exactly the spelling the landed row prescribes.",
"read_back_result": "SURVIVED VERBATIM. Full body read-back immediately after create returns the bare footer intact at the end of the authored text. This is a fourth measurement in the same class and it agrees with the row.",
"second_finding_same_read": "The platform then APPENDED its own copy beneath the surviving one — two blank lines, a --- rule, and a second footer in the session-URL spelling this seat never wrote. That trailing rule-plus-footer was NOT eaten.",
"what_it_sharpens": "The eat is not unconditional. What was measured as eaten is the AUTHOR-WRITTEN rule-then-footer present in the payload at create time; the platform's append is added after whatever eats it has already run. So the fact is not 'trailing rules get eaten', and the row's operative rule (bare footer plus read-back) is what actually covers both shapes. A seat reasoning from 'I saw a trailing rule survive' to 'my rule is safe' would be reading the platform's append as evidence about its own write.",
"practical_corollary": "A bare footer at the tail of a PR body with a rule above it is very likely the PLATFORM's, not yours downgraded — the caution AGENTS.md already states for issue comments now has a PR-body instance.",
"not_landed_in_the_row": "Deliberately. The file is 314/314 and this nuance needs its own cut. The row as landed does not claim the platform's append is eaten, so nothing here contradicts it and no re-edit is owed. Recorded in full as a comment on PR 13007 and flagged here for the PM.",
"measurement_artifact_preserved": "Recorded as a PR COMMENT, not a body edit — editing the body would destroy the very artifact the measurement is about."
},
"gates": [
{"name": "check:pm-skill-ratchet (ceiling)", "verdict_line": "✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/platform-readings.md is 314 lines (ceiling 314; headroom 0).", "exit": 0},
{"name": "check:pm-skill-ratchet (max-line)", "verdict_line": "✓ check-skill-line-ratchet self-test: 111 cases pass. — includes ✓ budget is 120 bytes; widest line in the file measures 120", "exit": 0},
{"name": "check:pm-skill-id-lint", "verdict_line": "✓ check-skill-id-lint: 23 file(s) clean (pattern /#[0-9]{3,}/g).", "exit": 0},
{"name": "check:pm-governed-prose", "verdict_line": "✓ check-governed-prose: 2 instruction surface(s) name all 5 registered governed surfaces (docs/adr/** · .claude/** · skills/** · AGENTS.md · CLAUDE.md) and claim no others.", "exit": 0},
{"name": "check:skill-frame-sync", "verdict_line": "✓ check-skill-frame-sync: 4 copies of the decision frame are structurally isomorphic across 3 files", "exit": 0},
{"name": "check:skill-frame-freshness", "verdict_line": "✓ check-skill-frame-freshness: the decision frame in this tree is current with origin/main (fetched just now).", "exit": 0},
{"name": "check:pm-governed-merges", "verdict_line": "✓ check-governed-merges --self-test: 206 assertions ...", "exit": 0},
{"name": "check:agent-test-spelling", "verdict_line": "✓ check-agent-test-spelling: 0 violations — 395 file(s) · 4455 bare -- token(s) · 1214 launcher-rooted run(s) · 9 separator(s) JUDGED", "exit": 0},
{"name": "check:doc-authoring", "verdict_line": "✓ doc authoring guard: 392 files clean — no bare metadata literals.", "exit": 0},
{"name": "check:doc-formula-expressions", "verdict_line": "✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 425 files / 1453 TS blocks judged clean by @objectstack/formula.", "exit": 0},
{"name": "check:nul-bytes (every-edit convention)", "verdict_line": "check-nul-bytes: OK (scanned 7220 text file(s) -- 7220 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes).", "exit": 0},
{"name": "check-governed-queue-guard (NOT MEASURED, not red)", "verdict_line": "⛔ Governed Surface Queue Guard: could not read GITHUB_EVENT_PATH — its own text says 'could not look' must never exit 0 here; it runs with the event payload in CI.", "exit": 1}
],
"tests": "Union derived mechanically with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` (it read the change set from git itself; 8 path-matched families) and run under `scripts/pm/os-verify-lock.sh` with slot issue-12909, AFTER the final commit, on head 2e9ff3e2b. Exit codes captured by redirecting each gate to its own file BEFORE any pipe — no tail status read as a gate verdict. Verdict lines above are the gates' own printed lines, not a bare $?. The ratchet reading is quoted POSITIVELY: the gate names this file and states 314 lines against a ceiling of 314. `@objectstack/lint` was built inside the same locked run because check:doc-formula-expressions requires it; the quoted verdict is the post-build reading. No changeset owed (one .claude markdown file, releases nothing).",
"labels": "skip-changeset applied via the MCP fallback because this seat's repo-scoped REST probe returned 403 (GitHub access is not enabled for this session), so the additive POST endpoint is unreachable. Read current values via the zero-quota public-repo payload channel (data-name= spelling, the PR-side one) = {size/s}; union-wrote {size/s, skip-changeset}; comparative read-back returned {documentation, size/s, skip-changeset} — nothing from the union missing, and documentation was added by the labeler after my read, not stripped by my write; delayed second read ~3 minutes later still returns all three. PR confirmed still draft.",
"mcp_calls": "12 — issue get, issue comments, two PR reads for premise verification, create PR, PR read-back, label write, PR comment, two searches (dedup + positive control), finding create, this report comment.",
"out_of_scope_findings": [
"filed as #13008: references/rest-channel.md header quotes a stale default read order (git -> REST -> MCP/GraphQL) while the source it names by name, platform-readings.md, has said git -> payload -> REST -> MCP/GraphQL since 2026-08-25 — the omitted tier is the zero-quota payload channel, and the population that needs it most is exactly the 403-gated seat the header degrades for. Unassigned, unlabelled, left for PM triage; not fixed here (different defect class, and rest-channel.md is a separate governed surface at 87/87 zero headroom with its line 3 already at 118 of 120 bytes)."
]
}
Activity
os-litant commentedon Aug 28, 2026
Claim: Director/skills seat, session
session_01MnijPVVDakqK2J335JoJtqBranch:
claude/issue-12909-prbody-sanitizer-factWorktree:
../objectstack-12909Domain: skills
File surface:
.claude/skills/pm-dispatch/references/platform-readings.mdonly — one fact row beside the existing sanitizer entries (314/314 zero-headroom, net 0 + cut ledger).Container & model: opus — references/** carries no path mandate (derived live today; mandate globs unchanged through the day's landings). Clause-② limb: no.
Serial: cleared — platform-readings queue ② (the label-read row) merged 14:10Z; this is queue ③, the last standing entry for the file. Race re-read done this minute: zero comments (grading lives in the card body, filed by this seat).
Generated by Claude Code
os-litant commentedon Aug 28, 2026
os-dev-report
{ "issue": 12909, "status": "done", "branch": "claude/issue-12909-prbody-sanitizer-fact", "pr_number": 13007, "pr_url": "https://github.com/objectstack-ai/objectstack/pull/13007", "pr": "https://github.com/objectstack-ai/objectstack/pull/13007", "draft": true, "net_lines": 0, "premise_still_valid": true, "stopped": false, "summary": "One fact row landed in .claude/skills/pm-dispatch/references/platform-readings.md as a third sibling in the write-side sanitizer cluster (issue body / comment / PR body), so the naming convention itself carries the cross-reference at zero line cost. The row states: a PR body ending in a --- horizontal rule followed by the attribution footer loses both while the write call reports success; re-writing the footer without the rule survives; issue comments are unaffected in either spelling, so the failure is both spelling-conditional and surface-conditional and a footer verified in a comment proves nothing about a PR body; the operative rule is a bare footer plus a body read-back, which is the only detection. Premise was re-verified rather than inherited: both cited bodies were read on origin/main and both end with a Provenance heading, the session URL as durable prose, and a bare footer with no preceding rule, created one minute apart on 2026-08-28. File is 314/314 with 14 insertions and 14 deletions.", "cut_ledger": [ { "cut": "The 写侧 sanitizer 作者规则 row, 6 lines to 3 (saves 3).", "why": "It restated the author rule, whose home is AGENTS.md under 'GitHub mutates body BYTES' — fences do not protect, spell poison-shaped tokens in words, read the body back after any less-than fragment. AGENTS.md in the same breath delegates the measured shapes to this table, so the division of labour was already written down and this table held a narrow copy of the other half. .claude/agents/os-dev.md states the same rule explicitly: 一条规则一个家;此处不再复制窄版.", "kept_because_this_is_their_only_home": "the entity spelling for a literal angle bracket, and bare identifiers surviving (what keeps the SKILL extraction contract valid)", "surviving_homes": [ "改花括号占位符 / 整句用词描述 -> AGENTS.md ('or are described in words'); os-dev.md carries the placeholder spelling 一律改占位词拼写", "⛔ 评论不豁免 -> the next row in this table is the comment-truncation measurement itself, and the new PR-body row now states the surface-conditionality explicitly", "⛔ 只回读标记不算验证 / 要回读尾部 -> os-dev.md 干净收尾: 写完读回那条评论到尾部, and PR 正文同欠一次全文回读" ] }, { "cut": "The credential-discrimination clause in the REST-availability row (saves 1).", "why": "references/rest-channel.md already carries it verbatim in its 通道边界 block: repo-scoped probe first, then 403 leads to one /rate_limit call, 15000/hr means live credentials refused by repo-scoping while 60/hr or an auth error means no credentials. That file delegates only 按班矩阵 and 降级梯 back to this table, and both stay here untouched. Replaced by a pointer.", "genuinely_lost_and_stated_not_hidden": "the parenthetical that one seat's token measured as a 14-byte placeholder string, and the phrase 两形同症不同治. The operative discrimination they decorate survives in full at the named home." } ], "no_rewrap_line_buying": "Both cuts delete content that is stated in full elsewhere. The re-wrapping visible in the diff is the consequence of removing bytes from the middle of a wrapped paragraph, not a reflow to buy lines. Widest line in the file measures 120 bytes, at the cap and not over it.", "footer_survival_measurement": { "what_was_written": "PR 13007 body ends with the bare attribution footer and NO preceding --- rule, exactly the spelling the landed row prescribes.", "read_back_result": "SURVIVED VERBATIM. Full body read-back immediately after create returns the bare footer intact at the end of the authored text. This is a fourth measurement in the same class and it agrees with the row.", "second_finding_same_read": "The platform then APPENDED its own copy beneath the surviving one — two blank lines, a --- rule, and a second footer in the session-URL spelling this seat never wrote. That trailing rule-plus-footer was NOT eaten.", "what_it_sharpens": "The eat is not unconditional. What was measured as eaten is the AUTHOR-WRITTEN rule-then-footer present in the payload at create time; the platform's append is added after whatever eats it has already run. So the fact is not 'trailing rules get eaten', and the row's operative rule (bare footer plus read-back) is what actually covers both shapes. A seat reasoning from 'I saw a trailing rule survive' to 'my rule is safe' would be reading the platform's append as evidence about its own write.", "practical_corollary": "A bare footer at the tail of a PR body with a rule above it is very likely the PLATFORM's, not yours downgraded — the caution AGENTS.md already states for issue comments now has a PR-body instance.", "not_landed_in_the_row": "Deliberately. The file is 314/314 and this nuance needs its own cut. The row as landed does not claim the platform's append is eaten, so nothing here contradicts it and no re-edit is owed. Recorded in full as a comment on PR 13007 and flagged here for the PM.", "measurement_artifact_preserved": "Recorded as a PR COMMENT, not a body edit — editing the body would destroy the very artifact the measurement is about." }, "gates": [ {"name": "check:pm-skill-ratchet (ceiling)", "verdict_line": "✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/platform-readings.md is 314 lines (ceiling 314; headroom 0).", "exit": 0}, {"name": "check:pm-skill-ratchet (max-line)", "verdict_line": "✓ check-skill-line-ratchet self-test: 111 cases pass. — includes ✓ budget is 120 bytes; widest line in the file measures 120", "exit": 0}, {"name": "check:pm-skill-id-lint", "verdict_line": "✓ check-skill-id-lint: 23 file(s) clean (pattern /#[0-9]{3,}/g).", "exit": 0}, {"name": "check:pm-governed-prose", "verdict_line": "✓ check-governed-prose: 2 instruction surface(s) name all 5 registered governed surfaces (docs/adr/** · .claude/** · skills/** · AGENTS.md · CLAUDE.md) and claim no others.", "exit": 0}, {"name": "check:skill-frame-sync", "verdict_line": "✓ check-skill-frame-sync: 4 copies of the decision frame are structurally isomorphic across 3 files", "exit": 0}, {"name": "check:skill-frame-freshness", "verdict_line": "✓ check-skill-frame-freshness: the decision frame in this tree is current with origin/main (fetched just now).", "exit": 0}, {"name": "check:pm-governed-merges", "verdict_line": "✓ check-governed-merges --self-test: 206 assertions ...", "exit": 0}, {"name": "check:agent-test-spelling", "verdict_line": "✓ check-agent-test-spelling: 0 violations — 395 file(s) · 4455 bare -- token(s) · 1214 launcher-rooted run(s) · 9 separator(s) JUDGED", "exit": 0}, {"name": "check:doc-authoring", "verdict_line": "✓ doc authoring guard: 392 files clean — no bare metadata literals.", "exit": 0}, {"name": "check:doc-formula-expressions", "verdict_line": "✓ check:doc-formula-expressions: 22 record-scoped formula example(s) across 425 files / 1453 TS blocks judged clean by @objectstack/formula.", "exit": 0}, {"name": "check:nul-bytes (every-edit convention)", "verdict_line": "check-nul-bytes: OK (scanned 7220 text file(s) -- 7220 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes).", "exit": 0}, {"name": "check-governed-queue-guard (NOT MEASURED, not red)", "verdict_line": "⛔ Governed Surface Queue Guard: could not read GITHUB_EVENT_PATH — its own text says 'could not look' must never exit 0 here; it runs with the event payload in CI.", "exit": 1} ], "tests": "Union derived mechanically with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` (it read the change set from git itself; 8 path-matched families) and run under `scripts/pm/os-verify-lock.sh` with slot issue-12909, AFTER the final commit, on head 2e9ff3e2b. Exit codes captured by redirecting each gate to its own file BEFORE any pipe — no tail status read as a gate verdict. Verdict lines above are the gates' own printed lines, not a bare $?. The ratchet reading is quoted POSITIVELY: the gate names this file and states 314 lines against a ceiling of 314. `@objectstack/lint` was built inside the same locked run because check:doc-formula-expressions requires it; the quoted verdict is the post-build reading. No changeset owed (one .claude markdown file, releases nothing).", "labels": "skip-changeset applied via the MCP fallback because this seat's repo-scoped REST probe returned 403 (GitHub access is not enabled for this session), so the additive POST endpoint is unreachable. Read current values via the zero-quota public-repo payload channel (data-name= spelling, the PR-side one) = {size/s}; union-wrote {size/s, skip-changeset}; comparative read-back returned {documentation, size/s, skip-changeset} — nothing from the union missing, and documentation was added by the labeler after my read, not stripped by my write; delayed second read ~3 minutes later still returns all three. PR confirmed still draft.", "mcp_calls": "12 — issue get, issue comments, two PR reads for premise verification, create PR, PR read-back, label write, PR comment, two searches (dedup + positive control), finding create, this report comment.", "out_of_scope_findings": [ "filed as #13008: references/rest-channel.md header quotes a stale default read order (git -> REST -> MCP/GraphQL) while the source it names by name, platform-readings.md, has said git -> payload -> REST -> MCP/GraphQL since 2026-08-25 — the omitted tier is the zero-quota payload channel, and the population that needs it most is exactly the 403-gated seat the header degrades for. Unassigned, unlabelled, left for PM triage; not fixed here (different defect class, and rest-channel.md is a separate governed surface at 87/87 zero headroom with its line 3 already at 118 of 120 bytes)." ] }Generated by Claude Code
Generated by Claude Code