CODING PLANNER

Documentation · AR Labs

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

Incident 1 at a glance
CampaignSalesbot diagnostic recovery
Failure windowTwo consecutive days in early August 2026
DetectedTwo days later, in a review of the campaign
Reached implementationNo

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 by stage: the intended contract against what happened
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.

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.

A diagram of the enforcement layer as it stood at diagnosis. Two stacked bands compare what the pipeline declared with what it enforced. The upper band runs left to right: grill-me marked pre-pipeline, then a solid enforced gate labelled init. The manifest declares generate-design next, but it is drawn dashed and greyed and tagged NOT WIRED, because the command contains no reference to the transition script. A solid arrow leaves the gate, passes over generate-design and enters create-prp, labelled init enters here, bypassing it; create-prp is drawn solid and annotated recorded here. create-prp is followed by to-issues, generate-spec and implement, with to-issues and implement each annotated with an empty verify list, and a bracket noting there is no gate at any later boundary. The lower band shows the transition script's seven verbs: handoff, init and entry-context are solid and marked called, while status, propose, complete and waive are dashed and struck through with chips reading zero callers. A consequence panel states that pipeline.stage is write-once, written at init and never advanced, so every campaign is permanently recorded at the entry stage, and that no stage's verify predicate has ever executed.
Fig 1.1 The declared pipeline against the enforced pipeline, as it stood at diagnosis: one gate reachable, and the four verbs that would advance a stage without callers. All four have callers now.

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:

Reclassification of Incident 2's four execution failures
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

Verbs of pipeline-transition.sh at the time of the incidents, with call sites verified by search
Verb Callers Reachable
handoffgrill-me/SKILL.mdYes
initcreate-prp.md; the only write of pipeline.stage that ranYes
entry-contextcreate-prp.mdYes
statusNoneNo
proposeNoneNo
completeNone; its stage write was unreachableNo
waiveMentioned in documentation, never called; its stage write was unreachableNo

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.