refactor(spec,lint): one declaration of where ADR-0031 regions live (#4401) - #4408
Merged
Conversation
…4401) A region is a sub-graph inside `FlowNodeSchema.config`, an open `z.record`. Nothing in the type system says which key on which node type holds one, so every pass that needs to reach a region node has to be told — and within one week three of them were told separately, by two changes that were each correct on their own (#4381 and #4388): mapFlowNodes (ADR-0087 conversions) spec FLOW_REGION_SLOTS validateControlFlow / normalizeControlFlowRegions / collectFlowGraphs spec regionSlotsOf walkFlowNodes (lint flow rules) lint REGION_SLOTS Each pinned its own copy with its own reconciliation test. So every copy was protected from drifting away from the schemas, and nothing would have failed if the copies drifted from each other — while adding a fourth construct meant editing three places, and missing one reproduces exactly the silent blind spot #4347 and #4380 were both filed about. - New `@objectstack/spec/automation` export `FLOW_REGION_SLOTS` (+ the `FLOW_REGION_SLOTS_BY_TYPE` / `FLOW_REGION_CONFIG_KEYS` views) is the only statement of the fact. Import-free module, so `spec/conversions/walk.ts` can read it and stay the pure shape walker it was written as; mapping a slot onto the Zod schema its value parses as stays in `control-flow.zod.ts`. - The three reconciliation tests collapse into `region-slots.test.ts`, keeping the strongest of them (#4388's): it derives each construct's region keys BEHAVIOURALLY, by asking the config schema what it accepts in a region shape, rather than reading names off `.shape`. It also probes every other exported `*ConfigSchema`, so a new region-bearing construct cannot be added without either declaring its slots or failing here. The three walks are deliberately left separate: different inputs (parsed vs raw authored records), different units (a graph, a node, a copy-on-write rewritten tree), and the lint one formats human diagnostic trails from node labels — consumer logic, not protocol (Prime Directive #2). No behaviour change: every existing test passes unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HSBbKMdDgHjGpQdrvQUQXj
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 2 package(s): 107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
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.
Closes #4401。接 #4381 / #4388 / #4399。
问题
region 是住在
FlowNodeSchema.config(开放z.record)里的子图。类型系统里没有任何东西说明「哪个节点类型的哪个 key 装着 region」,所以每一个需要够到 region 节点的 pass 都得被单独告知 —— 而在一周之内,有三个各自被告知了,来自两个各自都正确的改动:mapFlowNodes(ADR-0087 转换)specFLOW_REGION_SLOTSvalidateControlFlow/normalizeControlFlowRegions/collectFlowGraphsspecregionSlotsOfwalkFlowNodes(lint flow 规则)lintREGION_SLOTS三份各自都配了 reconciliation 测试钉住自己。于是每一份都被保护着不偏离 schema,却没有任何测试会因为三份互相偏离而失败 —— 同时加第四种构造要改三个地方,漏掉一个的失败模式恰好就是 #4347 和 #4380 各自报告的那种静默盲区。
我修正了 #4401 里自己提的方向
issue 里我提议「把
flow-walk.ts的能力搬进 spec,统一成一套 walker」。仔细比对三者之后这是错的:collectFlowGraphswalkFlowNodesmapFlowNodesFlowNodeParsed{scope, nodes, edges}{node, path, localConfig, regionTrail, depth}这是三件不同的事。而且
walkFlowNodes从节点 label 生成人读诊断面包屑 —— 那是消费方逻辑,不是协议,搬进 spec 违反 Prime Directive #2。把一张四行的表的重复,换成一个谁都服务不好的 walker,不划算。所以收敛范围窄得多:只统一那份数据,三个遍历各自保留。 这恰好也是 issue 陈述的风险(「加第四种构造要改三个地方」)的完整解法。
改动
@objectstack/spec/automation导出FLOW_REGION_SLOTS(以及FLOW_REGION_SLOTS_BY_TYPE/FLOW_REGION_CONFIG_KEYS两个派生视图),成为这件事的唯一陈述。spec/conversions/walk.ts能读它、同时保持它被写成的样子(纯形状 walker,不引 schema)。把槽位映射到「它的值该用哪个 Zod schema 解析」这半留在control-flow.zod.ts—— 那是 schema 的事。REGION_SLOTS/REGION_CONFIG_KEYS改为从 spec 投影。region-slots.test.ts,保留其中最强的那个做法(fix(lint): flow 规则能看进 try_catch / loop / parallel 嵌套 region(#4380) #4388 的):它行为性地推导每个构造的 region 键 —— 拿 region 形状的值去探测 config schema 看哪些键存活,而不是读.shape的键名。补上一个原本会留下的洞
合并后的测试仍要手写一份「哪些构造带 region」的映射,新增第四种时作者可能两处都忘。所以加了一条:遍历本模块导出的每一个
*ConfigSchema去探测,任何未申报却接受 region 形状的 schema 直接失败。已验证 ratchet 真的咬得住 —— 临时从表里删掉
try_catch.catch:无行为变更
这是纯重构,最强的安全性质就是所有既有测试原封不动通过 —— 这正是本次验证的重点:
pnpm buildpnpm testpnpm typecheckpnpm lintcheck:generatedapi-surfaceGenerated by Claude Code