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

vercelBot commented Aug 5, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 5, 2026 2:20pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

@os-zhuangos-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
@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 queueAug 5, 2026
Merged via the queue into main with commit 4d57387Aug 5, 2026
29 of 30 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-4966-timerelative-fixture branch August 5, 2026 15:35
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 6, 2026
* docs(pm-dispatch): 探活入例行、报告丢失直接验收、座位行自查等五处协议更新
2026-08-05 实测教训回写(维护者指示):
1. step 6 新增「探活」固定动作:完成通知在宿主进程重启下会静默丢失——
三个在飞 dev 死亡数小时无任何信号;每轮巡检对无产出的 dev 发状态
询问,「no active task; resumed from transcript」回包即当场复活。
2. step 6 新增「报告丢失直接验收」兜底:PR 全绿 + agent 确认死亡/静默
≥2h + 报告未达 ⇒ 直接按 PR 对照 origin/main 验收(objectstack-ai#5550/objectstack-ai#5556 先例)。
3. 座位表协议新增「每轮核对自己的正文行」:协议升级迁移状态可把在任 PM
记成待认领,叠加惰性回收条款即误回收风险(spec 座位当日实例)。
4. worktree 接手协议前置 SendMessage 复活路径(更便宜、保全上下文),
接手协议降为 resume 不可用时的后备。
5. step 7 新增两条评审判据:验收判据本身可被 dev 用测量证伪(objectstack-ai#5452 字面
grep 归零判据不成立,改钉配平不变量);tests/docs-only PR 走
skip-changeset 标签且 rerun 无效需新 labeled 事件(objectstack-ai#5497/objectstack-ai#5502)。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D
* docs(pm-dispatch): skip-changeset 条目随 objectstack-ai#5625 更新 —— 闸门已实时读标签,教训改记为「载荷 vs 现状」通病标本
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018fxLGQdatPbBUvCgiVxg6D
---------
Co-authored-by: Claude <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gatetests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants

@os-zhuang@claude