Skip to content

docs(pm-skills): a repo with zero legacy statuses reads the combined-status endpoint as an empty-set default - #18775

Merged
os-justin merged 2 commits into
mainfrom
claude/issue-18733-combined-status-pending-trap
Sep 17, 2026
Merged

os-justin merged 2 commits into
mainfrom
claude/issue-18733-combined-status-pending-trap

Conversation

@os-justin

Copy link
Copy Markdown
Collaborator

Fixes #18733

Clause-②: no

One file: .claude/skills/pm-dispatch/references/platform-readings.md, held at 466 / 466
(ceiling 466, headroom 0; widest line in the file still 120 B at :464, untouched). No ceiling
raised, net line change 0: one row added, one row retired in the same file.
skip-changeset — .claude/** is shipped by no package's files[], so nothing published moves.

The trap

:28 is the row that sends a reader to GET /commits/{sha}/status. On a commit that carries no
legacy commit statuses at all
, that endpoint answers state: "pending" with total_count: 0 —
the API's default for an empty set, not a verdict about anything. In a repo whose gates are all
check-runs the field is pending forever and means nothing, while reading exactly like "gates still
running": a seat that waits on it waits forever, and a seat that reads pending as not-yet-red can
read a red PR as merely unfinished. The gate reading is /commits/{sha}/check-runs.

Measurements

Three objectstack-ai/cloud heads — the card's table, verbatim (all fully green):

PR head check-runs combined status
objectstack-ai/cloud#2319 03c170ec 2/2 completed/success (2026-09-16T16:13:44Z) state: "pending", total_count: 0
objectstack-ai/cloud#2320 b4b1c5c4 2/2 completed/success (2026-09-17T15:47:03Z) state: "pending", total_count: 0
objectstack-ai/cloud#2321 23e928b7 2/2 completed/success (2026-09-17T17:14:33Z) state: "pending", total_count: 0

This repo, fetched through the REST proxy at 2026-09-17T19:57Z–19:59Z — measured here, ⛔ not
retyped from the card. Raw state / total_count on both sides:

commit /commits/{sha}/status its statuses[] /commits/{sha}/check-runs
d852dae1 (PR #18750 head) state: "success", total_count: 1 Vercel / success total_count: 40, 29 completed/success + 11 completed/skipped
702b4241 (PR #18756 head) state: "success", total_count: 1 Vercel / success total_count: 39, 28 completed/success + 11 completed/skipped
2f38ee0d (PR #18758 head) state: "success", total_count: 1 Vercel / success total_count: 32, 24 completed/success + 8 completed/skipped
95e17452 (main tip at branch point) state: "success", total_count: 1 Vercel / success total_count: 59
e2050cef (main) state: "success", total_count: 1 Vercel / success total_count: 71
bc2ec806 (main) state: "success", total_count: 1 Vercel / success total_count: 64

The reading that scopes the row: objectstack-ai/objectstack posts a legacy Vercel status on
every commit, so the empty-set default never arises here. One further control makes that exact,
taken on this branch's own first commit 55c8e844 at 19:59:48Z, seconds after the push and before
any workflow had started:

commit /commits/{sha}/status its statuses[] /commits/{sha}/check-runs
55c8e844 (this branch, pre-CI) state: "pending", total_count: 1 Vercel / pending total_count: 0

This repo answers pending too — for the opposite reason. There, total_count: 1 and a real
Vercel status genuinely in flight; in the card's three cloud heads, total_count: 0 and no status
at all. total_count is the discriminant, state is not, which is why the row names both
fields rather than the word pending alone.

⇒ the row is scoped 「零 legacy status 的仓」, ⛔ not 「所有仓」.

The row added — :29, 118 B

- 零 legacy status 的仓恒答空集默认值 `pending`+`total_count: 0`,⛔ 非门禁读数,门禁读 check-runs。

It sits directly under :28 「unstable 可源自 check-runs 看不见的 commit STATUS(如 Vercel)⇒
③ 另读 /commits/{sha}/status」, which names this repo's legacy status and points at the endpoint.
The new row inherits that subject — the idiom the block already uses at :30, :63, :65, :67 —
and states the condition under which the endpoint stops answering the question.

Against the card's proposed line, at the file's ≤ 120 B cap: 「那是空 legacy 集合的默认值」 is kept as
「空集默认值」 (「legacy」 already stands in the row's first clause), 「⛔ 不是门禁读数」 as
「⛔ 非门禁读数」, and 「门禁只认 /commits/{sha}/check-runs 的 completed/success」 as
「门禁读 check-runs」. What changed on purpose is the scope: the card wrote 「在只有 check-runs 的
commit 上」, and the measurement above makes it a property of the repo, which is where the legacy
status is configured.

The payment — :343 retired, 85 B

before (:343, pre-edit numbering):

- 署名页脚的写侧变异按通道与输入双重定域,⛔ 不是一条定律。

Why the tree no longer needs it as a separate line: it is a preamble that states the block's own
conclusion
, which the same block states again at its end, more precisely. It carried two clauses,
and both are held:

the retired clause where it is held (post-edit numbering)
「按通道与输入双重定域」 :355 「⇒ 形态随动作与送出体尾部变,改侧还随通道变」 — three loci named where the retired row named two, and :342 「⇒ 失效既依拼写又依载体:评论里验过页脚对 PR 正文什么都没证明」 states the same localisation at the point of use
「⛔ 不是一条定律」 :355 「⛔ 不由任一条推其余,写后必回读」, and :351 「⛔ 无受控对照(同通道只差该块两送)⇒ 是拟合不是定论,⛔ 不外推到别的动作」

No fact leaves the corpus: every reading the block holds (:340–:343, :344–:354) is untouched,
and the row that generalises them — the one a reader reaches after the evidence rather than
before it — keeps the generalisation with the read-back prescription attached. The retired row is not
one of the nine PR #18689 adopted, not one of the three 配额 rows #18744 names (:137, :145,
:146), and not one of the four PR #18741 landed today (:47–:49, :163).

The ratchet's standing one-file exception 「唯一例外:platform-readings.md 增量抬上限到落地行数,免
决策卡,记 ruledRaises 引常设裁决」 (scripts/pm/check-skill-line-ratchet.mjs) was not taken:
a payment with zero fact loss was available, so the ceiling stays at 466 and no ruling is spent.

② re-read — CARRIED, no row, and a premise correction to the card

The card's ② says: "The existing entry says /search/* is refused by the egress proxy. What it does
not say is the shape of the refusal." On main that sentence is false. Three consecutive rows
say exactly the shape (pre-edit numbering, the seat's :209–:211):

- 会话代理只服务 repo-scoped 路径,`/search/*` 的 403 体解析成净零。
- 代理回 403 加体 sessions are bound to their configured repositories,而那是合法 JSON。
- 读 `total_count` 得 None、打印成 total: None,与真空集只差一个字符,而请求根本没跑。

Mapped against the card's proposed addition, clause by clause: 「它的拒绝是成功形状的」 is
:209's 「403 体解析成净零」; 「回 JSON」 is :210's 「而那是合法 JSON」; 「total_count 键缺失 ⇒
.get() 读成 None、打印出来像 0」 is :211, which adds the measurement the card does not
have — 「与真空集只差一个字符,而请求根本没跑」.

The only residual is the card's prescription 「断言键在,⛔ 不断言值」, and that is precisely what
the open PR #18666 (for #18454, governed, awaiting the maintainer) lands on that same line. Its hunk
on this file is one line, @@ -209,7 +209,7 @@:

-- 读 `total_count` 得 None、打印成 total: None,与真空集只差一个字符,而请求根本没跑。
+- 读 `total_count` 得 None、打印成 total: None,与空集只差一字符;缺键即拒绝,判别式是状态码。

「缺键即拒绝」 is the assert-the-key prescription in the file's own voice, and 「判别式是状态码」 is
one notch sharper than the card asked for: it names the discriminant rather than the symptom. So the
residual is zero once #18666 lands, and the line a residual row would have to touch is #18666's own
hunk — a reserved line. ⇒ no row for ②, and the search_issues block :192–:201 is untouched.

The card's own attribution note observes that the domain:spec @ objectui seat recorded the same
shape independently and argues that is the reason to put it in the shared table. It is in the shared
table; what the card measured is that a reader did not find it there.

Reserved rows — verified against the open PRs' hunks, by content

Ratchet before / after

node scripts/pm/check-skill-line-ratchet.mjs, both pins, exit 0 on both sides:

main  447e2e895a  ✓ .claude/skills/pm-dispatch/references/platform-readings.md: widest table row is 0 bytes (pin 0; headroom 0).
main  447e2e895a  ✓ .claude/skills/pm-dispatch/references/platform-readings.md is 466 lines (ceiling 466; headroom 0).
head  d6bc4775e5  ✓ .claude/skills/pm-dispatch/references/platform-readings.md: widest table row is 0 bytes (pin 0; headroom 0).
head  d6bc4775e5  ✓ .claude/skills/pm-dispatch/references/platform-readings.md is 466 lines (ceiling 466; headroom 0).

Widest line in the file, measured per row in bytes: 120 B at :464 on both sides, unchanged; no
row exceeds 120 B on either side; the added row is 118 B.

 .claude/skills/pm-dispatch/references/platform-readings.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Derived gates

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack from the worktree,
no hand-fed path list — 18 families, all re-run at the final head d6bc4775e5 with $? captured
before any pipe:

node scripts/check-closing-keyword-parity.mjs :: exit 0
node scripts/check-closing-keyword-parity.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/pm/check-governed-queue-guard.mjs --self-test :: exit 0
node scripts/pm/check-harness-current.mjs --self-test :: exit 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions :: exit 0
pnpm check:agent-test-spelling :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:pm-governed-merges :: exit 0
pnpm check:pm-half-states :: exit 0
pnpm check:pm-skill-id-lint :: exit 0
pnpm check:pm-skill-ratchet :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:required-contexts :: exit 0
pnpm check:skill-frame-sync :: exit 0
pnpm check:watch-hint-literal :: exit 0

Reconciled: dispatch-gates.mjs --ran reads 「18 derived famil(ies) accounted for — 18 run, 0
NOT-MEASURED (a DERIVED zero — all 18 recorded an exit code and none of them is 3)」.

On the first pass check:doc-formula-expressions exited 3 — PREREQUISITE NOT MET, its own text
「Nothing was measured: this gate exited before running a single check」; its two declared
prerequisites were built under scripts/pm/os-verify-lock.sh (VERDICT command-exit 0, held 158 s,
waited 0 s) and it then exited 0, which is the reading recorded above.

Also run, outside the derived 18: pnpm check:pm-settings-deny-roster exit 0 — the derivation marks
it 「roster under .claude, which one of your paths is in」, where silence is evidence in neither
direction. Repo-wide pnpm lint (eslint . --no-inline-config): exit 0 at d6bc4775e5.


Generated by Claude Code

…status endpoint as an empty-set default

`GET /commits/{sha}/status` answers `state: "pending"` + `total_count: 0` on a
commit that carries no legacy commit statuses at all. That is the API's default
for an empty set, not a verdict about the gates, and it reads exactly like
"gates still running" — a seat that waits on it waits forever.

One row added beside the row that sends a reader to that endpoint, scoped to the
repos the trap is live in. Paid in the same file by retiring the footer-mechanism
preamble, whose whole content is held twelve rows later, more precisely.

Claude-Session: https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu
Co-authored-by: Claude <noreply@anthropic.com>
@os-justin os-justin added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 17, 2026 — with Claude
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 17, 2026

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: d6bc4775e5fefd6653063f8234e80b2ccfab9f06

Review of record on the PR thread (the queue guard's merge_group leg reads this thread and, since PR #18738, the card thread too). In-seat review of PR #18775 (#18733) by the dispatching domain:skills seat, 2026-09-17T20:39Z, read against the PR's diff, files and head on GitHub and a scratch worktree at the head — ⛔ not against the dev report (5720690168). Clause-②: no on the PR body and on the claim (5720262737); check-clause2-carriers --pair 18775 answers 0. Fact-layer landing: the one file is .claude/skills/pm-dispatch/references/platform-readings.md, so check-governed-merges --pr 18775 answers GOVERNED (exit 3, .claude/** ×1) and the seat lands it on this review of record — the references/** carve-out the SKILL names.

① Derived judgments

② Semver level

None — no package is touched; skip-changeset on the PR is correct for .claude/**.

③ Boundary flags

Implemented-by: claude/issue-18733-combined-status-pending-trap
Reviewed-by: session_01Gqi43smmqjJ5sUrhfoPeKu

VERDICT: PASS


Generated by Claude Code

@os-justin
os-justin marked this pull request as ready for review September 17, 2026 20:39
@os-justin
os-justin added this pull request to the merge queue Sep 17, 2026
Merged via the queue into main with commit 6de7a2d Sep 17, 2026
32 checks passed
@os-justin
os-justin deleted the claude/issue-18733-combined-status-pending-trap branch September 17, 2026 21:01
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…lete — probe on the branch the dev keeps (objectstack-ai#18808)

Fixes objectstack-ai#18774

`Clause-②: no`

Two files, both fact layer, both held at their ceilings:
`.claude/skills/pm-dispatch/references/platform-readings.md`
**466 / 466** and
`.claude/skills/pm-dispatch/references/dispatch-runbook.md` **241 /
241** — net line change **0** in
each, three rows added and three retired in the first, one added and one
retired in the second, every added row ≤ 120 B.
No ceiling raised, no ruling spent. `skip-changeset` — `.claude/**` is
shipped by no package's `files[]`, so nothing
published moves.

## The defect

A dispatched dev's container **creates** a remote branch and **cannot
delete** one on either channel, so any instruction
that says "probe before you write anything" manufactures a permanent
artefact: the dev reads "before writing anything" as
"not on your working branch", pushes a throwaway, and then cannot remove
it. The leftover is not inert — a
`claude/issue-NNNN-*` head is exactly what the AGENTS.md claim pre-check
greps for, so a stray branch answers "already
claimed" for a card nobody is working. That failure direction is the
silent one: a live card read as claimed, with no red
signal anywhere. This PR does not touch the capability (proxy policy is
not this repo's to decide) and does not touch the
pre-check text at AGENTS.md :459–:463 (rules layer, another serial). It
records the capability where branch and ref facts
live, and moves the instruction so the probe lands on the branch the dev
is keeping anyway.

## The capability, re-derived on this branch — ⛔ not taken from the card

Both attempts were made **against this PR's own working branch**, the
one that stays either way. ⚠️ No branch was created
in order to fail to delete it — that is the card's whole point.

**Channel ① — git, 2026-09-17T21:47Z**

```text
$ git push origin --delete claude/issue-18774-probe-branch-undeletable
✓ check:commit-card-trailers: 0 commit message(s) on this push carry no card relation and no model identifier in the trailer pair.
fatal: --negotiate-only needs one or more --negotiation-tip=*
warning: push negotiation failed; proceeding anyway with push
error: RPC failed; HTTP 403 curl 22 The requested URL returned error: 403
send-pack: unexpected disconnect while reading sideband packet
fatal: the remote end hung up unexpectedly
Everything up-to-date
$ echo $?
1
```

**Channel ② — REST, 2026-09-17T21:47Z**

```text
$ curl -X DELETE https://api.github.com/repos/objectstack-ai/objectstack/git/refs/heads/claude/issue-18774-probe-branch-undeletable
HTTP 403
{"message":"Write access to this GitHub API path is not permitted through this proxy.","documentation_url":"https://docs.anthropic.com/en/docs/claude-code/github-actions"}
```

**And the ref survived both**, read back immediately after:

```text
$ git ls-remote --heads origin claude/issue-18774-probe-branch-undeletable
9846f27	refs/heads/claude/issue-18774-probe-branch-undeletable
```

⇒ the card's premise **holds for this container**, on a third branch and
in a third session. One correction to the tree's
existing wording, which the rows below carry: the old row said
「容器发不出分支删除 refspec」 — the container **does**
send it. The RPC is made and answered **403**; nothing is held back
locally. Same outcome, different mechanism, and the
mechanism is what tells a reader not to go looking for a local git
config to fix.

## The census — the seat's, cited; and my re-take

**Handed down by the `domain:skills` seat, 2026-09-17T21:45Z** (⛔ not
retyped as mine): `git ls-remote --heads origin
'refs/heads/claude/issue-*'` answered **291** heads; against the newest
1200 PRs (back to 2026-09-05) — **26** with an
open PR, **8** with a closed / merged PR whose branch was never deleted,
**257** with **no PR in that window**.

**My re-take, 2026-09-17T21:48Z — one `ls-remote`, zero writes:**

```text
$ git ls-remote --heads origin 'refs/heads/claude/issue-*' | wc -l
291
```

⇒ the population **did not move** in those three minutes. The two named
strays are **both present**:

```text
d5e64d8	refs/heads/claude/issue-18545-formula-can-function
f22c632	refs/heads/claude/issue-18734-workflow-scope-probe
```

`f22c63215` matches the sha the card names. ⛔ I did not re-take the 26 /
8 / 257 split — that is the seat's reading,
used as an existing fact, and only the head count was re-checked for
increment.

## ① `platform-readings.md` — the rows

They land in the `## 读数陷阱` block at `:374`–`:379`, whose subject is
already refs: the row above them is the
zero-commit probe-branch reading, the row below is the two-read
criterion and then the ⛔ against reading a
`ls-remote | grep issue-` hit as a claim. ⇒ the capability, its
consequence and the claim reading now sit on the
criterion they qualify, instead of one being 57 rows away in the same
section.

**Added — three rows, verbatim, with byte counts**

```text
:375  114 B  - 两道皆 403:`git push --delete` 回 RPC failed,`DELETE /git/refs/heads/…` 回 not permitted,⛔ 不重试。
:376  114 B  - ⇒ 探针分支永久堆在 origin 上,只有带删权的手(维护者)清得掉,本闭环内无人有该权。
:378  117 B  - 「分支在」不是「卡已认领」:认领是 `Claim:` 评论,分支只是线索;291 个头里 257 个无 PR。
```

`:374` is **amended in place**, no line bought, to carry the corrected
mechanism and the replication count:

```text
before  - 零提交的探针分支不是在飞工作:容器发不出分支删除 refspec。
after   - 零提交的探针分支不是在飞工作:容器建得出远端分支却删不掉,两会话两分支实测。   (112 B)
```

**Paid — three rows**

| retired | why the tree no longer needs it as a separate line | what it
merged into |
|:--|:--|:--|
| `:375` (pre-edit) — the git-channel row, spelling the failure as
「send-pack: unexpected disconnect,三次退避全败,同会话普通 push 正常」 with a
placeholder branch token | it is a **narrative of one attempt**, and its
mechanism claim is the one this PR measured wrong: the disconnect is the
tail of an HTTP 403, not the signal. Its 「同会话普通 push 正常」 half is the
create-vs-delete asymmetry, which is what the amended `:374` now states
positively. Per this corpus's own 行文纪律 — 「原话仅限操作性判据;⛔ 实测叙事不进操作文本」 | the
added `:375`, which names **both** channels and keeps the operational ⛔
|
| `:376` (pre-edit) — 「⇒ 测量型派发留下的探针分支永久堆在 origin 上。」 | it states the
accumulation and stops there, leaving the reader with no disposition.
The one thing a reader needs next is **who can clear it**, and the
answer is nobody in this loop | the added `:376`, same 「⇒」 position,
same accumulation clause, plus the maintainer-only disposition |
| `:431` (pre-edit) — 「分支删除被拒有第二形态:代理回 403,与既有 send-pack 断连同处置 ⇒ 不可删,⛔
不重试。」 | the 「第二形态」 framing exists only because the fact was **discovered
in two parts, 57 rows apart**. Once one row names both channels, a
second row whose entire content is "and there is another one of these,
treat it the same" is pure discovery history. Its two operational
clauses — 不可删 and ⛔ 不重试 — are both inside the added `:375` | the added
`:375` |

⛔ No re-wrap was used as currency: nothing here is two wrapped halves of
one sentence pushed together to free a line.
Each retirement drops content — an attempt narrative, a dangling
consequence, a duplicate discovered twice.

## ② `dispatch-runbook.md` — the row

**Added — one row, 119 B**, last in `## 派发词构造细则` (`:236`), directly
under the measurement-first row, because a
measurement-first dispatch is the shape that produced the artefact on
`origin` today:

```text
:236  119 B  - 探针做在 dev 要留的分支上,⛔ 不用一次性分支:建得出删不掉,遗留分支被预检读成认领。
```

**Paid — `:201` (pre-edit), 59 B**

```text
- ⛔ 不让 N 个 dev 各重跑同样贵的 GraphQL 读。
```

Why the tree no longer needs it as a separate line: it is the
**rationale half of the row above it**. `:200` already
carries the operational rule — 「派发那刻现取、随派发词下发的两份读数,判据同一:PM 已有的读数下发一次」 —
and the
prohibition it spells is stated operationally **twice more in the same
block**: at `:201` (post-edit) 「⛔ 不让 dev 枚举
全 farm」 for the gate-list reading, and at `:204` (post-edit)
「当既有事实用,只复核其后的增量,⛔ 不重跑」 for the
dedupe reading, which is also where the dev-side half of the contract is
pointed at. Its cost clause is held by
`:203` (post-edit) 「② 去重或看板读数取一次(查询式加时间戳)整批共用」. No rule leaves the
corpus.

## Reserved rows — verified by content on my head

Every change in this diff is at `:374` or below, so **nothing above
`:374` moved by a single byte or a single line**.

- `:10`–`:12` (objectstack-ai#18469 PR-A) — byte-identical, and 362 lines above the
first hunk.
- PR objectstack-ai#18775's `:29` — byte-identical: 「零 legacy status 的仓恒答空集默认值
`pending`+`total_count: 0`,⛔ 非门禁读数,门禁读 check-runs。」
- **PR objectstack-ai#18666's band** — read against `/pulls/18666/files` at
2026-09-17T21:52Z: its only hunk on this file is
`@@ -209,7 +209,7 @@`, one changed line, the `total_count` row. **On
`main` and on my head the numbers are the same**:
the changed line is `:212`, the hunk's context window `:209`–`:215`.
Every byte in `:209`–`:215` is unchanged, so
  objectstack-ai#18666 still applies cleanly.
- `:432` (objectstack-ai#18469 PR-A) — 「harness 按内容拒写:同会话派发 PR 上 PASS 拒为
`[Self-Approval]`」 is **still at `:432`** on my
head: the insertion at `:378` and the retirement at `:431` cancel across
it. ⚠️ The row I retired is `:431`, which is a
**different row** — the same off-by-one PR objectstack-ai#18775's body diagnosed, and
the reason I checked the content rather than
  the number before touching anything.
- Overlap re-checked: 28 open PRs at 2026-09-17T21:52Z, the same 28 the
seat read at 21:46Z — no increment, and objectstack-ai#18666
  is still the only open PR touching this file.
- objectstack-ai#18798 (p1, dispatched beside this card) edits the reading-discipline
rows; those live in the instrument/zero-hit block
higher in `## 读数陷阱`. My rows are at `:374`–`:379`, the container-restart
/ probe-branch neighbourhood. No overlap.

## Ratchet before / after, and the diff

`node scripts/pm/check-skill-line-ratchet.mjs` — **exit 0 both times**:

| file | at `main` `9846f2763c` | at head `de12f71b8a` |
|:--|:--|:--|
| `platform-readings.md` | 466 lines (ceiling 466; headroom 0) | 466
lines (ceiling 466; headroom 0) |
| `dispatch-runbook.md` | 241 lines (ceiling 241; headroom 0) | 241
lines (ceiling 241; headroom 0) |

Widest-table-row pins are 0 for both files (neither carries a markdown
table) and are unchanged.

```text
$ git diff --stat 9846f27 HEAD
 .claude/skills/pm-dispatch/references/dispatch-runbook.md  | 2 +-
 .claude/skills/pm-dispatch/references/platform-readings.md | 8 ++++----
 2 files changed, 5 insertions(+), 5 deletions(-)
```

Five for five: 4 rows rewritten or inserted against 4 retired or
replaced on `platform-readings.md` (3 added + 1 amended
in place vs. 3 retired + 1 amended in place), 1 added against 1 retired
on `dispatch-runbook.md`.

## ⏹️ For the MAINTAINER — the stray branches, and the one hand that can
remove them

Both are on `origin` right now and **no account in this loop can delete
them** — the dev container and the triage seat
both 403 on both channels. The two commands, to be run by a hand with
delete rights:

```bash
git push origin --delete claude/issue-18734-workflow-scope-probe
git push origin --delete claude/issue-18545-formula-can-function
```

- `claude/issue-18734-workflow-scope-probe` —
`f22c63215c6935552f9761d1ec2bd59556c7b656`, one commit ahead of `main`,
no
PR. This is the branch the card names; it is what makes `ls-remote
--heads origin | grep issue-18734` answer **two**
  branches for one card.
- `claude/issue-18545-formula-can-function` —
`d5e64d8d9dae7b58bb7fa4aabccd95247b1fb9de`, no PR.

⛔ This PR's own branch, `claude/issue-18774-probe-branch-undeletable`,
is **not** on that list — it is live work and it
carries this PR.

## Gates

Derived from the worktree with `node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack`
(no hand-fed path list; change set 2 paths vs merge base `9846f2763`,
three-dot). All 18 run, exit code captured with
redirect-then-`$?`, never across a pipe:

| command | exit |
|:--|:--|
| `node scripts/check-closing-keyword-parity.mjs` | 0 |
| `node scripts/check-closing-keyword-parity.mjs --self-test` | 0 |
| `node scripts/check-comment-mask-corpus.mjs` | 0 |
| `node scripts/pm/check-governed-queue-guard.mjs --self-test` | 0 |
| `node scripts/pm/check-harness-current.mjs --self-test` | 0 |
| `pnpm --filter @objectstack/lint run check:doc-formula-expressions` |
0 |
| `pnpm check:agent-test-spelling` | 0 |
| `pnpm check:doc-authoring` | 0 |
| `pnpm check:driver-memory-census` | 0 |
| `pnpm check:nul-bytes` | 0 |
| `pnpm check:pm-governed-merges` | 0 |
| `pnpm check:pm-half-states` | 0 |
| `pnpm check:pm-skill-id-lint` | 0 |
| `pnpm check:pm-skill-ratchet` | 0 |
| `pnpm check:refd-timer-probe` | 0 |
| `pnpm check:required-contexts` | 0 |
| `pnpm check:skill-frame-sync` | 0 |
| `pnpm check:watch-hint-literal` | 0 |

⚠️ `check:doc-formula-expressions` answered **exit 3 — PREREQUISITE NOT
MET** on its first run (「@objectstack/lint — a
workspace package that is not built」), which is **not a finding and not
a measurement**. The gate names its own fix; it
was run: `pnpm exec turbo run build --filter=@objectstack/formula
--filter=@objectstack/lint --concurrency=2` through
`scripts/pm/os-verify-lock.sh` (`VERDICT command-exit 0 · held the lock
144s · waited 0s`), after which the gate answered
**exit 0**. The 0 above is the second run.

Reconciliation, exit codes recorded per family:

```text
$ node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.txt
Run reconciliation — 18 derived, 18 run, 0 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 18 derived famil(ies) accounted for — 18 run, 0 NOT-MEASURED
  (a DERIVED zero — all 18 recorded an exit code and none of them is 3).
```

Repo-wide `pnpm lint` (`eslint . --no-inline-config`) — **exit 0**, run
whole, not narrowed.

Control-character self-scan over both edited files, beyond
`check:nul-bytes`:
`grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'` on both paths — zero
matches (grep exit 1).

⛔ Not run here and not claimed: the 53 artifact-roster families, the 11
wide-population families, the 14
pending-changeset families and the CI-measured-only families the
derivation prints under their own headings — CI owns
those.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu)_

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…t on the write-side footer cell (objectstack-ai#18837)

Fixes objectstack-ai#18622

`Clause-②: no`

Written by session `session_01Gqi43smmqjJ5sUrhfoPeKu` on branch
`claude/issue-18622-edit-footer-provenance` — the session id is stated
here, in prose, because this card is about exactly the reason that is
necessary.

This is a re-derivation against the tip plus **one row**, paid in place.
The cell has grown a lot since the card was filed at 09:27Z (PR objectstack-ai#18689,
objectstack-ai#18713, objectstack-ai#18741, objectstack-ai#18775, objectstack-ai#18808 all landed on this file today), and most
of what the card asked for is already carried.

## The carried-map — six readings against `origin/main` `ad1f94e8ec`

The bands re-derived on this head: the write-side footer cell is
:340–:359, the issue-body cell :409–:414.

| # | reading | verdict | carried by |
|:--|:--|:--|:--|
| a | `POST /pulls` stores the author's session-URL footer verbatim |
**CARRIED** | :350 「尾部已是该块则一字不追加,两通道各实测两向 ⇒ 建侧通道不是变量,判据是送出体尾部。」 read
with :348 「第四形:建 PR 两通道同判 —— 送出体尾部不是 `---` 加页脚块时,追加一条同形页脚。」 — 「同形」 is
what makes the stored block the author's, the session-URL spelling
included |
| b | `PATCH /pulls` appends the BARE form | **CARRIED** | :345 「裸 REST
`PATCH /pulls` 追加一个裸页脚并保留既有 session-URL 页脚,差恰 58 字节。」 and :346
「同路送无页脚正文存回恰一条(平台裸形)⇒ 该格处方是不送页脚,⛔ 不是不重送正文。」 |
| b′ | ⇒ so an edited body silently loses which session wrote it |
**RESIDUAL** — and, as the card states it, *falsified* on this tip |
nothing. See below |
| c | ⇒ durable attribution on an edited body lives in body PROSE, not
in the footer | **CARRIED — in the rule layer, by design** | `AGENTS.md`
:433 「Durable attribution lives in body prose or a comment.」 and
`.claude/agents/os-dev.md` :403 「耐久归属写进正文散文或评论,⛔ 不循环重贴页脚;完整读数住 AGENTS.md
同条。」 Nothing in :340–:359 / :409–:414 carries it, and nothing should |
| d | an issue-body `PATCH` synthesises one bare block on a block-less
body and re-anchors a bare-block tail to one | **CARRIED** | :412 「issue
正文 `PATCH` 识别按整块:送全块或不送页脚都存回恰一条,已有页脚归一末尾不复制。」 — both arms in one row;
with :411 for the create-side +58 and :413 for the rule-less
counter-case (total two) |
| e | a `PATCH` appends a bare block under a session-URL tail |
**CARRIED** | :345, verbatim above — the bare one is appended, the
session-URL one is kept, difference exactly 58 bytes |
| f | `POST /pulls` stores a session-URL tail byte-exact | **CARRIED** |
:350 for the 「一字不追加」 half. The trailing-newline trim reported beside it
in PR objectstack-ai#18786 (10841 sent / 10840 stored) is :356 「平台在尾部 `---`
前后正反两向归一空行:比对正文只按首个差异偏移,⛔ 不按长度。」 — which is precisely why the file
already refuses to let length be the verdict;
`trailing-newline-stripped` is a declared clean class in
`scripts/pm/post-stamped.mjs` |

### Why (c) is not written into the fact table

The card itself says 「这不是新规则」, and the ratchet's own maintainer ruling
for `references/` reads: *「one rule per ≤120-byte line, no rule already
stated in SKILL.md」*. `AGENTS.md` :433 already states the prescription,
and `.claude/agents/os-dev.md` :403 explicitly routes the full reading
to it — 「完整读数住 AGENTS.md 同条」. Writing 「耐久归属写正文散文」 into the fact table
would be a third copy of a rule that this corpus has deliberately given
exactly one home, the same content-ownership call the 2026-09-01 ruling
made for the red-window rule (「红窗规则由 `platform-readings.md` 配额段独家持有」).
So the new row **points** at `AGENTS.md` instead of restating it.

### Why (b′) is the residual, and why the card's own wording needed
correcting

The card reads 「一次正文编辑会静默丢掉「是哪个会话写的」」 — unconditionally. On this tip
that is **false as stated**, and :345 is what falsifies it: an
un-stripped `PATCH /pulls` **keeps** the existing session-URL block and
adds a bare one, +58 bytes — attribution intact, merely duplicated. The
loss happens on exactly one path: the one the cell itself prescribes at
:346–:347 (send no footer, strip *both* spellings). Then the stored
body's only attribution block is the platform's bare form, which carries
no session id.

A reader can get this wrong in both directions today: from :345 alone,
"my session-URL footer is preserved, attribution is safe"; from the card
alone, "any edit loses it". The residual sentence is the price tag on
the cell's own prescription, and nothing in the cell states it.

## The one row, and what paid for it

**Added**, at :348, immediately after the two rows whose prescription it
prices (118 bytes):

```
- ⇒ 代价是归属:裸形无 session id,按此剥净的 PR 正文不载明哪个会话写的;另置见 AGENTS.md。
```

**Retired**, the old :353 (114 bytes):

```
- 删页脚那条读数是唯一不合此判据的观察,通道已拒 ⇒ 活通道无反例,写后仍必回读。
```

Where its content survives, all of it inside the same cell:

| clause of the retired row | survives at |
|:--|:--|
| 「删页脚那条读数」 + 「通道已拒」 | :344 「MCP `update_pull_request` 包装器删掉 PR
正文的页脚块;该通道锁 1 已拒,读作历史。」 |
| 「活通道无反例」 | the same :344 — 「读作历史」 is what makes the single deleting
observation a dead-channel one |
| 「写后仍必回读」 | :355 「⇒ 形态随动作与送出体尾部变,改侧还随通道变;⛔ 不由任一条推其余,写后必回读。」 and :343 「⇒
PR 正文页脚不带前置横线,且写后回读正文 —— 那是唯一检测手段;评论两形皆可。」 |

⛔ No re-wrap was used as currency; ⛔ no ceiling was raised; net line
change is **zero**. Nothing outside the write-side footer cell was
touched — `git diff --stat` is `1 insertion(+), 1 deletion(-)`.

## Reserved bands — verified by content, md5 before and after

| band | content | md5 before | md5 after |
|:--|:--|:--|:--|
| :10–:12 | objectstack-ai#18469 PR-A (timeline `added_to_merge_queue` rows) |
`4b1a925b6f6004ebd8bf05ea4f477d24` | `4b1a925b6f6004ebd8bf05ea4f477d24`
|
| :29 | PR objectstack-ai#18775's zero-legacy-status row |
`9a1ac5fab8b1c0351d95472397eb1614` | `9a1ac5fab8b1c0351d95472397eb1614`
|
| :209–:215 | PR objectstack-ai#18666's band — its one hunk on this file, confirmed
from `/pulls/18666/files` (1/1, context :209–:215, changed line :212) |
`88505120d3f807422999b1e17e9e1266` | `88505120d3f807422999b1e17e9e1266`
|
| :432 | the `[Self-Approval]` row | `ed771ed2703b2ee5bcb02f29eddf4844`
| `ed771ed2703b2ee5bcb02f29eddf4844` |

All four byte-identical. Line numbers are unmoved too: the insert lands
at :348 and the retirement at the old :353, so every line from :354 on
keeps its number.

## The ratchet

```
before: .claude/skills/pm-dispatch/references/platform-readings.md is 466 lines (ceiling 466; headroom 0)
after : ✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/platform-readings.md is 466 lines (ceiling 466; headroom 0)
        ✓ check-skill-line-ratchet: .claude/skills/pm-dispatch/references/platform-readings.md: widest table row is 0 bytes (pin 0; headroom 0)
node scripts/pm/check-skill-line-ratchet.mjs :: exit 0
```

## Derived gates

`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` from the worktree, no hand-fed path list.
Change set: 1 path,
`.claude/skills/pm-dispatch/references/platform-readings.md`, three-dot
against merge base `ad1f94e8e`. Every exit code captured
redirect-then-`$?`.

```
0  node scripts/check-closing-keyword-parity.mjs
0  node scripts/check-closing-keyword-parity.mjs --self-test
0  node scripts/check-comment-mask-corpus.mjs
0  node scripts/pm/check-governed-queue-guard.mjs --self-test
0  node scripts/pm/check-harness-current.mjs --self-test
0  pnpm --filter @objectstack/lint run check:doc-formula-expressions   (SECOND run — see below)
0  pnpm check:agent-test-spelling
0  pnpm check:doc-authoring
0  pnpm check:driver-memory-census
0  pnpm check:nul-bytes
0  pnpm check:pm-governed-merges
0  pnpm check:pm-half-states
0  pnpm check:pm-skill-id-lint
0  pnpm check:pm-skill-ratchet
0  pnpm check:refd-timer-probe
0  pnpm check:required-contexts
0  pnpm check:skill-frame-sync
0  pnpm check:watch-hint-literal
```

`check:doc-formula-expressions` answered **exit 3 PREREQUISITE NOT MET**
on its first run — 「the workspace package `@objectstack/formula` is not
built」, with `@objectstack/lint` unbuilt as a second unmet prerequisite.
Built under the shared verify lock (`bash scripts/pm/os-verify-lock.sh
-c "NODE_OPTIONS=--max-old-space-size=4096 pnpm
--workspace-concurrency=2 --filter '@objectstack/lint...' --filter
'@objectstack/formula...' build"`, `VERDICT command-exit 0 · held the
lock 152s · waited 0s`) and re-run: **the 0 above is that second run.**

Reconciliation, `--ran` with a code recorded per family:

```
✓ dispatch-gates --ran: 18 derived famil(ies) accounted for — 18 run, 0 NOT-MEASURED
  (a DERIVED zero — all 18 recorded an exit code and none of them is 3).
```

One family was run beyond the derived union, because the derivation
marked its roster ⛔ *「roster under .claude, which one of your paths is
in」* and said the silence is not evidence in either direction:

```
0  pnpm check:pm-settings-deny-roster
```

Repo-wide, at the final commit `deaa1d0fd7`:

```
0  pnpm lint     (node --stack-size=4000 eslint . --no-inline-config)
```

Byte discipline: `grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]'` over the
edited file returns no hits (exit 1), beyond `check:nul-bytes` above.

## Out of scope, noted here and reported to the seat

**To file (class a, a fact-table row a merged PR falsified).** :357
reads 「送全块即触发该归一 ⇒ `post-stamped` 的 `body` 档把这点空白判 `mutated`,净零字节良性告警。」
PR objectstack-ai#18786 (objectstack-ai#18693) landed at `94b3f37be3`, on this head: the body-mode
trailing-rule re-anchor is now its own declared clean class
`footer-re-anchored`, exit 0, printing `read-back: clean`, and
`body_mutated` is false. So that row is wrong on both the class word and
「告警」. It is a different reading in a different part of the cell, it
needs a fresh measurement rather than a mechanical rewrite, and this
card's dispatch bounds the diff to the residual — so it is reported, not
fixed here. Dedupe words: `post-stamped body mutated` ·
`footer-re-anchored` · `platform-readings 357` · `classifyReadBack
class` · 「净零字节良性告警」.

**Noted, not filed.** objectstack-ai#18686's create-side tension (:343's 「PR
正文页脚不带前置横线」 against :348's fourth form) was read and deliberately left
alone — it is the next card on this cell and its controlled comparison
was not run here. Successor: card objectstack-ai#18686, already open and queued behind
this one.

## Verification method note

This PR body was created with a tail of blank line + rule + the
session-URL footer block, which is :350's measured no-append shape on
the create side and `AGENTS.md`'s prescribed form — `AGENTS.md` wins
over :343 where the two differ, per its own precedence line. The body
was read back after the write.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu)_

Co-authored-by: Claude <noreply@anthropic.com>
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/xs skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants