feat(prompt): make the plan handoff a versioned, validated contract
helexa#179's application-owned system prompts let tireless shape both ends of the cc->oc handoff, so the "will a 27B execute an Opus plan" risk becomes a tunable rather than a hope. Three prompts, versioned as one set: plan.cc.md tells Claude Code it is writing for a literal, absent reader; implement.oc.md tells OpenCode to execute exactly that and report rather than improvise; implement.cc.md covers unplanned issues. PromptSet::load refuses a mismatched set, and tests assert the prompts mention every section ChildSpec requires. The middle is validated, not trusted. plan::validate rejects a plan before any implementation job is enqueued unless every child carries a runnable acceptance command (a stopping condition) and a non-empty out-of-scope list (a boundary) -- the two sections a small model needs and a human reader does not. Dangling and cyclic dependencies are caught too, and implementation_order derives the start order. cc uses --append-system-prompt, never --system-prompt: replacing Claude Code's default discards the tool-use scaffolding that makes it a coding agent. Pin bumped to 2.1.220, the version this flag surface was verified against. The oc path depends on helexa#179's passthrough guarantee, which is still open and unverified for qwen3 arch templating. Stage 5 now opens with a PONG probe rather than debugging it through a failed implementation run. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DHhHtohxcdk1PL3tfnYJdH
This commit is contained in:
@@ -95,6 +95,65 @@ This also produces a pleasing economic shape. The subscription is spent on the
|
||||
scarce thing (planning, and interpreting under-specified work), while the bulk
|
||||
of mechanical implementation runs on hardware already sitting in the office.
|
||||
|
||||
### 2.4 The plan contract
|
||||
|
||||
The handoff between the lanes is the load-bearing interface in this design: an
|
||||
Opus-authored plan must be specific enough for a 27B model to execute alone. That
|
||||
is not left to chance in either direction.
|
||||
|
||||
**Both ends are shaped by system prompts.** `prompt/plan.cc.md` tells Claude Code
|
||||
it is writing for a literal, competent, absent reader that cannot ask questions
|
||||
and will fill any gap with an invention. `prompt/implement.oc.md` tells OpenCode
|
||||
to execute the specification faithfully, stop when acceptance passes, and report
|
||||
rather than improvise. They are two halves of one contract and are versioned
|
||||
together — see `prompt/readme.md`.
|
||||
|
||||
The surfaces are not symmetric, and the difference matters:
|
||||
|
||||
| Lane | Mechanism | Ownership |
|
||||
| --- | --- | --- |
|
||||
| Claude Code | `--append-system-prompt` | Anthropic owns the base prompt; tireless appends |
|
||||
| OpenCode | `AgentConfig.prompt` | tireless owns the whole prompt |
|
||||
|
||||
Claude Code also offers `--system-prompt`, which replaces its default outright.
|
||||
tireless does not use it: the default carries the tool-use and repository
|
||||
navigation scaffolding that makes Claude Code a coding agent, and discarding it
|
||||
yields a less capable agent rather than a more obedient one.
|
||||
|
||||
**The middle is validated, not trusted.** A plan is parsed into
|
||||
`tireless_entities::ChildSpec` and checked by `tireless_core::plan::validate`
|
||||
before any implementation job is enqueued. Every child must carry five sections,
|
||||
two of which exist specifically because a small model needs them and a human
|
||||
reader does not:
|
||||
|
||||
- **Acceptance** must include at least one runnable command. Without a stopping
|
||||
condition a literal implementer does not know when it is finished, and keeps
|
||||
going — usually by rewriting adjacent code it was not asked to touch.
|
||||
- **Out of scope** must be non-empty. Without a declared boundary nothing stops a
|
||||
capable model expanding the work.
|
||||
|
||||
Plans are also checked for dangling and cyclic dependencies, and
|
||||
`implementation_order` derives the order in which children may be started.
|
||||
|
||||
This makes the risk cheap to discover. A plan that fails validation costs one
|
||||
comment and a `tireless/blocked` label, seconds after the planning run. The same
|
||||
plan unvalidated costs an OpenCode run, a branch, and an operator's review
|
||||
attention before anyone notices the spec was unusable.
|
||||
|
||||
**Dependency: helexa faithful passthrough.** The OpenCode system prompt reaches
|
||||
the model via OpenCode → cortex → neuron, which relies on helexa's guarantee of
|
||||
no injection, no rewriting, no defaults
|
||||
([helexa/helexa#179](https://git.lair.cafe/helexa/helexa/issues/179)).
|
||||
|
||||
That issue is **open** — the principle was decided 2026-07-17, but verification
|
||||
and documentation are outstanding, and two unchecked items bear directly here:
|
||||
neuron chat templating applying the system role correctly per arch family
|
||||
*including `/no_think` interaction* (Qwen3.6-27B is the target model), and
|
||||
behaviour with multiple system messages. Stage 5 is effectively the second
|
||||
consumer of that guarantee after the chat SPA, and is well placed to surface
|
||||
exactly those bugs. Debugging order follows from this: if an OpenCode run behaves
|
||||
as though it never saw its prompt, suspect the passthrough before the prompt.
|
||||
|
||||
---
|
||||
|
||||
## 3. Constraints
|
||||
@@ -382,12 +441,19 @@ Spawn the pinned CLI, read `stream-json`, capture session id for `--resume`,
|
||||
record `apiKeySource` as billing mode, consume `rate_limit_event` into the
|
||||
governor, enforce budgets and the circuit breaker.
|
||||
|
||||
Apply `prompt/plan.cc.md` via `--append-system-prompt`; parse the result into
|
||||
`PlanSpec` and gate it through `plan::validate` before creating any issue.
|
||||
|
||||
First real capability: `tireless/plan` on a real issue produces an epic and child
|
||||
issues.
|
||||
|
||||
*Done when:* a planning run completes against a real issue, the children are
|
||||
sensible, the journal shows the billing mode, and an artificially lowered window
|
||||
budget demonstrably holds the lane.
|
||||
*Done when:* a planning run completes against a real issue, the plan passes
|
||||
validation, the children are sensible **when read as an implementer would read
|
||||
them** — cold, with no other context — the journal shows the billing mode, and an
|
||||
artificially lowered window budget demonstrably holds the lane.
|
||||
|
||||
This is also where plan quality is judged, while the only cost of a bad plan is a
|
||||
comment thread. Iterate on `prompt/plan.cc.md` here, not in stage 5.
|
||||
|
||||
*Why planning first:* the output is issues, not code. A bad plan is a comment
|
||||
thread; a bad implementation is a branch. Start where mistakes are cheapest.
|
||||
@@ -404,11 +470,19 @@ than duplicates.
|
||||
### Stage 5 — OpenCode executor
|
||||
|
||||
Spawn `opencode serve` on loopback with a per-spawn password, drive it over HTTP,
|
||||
target helexa cortex. Enforce `assert_not_anthropic` from config. Route
|
||||
plan-descended implementation jobs here.
|
||||
target helexa cortex. Enforce `assert_not_anthropic` from config. Register a
|
||||
custom OpenCode agent carrying `prompt/implement.oc.md` as its system prompt.
|
||||
Route plan-descended implementation jobs here.
|
||||
|
||||
**Start with a passthrough probe.** Before wiring anything real, confirm a system
|
||||
prompt reaches Qwen3.6-27B intact through OpenCode → cortex → neuron — the
|
||||
"reply only with the word PONG" test named in helexa#179. That guarantee is not
|
||||
yet verified (§2.4), and discovering it fails here is far cheaper than debugging
|
||||
it through a failed implementation run.
|
||||
|
||||
*Done when:* a child issue created by a stage-3 planning run is implemented
|
||||
end-to-end by OpenCode on the GPU fleet, with zero subscription usage.
|
||||
end-to-end by OpenCode on the GPU fleet, with zero subscription usage, and the
|
||||
run demonstrably respected its `Out of scope` section.
|
||||
|
||||
### Stage 6 — Scheduling and dashboard control
|
||||
|
||||
@@ -475,6 +549,20 @@ models implement — rests on tireless-authored plans being specific enough for
|
||||
fails, the fallback is routing more implementation to Claude Code, which costs
|
||||
subscription budget but not a redesign.
|
||||
|
||||
The plan contract (§2.4) narrows this considerably: paired system prompts shape
|
||||
both ends, and structural validation rejects a plan lacking a runnable stopping
|
||||
condition or a declared boundary before any implementation job is enqueued. What
|
||||
remains genuinely unknown is *semantic* quality — whether a plan that satisfies
|
||||
the schema is also correct and specific enough in substance. No validator
|
||||
catches a well-formed plan that is simply wrong about the codebase, and there is
|
||||
no unit test for whether a prompt produces good plans. That is measured on real
|
||||
issues in stage 3, before stage 5 spends anything on acting on them.
|
||||
|
||||
**The OpenCode prompt path is unverified end to end.** It depends on helexa#179,
|
||||
which is open (§2.4). Worth confirming with a trivial probe — a system prompt
|
||||
that measurably changes output — at the start of stage 5 rather than debugging it
|
||||
through a failed implementation run.
|
||||
|
||||
**Not yet decided:** whether a failed implementation should automatically open a
|
||||
`tireless/blocked` issue describing what it could not do, or simply comment on
|
||||
the original. Deferred to stage 4, when there is real failure data to look at.
|
||||
|
||||
Reference in New Issue
Block a user