Skip to content

feat(pm): gate the clause-② carrier strip on the served tier the verdict declares - #17990

Merged
os-zhuang merged 4 commits into
mainfrom
claude/issue-17915-served-tier-gates-the-strip
Sep 13, 2026
Merged

os-zhuang merged 4 commits into
mainfrom
claude/issue-17915-served-tier-gates-the-strip

Conversation

@claude

@claude claude Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor

Fixes #17915

CONTRACT_REVIEW_TIER's own docblock declares the comparison against the served tier EXACT — "never a family or prefix floor" — and nothing in this tree performed it. The dispatching seat passes a model as a dispatch parameter, and a passed parameter is configuration rather than a reading; the docblock's other half said the re-review sub-round's "opening self-check reads this", but a self-check is prose to the reviewer, so a round that simply did not run it produced a verdict indistinguishable from one that did. The census on the card measured 11 rounds served below the declared tier across four days and eleven PRs, five of them the only clearance a merged Clause-②: yes pair ever had.

This lands items 1–3 of the director ruling: the verdict carries the reading, and the strip is gated on it.

What changed

.claude/skills/pm-dispatch/references/contract-review.md (2 lines of new rule, net 0 at the ratchet):

  • 〈复核归属与资格(席内)〉 gains, beside the 同形 definition:

    同形含首行 Served-tier:,值取复核者转录的 harness model 盖章;无此行不成裁决。

  • 〈降档保险丝(机读)〉 turns the prose instruction into the mechanism it described:

    清标前 --pair:裁决 Served-tier:CONTRACT_REVIEW_TIER ⇒ exit 4,点名 PR、评论、读数。

