Skip to content

test(lint): time-relative fixture 改成运行时真能绑的描述符,并对 bind 期同一个 schema 钉住 (#4966) - #5497

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-4966-timerelative-fixture
Aug 5, 2026
Merged

test(lint): time-relative fixture 改成运行时真能绑的描述符,并对 bind 期同一个 schema 钉住 (#4966)#5497
os-zhuang merged 1 commit into
mainfrom
claude/issue-4966-timerelative-fixture

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes #4966

前提复核(对 origin/main 实测,成立)

packages/lint/src/lint-flow-patterns.test.ts 的用户缺失触发器用例里,fixture 写的是 { object: 'task', field: 'due_at', offsetDays: -1 }。对 TimeRelativeTriggerSchema(packages/spec/src/automation/time-relative-trigger.zod.ts)实测 safeParse,issue 说的两处全中,外加一条根级 unknown-key:

--- CURRENT fixture: {"object":"task","field":"due_at","offsetDays":-1}
    success = false
    issue [dateField]: Invalid input: expected string, received undefined
    issue [offsetDays]: Invalid input: expected array, received number
    issue [(root)]: Unrecognized key(s) on this flow start node's `config.timeRelative`
                    descriptor: `field`. … Did you mean `field` → `dateField`?
--- PROPOSED fixture: {"object":"task","dateField":"due_at","offsetDays":[-1]}
    success = true
    parsed  = {"object":"task","dateField":"due_at","offsetDays":[-1]}

补一处 issue 正文没展开、但值得写下来的细节:field 现在确实出现在该 schema 的 aliases 表里。但 strictObjectaliases 只在 unrecognized_keys 诊断路径上被查阅(packages/spec/src/shared/strict-object.ts 的注释写明了这一点),它是「你大概想写 dateField」的提示表,不是可接受的键 —— 所以 issue 的结论(「不是别名、不是转换项」)在语义上成立:这个键被拒绝,只是拒绝得比较友好。

TimeRelativeTriggerPlugin.start()(packages/triggers/trigger-schedule/src/time-relative-trigger.ts:164)对 binding.config.timeRelativesafeParse,失败即 warn 并 return —— 不绑定。所以这个 fixture 描述的,正是 issue 说的那种 flow:lint 认定它是 time-relative 触发,运行时永远不会为它装上 sweep。

改动

只动 packages/lint/src/lint-flow-patterns.test.ts 一个文件:

  1. fixture 改成可绑形状 { object: 'task', dateField: 'due_at', offsetDays: [-1] };
  2. 原地注释说明为什么它必须可绑(照 未知键静默剥离仍是全仓默认:把 #3405 的 strict 收紧从一个 schema 推广到整个可授权面(ADR-0078 完整性闸门) #4001 惯例);
  3. 把 fixture 提成常量,并对 bind 路径用的同一个 schema 断言 safeParse(...).success === true

第 3 点略微超出「fixture 一处 + 注释」,理由见下面的反向验证:没有它,这个 fixture 明天可以再次腐化成不可绑形状,而全仓无人报警 —— 这正是它第一次腐化的原因。

反向验证(方向先判、再跑)

预判:把 fixture 退回旧拼写后,lint 的两条断言不会变红(规则只看 startCfg.timeRelative != null),只有新增的 schema pin 会变红。两半实测都对上。

半一 —— 旧 fixture + 保留 pin:

FAIL src/lint-flow-patterns.test.ts > flags the OTHER provably user-less triggers too — time-relative and api
AssertionError: expected false to be true // Object.is equality
❯ src/lint-flow-patterns.test.ts:356:81

半二 —— 旧 fixture + 注释掉 pin:

Test Files  1 passed (1)
     Tests  1 passed | 67 skipped (68)

这就是 issue「为什么绿」那一节的实证:单改 fixture 不翻转任何一个测试的颜色,本 PR 的可检测性完全来自 pin。按报告纪律如实写在这里,而不是把它包装成「修复后由红转绿」。

按「键 vs 值」判据,这里钉的是一个的裁决(描述符能不能绑),所以要求 full safeParse 绿,而不是只断言没有 unrecognized_keys —— 后者会漏掉 offsetDays: -1 这一半。

验证

pnpm --workspace-concurrency=2 --filter @objectstack/lint test
  Test Files  58 passed (58)
       Tests  1225 passed | 4 skipped (1229)

pnpm --workspace-concurrency=2 --filter @objectstack/lint typecheck
  tsc --noEmit   (无输出)

node scripts/check-nul-bytes.mjs
  OK (scanned 5460 tracked text file(s); … no raw C0 control bytes)

消费半径普查:全仓 grep timeRelative / dateField / offsetDays,除本 fixture 外的描述符 —— packages/services/service-automation/src/engine.test.tspackages/lint/src/validate-flow-trigger-readiness.test.tspackages/triggers/trigger-schedule 自测、examples/app-showcase/src/automation/flows/index.tscontent/docs/** —— 全部已是 canonical 拼写,没有第二处需要一起改。

范围


Generated by Claude Code

… can bind (#4966)

`lint-flow-patterns.test.ts`'s user-less-trigger case described a flow the
runtime would never sweep: `{ object: 'task', field: 'due_at', offsetDays: -1 }`
fails `TimeRelativeTriggerSchema` on both counts — `field` is a diagnostic
alias, not an accepted key (`dateField` is declared), and `offsetDays` is
`z.array(z.number().int()).min(1)`, not a scalar. `TimeRelativeTriggerPlugin
.start()` `safeParse`s that descriptor at bind time, so the fixture's flow
would warn and stay unbound.

The fixture stayed green because the rule decides `time-relative` from
`startCfg.timeRelative != null` alone and never reads the shape, so nothing
in the suite could see it. Correct the spelling to the bindable shape, keep
an in-place comment explaining why it must stay bindable, and pin it against
the same schema the bind path parses so it cannot rot back.

No production behaviour changes — test-only.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D
@vercel

vercel Bot commented Aug 5, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 5, 2026 2:20pm

Request Review

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

No hand-written docs reference the 0 changed package(s). ✅

@os-zhuang os-zhuang added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed size/s labels Aug 5, 2026 — with Claude
@github-actions github-actions Bot added the size/s label Aug 5, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review August 5, 2026 15:23
@os-zhuang
os-zhuang added this pull request to the merge queue Aug 5, 2026
Merged via the queue into main with commit 4d57387 Aug 5, 2026
29 of 30 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-4966-timerelative-fixture branch August 5, 2026 15:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

lint-flow-patterns.test.ts 的 fixture 教了一个永远绑不上的 timeRelative 描述符(#4001 第一类发现的第八例)

2 participants