Skip to content

Activity exits: the corpus moves its routing into the workflows that own it - #500

Merged
m2ux merged 2 commits into
workflowsfrom
workflow/496-activity-exits
Aug 24, 2026
Merged

Activity exits: the corpus moves its routing into the workflows that own it#500
m2ux merged 2 commits into
workflowsfrom
workflow/496-activity-exits

Conversation

@m2ux

@m2ux m2ux commented Aug 24, 2026

Copy link
Copy Markdown
Owner

Summary

Every activity in this corpus named the activity that ran next, in its own file. So a workflow file
did not describe its own shape, and an activity could not be borrowed and rearranged.

This moves the routing to the workflows that own it. Each activity now declares exits — named
outcomes with the predicate that selects each — and each workflow.yaml declares the graph
binding those exits to destinations.

The corpus half of #496. Paired with the server change in #501, which defines the schema this
depends on; the tooling on this branch is red until that merges, so it was verified locally against
the server branch.

What moved

117 activity files across 17 workflows carried 176 route declarations: 127 transitions[]
entries (48 with a condition), 15 decision branches, and 34 checkpoint options with a
transitionTo. After this change, the count carried by an activity file is zero, and the routing
is 163 exits bound in 17 workflow files.

The count falls by 13, every case a route that named nothing:

  • Nine decision branches named no destination. Only 6 of the 15 branches carried a
    transitionTo; the other 9 answered nothing about where next — the activity's transitions decided,
    and the branch merely labelled which case had been reached. They are retired rather than renamed.
  • Six checkpoint options reached a destination an exit already reached. Rather than name the same
    outcome twice, the option selects the existing exit. Five are the "proceed" half of a confirmation
    gate in work-packages; the sixth is workflow-design's pair of review dispositions, which differ
    in what they set and not in where they go.
  • Two activities gained an exit they lacked. meta/04-end-workflow and
    midnight-system-review/05-verdict-and-report ended the run by falling off the end of their
    routes. That ending is a real outcome, so it is named (completed, report-only) and bound to
    __terminal__ — which is also what makes a dismissed checkpoint at either site resolve to a named
    exit.

What an activity looks like now

Instead of seven conditional transitions naming seven activities, prism/00-select-mode declares
seven outcomes:

exits:
  - id: dispute
    when: pipeline_mode == "dispute"
  - id: subsystem
    when: pipeline_mode == "subsystem"
  # ...
  - id: structural
    isDefault: true

and prism/workflow.yaml says where each one goes:

graph:
  select-mode:
    dispute: dispute-pass
    subsystem: subsystem-pass
    structural: structural-pass

Exit names come from what the route already said: a comparison against a value gives the value, a
comparison against true gives the variable, a decision branch gives its branch id, a checkpoint
option gives its option id, and an activity with one unconditional route exits by done. Thirty
routes had no name to derive — a compound predicate, or a default standing for the case its siblings
do not cover — and were named by hand; the table is in the investigation record.

Borrowing without mirroring

remediate-vuln runs 14 of work-package's activity files. It now binds their exits in its own
workflow.yaml, so it can place them in a different order — insert a stage, drop one, swap two —
without editing files it borrows but does not own. Today's bindings mirror work-package's, which is
what the corpus does; the difference is that the mirroring is now a choice recorded in one file
rather than a constraint imposed by fourteen.

Aborts that end the activity

An exit may be declared immediate, and selecting one at a checkpoint ends the step sequence there.
Five abort exits carry it. Four are followed by steps that run today: aborting a repository binding
in meta/00-discover-session was followed by eleven steps, five of them ungated; aborting a
submission in work-package/13-submit-for-review by eleven, one of which announces the pull request
is ready for review; aborting in prism-audit/01-prompt-generation by five, all ungated; and
prism-update/01-review-changes by one. The fifth, prism-update/03-verify, has no tail and takes
the flag so the abort vocabulary is uniform.

Every other site keeps the timing it has now, so the move is not a behaviour rewrite. The two review
dispositions in workflow-design/08-quality-review and workflow-authoring/09-validate-and-commit
are where an author wrote a gate onto all 20 and 13 following steps by hand to simulate an early
exit; replacing those gates with an immediate exit is a behaviour change at a site that already
behaves correctly, so it is left for separate work.

The canon and the engine protocol

The orchestrator's own protocol changes with the corpus. evaluate-transition takes the exit a
checkpoint option named, else the first whose predicate holds, else the default, then reads the
destination from the workflow graph; finalize-activity carries the exit taken in its envelope for
next_activity to report.

The authoring canon follows: the construct inventory pairs the activity-level exit with the
workflow-level graph binding and states that no activity file names another activity, and the
anti-pattern catalogue, format and conformance conventions, elicitation guide, impact-analysis
techniques and work-package/REVIEW-MODE.md name exits and the graph where they named transitions
and decisions.

Verification

Verified locally against the paired server branch: all 17 workflows load, all 117 activity files
validate, the 30-guard suite passes, and the end-to-end walk snapshots are byte-identical — every
workflow walks the same path it walked when its activities carried the routing.

Investigation detail

Route counts per workflow before and after, the exit naming table, and the tail measured after every
routed checkpoint option:
.engineering/artifacts/planning/2026-08-24-activity-exits/.

🤖 Generated with Claude Code

m2ux added 2 commits August 24, 2026 08:55
Every activity declares exits — named outcomes with the inline predicate that
selects each — and every workflow.yaml declares the graph binding those exits to
destinations. A checkpoint option selects an exit rather than an activity.

The 176 route declarations the activity files carried are now 163 exits bound in
17 workflow files, and the count carried by an activity file is zero. Nine decision
branches named no destination, so they answered nothing about where next and are
retired outright rather than renamed. A checkpoint option whose destination an exit
already reaches selects that exit instead of naming the same outcome twice.

remediate-vuln binds the fourteen activities it borrows from work-package in its
own file, so it can run them in a different order without editing them.

Five abort exits are immediate: selecting one ends the activity where the user
chose it. Four of them are followed by steps that run today, one after a decision
to abort a submission and one after a decision to abort a repository binding.
The orchestrator's own protocol resolves an exit and reads its destination from
the workflow graph: evaluate-transition takes the exit a checkpoint option named,
else the first whose predicate holds, else the default, and finalize-activity
carries the exit taken in its envelope for next_activity to report.

The construct inventory pairs the activity-level exit with the workflow-level
graph binding and states that no activity file names another activity. The
anti-pattern catalogue, format and conformance conventions, elicitation guide,
impact-analysis techniques and work-package's review-mode notes name exits and
the graph where they named transitions and decisions.
@m2ux
m2ux merged commit 2f89556 into workflows Aug 24, 2026
1 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant