Expand description
kranz decompose core (ticket: .kranz/tickets/ticket-dag-decomposition.md):
one planner turn decomposes a complex goal into a small DAG of ordinary
tickets linked by blocked-by edges — reusing the existing dependency
machinery (deps.rs satisfaction, cycle detection, work-time skip-on-failed-
blocker) instead of inventing new orchestration. The drain then executes
the DAG in dependency order under the existing claim protocol.
drive_decompose owns the call order — planner turn (plan_decomposition)
→ deterministic validation (validate_nodes) → all-or-none write
(write_dag, only when the caller passed --yes). It never prints; the
CLI renders the DAG preview (render_preview) and the result. Each
emitted ticket is an ordinary .kranz/tickets/<slug>.md: it flows through
kranz draft / kranz ticket queue exactly like a hand-written ticket —
the mission stays the atom, the DAG is the molecule.
Structs§
- Decompose
Drive drive_decompose’s return: the validated nodes plus, whenyeswas passed, the pathswrite_dagwrote (None= dry-run preview, nothing written). The core never prints; the caller rendersrender_previewand the outcome.- Planned
Node - One node of the planner’s proposed decomposition — the JSON shape the
planner prompt contracts for:
{"slug","title","priority","goal","context", "acceptanceHints":["..."],"blockedBy":["slug"]}.
Constants§
- MAX_
NODES - Hard cap on the tickets one decomposition may propose (the DAG review is a single up-front glance; past this the operator should split by hand).
Functions§
- drive_
decompose - Drive one decomposition: planner turn →
validate_nodes(the preview and the write are gated identically, so a dry run of an invalid DAG refuses just as loudly) →write_dagwhenyes, nothing otherwise. - parse_
planner_ output - Parse the planner’s reply into nodes: strict whole-text parse, then the
first-
[-to-last-]substring (prose-wrapped or fenced output) — the array-shaped twin of runner.rs’sparse_reportleniency. Anything else is a clear error quoting the reply’s opening, never a panic. - plan_
decomposition - Run the one-shot planner turn against
backendand parse the proposed DAG. Session construction mirrors the draft loop’s orchestrator turn (role model/ effort/budget, read-only orchestrator permissions); errors surface the same way — backend failures asEngineError::Backend, an unparseable reply as a clearEngineError::Othernaming the contract (never a panic). - planner_
prompt - Build the planner’s user prompt: the goal plus the exact output contract
and the validation rules
validate_nodeswill enforce deterministically. - render_
preview - Render the dry-run/pre-write DAG preview: one line per node with its
priority, its edges (or
(root)), and its title. - render_
ticket - Render one node as
.kranz/tickets/<slug>.mdmarkdown. The result parses back cleanly throughTicket::parse(write_dagproves it per node). - validate_
nodes - Validate a proposed DAG against the repo’s backlog: 1..=
MAX_NODESnodes; every slug slug-valid, unique within the set, and not colliding with an existing ticket; everyblockedByedge resolving to a slug in the proposed set or an existing ticket; no cycle within the proposed set; at least one root (a node with no blockers). Cycles that run THROUGH pre-existing ticket edges are caught by the authoritativedeps::detect_cyclegate insidewrite_dag(it reads the files, so it needs the staged write first). - write_
dag - Validate
nodes, render every ticket, write them all, then run the authoritativedeps::detect_cyclegate per new slug. Any refusal — a validation failure, an I/O error mid-write, or a cycle (including one running through pre-existing ticket edges) — rolls back every file this call wrote and returns the error loudly: the operator never ends up with half a DAG. Returns the written paths on success.