Skip to content

docs(pm-skills): adopt eight measured platform readings into the governed fact table, each paid in place - #18689

Merged
os-justin merged 2 commits into
mainfrom
claude/issue-18426-platform-readings-batch
Sep 17, 2026
Merged

os-justin merged 2 commits into
mainfrom
claude/issue-18426-platform-readings-batch

Conversation

@os-justin

Copy link
Copy Markdown
Collaborator

Part of #18426 — the carrier of a seven-card batch, all landing rows in one governed file.

Clause-②: no

One file: .claude/skills/pm-dispatch/references/platform-readings.md, held at 466 / 466 (headroom 0,
widest line 120 B). No ceiling was raised. Every adopted row is paid by a compression or a dedup in the same
file
, listed beside the sentence it shortened. Rows :10–:12, :212 and :431 are untouched — verified by content,
and none of them appears in any hunk of the diff.

Each reading was adopted or rejected on its own, as the cards ask.

Verdicts

card reading verdict row line bytes paid by
#18426 ① zod v4 defines the parse methods per INSTANCE, so prototype spying throws; the seam is the barrel population ADOPTED :277 + :278 97 B + 116 B :172 — the conditional-request row absorbs the payload-precedence row (its 「只有 body 精确」 is restated at :178–:179) · :185 — the payload-staleness pair
#18426 ② the REST write identity is per-TOKEN, not per-channel; read back your own write REJECTED already at :126–:132 — —
#18426 ③ os-verify-lock.sh is objectstack's and absent in objectui ADOPTED :393 112 B :207 — the created-order pagination pair
#18426 ④ (comment) ref deletion is refused while every other write on the token succeeds REJECTED already at :374–:377 and :430 — —
#18426 ⑤ (comment) a suspended author's comment is counted but not served REJECTED already at :250 (cause at :164–:167) — —
#18446 a label strip re-triggers the label-sensitive checks ⇒ pre-check ③ is read after the last state write ADOPTED :85 118 B :289 — the missing-command pair
#18458 gh-readonly-queue/* ref names embed the stacked-on sha ⇒ queue ORDER from ls-remote REJECTED already at :70–:73 — —
#18467 (main) a full rule-plus-footer BLOCK to an issue body is re-anchored, not doubled REJECTED already at :410 + :357 — —
#18467 (operating half) that re-anchor is what post-stamped's body mode reports as mutated ADOPTED :357 (new row under it) 119 B :318 — the MCP-truncation pair
#18467 (addendum) bare REST POST /issues with a footerless body synthesises exactly one, +58 ADOPTED :410, refined in place 120 B zero — folded into the existing create row
#18498 row 1 grep -z with a literal newline is an OR — a false POSITIVE; grep -Pz with the backslash-n escape discriminates ADOPTED :291 118 B :451 — the usage-wall pair
#18498 row 2 (withdrawn on the card as a duplicate) not adopted — — —
#18521 unstable can come from a commit STATUS the /check-runs enumeration cannot see ADOPTED :28 115 B :303 — the two CI-log reading rows
#18522 the footer has two spellings, so a bare-only strip strands the session-URL one mid-body ADOPTED :347 106 B :331 — the fence caveat folds into the trap row beside it

8 readings adopted, 9 lines, 8 payments (the create-channel cell costs nothing — it refines a row that was
already there). Net line change: 0.

Every payment, and what it shortened

payment before after fact lost
:172 「文档载明未实测:条件请求答 304 不计 core 池。」 + 「公开仓零配额读法两档,payload 档优先 —— 只有 body 精确。」 one row 「只有 body 精确」 — restated in full at :178–:179 (assignees stale, claim-bearing fields never read from this channel)
:185 「⇒ 前 15 项那条判别式…」 + 「失效方向是据它对别人的工作下没有认领的判词。」 one row none
:207 「created 序承重…」 + 「以两趟的重叠证覆盖…」 one row none
:289 「⛔ 一般规则:任何可能不存在的命令…」 + 「PR 与查重类核验改走 MCP GitHub 工具或 git。」 one row none
:303 「get_check_run 对本仓 CI job 回空 output.text。」 + 「get_job_logs 无论 tail_lines 只回占满日志尾部的 post-step service-container teardown。」 one row 「post-step」 and 「占满」 — the position is still given by 「尾部的」
:318 「GitHub 存储字节完好,raw REST 取回一字不差。」 + 「⇒ ⛔ 永不单凭 MCP 读判截断…」 one row none
:331 「⛔ 围栏不防护:照删、留空围栏。」 + 「另一坑:感叹号紧跟左方括号即触发(TS 非空断言下标),无需尖括号。」 one row 「TS」 and 「下标」 from the parenthetical — the mechanism is already spelled out by 「感叹号紧跟左方括号」
:451 「盲区:窗口起点是本地推断的近似值。」 + 「撞墙报文形如 limit reached, resets at HH:MM…」 one row none

Why the five rejections — the four axes

All five reject for one reason: the reading is already in the fact table, measured and phrased. That makes
this a single class, so the axes are argued once and the per-card evidence follows.

  • 实际业务需求 — the need each card names is real, and it is already served. A seat asking 「who authors my
    REST writes」 lands on :129 (「类只认每次写回读的 user.login/user.type」); a seat asking 「where is my PR in
    the queue」 lands on :70–:73, which carries the parent-tip chain recipe and a caution the card does not
    have (:72: the embedded parent is a presumed base, measured to reach main ten-plus minutes later). Adding a
    second row would not add a reader path; it would add a second place to disagree.
  • 项目长远合理性 — a fact table with two rows for one fact is the workaround shape this repo refuses. When one
    copy is later corrected, the other stays, and the next seat reads whichever it reaches first. One row, one
    reader path.
  • 防 AI 写代码犯错 — this is the axis that decides it. A duplicated row is a lenient surface: it lets a
    future edit narrow one copy while the other keeps the old claim, and nothing goes red. The strict form is one
    row, which the ratchet already prices. Note the direction of the rejection: none of the five loses a fact —
    each is being kept, in the line that already carries it.
  • 创业阶段不扩散需求 — at 466 / 466 every row is paid by deleting or compressing another. Paying real
    compression for a fact that is already written down spends the budget twice and shrinks the table's coverage by
    exactly the sentence that was compressed to make room.

