fix(pm): the contract-review tier constant names the served tier and is the only place the tier's value lives (#14303) - #14644
Merged
Conversation
…is the only place the tier's value lives Bump CONTRACT_REVIEW_TIER in scripts/pm/dispatch-gates.mjs to the tier the platform serves, hoist its definition above the clause-① mandate table so the three table rows read the constant instead of spelling the literal, and make the two header comments, the seven self-test assertions and the three prose sites in .claude/skills/pm-dispatch/SKILL.md name the constant. The comparison against the served tier stays exact — no family or prefix floor. SKILL.md stays line-neutral under the skill line ratchet: two sentences are rebalanced across their own existing lines so every touched line stays under the 120-byte cap; no words are dropped. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
os-zhuang
approved these changes
Sep 2, 2026
os-zhuang
marked this pull request as ready for review
September 2, 2026 16:54
os-zhuang
enabled auto-merge
September 2, 2026 16:54
This was referenced Sep 2, 2026
This was referenced Sep 2, 2026
This was referenced Sep 3, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #14303
Governed surface (
scripts/pm/**,.claude/**) — draft-only, human merge;needs:contract-reviewrides on the PR. Releases nothing: the diff isscripts/pm/**+.claude/skills/**, both insidescripts/check-empty-changeset.mjs's own release-nothing enumeration (.claude/,skills/, a CI-internal script) — henceskip-changeset.What changed
Exactly the limb triage queued (issue comment 5503739916): the constant is bumped to the served tier, and every other site under
.claude/skills/pm-dispatch/**andscripts/pm/**NAMESCONTRACT_REVIEW_TIERinstead of spelling the model. The comparison stays exact — no family/prefix floor anywhere (that widening is the human floor's and is explicitly out of scope).SKILL.md:773's promise 「模型升级只改一行一个文件」 is now true: the model id is spelled as a value on one line of one file.The diff carries the values; by design this body spells no model identifier. The served-tier
get_sessionreadings and the--tieroutput are on the card, in the flight's report comment.Acceptance test — hit counts, not strings
git grep -nfor the model-family pattern of the triage acceptance test over.claude/skills/pm-dispatch scripts/pm .claude/agents skills/objectstack-pm-dispatch:a5b95ee6)2a8afbc0).claude/skills/pm-dispatch/SKILL.md:361:591:599)scripts/pm/dispatch-gates.mjs.claude/agents/**skills/objectstack-pm-dispatch/**(published twin)Whole-repo
git grep -cfor the same pattern at head: one file, one hit (the same line).Per-site: site | before | after (line numbers from base
a5b95ee6; LITERAL stands for the spelled model id)scripts/pm/dispatch-gates.mjs:8091(now:7955)export const CONTRACT_REVIEW_TIER = LITERAL;, declared AFTER the mandate tableMANDATORY_TIER_GLOBS— the rows must read an initialised binding, and aconstreferenced before its declaration throws at import (import probe after the edit: constant and all three rows resolve to the same value). Docblock now states: one value site, exact comparison, no family/prefix floor:8064:8069:8074(mandate rows)tier: LITERAL, three timestier: CONTRACT_REVIEW_TIER— one definition, zero literals:7970:7980(header comment, clause ① and clause ②)CONTRACT_REVIEW_TIER:15301:15302:15303:15306:15378(self-test).tier === LITERAL.tier === CONTRACT_REVIEW_TIER:15319(self-test)mandLines.includes(LITERAL)mandLines.includes(CONTRACT_REVIEW_TIER):15340(self-test fixture, ambiguity refusal)tier: LITERALbeside atier: 'opus'rowtier: CONTRACT_REVIEW_TIERbeside the same'opus'row:15408(non-empty model id).claude/skills/pm-dispatch/SKILL.md:361(+:360):361opened with LITERAL (the urgent-card triage subagent):360ends立即起 \CONTRACT_REVIEW_TIER` 档,:361opens分诊子代理,…— one sentence rebalanced across its own two lines (:361` was 119 bytes against the 120-byte cap; no words dropped)SKILL.md:591(+:590)model: "LITERAL"一律 \CONTRACT_REVIEW_TIER` 档(`--tier` 输出)— names the constant AND the command whose output the seat types (node scripts/pm/dispatch-gates.mjs --tier, spelled in full in the claim-comment template at:546);① 凡改moved to the tail of:590` for the byte cap, same paragraphSKILL.md:599一律 LITERAL一律 \CONTRACT_REVIEW_TIER` 档, the formreferences/lanes/director.md:13` already usesLine-neutral under the ratchet:
SKILL.mdis 989 lines before and after (ceiling 1005, headroom 16), widest table row 642 bytes (pin 642, unchanged), max-line-bytes scan 0 offenders; touched lines measure 118 / 101 / 110 / 115 / 85 bytes (:360:361:590:591:599).scripts/pm/check-skill-line-ratchet.mjsuntouched.Self-test assertions rewritten, and what each still proves
:15301:15302:15303— each protocol-semantic file derives a mandate whose tier equals the single-source constant: still an exact string equality, still fails on a missing row or a row spelling any other value.:15306— a mixed surface is mandatory and its tier equals the constant.:15319— the mandatory rendering contains the constant's VALUE (the claim comment quotes those lines).:15340— the ambiguity fixture still mandates two DIFFERENT tiers (the constant vs'opus'), so the refusal it pins is unchanged.:15378— every frame-sync-enforced copy derives a tier equal to the constant.:15408— unchanged (non-empty model id).Nothing weakened: every comparison remains an exact equality against one concrete string; only the string's source moved from a second literal to the constant.
premise_false (skipped, listed, never forced)
references/contract-review.md:51,references/platform-readings.md— name the constant, no literal at this base; untouched (comparison text stays "against the constant"; "floor" never written).scripts/pm/ensure-pm-labels.sh:264,scripts/pm/check-governed-queue-guard.mjs:267— comments naming the constant/pattern, no value; untouched..claude/agents/os-dev.md— zero hits at this base; untouched.skills/objectstack-pm-dispatch/**— zero hits at this base; follow-up for objectstack-pm-dispatch: none.check:skill-frame-syncCOPIES anchors inSKILL.mdsit at:880–:897; all three edited sites are outside every frame and the other three copies carry no model id — the model name is not part of any frame, so the published twin does not have to move.SKILL.md:586,references/dispatch-runbook.md:17) are quotations, not values, are not matched by the acceptance pattern, and stay verbatim.Gates — union derived by
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsat the merge-base (22 commands) plus the PM-named extras, run in order at head2a8afbc0underscripts/pm/os-verify-lock.sh(slotissue-14303), each exit code captured before any pipenode scripts/check-ci-filter-parity.mjscoreorcrosspkg, everycrosspkgentry still covers one, and thetestjonode scripts/check-cross-package-test-inputs.mjsnode scripts/check-self-test-wired.mjs--self-testhas that self-test run by CI.node scripts/check-shard-attestation.mjsnode scripts/check-test-completeness.mjsturbo run testlog, which CI's own run suppliespnpm --filter @objectstack/lint run check:doc-formula-expressionspnpm exec turbo run build --filter=@objectstack/formula; prerequisite: a built@objectstack/formula— CI builds the closure and supplies the reading (a local build attempt was cut by the container command cap and is not a reading)pnpm check:agent-test-spelling--token(s) · 1380 launcher-rooted run(s) · 9 separator(s) JUDGED · 5 vitest-bacpnpm check:bash32-floorpnpm check:cli-command-idspnpm check:cross-package-test-inputspnpm check:declared-population-livepnpm check:doc-authoringpnpm check:entry-guardpnpm check:parse-guardpnpm check:pm-governed-mergespnpm check:pm-governed-prosepnpm check:pm-skill-id-lintpnpm check:pm-skill-ratchetpnpm check:pnpm-filter-targets--filteroccurrence(s) across 32 file(s) resolve against 79 workspace package(s); 39 not judged (2 foreign, 17pnpm check:skill-frame-syncpnpm check:watch-hint-literalpnpm check:skill-frame-freshnesspnpm check:nul-bytesnode scripts/pm/check-governed-queue-guard.mjs --self-testpnpm check:pm-dispatch-gatesLock wrapper's own VERDICT:
command-exit 0 · held the lock 380s (6m20s) · waited 0s.check:pm-dispatch-gatesstreamed 1241 ✓ / 0 ✗ case lines. The line-ratchet gate was also run standalone right after the edit (self-test 111 cases pass;SKILL.md989 lines, widest row 642).NOT MEASURED
node scripts/check-test-completeness.mjs— exit 3. Prerequisite: a savedturbo run testlog; CI's run supplies it. Not a red, nothing to fix locally.pnpm --filter @objectstack/lint run check:doc-formula-expressions— exit 3. Prerequisite: a built@objectstack/formula(pnpm exec turbo run build --filter=@objectstack/formula); CI builds the closure and supplies the reading. Neither gate reads the two files this PR touches.Session (durable attribution): https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
🤖 Generated with Claude Code
Generated by Claude Code
Generated by Claude Code