CODING PLANNER

Documentation · AR Labs

Final implementation

The entry boundary

This chapter covers one boundary in depth: the handoff out of the /grill-me interview and into create-prp, where a deliberately fresh session resumes from persisted state instead of a transcript.

Layer 1 - Orientation

What the boundary settles Built

/grill-me ends with a durable clarification result that a fresh agent session can consume without access to the interview conversation. The skill conducts the interview and proposes semantic choices. pipeline-transition.sh is the designated deterministic authority for campaign lifecycle and pipeline state in the supported workflow. Those two sentences are the whole split: one side judges, the other side writes.

The scope is narrow on purpose. This boundary may create or reuse one campaign bead (one tracked unit of work in the bd issue tracker). It does not decompose the campaign into implementation beads, generate a PRP (product requirements prompt — the approved plan a campaign enters on), or advance beyond entry into create-prp. Everything downstream of that entry belongs to other commands, and nothing in this boundary reaches into them.

There are no headless advisers here either. /grill-me presents the search evidence and its recommendation directly to the User, and the User's confirmed values become inputs to the deterministic transition operation. No model gets to decide the parent epic quietly, and no script gets to guess it.

Flow diagram of the Grill handoff. At the top, the grill-me skill conducts the interview and writes a four-block draft brief with no state mutation; the brief persists at a known project build path or under working/grills. The user then chooses abandon, park, or continue, drawn as a decision diamond that fans into three columns. Abandon calls pipeline-transition.sh with decision abandon, which rejects an already linked brief and otherwise archives it, leaving no bead mutation and no pipeline state. Park and continue each begin with read-only bd discovery in the skill and user confirmation of the duplicate and parent choices, then call pipeline-transition.sh, which creates or reuses one campaign, links it once, and either defers it or leaves it open. The producing session stops at the open campaign. A separate fresh session runs init, which validates the campaign, brief, blocks, and state; a dashed red edge shows validation failure returning to the open campaign unchanged. On success the flow reaches create-prp entry, which verifies and supplies the exact persisted brief. A footer band states that pipeline-transition.sh owns handoff, archive, initialization, and entry verification, that the brief never stores a bead or session backlink, and that decomposition into issues is outside this flow.
Fig 04.1 The programmatic flow. The skill interviews and searches; the User decides; one script performs every lifecycle and pipeline write.

Layer 2 - Mechanics

Who owns what

Five actors touch this boundary, and each one is defined as much by what it is refused as by what it does.

Actor responsibilities across the Grill handoff
Actor Owns Does not own
/grill-me Interview, four-block draft brief, read-only bd search, and parent and duplicate proposals. Bead lifecycle or pipeline mutations.
User Confirm parent, duplicate handling, and continue, park or abandon. Repairing state by hand.
pipeline-transition.sh Grill handoff Create or reuse the confirmed campaign, apply its lifecycle, archive abandonment, and verify the result. Parent or duplicate judgment.
pipeline-transition.sh init Validate a continued handoff, approve the brief, and initialize create-prp. Design-quality judgment.
Fresh consumer session Invoke init and continue from persisted state. Reliance on the interview transcript.

The decision it makes

Semantic authority and write authority are held by different actors, and neither borrows from the other. The skill may look at the bead graph but may not touch it. The script may change the graph but may not decide which epic is the right parent. The User sits between them and supplies the one judgment a script cannot make.

The data contract

The brief owns design content. Its frontmatter carries no bead or session backlink:

feature: <human-readable feature name>
date: <YYYY-MM-DD>
status: draft
source: /grill-me session

For continue or park, the campaign bead carries the other half of the contract:

  • the human-confirmed governing epic as its parent;
  • grill_brief set to the normalized repository-relative brief path, the sole canonical artifact pointer;
  • grill.session_id set to the producing session id, used only to enforce a fresh consumer;
  • campaign-level purpose only;
  • no implementation children; and
  • no pipeline.* metadata before init.

The link runs one way. The brief does not identify the bead; the transition receives the campaign bead ID and validates the bead's one-way pointer before pipeline entry. That is what stops two editable fields from competing to identify the same relationship, which is the usual way a pointer pair goes wrong: someone edits one side, both sides now claim to be authoritative, and nothing in the system can say which is stale.

For abandon, no campaign bead is created, reused, changed, or closed. The brief is archived and remains unapproved.

Lifecycle and entry state

Three decisions produce three terminal states, and a fourth state exists only after a different session has done work.

State machine for the Grill handoff. A single draft brief state sits at the top, marked status equals draft with no campaign or pipeline state yet. Three labelled edges leave it. The abandon edge reaches an archived brief state, described as no bead mutation. The park edge reaches a deferred campaign state, where the brief remains draft and there are no children and no pipeline state. The continue edge reaches an open campaign state, which records the producer and stops, and which carries a resume-later edge back into itself that replaces the producer and stops again. From the open campaign, an edge labelled fresh-session init passes leads down to the create-prp entry state, where the brief is approved and pipeline stage equals create-prp. A dashed red edge labelled init validation fails loops back to the open campaign with the note no state change, correct and retry, so a rejected transition creates no separate persisted state. A footer band states that the bead owns lifecycle and the one-way brief pointer, the brief owns design content, and only init approves the brief and writes initial pipeline state.
Fig 04.2 Lifecycle and entry state. A failed check rejects a transition rather than inventing a state to hold the failure in.

Continue

Creates or reuses one open campaign, records the producing session, and stops. The producing session never initializes the pipeline itself.

Park

Creates or reuses one deferred campaign and creates no pipeline state. Resuming it records the resuming session as the new producer, opens the campaign, and stops again; initialization still requires another session.

Abandon

Archives the brief and performs no bead mutation. It first rejects any brief that already has a campaign pointer, so abandonment cannot orphan a live campaign.

The Grill handoff operation checks the mechanical conditions required for its chosen lifecycle. The brief must exist, have parseable frontmatter, remain draft, occupy an agreed location, and resolve to a safe repository-relative path. Continue and park also require the User's confirmed parent and duplicate resolution; the operation creates or reuses only that campaign and verifies its status, parent, pointer, and absence of premature children or pipeline state. It does not judge the brief's design.

What init checks before it writes

pipeline-transition.sh init performs the stronger entry check for continue. Before writing any state, it verifies that:

  • a consumer session ID is supplied and differs from the bead's grill.session_id;
  • the bead's repository-relative grill_brief pointer resolves safely to a parseable, draft brief in an agreed location;
  • all four /grill-me blocks are present and non-empty;
  • the campaign is open, has a governing parent, has no implementation children, and has no existing pipeline.* state; and
  • the manifest declares create-prp as its entry stage.

Only after all five hold may init proceed, and then it proceeds in a fixed order:

  1. Approve the brief.
  2. Record approval and the entry artifact.
  3. Initialize the pipeline at create-prp.
  4. Append the transition audit record.
  5. Read the result back.

The fresh session then passes the exact campaign ID and resolved brief path to /create-prp. That command verifies both against pipeline.entry_artifact and grill_brief through the read-only entry-context operation before consuming the four blocks. It does not rediscover the entry brief by filename, which is the failure this check exists to prevent: a filename match is a guess dressed as a lookup, and it will happily bind the pipeline to the wrong brief. These checks establish structure and sequence, not design quality.

Invariants and recovery

The invariants fall into three groups. First, single ownership: the supported workflow routes all Grill lifecycle and pipeline writes through pipeline-transition.sh, and no second helper owns part of the same state. The campaign bead's repository-relative grill_brief metadata is the only canonical link. /grill-me may search beads, but it does not create, reuse, defer, close, or add metadata to them directly.

Second, one writer at a time. Handoff operations for the same brief hold an exclusive local lock while checking and writing campaign state, which prevents two concurrent creators from both observing an unlinked brief and both deciding to create the campaign. Only init changes the brief from draft to approved and writes the initial pipeline state, and init rejects the session that produced the campaign handoff.

