The planner

orch.py plan (implemented in planner.py) generates ralph_orchestrator/tickets.yaml from ralph_orchestrator/SPEC.md (or --spec PATH) using the llm.planner model. If the output file exists, --force is required to overwrite it. The planner is optional: the identical file can be written by hand (see templates/tickets.example.yaml), in which case orch.py check validates it directly.

Inputs

  • The spec — the text of SPEC.md. The shipped template is a directive skeleton (Goal, Context to reuse, Acceptance criteria, Constraints, Out of scope) with the instruction to write what must be true when the work is finished.

  • The repository overview — built by planner.overview(root): the count of git-tracked files plus the first 300 (the kit’s own ralph_orchestrator/ and .ralph/ are excluded), and the head (2500 chars) of the first existing manifest among README.md, README.rst, pyproject.toml, package.json.

  • The project’s test command — project.test_cmd (or "(none configured)"), injected so the planner can align acceptance commands with the real suite.

The prompt

prompts/planner.md casts the model as a senior software architect and requires one JSON object {"tickets": […​]} where each ticket has exactly the fields documented in data-models.adoc#tickets-yaml. Its rules: 8–20 small tickets, each completable in one agent session; honest depends_on (maximise parallelism, never depend on code that does not exist yet); parallel tickets must have disjoint scopes; accept must be one real shell command that exits 0 only when the work is done — never true or echo; first ticket is design/analysis when the spec needs it; last ticket is regression + adversarial review; reuse existing code, framework, style.

The repair loop

flowchart LR A[invoke planner LLM
with spec + overview + repair notes] --> B[extract JSON tickets] B --> C{graph.validate:
duplicate ids, empty accept/scope,
unknown/self deps, cycles,
complexity ∈ S/M/L} C -->|valid| D[dump tickets.yaml
'REVIEW AND EDIT before running'] C -->|invalid| E{rounds < 3?} E -->|yes| F[append the error list
as 'repair' to the prompt] F --> A E -->|no| G[RuntimeError: planner
could not produce a valid plan]

Each round the model receives the previous round’s validation errors verbatim and must return the full JSON again. Extraction strips think blocks and takes the outermost JSON object.

Output

graph.dump_tickets writes tickets.yaml with a header comment:

# Generated by orch.py plan - REVIEW AND EDIT before running.

The plan is a suggestion with teeth: orch.py check re-validates it, orch.py graph prints it as Mermaid, and the run refuses an invalid file (INVALID tickets.yaml: with the error list).