Repository navigation
fix(service-automation,mcp): type a time-relative flow's conditions against the object its sweep scans - #30453
objectstack-fleet[bot] wants to merge 3 commits into
Conversation
…gainst the swept object The registration advisory read the start node's objectName; the run binds timeRelative.object. Read it off deriveTriggerBinding instead, and reword the validate_expression sweepObject advice to name the publish refusal. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CbRQNg8jRbhpBF2rFY
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Utp6CbRQNg8jRbhpBF2rFY
📓 Docs Drift CheckThis PR changes 2 package(s): 2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 4 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 19 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 1769ef19ed0cecfd179beb01bab0cb6f742be532 && git checkout 1769ef19ed0cecfd179beb01bab0cb6f742be532
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin df4c06e6b165bb0a30eb86e4ace6258932f69d8a 9fe18676371e491a5b842d595750eee0a645cf53 && git checkout -B drift-repro df4c06e6b165bb0a30eb86e4ace6258932f69d8a && git merge --no-ff 9fe18676371e491a5b842d595750eee0a645cf53
node scripts/docs-audit/affected-docs.mjs --json df4c06e6b165bb0a30eb86e4ace6258932f69d8a
|
|
domain:server#2 · os-bill · session_01Utp6Cbvcmv8YWFhFJwzjKL 维护者速读
席位核对 — ACCEPT · domain:server#2 · head
|
Requested by Bill · project thread
domain:server#2 · os-bill · session_01Utp6CbRQNg8jRbhpBF2rFY
Fixes #30396
Clause-②: no — a logged registration advisory (never thrown, never a refusal) now types a time-relative flow's conditions against the object the run binds, and the MCP
validate_expressionprose stops presenting a start the publish door refuses as a supported shape; the tool's input schema and verdicts are unchanged.Metadata-protocol: none
维护者速读
timeRelative.object)检查条件,不再按开始节点的objectName;MCPvalidate_expression的说明和 SKILL.md 改为「把被扫描对象作为 objectName 传入,开始节点写别的对象会在发布时被拦(flow-time-relative-object-mismatch)」。timeRelative.object不写objectName的流程完全不做字段检查。MCP 文案还在教 agent 写一种现在发布会被拒的流程。deriveTriggerBinding),与运行时同一答案;非时间相对流程不变;MCP 只改文字,输入 schema 与判决不变。回滚即撤 PR。packages/services/service-automation/src/flow-registration-checks.ts的validateFlowExpressions取对象那几行;engine.ts宿主里新增的一行deriveTriggerBinding;packages/mcp/src/mcp-http-tools.ts的sweepObject说明。pnpm --filter @objectstack/service-automation exec vitest run --maxWorkers=2 src/flow-registration-checks.test.ts,预期 4 个用例通过。What changes
flow-registration-checks.ts,validateFlowExpressions). The schema-aware pass (Formula guardrail: cel-js arithmetic silently returns null (double × int + bare identifiers) #1928: loggedunknown field/ typo / type-mismatch warnings, never thrown) took its object from the start node'sconfig.objectName. It now takes it from the flow's trigger binding when that binding istime_relative:binding.object, whichderiveTriggerBindingresolves astimeRelative.objectwhen it is a string,config.objectNameonly otherwise — the object whose row the sweep hands each run. The rule is reused, not restated: the pass calls the engine's ownderiveTriggerBinding(the same methodvalidateApiTriggerSecretalready calls at registration). Every other flow keepsconfig.objectName. The binding is derived only on the advisory pass with a resolver wired; the stored pass (advisory: false) never reads the hint.engine.ts). One line: thevalidateFlowExpressionshost now passesderiveTriggerBinding, plus a sentence in that method's docblock. Outside the claim's expected file list (not a banned path); see Acceptance notes.mcp-http-tools.ts,skill-md.ts).validate_expression's description, itssweepObjectinput text and the SKILL.md line no longer advise passing asweepObjectother thanobjectName. They say a time-relative flow's conditions read rows of the swept object, so pass it asobjectName, and that a start whoseobjectNamenames another object is refused at publish (flow-time-relative-object-mismatch). ThesweepObjecttext keeps what the tool answers when the two differ (the gate's advisory type-mismatch warning). No schema change: the other-object arm still mirrors what@objectstack/lintgrades for such a flow's expressions, so retiring it would move the tool off the gate.flow-registration-checks.test.ts, new). On a stored two-object flow (startobjectName: crm_account, sweepcrm_contract): a field of the swept object passes; a field onlycrm_accounthas warnsunknown field. A one-object flow is unchanged (clean, and a typo still warns). A sweep-only flow (noobjectName) is typed against the swept object..changeset/30396-time-relative-condition-object.md:patchon@objectstack/service-automationand@objectstack/mcp.Acceptance notes
09720e2c, before the fix). The four tests above against the old code: 3 red. The two-object flow logged[flow 'renewal_alert'] node 'check' (decision) condition: unknown field \renewal_status` on `crm_account`for the swept object's real field, and nothing forrecord.industry(a field that fails every run); the sweep-only flow logged nothing forrecord.industry` because no object was typed at all. The one-object case was already green.objectName) now gets the schema advisory where it got none. It is a log line at registration; nothing is refused.engine.tsis outside the claim's expected list:validateFlowExpressionsreceives its host members from the engine (RegistrationCheckHostPick), so reusing the binding means one wiring line there. The alternatives were a third copy of the binding rule besidederiveTriggerBindingand@objectstack/lint's, which the order rules out, or callingderiveTriggerBindingwith a stand-in host.skill-md-surface-guard.test.tschecks tool names only), and hint text is not pinned by convention; no text pin added.git grep sweepObjectoverskills/,content/,docs/,apps/,ui/: no hit; no skill page restates the MCP advice.Verification
Readings on head
9fe18676(fa901c10the change,35374475merged withmainatfdd06415,9fe18676a comment-only edit) unless named.Build, typecheck, tests
pnpm --filter '@objectstack/service-automation^...' --filter '@objectstack/mcp^...' buildunder the lock, VERDICT command-exit 0; laterpnpm --filter '@objectstack/dogfood^...' buildon35374475, exit 0.@objectstack/service-automation:pnpm testTest Files 264/264, Tests 3418/3418 (onfa901c10);pnpm typecheck0.@objectstack/mcp:pnpm testTest Files 56/56, Tests 678/678 (onfa901c10);pnpm typecheck0.pnpm lint(eslint . --no-inline-config) on9fe18676: exit 0.Ablation (scripts/ablation-replace.mjs; the suite imports `src`, no dist leg)
config.objectNameagain (const objectName = 'ABLATION_30396' && bound?.triggerType === 'never'): mutation landed (anchor 1→0, blobc53973cf→488218fa); 3 red, among thema stored two-object flow: a field of the swept object passeswith+ "[flow 'renewal_alert'] node 'check' (decision) condition: unknown field \renewal_status` on `crm_account`"; restored, blob == HEAD,git diff HEAD` empty.Derived gates
node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackon9fe18676: 62 families (the same list as on35374475), all 62 exit 0 (exit code captured per line before any pipe).check:dual-build-cjs-loads(PREREQUISITE NOT MET: seven packages outside the dogfood closure had nodist/; built under the lock, then exit 0).check-plugin-teardown-shape --self-test's pinned fixture commit was fetched into this shallow clone before the run.--ranreconciliation: "62 derived famil(ies) accounted for — 62 run, 0 NOT-MEASURED (a DERIVED zero …)".Dogfood (full battery, run as the order asks; no narrowing arm)
pnpm --filter '@objectstack/dogfood^...' build && pnpm --filter @objectstack/dogfood testunder the lock, started on35374475(no shard, no path): Test Files 419 passed, 3 skipped (422); Tests 3492 passed, 36 skipped (3528); VERDICT command-exit 0.9fe18676changes only a code comment inflow-registration-checks.ts, written while that build was running.🤖 Generated with Claude Code
https://claude.ai/code/session_01Utp6CbRQNg8jRbhpBF2rFY