Background
The two incidents
Two planning failures on one campaign, about a week apart: what happened mechanically, what caused it, and what the rebuild changed so that it cannot happen the same way again.
Layer 1 - Orientation
Two failures, one campaign
Both incidents happened on one campaign: a diagnostic-recovery feature for the lab's Salesbot voice agent. Two different sessions failed at two different stages of the same planning pipeline. The first skipped two required planning stages because a bead (one tracked unit of work in the bd issue tracker) looked like something it was not. The second reviewed an archived document that no longer carried any authority, and reasoned onward from it.
Neither failure reached implementation, and no unattended run was dispatched. What they showed was structural: at the time, the pipeline's ordering lived in instructions, and nothing recorded which stage a piece of work had reached. The harness and the system reference describe what replaced that.
A detailed bead or document does not prove that the required earlier work happened. Each completed step, exception, and approval needs a record that the next step can check.
The lesson of Incident 1
Layer 2 - Mechanics
Incident 1 - two planning steps skipped
| Campaign | Salesbot diagnostic recovery |
|---|---|
| Failure window | Two consecutive days in early August 2026 |
| Detected | Two days later, in a review of the campaign |
| Reached implementation | No |
A /grill-me interview session produced a design brief and created one bead to carry the campaign. Creating that one bead is correct under the workflow. The defect was that the bead's role was not explicit: its rich content made it look like an implementation issue of the kind the later /to-issues stage produces.
The next day a new session took that appearance at face value. It treated the design brief as a substitute for a PRP (product requirements prompt: the approved plan a campaign enters on), skipped both /create-prp and /to-issues, and ran /generate-spec. The skip was never put to a person as a question.
The brief itself said /create-prp consumes this brief. Nothing may be dispatched AFK until the resulting PRP is approved.
That sentence was text inside the loop it was meant to police, and text can be weighed and set aside.
The route actually taken
Intended: /grill-me → brief plus one campaign bead → continue decision → /create-prp → PRP approval → /to-issues → slice review → /generate-spec → spec approval → /implement.
Taken: /grill-me → brief plus a campaign bead of unclear role → the session infers that the PRP and the decomposition already exist → /generate-spec.
| Stage | What should have happened | What happened |
|---|---|---|
| /grill-me | Produce a draft brief and exactly one linked campaign bead; no implementation children | Produced the brief and the campaign bead, with no contract distinguishing that bead from /to-issues output |
| Continue decision | Bind brief and campaign bead to the pipeline | No enforceable campaign-stage state existed |
| /create-prp | Create a PRP, or stop for an explicit waiver | The brief was treated as equivalent |
| PRP approval | A person approves the PRP or a named substitute authority | No approval question was asked |
| /to-issues | Create reviewed vertical-slice beads from the authority document | Skipped; the existing bead looked decomposed |
| /generate-spec | Ground an approved slice in current code | Ran against the out-of-sequence bead |
| /implement | Implement only from an approved spec | Never reached |
Root cause
No machine-readable record of workflow stage existed, so the stage was inferred from how the artifacts looked. A bead that is detailed enough looks finished, whatever happened before it.
Containment
The child beads created out of sequence were closed without implementation and their identifiers retired, and the specs generated for them were declared non-authoritative. The design brief was reviewed, approved and linked to the campaign, and the campaign stayed open with /create-prp as its next step. The generated specs were never approved, no implementation began, and no unattended run was dispatched.
What changed
Every node now carries its step as recorded state, pipeline.stage, written only by the transition script; a campaign or a task enters only through a recorded door; and leaving a step runs that step's deterministic checks. A skipped step is now a refusal rather than a silent gap. See entry doors and the single writer.
Incident 2 - the wrong document reviewed
About a week later, a careful session took the same campaign's PRP through a revision and a document review. Five things went wrong.
- The review ran against the wrong document. It consumed an archived, non-authoritative spec, one of the artifacts Incident 1 had invalidated, and then reasoned onward from child specs that no longer counted. The fact that they no longer counted was recorded in a working document, not on the artifacts themselves, so the files stayed readable and indistinguishable from live ones.
- A slice that had been cut survived into the PRP. One vertical slice had been dropped when the scope was reduced. Nothing checked the downstream artifact against that decision, so it persisted.
- Settled decisions were raised again. The brief's resolved-decisions block is prose. Nothing binds a downstream stage to it, so questions already closed were reopened.
- A single-source conflict was fixed by syncing two documents. The brief and the PRP held the same fact in conflicting forms. The session reconciled them by correcting both instead of retiring one, which leaves the fact with two homes that can disagree again.
- The manifest and the documentation disagreed about the stage list. The documentation described a /generate-design stage that the pipeline manifest did not declare.
The defect underneath
The lifecycle joining the brief and the PRP was contradictory. The pipeline said create-prp consumes the brief, and also that generate-spec reads the brief as background. Both documents stayed live, both had to be maintained, and a conflict had two homes.
What changed
The lab decided that the PRP supersedes the brief outright, and that /generate-spec reads the PRP only. That removes a document from maintenance instead of adding a rule. The stage-list disagreement was closed by declaring generate-design in the manifest, where it now sits ahead of the entry stage, declared but not enforced. The broader mechanism, in which a producer retires the artifact it replaces and a change upstream invalidates only the approvals that depend on it, is designed and not yet built: see supersession and rewind and what is not built.
The enforcement layer, as it stood at the time
Both incidents happened while an ordering engine already existed in the repository. These facts were verified by searching the code at the time, and they are why "we have a state machine" was no defence.
- The transition script, pipeline-transition.sh, implemented seven verbs: handoff, init, entry-context, status, propose, complete, waive.
- Only handoff, init and entry-context had callers. propose, complete and status had none, and waive was mentioned in documentation but never called.
- So pipeline.stage was written once, at init, and never advanced. Every campaign was permanently recorded at its entry stage, which made the field misleading rather than merely incomplete.
- The manifest declared four stages, and two of them, to-issues and implement, declared no checks at all.
- The script described itself as the single writer of pipeline.*, but a direct metadata write through the tracker was not blocked.
- Permission prompts were not a reliable record of a person's approval, so an answered prompt proved nothing about who approved.
The ordering engine was built, argument-validated and tested at the entry boundary, and unreachable past the first stage.
The classification rule
If a harness property would have made the failure impossible, it is a process defect, whichever agent committed it.
The second incident's own first analysis put four of its five failures down to execution and one to process. Applying the rule reclassifies all four:
| What happened | First attributed to | Harness property that would have caught it | Verdict |
|---|---|---|---|
| Review ran against an archived, non-authoritative spec | execution | Authority as a checked property of the artifact: its status, campaign and revision, verified at the gate | Process defect |
| A cut slice survived into the PRP | execution | Resolved decisions as durable records, re-verified against downstream artifacts | Process defect |
| Settled decisions were raised again | execution | The same decision records, carrying an owner and a reopen trigger | Process defect |
| A single-source conflict was fixed by syncing two documents | execution | A declared supersession rule: the successor artifact retires its predecessor | Process defect |
Only the frequency of these failures was down to execution; each had a missing harness property behind it. The first incident could be read as one session guessing badly. The second cannot: a careful session, on a campaign that had already been through an incident review, reproduced the same class of failure by trusting an artifact's appearance rather than a checked property. A workflow whose correctness depends on every agent being careful has no correctness property at all. A skill is text evaluated inside the loop it would need to police, which is why the rebuild moved ordering out of skills and into a deterministic harness.
Layer 3 - Contract
Contract: the transition verbs at diagnosis
| Verb | Callers | Reachable |
|---|---|---|
| handoff | grill-me/SKILL.md | Yes |
| init | create-prp.md; the only write of pipeline.stage that ran | Yes |
| entry-context | create-prp.md | Yes |
| status | None | No |
| propose | None | No |
| complete | None; its stage write was unreachable | No |
| waive | Mentioned in documentation, never called; its stage write was unreachable | No |
Today the same script has callers for every advancing verb, a driver that calls them on every pass, and a guard hook that denies any other writer. Its current verbs are listed in the agent reference.