Pipeline
/workflow refactor-guard
Characterization tests first and green at HEAD, then a partitioned behaviour-preserving refactor, then a drift hunt. A refactor whose oracle is written afterwards has no oracle.
Shape
What runs, in order
7 stages, and the engine aborts the run at $30. A stage with more than one step runs those beside each other.
- @architectProduce the ADR for this refactor: {{input}} — state the invariant being enforced and the target shape, name at least two rejected alternatives including the alternative of doing nothing with its real cost, write every declared invariant with the exact command or lint rule id that checks it, and produce the impact map of will-change, must-not-change and changes-together globs that the lead will partition against. If the refactor cannot be done without changing observable behaviour, emit a line beginning ORG-HALT: not behaviour-preserving and stop.
- @qa-functionalIf the following text contains an ORG-HALT line (a genuine stop: ambiguity, safety, or an impossible repository state — never merely 'this is small'), re-emit it verbatim and stop; otherwise write characterization tests that pin the current observable behaviour of the modules named in the impact map below, run them at HEAD before anything changes and paste the real passing transcript with its exit code, and if the suite cannot be made to pass at HEAD emit a line beginning ORG-HALT: no oracle followed by what is untestable and why. Emit ARTIFACT: TESTREC with the green-at-HEAD transcript as its centrepiece. ADR: {{prev}}
- @tech-leadIf the following text contains an ORG-HALT line (a genuine stop: ambiguity, safety, or an impossible repository state — never merely 'this is small'), re-emit it verbatim and stop; otherwise run in Mode PLAN and partition this refactor into exactly two module-aligned subtasks with provably disjoint write scopes, printing every pairwise glob intersection you checked, naming the characterization command each lane must keep green and the invariant check each lane must satisfy, and stating the hard budget ceiling for each lane. Characterization record: {{prev}}
- @developerIf the following text contains an ORG-HALT line (a genuine stop: ambiguity, safety, or an impossible repository state — never merely 'this is small'), re-emit it verbatim and stop; otherwise take the first lane of the plan below, change structure without changing observable behaviour, treat any need to edit a characterization test as an automatic stop rather than a fix, run the characterization command and the invariant check and report their real exit codes, and emit ARTIFACT: PATCH. Plan: {{prev}}
- @developerIf the following text contains an ORG-HALT line (a genuine stop: ambiguity, safety, or an impossible repository state — never merely 'this is small'), re-emit it verbatim and stop; otherwise take the second lane of the plan below, change structure without changing observable behaviour, treat any need to edit a characterization test as an automatic stop rather than a fix, run the characterization command and the invariant check and report their real exit codes, and emit ARTIFACT: PATCH. Plan: {{prev}}
- @qa-functionalIf the following text contains an ORG-HALT line (a genuine stop: ambiguity, safety, or an impossible repository state — never merely 'this is small'), re-emit it verbatim and stop; otherwise verify that the characterization suite is byte-for-byte unchanged by running git diff against the test files and pasting the result, re-run the suite and paste the real transcript with its exit code, run the full suite and report its pre-existing failure count, and emit ARTIFACT: TESTREC. If any characterization test was modified, emit a line beginning ORG-HALT: oracle tampered with and name the file. Refactor lanes: {{prev}}
- @qa-adversarialIf the following text contains an ORG-HALT line (a genuine stop: ambiguity, safety, or an impossible repository state — never merely 'this is small'), re-emit it verbatim and stop; otherwise run git diff yourself rather than trusting any diff quoted to you, and hunt specifically for observable behaviour the characterization suite did not pin — error messages, thrown types, ordering, timing, logging, resource cleanup, edge cases at the boundaries of the refactored modules and the seam where they meet untouched code — emitting ARTIFACT: FINDINGS in which every blocker carries a reproduction command and its observed output. Verification record: {{prev}}
- [tier:build] @tech-lead Run in Mode EVIDENCE and assemble the merge-gate packet whose exhibits are the green-at-HEAD characterization transcript, the unchanged-suite proof, the post-refactor transcript, the invariant check results, the drift findings referenced by id rather than paraphrased, the ARCTURN-PATCH trailers from stages 2, 4, 5 and 6 (expect a real patch, or status=empty, from the write-lane stages 2, 4 and 5; expect status=discarded from stage 6, which runs on the exec lane and can never land anything regardless of what it did), the blast radius and the budget actuals against the ceiling. End with the DECISION-REQUEST block. If any input carries an ORG-HALT line, reproduce it verbatim at the top and assemble the packet around it instead of recommending a merge. Drift findings: {{prev}}
The reasoning
Why it is shaped this way
The pipeline file's own argument for its shape: which stages can write, which cannot, and where the human gate sits. The stages themselves are above.
Run it as /workflow refactor-guard <the invariant you are trying to enforce and the target shape>.
One rule decides whether this pipeline is worth running: the characterization tests are written before the refactor starts and they must pass at HEAD. A suite written after the change describes the change; a suite written before it describes the behaviour the change must preserve. Only one of those is an oracle, and stage 2 halts if it cannot produce one.
Refactors are the highest-risk engagement class for scope creep, so the budget ceiling is hard and any request to widen a lane's scope is an automatic stop rather than a negotiation. If the refactor turns out to require changing a declared invariant, that is an architecture change and it belongs in feature-build with the architect and a human gate, not here.
Stage 6 exists because a characterization suite only pins the behaviour somebody thought to characterize. The drift hunt goes looking for the behaviour nobody pinned.
Stages 2, 4 and 5 dispatch qa-functional and developer, both of which carry write/edit on top of bash, so all five of those steps run on the write lane: each in its own detached worktree, seeded with the run's state so far, diff captured to a patch, replayed into your real checkout with a plain git apply. Stage 4's two branches are two separate worktrees over the plan's two disjoint module scopes, so they can never collide even though they share a role. Stage 6's qa-adversarial carries bash alone, with neither write nor edit, so it runs on the exec lane instead: the same worktree isolation so it can actually run the drift hunt, but with an unconditional guarantee rather than an expectation — whatever it does in that worktree is never captured and never applied, and the engine appends ARCTURN-PATCH: status=discarded to its output itself, regardless of what the step's own text says. architect and tech-lead are the only roles in this pipeline with no mutating tool at all, and are the only two that run on the fresh-context read lane — no worktree, nothing to execute. Plan mode has no write lane: stage 2 fails immediately, before a token is spent, if the parent session is in plan mode.
- stepTimeoutMs
- 1800000
- continueOnError
- false
- budgetUsd
- 30
Part of enterprise-org. Install it with arcturn add sitharaj88/arcturn/kits/enterprise-org, then type /workflow refactor-guard in a session.