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 :28unstable 可源自 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-runscompleted/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
This was referenced Sep 17, 2026
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