Skip to content

fix(app-shell): clearing the flow designer's cron no longer leaves a schedule block with no cadence - #31412

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-xmmugm
Nov 8, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-xmmugm

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Leehom · project thread

Fixes #31383

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:流程设计器开始节点的「Cron schedule」输入框被清空时,如果剩下的 config.schedule 已经没有任何周期(只剩 type、timezone 之类),现在整块删掉,不再写出 { type: 'cron' } 或 { type: 'cron', timezone }。无类型的 { expression, timezone } 清空后原来会剩 { timezone },同一个缺陷,一并处理。输入新的 cron 时行为不变:带类型的块照旧保留 type 和 timezone。
  • 为什么改:留下的无周期块引擎读不出调度,定时流程静默不绑定,时间相对扫描也不会按帮助文字说的「留空默认每天」处理;PR fix(spec)!: refuse a start-node filter beside timeRelative and a schedule the readers cannot use #31376 落地后,同样的草稿保存会被 FlowSchema 以 422 拒绝,而这个形状正是设计器自己写出来的。
  • 风险与回滚:只改 @object-ui/app-shell 里 scheduleWithExpression 一个函数的「清空」分支;不改 spec、不改服务端、不加 i18n 文案。回滚即 revert。
  • 先看:ui/packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.ts 中 scheduleWithExpression 的清空分支(return normalizeScheduleCadence(next) === null ? undefined : next;),以及两个测试文件末尾新增的 objectstack#31383 describe。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.scheduleCadence-12779.test.ts packages/app-shell/src/views/metadata-admin/inspectors/FlowNodeInspector.scheduleCadence-12779.test.tsx,预期 2 个文件 68 个用例全绿。
  • 需要你做的:无,席位落地。

What changes

  • scheduleWithExpression (flow-node-config.ts, @object-ui/app-shell). The clearing arm used to delete expression and cron and keep whatever else the object held, collapsing only an object left empty. It now returns undefined whenever the remaining object resolves to no cadence through normalizeScheduleCadence, the designer's branch-for-branch mirror of the engine's normalizeSchedule. So:
    • a cleared { type: 'cron', expression } or { type: 'cron', expression, timezone } stores no config.schedule;
    • a cleared untyped { expression, timezone } stores no config.schedule either (it used to leave { timezone });
    • an untyped { expression } stores nothing, as before;
    • a key the cron stood in front of and that still binds is kept: clearing { expression, intervalMs } leaves { intervalMs }, which the engine binds as an interval, as before.
      The set arm is unchanged. Its tail is rewritten without the else, because the set arm always writes expression, so the old empty-object check could only fire on the clearing arm. The function's doc comment and the field's comment say what clearing now stores.
  • Other designer write paths. None writes config.schedule. FlowNodeInspector.tsx routes every commit on the Cron schedule path through scheduleWithExpression (line 552); the other schedule* helpers in the file (normalizeScheduleCadence, scheduleCadenceOf, scheduleFieldValue, readOnlyScheduleCadence) only read. The Advanced JSON box writes whatever the author types and is not a designer-produced shape.
  • Changeset .changeset/31383-flow-designer-cleared-cron-drops-typed-schedule.md: @object-ui/app-shell patch, both declaration lines.

Tests

  • flow-node-config.scheduleCadence-12779.test.ts: the old assertion that pinned the defect (clearing { type: 'cron', expression, timezone: 'UTC' } returned { type: 'cron', timezone: 'UTC' }) is removed from the existing clearing case, and a new describe for objectstack#31383 adds 11 cases:
    • clearing a typed cron, a typed cron with a timezone, a typed cron cleared to undefined, a typed { cron } with a timezone, a typed cron that already had no cadence, an interval type that carried only an expression, and an unknown type beside an expression: each returns undefined;
    • clearing an untyped { expression, timezone } returns undefined;
    • CONTROLS: an untyped { expression } still clears to undefined; a cron typed over { type: 'cron', expression, timezone } or { type: 'cron', timezone } keeps type and timezone; clearing { expression, intervalMs } keeps the interval the engine then binds.
  • FlowNodeInspector.scheduleCadence-12779.test.tsx (rendered, through the real Cron schedule input and fireEvent.change): clearing the box on a typed cron, and on a typed cron with a timezone, leaves the start node's config with no schedule key and its objectName kept, and the box reads empty. CONTROL: clearing an untyped { expression } stores no schedule, as before. The file now imports @objectstack/lint at module top, as its sibling inspector tests do, because the inspector reaches it through function-body import()s and check:test-source-alias asks an edited test file to pay that load at collection.
  • Ablation through scripts/ablation-replace.mjs: the clearing arm's return put back to the old Object.keys(next).length === 0 ? undefined : next (anchor 1 to 0, blob aa3d830d to 91bc6d94). Result: 10 failed, 58 passed (68): the 8 new table cases and the 2 rendered typed cases red; every CONTROL green. Restored: blob equal to HEAD (aa3d830d), git diff HEAD empty. Both tests import flow-node-config.ts from source by relative path, so no build sat between the mutation and the run.