Per-card evidence:

Measurements taken for this PR

Gates

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived the list from the tree
(no hand-fed paths); --ran reconciles: 18 derived, 18 run, 0 NOT-MEASURED, 0 UNRUN — a DERIVED zero, all 18
carry an exit code and none of them is 3.
Every code captured by redirect-then-$?, never through 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

Named explicitly by the dispatch, both inside that list:

  • pnpm check:pm-skill-ratchet → exit 0; its own verdict lines:
    platform-readings.md is 466 lines (ceiling 466; headroom 0) and
    platform-readings.md: widest table row is 0 bytes (pin 0; headroom 0);
    contract-review.md is 60 lines (ceiling 60; headroom 0).
  • pnpm check:skill-frame-sync → exit 0: the one declared copy of the decision frame is internally coherent,
    4 axes, 74 markdown files scanned for undeclared copies.

Governed-surface predicate, as the dispatch asks it be recorded:

  • node scripts/pm/check-governed-merges.mjs --test .claude/skills/pm-dispatch/references/platform-readings.md
    → exit 3 = GOVERNED (not a failure): 1 of 1 path(s) hit the register, .claude/** ×1.

First eight of those ran at exit 3 = PREREQUISITE NOT MET on a fresh worktree with no node_modules; that is
not a measurement, so pnpm install ran and every one was re-run. check:doc-formula-expressions then still
answered 3 (@objectstack/formula and @objectstack/lint unbuilt); its own prescribed fix was run through the
shared verify lock — os-verify-lock: VERDICT command-exit 0 — and the gate re-ran at 0.

Manual byte discipline beyond the gates: grep -naP for control characters over the edited file → exit 1
(none).

Acceptance notes

Noted while measuring, not filed, and not touched here:

维护者速读(草稿)

改了什么 —— 只改一个文件:PM 车道的平台读数事实表。八条今天实测出来的 GitHub/工具行为写进去,五条因为
表里已经有了而没写。文件行数一行没涨(466 / 466),每加一行都在同一个文件里删掉或压掉一行来付账。

为什么改 —— 这些读数原本只存在于各个 issue 的讨论里。没有载体的读数,下一个席位会从零重新推一遍;这次
的批次本身就是证据:五条里有五条已经在表里了,而填报席没查表就填了卡。

风险与代价(含回滚) —— 风险在八处压缩:两行合成一行,可能丢掉措辞。逐条列在正文的「Every payment」表里,
三处明确写了丢掉哪几个字以及那个事实在哪一行还能读到,其余五处无损。回滚是 git revert,单文件、无生成物、
无下游消费者。

席位意见 —— (留空,由席位在复核时填)

你要做的 —— 不需要做什么。这是事实层(references/**),按裁决 #17950 由技能席在自己的契约档复核后经队列
落地;⛔ 没有席位把它翻 ready、入队或挂 auto-merge。

Closing lines

Part of #18426 — readings ① and ③ adopted; readings ②, ④ and ⑤ rejected as already recorded, so the card stays
open for the seat to confirm those three duplication claims before it is retired.
Fixes #18446
Part of #18458 — the entire reading is already recorded at the queue-ref rows; no row was added, and the card
stays open for the seat to confirm that before it is retired.
Part of #18467 — the operating consequence and the create-channel cell are adopted; the re-anchor itself is
rejected as already recorded, so the card stays open for the seat to confirm that one.
Fixes #18498
Fixes #18521
Fixes #18522


Generated by Claude Code

…place

Six readings land in the governed fact table at zero headroom; each new row is
paid by a compression or a dedup in the same file, so the line count is
unchanged at 466/466 and every row stays under the 120-byte width rule.

New rows:
  - zod v4 hangs parse methods per instance, so prototype spying throws; the
    working seam is the barrel population, with its radius stated.
  - os-verify-lock.sh lives only in objectstack; a sibling-repo seat running
    this repo's copy reads a container-level lock, not that repo's depth.
  - a label strip and a ready flip are both state writes that restart the
    label-sensitive checks, so landing pre-check 3 is read after the last one.
  - sending a whole footer block to an issue body trips the blank-line
    normalisation, which post-stamped's body mode reports as `mutated`.
  - grep -z treats a literal newline in the pattern as a separator, so a
    wrapped phrase matches one side only -- a false positive, not a false zero.
  - the issue create channel synthesises exactly one footer for a footerless
    body (+58), folded into the existing create row at zero cost.

Payments: the conditional-request/payload row pair, the payload-staleness pair,
the created-order pagination pair, the missing-command pair, the MCP-truncation
pair and the usage-wall pair each compress two lines into one, losing no fact.

Claude-Session: https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu
Co-authored-by: Claude <noreply@anthropic.com>
The seat folded two more sibling cards onto this branch. Both land as one row
each, each paid by a compression in the same file, so the count stays 466/466.

New rows:
  - mergeable_state `unstable` can be produced by a commit STATUS, which the
    /check-runs enumeration cannot see, so landing pre-check 3 reads the
    commit status too before calling a head green.
  - the attribution footer has two spellings, so a "send no footer" strip that
    matches only the bare one strands the session-URL one mid-body while the
    58-byte read-back delta still looks exactly as documented.

Payments: the two CI-log reading rows compress into one, and the fence caveat
folds into the trap row beside it; no fact is dropped by either.

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
@os-justin
os-justin marked this pull request as ready for review September 17, 2026 15:14
@os-justin
os-justin added this pull request to the merge queue Sep 17, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 17, 2026

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 5f809c1817b67f31dca0438b5677e0d35bfcb9aa

Review of record on the PR thread — the same record the seat posted on the carrier card as 5716694216 at 2026-09-17T15:10:43Z; the merge-group leg of the Governed Surface Guard reads the PR thread, not the card, and dequeued this PR at 2026-09-17T15:36:18Z for want of it. In-seat review of PR #18689 (the seven-card platform-readings batch carried by #18426) by the dispatching domain:skills seat, 2026-09-17T15:38Z, read against the PR's diff, files and head on GitHub and a scratch worktree at the head — ⛔ not against the dev report (5716488532). Clause-②: no on the PR body and on the claim (5716076148); check-clause2-carriers --pair 18689 answers 0 for all seven cards. Fact-layer landing: the one file is .claude/skills/pm-dispatch/references/platform-readings.md, so check-governed-merges --test answers GOVERNED (exit 3, .claude/** ×1) and the seat lands it on this review of record, not on a maintainer approval — 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-18426-platform-readings-batch
Reviewed-by: session_01Gqi43smmqjJ5sUrhfoPeKu

VERDICT: PASS


Generated by Claude Code

@os-justin
os-justin added this pull request to the merge queue Sep 17, 2026
Merged via the queue into main with commit 72dd95f Sep 17, 2026
32 checks passed
@os-justin
os-justin deleted the claude/issue-18426-platform-readings-batch branch September 17, 2026 15:59
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…ne, and the arming echo is not the landing method (objectstack-ai#18713)

Fixes objectstack-ai#18461

`Clause-②: no`

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

Reserved rows are untouched and verified by content: `:10`–`:12` and
`:431` (objectstack-ai#18469 PR-A's pending patch),
`:209` (PR objectstack-ai#18666's one-line change, awaiting the maintainer — it rides
at `:208` after the payment, byte
identical), and the nine rows PR objectstack-ai#18689 adopted an hour before this
branch (`:28`, `:85`, `:249`, `:277`–`:278`,
`:291`, `:347`, `:357`, `:393`, `:410` — none appears in any hunk of
this diff).

## Row ② — rewritten in place at `:50`

Ruling reference: the maintainer's letter **A** on objectstack-ai#18421, comment
5716260764 (2026-09-17T14:41Z, 「同意」).
Its operative conclusion is the one this row carries:
**`auto_merge.merge_method` is not a reading of what will
land** — the landing shape is the merge queue's to decide.

**before** (120 B, the falsified form):

```text
- 挂上的 auto-merge 存的方法恒为 `merge`,不论请求了什么;REST `auto_merge.merge_method` 读回 `merge`。
```

**after** (117 B):

```text
- auto-merge 回读 `merge_method` 不恒定:同 `SQUASH` 载荷 `merge`/`squash` 皆现,⛔ 非落地方法判据。
```

**Why 「不恒定」 and not either constant.** 「恒为 `merge`」 is falsified by this
card's four PRs; 「恒为
`squash`」 is falsified by the same table's first row and by the ruling's
own measurement. Same endpoint
(`PUT .../pulls/{n}/ccr/auto_merge`), same `{"merge_method":"SQUASH"}`
payload:

| PR | echoed `merge_method` | landed as |
|---|---|---|
| objectstack-ai#18395 | `merge` | single-parent squash |
| objectstack-ai#18392 | `squash` | single-parent squash |
| objectstack-ai#18416 | `squash` | single-parent squash |
| objectstack-ai#18445 | `squash` | single-parent squash |

The objectstack-ai#18421 ruling measured the same endpoint answering `merge` (both
arming channels, MCP and CCR), and the
dispatching seat's own arms today echoed `squash` on PR objectstack-ai#18689 / objectstack-ai#18690
/ objectstack-ai#18700 and `merge` on earlier ones.
Four landings, two echoes, one landing shape ⇒ the echo varies and
decides nothing. A row asserting either
constant would teach a seat to investigate a read-back that is simply
not a signal.

**Neighbours left as they are.** `:49` is about the bare `PATCH` draft
bit — unrelated. `:51`
(「仓库 `allow_merge_commit:false` 时同样读回 `merge`」) does not restate 「恒」: it
is one conditioned
measurement, and 「同样」 now attaches to `merge`, which the rewritten row
names as one of the two observed
values. `:53`–`:55` already carry the landing half (the branch rule
decides; three carriers agreeing proves
nothing) and are untouched — the row above them no longer contradicts
them.

## Row ① — added at `:272`

**after** (118 B):

```text
- 多标签页还静默截断:`totalCount` 231 而 `returned` 30,单标签同车道 24/24 ⇒ 求交读成空车道。
```

**Placement — a declared deviation from the dispatch.** The dispatch
named the `:247`–`:248` neighbourhood
(the MCP `list_issues` rows). The OR half of this reading is already in
the file, in the 读数陷阱 cluster at
`:269`–`:274`: `:269` 「`labels` 数组是并集」, `:270` 「⛔ 永不读作交集」, `:272`
「失效全静默」, `:273`
「正确读法 = 整车道单标签一次读全加本地对 labels 求交」. Writing the new row at `:247` would
have put a second
home for the labels-OR fact six rows away from the first — the same
duplication the dispatch forbids for row ②
(one row for one fact, never two). So the row is placed where the fact
it compounds already lives: directly
between the silent-failure row `:272` and the prescription `:273`, which
it is the reason for. The fact
delivered is unchanged; only the address is.

**What the row adds over `:269`–`:274`.** Those rows carry the OR
semantics and the prescription. They do not
carry the **truncation**, and the two compound: the returned page is a
fraction of the set with nothing in the
response saying which fraction, so a local intersection taken over *that
page* can return zero and read as
「本车道无活」. The measurement is inside the row:
`labels=["domain:cli","pm:queue"]` answered `totalCount=231`
with `returned=30`, while the single-label query on the same lane in the
same minute answered 24 / 24 —
complete, and intersectable. That is what makes `:273` load-bearing
rather than a style preference.

## The payment — one dedup, `:202`+`:203` become one row

| payment | before | after | fact lost |
|---|---|---|---|
| `:202`+`:203` | 「换道:探针绿走 REST 列表端点 `GET
/repos/{o}/{r}/issues?state=open&labels=a,b&per_page=N`。」 + 「它走 core 桶且
`labels` 是真 AND;⛔ 完整性自证靠 `&page=N` 加总数核对。」 | 「换道:探针绿走 REST 列表端点列卡,走 core
桶且 `labels` 真 AND;拼写与自证见 `rest-channel.md`。」 (118 B) | the inline
endpoint spelling and the `&page=N`-plus-total spelling — both held
verbatim in `rest-channel.md` 〈读侧〉, which `:137` already names as their
single home (「逐操作通道归属、写侧配方与队列路由三读法见 `rest-channel.md`,⛔ 不在本表复述」). The
two facts the row keeps inline are the ones a seat needs before it
switches channel; `labels` being a true AND on REST is also stated
in-file at `:274`, and the core bucket at `:115` / `:128`. |

Net: 3 lines changed, 466 in, 466 out.

## Gate readings

`node scripts/pm/check-governed-merges.mjs --test
.claude/skills/pm-dispatch/references/platform-readings.md`
exits **3 = GOVERNED** (`.claude/**` ×1) — recorded as the verdict it
is. This PR stays draft; no seat flips it
ready, enqueues it or arms auto-merge. The references tier applies: the
skills seat's `## Contract review`
record goes on this thread, and the seat lands it.

- `pnpm check:pm-skill-ratchet` :: exit 0 — 「platform-readings.md is 466
lines (ceiling 466; headroom 0)」,
「widest table row is 0 bytes (pin 0; headroom 0)」; no line over 120 B
(`awk 'length > 120'` returns nothing).
- `pnpm check:skill-frame-sync` :: exit 0 — 「the one declared copy of
the decision frame is internally
  coherent … 4 axes … 74 markdown files scanned for undeclared copies」.
- `pnpm lint` (repo-wide, `eslint . --no-inline-config`) :: exit 0.
- `grep -naP` for control characters over the file: no hits. No
tag-shaped fragment in either new row.

### Derived gate list, with exit codes

Derived from the worktree with `node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack`
(no hand-fed paths; change set 1 path vs merge base `72dd95fa5`), every
command run, each exit code captured by
redirect-then-`$?`:

```text
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
pnpm check:pm-settings-deny-roster                                  :: exit 0   (outside the derivation; run
                                                                                 because its roster lives under
                                                                                 .claude, which this path is in)
```

`check:doc-formula-expressions` first exited **3 — PREREQUISITE NOT
MET** (`@objectstack/formula` and
`@objectstack/lint` not built, 「Nothing was measured」). It was re-run to
a real verdict after
`pnpm exec turbo run build --filter=@objectstack/formula
--filter=@objectstack/lint` through
`scripts/pm/os-verify-lock.sh` (VERDICT command-exit 0, held 1s, waited
0s); only the exit 0 is recorded above.

Reconciliation: `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --ran ran.txt` →
「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)」. Outside that union: the 53 artifact-roster
families, 11 wide-population families,
14 changeset-pending families and the CI-only families
(`--verify-required-set`, `check-half-states
--provenance`) — CI's, not this branch's, and named here so their
absence is not read as a clearance.

## Out of scope, reported not written

The card's third item — `scripts/pm/check-expected-skips.mjs` cannot run
in a seat container
(`ERR_MODULE_NOT_FOUND: Cannot find package 'yaml'`) — is not a
references line and is not written here. In this
worktree, after `pnpm install`, `yaml@2.9.0` is present; the crash is a
property of the container the seat runs
it from, not of the script.

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

---
_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
…_limit cannot see (objectstack-ai#18741)

Fixes objectstack-ai#18601

`Clause-②: no`

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

Reserved rows are untouched, verified by content after the edit:
`:10`–`:12` and `:431` (objectstack-ai#18469
PR-A), PR objectstack-ai#18666's one line (now at `:209`, byte identical), `:51` (was
`:50`) and `:272` (PR
objectstack-ai#18713), and the nine rows PR objectstack-ai#18689 adopted (`:28`, `:86`, `:249`,
`:277`–`:278`, `:291`, `:347`,
`:357`, `:393`, `:410` — none appears in any hunk of this diff).

## What the card measured, and what the table now says

The seat's only undraft channel, `POST
.../pulls/{n}/ccr/ready_for_review`, draws on the **session
credential's hourly GraphQL quota** and answers **HTTP 429 with a
zero-byte write** when that quota
is spent — `GET /pulls/{n}` still read `draft: true`. In the same
minute, on the same credential,
`GET /rate_limit` reported `graphql` **10000 / 10000** and `core` 15000
/ 15000. So the table's
prescription 「graphql remaining < 1000 ⇒ 本轮走 git 加 REST」 reads an
instrument that cannot see
the pool that stops you, and a full reading is **not a pass** for the
undraft act.

The reading is **one session's, one hour**: this seat's own arms today
(four PRs) went through the
same ccr route without a 429. The row carries that by naming the pool as
会话凭据的 GraphQL 小时池
rather than generalising the trigger to the route as such.

## The undraft rows — `:47` rewritten, `:48` rewritten, `:49` added

**before** (`:47`, 97 B · `:48`, 118 B):

```text
- undraft 单通道:席位凭据走 `POST .../pulls/{n}/ccr/ready_for_review`;MCP 兜底已拒。
- 2026-09-12 两席实调:裸 GraphQL 会话内被拒,建议的 REST 正是 ccr 路 ⇒ 池 0 不再只能等重置。
```

**after** (`:47`, 115 B · `:48`, 116 B · `:49`, 104 B):

```text
- undraft 单通道:席位凭据走 `POST .../pulls/{n}/ccr/ready_for_review`;MCP 兜底与裸 GraphQL 均已拒。
- 2026-09-17 单席实测:该路吃会话凭据的 GraphQL 小时池,尽则 429 且零字节写入,`draft` 未动。
- 唯一可读信号是该 429 体自报的 `resets at` 时刻;体自带指令 ⛔ 重置前不重试。
```

`:48`'s facts are kept, not dropped. 「裸 GraphQL 会话内被拒」 folds into `:47`
as 「MCP 兜底与裸
GraphQL 均已拒」, and 「建议的 REST 正是 ccr 路」 is what `:47`'s main clause
already states — one
row for one fact. What is deleted is the tail 「⇒ 池 0 不再只能等重置」, and only
because today's
reading is its reverse: the ccr route itself draws on the pool, so a
spent pool is again a wait.
The routing rule is untouched — the seat still undrafts through this
route; there is no second one.

`:49` carries the 429's own two halves: the `resets at` instant is the
only readable signal that
exists before the act, and the refusal to retry is the body's own
instruction, attributed to it so
that no retry cadence is invented here.

## The prescription row — `:162` (now `:163`) rewritten in place

**before** (109 B):

```text
- 它答不了通道在不在,repo-scoped 探针另跑;graphql remaining < 1000 ⇒ 本轮走 git 加 REST。
```

**after** (118 B):

```text
- graphql remaining < 1000 ⇒ 本轮走 git 加 REST;⛔ 满余额不放行 undraft:同分钟满额而 ccr 路 429。
```

## The payment — one dedup, `:223` folded into `:94`

| payment | before | after | fact lost |
|---|---|---|---|
| `:162` first clause dropped (in-file duplicate), which paid for the
blind-spot clause in the same row | 「它答不了通道在不在,repo-scoped 探针另跑」 | —
(clause removed) | the point-of-use restatement beside `:161`. The fact
is held twice in the same section, 50+ rows earlier: `:106`
「开轮探一次…探针必须是一条真 repo-scoped 读」 and `:109` 「`/rate_limit` 照答 200,兼任不了探针」
|
| `:223` deleted, its one unique clause folded into `:94` (78 B → 103 B,
now `:95`), which paid for the new row `:49` | 「配额按账户计,不跨席共享:各席位跑在不同
GitHub 账户下。」 + 「配额池按身份计,换身份即清零燃烧,共享身份结构不变。」 | 「配额按账户计,不跨席共享:各席位跑在不同
GitHub 账户下;换身份即清零燃烧。」 | 「共享身份结构不变」 — held by `:96` 「所有 agent
共用一个身份只在席位内部成立」 and `:97` 「⛔ 不据限流报文里的 user ID 推池子跨席共用」 (post-edit
numbering). 「配额池按身份计」 is the same statement as the kept row's opening
clause |

Net: 5 lines changed, 466 in, 466 out.

## Why this row shape — the four axes

- **实际业务需求** — measured, not speculative: a green, reviewed,
clause-②-PASS undraft was stuck
27 minutes with no readable indicator before the act. Both edited sites
are points of use: a seat
reads `:47`–`:49` at the moment it undrafts, and `:163` at the start of
a round, when it budgets
the pool. Writing the fact at one site only would leave the other
reading exactly as it read this
  morning — which is how the seat got there.
- **项目长远合理性** — no workaround: no second channel is invented, no probe
is added, and the
correction is made where the falsified claim lives rather than appended
as a caveat. The stale
tail is removed instead of being qualified, so the table keeps one
statement per fact.
- **防 AI 写代码犯错** — the axis that decides the shape. The failure mode
here is a **tolerant
reading**: 10000 / 10000 looks like clearance, and the seat acts on it.
So the prescription row
itself carries the refusal (`⛔ 满余额不放行 undraft`) with its own evidence
(同分钟满额而 ccr 路
429) rather than a pointer to another row — a reader who never scrolls
to `:47` still cannot read
a full balance as a pass. Splitting the undraft fact across `:48` and
`:49` is what bought room
for the `resets at` instant; folding both into one row would have
dropped it, and it is the only
  signal a seat can act on.
- **创业阶段不扩散需求** — zero net lines, zero new surface, zero tooling. The
card's second
suggestion (make the 429's named wait a legal state in the landing flow)
is a `SKILL.md` /
lane-text matter and is deliberately NOT written here; it is raised in
the report's
  `open_questions` instead.

The alternative shape — both facts in one new row — was rejected on the
third axis: at ≤ 120 B one
row holds either the pool ownership or the instrument blind spot, not
both, and the half that would
have been dropped is the half a seat reads at round start.

## Gate readings

`node scripts/pm/check-governed-merges.mjs --test
.claude/skills/pm-dispatch/references/platform-readings.md`
exits **3 = GOVERNED** (`.claude/**`) — recorded as the verdict it is.
This PR stays draft; this
branch never flips ready and never arms auto-merge. The skills seat's
`## Contract review` record
goes on this thread, and the seat lands it.

- `pnpm check:pm-skill-ratchet` :: exit 0 — 「platform-readings.md is 466
lines (ceiling 466;
  headroom 0)」, 「widest table row is 0 bytes (pin 0; headroom 0)」.
- `pnpm check:skill-frame-sync` :: exit 0 — 「the one declared copy of
the decision frame is
internally coherent … 4 axes … 74 markdown files scanned for undeclared
copies」.
- `pnpm lint` (repo-wide, `eslint . --no-inline-config`) :: exit 0.
- Byte discipline: `LC_ALL=C awk 'length($0) > 120'` over the file
prints nothing; `grep -naP` for
control characters prints nothing; nothing tag-shaped is written in the
new rows.

### Derived gate list, with exit codes

Derived from the worktree with `node scripts/pm/dispatch-gates.mjs
--commands --repo objectstack-ai/objectstack`
(no hand-fed paths; change set 1 path vs merge base `080688b56`), every
command run, each exit code
captured by redirect-then-`$?`:

```text
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
pnpm check:pm-settings-deny-roster                                  :: exit 0   (outside the
                                                                                 derivation; run
                                                                                 because its roster
                                                                                 lives under .claude)
pnpm lint                                                           :: exit 0   (outside the
                                                                                 derivation)
```

`check:doc-formula-expressions` first exited **3 — PREREQUISITE NOT
MET** (`@objectstack/lint` not
built, 「Nothing was measured」), which is not a failed measurement. It
was re-run to a real verdict
after `pnpm exec turbo run build --filter=@objectstack/formula
--filter=@objectstack/lint` through
`scripts/pm/os-verify-lock.sh` (`VERDICT command-exit 0 · held the lock
167s · waited 0s`); only the
exit 0 is recorded above.

Reconciliation: `node scripts/pm/dispatch-gates.mjs --repo
objectstack-ai/objectstack --ran ran.txt`
→ 「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)」. Outside that union and named so
their absence is not read as
clearance: the 53 artifact-roster families, the 11 wide-population
families, the 14
changeset-pending families and the CI-only families
(`--verify-required-set`, `check-half-states
--provenance`) — CI's, not this branch's.

## Acceptance notes

- Noted, not filed: nothing in this diff's neighbourhood is a defect of
its own except the item
raised in the report's `out_of_scope_findings` (three rows in the 配额
section rest on the same
rationale this card falsified — 「ccr 路是 REST ⇒ 不吃 GraphQL 池」 — and
correcting them needs a
measurement nobody has taken: whether a `/rate_limit`-red window
predicts a ccr 429 at all). That
one is filed rather than fixed here: the fix shape is not pinned, and
the dispatch scopes the
  routing and red-window scheduling rules out of this PR.

---
_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
…nd one constant holds both tools to it (objectstack-ai#18738)

Fixes objectstack-ai#18701

Clause-②: no

## The defect

`check-clause2-carriers.mjs --template` prints, and has always printed,
that a review of record is `ONE comment on the PR or its card`; C6/C7
judge it on both threads through `locateReviewOfRecord`; the governed
text says the same in four places. The Governed Surface Queue Guard's
REFERENCES tier read **the pull request's thread alone**. The two tools
already shared every recogniser — the heading marker, the head-sha span
test, the newest-of resolution, the `Reviewed-by:` key line, the
`Served-tier:` reader — and the one thing they did not share was the
**set of threads** to run them over, which is the only fact a
spelled-out copy of the filter could not copy. Measured cost, on one
pull request in one afternoon: the skills seat posted its `## Contract
review` on carrier card objectstack-ai#18426 (comment `5716694216`, 15:10:43Z),
`--pair 18689` read it as C6-RECORD on the `card thread`, the
`merge_group` leg (run `35238999808`) refused with `0 comment(s) read on
the PR thread`, PR objectstack-ai#18689 was dequeued `CI_FAILURE` at 15:36:18Z, and
only a **second copy** of the same comment on the PR thread
(`5717108355`) cured it.

## The canonical sentence — the governed text is not silent

Read on `origin/main` (`080688b561`, the branch base). Quoted verbatim
and untranslated, because rewriting a quoted ruling rewrites the ruling:

- `.claude/skills/pm-dispatch/references/contract-review.md` **:27** —
「复核记录 = 一条评论落 PR 或卡,达档与默认档同形;散文、dev 自评、`os-dev-report` 恒不算。」
- same file **:40** — 「落地前检三条:① 席内条款②复核 PASS 在案,即 PR 或卡上同形的复核记录(档位按实测)。」
- same file **:59** — 「PASS、FAIL 与作废都落 PR 或卡;……」
- `.claude/skills/pm-dispatch/SKILL.md` 〈入队与落地〉 **:640** —
「交付后复核归派发席:达档席内审,未达档循保险丝起子代理;记录 = 同形评论落 PR 或卡。」

(The claim comment's aside that `references/contract-review.md` carries
no such sentence is the one premise this card's dispatch got wrong; :27
is that sentence, and it is on `main`. Nothing else in the premise moved
— the guard, the template, the docblocks and the objectstack-ai#18689 evidence all
read as the card describes them.)

**Which tool disagrees with it:
`scripts/pm/check-governed-queue-guard.mjs`.** So the GUARD moves, all
of it. No governed file is touched by this PR.

## Why the guard and not the template — the four axes

- **实际业务需求.** The record on the card is not a hypothesis: it is what a
seat did, on 2026-09-17, following this repo's own `--template`. Both
carriers are live on every pair — the clause-② declaration is hung on
both, `--pair` judges both, the carrier label is stripped from both —
and `check-clause2-carriers` has read both threads since objectstack-ai#17302. The
population that would be served by moving the template instead is empty:
nobody is asking for the card to stop counting.
- **项目长远合理性.** Contract-first. The governed text is the contract and the
two tools are its consumers; a consumer reading a narrower set than the
contract declares is the defect, and the repair belongs at the consumer,
not at the contract. 基本裁决原则 puts 声明 above 实现 above 文档面 — moving the
template would be narrowing the declaration to match an implementation
gap, which is the one direction that rule forbids.
- **防 AI 写代码犯错.** This is a declared-but-not-honoured capability in its
purest form: an instruction tells an author where to put an artifact,
and a gate silently does not look there. The structural repair is not
"say the same thing in more places" — it is ONE exported constant that
the instruction and the reader both derive from, plus a pin that reds if
either is edited alone. Moving the template would leave three more
hand-written location sentences (the guard's option-3 text and two
docblocks) free to drift the same way one layer along.
- **创业阶段不扩散需求.** Moving the guard is the SMALLER change and adds no
capability: the helper already exists, the card number is read off the
pull object the head read already fetches (zero extra requests for it),
and at most one extra thread read is bought — only for a references-tier
pull request with no authorized approval, a population refused outright
today, so nothing that passes now pays anything. Moving the template
would be a RETIREMENT of a carrier the protocol uses everywhere else,
across governed files, which is a maintainer's ruling and not a dev PR.

No axis conflicts with another. A fifth, decisive fact: the template
side cannot move from here at all — its sentences live in
`.claude/skills/pm-dispatch/**`, off this card's file surface and
governed.

## The shared constant, and one derivation of "the record on this head"

`check-clause2-carriers.mjs` — the file that owns the reader — now
declares the thread set once:

```js
export const REVIEW_OF_RECORD_THREADS = Object.freeze([
  Object.freeze({ where: 'PR',   rows: 'prComments',   number: 'pr',   words: 'the PR' }),
  Object.freeze({ where: 'card', rows: 'cardComments', number: 'card', words: 'its card' }),
]);
export const REVIEW_OF_RECORD_LOCATION = REVIEW_OF_RECORD_THREADS.map((thread) => thread.words).join(' or ');
```

Three consumers, all derived from it and none spelling a set of its own:

1. `locateReviewOfRecord` builds the rows it searches, its gap sentences
and its per-thread read counts from the list. `--template`'s and
`--pair`'s output is **byte-identical** to `main`'s.
2. `contractReviewTemplateLines` builds its printed sentence from
`REVIEW_OF_RECORD_LOCATION`, so an instruction can never offer a thread
no reader searches.
3. `check-governed-queue-guard.mjs` fetches **one thread per entry in
the loaded list** — and its whole record reading is now
`locateReviewOfRecord`, imported through the existing lazy loader rather
than re-spelled. The guard's own two-line filter is gone; what is left
there is the tier gate on top of the located record (`Served-tier:`
stands, and the objectstack-ai#18060 model-identifier refusal), which is that leg's
own fact.

The card the record may live on is **derived, never configured**:
`deliveredCardNumber(pull)` enumerates the numbers the body or branch
could be naming and asks `deliveryEvidence` — the one relation
`derivePairs`, H8 and H31 already pair a PR with its card by — so it can
never accept a card that relation rejects. A pull request naming no
card, or two cards at equal strength, searches NO card thread and the
refusal says so: the refusing direction, the only one a governed reading
may be wrong in. The card's number and the head sha come from ONE `GET
/repos/{owner}/{repo}/pulls/{number}` — the request the head read
already made — so the card thread costs no extra call for the number,
and the guard keeps its zero-write, event-payload-only posture with no
checkout.

`REVIEW_OF_RECORD_LOCATION` is mirrored at the guard's module scope for
the renderer only (`renderGuardVerdict` is pure and synchronous, and a
module-scope import of the sibling is the measured exit-13 deadlock its
own docblock records). The SET the leg acts on is never a mirror — it is
the loaded list — and the mirror's words are pinned to the constant in
the guard's battery.

## The pin

`check-clause2-carriers.mjs` owns the reader, so the pin lives in its
self-test — and it is a **measurement**, not two lists written side by
side:

- **STATED** — the locations read out of the text
`contractReviewTemplateLines()` actually prints.
- **READ** — obtained by **driving `check-governed-queue-guard.mjs`'s
`runGuard`** once per entry in `REVIEW_OF_RECORD_THREADS`, with a record
built from this file's own `contractReviewRecordLines()` sitting on that
thread **alone**, and collecting the threads whose run answers `exitCode
=== EXIT_CLEAR` with `record.state === 'stands'`.

The case asserts `stated === read === the constant's own set`, plus its
own controls: the set has two locations (not vacuous); each run located
the record on the thread it was posted to; the guard fetched exactly one
thread per entry, no more and no fewer; and a run with the record on
NEITHER thread is refused, so the pin can fail. `selfTest()` became
`async` to take that lazy import — the dispatch uses `.then(...)`, never
a top-level `await`.

## Counterfactual — the objectstack-ai#18689 dequeue, offline

One fixture, driven through `runGuard` with no network and no git: a
references-tier pair whose record sits only on the card thread. The same
script runs against either generation (it passes both the old
`fetchPullHead` and the new `fetchPull`).

| | exitCode | conclusion | `record.state` | `record.where` |
|---|---|---|---|---|
| **before** (`080688b561`) | `3` REFUSED | `refused` | `absent` | — |
| **after** | `0` | `cleared` | `stands` | `card` |

Before, rendered: `⛔ NO review of record on this head either (1
comment(s) read on the PR thread)` — the objectstack-ai#18689 sentence, reproduced.
After, rendered: `✅ review of record on this head: comment 5716694216
(2026-09-16T15:10:43Z) on the card thread is a ## Contract review
comment naming ...`

## Ablation

Two legs, each: on-disk proof of the mutation (`grep -c` of the removed
text and of the injected text, plus the `git hash-object` blob before
and after), then the self-tests, then restore, then a byte-identity
check against the HEAD blob. Both scripts carry `trap restore EXIT INT
TERM` with an absolute path. No build or `dist/` is involved — these two
files are run directly by node and by `package.json`, so the on-disk
proof is the whole preflight.

**Leg A — the loop that unifies the thread set** (`for (const thread of
recognisers.threads)` narrowed to the first entry). Blob `9a111f6c` →
`b57227d0`; removed-text count 1 → 0, injected-text count 0 → 1.
Cross-tool pin: **4 of 802 failed**. Guard battery: **25 of 253 failed**
— the loop is load-bearing for the whole references leg, not only for
the card.

**Leg B — the line that supplies the card's number** (`card: card.card`
→ `card: null`), which reproduces the pre-objectstack-ai#18701 thread set exactly.
Blob `9a111f6c` → `9d935d92`; counts 1 → 0 and 0 → 1. This is the clean
reading the card asks for:

- **new pin RED**: `check-clause2-carriers` **3 of 802 failed**, all
three the new battery — `stated=[PR,card] read=[PR]`, the card run
locating nothing, and the fetch list `[[4101],[4101]]` instead of
`[[4101,4102],[4101,4102]]`.
- **new guard cases RED**: `check-governed-queue-guard` **5 of 253
failed**, all five in the `objectstack-ai#18701` battery (record on the card alone,
both threads fetched, the rendering naming the thread, newest-governs
across threads, the branch-name fallback).
- **old cases GREEN**: 799 of 802 and 248 of 253 — every pre-existing
battery in both files, `objectstack-ai#18020` included, passes under the ablation.

Restore, both legs: worktree blob `9a111f6c…` equals the HEAD blob, `git
diff HEAD` empty, `git status --porcelain` empty, and both self-tests
back to exit 0.

## Self-tests

| family | before | after |
|---|---|---|
| `node scripts/pm/check-governed-queue-guard.mjs --self-test` | 239
cases, 21 batteries declared | **253 cases**, 22 batteries — new: `⭐
objectstack-ai#18701: the record lives on the PR or its card, and BOTH are read` (14,
floor 14); `SELF_TEST_BATTERY_FLOOR` 20 → 21 |
| `pnpm check:pm-clause2-carriers` | 786 cases, 28 batteries declared |
**802 cases**, 29 batteries — new: `objectstack-ai#18701: ONE thread set -- what the
template STATES is what the queue guard READS` (16, floor 16);
`SELF_TEST_BATTERY_FLOOR` 27 → 28 |

Both exit 0. `--template`, `--help` and the no-event refusal path (exit
1, its own message) all still answer.

## Gates

`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` derived from the worktree with no hand-fed
path list (change set: the two scripts, 2 committed / 0 working tree / 0
untracked, three-dot against merge base `080688b56`). **36 derived, 36
run, every one exit 0**, reconciled with `--ran` after a fresh `git
fetch origin main` — `✓ dispatch-gates --ran: 36 derived famil(ies)
accounted for — 36 run, 0 NOT-MEASURED (a DERIVED zero — all 36 recorded
an exit code and none of them is 3)`.

```text
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: exit 0
node scripts/check-changeset-no-major.mjs --self-test :: exit 0
node scripts/check-ci-filter-parity.mjs :: exit 0
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/check-declaration-mirrors.mjs :: exit 0
node scripts/check-declaration-mirrors.mjs --self-test :: exit 0
node scripts/check-scripts-symbol-anchors.mjs :: exit 0
node scripts/check-scripts-symbol-anchors.mjs --self-test :: exit 0
node scripts/check-self-test-wired.mjs :: exit 0
node scripts/check-self-test-wired.mjs --self-test :: exit 0
node scripts/check-self-test-workflow-commands.mjs :: exit 0
node scripts/check-self-test-workflow-commands.mjs --self-test :: exit 0
node scripts/check-whole-set-label-write.mjs :: exit 0
node scripts/check-whole-set-label-write.mjs --self-test :: exit 0
node scripts/pm/bare-root-worklist.mjs --self-test :: exit 0
node scripts/pm/check-governed-queue-guard.mjs --self-test :: exit 0
pnpm check:agent-test-spelling :: exit 0
pnpm check:bash32-floor :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:cli-command-ids :: exit 0
pnpm check:closing-target-claim :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:entry-guard :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:parse-guard :: exit 0
pnpm check:pm-clause2-carriers :: exit 0
pnpm check:pm-dispatch-gates :: exit 0
pnpm check:pnpm-filter-targets :: exit 0
pnpm check:ratchet-remedy-authority :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:watch-hint-literal :: exit 0
```

Repo-wide `pnpm lint` (`eslint . --no-inline-config`, the whole tree, as
PR objectstack-ai#18654 did) :: **exit 0**, no output. Every exit code above was
captured with redirect-then-`$?`, never across a pipe.

`origin/main` moved to `62bce5c297` while this ran; its three commits
touch `packages/**` and `.changeset/**` only — nothing under
`scripts/**` and nothing this diff reads — so no merge was taken and the
derivation above is unaffected.

## Scope

Two files, `scripts/pm/check-governed-queue-guard.mjs` and
`scripts/pm/check-clause2-carriers.mjs`. No governed file.
`skip-changeset`: nothing published moves — `scripts/pm/**` is not in
any package's `files[]`.

⚠️ **The card read this PR adds is built on `readCardComments`'s paged
sibling, not on the un-paged path objectstack-ai#18683 names.** The guard's own
`makeCommentReader` is paginated (100 per page, 10 pages, and it THROWS
rather than truncating), and it is what serves both threads here — `GET
/repos/{owner}/{repo}/issues/{number}/comments` is the same route for a
pull request and for a card. objectstack-ai#18683 remains exactly as it was, on
`check-clause2-carriers`' own reader, untouched by this PR.

Not in scope, as dispatched: `check-clause2-carriers`' un-paged
`readCardComments` (objectstack-ai#18683 — read, not fixed); objectstack-ai#18536's machine-side
lane/tier re-keying; the REFERENCES-tier rule itself (which paths
qualify), untouched.

## Acceptance notes

- `deliveryEvidence`'s ranking had no exported ordering, so
`DELIVERY_EVIDENCE_PRECEDENCE` is declared in
`check-clause2-carriers.mjs` as a MIRROR and measured against that
function pair by pair in the self-test rather than trusted. If
`check-half-states.mjs` ever wants to own it, that is a one-line move;
noted, not filed — no PR or person reaches for a ranking that today has
exactly one caller.
- The guard no longer loads `check-half-states.mjs` directly: every
recogniser it reads now arrives through the file that composes them, and
a rename upstream stops THAT module loading, which the leg already turns
into a fail-closed refusal with the loader's own reason.
`RECOGNISER_SOURCES` is down to one entry and the self-test pins that.
Keeping the old direct load would have been a check on something this
file no longer reads.

---
_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
…status endpoint as an empty-set default (objectstack-ai#18775)

Fixes objectstack-ai#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 objectstack-ai#18750 head) | `state: "success"`, `total_count: 1` |
`Vercel` / `success` | `total_count: 40`, 29 `completed/success` + 11
`completed/skipped` |
| `702b4241` (PR objectstack-ai#18756 head) | `state: "success"`, `total_count: 1` |
`Vercel` / `success` | `total_count: 39`, 28 `completed/success` + 11
`completed/skipped` |
| `2f38ee0d` (PR objectstack-ai#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

```text
- 零 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):

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

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 objectstack-ai#18689 adopted, not one of the three `配额` rows objectstack-ai#18744
names (`:137`, `:145`,
`:146`), and not one of the four PR objectstack-ai#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`):

```text
- 会话代理只服务 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 objectstack-ai#18666 (for objectstack-ai#18454, governed, awaiting the maintainer) lands
on that same line. Its hunk
on this file is one line, `@@ -209,7 +209,7 @@`:

```text
-- 读 `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 objectstack-ai#18666 lands, and the line a residual row would
have to touch is objectstack-ai#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

- `:10`–`:12` (objectstack-ai#18469 PR-A) — byte-identical before and after; the
diff's two hunks are at `:26`–`:32`
  and `:341`–`:347`, so these lines are in neither.
- `:208`–`:212` (PR objectstack-ai#18666) — the only open PR touching this file,
confirmed by reading all 26 open
PRs' file lists at 2026-09-17T19:50Z; its hunk's changed line is the
`total_count` row. **The band
moves by the insertion**: on `main` the changed line is `:211` and the
hunk's full context window
is `:208`–`:214`; on this head they are `:212` and `:209`–`:215`. Every
byte in the band is
  unchanged, so objectstack-ai#18666 still applies.
- `:431` (objectstack-ai#18469 PR-A) — **this number was already stale before this
diff**. The row the card names,
「harness 按内容拒写:同会话派发 PR 上 PASS 拒为 `[Self-Approval]`」, reads `:432` on
`main`, and
read `:432` at `7636cd9b81^` too, so PR objectstack-ai#18741's body carried the
off-by-one rather than the row
having moved. `:431` is a different row (「分支删除被拒有第二形态:代理回 403」). After
this diff the
`[Self-Approval]` row is still at `:432`: the insertion at `:29` and the
retirement at `:344`
  cancel across it.

## Ratchet before / after

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

```text
main  447e2e8  ✓ .claude/skills/pm-dispatch/references/platform-readings.md: widest table row is 0 bytes (pin 0; headroom 0).
main  447e2e8  ✓ .claude/skills/pm-dispatch/references/platform-readings.md is 466 lines (ceiling 466; headroom 0).
head  d6bc477  ✓ .claude/skills/pm-dispatch/references/platform-readings.md: widest table row is 0 bytes (pin 0; headroom 0).
head  d6bc477  ✓ .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.

```text
 .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:

```text
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](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