scripts/pm/check-clause2-carriers.mjs — a new finding row, C7:

  • readServedTier reads a Served-tier: key line with Reviewed-by:'s own discipline (case-sensitive key, the markdown decoration a seat writes without meaning it). Three-valued: read / unreadable / missing — a carrier never started and one started and left unreadable are different facts.
  • ⭐ The value grammar is [N/M ]tier, corrected from a fixture against the live board before shipping. Every record the ruling's own remediation rounds are posting right now spells it control-first — Served-tier: 75/75 \…`on PR #17877,138/138on #17498 — and the ruling's specimen is written the same way. A reader that demanded the tier token immediately after the colon would have refused every verdict produced under the rule it enforces, on day one. TheN/M` is judged rather than skipped: it is the zero-hit control the discipline already requires, and a count that is not total is the 「回退证据」 whose own rule text voids a verdict entire. Absent, it is vacuous — the tier alone decides, which is the ruling's minimum, so nothing the ruling permits is refused.
  • reviewOfRecord carries the reading on the record it already chose, so C6 and C7 can never disagree about which verdict a clear stands on.
  • c7ServedTierBelow fires on C6's population and no otherneedsRecordRead's completed state, i.e. a clearance judgment of a hung carrier: declared yes, the gate bound and cleared on both carriers, head unmoved. The refusal names the PR, the verdict comment and the served value, at exit 4 (a limb not standing), never 3.
  • The comparison is EXACT against CONTRACT_REVIEW_TIER, imported from dispatch-gates.mjs so the model id keeps exactly one value site across scripts/pm/** and .claude/skills/pm-dispatch/**. No model identifier appears anywhere in this diff outside that import.
  • 42 self-test cases in a new battery: the ruling's three (at tier green, below tier red, line missing red), the live value shape and its bulleted/bolded spelling, the control pins (0/0 void, 12/133 refused as fallback evidence, absent vacuous, a perfect control never rescuing a below-tier value), the exactness pins (a family prefix and an extended value both refused), and the four populations the row must never reach.

Deliberately NOT in scope

A Clause-②: no pair that never carried the label is never refused for lacking the line — it is not in the candidate shape, so the row cannot reach it. A pair still carrying the gate owes nothing yet. An absent or unsigned record stays C6's row alone. No PASS/FAIL token is read to reach any of it: what produced a verdict is measurable, what it concluded stays human.

One deviation from the dispatched file surface — and why it is inside it

The dispatch scoped the diff to those two files. It is those two files — but one edit inside the checker was not foreseen and is worth reading before approving.

scripts/pm/check-clause2-carriers.mjs carried a dispatch-gates: no-path-population marker: "this gate reads no file in the tree at all … so no card's file surface can predict it". The input half is still exactly true. The other half stopped being true the moment C7's import landed: a card editing CONTRACT_REVIEW_TIER moves the value every clearance is judged against, so it does predict this gate. pnpm check:pm-dispatch-gates catches this directly — its live-half case no family both DECLARES no path population and names paths anyway went red on check:pm-clause2-carriers, measured by ablation (base tree: hints []; with the import: hints [".github/workflows"]).

Keeping a declaration that stopped being true is the exact shape C7 itself exists against, so the marker is retired, and the comment left in its place states the trade rather than hiding it: the import channel contributes a followed module's own literals, so this family now also inherits a .github/workflows lead it never opens, on a gate whose CI step runs the self-test only. The designed narrowing (inherited-population, declared by the followed module) cannot express this case — it is per-module, and the same module's globs are a real population for check:pm-widening-tells, which reads them. Filed separately as #17991 rather than worked around here.

⛔ The alternative — restating the tier in this file — is the thing that let the declared tier and the served one drift apart in the first place, and is refused.

Acceptance measurements

All taken at fed29ced, against base 9ccc4179.

reading before after
Served-tier in references/contract-review.md 0 2
Served-tier in check-clause2-carriers.mjs 0 26
Reviewed-by in references/contract-review.md (lit control) 1 1
references/contract-review.md lines 60 60
check-clause2-carriers.mjs longest line (bytes) 390 390
  • Every line of references/contract-review.md is ≤ 120 bytes (LC_ALL=C awk 'length($0)>120' prints nothing); the four edited lines measure 105 / 113 / 111 / 108 B. The 120-byte register is the reference file's; the checker keeps its own line style unchanged (471 → 502 lines over 120 B, max unmoved at 390).
  • git diff --stat origin/main...HEAD → exactly .claude/skills/pm-dispatch/references/contract-review.md and scripts/pm/check-clause2-carriers.mjs.
  • Line budget, paid by density and not by re-wrap: +1 the 同形 line, +1 the mechanism line replacing the 转录档位核验 prose, -1 by folding 「⛔ 自述档位不是读数」 and 「传参只是配置 ⛔ 不作达档读数」 into one clause (they are one rule: a tier claim that is not a harness stamp is not a reading), -1 by dropping the 转录核验 grep recipe, whose method survives at :49 (「每场前必读服役档,读法见 platform-readings.md」) and in the new 同形 line. node scripts/pm/check-skill-line-ratchet.mjs green at 60/60, headroom 0.

Checks

  • pnpm check:pm-clause2-carriers588 cases pass (546 before this PR; the C7 battery is 42 and is registered with its own floor, SELF_TEST_BATTERY_FLOOR 19 → 20, preserving the roster's existing slack).
  • Gate families derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths) on the merged head: 41 families, 41 run, 41 exit 0. Reconciled: --ran reports 41 derived famil(ies) accounted for — 41 run, 0 NOT-MEASURED (a DERIVED zero — all 41 recorded an exit code and none of them is 3).
    • pnpm --filter @objectstack/lint run check:doc-formula-expressions first answered PREREQUISITE NOT MET (exit 3, nothing measured). Built its two declared prerequisites under the shared verify lock (VERDICT command-exit 0 · held the lock 141s) and re-ran it: exit 0.
  • Reverse verification (both legs on the committed tree, restored and verified by blob hash): adding the Served-tier: requirement turned the reference record fixture red — ⭐ the #14155 specimen WITH its record still reads CLEAN overall failed with ["C7"] — before the fixture gained the line. The row can fail.
  • Live control, node scripts/pm/check-clause2-carriers.mjs --pair 17956: exit 2 on this head and exit 2 on the base script for the same PR, same sentence (PR #17956 is not open, or names no card this file can derive). The pair could not be formed, so nothing about it was judged — the dispatch expected 0 for a Clause-②: no pair, and that PR has since left the open set. ⛔ Not a C7 refusal, and unmoved by this diff.
  • Narrowed lint, with its three readings: universe is eslint . over one eslint.config.mjs; npx eslint --no-inline-config --format json scripts/pm/check-clause2-carriers.mjs → 1 file, 0 errors, 0 warnings; the narrowing excludes nothing because that config "never enables type-aware linting (no parserOptions.project, no typed @typescript-eslint rules) for ANY file" (its own line 328), so this diff cannot move any untouched file's verdict. The .md is not an eslint input.
  • grep -naP '[\x00-\x08\x0b\x0c\x0e-\x1f\x7f]' over both edited files: no hits; pnpm check:nul-bytes green.

Changeset

skip-changesetscripts/pm/** and .claude/** publish nothing: neither path is in any package's files[], and both are on the fast track (.claude/** and PM tooling). Label applied and read back.

Acceptance notes

维护者速读(草稿)

改了什么 — 契约复核裁决从此必须带一行 Served-tier:,值取复核者转录里 harness 逐消息盖的 model 字段;check-clause2-carriers.mjs --pair 在判定「双载体已清」时读这一行,不等于 CONTRACT_REVIEW_TIER 就拒(exit 4),并点名 PR、裁决评论和读到的档位。规则文本同步落在 references/contract-review.md,行数 60 → 60。

为什么改 — 常量自己的 docblock 写着「与服役档的比较是 EXACT」,而树上没有任何东西在比。派进去的 model 是配置不是读数,「开场自检」是写给复核者的散文:一轮不跑它,产出的裁决与跑了的长得一模一样。卡上实测 11 轮在档下产出裁决,其中 5 轮是已合并 Clause-②: yes PR 唯一的清标依据。这是本仓在别处一律拒绝的 declared ≠ enforced,落在「一次公共契约加宽到底有没有被复核过」那道门本身。

风险与代价(含回滚) — 失败方向是响亮的:清标被拒,不是被静默放行。代价一:规则落地前写的历史裁决没有这一行,再被判定时会红,补救是复核席把自己转录里已经盖好的读数补写成一条新记录(最新的记录优先,不动载体)。代价二:本 PR 让这个门禁第一次有了树内依赖(CONTRACT_REVIEW_TIER 所在文件),因此退掉了它「无路径面」的旧声明;派生因此多送一条 .github/workflows 的线索,是噪音、已在文件里写明,并已记入验收备注。回滚 = revert 本 PR,一次 revert 即可,门禁回到今天的状态。

席位意见 — (留空,待席位定稿)

你要做的 — 受管面(.claude/**),本 PR 恒为 draft,⛔ 不由任何 AI 席位合并、入队或挂 auto-merge。请人工确认两件事:① 规则文本那两行的措辞;② 退掉 no-path-population 声明这一步是否接受(替代方案是把常量在本文件再写一遍,那正是让档位漂移的那个形状,已拒)。

Provenance

Authored by the domain:skills seat's dispatched executor, session session_01DAcomhvR9kKizeYgg89Vo8 (https://claude.ai/code/session_01DAcomhvR9kKizeYgg89Vo8), on branch claude/issue-17915-served-tier-gates-the-strip. Attribution is stated here in prose because a REST edit of a PR body appends its own footer block: the first edit of this body left two, and this revision sends none so the appended one is the only one.


Generated by Claude Code

`CONTRACT_REVIEW_TIER`'s own docblock declares the comparison against the
SERVED tier EXACT, and nothing performed it: the dispatching seat passes a
model as a parameter, a parameter is configuration rather than a reading,
and the reviewer's "opening self-check" was prose a round could skip while
producing a verdict indistinguishable from one that did not.

A contract-review verdict now carries a `Served-tier:` line whose value is
the harness-stamped served-model field of the reviewer's own transcript,
and `check-clause2-carriers.mjs --pair` refuses to treat a
`needs:contract-review` pair as cleared unless the newest verdict on the
current head declares exactly the constant — naming the PR, the verdict
comment and the served value, at exit 4.

The row (C7) rides C6's population and C6's chosen comment, so a clearance
judgment of a hung carrier is the whole of its scope: a `Clause-②: no` pair
that never carried the label is never refused for lacking the line, and an
absent or unsigned record stays C6's row alone. The constant is imported
from `dispatch-gates.mjs`, which keeps its single value site.

Claude-Session: https://claude.ai/code/session_01DAcomhvR9kKizeYgg89Vo8
Co-authored-by: Claude <noreply@anthropic.com>
C7 compares against `CONTRACT_REVIEW_TIER`, which is declared in
`dispatch-gates.mjs` and imported here, so a card editing that constant
now predicts this gate — and the marker claiming no card's file surface
can predict it stopped being true the moment the import landed. A false
declaration is the shape the new row itself exists against, so it is
retired rather than kept.

The note left in its place states the input half that is still true, the
half that is not, and the imprecision the import channel adds on the way:
a followed module contributes its own literals, so this family inherits a
`.github/workflows` lead it never opens, and the designed narrowing is
per-module and cannot express one caller's case while the sibling really
does read those globs.

Claude-Session: https://claude.ai/code/session_01DAcomhvR9kKizeYgg89Vo8
Co-authored-by: Claude <noreply@anthropic.com>
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 13, 2026
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation labels Sep 13, 2026
Measured against the live corpus rather than the fixture: every record the
remediation rounds post spells the value as the stamp control first, then
the tier — `Served-tier: 75/75 \`<tier>\`` — and the ruling's own specimen
is written the same way. A reader that demanded the tier token immediately
after the colon would have refused every verdict produced under the rule it
enforces, on its first day.

