feat(prompt): make the plan handoff a versioned, validated contract
Some checks failed
deploy / build (push) Has been cancelled
deploy / deploy (push) Has been cancelled

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:
rob thijssen
2026-08-02 15:26:05 +03:00
parent 4e42f87576
commit 7b8308d34e
19 changed files with 1053 additions and 8 deletions

View File

@@ -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.