Acceptance notes

  • The untyped { timezone } leftover is fixed in place. The grade record says the untyped form already collapses; that holds for { expression } but not for { expression, timezone }, which left { timezone }. The engine binds nothing from it, and PR fix(spec)!: refuse a start-node filter beside timeRelative and a schedule the readers cannot use #31376's flowStartScheduleReadsNothing refuses an untyped object it reads nothing from, so it is the same defect in the same function. All four conditions of the bounded in-place fix hold: same defect class, a mechanical fix pinned here, the file is inside this claim's surface, and no new gate family. Named here so the seat can rule it out if it prefers the narrower reading.
  • PR fix(spec)!: refuse a start-node filter beside timeRelative and a schedule the readers cannot use #31376 has not merged (open, draft, at the time of this PR), so the cleared case was not run through its FlowSchema refusals. The shapes this PR stops writing are the ones its review named: a typed cron with no expression, which the published ScheduleSchema already refuses on main.
  • Declaration lines measured on the diff: no published key, export, schema arm or i18n string is added or removed. Clause-②: no, Metadata-protocol: none, as the claim opened.

Verification

Run on head 3251f97c (base origin/main 03533d2c).

Build, tests and lint
  • Build under the verify lock: pnpm --workspace-concurrency=2 --filter '@objectstack/spec...' --filter '@objectstack/formula...' --filter '@objectstack/client...' --filter '@objectstack/lint...' --filter '@objectstack/sdui-parser...' build: VERDICT command-exit 0.
  • pnpm --dir ui exec vitest run --maxWorkers=2 on the two touched test files: Test Files 2 passed (2), Tests 68 passed (68).
  • Every inspector test that imports scheduleWithExpression's module or renders FlowNodeInspector (inspectors/FlowNodeInspector* and inspectors/flow-node-config*, 39 files): Test Files 39 passed (39), Tests 667 passed, 1 skipped.
  • eslint on the three changed source and test files: exit 0. pnpm --dir ui run check:new-line-citations: 0 new citations, exit 0.
  • @object-ui/app-shell type-check was not run locally (it needs every @object-ui/* dist built); check:type-check-debt below exits 0, and CI's type-check lane runs the package.
Derived gates (36)

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 36 families on this diff. On head 3251f97c all 36 exited 0, among them pnpm --dir ui exec vitest run scripts/ and pnpm check:type-check-debt. --ran reconciliation with exit codes recorded: 36 derived, 36 run, 0 NOT-MEASURED, 0 UNRUN. The first round, on 93b35e72, had one red: check:test-source-alias asked the edited rendered test to load @objectstack/lint at module top; 3251f97c adds that import, and the whole list was re-run. Changed lines: 106, under the 3,000 threshold.


Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01FEkQ5oAnL2z711MLVm7ZnB

…ule block left with no cadence

Co-Authored-By: Claude <noreply@anthropic.com>
…no cadence, untyped { timezone } included

Co-Authored-By: Claude <noreply@anthropic.com>
… cadence inspector test

Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 8, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · os-project-manager · session_01FEkQ5nLA7wiVY5YWjT9Svr

席位核对 · ACCEPT · 2026-11-08T06:45Z · head 3251f97c2157646a242751f9772180e62b86ed49

Seat domain:ui#1 reviewed the diff of its own dispatch (claim 6717821947 on #31383, grade record 6717793605, card thread session_01FEkQ5oAnL2z711MLVm7ZnB, report 6718265859). Read against GitHub, not the report.

  1. Shape: 4 files, +98 / -8. scheduleWithExpression (ui/packages/app-shell/src/views/metadata-admin/inspectors/flow-node-config.ts) now returns undefined when clearing the cron leaves a block with no cadence (normalizeScheduleCadence(next) === null). Typing a cron over a typed block still keeps its type and timezone. There are pins in flow-node-config.scheduleCadence-12779.test.ts and a rendered case in FlowNodeInspector.scheduleCadence-12779.test.tsx, plus one changeset. Body says Fixes #31383; no other closing keyword.
  2. Premise: on main the designer stored { type: 'cron' } / { type: 'cron', timezone } after a cleared cron, and the engine binds nothing from either. PR fix(spec)!: refuse a start-node filter beside timeRelative and a schedule the readers cannot use #31376 has since merged, and it refuses that block at save.
  3. Gates / check-runs: 55 runs on this head, 44 success, 11 skipped, 0 red. Dev, on this head: 36/36 derived gates exit 0, 0 NOT MEASURED. The ablation turned 10 pins red with the controls green, then restored. mergeable_state clean.
  4. Accepted deviations / notes: a cleared untyped { expression, timezone } now also drops whole instead of leaving { timezone }. That block binds nothing, and fix(spec)!: refuse a start-node filter beside timeRelative and a schedule the readers cannot use #31376's flowStartScheduleReadsNothing refuses it as well. It sits inside the card's Expected ("never leaves a block with no cadence"), and the seat accepts it.
  5. Out of scope / Skills-docs: no spec or server change. Clause-②: no and Metadata-protocol: none hold as measured, so no contract review is owed. No packages/spec/src path, so Skills-docs: is not owed.
  6. Landing: check-governed-merges --pr 31412: not governed, 106 changed lines, so no human approval owed. Landing on the maintainer's standing words 「按照 pm-dispatch 规则复核过的都授权」 (2026-10-25T11:35Z) and 「你应该使用 pm-dispatch 技能审核和落地pr」 (2026-11-07T16:59Z). Relay pr_ready + automerge_enable now. On merge, ui: flow designer leaves a typed schedule block with no cadence when the cron is cleared, which the flow parse now refuses #31383 closes by Fixes, and the seat removes pm:dispatched.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 8, 2026 06:47
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 8, 2026 06:47
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 8, 2026
Merged via the queue into main with commit 218f834 Nov 8, 2026
56 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-xmmugm branch November 8, 2026 07:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tests tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ui: flow designer leaves a typed schedule block with no cadence when the cron is cleared, which the flow parse now refuses

1 participant