Skip to main content

Module decompose

Module decompose 

Source
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§

DecomposeDrive
drive_decompose’s return: the validated nodes plus, when yes was passed, the paths write_dag wrote (None = dry-run preview, nothing written). The core never prints; the caller renders render_preview and the outcome.
PlannedNode
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_dag when yes, 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’s parse_report leniency. Anything else is a clear error quoting the reply’s opening, never a panic.
plan_decomposition
Run the one-shot planner turn against backend and 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 as EngineError::Backend, an unparseable reply as a clear EngineError::Other naming 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_nodes will 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>.md markdown. The result parses back cleanly through Ticket::parse (write_dag proves it per node).
validate_nodes
Validate a proposed DAG against the repo’s backlog: 1..=MAX_NODES nodes; every slug slug-valid, unique within the set, and not colliding with an existing ticket; every blockedBy edge 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 authoritative deps::detect_cycle gate inside write_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 authoritative deps::detect_cycle gate 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.