Repository navigation
fix(service-automation): the two-package flow collision line says neither flow is armed - #31802
Conversation
…flow is armed Claude-Session: https://claude.ai/code/session_01ADHkmT3p3cREt88Gsd1rP3 Co-authored-by: Claude <noreply@anthropic.com>
…ther flow is armed Claude-Session: https://claude.ai/code/session_01ADHkmT3p3cREt88Gsd1rP3 Co-authored-by: Claude <noreply@anthropic.com>
…t module top Claude-Session: https://claude.ai/code/session_01ADHkmT3p3cREt88Gsd1rP3 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 1 package(s): ⛔ 1 release-owned page(s) name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 10 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 8d839ed0583d5040a4a4743c5a2ce5b586890158 && git checkout 8d839ed0583d5040a4a4743c5a2ce5b586890158
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 5b7269ff40195849427c5be7746acfbd7da1ad71 ea1c60cf3078cb294a8058701188896d38dc1726 && git checkout -B drift-repro 5b7269ff40195849427c5be7746acfbd7da1ad71 && git merge --no-ff ea1c60cf3078cb294a8058701188896d38dc1726
node scripts/docs-audit/affected-docs.mjs --json 5b7269ff40195849427c5be7746acfbd7da1ad71
|
…solver, and says no flow is armed when it refuses Claude-Session: https://claude.ai/code/session_01ADHkmT3p3cREt88Gsd1rP3 Co-authored-by: Claude <noreply@anthropic.com>
|
epic:#31527#1 · os-steve · session_01ADHkmP7o3uDTti21wLTfPf Epic red-line review: #31754 (E1 follow-up, collision log), head ea1c60c · 2026-11-09T05:23Z Verdict: PASS.
Seat 3 accepts and lands this after green CI. It is not a governed surface. |
|
domain:server#3 · os-steve · session_01ADHkmQXLfvyB5R53VDm3Ka 席位核对 · ACCEPT · #31802 (Fixes #31754, ADR-0150 E1 follow-up) · 2026-11-09T05:25ZHead-sha:
|
Requested by Steve · project thread
Fixes #31754
Clause-②: no — log text only; what the engine arms, refuses or runs does not change. Measured on head ea1c60c on a real cold boot (ObjectKernel + ObjectQLPlugin + SqlDriver): in all three refused shapes the engine refuses the name and arms nothing, the same as on base; only the collision line and its meta change.
engine.tsis not in the diff, and the returned winner andFlowShadowingRecordare unchanged.Metadata-protocol: none
维护者速读
packages/services/service-automation/src/flow-precedence.ts(refusalOf分支),plugin.ts的resolveFlowContenders,再看flow-declared-override-first-boot-pull.integration.test.ts末尾新增的 describe。pnpm --filter @objectstack/service-automation exec vitest run src/flow-declared-override-first-boot-pull.integration.test.ts -t 31754,预期 7 条全绿。What changes
The boot pull's collision warning (
resolveFlowPrecedence) now asks the resolver the engine asks. Following the seat's ruling (A, card comment 6732521115):resolveFlowPrecedencetakes an optional fifth argument,refusalOf(name)(typeFlowNameRefusalReader, exported as a type from the package root). It returns the refusal message the engine's flow-name resolver givesnamefor a caller with no package, orundefinedwhen it serves the name.AutomationServicePlugin.resolveFlowContenders(the one precedence path for the boot pull, thekernel:readysync and themetadata:reloadedre-sync) fills it fromflowNameResolver(ctx), the same readerinit()attaches to the engine (setFlowNameResolver), so the warning says "refused" exactly whenregisterFlowrefuses.("arming none of them" when three or more definitions are listed), and the warning's meta is
{ flow, refused: true, contenders }(noarmed, noshadowed).Measured on base f647430 with a real cold boot (the round-2 report, 6732507604), three shapes in which the engine refuses while main's line said a flow was armed:
.custom) beside a second packageThe shadowing record and its three readers (the bootstrap restatement in
plugin.ts, the CLI banner viaserve.ts/format.ts,armedFromon runtime states) are unchanged: measured on the dogfood showcase boot holding a two-package clash, the engine records no receipt for a refused name (getShadowedFlows()is[], no runtime-state row, no "is ARMED" line), because the boot pull records it only afterregisterFlowsucceeds.Pinned strings, found by searching the tree for "by package id", "arming", "Flow name collision" and "other definition(s)":
packages/cli/src/utils/format.boot-warning-classes.test.ts(print-once case): forced test change. Its collision record paired a package with a runtime row (the sealed class) but spelled the "by package id" arm. It now carries the line the sealed class prints. The assertions (one collision record kept, the restatement withheld) are unchanged. Because this PR edits the file,check:test-source-aliasjudged its reach and asked for ten module-top side-effect imports (the dynamicimport()sserve.tsreaches), the same blockdev-mcp-connect-hint-origin.test.tscarries; added, gate green.flow-name-shadowing.test.ts,flow-precedence-loader-set.test.ts: pin the sealed and override arms with no resolver; unchanged and green.flow-declared-override-first-boot-pull.integration.test.ts: its boot helper gains an optional second package (Second: none, before or after the shipping package); every existing case passesnoneby default and is unchanged.Tests
[#31754]inflow-declared-override-first-boot-pull.integration.test.ts): the three shapes above × the second package registered before and after the shipping one (6 cases). Each asserts the engine's refusal (every 'failed to register flow' record for the name says "held by 2 packages" and names both packages;getFlownull before and afterkernel:ready; no receipt) and that every collision line for the name agrees ("arming neither"/"arming none of them", the pointer, no "arming package / the declared override / a runtime-authored", no "Only the armed definition dispatches"; metarefused: truewithoutarmed).toBemain's sealed line, the meta is main's, nothing refused, the base armed.flow-package-scoped-resolution.test.ts): the four arms (by package id, sealed, declared override, arrival order), eachtoBemain's line.refusalOfbuilt as the plugin builds it; linetoBethe new text, and the engine refuses naming both.Ablations (fix committed first,
scripts/ablation-replace.mjs, each restored to the HEAD blob withgit diff HEADempty):refusalOf dropped in plugin.resolveFlowContenders
The refused branch in flow-precedence.ts disabled
The 5 passing cases are the five CONTROLs.
Acceptance notes
flowNameResolver(ctx), the readerinit()attaches to the engine, not the engine itself (no new engine method). A host that attaches a different resolver to the engine by hand would bypass the plugin altogether; no such caller exists in this repository.FlowNameRefusalReaderis a new exported type name (the optional parameter's type), named after the existingFlowNameResolver. Open question for the seat.Verification
Every reading below is on head ea1c60c (the final commit), base f647430.
pnpm --filter @objectstack/service-automation test: Test Files 267 passed (267), Tests 3493 passed (3493).typecheck: exit 0. Rooteslint --no-inline-config packages/services/service-automation: exit 0 (the package has nolintscript).pnpm --filter @objectstack/cli exec vitest run --project unit src/utils/format.boot-warning-classes.test.ts src/commands/serve-automation-shadowing.test.ts: 34 passed (34). The CLIintegrationtier is declared to CI.node scripts/pm/dispatch-gates.mjs --commands: 73 derived gates, each run, all exit 0;--ranreconciliation: 73 run, 0 NOT-MEASURED, 0 UNRUN.!in the title, no(narrowing)arm); the CI queue runs it.