Skip to content

service-automation: the packaged-subflow disable refusal tells the admin to disable the calling flow first, but a disabled caller still blocks the disable — the prescribed remedy can never complete #20678

Description

@objectstack-fleet

QA-source: #20674 · automation.packaged-flow-subflow-disable-refusal · c5

Recorded while grounding as docs/qa/platform-checklist/FOLLOW-UPS.md §8a D19 (2026-08-26); no card existed. Now measured live.

What happens

Disabling a packaged flow that another packaged flow calls as a subflow is refused with 409 DELETE_RESTRICTED and the remedy "Disable the calling flow first, or leave this one armed." Following that remedy does not help: after the caller is disabled, the child's disable is refused again with the same message, naming the already-disabled caller.

Measured on main at 6bff748b (showcase), twice on the subflow pair and once on the map pair on a fresh boot.

Reproduction

  1. As the seeded admin: POST /api/v1/automation/showcase_notify_owner/toggle {"enabled":false} → 409 DELETE_RESTRICTED "Flow 'showcase_notify_owner' cannot be disabled while 1 packaged flow still calls it as a subflow: 'showcase_task_done_notify_owner'. … Disable the calling flow first, or leave this one armed."
  2. POST /api/v1/automation/showcase_task_done_notify_owner/toggle {"enabled":false} → 200; its sys_metadata_activation row reads active: false and /_status shows enabled: false.
  3. Retry step 1. Expected: 200 and a ledger row active: false for showcase_notify_owner. Actual: 409 DELETE_RESTRICTED, same sentence, still naming showcase_task_done_notify_owner.
  4. Map pair, same result: disable showcase_release_signoff (200), then showcase_one_task_signoff is still refused naming it.

Mechanism (read from source at 6bff748b)

packagedSubflowCallers in packages/services/service-automation/src/engine.ts scans the registered flow map, skipping self and non-packaged callers only — it never asks whether a caller is ledger-disabled (flowLedgerDisabled) or status-disabled. toggleFlow throws from that list before any durable write. ADR-0126 §7.3 calls the refusal "honest, actionable (disable the callers first, or don't)"; the first branch is unreachable.

Expected

Either a disabled caller no longer guards its callee (so the prescribed sequence completes), or the refusal stops prescribing a sequence the door will refuse. The engine-level pins in flow-activation-ledger.test.ts do not cover the sequence.

Full evidence chain: #20674 (F-3).


Generated by Claude Code

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:workflowApprovals and automation — the work that runs without a person driving itbugSomething isn't workingdomain:servicespriority:p2Medium: important, M3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions