ADR-090: AI is the scaffold; pipelines are the runtime
Status: Accepted Date: 2026-04-25 Related: ADR-067 (playbooks unified), ADR-068 (playbook runtime), ADR-072 (infra as playbooks), ADR-083 (rule promotion), ADR-086 (structural rule graduation)
Decision
The Gyrum platform is built on a two-phase contract. Phase 1 (today): AI agents help author, debug, and extend playbooks — the human talks to Claude, Claude writes the YAML + ansible + cloud-init + outbound-toolkit code, the human reviews and iterates. Phase 2 (target): operators fire playbooks directly — gyrum-fire-playbook provision-and-deploy-warp — and the pipeline runs end-to-end without any AI in the decision loop.
AI remains available for NEW capability development, but every established workflow graduates out of AI-dependence. Calling the pipeline is the stable API; AI is just what got us to the pipeline faster. This is not an accident of the tooling — it is the design.
Context
The Gyrum platform has been running on this two-phase model since ADR-067 unified playbooks as the canonical primitive, but the model itself was never named in an ADR. Every shipped pipeline (warp deploy, distill provisioning, observability lifecycle, host security lifecycle) was authored AI-assisted and then shipped to operate AI-free, yet the principle was implicit. As the platform's playbook surface grew past 30+ canonical pipelines in April 2026, the implicit assumption started leaking: some proposals quietly assumed AI would be in the operating path, and reviewers had no anchor ADR to point at.
A separate force pushed in the same direction: AI session cost (tokens, time, context window) became measurable and non-trivial. Phase 1 work pays for itself once; Phase 1 work that never graduates pays for itself every operator session forever. Without an explicit principle, that asymmetry was invisible at review time.
Why this matters
Three concrete incidents from 2026-04-24/25 make the principle visible.
1. Warp first-deploy. A senior engineer pasted Hetzner/Cloudflare creds, typed one command, and provisioned a production host end-to-end — because the pipeline existed. It took 2+ hours of AI-assisted debugging to write the pipeline (SSH key naming, ufw syntax, vault paths). Once written, the pipeline is stable. Next operator (or me-in-a-month) just fires it.
2. Structural rule graduation (ADR-086). Every prose rule that drifted twice got promoted to a structural check. Hard-wrapped commits became a commit-msg hook. Agent-isolation became auto-workdir. Once the structural default is in place, nobody has to remember the rule. Same shape: AI scaffolded the discovery; the structural gate is the stable runtime.
3. Outbound toolkit (ADR-084 + providers). Every call to Hetzner, Cloudflare, GitHub, Ansible now routes through a typed Go client with journal + retry + error taxonomy. AI drove the scaffolding (four providers in a single session); the toolkit is the stable API — consumers call it forever, with no AI in the call path.
The two-phase model
| Phase | Characteristic | AI role | Human role | Exit / entry criterion |
|---|---|---|---|---|
| 1 — Scaffold | New capability, shape uncertain, iteration required. | Propose structure, write code, debug failures, update docs. | Review, redirect, approve merges. | Capability has stable interface + structural checks + runbook + rollback. |
| 2 — Runtime | Established workflow, interface stable, failure modes documented. | None in the hot path. Available for breaking-change redesign or failure triage (as scaffold-back-to-Phase-1). | Operator fires the pipeline, reads output, responds to alerts. | Operator can run end-to-end without Claude in the loop. |
flowchart TB
A[New capability discovered] --> B[Phase 1: AI scaffolds]
B --> C{Stable interface + checks + runbook?}
C -- No --> B
C -- Yes --> D[Phase 2: pipeline fires directly]
D --> E{Novel failure or redesign?}
E -- Yes --> B
E -- No --> D
Why this is being written down now
The principle has been implicit in every ADR from 067 onward but never stated. Reviewers and contributors had to infer it from the shape of the existing playbooks. Three things converged on 2026-04-24/25 that made implicit-no-longer-good-enough.
First, the warp deploy made the cost visible. 2+ hours of AI debugging produced a one-command pipeline; the contrast between "before pipeline" and "after pipeline" was sharp enough to name. Second, ADR-086 generalised prose-to-check graduation, and the AI-to-pipeline graduation is the same shape one level up. Third, observability and host-security ADRs (088, 089) were both being written assuming this principle — making it explicit lets them cite a single anchor rather than re-derive the rationale.
Naming the principle now also makes it reviewable. Future ADRs can be checked against it: does this proposal assume AI in the hot path? If yes, where is the Phase 2 design? An implicit principle can't be enforced; an explicit one can.
How we tell which phase a workflow is in
| Signal | Phase 1 (scaffold) | Phase 2 (runtime) |
|---|---|---|
| Operator command | Multi-part prompt explaining context | One-line gyrum-fire-playbook <id> |
| Failure handling | Operator pastes logs to AI for triage | Runbook troubleshooting table |
| Step composition | Adjusted ad-hoc based on prior output | YAML declares all dependencies |
| Design decisions | Cross-file ("does this go in pkg/outbound or internal/playbook?") |
None — structure already chosen |
| Structural rules | New ones being added via ADR | All known paths covered |
| Operator expertise | The tooling itself (Claude prompts, debugging) | Domain (warp, Hetzner, Cloudflare) |
The signals are observational — read off the operator's behaviour, not declared in advance. A workflow can be Phase 2 for one operator and Phase 1 for another; the platform-wide claim is that the Phase 2 path EXISTS and is the documented default.
What this means for product design
Every tool we ship must have a non-AI invocation path. Not optionally — load-bearingly. Three invocation surfaces, one pipeline definition.
| Surface | Invocation | AI in path? |
|---|---|---|
| Shell CLI | gyrum-fire-playbook provision-and-deploy-warp --input server_name=warp-01 |
None |
| Web UI | /playbooks/<id> (stepper + live terminal output) |
None |
| HTTP API | POST /api/v1/playbook-runs (called by cron, webhook, external systems) |
None |
When a feature is proposed, the question is: what does the non-AI invocation path look like? If you can't answer that, the feature is still in Phase 1 and should be treated as scaffold, not runtime.
What this rules out
A workflow that requires the operator to "ask Claude" to do something routine. Routine = Phase 2 = pipeline. The "ask Claude" pattern is acceptable as a discovery surface, but the moment a workflow recurs, the routine path must be a pipeline.
AI agents calling AI agents to run established workflows. The pipeline should be the single invocation, not a chain of AI sub-tasks. An agent-of-agents architecture for routine work is a smell — it means the routine has no pipeline yet.
Critical paths (production deploys, security response, data migrations) that lack a pipeline equivalent. These are Phase 2 priorities even if they started as Phase 1 AI-assisted work. A critical path running Phase-1-only is a P1 backlog item, not a stable state.
What this does NOT rule out
AI for novel problems. When the warp deploy hit a ufw syntax bug or SSH key name mismatch tonight, AI triaged + fixed. That is correct — the scaffold still works while the runtime is maturing.
AI for failure explanation. When a Phase 2 pipeline fails in an unexpected way, AI can read the logs + propose a fix. That fix graduates into the runbook or the pipeline itself, moving the failure back to Phase-2-with-better-coverage.
AI for cross-workflow analysis ("which hosts are we over-provisioning?"). AI is a good fit for queries over the fleet; pipelines are a good fit for actions on the fleet. AI is also still correct for one-off exploratory work that won't recur.
Graduation criterion
Following ADR-086's structural rule graduation, a workflow that runs successfully 3+ times via AI-assisted invocation is a candidate for pipeline graduation. After 5 successful runs, it MUST have a pipeline equivalent. At 10 successful runs without a pipeline, the unit is structurally broken and scaffolding-forever is the failure mode.
The graduation is enforced at ADR-review time. When a new ADR proposes a workflow, reviewers ask: "what is the Phase 2 invocation?" If the answer is "ask Claude with this prompt", the ADR is incomplete and goes back for a Phase 2 design. This mirrors ADR-086's prose-to-check graduation, applied to the AI-to-pipeline axis.
| Successful AI-assisted runs | State |
|---|---|
| 1–2 | Phase 1 (genuinely novel — keep iterating) |
| 3–4 | Pipeline candidate (open a Phase-2 ticket) |
| 5+ | MUST have pipeline equivalent |
| 10+ without pipeline | Structurally broken — escalate as drift incident |
The counts aren't magic; the principle is that the AI session cost is real (tokens, time, context window) and every Phase-1 repetition signals a Phase-2 absence.
How this lineages with existing ADRs
ADR-067 establishes that every Phase 2 runtime is a playbook. ADR-068 is the substrate playbooks run on. ADR-072 specifically calls out that infra operations must graduate to playbooks. ADR-083 applies this principle to the rule layer.
ADR-086 — the three-layer progression prose → check → default applies equally to AI → pipeline → invisible-default. ADR-087 collapses multi-step AI-debugged provision into one stateless cloud-init. ADR-088 (observability lifecycle, in flight) — monitoring should auto-register, not require AI to wire up. ADR-089 (host security lifecycle, in flight) — security posture is pipeline-provisioned, not AI-checked each time.
Consequences
Positive. Operators gain confidence that gyrum-managed infrastructure has a non-AI invocation path. Cost of ongoing operations drops to zero-AI after Phase 2. AI tokens go into new capability, not repeating yesterday's work.
Negative. Phase 1 → Phase 2 migration is an ongoing investment. Some workflows will always be Phase 1 (genuinely novel problems). The discipline of "what does the non-AI path look like?" is a review-time question every PR must answer.
Risks. Scaffolding-forever is seductive because it's faster-feeling than proper pipeline-making. The graduation criteria (3/5/10-run thresholds) need to be enforced at ADR-review time, not left to intent. Otherwise every workflow stays Phase 1 indefinitely and the platform becomes an AI-dependency monument.
A second risk is mis-classifying genuinely novel work as Phase 1 forever. Some research-shaped workflows ("which 2026 macroeconomic indicators correlate with our churn?") are correctly AI-driven and have no Phase 2 equivalent. The discipline is to NOT force a pipeline where one is wrong, while also NOT letting routine work hide behind a "novel" label.
A third risk is the AI cost itself becoming invisible. If every operator session quietly burns 50k tokens triaging a known failure mode, the missing pipeline equivalent is a real cost line on the AI bill. Phase 1 cost should be tracked and surfaced at retro time so the graduation pressure is visible, not implicit.
For this session specifically
Tonight's warp deploy is the paradigm case. After tonight, four Phase-2 affordances exist for warp.
| Affordance | Phase 2 surface |
|---|---|
| Provision + deploy | gyrum-fire-playbook provision-and-deploy-warp --input server_name=warp-01 |
| Reference | dark-factory/docs/runbooks/deploy-warp.md |
| Destroy | gyrum-fire-playbook destroy-warp (the Phase 2 complement) |
| UI | /infra/hetzner destroy button |
AI was the scaffold that got us here in 14 hours instead of 14 weeks. The operator never needs to talk to Claude to deploy warp again. That is the point — and the same shape applies to every other workflow on the platform.
Alternatives considered
A) AI-in-the-loop forever. Treat AI as a permanent runtime component — the operator always asks Claude to invoke the workflow. Rejected: AI sessions are not deterministic, do not scale to cron/webhook callers, leak context-window cost on every invocation, and concentrate institutional knowledge in prompt-engineering rather than pipeline design.
B) AI-free from day one. Forbid AI assistance even during scaffold work. Rejected: empirically, AI cuts new-capability authoring from weeks to hours (warp deploy: 2+ hours vs the prior all-day-then-give-up baseline). Banning the scaffold doubles the cost of ever reaching Phase 2.
C) Per-workflow ad-hoc decision. Let each ADR decide independently whether AI stays in the runtime. Rejected: this is what we had until tonight, and it produced silent drift. Some proposals quietly assumed AI in the path; reviewers had no anchor to push back from. The platform-level principle is the lever; per-workflow re-derivation isn't.
The chosen position is the conjunction of A's permissiveness during authoring and B's strictness during operation, with explicit graduation criteria from ADR-086 controlling the transition.
Open questions
How should we measure Phase 2 maturity per workflow? A first pass is "successful runs since last AI assist" — but instrumenting that requires the playbook runtime to emit a "no-AI run" signal. ADR-068's run record may be the right place; revisit when ADR-088 (observability lifecycle) lands.
How should we treat AI-assisted runbook authoring? The runbook is part of the Phase 2 substrate — but writing it is a Phase 1 activity. The right answer is probably "AI drafts the runbook during Phase 1, the runbook is canonical during Phase 2, AI re-edits only when the workflow itself changes shape". Worth a follow-up ADR if the boundary stays fuzzy.
Related
ADR-067, ADR-068, ADR-072, ADR-083, ADR-086, ADR-087, plus the host-lifecycle guide from the ongoing consolidation doc shipment (ADR-088, ADR-089 in flight).
Amendment — 2026-04-29 (warp#698 / ADR-124)
ADR-124 adds the operational definition of "pipelines are the target": block-composition pipelines with declarative logic-routing — not imperative Go orchestration around a shared RunContext. ADR-086's third-repeat heuristic graduates code into a registered Block plus pipeline edit, never into another monolithic Go function. This closes a gap where "pipelines are the target" was being read compatibly with "imperative Go is fine if it's behind a Pipeline type"; it is not.