feat(lint): never-fire 族三条规则 warning → error,注册表 tier 同步 gating (#5762) - #5952
Merged
Conversation
`validateFlowTriggerReadiness` 的规则此前一律 warning。族内复审后按**一个**标准 重新定级 —— 仅凭这份 stack 是否已足够断定 flow 不会运行 —— 三条答「是」升 error: - `flow-time-relative-descriptor-invalid`(#5496):判它的正是 trigger 在 bind 时 `safeParse` 的同一个 `TimeRelativeTriggerSchema`,schema 拒的描述符运行时同样被 拒,sweep 永不安装。 - `flow-time-relative-descriptor-unroutable`(#5647):判据就是引擎自己的路由谓词 `typeof … === 'object'`,任何部署都不会把它路由到 time-relative trigger —— 全族最硬的判定,也是唯一连 bind 时那行 warn 都没有的一条。 - `flow-trigger-unknown-event`(#3427/#3457/#3481):引擎用**硬编码前缀**把任何 `record-` token 路由给 record-change trigger(不查注册表,故装任何包都无法声明 新 `record-*` token),该 trigger 再用 `triggerTypeToHookEvents` 的**封闭文法** 映射 —— 文法外即零 hook event。与 unknown-object 不同,没有跨包免责的读法。 两条对照维持 warning,这正是本次不是「整个文件都升 error」的原因: `flow-trigger-unknown-object` 的跨包免责是真的(对象可能由另一个已安装包提供, 规则看不见);`flow-draft-status-ambiguous` 的 draft flow **确实会**触发,是意图 歧义而非死 flow。两者作为反例钉在新增的 severity map 测试里。 注册表 tier `advisory` → `gating`。tier 声明并非自述:`authoring-rule-wiring.test.ts` 会读规则源码核对,故留 advisory 必红 —— 已反向验证。 P1 运行时发布门边界经实测,与直觉相反且对租户有利:门交给规则的快照里 `flows` 只装 正在写入的那一个 item,且减掉 baseline 的 finding,`RuntimeStackContext` 也只有 `objects` 一个通道 —— 已存储的 flow 进不了快照。实测:发布死 flow 自身 errors=1 (拒收),已存死 flow 时发布好 flow errors=0(不受连带)。被拒的是死 flow 自己的 发布,以及 CLI 侧含死 flow 的包。 仓内 showcase / CRM / todo 三个示例 `os validate` 升级前后输出逐字一致、均通过, 无需修改任何示例。 Claude-Session: https://claude.ai/code/session_01GX3sL71LFq8m2usg6VqTSE Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 3 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
August 6, 2026 13:04
This was referenced Aug 6, 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.
Closes #5762
按维护者 2026-08-06 批复(三问全批)执行。族内同批定级,单独 PR,不逐条搭车。
定级标准:一个问题,不是「后果有多严重」
本文件的规则此前一律 warning。复审后按一个标准重新定级 —— 仅凭这份 stack,是否已经足够断定这个 flow 不会运行。按后果排序是没用的:这里每条规则描述的都是「不运行的 flow」。
升 error(三条)
flow-time-relative-descriptor-invalid(#5496)TimeRelativeTriggerSchemasafeParse的同一个 schema —— schema 拒的描述符运行时同样被拒,sweep 永不安装flow-time-relative-descriptor-unroutable(#5647)typeof … === 'object'flow-trigger-unknown-event(#3427/#3457/#3481)triggerTypeToHookEvents的封闭文法三条的共同点:判据都是本仓库内发布的契约,装任何包都改不了答案。
维持 warning(两条,反例)
这两条正是本次不是「整个文件都升 error」的原因,已作为反例钉进新增测试:
flow-trigger-unknown-object—— 本 stack 没定义的对象名可能由另一个已安装包提供,而规则看不见那个包的对象。免责是真的,规则自己的 hint 就这么写(新增测试断言:error 级 finding 的 hint 里不得出现 "another installed package" / "can be ignored",warning 级必须出现)。⛔ 按裁定未动。flow-draft-status-ambiguous—— draft flow 确实会触发(只有obsolete/invalid停用),这是意图歧义而非死 flow。升它会拒收一个所有 flow 都正常的 stack。随批复审:
flow-trigger-unknown-event实测为同性质,故同 PR 升裁定要求实测其性质:若同为「确定性死 flow、无跨包免责」则同 PR 升;若有真 advisory 理由则维持并写进报告。实测结论:同性质,无跨包免责。 证据:
engine.ts:1532用硬编码前缀判定triggerType.startsWith('record-')即路由给record_change—— 不查注册表。所以装任何包都无法声明一个新的record-*token。record-change-trigger.ts:115的/^record-(before|after)-(create|insert|update|delete|write)$/与本规则的VALID_RECORD_TRIGGER逐字对应;不匹配即hookEvents.length === 0,绑不上任何 hook。record_changetrigger 只有packages/triggers/trigger-record-change一个实现,没有可替换的竞争实现给出「另一种读法」。对照
flow-trigger-unknown-object:对象名的跨包免责是结构性的(对象由包提供、规则看不见),而 token 文法的免责不存在(文法不由安装决定)。两者不同性质,故一升一留。影响面:P1 运行时发布门 —— 实测边界与立单时的假设不同
注册表 tier
advisory→gating(两条 surface 本就是CLI_AND_RUNTIME)。error会让state: 'active'的元数据写入被拒。validateFlowTriggerReadiness(stack)确实收整个 stack,但运行时门从不把租户的整份 flow 集合交给它:runtime-gate.ts构造的候选快照是candidate = { objects, [stackKey]: [item] }—— flow 写入时flows数组里只有正在写入的那一个 item;RuntimeStackContext只有objects一个通道 —— 已存储的 flow 在结构上进不了快照。实测(直接调
runRuntimeAuthoringRules):真实影响面:
os validate/os build/os lint判整份 stack,所以包里含一个死 flow 会让整包转红 —— 修无关 flow 的作者也会看到它。这是本次强制修复的主要着力点。zod history 句噪音(裁定第三问):已实测,本 PR 不折叠,理由如下
裁定允许「若碍事则同 PR 折叠展示」。实测噪音是真的:
timeRelative描述符跑不通时 authoring 期零诊断 —— 两条 flow lint 一条只看非空、一条只看对象名(#4966 建议 2) #5496 那条 finding 全长 709 字符,Did you mean \field` → `dateField`?` 落在最末;console.log(" • " + where + ": " + message)打单行,error 块里作者必须读完才能动手。但折叠机制在本 PR 的文件面内都不干净,故未做:
Unrecognized key(s) on …: ….+ history 句 +Did you mean …?)= 把生产者的消息结构复制进消费者 —— 恰是本文件模块注释声明要避免的漂移,也违反 contract-first;Did you mean切掉(它在末尾),使情况更糟;strictObjectDeclarations()/directAliasTables()都刻意不进 barrel(内部接缝),消费者拿不到 history 原文;strictObject把 history 排到末尾或单独暴露),但那涉及 62 个strictObject站点 / 293 处 history 声明与跨包钉死测试,是 spec 的授权错误契约对外变更,应单独立单、单独拍板。已另立 finding 单记录测量与建议。不在本 PR 内以「消费者容忍」的方式绕过。
反向验证(方向先预判,两个不同机制,均如预判转红)
advisory(严重度保持 error)→ 预判authoring-rule-wiring.test.ts的 "every advisory rule really is advisory" 转红(它读规则源码核对声明)。实测:× every advisory rule really is advisory/A rule that can emit `error` is `gating` and must run on all three commands.…-unroutable降回 warning → 预判新增 severity map 测试转红。实测 2 条红:× flow-time-relative-descriptor-unroutable is error与 标量config.timeRelative(如timeRelative: 'daily')= 引擎解析不出任何 trigger,flow 永不触发且全层零输出 #5647 原有那条。仓内自查:三个示例 app 的
os validateA/B裁定要求「若任何 in-repo example/fixture 因此转红,在同 PR 内修掉」。实测无一转红,无需修改。
原因可从 diff 直接读出:规则文件的非注释改动只有
severity:字面量,判据一行未动,所以任何 stack 的 finding 集合与main逐字节相同,只有四处 severity 字段变了。三个示例本就不产出这三条 id 的 finding(showcase 唯一的timeRelative描述符是已被测试钉为 spec-valid 的 Task Due Reminder;全部record-*token 都在文法内)。app-todo 的两条flow-draft-status-ambiguous仍是 warning —— 正是维持 advisory 的那条在真实示例上的体现。record-created(全仓 12 处、文法外)全部位于其他规则的测试 fixture 里,那些测试直接调用各自的规则、不经过本规则,故不受影响(已逐一核对;validateFlowTriggerReadiness不在 reference-integrity suite 成员内)。文档
content/**无任何 lint 规则 severity 表引用这一族 id(已实测搜索),packages/lint/src/docs不存在,故无同步项。⛔ 未动content/docs/releases/**。发布
真 changeset:
@objectstack/lintminor(已发布规则严格度的对外行为变更),正文含升级原因、P1 实测边界与迁移指引(修死 flow,而非降级规则)。未用skip-changeset。Generated by Claude Code