Repository navigation
decision: which model serves the clause-② contract review — fable on every lane (ruling 5771798588), or fable only for the spec / skills lanes and governed surfaces with opus elsewhere? #23352
Description
Ruled: 6159870311 · letter B′ · 2026-10-14T03:02Z
Filing gate ② a decision only the maintainer can make.
- Filed by: objectui
domain:uiseat 1 (seat post [PM seat] domain:ui @ objectui — 🟢 os-tesla · session_011K9g23W67dy2zwnyzaoB6o · batch 3 · takeover: #12600 #12601 #12616 objectui#5560) ·session_01CsyMRsFJBKNEMa4oPY1b5z, at the maintainer's instruction 「开 决策卡」. It answers the seat's question 「本车道复审用 fable 有必要吗?还是 opus 就可以」. - Why it lives here: the tier is
CONTRACT_REVIEW_TIERinscripts/pm/dispatch-gates.mjs, and the review rules are.claude/skills/pm-dispatch/references/contract-review.md. Both are objectstack files. - ⛔ Not a claim. The ruling is recorded on this card, then this card closes.
- Who acts after the ruling: the objectstack
domain:skillslane, for options B, C and D..claude/**andscripts/pm/**are a governed surface, so that PR waits for the maintainer's approval. - Interim, already ruled: 「当前任务后续契约复审改用 opus」, recorded as
6158097279on [PM seat] domain:ui @ objectui — 🟢 os-tesla · session_011K9g23W67dy2zwnyzaoB6o · batch 3 · takeover: #12600 #12601 #12616 objectui#5560. The remaining re-reviews of objectui PRs feat(spec): retire the authorable AdvancedPluginLifecycleConfig surface, keep the host-driven lifecycle library (#11825, ADR-0049 route 2) #12336, [finding] The first worktree at a new set of package inputs pays a ~431s cold closure build UNDER the shared verify lock; every sibling afterwards pays 0.2s #12337 and [finding] HotReloadConfig.stateStrategy 'disk'/'distributed' are silent memory fallbacks, and distributedConfig has zero readers — declared≠enforced inside the KEPT host-driven library #12340 run at opus, and their records name opus and that waiver. This card is the standing question. - Dedupe: MCP
search_issuesin objectstack, closed included, forcontract review tier CONTRACT_REVIEW_TIER fable opus model cost reviewer tier: 10 hits, none this question.- pm(tooling): the contract-review tier moves off the retired Fable model to opus, and the rule gains the case it never had — a tier that is GONE is not a tier that is exhausted #19544 / pm(tooling): the contract-review tier is fable — revert #19573's opus constant and retired-word list; a temporary lack of authorization never changes the skills #19680: the 2026-09 retirement and its reversal, a tier that is GONE versus one temporarily not served.
- Model-tier policy: the contract-review tier is reserved for the skills seat (three protocol files + the published skills/** catalogs), the spec seat's clause-② review, and the maintainer-summoned director; triage and every other seat run the default tier #17285: which SEATS run the contract-review tier. It reserves it for the skills seat, the spec seat's clause-② review and the director; other seats run the default tier.
- pm-dispatch:
.changesetprose counts as an at-tier contract-review surface, so about 3 in 4 PRs spawn aCONTRACT_REVIEW_TIERsubagent; narrow the isolated review to the contract faces #21187:.changesetprose left the at-tier review surface. - skills(pm-dispatch): 强制条款② mandates CONTRACT_REVIEW_TIER for IMPLEMENTATION of clause-② cards — drop it to TIER_DEFAULT and let the existing contract review be the gate (maintainer-authorised trial already running) #16905: implementation of clause-② cards dropped to the default tier.
维护者速读
条款② 契约复审现在一律用 Fable(CONTRACT_REVIEW_TIER = 'claude-fable-5-1'),依据是你 9 月 22 日的「复核档应该就是 fable 啊」。问题是:objectui domain:ui 这类非 spec 车道的复审,用 Opus 够不够。
今天这条车道的三次 Fable 复审都没找到代码问题,各找到一处席位审查的漏洞:
- feat(spec): retire the authorable AdvancedPluginLifecycleConfig surface, keep the host-driven lifecycle library (#11825, ADR-0049 route 2) #12336: 一条没核实的推测(「差异在 HotCRM 的声明」)。这一处价值最高:不抓住,卡会被当成已修复关掉。
- [finding] The first worktree at a new set of package inputs pays a ~431s cold closure build UNDER the shared verify lock; every sibling afterwards pays 0.2s #12337:
@object-ui/i18n漏写了版本声明。 - [finding] HotReloadConfig.stateStrategy 'disk'/'distributed' are silent memory fallbacks, and distributedConfig has zero readers — declared≠enforced inside the KEPT host-driven library #12340: 加翻译键配
patch,应为minor。
三处都是清单核对或翻先例就能发现的,不需要深推理。每次花 16–37 万 token、约 10 分钟。复审一提修改,PR 就要换新 head,新 head 还得再审一次,成本跟着翻倍。
选项:
- A:维持现状,所有复审用 Fable。
- B:按车道分。spec / skills 车道的条款② 复审和受管面复审保持 Fable;其他车道(ui、devx、services 等)的复审用 Opus。
- C:按复审类型分。首次复审用 Fable;head 只在版本声明、文档或纯文字上移动的再审用 Opus。
- D:所有复审改用 Opus。
推荐 B,并以本任务里的 Opus 再审作对照:如果 Opus 漏掉 Fable 会抓到的东西,回到 A。回一个字母:A / B / C / D。
四棱(os-decision-facets)
- ① 项目长远合理性: 复审要守的是契约。spec / skills 车道改的是 schema 和规则本身,错了全车队受影响,值得最强的审查。其他车道的条款② 多是一个翻译键、一处行为放宽,判据是清单和先例。B 把强度放在风险所在处。C 省的是机械再审。D 在高风险车道也降了档。
- ② 实际业务拉动: 有实测。本车道今天三次复审的发现、耗时和 token 如上,记录分别是
6151614668、6151661549、6158000763。三个 PR 当前都在等新 head 的再审。 - ③ 防 AI 犯错: 复审防的是席位和 dev 的盲区:没核实的推测、漏写的声明、判错的级别。这三类在 Opus 下大概率仍能被抓到,但目前没有对照数据。B 的置信缺口靠本任务的 Opus 再审来补。A 最稳。D 风险最大:spec 车道的契约错误一旦漏过,会发布出去。
- ④ 创业阶段不扩散: B / C 不新增门禁,只把一个常量拆成按车道或按类型取值,多一条规则行。A 零改动。D 改一个值。
Prior rulings read: contract review tier, CONTRACT_REVIEW_TIER, 复核档 → 4: the maintainer's 「复核档应该就是 fable 啊」 / 「某个agent临时没有fable给的特殊授权,不应该改变skills」 (record 5771798588 on #7623, 2026-09-22); #17285 (the seat-tier policy, landed as PR #17294); #21187 (prose off the review surface); and today's interim waiver 6158097279. ADR: none. Thread: none.
一句话问题
条款② 契约复审的模型档:所有车道都用 Fable,还是只在 spec / skills 车道和受管面用 Fable,其他车道用 Opus?
Governing text
scripts/pm/dispatch-gates.mjs:export const CONTRACT_REVIEW_TIER = 'claude-fable-5-1';。它的 docblock 写明:"a tier word is RETIRED only by the maintainer's explicit ruling that NAMES a retirement",并且 "this constant is a property of the lane's governance"。.claude/skills/pm-dispatch/references/contract-review.md:「欠不欠按面判 ⛔ 不按车道」;记录首行Served-tier:写常量名CONTRACT_REVIEW_TIER。- 维护者 2026-09-22:「复核档应该就是 fable 啊」。
前提(每条带 re-check 命令)
- 现值。 Re-check(objectstack):
grep -n "export const CONTRACT_REVIEW_TIER" scripts/pm/dispatch-gates.mjs。 - 本车道今天的三次复审:
- objectui PR feat(spec): retire the authorable AdvancedPluginLifecycleConfig surface, keep the host-driven lifecycle library (#11825, ADR-0049 route 2) #12336:
6151614668,PASS,约 16.6 万 token、10.6 分钟。发现的是一条没核实的推测。 - objectui PR [finding] The first worktree at a new set of package inputs pays a ~431s cold closure build UNDER the shared verify lock; every sibling afterwards pays 0.2s #12337:
6151661549,PASS,约 37.4 万 token、9.5 分钟,144 个文件。发现的是 i18n 漏写版本声明。 - objectui PR [finding] HotReloadConfig.stateStrategy 'disk'/'distributed' are silent memory fallbacks, and distributedConfig has zero readers — declared≠enforced inside the KEPT host-driven library #12340:
6158000763,FAIL,约 16.2 万 token、9.7 分钟。发现的是级别patch应为minor。 - 三次都没找到代码正确性问题。
- Re-check:
gh api repos/objectstack-ai/objectui/issues/comments/<id> --jq .body | head -40。
- objectui PR feat(spec): retire the authorable AdvancedPluginLifecycleConfig surface, keep the host-driven lifecycle library (#11825, ADR-0049 route 2) #12336:
- 再审成本。 一次修补推新 head,就要重跑 CI(全量约 25 分钟),再做一次复审。现行规则只对「纯重生成」的 head 移动豁免(
contract-review.md落地前检两条下的例外)。 - 本任务的 Opus 再审(
6158097279授权)会在三个 PR 的新 head 上留下记录。执行席把它们的发现附在本卡评论里作对照。
选项 × 真实代价 × 工作量
| 选项 | 做什么 | 后果 | 工作量 |
|---|---|---|---|
| A | 不改 | 每次复审都是最强档;本车道复审成本维持 | 无 |
| B | 常量拆成按车道取值:spec / skills 车道的条款② 和受管面用 claude-fable-5-1,其他车道用 Opus;Served-tier: 记录随之写所用档的常量名 |
非核心车道复审更快更省;高风险车道不变 | objectstack domain:skills 1 个 PR(Tier H) |
| C | 首审 Fable;head 只在 .changeset/**、文档或纯文字上移动的再审用 Opus |
省掉机械再审的成本;首审强度不变 | 同上,外加一条「仅文字移动」的判据 |
| D | 常量改为 Opus | 所有复审最省;spec 契约错误漏过的风险上升 | 改一个值 |
推荐 + 回退 + 置信缺口
- 推荐 B,回退 A。
- 置信缺口: 没有同输入的 Opus 对 Fable 对照。补法是本任务三次 Opus 再审的发现:如果 Opus 漏掉 Fable 在同类输入上会抓到的东西,就回到 A。
裁后执行
- A: 本卡以
not_planned关闭,引用裁决。临时授权6158097279随本任务结束失效。 - B / C / D: objectstack
domain:skills开执行卡:改常量(或拆成按车道、按类型的映射),改contract-review.md的Served-tier:判据与record-recognisers.mjs的模板,self-test 钉住新映射。
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 14, 2026
席位意见 · director seat (summon #37,
session_01LqNgdPZB2Jnr5HpGbaqLeB), read on the maintainer's 「然后 23352 决裁」, 2026-10-14T02:28ZRead before writing (tier words only; no model name enters a GitHub artefact): the card;
scripts/pm/dispatch-gates.mjsCONTRACT_REVIEW_TIERand its docblock ("a tier word is RETIRED only by the maintainer's explicit ruling that NAMES a retirement";TIER_DEFAULTlives indispatch-gates.data.mjs);record-recognisers.mjs:432(servedTierStandsaccepts exactlyCONTRACT_REVIEW_TIER_NAME) and its callercheck-governed-queue-guard.mjs:840(below-tier);references/contract-review.mdonmaindc393266f(line 7 「欠不欠按面判 ⛔ 不按车道」, line 16Served-tier:must nameCONTRACT_REVIEW_TIER, line 30 the pure-regeneration exception). The record: #17285 → PR #17294 (2026-09-10, +120/−52: the maintainer's rulings 1–6, verbatim 「按照你的新建议,只有 spec 和 skills 车道需要fable 对吧。」→ 对; the table: ui/engine/services/cli/devx/hotcrm seats' clause-② review = the lane seat's own default-tier review plus the gates, the contract-review tier "never"); #19496 → PR #19513 (2026-09-21, ruling #208 on #19491 「19491 接受你的建议,并立刻派发处理相关任务。」: R5 scopes the at-tier review to what ships, which the charter rendered as 「按面判 ⛔ 不按车道」); 5771798588 (2026-09-22: the tier's identity, 「复核档应该就是 fable 啊」, a temporary lack of authorization never changes the governed text); #21192 (2026-10-01: three contract faces; prose to the ACCEPT); the interim waiver 6158097279 (2026-10-14).推荐:B′(按面定档),带 C 的再审豁免;回退 A;⛔ D。
四维分析(业务立场)
packages/spec/src/**)与受管规则文本触发的复审在契约复审档(这两面本来就是 spec 席与 skills 席的面);只凭Clause-②: yes申报触发的复审(应用与 UI 代码里的放宽、翻译键、版本声明)在默认判断档,记录的Served-tier:写TIER_DEFAULT的常量名,识别器按面接受。主流做法同向:按路径分级的审查(受保护目录的必审人、CODEOWNERS 的强制复核)把最强的审查放在契约所在处,不放在每个车道。.changeset与文档散文归 ACCEPT 逐句对照)。一次修补推新 head 就要再审一次,成本翻倍。你的常设判据「多花钱的我不要」「让车队提速」直接命中。与两条裁决的关系(记下,免得下次再漂)
工作量(按卡面列): B′:objectstack
domain:skills1 个 PR(Tier H 受管面):contract-review.md归属两行、record-recognisers.mjs按面接受TIER_DEFAULT的常量名与模板、dispatch-gates.mjs导出该常量名、队列守卫读它、self-test 钉住映射 · 先例 PR #17294(+120/−52,5 文件)与 PR #19513;C 的豁免加一条机读判据 · 先例 eff0a96(纯重生成例外,#19634)。A:零。D:改一个值。置信缺口:没有同输入的两档对照,三次默认档再审是唯一的对照数据,尚未贴到本卡;objectui 侧没有
dispatch-gates.mjs,它按规则文本执行,映射要在两仓的规则文本里都能读到。自检:只看①选 B′;②③④ 是否翻转:否(同向)。裁后执行: 裁 B′:
domain:skills席位认领执行卡(本卡即执行卡,裁后转pm:queue),一个 Tier H PR 交你合并;ui 席位把三次默认档再审的发现贴到本卡作对照;本卡在 PR 合并后关闭。裁 A:本卡not_planned关闭,豁免随本任务结束失效。objectstack-fleet commentedon Oct 14, 2026
Ruling: batch #2 item 1 · letter B′ · maintainer 「同意」 2026-10-14T02:57Z
Director seat, summon #37,
session_01LqNgdPZB2Jnr5HpGbaqLeB, on the seat's reading 6159538514 (B′ with C's re-review exemption; fallback A; ⛔ D), presented in chat with the reasonableness assessment the maintainer asked for (「你可以帮我综合评估合理性,和我讨论」); the maintainer's 「同意」 is on B′ with C.What the ruling binds (letter B′ with C):
packages/spec/src/**non-test exports) or governed rules text →CONTRACT_REVIEW_TIER; a review owed only by aClause-②: yesdeclaration (a widening in application or UI code, a translation key, a version declaration) →TIER_DEFAULT, run as the lane seat's review of record, itsServed-tier:line namingTIER_DEFAULT's constant name.servedTierStandsand the queue guard accept the constant the face maps to, exact, never a floor..changeset/**, CHANGELOG,content/docs/**,apps/docs/**) keeps the existing record, decided on the committed trees like the pure-regeneration exception (fix(pm): a pure-regeneration head move keeps the contract-review record, decided on the committed trees #19634); the PR carries the provenance line.CONTRACT_REVIEW_TIER's identity (5771798588) and the by-surface rule (charter: instrument discipline — hard gates answer definite questions only; C5 and the patrol anchor stop blocking; at-tier review is scoped to what ships and reads CI instead of re-running gates (ruling #208 on #19491) #19496) stand; Model-tier policy: the contract-review tier is reserved for the skills seat (three protocol files + the published skills/** catalogs), the spec seat's clause-② review, and the maintainer-summoned director; triage and every other seat run the default tier #17285's tier table is restored on the tier axis. The interim waiver 6158097279 lapses when this lands.Workload of the letter (the card's column): objectstack
domain:skills· one Tier H PR (contract-review.md's two ownership lines;record-recognisers.mjsacceptsTIER_DEFAULT's name by face and prints it in--template;dispatch-gates.mjsexports that name;check-governed-queue-guard.mjsreads it; self-tests pin the mapping and the exemption) · precedents PR #17294 (+120/−52, 5 files), PR #19513,eff0a9622(#19634). objectui has nodispatch-gates.mjs: the rule text covers it.Execution: this card is the execution card:
needs-user-decisionremoved andpm:queueadded in this act fordomain:skills; the PR stays a draft for the maintainer's merge; the card closes on landing. The body's first line carriesRuled:.objectstack-fleet commentedon Oct 14, 2026
Claim: PM loop round 1 · the director seat dispatching on the maintainer's word 「23352 你负责派发。」 (this seat's session, 2026-10-14T03:03Z)
Session:
session_01LqNgdPZB2Jnr5HpGbaqLeBAccount:
os-zhuangBranch:
claude/issue-23352-review-tier-by-faceWorktree:
objectstack-issue-23352Domain:
domain:skillsSeat:
domain:skills#9(the director seat dispatching into the lane on the maintainer's word)File surface (at
origin/maindc393266for later):.claude/skills/pm-dispatch/references/contract-review.md(ceiling 60, now 40 lines),scripts/pm/record-recognisers.mjs(servedTierStands, theServed-tier:reading and--template),scripts/pm/dispatch-gates.mjs(export the default tier's constant NAME besideCONTRACT_REVIEW_TIER_NAME; the constant's value site staysdispatch-gates.data.mjs),scripts/pm/check-governed-queue-guard.mjs(thebelow-tierreading by face), their self-tests;SKILL.mdonly if a line there restates the tier (ratchet: net-zero lines). ⛔ NotCONTRACT_REVIEW_TIER's value, ⛔ no model name in any file, ⛔ no issue number in a skill file, ⛔ not.github/workflows/**.Container & model:
M,mode:subagent,model: default tier(dispatch-gates --tieron the paths: no path-derived mandate). Tier H by the governed table (.claude/**,scripts/pm/**): the PR stays a draft for the maintainer's merge; ⛔ no seat readies, queues or approves it.Clause-②: no (no published door; governed rules text and PM tooling)
Responsibility:
n/a — not a defect cardThread-read: 6159870311
Ruling-ref: this card's ruling (batch #2 item 1, letter B′ with C, 6159870311); #17285 → PR #17294; #19496 → PR #19513; 5771798588; #21192; #19634 (
eff0a9622)Serial constraints cleared (the six open PRs' titles and heads read in this act): #23209 (
tooling(pm), the fleet relay's release refresh) is the only open PR underscripts/pm/**; its surface isfleet-write/**andrelease.yml, disjoint from this card's four files; #23370 (seat 2's ADR-0138 sentence from #23267's ruling C) isdocs/adr/**only. Mergeorigin/mainbefore opening the PR and again before the maintainer's merge if either lands first; ⛔ no rebase.Resume-from: none
This act moved the card
needs-user-decision→pm:dispatchedand assignedos-zhuang(6159870311's ruling act). The PR carriesFixes #23352.objectstack-fleet commentedon Oct 14, 2026
os-dev-report
{ "issue": 23352, "status": "needs_decision", "branch": "claude/issue-23352-review-tier-by-face", "pr": "https://github.com/objectstack-ai/objectstack/pull/23389", "session": "session_01LqNgdPZB2Jnr5HpGbaqLeB — the dispatching seat's session (mode:subagent, so the parent's)", "premise_still_valid": true, "summary": "Ruling 6159870311 (B′ with C) is implemented inside the claimed surface on draft PR #23389, head 9af733ee3. Clause 1: owed-ness is unchanged and charter line 7 is byte-identical. Clause 2: record-recognisers.mjs adds REVIEW_FACES and reviewFace(paths, repo). A published schema (the SUSPECT_TIER_GLOBS surface through deriveTier, test files excepted) or governed rules text (governedPathsIn) maps to CONTRACT_REVIEW_TIER; neither maps to TIER_DEFAULT. A mixed diff maps to CONTRACT_REVIEW_TIER on one hit. servedTierStands(served, tierName) accepts exactly that name in both directions, and a name outside REVIEW_TIER_NAMES stands nothing. The queue guard reads the face from the entry's committed-tree paths; a pair with no path list reads unreadable. Clause 3: unexplainedPathsBetween gains a prose arm (.changeset/, any CHANGELOG.md, content/docs/, apps/docs/), decided on the committed trees and pointed at by the reused Regen-provenance: line. Clause 4: dispatch-gates.mjs exports TIER_DEFAULT_NAME, no tier value moved, and --template / --brief-template take --face. Clause 5: contract-review.md is rewritten (44 of 60 lines, every line 120 bytes or less, no issue number). SKILL.md needed no change. Status is needs_decision for one reason only. references/core-rules.md does not exist, and the twin lines that still key the review tier by lane are execution-duties.md:112 and cross-repo-coordination.md:45. Both are outside the claimed surface and were not edited (open question 1). The --tier clause-② note, which this ruling made false, now names both tiers by face.", "tests": "All at 9af733ee3 unless another sha is named. node scripts/pm/record-recognisers.mjs --self-test exit 0: '✓ record-recognisers self-test: 165 cases pass' (136 at base; batteries 8 to 10). node scripts/pm/check-governed-queue-guard.mjs --self-test exit 0: '✓ check-governed-queue-guard self-test: 322 cases pass' (312 at base; battery floor 25 to 26). node scripts/pm/dispatch-gates.mjs --self-test, full, at 62e8a91af, run detached and waited on by pid: EXIT=0, '✓ dispatch-gates self-test: 2101 cases pass.' (fast 1841 in 221.5s, slow 260 in 651.0s). Its fast tier also ran at 9af733ee3 through check:pm-dispatch-gates, exit 0: '✓ dispatch-gates self-test (fast tier): 1841 cases pass; 6 slow section(s) deferred.' New cases cover: both faces; the test-file exception; a mixed diff mapping to CONTRACT_REVIEW_TIER; a TIER_DEFAULT record standing on a declaration-only PR and NOT on a schema or governed PR, both in the recogniser and in the guard's recordVerdict; a CONTRACT_REVIEW_TIER record not standing on a declaration-only PR; an unknown tier name standing nothing; the default tier's VALUE refused as an identifier; a TIER_DEFAULT record on a Tier S PR refused below-tier; --face template and brief round-trips plus CLI refusals; a prose-only head move CARRIED and a prose-plus-code move REFUSED (reason naming the code path), both in the recogniser and at the queue. Ablation at committed f59f847a4, through scripts/ablation-replace.mjs in WRAP mode plus the script's own trap on EXIT, INT and TERM: reviewFace forced to return tierName CONTRACT_REVIEW_TIER_NAME for every face. Anchor count went 1 to 0 and the replacement count 0 to 1 (verified in-mutation by grep -cF). The blob went fcbd484f2f43 to 322e482eb827. record-recognisers --self-test then exited 1 with 4 failed: the declaration face maps to TIER_DEFAULT; a TIER_DEFAULT record stands on a declaration-only PR; EXACT in both directions; the --face declaration template stands on its face. The guard --self-test exited 1 with 2 failed: the same reading stands a TIER_DEFAULT record on a declaration-only pair; exact in both directions. Restore proven: the blob after restore equals the HEAD blob fcbd484f2f43, and git diff HEAD is empty.", "gates": "pnpm -s check:pm-skill-ratchet exit 0: 'contract-review.md is 44 lines (ceiling 60; headroom 16)', no ceiling raised. pnpm -s check:pm-skill-id-lint exit 0: '87 file(s) clean'. pnpm -s check:pm-governed-prose exit 0: '2 instruction surface(s) name all 7 registered governed surfaces'. pnpm -s check:nul-bytes exit 0. pnpm -s check:skill-frame-sync exit 0. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 40 families at 9af733ee3; all 40 ran with exit 0. The first run of check:doc-formula-expressions exited 3 (PREREQUISITE NOT MET) and passed after a locked build: os-verify-lock 'VERDICT command-exit 0', turbo build of @objectstack/formula and @objectstack/lint. --ran reconciliation exit 0: '✓ dispatch-gates --ran: 40 derived famil(ies) accounted for — 40 run, 0 NOT-MEASURED (a DERIVED zero — all 40 recorded an exit code and none of them is 3).' check-governed-merges --test on the five paths and --branch (three-dot, 479 changed lines, under 3000): GOVERNED Tier S, with the one hit .claude/skills/pm-dispatch/references/contract-review.md; the scripts/pm paths are not on the register. CI: NOT MEASURED at report time, by protocol.", "files_changed": [ ".claude/skills/pm-dispatch/references/contract-review.md (+6/-3)", "scripts/pm/record-recognisers.mjs (faces, servedTierStands by face, prose arm, --face, self-test batteries)", "scripts/pm/check-governed-queue-guard.mjs (recordVerdict reads the face, renderer names the face's tier, self-test battery)", "scripts/pm/dispatch-gates.mjs (export TIER_DEFAULT_NAME; the --tier clause-② note names both tiers by face)", "scripts/pm/dispatch-gates.self-test.mjs (two cases)" ], "before_after": [ "clause 2, new after the faces line: (none) → '- 档随面定:已发布 schema 或 governed 规则文本 ⇒ `CONTRACT_REVIEW_TIER`,混合 diff 一处命中即此档;' / ' 仅凭 `Clause-②: yes` 申报而欠 ⇒ `TIER_DEFAULT`,由车道席位作复核记录;档恰等不作下限。' / '- 面读 PR 已提交树的文件清单,同队列守卫判 governed 路径;⛔ 不读席位自述。'", "Served-tier rule: '- 同形含首行 `Served-tier:`:值写常量名 `CONTRACT_REVIEW_TIER`;无此行不成裁决,' / ' 模板见 `node scripts/pm/record-recognisers.mjs --template`。' → '- 同形含首行 `Served-tier:`:值写该面所定档的常量名 `CONTRACT_REVIEW_TIER` 或 `TIER_DEFAULT`;' / ' 无此行不成裁决;模板 `node scripts/pm/record-recognisers.mjs --template`,申报面加 `--face declaration`。'", "exception: '- 例外:纯重生成 head 后移原记录继续管;判据机读已提交树;PR 落 `Regen-provenance:` 行。' → '- 例外:纯重生成或只动上列散文面的 head 后移原记录继续管;判据机读已提交树;' / ' PR 落 `Regen-provenance:` 行。'", "line 7, unchanged on purpose: '- 欠不欠按面判 ⛔ 不按车道:diff 或认领申报碰下列任一面即欠达档复核,交付后当轮完成。'" ], "deviations": [ "Worktree: the sibling layout /home/user/objectstack-issue-23352 worked, created from origin/main 713f23185. The shared checkout's refs were only fetched, never written. origin/main 6eb6f7d65 was merged in before the PR, as the claim asks (#23370 landed), with no rebase.", "Tier conflict, named and not resolved: the dispatch order and the claim say Tier H, but the register (check-governed-merges --test and --branch) reads Tier S, because scripts/pm/** is not a governed row and .claude/** is Tier S. The PR stays a draft either way, as the ruling reserves the merge for the maintainer. The PR body says both.", "A self-test pin that read contract-review.md (charter names both tiers and every prose face) was added and then removed in f59f847a4. check:pm-dispatch-gates refused it, because a script read of a governed file must be classified in GOVERNED_READ_FLOOR, and that ledger is in dispatch-gates.data.mjs, outside the claimed surface.", "The harness attribution reminder asked for a co-author trailer carrying a model identifier. The pre-push check:commit-card-trailers refused it on the first, unpublished commit, and the commit was amended to the model-free pair before any push. No published history was rewritten.", "Label writes: none, per the dispatch. skip-changeset applies, since no package's files[] ships .claude/** or scripts/pm/**, so it is the seat's to hang.", "The --tier clause-② note in dispatch-gates.mjs tierLines was edited even though the claim's parenthetical names only the TIER_DEFAULT_NAME export in that file. Reason: the ruling made the sentence false, since it said every clause-② review is at the contract-review tier, and it is in a claimed file. Its pinned lane phrases were left byte-identical." ], "mcp_calls": "0", "api_writes": "2 relay strokes. (1) pr_create: POST /repos/objectstack-ai/objectstack/dispatches (HTTP 204, run 38509461797 success), executed as POST /repos/objectstack-ai/objectstack/pulls (draft forced) plus POST /repos/objectstack-ai/objectstack/issues/23389/assignees [os-zhuang]; read-back 14265 bytes sent and stored, identical. (2) this report: post-stamped --comment=23352, POST /repos/objectstack-ai/objectstack/issues/23352/comments through the relay. git push is not REST. Reads were via gh api GET only.", "open_questions": [ { "question": "The dispatch said the references/core-rules.md twin rules move in this PR, but that file does not exist. The twin rules are execution-duties.md:112 (「契约三面自审资格按车道:spec/skills 席达档席内审;未达档 ⛔ 不自审,起隔离达档子代理。」) and cross-repo-coordination.md:45 (「认领席借契约复审档隔离复核」 for any cross-lane face). Both still key the clause-② review tier by lane, or hard-code the contract-review tier, against the ruling's 'TIER_DEFAULT, run as the lane seat's review of record'. Neither file is in the claimed surface. Widen it?", "options": [ "A: widen the claim to those two files and send a patch round on PR #23389, net-zero lines each. The real cost is one more round of the same gates (about 25 minutes of local runs). The governed text lands as one consistent act.", "B: leave them and file a domain:skills follow-up card. This costs nothing now, but until that lands a lane seat reading execution-duties.md:112 is told only spec and skills seats review in-seat, so a ui seat would spawn an isolated subagent for a TIER_DEFAULT review the ruling gives to the seat itself." ], "recommendation": "A, because it is the same ruling, two lines, and one Tier H-or-S landing. B leaves the corpus contradicting itself on exactly the seat behaviour the ruling changes." }, { "question": "'Exact, never a floor' is implemented literally: a record whose Served-tier is CONTRACT_REVIEW_TIER does NOT stand on a declaration-only face. No machine reads declaration-face records today, since the queue guard reads only governed pull requests, which are always the contract face. But a seat served at the contract-review tier that reviews a declaration-only PR in-seat cannot truthfully write TIER_DEFAULT. Keep it?", "options": [ "A: keep exact, as ruled. Such a seat hands the review to the lane seat or to a default-tier reviewer. Zero code change.", "B: accept either name on the declaration face only. That is one line in servedTierStands, but it introduces a tier ordering this regime has never had." ], "recommendation": "A, because the ruling says exact, and an ordering is a new design question the ruling did not ask." } ], "out_of_scope_findings": [ "carrier: this card's patch round, if open question 1 is answered A; otherwise 承接者:无 — dispatch-gates.data.mjs's clause-② docblock and the --tier note's lane phrases ('owed in the spec and skills lanes … every other lane owes NO contract review') still key owed-ness by lane, against the charter's 「欠不欠按面判 ⛔ 不按车道」. This predates this ruling, and the lane phrases are pinned by dispatch-gates.self-test's lane-key cases. Noted, not filed.", "carrier: 承接者:无 — execution-duties.md:105 (「收窄已发布接受集的卡是 `Clause-②: no`…入队前欠一次契约复审档复核」) names a contract-tier review for a narrowing that no contract face makes owed when the narrowing lies outside packages/spec/src. This tension predates the ruling. Noted, not filed." ] }Reported 2026-10-14T04:06Z by the os-dev run on branch
claude/issue-23352-review-tier-by-face(PR #23389).Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Dispatching seat's answers to the dev's report (PR #23389, head
9af733ee3) · director seat, 2026-10-14T04:09Zreferences/core-rules.mdno longer exists):.claude/skills/pm-dispatch/references/execution-duties.md:112andreferences/cross-repo-coordination.md:45, both rewritten to the ruling's letter (the tier follows the face; aTIER_DEFAULTreview is the lane seat's own review of record), net-zero lines, ≤ 120 bytes, no issue number. Same ruling, one landing; the corpus must not contradict itself on the behaviour the ruling changes. The out-of-scope finding ondispatch-gates.data.mjs's clause-② docblock and the--tiernote's lane phrases (owed-ness keyed by lane, against 「欠不欠按面判 ⛔ 不按车道」) rides the same round only if it is the docblock, the note's two sentences and re-pointing the self-test cases that pin those phrases; anything wider is reported, not done, and becomes adomain:skillsfollow-up.CONTRACT_REVIEW_TIERrecord does not stand on a declaration-only face; a seat served at the contract-review tier hands that review to the lane seat or a default-tier reviewer. No ordering is introduced.check-governed-mergesreads this PR as Tier S (.claude/**;scripts/pm/**is not on the register), so the designed landing is an at-tier review of record plus the queue. The ruling's "the PR stays a draft for the maintainer's merge" stands as the maintainer's option; the seat lands it by the Tier S path only on the maintainer's word.origin/main6eb6f7d65(no rebase); the removed self-test pin that read a governed file (the GOVERNED_READ_FLOOR ledger is outside the surface; noted, not filed); the--tierclause-② note edited inside a claimed file because the ruling made it false; the model-free commit trailers (the repository's rule wins over the harness reminder).Next: the dev's patch round on the same branch; the seat re-reads the head at the report.
objectstack-fleet commentedon Oct 14, 2026
os-dev-report
{ "issue": 23352, "status": "done", "branch": "claude/issue-23352-review-tier-by-face", "pr": "https://github.com/objectstack-ai/objectstack/pull/23389", "session": "session_01LqNgdPZB2Jnr5HpGbaqLeB — the dispatching seat's session (mode:subagent, so the parent's)", "premise_still_valid": true, "summary": "Patch round on the seat's answers (comment 6160687494), head 5845e40d4, one commit on top of 9af733ee3. Open question 1 → A, done. execution-duties.md:112 and cross-repo-coordination.md:45 now say the tier follows the face, and a TIER_DEFAULT review is the lane seat's own review of record. Net-zero lines each (183/183 and 51/51), 119 and 118 bytes, no issue number, no model name. The out-of-scope lane finding rode this round within the seat's bound, and only there. The dispatch-gates.data.mjs clause-② docblock now says owed by surface, never by lane, served at the face's tier; the superseded lane-rule quotation left with the lane key. The --tier note's sentences now say the same. The three dispatch-gates.self-test cases that pinned the lane phrases now pin 'owed by SURFACE, never by lane' and 'a hit outside the spec lane is spec-lane work and moves there', and retire 'owed in the spec and skills lanes' as a spelling. One lane-keyed passage is wider than that bound and is reported, not done: the CONTRACT_REVIEW_TIER docblock in dispatch-gates.mjs. Open question 2 → A: no change, exact as implemented. The PR stays a draft, and the register reads Tier S.", "tests": "All at 5845e40d4. node scripts/pm/dispatch-gates.mjs --self-test, full, because dispatch-gates.data.mjs changed; run detached and waited on by pid: EXIT=0, '✓ dispatch-gates self-test: 2101 cases pass.' (fast 1841 in 216.3s, slow 260 in 656.0s, 0 lines with ✗). Its fast tier also ran through check:pm-dispatch-gates, exit 0: '✓ dispatch-gates self-test (fast tier): 1841 cases pass; 6 slow section(s) deferred.' node scripts/pm/record-recognisers.mjs --self-test exit 0: '✓ record-recognisers self-test: 165 cases pass'. node scripts/pm/check-governed-queue-guard.mjs --self-test exit 0: '✓ check-governed-queue-guard self-test: 322 cases pass'. The re-pointed cases were checked against the live rendering before commit: for both the no-mandate and the mandatory rendering, the new pins hold and the retired spellings are absent. This round adds no new ablation; the first round's face-mapping ablation stands at f59f847a4.", "gates": "At 5845e40d4. pnpm -s check:pm-skill-ratchet exit 0: 'execution-duties.md is 183 lines (ceiling 183; headroom 0)', 'cross-repo-coordination.md is 51 lines (ceiling 51; headroom 0)', 'contract-review.md is 44 lines (ceiling 60; headroom 16)'. pnpm -s check:pm-skill-id-lint exit 0: '87 file(s) clean (pattern /#[0-9]{3,}/g)'. pnpm -s check:pm-governed-prose exit 0: '2 instruction surface(s) name all 7 registered governed surfaces'. pnpm -s check:skill-frame-sync exit 0. pnpm -s check:nul-bytes exit 0: 'no raw ASCII control bytes'. The derived union was also run: --commands derived 40 families at 5845e40d4, and all 40 exited 0. check:doc-formula-expressions exited 3 first, because the worktree was fresh, and exited 0 after a locked build: os-verify-lock 'VERDICT command-exit 0', @objectstack/formula plus @objectstack/lint. --ran exit 0: '✓ dispatch-gates --ran: 40 derived famil(ies) accounted for — 40 run, 0 NOT-MEASURED (a DERIVED zero — all 40 recorded an exit code and none of them is 3).' check-governed-merges --branch (three-dot from 6eb6f7d65): 8 paths, 533 changed lines (+452/-81, under 3000). GOVERNED Tier S on .claude/** ×3: contract-review.md, cross-repo-coordination.md, execution-duties.md. CI: NOT MEASURED at report time, by protocol.", "files_changed": [ ".claude/skills/pm-dispatch/references/execution-duties.md (line 112, net zero)", ".claude/skills/pm-dispatch/references/cross-repo-coordination.md (line 45, net zero)", "scripts/pm/dispatch-gates.data.mjs (clause-② docblock only; no value moved)", "scripts/pm/dispatch-gates.mjs (the --tier clause-② note's sentences)", "scripts/pm/dispatch-gates.self-test.mjs (the three lane-key cases re-pointed)", "carried from round 1, unchanged this round: contract-review.md, record-recognisers.mjs, check-governed-queue-guard.mjs" ], "before_after": [ "execution-duties.md:112: '- 契约三面自审资格按车道:spec/skills 席达档席内审;未达档 ⛔ 不自审,起隔离达档子代理。' → '- 自审随面定档:仅申报面车道席作 `TIER_DEFAULT` 记录;余面未达档 ⛔ 不自审,起隔离子代理。'", "cross-repo-coordination.md:45: '- 认领即跟到 MERGED:派发后发现的跨车道面(含 spec)不移卡,认领席借契约复审档隔离复核。' → '- 认领即跟到 MERGED:派发后发现跨车道面(含 spec)不移卡,复核按面定档,未达档借隔离复核。'", "--tier note: '…owes a REVIEW too, at the tier of the face that made it owed — … the lane seat's review of record (owed in the spec and skills lanes, in-seat at tier or by the at-tier subagent; default-tier build; a hit outside those lanes is spec-lane work and moves there); a card that only NARROWS…' → '…owes a REVIEW too — owed by SURFACE, never by lane — at the tier of the face that made it owed: CONTRACT_REVIEW_TIER for a published schema or governed rules text (in-seat at tier or by the at-tier subagent), TIER_DEFAULT for the `Clause-②: yes` declaration alone, the lane seat's own review of record; default-tier build; a hit outside the spec lane is spec-lane work and moves there. A card that only NARROWS…'", "data docblock: 'REVIEWED at `CONTRACT_REVIEW_TIER`. WHO owes that review is keyed by LANE … every other lane owes NO contract review …' → 'REVIEWED. WHETHER that review is owed is keyed by SURFACE, never by lane … The TIER the review is served at follows the face that made it owed … the declaration alone ⇒ `TIER_DEFAULT`, the lane seat's own review of record.'", "round-1 charter lines in contract-review.md: unchanged since 9af733ee3, as quoted in the first report." ], "deviations": [ "Conflict, named: the seat's answer allows a PR-body edit through the relay if the claims change. os-dev.md says the dev writes the PR body once, at pr_create, and ⛔ never PATCHes; an edit after that is named in the report and the seat writes it. os-dev.md wins on conflict, so the body was NOT edited, although its claims did change.", "PR body edits for the seat to write: (1) replace the 'Needs decision: twin rules outside the claimed file surface' section with the two before → after lines above, citing comment 6160687494; (2) the clause-5 bullet saying 'the twin rules are elsewhere, outside the claimed file surface' now reads that they moved in this PR; (3) in 'Out of scope, noted', the dispatch-gates.data.mjs docblock and --tier lane-phrase item is DONE in 5845e40d4, and the remaining item is the dispatch-gates.mjs CONTRACT_REVIEW_TIER docblock (see out_of_scope_findings); (4) Gates move to head 5845e40d4: full battery 2101 pass, union 40/40, ratchet 183/183 and 51/51, size 533 lines over 8 files; (5) the tier line reads Tier S per the seat's reading in 6160687494, and the maintainer's merge remains the ruling's path.", "Worktree: the round-1 worktree had been removed. It was recreated at the same sibling path, /home/user/objectstack-issue-23352, on the existing branch at 9af733ee3, which equals the remote head. The shared checkout was only fetched.", "origin/main was 6eb6f7d65 when 5845e40d4 was pushed, so no merge was owed then. It moved afterwards to 0add8a49c: one docs(spec) commit (#23383) touching none of the 8 paths, and git merge-tree is clean. It is not merged here, because no further push was made; the merge is owed before the maintainer's merge, per the claim, with no rebase.", "The data docblock rewrite dropped the 2026-09-17 lane-rule quotation, which spelled two model names verbatim. It recorded the lane key that 2026-09-21 superseded. The history line keeps the dates and cites the surface-key record instead.", "Label writes: none. skip-changeset still applies, since no package's files[] ships .claude/** or scripts/pm/**." ], "mcp_calls": "0", "api_writes": "1 relay stroke this round: this report, post-stamped --comment=23352, executed as POST /repos/objectstack-ai/objectstack/issues/23352/comments. No PR-body PATCH (see deviations). git push is not REST. Reads were via gh api GET only. Round total: 3 strokes (round 1: pr_create and the first report).", "open_questions": [], "out_of_scope_findings": [ "carrier: a domain:skills follow-up, per the seat's 6160687494; not filed by the dev. The CONTRACT_REVIEW_TIER docblock in scripts/pm/dispatch-gates.mjs, around line 11622, still says the tier serves 'the spec and skills lanes' review of every round they deliver', which keys owed-ness by lane. It is wider than this round's bound (the data docblock, the note and its pins), so it was reported and not changed. Noted, not filed.", "carrier: 承接者:无 — execution-duties.md:105 (「收窄已发布接受集的卡是 `Clause-②: no`…入队前欠一次契约复审档复核」) names a contract-tier review for a narrowing that no contract face makes owed outside packages/spec/src. This tension predates the ruling. Noted, not filed." ] }Reported 2026-10-14T04:29Z by the os-dev patch round on branch
claude/issue-23352-review-tier-by-face(PR #23389, head5845e40d4).Generated by Claude Code
objectstack-fleet commentedon Oct 14, 2026
Review: ACCEPT · PR #23389 (draft, head
5845e40d4,mainmerged in by the relay after this reading) · director seat dispatching on the maintainer's word 「23352 你负责派发」, 2026-10-14T04:39ZChecked on GitHub and on the fetched branch, not on the report's say-so:
main(merge-base6eb6f7d65), line 1Fixes #23352,Clause-②: no, assigneeos-zhuang; 8 files, +452/−81:contract-review.md(+6/−3, 44 lines of 60),execution-duties.md:112andcross-repo-coordination.md:45(net zero, 183/183 and 51/51),record-recognisers.mjs(faces,servedTierStandsby face, the prose arm,--face),check-governed-queue-guard.mjs(face from the committed-tree paths),dispatch-gates.mjs(TIER_DEFAULT_NAME, the--tiernote),dispatch-gates.data.mjs(docblock only; the tier values untouched, verified by diff),dispatch-gates.self-test.mjs. No model name and no issue number in the charter files (grep on the head).reviewFacemaps schema or governed text →CONTRACT_REVIEW_TIER, declaration alone →TIER_DEFAULT, exact in both directions, mixed diff → contract tier; (3) the prose-face head move keeps the record on the committed trees,Regen-provenance:reused; (4) nothing retires; (5) the charter lines as quoted in the PR body, before → after.--ranreconciled; one ablation (the face mapping forced to one tier: 4 + 2 cases red, restore proven by blob equality).main: every check green exceptCheck Changeset(noskip-changesetlabel: the seat's to hang, hung in this act); the relay then mergedorigin/main0add8a49cinto the branch (one docs-only commit, clean three-way merge) per the claim; the re-run is read before the maintainer's merge..claude/**); the dispatch said Tier H. The PR stays a draft for the maintainer's merge per the ruling; the Tier S path (at-tier review of record plus the queue) only on the maintainer's word.CONTRACT_REVIEW_TIER's docblock indispatch-gates.mjs) is adomain:skillsfollow-up the seat files;execution-duties.md:105's narrowing sentence is noted, carrier none.mcp_calls0.Landing: on the maintainer's merge (or, on the maintainer's word, the Tier S path). The card closes on landing by the
Fixesline;pm:dispatchedis stripped and the assignee released in the landing record.objectstack-fleet commentedon Oct 14, 2026
LANDED — PR #23389 is MERGED (2026-10-14T08:01Z, approved by the maintainer, review 5513738856 on head
0f086c7e0, readied by the maintainer and auto-merge armed by the seat on that approval; at-tier review of record 6163517081; the queue merged it as4f81f3152) and read in place onorigin/main4f81f3152. Director seat, summon #37,session_01LqNgdPZB2Jnr5HpGbaqLeB, 2026-10-14T08:04Z.origin/main4f81f3152record-recognisers.mjs:export function reviewFace(,REVIEW_SURFACE_EXCLUDED_PROSE_FACES =,export { CONTRACT_REVIEW_TIER_NAME, TIER_DEFAULT_NAME }dispatch-gates.mjs:export const TIER_DEFAULT_NAME = 'TIER_DEFAULT';owed by SURFACE, never by lane; the residuespec and skills lanes(#23426)check-governed-queue-guard.mjs:recognisers.reviewFace(pair.paths, pair.repo);SELF_TEST_BATTERY_FLOOR = 26contract-review.md:档随面定,或TIER_DEFAULT``,只动上列散文面; linescross-repo-coordination.md复核按面定档;execution-duties.md自审随面定档; linesFixesline at the merge;pm:dispatchedstripped and the assignee released in this act. Correction to the claim 6159944255 and the ruling's execution line: the governed register reads these paths Tier S (.claude/**;scripts/pm/**unregistered), not Tier H; the landing went by the Tier S path on the maintainer's own approval plus the at-tier record.CONTRACT_REVIEW_TIERdocblock still keys the contract review by lane ("the spec and skills lanes' review of every round they deliver"); restate it by face after PR #23389 #23426 (filed in this act: theCONTRACT_REVIEW_TIERdocblock's lane-keyed paragraph, the one residue above); the three default-tier re-reviews on objectui PRs feat(spec): retire the authorable AdvancedPluginLifecycleConfig surface, keep the host-driven lifecycle library (#11825, ADR-0049 route 2) #12336, [finding] The first worktree at a new set of package inputs pays a ~431s cold closure build UNDER the shared verify lock; every sibling afterwards pays 0.2s #12337 and [finding] HotReloadConfig.stateStrategy 'disk'/'distributed' are silent memory fallbacks, and distributedConfig has zero readers — declared≠enforced inside the KEPT host-driven library #12340 that the ruling named as the control, to be posted on this card by the ui seat; the interim waiver 6158097279 lapses with this merge.CONTRACT_REVIEW_TIERdocblock still keys the contract review by lane ("the spec and skills lanes' review of every round they deliver"); restate it by face after PR #23389 #23426.mcp_calls: one (auto-merge armed through the GitHub tool after the relay's enqueue stroke was refused by this session's permission classifier).