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
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