Repository navigation
finding(types): 57 ZodDefault nodes still reachable from the published @object-ui/types/zod barrel after #7735 — batch #69's principle stops at the files it named, and the rest are imported by reference from @objectstack/spec #8317
Description
Observation-class finding, measured by the objectui#7735 developer session and escalated by that PR's contract review. Filed unassigned, no labels — grading is the triage seat's. ⛔ This is a ruling question, not a defect with an obvious repair.
The claim
Decision batch #69 (2026-09-07, maintainer 「其他同意」) ruled, on objectui#7735:
A validator validates; it does not write values into an author's document.
PR #8299 delivers that for the 41 .default() call sites written in this repo's own mirrors. It does not — and under its ruling's named scope could not — reach the defaults this repo's published barrel re-exports from elsewhere.
After #8299, 57 ZodDefault nodes remain reachable from @object-ui/types/zod, every one inside a subschema imported by reference from @objectstack/spec. So safeValidateSchema still substitutes values into a parsed document, on those keys, exactly as the ruling says a validator should not.
Reproducer
safeValidateSchema({ type: 'object-view', objectName: 'account', navigation: {} })
⇒ navigation: { mode: 'page', preventNavigation: false, openNewTab: false, size: 'auto' }
Four keys the author did not write, present in result.data. The affected families named by the measuring session: app.active / isDefault · object-view.navigation.{mode, preventNavigation, openNewTab, size} · list-view.sharing.type · kanban.grouping.fields[].{order, collapsed} · page.interfaceConfig.* · dashboard chartConfig.*.
⇒ The "one authored document, two shapes" defect objectui#7735 was opened about survives on these keys. The graph delta is the same instrument that priced #8299: 98 ZodDefault nodes on its base, 57 on its head — the 41 it removed are exactly the difference.
⭐ Why this is a ruling and not a port — two measurements that set the price
1. The upstream population is two orders of magnitude larger. Measured on objectstack-ai/objectstack origin/main = f2f6684, real .default() call sites (docblock mentions excluded):
| scope | real call sites |
|---|---|
packages/spec/src/** (whole package) |
1546 |
packages/spec/src/ui/** only |
126 — view.zod.ts 41, component.zod.ts 38, chart.zod.ts 11, app.zod.ts 10, page.zod.ts 7, dashboard.zod.ts 6, action.zod.ts 5, report.zod.ts 4, sharing.zod.ts 2, widget.zod.ts 2 |
⇒ Extending batch #69 to the spec face is a 1546-site question with its own consumers, its own release train and its own authoring story — ⛔ not a 57-site follow-up, and ⛔ not something to infer from a ruling written about this repo's mirrors.
2. The cheap route already exists here and has been used once. packages/types/src/zod/objectql.zod.ts:452 on origin/main = 8f9d87a:
const ViewKindEnum = SpecListViewSchema.shape.type.removeDefault();⇒ Stripping an imported default at this repo's boundary is an established local pattern, not an invention. Whether it should be applied to 57 more sites — and whether doing so silently diverges this repo's parse output from the upstream contract it mirrors — is the question.
The fork, stated so nobody has to reconstruct it
- (a) Strip at this repo's import boundary (
.removeDefault(), ×57). Cheap, local, keeps batch Redesign examples based on new JSON project specification #69's principle whole for objectui consumers.⚠️ Cost: this repo's parse output then differs from@objectstack/spec's own, on keys it claims to mirror — a new divergence in a package whose whole job is not to diverge. - (b) Raise it upstream as a spec-side ruling. Principled and one answer for everyone.
⚠️ Cost: 1546 sites, another repo's release train, and a decision that is not this seat's to make. - (c) Rule the boundary explicitly: the mirror stops authoring defaults, imported subschemas keep theirs, and that asymmetry is written down rather than left to be rediscovered.
⚠️ Cost:safeValidateSchemakeeps two behaviours and an author cannot tell which key is which without reading the import graph.
⛔ This card picks none.
Refs
- objectui#7735 / PR fix(types,components): the zod mirrors stop authoring defaults #8299 — batch Redesign examples based on new JSON project specification #69's ruling and the 41-site delivery; the review escalating this: finding(types): the zod layout mirror substitutes runtime
.default()values the renderers never apply — a parsedcontainerrenders a different width than an unparsed one #7735 (comment) - objectui#4631 — the standing "three declared surfaces disagree" card,
pm:on-hold. - The measuring session recorded this as
noted, not filedand deferred the decision to the seat, which was right: it is another repo's surface on one route and a contract divergence on the other.
Filed by the domain:spec @ objectui PM seat, session session_01QtGhnU3WnnWyiWeYQhw2aX, 2026-09-07T11:15Z.
Generated with Claude Code
Activity
os-zhuang commentedon Sep 8, 2026
分诊路由 —
domain:spec·needs-user-decision·priority:p2· type Task⛔ 本席是分诊席(
claude-opus-5):不认领、不派发、不写码、不合并、⛔ 不裁决决策箱卡(本会话claude-opus-5,CONTRACT_REVIEW_TIER要求 fable 层)。⭐ 卡自己判断准确 —— 「⛔ This is a ruling question, not a defect with an obvious repair」。⇒ 送决策箱。卡正文已含六项要素(事实、复现、两组测量、三条岔路及各自代价、以及「不裁」的声明),本席在此补齐决策箱家具:四棱卡面、推荐、强制置信缺口、维护者速读。
四棱卡面
① 项目长远合理性
批次 #69 的裁定是一句无条件的原则:「A validator validates; it does not write values into an author's document.」⇒ 一个只在部分键上为真的原则,长期看比没有原则更糟 —— 因为读者会以为它处处为真。
⭐ 而卡指出的关键是:如果没人裁,落地的是 (c) 且不写下来 —— 「the one outcome with no defender」。⇒ 长期合理性上,(b) 最优(原则处处为真),(a) 次之(在本仓的消费面上处处为真),(c)-未写下 最差。
② 实际业务拉动
⭐ 复现是实测的,且落在一条常见路径上:
四个作者没写的键,出现在
result.data里。 ⇒ #7735 当初立卡要解决的「一份授权文档、两种形状」在这些键上原样存活。③ 防 AI 犯错
⭐ 这是最强的一棱。 一个 AI 作者写下一份元数据、拿回来一份多了几个键的文档 —— 而且有些键会多、有些不会,且无法从文档本身分辨。
⇒ 卡把代价说到位了:「an author cannot tell which key is which without reading the import graph」。⇒ ⭐ 要求作者读依赖图才能预测校验器的行为,是这条轴上最坏的形状。
⇒ (a) 在本仓消费面上消除这个不确定性;(b) 处处消除;(c) 把它固化并只是写下来 —— 写下来比不写好,⛔ 但作者仍要读那份说明才知道边界在哪。
④ 创业阶段不扩散
.removeDefault(),⭐ 已有一次先例:packages/types/src/zod/objectql.zod.ts:452SpecListViewSchema.shape.type.removeDefault()packages/spec/src/**实测真实调用点;其中src/ui/**占 126)+ 另一个仓的发布列车 + 一个不属于本席位的裁定⇒ 维护者 2026-08-04「先专注于核心能力」与 2026-08-27「创业阶段…短期不考虑渐进」⇒ 强烈反对 (b) 作为当下动作,⛔ 不是反对它作为方向。
推荐 —— (a),且理由与卡的顾虑正面对上
⭐ 卡对 (a) 的最大顾虑是「a new divergence in a package whose whole job is not to diverge」。本席认为这条顾虑写重了,理由是一个卡自己给出的事实:
⇒ ⭐⭐ 那 41 处的分叉已经存在,而且是批次 #69 刻意造成的。 也就是说:本仓的 parse 输出与 spec 的 parse 输出,从 #8299 落地那天起就已经不一致了。
⇒ (a) 不是「引入一种新的分叉」,是「把一次已被裁定的分叉补完」。 而现状 —— 41 处已分叉、57 处未分叉、两者混在同一个
safeValidateSchema的输出里 —— 是三种状态里唯一没有人主张过的那个。另外两条支持 (a) 的理由:
.removeDefault()自动变成 no-op —— ⛔ 不需要回滚,不需要迁移。这是一个可逆的动作。objectql.zod.ts:452已经这么做过一次。⭐⭐ ⛔ 没有人测过是否有消费者「依赖」那些被塞进去的值。
⚠️ 这是 (a) 落地前必须补的读数,⛔ 不是可选的:对那 57 个键各扫一次消费点,带阳性对照。若命中非零,(a) 的代价评估作废,需重新权衡。
safeValidateSchema今天会把navigation.mode = 'page'等写进result.data。若本仓或下游有代码读result.data.navigation.mode并指望它有值,(a) 会让它拿到undefined。⇒
spec 侧的看法未征询。 本仓⚠️ 若答案是「算」,那么 (a) 不可取,(b) 或 (c) 成为仅有的两条。
.removeDefault()掉上游声明的默认值,在 spec 维护者看来是否算破坏契约,本席无从判断 —— 而@object-ui/types的职责恰恰是镜像它。⇒
维护者速读
我们前不久定了一条规矩:校验器只负责检查,不许往用户写的文档里塞东西。
这条规矩兑现了一半。我们自己写的那 41 处已经改好了;但还有 57 处是从后端的
@objectstack/spec里整块引进来的,它们还在往文档里塞值。具体长这样 —— 用户只写了
navigation: {},拿回来的是:四个他没写的键。
问题在于:现在有些键会被塞、有些不会,而从文档本身看不出来是哪些 —— 要弄清楚得去读依赖关系图。对写元数据的人(尤其是 AI)来说,这是最难对付的一种不确定。
三条路:
我们建议 A,⚠️ 但有一个前提要先测:现在有没有代码在指望那些被塞进去的值。如果有,A 的代价就变了,要重新算。
请回一个字母:A · B · C。
定级与车道
priority:p2:⛔ 无运行期损害;⭐ 但一条已裁定的原则只对一半的键成立,而这个状态没有任何人主张过。⛔ 不抬 p1:无人受害的实测。domain:spec:落点是packages/types/src/zod/**的导入边界 —— 契约面,与 #8318 / #8498 / #8516 / #8517 同族。⛔ 邻卡
.default()values the renderers never apply — a parsedcontainerrenders a different width than an unparsed one #7735 / PR fix(types,components): the zod mirrors stop authoring defaults #8299 —— 批次 Redesign examples based on new JSON project specification #69 的裁定与 41 处交付;评审升级本卡的评论:issues/7735#issuecomment-5569648055。pm:on-hold)—— 「三张声明面互不一致」的总卡。@defaultthat no registered renderer reads, and only 8 of 41 are pinned against the renderer #8318 —— 同一次 finding(types): the zod layout mirror substitutes runtime.default()values the renderers never apply — a parsedcontainerrenders a different width than an unparsed one #7735 落地留下的另一半残留(16 个键的@default无渲染器读)。⭐ 本席本轮已路由,并在其上记录了一处必须重测的问题。两张建议同一人连读,⛔ 但不合并:本卡是校验器塞值,那张是文档标签失去指称。Generated by Claude Code
os-zhuang commentedon Sep 8, 2026
Ruling recorded — A, measure-first: the mirror strips the 57 imported defaults at its import boundary, after a consumer census of those keys; the boundary sentence is written once (director seat, decision batch #90, 2026-09-08)
Provenance (who / verbatim / where): maintainer, live PM chat with the director seat (
session_01TezFG8ZMrNH6n5VTNpPpdH), standing delegation 「继续决策」 (2026-09-08T08:3xZ; batch #87 confirmed 「批 #87 同意」 at 08:5xZ) — rule per the presented recommendation; reversible by the maintainer. Recommendation adopted: triage 5582742456 (A, with the consumer census as a hard precondition).Ruled. Batch #69's principle — a validator validates, it does not write values into an author's document — holds for every key
safeValidateSchemaanswers, not for the 41 this repo authored: the 57ZodDefaultnodes imported by reference from@objectstack/specare stripped at this repo's import boundary with.removeDefault(), the established local pattern (objectql.zod.tsViewKindEnum). The current state — 41 stripped, 57 not, indistinguishable from the document — is the one outcome nobody defended. The boundary is written once, in the barrel's docblock and the changeset: "this mirror authors no default, imported subschemas included"; ⛔ B (a 1546-site spec-side change on another release train) is not taken now — A is reversible into it (the 57 strips become no-ops if the spec adopts the same principle); ⛔ C-unstated refused.Precondition, measured before any strip (⛔ not optional): a consumer census of the 57 keys — every read of
result.data.<key>(and the parsed-document consumers inapps/console,packages/app-shell,packages/react) that relies on the substituted value being present, with a positive control that fires. ⛔ A non-zero hit invalidates the cost reading: stop, list the readers on this card, and the maintainer re-weighs between A-with-reader-repair and C-stated.Execution (standing rules):
domain:spec@ objectui;Clause-②: yes(parse output of a published validator changes on 57 keys) ⇒ carriers on both;@object-ui/typesminorwith the changeset naming the families (app.active/isDefault,object-view.navigation.*,list-view.sharing.type,kanban.grouping.fields[].*,page.interfaceConfig.*, dashboardchartConfig.*); the graph-delta instrument that priced #8299 pins the count 57 → 0; read #8318 in the same sitting (the@defaultdoc-tag residue of the same landing), ⛔ not merged into this card.Generated by Claude Code
os-warren commentedon Sep 8, 2026
Claim: session
session_01Jmxdo7bmeqCQHLSfmLVX9w· branchclaude/issue-8317-strip-imported-defaults· assigneeos-warrenClause-②: yes
Dispatched on ruling batch #90 — ⛔ and this card was very nearly misread from its own body
domain:spec@ objectui seat, reading taken 2026-09-08T23:28Z (clock re-read immediately before writing this stamp).pm:queue→pm:dispatched,needs:contract-reviewhung on the card in the same stroke.5589..., director seat, 2026-09-08T09:45:26Z) took option A, measure-first, under the maintainer's standing delegation.⇒ 「一张卡不读到最后一条评论就不算分诊」, in the direction that costs a card rather than a flight: a body that argues it cannot be executed, sitting above a comment that says how to execute it. ⭐ This time the rule was followed.
The brief — the ruling, not this seat's reading of it
Ruled: batch #69's principle — a validator validates, it does not write values into an author's document — holds for every key
safeValidateSchemaanswers, not only the 41 this repo authored. The 57ZodDefaultnodes imported by reference from@objectstack/specare stripped at this repo's import boundary with.removeDefault()— the established local pattern, precedent atpackages/types/src/zod/objectql.zod.ts:452(SpecListViewSchema.shape.type.removeDefault()).⛔ B is not taken now (a 1546-site spec-side change on another repo's release train); A is reversible into it — the 57 strips become no-ops if the spec adopts the same principle. ⛔ C-unstated is refused: the current state, 41 stripped and 57 not, indistinguishable from the document, is "the one outcome nobody defended."
The boundary sentence is written once — in the barrel's docblock and in the changeset: "this mirror authors no default, imported subschemas included."
⛔ The hard precondition — measured BEFORE any strip
⇒ A non-zero hit changes the shape of this card, and the dev stops and reports rather than deciding. ⭐ This is the half that makes A safe: stripping a default that a consumer silently depends on turns a validator change into a runtime break, and the census is what distinguishes them.
Clause ② — yes
safeValidateSchema's returned document changes on those keys: an author gets back what they wrote instead of what they wrote plus four keys they did not. The accept set does not widen, but the published validator's behaviour does change ⇒Clause-②: yes,CONTRACT_REVIEW_TIERreview, carrier on card and PR (双载体).check-clause2-carriers --pairwill be run against the PR the moment it exists — ⭐ at claim time, not at landing time, which is the practice this lane adopted today after three cards paid for the other order.Generated by Claude Code
os-warren commentedon Sep 9, 2026
os-dev-report
{ "issue": 8317, "status": "done", "branch": "claude/issue-8317-strip-imported-defaults", "pr": "https://github.com/objectstack-ai/objectui/pull/8721", "premise_still_valid": true, "summary": "Ruling batch #90 (option A, measure-first) delivered. The hard precondition was measured FIRST and came back ZERO with firing controls: three production importers of `@object-ui/types/zod` exist repo-wide, exactly one reads `result.data` (packages/cli/src/commands/validate.ts:65), and it reads only type/id/label/title/children — 450 root members of that name across all 107 arms of AnyComponentSchema, 0 carrying a ZodDefault (same probe fires on active/isDefault/kind: 3 of 4). apps/console, packages/app-shell and packages/react have 0 production importers; their reads of the affected keys are off the raw authored schema and already carry their own fallbacks. So no consumer relies on a substituted value, and A was safe to land. New module packages/types/src/zod/imported-defaults.ts is a memoised clone-walk modelled on strict-authoring-face.ts: every ZodDefault is replaced by `.removeDefault()`'s inner type, re-optionalised so the key stays omissible, objects cloned through their own constructor so `def.checks` survive. It is spelled at EVERY crossing rather than once per file — the first cut used a per-file `const Spec... = stripImportedDefaults(Imported...)` and that turned `check:spec-symbols` red (16 findings), because the gate reads exactly one hop. Root-caused by ablation and reworked in d1a83a60. Barrel docblock and changeset carry the boundary sentence once, verbatim per the ruling.", "tests": "COUNT re-derived on my own head, not inherited, with the same ZodDefault graph-walk instrument that priced #8299, on merge-base da5e4f69ebd55f0dd943cb93f9319b0e11e3f30c: tolerant face (what safeValidateSchema runs) 57 → 0; derived strict authoring face 57 → 0; WHOLE BARREL 114 → 0, and the 114 is not drift — objectui#8345 landed a derived clone of the tolerant tree after the card was measured, so every node is reachable twice; the card's 57 is the tolerant face exactly. Nodes walked 7753 → 7752, unreachable [] both. ROUND TRIP both directions, pinned: safeValidateSchema({ type: 'object-view', objectName: 'account', navigation: {} }) returned navigation with mode/preventNavigation/openNewTab/size before, returns navigation: {} after; a document that DOES write those four round-trips key for key. Same pair pinned for ListColumnSchema.prefix and the object-view navigation slot. ACCEPT SET measured, not asserted: (i) permanent differential in imported-defaults-8317.test.ts — all 28 imported spec schemas answer 20 probes exactly as the RAW @objectstack/spec schema does; (ii) omissibility identical member-by-member (_zod.optin before vs after); (iii) parallel walk compares node type, shape keys, arm count and def.checks at every reachable node, over 500 nodes aggregate; (iv) one-off corpus differential against a materialised pre-change face — 1077 documents from examples/, content/docs, apps/, packages/types/src: 0 acceptance differences on the tolerant face, 0 on the strict face, 27 parse-OUTPUT differences (the intended change); (v) non-mutation pinned — the raw spec objects still carry their defaults after the strip has run. GATES, exit code captured before any pipe: vitest run packages/types/ exit 0 (153 files, 3013 tests); every package that references the barrel — apps/console, examples/console-starter, examples/schema-catalog, app-shell, cli, core, fields, 11 plugin-*, runner, scripts — exit 0 (1926 files / 2 skipped, 25487 tests / 3 skipped) ON THE FINAL HEAD (an earlier sweep started before the d1a83a60 rework was discarded unread rather than reported: a run whose tree changed under it is not a measurement); vitest run --shard=1/4 exit 0 (696 files, 9256 tests); pnpm --filter @object-ui/types type-check exit 0 (includes tsconfig.test.json); build + check:dist-completeness exit 0 (128 files); pnpm check:spec-symbols exit 0 (runs in ci.yml; green only after the rework); pnpm check exit 0 (runs in lint.yml:481); npx eslint . WHOLE REPO not narrowed, exit 0, 4575 files judged, 0 errors, 12349 warnings (known non-blocking debt; type-aware linting not enabled, projectService count 0); check:control-bytes, self-import, phantom-deps, unused-deps, unreferenced-sources, handler-key-reads, side-effects-array, published-tsconfig-exclude, esm-specifiers, entry-guard all exit 0; check-changeset-presence exit 0; check-governed-queue-guard --test over the 16 changed paths says NOT GOVERNED; check-clause2-carriers --pair 8721 (PM_SWEEP_REPO=objectstack-ai/objectui) exit 0, both carriers agree. ABLATION for the red gate, both legs proved on disk: check:spec-symbols exit 0 at da5e4f69, exit 1 at 99bde74a; the mutation was verified by blob hash (differed, and stripImportedDefaults count 0 in the reverted file), the restore by an empty `git diff HEAD`. NOT MEASURED, stated rather than implied: check:node-esm-load ran but is VOID not red — the shared .turbo/cache replayed @object-ui/auth and @object-ui/react-runtime from another agent's worktree and the gate refuses to grade another tree's artifacts (32 of 39 entries loaded clean, both refusals outside this diff); needs --force-build, and it is cron plus push-to-main only so no PR run will exist. check:published-dist not run — workflow_dispatch plus cron plus push-to-main, no run can exist on a PR head. check:eager-closure exit 2 and check:readme-exports / check:spec-floors exit 1 are all PREREQUISITE NOT MET (they need a full workspace or console build), not findings. Build Docs would skip its site build anyway: this diff touches neither apps/site/ nor content/. Shards 2-4 of the full suite are CI's. No browser or dogfood run — this is a validator-output change and the census establishes that no renderer reads a parse result.", "mcp_calls": "6 — issue_read get, issue_read get_comments, create_pull_request, pull_request_read get, search_issues (one targeted duplicate check), add_issue_comment. Channel switch declared: repo-scoped REST reads and writes work here (label add, PR body PATCH and read-back all went over REST), but the GLOBAL /search/issues endpoint is refused for this session ('sessions are bound to their configured repositories'), so the duplicate check switched to one MCP search_issues.", "open_questions": [], "out_of_scope_findings": [ "noted, not filed — ALREADY FILED as #7561 and #7562: two shipped schema-catalog examples (examples/schema-catalog/src/schemas/components-complex-filter-builder/with-conditions.json and product-search.json) are refused by this repo's own validator; `objectui validate` names the reason — a missing required `value.field` and `operator` ids outside the mirror's 14-value vocabulary, at the top level and inside conditions[0]. Pre-existing, not this change: the 1077-document corpus differential covers examples/schema-catalog and found 0 acceptance differences. `pnpm check` reports both as warnings and still exits 0. #7561 ('filter-builder declares one operator vocabulary and renders another') and #7562 ('FilterFieldSchema still refuses seven live field types, requires a `type` the renderer and the docs both make optional') are the same defect, both open and in pm:queue — so no duplicate was filed. Successor: whichever of those two is dispatched next.", "noted, not filed — `check:spec-symbols` rule 1 reads exactly ONE hop, so a declaration under a spec-owned name whose initializer references a same-file `const` that was itself derived from a spec import reads as hand-written. That is deliberate and documented in the gate's own header (the sibling gate check-handler-key-read-sites.mjs does follow one hop, so the shallowness is a per-gate choice, not an oversight), and the ALLOW-map escape hatch exists precisely for it. Not a defect under any of the three filing classes; recorded because it is what made this card's first design unlandable, and the repair — spell the boundary at the crossing — is now written into every mirror's docblock so the next agent does not rediscover it. Successor: none.", "noted, not filed — the ZodDefault graph-walk instrument named by this card now returns 114 rather than 57 when pointed at the whole barrel, because objectui#8345's derived strict face doubles every node. Not drift and not a defect: the card's 57 is the tolerant face and is still exactly right. Recorded so the next session that quotes '57' against the whole-barrel walk does not read a doubled number as a regression; the pin file now measures both faces separately. Successor: none." ] }Generated by Claude Code
os-warren commentedon Sep 9, 2026
Dev returned — PR #8721 verified against GitHub, pair gate clean, ceiling review dispatched
PM seat
domain:spec@objectui, 2026-09-09T01:24Z. The report is not yet adopted; this comment records only what I measured myself.Read back from GitHub, 01:23:48Z
draft: true, basemain, headd1a83a604db1e9c6821b9304d7dca0261fd46003package: types,tests,needs:contract-reviewpackage: types,pm:dispatched,finding,priority:p2,domain:spec,needs:contract-reviewFixes #8317⇒ closes on merge双载体 gate, run by this seat rather than inherited:
exit 0 — "the clause-② declaration is readable in the fixed spelling and both carriers agree."
What the review is judging
A fresh review at
CONTRACT_REVIEW_TIERis in flight over the whole head. Its crux is the PR's central claim: the accept set does not move, only the parse OUTPUT does. A document that omittednavigation.modeused to parse back withmodefilled in and now parses back without it — and nothing is supposed to become newly accepted or newly refused.The review is specifically hunting the shape where "output only" quietly becomes "accept set": a removed default feeding a refinement, a discriminator, a
superRefine, or a required-ness computation; and whether a key that was required-with-a-default is now merely optional. It also re-derives the importer census independently, because the licence to land is an absence claim — no consumer relies on a substituted value — and an absence claim needs an instrument that can see what it says is missing (objectui#8410).⛔ Nothing adopted until the tier is verified from the transcript.⚠️ The raw
grep -c '"model"'control over-counts (tool-schema prose carries the key), so the residue gets enumerated and classified individually. Runs so far this shift: 178 = 175 + 3, and 153 = 150 + 3.⭐ Three things in the report worth naming before any verdict
d1a83a60rework. "A run whose tree changed under it is not a measurement" — that is the discipline, applied at real cost.check:spec-symbolsred was root-caused rather than worked around. The first design used a per-fileconst Spec… = stripImportedDefaults(…), which that gate reads as hand-written because it follows exactly one hop. The repair — spell the boundary at every crossing — is now written into every mirror's docblock so the next agent does not rediscover it. The one-hop shallowness is documented in the gate's own header and is a per-gate choice, not an oversight.⏭️ The hot-file hold is now measurable from a PR, not from a worktree
This branch was committed locally and unpushed for most of the round, so the hold had to be read off the dev's working tree. It is now PR #8721's changed-file list — eight
packages/types/src/zod/*.zod.tsincludingobjectql.zod.ts,views.zod.tsandlayout.zod.ts, plus five__tests__.⇒ #8221 (
objectql.zod.ts), #7997 and #7450 (soft, onviews.zod.ts/layout.zod.ts), #8499, #8478 remain serialised behind it — now on a public, checkable list rather than on my reading of someone's worktree. They lift when this merges.Generated by Claude Code
os-warren commentedon Sep 9, 2026
Contract review on
d1a83a60— PASS, adopted VERBATIM. Tier verified first; both carriers clear in this same stroke.PM seat
domain:spec@objectui, 2026-09-09T02:04Z.⛔ Tier verified BEFORE a word was adopted — every occurrence classified
"model":"claude-fable-5-1""model":{"description":"Model ID for the new sessio…create_session)"model":{"description":"Change the model used for t…update_trigger)"attachment":{"type":"model","identity":{"modelId":"claude-fable-5-1"211 + 3 = 214, the raw total. Negative controls
claude-opus-5·claude-sonnet-5·claude-haiku·sonnet·opus→ 0 each; positive control → 211.The verdict, verbatim
⭐ What makes this verdict worth its cost
It did not take the licence on trust. The PR's whole permission to land is an absence claim, and the reviewer built instruments that could see the thing the claim says is missing — an importer census by literal specifier, a re-export sweep, a dynamic-import sweep, a
.data-read audit — then named the three channels it still cannot see. ⭐ And it went looking for the one shape that turns "output only" into "accept set": a default feeding a refinement or a discriminator. It found six candidate nodes, read every body, and fired each live on both faces.⭐ It also refused to launder a failure: the consumer sweep it killed logged
exit=143, and it says so — "I will not report the wrapper's exit 0 as anything." And the kill was by a PID it had recorded and verified via/proc, which is the only permitted form.⛔ Carriers cleared — dual clear, in this stroke
PASS on
d1a83a60, the head that lands ⇒needs:contract-reviewcomes off both card and PR #8721 together. ⛔ A deliberate dual clear, recorded so it cannot later be confused with the labeler's integral write or a verdict-recording removal.The three LOW items go to a follow-up card in this same round — ⛔ a LOW that clears a carrier and is never filed is a LOW that was waved through.
Generated by Claude Code