So the grammar is `[N/M ]<tier>`, and the count is judged rather than
skipped: it is the zero-hit control the discipline already requires, and a
count that is not total is the fallback evidence whose own rule text voids
the verdict entire. Absent, it is vacuous — the tier alone decides, which
is the ruling's minimum, so nothing the ruling permits is refused.

Claude-Session: https://claude.ai/code/session_01DAcomhvR9kKizeYgg89Vo8
Co-authored-by: Claude <noreply@anthropic.com>
@claude

claude Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor Author
  • Served-tier: 621/621 claude-fable-5-1 — harness model stamp counted over this seat's own transcript (non-sidechain assistant messages) at 2026-09-13T10:25Z; get_session external_metadata.last_served_model read claude-fable-5-1 at 2026-09-13T10:21Z.

Contract review

Head: fed29ced (PR #17990, card #17915) — read at 2026-09-13T10:25Z by the skills seat at the contract-review tier.

Files: .claude/skills/pm-dispatch/references/contract-review.md (net 0 lines, 60/60, no line over 120 bytes — measured on the head tree) · scripts/pm/check-clause2-carriers.mjs (+401 −23). Both inside the dispatched surface; scripts/pm/dispatch-gates.mjs untouched, as the dispatch's zone rule required (its :49 self-check survives, so the mechanism sits beside it).

① derived judgments — four changes to what the gate accepts or reports, each read against ruling 5651924370 items 1–3:

  1. C7 row (c7ServedTierBelow): on C6's completed population only (declared yes, gate bound and cleared on both carriers, head unmoved) it reads Served-tier: off the SAME comment reviewOfRecord chose for C6, and refuses at exit 4 (EXIT_PAIR_ADVERSE, never 3) unless the value equals CONTRACT_REVIEW_TIER exactly — no family, no prefix; a missing or unreadable line is refused beside a below-tier value; the refusal names the PR, the comment id and the served value, and names the required tier by its constant. Item 2 as ruled. The strip's accept set NARROWS; nothing widens.
  2. Value grammar [N/M ]tier: the stamp control is read and judged — absent is vacuous (the ruling's minimum), present must be non-zero and total; the not-total case is the 回退证据 the file's own :57 rule already voids a verdict for. Corrected against the live corpus (remediation records spell it control-first: 75/75, 138/138); a tight-after-colon reader would have refused every record written under the rule on its first day. Right direction.
  3. no-path-population marker retired because CONTRACT_REVIEW_TIER is now IMPORTED (one value site kept): a card editing dispatch-gates.mjs now predicts this family. Ablation measured base hints [] → head ['.github/workflows']. Effect: such cards run MORE gate families, never fewer; the report-only sweep is unchanged. In-surface, stated in the file, over-reach filed as [finding] a constant-only import inherits the exporter's whole watch surface — inherited-population is per-module, so no caller can decline a fabricated lead #17991. Accepted.
  4. keyLineRegex factoring: AUTHORSHIP_KEY_LINES builds the byte-identical pattern; reviewOfRecord gains a served field (additive); c6RecordNote gains one sentence; SELF_TEST_BATTERY_FLOOR 19 → 20 by exactly the one battery added (42 cases). C1–C6 behaviour-preserving.

Seat measurements on the head tree (git archive fed29ced): check-clause2-carriers.mjs --self-test → 588 cases pass, exit 0. check-skill-line-ratchetcontract-review.md 60 lines (ceiling 60), widest table row 0. LC_ALL=C awk 'length>120' over contract-review.md → nothing. Live blast radius, the PR's script in report-only sweep at 2026-09-13T10:23Z: 24 pairs from 25 open PRs, exactly 2 C7 rows — #17983/#17781 (record 5652680125) and #17979/#17911 (record 5652668107), both ## Contract review records posted after the ruling with no Served-tier: line: the priced transition — each owes one re-post carrying the line, and nothing re-blocks until a landing --pair. --pair 17990 on main's script → exit 0 (a Clause-②: no pair, outside C7's population by design). The dispatch's live control --pair 17877 → exit 2 on both scripts (the PR is no longer open), so the at-tier live spelling is pinned in the battery instead.

② semver: no package touched; skip-changeset is right; ratchets unmoved.

③ boundary flags: open_questions empty. Deviations answered in ① 3–4. #17991 left bare for triage per ruling ③ (5652544529).

Checks on fed29ced: 38 runs; Test Core (1/6) red in packages/spec/scripts/gen-sdui-manifest-cleanup.test.ts ("reaps the whole session it started…": LOCKFDS expected 0, received 1) — a file this diff does not touch, in a package it does not import; main's Test Core (1/6) on 65767d23 is green. Not this PR's. A re-run is refused to this seat (POST /actions/jobs/103706765153/rerun → 403), so the shard stays red until a maintainer re-run or the next push; landing under ruling C waits on a green shard. Lint & Repo Gates was still in progress at 10:20Z.

Implemented-by: claude/issue-17915-served-tier-gates-the-strip
Reviewed-by: session_01DAcomhvR9kKizeYgg89Vo8

Verdict: PASS — items 1–3 of ruling 5651924370 implemented as ruled; nothing widens. Governed (references/**): draft stays; needs-user-decision + reviewers os-zhuang / hotlong; landing by this seat under ruling C (#17971) once an authorized approval exists and the checks read green.


Generated by Claude Code

@claude

claude Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor Author

维护者速读(终稿)

改了什么 —— 一行规则、一道门。规则:契约复审裁决首行必带 Served-tier:,值是复核者转录里 harness 盖的 model 章(可前置 N/N),无此行不成裁决(contract-review.md 60/60,净零行)。门:check-clause2-carriers.mjs 新增 C7 —— 清标所依的最新裁决若无此行、读不出、或不等于 CONTRACT_REVIEW_TIER(精确,不认族名/前缀)⇒ exit 4,点名 PR、评论、读数。只看已清标的 Clause-②: yes 对;no 对与门未摘的对碰不到。

为什么 —— 裁决 5651924370 第 1–3 项:常量 docblock 写着「与服役档精确比较」,树上没人在比;卡上实测多轮在档下清了标。

风险 —— 收紧,不放宽。现网今天已有两条裁决没带这行(#17983#17979 的记录),各补发一条记录即可,落地前不会被卡。一处顺手:该文件的 no-path-population 标记因 import 常量而退役,改 dispatch-gates.mjs 的卡会多跑这道自测,不会少跑。

一红 —— Test Core (1/6) 红在 packages/spec/scripts/gen-sdui-manifest-cleanup.test.ts(本 diff 不碰、main 同分片绿),本席无重跑权限(403)。

批准即由本席按裁决 C 落地;顺手点一次 re-run 即可,或等下一次 push。要不要批?


Generated by Claude Code

@claude

claude Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor Author

The red, once more and with a card — skills seat, 2026-09-13T11:29Z. Test Core (1/6) on fed29ced is still the single red (job 103706765153, 10:01Z): packages/spec/scripts/gen-sdui-manifest-cleanup.test.ts counted one caller lock fd where it expects none — a file this diff does not touch, in a package it does not import; main is green on that shard at ae1b2588 (11:03Z) and c45ea8c3 (11:18Z). A re-run is refused to this seat (403), so the shard stays red until someone with the button re-runs it or the branch gets its next push. Filed as #18010 for the owning lane (bare, for triage). Nothing else on this PR changed: the contract review 5652701385 stands on this head, and the seat lands it under ruling C once an authorized approval exists and the shard reads green.


Generated by Claude Code

@claude

claude Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor Author

Green — skills seat (session session_01DAcomhvR9kKizeYgg89Vo8), 2026-09-13T12:29Z. The one confirming re-run of the failed shard was triggered by this seat at 2026-09-13T12:21Z through the MCP GitHub channel (actions_run_trigger rerun_failed_jobs on run 34750740645 → 201; the seat's REST token was the channel that read 403, now recorded as #18025). Attempt 2: Test Core (1/6) completed success, the run's conclusion success; head fed29ced reads 42 checks, 0 pending, 0 red. Same head, same commit, second run green ⇒ the LOCKFDS failure was the flake shape #18010 describes, not this PR's. Nothing else changed: the contract review 5652701385 stands on this head; the PR now waits only on an authorized approval, after which this seat lands it under ruling C.


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review September 13, 2026 13:12
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 13, 2026
@claude

claude Bot commented Sep 13, 2026

Copy link
Copy Markdown
Contributor Author

Ruling C landing step — skills seat (session session_01DAcomhvR9kKizeYgg89Vo8), 2026-09-13T13:13Z. Authorized approval read: os-zhuang APPROVED on head fed29ced at 2026-09-13T13:12Z (review 5190785460; not dismissed), who also flipped the PR ready and enqueued it in the same minute. Seat readings at 2026-09-13T13:13Z: ① contract review of record 5652701385 names this head fed29ced (unchanged since); ② check-clause2-carriers --pair 17990 exit 0; ③ 42 checks on fed29ced, 0 pending, 0 red (the one red shard re-ran green at 12:2xZ — 5653266976); ④ needs-user-decision cleared in this pass and read back. Queue ref gh-readonly-queue/main/pr-17990-* present. Landing record follows on card #17915 once origin/main carries (#17990); that landing unblocks #18020 (the queue-guard tier, Blocked-by: #17915) and frees references/contract-review.md for the charter chain's references PR.


Generated by Claude Code

Merged via the queue into main with commit 273a665 Sep 13, 2026
60 of 62 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-17915-served-tier-gates-the-strip branch September 13, 2026 13:39
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/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants