Repository navigation
zodShapeOf 的 unwrap 面比三个同族 walker 窄:缺 union 分支与 prefault(#5317 修完管道方向后剩下的两格) #6098
Description
Activity
Findings triage — first grading (this issue had never been graded). Verdict: HOLD as
finding, +domain:spec-tooling, with a mechanically checkable restart condition.Why hold, not promote. The observation-class argument was checked, not accepted on trust, and it holds: both gaps land in
computeSurfaceReachability'sreachableVia, which returns'root-graph'when no shape resolves — fail-closed, so the current behaviour is conservative (an extra tombstone), never a wrong answer; and that path only runs when #4650's deletion gate sees a baseline row removed, not during ordinarygen:schema. Nothing a user reaches today, which is exactly thefindingcriterion rather than the queue's.And a hard serial reason on top of the grading. PR #6102 (draft, for #5317) is rewriting
packages/spec/scripts/lib/zod-graph.tsandzod-graph.test.tsright now — the same function and the same test file this issue is about. Queuing it today would put a second agent inside a file already in flight, on work whose own body describes it as "what remains after #5317". Even on the promote reading, the correct state would bepm:queue+pm:blocked; holding is the more honest of the two, since the observation grade stands on its own.Restart condition (checkable, no memory required): PR #6102 merges (or #5317 closes) ⇒ re-grade this issue in the next findings round, with promotion as the expected outcome. At that point the file is free, the pipeline-direction cell is settled, and the body already carries the measurement protocol a dispatch would need (dump every def's
reachableViabefore and after, explain each moved verdict, rungen:schema+check:generated, and account for any entry that becomesderived-clone— the #5056 bridge hazard). Both cells go in one PR, as the body argues, so the measurement runs once.Also noted for the re-grade:
packages/spec/scripts/zod-graph.test.tscurrently pins the honest present state ("documents that view's preprocess OUT is a union, so it still derives no shape"). Whoever adds the union branch must change that test — a green test deleted without comment would erase the record of why the gap existed.Routing.
domain:spec-tooling, matching its parent #5317 (same file family,packages/spec/scripts/**) so the pipeline-direction fix and its leftovers stay in one lane.⚠️ Recording a taxonomy gap for the maintainer rather than papering over it:domain:spec-toolingis in active use on six open issues but does not appear in the SKILL's domain table, whosedomain:specrow readspackages/spec及其生成物. Existing usage was followed here for lane consistency; the table should either gain the row or the label should be retired, by PR — a label the table cannot explain will eventually be applied two different ways.Dedup. Full paginated enumeration of open issues and PRs across the three repos (search API unavailable to repository-scoped sessions). #5317 / PR #6102 is the parent, not a duplicate — it fixed the pipeline direction only, and this issue is explicitly the residue. No other open record covers
zodShapeOf's unwrap surface.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
Findings triage (objectstack#4949 discipline) — restart condition FIRED ⇒ PROMOTE to
pm:queue. Domain unchanged:domain:spec-tooling.1. The trigger, verbatim from the previous verdict
Restart condition (checkable, no memory required): PR #6102 merges (or #5317 closes) ⇒ re-grade this issue in the next findings round, with promotion as the expected outcome.
Both halves fired, ~1.6 h apart from this round: PR #6102 merged
2026-08-07T03:37:02Z, #5317 closed2026-08-07T03:37:03Z.2. Stale-premise check (
origin/main@ede5a8e) — both gaps still standunionbranch, absent.zodShapeOf(packages/spec/scripts/lib/zod-graph.ts:165) branches on exactly four shapes —object,pipe(viapipeAuthorableSide, which is what fix(spec): zodShapeOf 按管道方向解 z.preprocess 的授权面(#4488 盲点的第四个现场) #6102 rewrote),lazy, andSHAPE_WRAPPER_TYPES— then falls through toreturn null. No union arm.prefault, absent.SHAPE_WRAPPER_TYPES(:87) is exactlyoptional, nullable, default, catch, readonly, nonoptional. The sibling walker atpackages/spec/scripts/liveness/check-liveness.mts:225does unwrapprefault, so the asymmetry the body reports is live.
3. The merged code names this issue as the owner of the remainder
zod-graph.ts:83-86, landed by #6102:prefault— which the three sibling walkers below do unwrap — is knowingly absent; adding it is a separate, separately measured change (filed as its own finding, not smuggled in here).The deferral was deliberate and this is the designated follow-up, which removes the "is this in scope for someone else?" question a dispatch would otherwise have to answer first.
4. The hard serial reason for the previous hold is gone
The last verdict held partly because #6102 was rewriting
zod-graph.tsandzod-graph.test.tsin flight. Enumerated all 5 open PRs in this repo this round (#6171, #6165, #6164, #6160, #4935) — none touchespackages/spec/scripts/**. The file is free.5. Not a release blocker
⛔ No
target:<major>:computeSurfaceReachability'sreachableViareturns'root-graph'when no shape resolves, so the behaviour is fail-closed (one extra tombstone, never a wrong answer), and the path only runs under #4650's deletion gate — not a shipped-surface defect on the binary criterion.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
认领:PM 循环第 5 轮(spec-tooling 车道,座位登记 #6018;分诊座位已核 restart condition 双半触发并晋级)
会话:session_014wsZeReNTqiceBfLb5Pyf5
分支:claude/issue-6098-zod-graph-unwrap-parity
Worktree:objectstack-issue-6098
域:domain:spec-tooling
文件面:packages/spec/scripts/lib/zod-graph.ts(union 分支 +prefault两格)+packages/spec/scripts/zod-graph.test.ts(含把view现状钉死的那条测试——补 union 后它必然要改,这是预期的,改动要在 PR 里逐条说明)+ 分片生成物(若可达性判定移动)+.changeset/*.md。⛔ 不碰build-schemas.ts(#5371 本轮并行)、lib/file-description.ts、lib/format-type.ts、zod 文件。(越界即停,报告说明)⚠️ #5056 测量轮是本单的核心要求:两格任一补上都会让更多节点解出 shape、给bridged表新增桥项。派发令要求逐条解释每个新增桥项,并特别标注任何root-graph → derived-clone的迁移(那正是 #5056 警告的假可达面)。#5317 的 dev 特意没有顺手加这两格,就是因为它需要独立的测量轮——本单就是那一轮。
Generated by Claude Code
发现于 #5317 实施途中(observation-class:两格今天都被 fail-closed 兜住,没有已知的错误产物)。
packages/spec/scripts/lib/zod-graph.ts的zodShapeOf是 #5317 从build-schemas.ts抽出来的;它有三个同族 walker,做的是同一件事(把一个 Zod 节点解到对象 shape):packages/spec/scripts/liveness/check-liveness.mts的unwrap/shapeOfpackages/spec/src/kernel/metadata-authoring-lint.ts的unwrap/keyPosturepackages/spec/src/system/metadata-form-zod-reconciliation.test.ts的unwrap/keysOf#5317 已经把管道方向这一格对齐了(
z.preprocess走 OUT)。剩下两格仍然只有zodShapeOf缺:事实 1 —— 没有 union 分支
zodShapeOf只认object,遇到union返回null。三个同族 walker 都合并 union 成员的键(check-liveness 的shapeOf带着 #3095 的注释,说的正是「一个 metadata type 可能注册一组 shape 的 UNION 而不是单个对象」)。后果,#5317 实测:
view是注册表里唯一的z.preprocess根,其 OUT 是z.union(#5074 的 console-decoration 剥离)。#5317 把方向修对之后,pipeAuthorableSide(view)从transform变成了union—— 但zodShapeOf(view)依然是null,因为没有 union 分支。所以「preprocess 根能被解出真实 shape」这句话对view今天仍然不成立,方向修对是必要但不充分。packages/spec/scripts/zod-graph.test.ts里有一条测试把这个状态诚实钉住了(documents that view's preprocess OUT is a union, so it still derives no shape),加 union 分支的人会先撞到它。事实 2 —— wrapper 集合缺
prefaultzodShapeOf的SHAPE_WRAPPER_TYPES是optional / nullable / default / catch / readonly / nonoptional。三个同族 walker 的对应列表都还有prefault。一个prefault包着的对象在zodShapeOf下解不出 shape。#5317 特意没有顺手加:它没被测量过,而
zodShapeOf喂的是 derived-clone 桥,任何解出更多 shape 的改动都会新增桥项。为什么是 observation-class 而不是缺陷
两格的后果都落在
computeSurfaceReachability的reachableVia,而那里解不出 shape 时return 'root-graph'(fail closed —— 宁可多要一个 tombstone,不静默放宽)。所以当前表现是保守,不是错答;而且reachableVia只在 #4650 删除门禁发现「基线行被删」时才被调用,平时的gen:schema根本不走。今天没有用户能撞到。修的时候注意(#5056)
这两格任何一格补上,都会让更多节点解出 shape,从而给
bridged表新增 (propName → propSchemaInstance) 项 —— 这正是 #5056 提醒过的、会把死形状标成可达(null→derived-clone)的桥。所以不是加一行就完事:pnpm --filter @objectstack/spec gen:schema+check:generated;derived-clone的条目;build-schemas.ts的zodShapeOf对z.preprocess走错管道方向(#4488 已在 check-liveness 修过的同一个盲点) #5317 的做法:在computeSurfaceReachability末尾临时 dump 全部 def 的reachableVia判定,改前改后各跑一次做 diff。build-schemas.ts的zodShapeOf对z.preprocess走错管道方向(#4488 已在 check-liveness 修过的同一个盲点) #5317 用这个办法量到「管道方向修正只移动 1 条判定、0 条新桥」,分片生成物零 diff。两格建议放同一个 PR 一起量:它们改的是同一个函数的同一片 unwrap 面,分开做要把上面那套测量跑两遍。