Third, failure is inert. Handoff and initialization operations validate their resulting state and are safe to retry against the explicitly selected campaign. A failed check reports the contradiction and does not guess, create a replacement campaign, or advance the pipeline. Retry is the recovery path, and it is a safe one precisely because a rejected transition leaves nothing behind to clean up.

What the session check does not prove

The session check compares the producer identifier recorded at handoff with the consumer identifier supplied by the new session. It fails closed when either is missing or they match, but it is not cryptographic identity proof: a caller able to forge --session-id or mutate bead metadata can bypass it. When this boundary was built, bd did not block direct writes to the reserved metadata namespace; a guard hook now denies them (see the single writer).

Turning the session check into identity proof requires a trusted harness identity source, which does not exist yet. This implementation makes the supported path deterministic without pretending those external controls exist.

The one idea

Humans and models make semantic judgments; programs enforce lifecycle, artifact identity, and workflow order.

Every rule on this page follows from that split. The skill can see the bead graph and cannot write to it. The script can write to it and cannot decide what belongs where. The User supplies the parent and the duplicate call, and those confirmed values become inputs rather than suggestions. When the split is kept, a wrong judgment is a wrong judgment and a wrong write is impossible; when it is blurred, every failure becomes both at once and neither is recoverable.

Layer 3 - Contract

Contract: paths, keys, and outcomes

Paths are repository-relative.

Brief frontmatter

feature: <human-readable feature name>
date: <YYYY-MM-DD>
status: draft
source: /grill-me session

Note the absence: no bead field, no session field. The brief does not identify the campaign.

Campaign bead metadata

Metadata keys written at the boundary
Key Written by Meaning
grill_brief Grill handoff, continue and park Normalized repository-relative brief path. The sole canonical artifact pointer.
grill.session_id Grill handoff, continue and park; replaced on resume Producing session id. Used only to enforce a fresh consumer.
pipeline.stage init only Set to create-prp. Absent before initialization.
pipeline.entry_artifact init only The resolved entry brief, verified by /create-prp through entry-context.

Operations

handoff --decision abandon
Rejects a brief that already has a campaign pointer; otherwise archives the brief. No bead mutation, no pipeline state.
handoff --decision park
Creates or reuses one campaign, links it once, defers it. Brief remains draft. No children, no pipeline.*.
handoff --decision continue
Creates or reuses one campaign, links it once, leaves it open, records the producer, and stops.
init
Validates campaign, brief, blocks and state; approves the brief; writes initial pipeline state at create-prp; appends the audit record; reads the result back. Rejects the producing session.
entry-context
Read-only. Verifies a supplied campaign ID and brief path against pipeline.entry_artifact and grill_brief.

Brief locations

A known project build/ path, or working/grills/<slug>.md. The path must resolve safely and repository-relative.

Invariants, verbatim

  • The supported workflow routes all Grill lifecycle and pipeline writes through pipeline-transition.sh; no second helper owns part of the same state.
  • The campaign bead's repository-relative grill_brief metadata is the only canonical link; the brief has no bead or session backlink.
  • /grill-me may search beads, but it does not create, reuse, defer, close, or add metadata to them directly.
  • Continue creates or reuses one open campaign and stops before initialization.
  • Park creates or reuses one deferred campaign and creates no pipeline state.
  • Resuming a parked campaign records the resuming session as the new producer, opens the campaign, and stops; initialization still requires another session.
  • Abandon archives the brief and performs no bead mutation; it first rejects any brief that already has a campaign pointer.
  • Handoff and initialization operations validate their resulting state and are safe to retry against the explicitly selected campaign.
  • Handoff operations for the same brief hold an exclusive local lock while checking and writing campaign state, preventing two concurrent creators from both observing an unlinked brief.
  • Only init changes the brief from draft to approved and writes the initial pipeline state.
  • init rejects the session that produced the campaign handoff.
  • A failed check reports the contradiction and does not guess, create a replacement campaign, or advance the pipeline.