Young's Mind
06 Implementation

SolOS v3 — the staged plan

Young, 2026-10-10: "Keep our spine and adopt their essences so we don't have to rebuild from scratch. Let put together the plan and break into stage for V3."

SolOS v3 — the staged plan

Young, 2026-10-10: "Keep our spine and adopt their essences so we don't have to rebuild from scratch. Let put together the plan and break into stage for V3."

Status: THE PLAN OF RECORD. This is the single implementation plan. The three per-lane plans that preceded it (Min's, Sol's v3-staged-plan.md, Mira's mira-v3-staged-plan.md) are superseded — their unique content was folded in here on 2026-10-10 and the originals archived to 05-archive/.


0. The stage register — owner, deliverable, proof

Live Hermes is our go-to sample and reference. Stages 2–5 adopt from it — as a SPECIFICATION, not as code. Hermes ships a working A2A server, a kanban board, progressive-disclosure skills and a memory tier, but as a separate codebase we do not import from (.solos has 0 .py files). We adopt the behaviour and build our own code.

So "adopt" here means read it, match it, prove we matched it — not import it. A stage owner looking for a Hermes import will not find one.

And Stage 5 is BUILD, not adopt — cortex/memory/ does not exist (measured: cortex/ holds agents · registry · skills · standards · tasks).

StageOwnerDeliverableExit proof
0 — root + the proverMin (0.1, 0.3, 0.5) · Sol (0.2, 0.4).solos/ root with no V2 fallback · a probe harness with evidence · a built-never-wired detectorthe detector catches an unwired control; the root guard refuses a V2 path; every probe has a MUST-FAIL case
1 — the registryMinagents.yaml as THE source; every projection derived; drift test per projectiona2a_call to each of the 11 agents returns that agent's own identity
2 — A2A transportSolthe A2A spine on a2a-sdk 1.2.2 ✅ ruledInspector passes 8/8; each card matches the registry; all 9 states are spec values
3 — the task contractMin (3.1–3.3) · Sol (3.4–3.5)states → wire mapping · the stuck rule = 300 s ✅ ruleda stopped task is a state, not an absence, and the state names the cause
4 — skills + toolsMin (4.1–4.3, 4.6 · curator ✅) · Mira (store) · Sol (4.4–4.5, 4.7)the skill store and tool permissions, loaded on demanda search adds no instruction text; a load does; a heart agent cannot see a brain skill
5 — memoryMin ✅ ruled (build) · Sol (proof)the memory model (short / long)a turn that forgets to save is still captured; an empty review writes nothing
6 — cost + retentionMira (with Sol on the seam; stuck-task cost ✅)GO/ZEN ledger; GO→ZEN rollover ✅ ruled; retention as an enforced routing input; the dynamic model routerfour probes report four different things; the classifier's removal turns the probe red; the router's answer changes when the free list changes, with no file edited
7 — the executorSolHarnessDefinition · run-scoped credential · live steeringthe executor's environment contains no provider key; a correction reaches a running agent
8 — observabilitySol ✅ ruledhow we see a running V3 — metrics, audit trail, the control-in-force surfacean unwired control is visible from the running system, not only from a test
9 — backup & recoveryMira ✅ ruledthe registry, the task store and the ledger survive a lossrestore from backup into an empty root and the drift test passes

✅ Young ruled 2026-10-10 — Min owns the V3 build, plus coordination.

The code root .solos IS the repo (no solos3). D1–D5 are all CLOSED. See 03-decisions/young-rulings-2026-10-10-second.md.

Two owners per stage where a control is involved. The owner builds it; the second owner proves it. A control proven by its own author is a claim, not a proof — that is the V2 pattern this plan exists to break.


0.1 The rule the whole plan obeys

A stage is done when its proof runs — not when the code is written, and not when a test is green. That is Young's scope rule and it is the only defence against V2's failure mode: built, never wired.

And a proof is only worth what its ability to fail is worth. Every probe carries a MUST-FAIL case and a false-positive case.

**This is the plan. It keeps the spine (A2A · cortex registry · .solos/ root · GO/ZEN · 11 agents, 3 planes) and adopts 13 essences from Moyai, LiteLLM and LiteAgents rather than rebuilding them.

⚠️ AMENDED 2026-10-10 — read 01-research/hermes-as-a-subsystem.md first

Studying Hermes changed three stages from "build" to "adopt".

Our own Hermes already ships — natively, live on LAB — an A2A v1.0 server (both dialects, spec-correct states, proven by probe on port 9900), a durable task board with a stuck rule (kanban: 5 states, heartbeats, 3600 s stale), progressive-disclosure skills with a curator, and a two-tier bounded memory.

And Moyai is literally built on Hermes — its package is named hermes-workspace.

What changes:

StageWasNow
2 — A2A transportinstall a2a-sdk, build a serveradopt Hermes' A2A plugin, prove conformance — ⚠️ conflicts with step 2.1; Sol's call
3 — task contractinvent the stuck rulechoose among Hermes' three existing numbers (300 s / 60 s / 3600 s)
4 — skillsbuild the load modeladopt progressive disclosure + the curator, prove the guard
5 — memorydesign the modeladopt the two-tier model + provider ABC, prove the second mechanism

Unchanged and still ours: Stage 0 (the prover — still first), Stage 1 (the registry — Hermes has no single-source registry), Stage 6 (GO/ZEN — Hermes has none), Stage 7 (the executor — now HarnessDefinition over Hermes' own harness pattern).

The plan is not wrong. Its unit of work changed: for three stages the deliverable is no longer the mechanism, it is the proof that we adopted the mechanism correctly.


1. The shape of the plan

Eight stages in three arcs. Every stage exits on a proof, not on code.

ArcStagesWhat it establishes
A — Foundation0 · 1the root, and the one source of every fact
B — The spine2 · 3A2A transport, and the task contract
C — The capabilities4 · 5 · 6 · 7skills, tools, memory, cost, the executor

Three rules the plan obeys, each learned from a V2 defect:

  1. A stage exits on a live probe, not a green test. V2 had six controls built with zero callers, and every one was found by a human noticing.
  2. Nothing is restated. Every fact has one home; every projection is derived and drift-tested. V2's five-roster-sources defect is the reason.
  3. A stage is ordered by what it proves, not by what it contains. Stage 0 is first because nothing else can be trusted before it.

2. The essence ledger — what we adopt, and where

This is the answer to "so we don't have to rebuild from scratch." Thirteen essences, each assigned to the stage that adopts it.

#EssenceFromStage
E1Skills: search by keyword → ≤5 as metadata → content only on explicit loadMoyai4
E2Live steering — a correction reaches an agent while it worksMoyai3 · 7
E3Cost ledger at the boundary — per-requester attribution, spend cap, "a request limit is not a cost limit"Moyai6
E4HarnessDefinition(name, label, module, class, live_steering=, durable_context=)Moyai7
E5Run-scoped credential — keys stay at the relay; the sandbox gets a tokenMoyai7
E6Checkpoint boundary named; interruption recorded — where it stopped ≠ whyMoyai3
E7A second memory mechanism — capture must not depend on the model rememberingMoyai5
E8"Project runtime configuration is untrusted"Moyai0 · 7
E9validate() raises if the tools did not load — refuse to start rather than run blindMoyai0 · 4
E10Acceptance: stop a real task mid-flight and verify what remainsMoyai3
E11protocolVersion pinned per agentLiteLLM2
E12Cost at the boundary, with trace-id threadingLiteLLM6
E13Declarative profiles, validated; unknown field = errorLiteAgents1
E14The honest scope note — "tasks/cancel cannot abort the live session's in-flight turn"Hermesevery stage
E15test_portable_contract — one identical task through every harness, asserting identical message typesLiteAgents (Sol's finding)7

E15 is Sol's strongest single borrow, and it closes a real gap: without it, one harness can silently diverge in what it returns — "the four-layers-drift problem in executor form." It is the anti-drift instrument for the Arms seam, and the arms-side twin of the A2A conformance probe.

Also from Sol's review, adopted: V2 conflated two axes — which framework runs the loop and which model answers inside it. The model list was the fallback list, so choosing a model implicitly chose an executor. They are separate decisions, and Stage 7's HarnessDefinition keeps them separate.

And the credential framing, adopted: not where the secret lives but where it cannot reach — a property someone can ask about, rather than a convention.

Not adopted, deliberately: Temporal (our problem is not orchestration — it is that nothing records a stuck task), cloud sandboxes (LAB rules), and a second agent SDK on top of ours (our Arms works and its fence is verified).


3. Arc A — Foundation

Stage 0 — The root, and the instrument that proves things

Delivers: a pinned root, and the harness every later stage's proof runs in.

Must be here because: operation/runtime/ cannot be designed without a root (Sol, §4.5), and every later stage's exit is a proof — so the prover must exist first. This stage is the answer to the Tier-1 finding: six controls were built and never consulted, and nothing in the system could tell.

#StepProof
0.1core/config/paths.py — the .solos/ root, no fallback to any V2 patha reference to a V2 path raises
0.2The probe harness — runs a named probe, records pass/fail with evidencea deliberately-failing probe is recorded as failed, with its evidence
0.3The built-never-wired detector — for each declared control, assert a live caller existsit catches a deliberately-unwired control in a fixture
0.4E8/E9 guard — a harness cannot reconfigure the system it runs in; a harness that cannot see its tools refuses to starta harness with 0 tools raises instead of running
0.5Every probe gets a MUST FAIL case and a false-positive casea probe that always returns FAIL must not satisfy the suite

Owner: Min (0.1, 0.3, 0.5) · Sol (0.2, 0.4)

Proof of the stage: the detector catches an unwired control, and the root guard refuses a V2 path. Both must fail on purpose before either can pass.

0.5 is Sol's doctrine and it is the sharpest rule in the plan:"A test suite is worth exactly what its ability to fail is worth." OpenAgentsControl ships 26 MUST FAIL / MUST PASS assertions and a dedicated false-positive suite — "this addresses the concern: 'sometimes I feel it just passes even if it should fail'."V2's G4 probe inserted "self-edit-proposal" into a table and then asserted that string was present. Two of its three assertions were satisfied by their own fixtures. It could not fail.Without the false-positive direction, a probe that always returns FAIL would satisfy every negative test.

Does not: create the registry, install anything, or bind a port.


Stage 1 — The registry: one source, everything derived

Delivers: cortex/registry/agents.yaml as THE source, with every projection derived from it.

Must be here because: A2A agent cards, skills projection, tool permissions, cost instruments and the fleet roster all read it. Nothing is safe before it — this is where V2's worst live defect lived.

#StepProof
1.1cortex/registry/agents.yaml — 11 agents, all fields incl. model, billing_instrument, retentionfile parses; 11 agents; every agent has a port and a lane
1.2Derive the A2A fleet roster from ita2a_fleet reads the registry; 0 port literals
1.3Derive the Hermes a2a_agents config from ita2a_call ace returns ace's identity
1.4Derive the steward's ports and the coordinator's lanesno port or lane literal outside the registry
1.5E13 — unknown field in the registry is an errora typo'd field fails validation
1.6Drift test per projection — mutate the registry, every projection must faileach projection fails on mutation

Owner: Min

Proof of the stage: a2a_call to each of the 11 agents returns that agent's own identity. In V2 all 8 Hermes peers pointed at wrong ports and three formed a rotation — a call reached the wrong agent and returned success. A dead port fails loudly; a rotation lies. This probe is the one that would have caught it.

Does not: speak A2A, hold tasks, or run an agent.


4. Arc B — The spine

Stage 2 — A2A transport: the SDK, and conformance

Delivers: the A2A spine on the official SDK, proven by the Inspector.

Must be here because: it is the interface between Brain and Heart. The task contract (Stage 3) maps onto its wire states, so the transport must exist first.

#StepProof
2.1Install a2a-sdk; import a2a succeedsimport works
2.2Implement AgentExecutor over the V3 dispatchone agent serves via the SDK
2.3E11 — pin protocolVersion per agent from the registryeach agent's card declares its version
2.4Run the Inspector against one agentInspector passes
2.5Migrate all 8 LAB agentsInspector passes 8/8
2.6Confirm the state vocabulary against the specall 9 values are spec values

Owner: Sol

Proof of the stage: Inspector 8/8, and each agent card matches the registry. This is where V2's dialect defect dies: V2 declared PROTOCOL_VERSION = "1.0.0" and served v0.3 slash names, with three states misspelled on the wire (cancelled, input_required, auth_required). Pinning the version per agent makes the dialect a choice, not an accident.

Migration is a flag, not a rewrite: a2a.compat.v0_3.JsonRpc03Adapter accepts our exact current method names — so adopt the SDK with v0.3 compat on, prove with the Inspector, then flip to v1.0 and re-prove. Two proofs, not one leap.

Does not: own tasks, or decide what a task means.


Stage 3 — The task contract: states, wire mapping, and the stuck rule

Delivers: the task vocabulary, the wire mapping, and the rule that makes follow-up possible.

Must be here because: Sol's §4.1 — the task lifecycle has a store and states but no timeout rule. Young's requirement is "brain Unit can follow up if agent got stuck or blocked" — and follow-up without a duration is the built-never-wired pattern waiting to happen. V2 had exactly this shape: the prober trusted verdicts for 30 minutes on coverage that was 25 minutes old.

#StepProof
3.1cortex/tasks/states.yaml — local states → wire statesevery local state maps to a spec wire state
3.2Wire the mapping into the A2A layertasks/get returns spec values only
3.3The stuck rule — a declared duration, per task classa task past its duration enters stuck without a human deciding
3.4E2/E6 — a correction reaches a task while it runs; an interrupted task records why, not just wherea correction changes a running task's behaviour; an interrupted task's record names the cause
3.5E10 — the acceptance probe: stop a real task mid-flight, verify what remainsthe stopped task's state, evidence and leftovers are all as declared

Owner: Min (3.1–3.3) · Sol (3.4–3.5)

Proof of the stage: a task that stops is a state, not an absence — and the state names the cause. That is the probe V2 never had.

Note on the store: V2's core/queue.py is the one component that works correctly (atomic claim with BEGIN IMMEDIATE, a sound state machine, separation-of-duties enforcement). Cortex owns the contract; the store is an implementation detail. Relocating working code to satisfy a diagram is not a stage.

Does not: load skills, or spend money.


5. Arc C — The capabilities

Stage 4 — Skills and tools: one source, loaded on demand

Delivers: the skill store, the tool permissions, and the load model Young asked for — adopted, not rebuilt.

Must be here because: Young's rule — "skills and tools stored on cortex for agents to access and load when required, similar to Brain Unit load skill on Hermes." Moyai already implements exactly this (E1), and it settles the open question we had left between capability-gated and task-driven.

#StepProof
4.1Declare plane/capability per skillevery skill declares a plane
4.2Generate card skills from cortex/skillscard matches the declared set
4.3Filter the profile projection by planea heart agent receives no brain skills
4.4E1 — skills_search by task keywords → ≤5 as metadata only → content only after an explicit loada search returns ≤5 metadata entries and adds no instruction text; a load adds it
4.5E9 — an agent that cannot load its declared tools refuses to starta 0-tool agent raises
4.6Move the MCP tool list into the registry; declare per-agent permissiona refused tool call fails by declaration
4.7The drift probe — Sol's §4.3: the guard exists in prose, no probe is specifiedcortex and card disagree → the probe fails

Owner: Min (4.1–4.3, 4.6) · Sol (4.4–4.5, 4.7)

Proof of the stage: a search adds no instruction text; a load does; and a heart agent cannot see a brain skill.

Note: V2 served skills: [] to all eight agents and read as healthy. The 4.5 refusal is the defence: absence must announce itself.

Does not: remember anything between tasks.


Stage 5 — Memory: what an agent remembers

Delivers: the memory model — unowned in the current division (Sol, §4.2).

Must be here because: cortex/agents/ declares prose and cortex/skills/ declares skills, but nothing declares what an agent remembers between tasks. V2 ran 4 memory tiers with 40,826 rows. V3's tree has no cortex/memory/ at all. Young's architecture listed memory as a Cortex concern — short / long.

#StepProof
5.1Declare the tiers (short / long) and what belongs in eachevery memory has a declared tier and owner
5.2E7 — a second mechanism so capture does not depend on the model rememberinga turn that never calls save is still captured
5.3The anti-fabrication rules — an empty review is a normal result, not a reason to invent a memoryan empty review writes nothing
5.4The injection boundary — tool, file and web instructions are not memory directivesa tool output claiming a preference writes no memory

Owner: 🔴 NEEDS A RULING. Sol flagged it as unowned. I recommend Min — what an agent remembers is identity-adjacent, which is my lane — with Sol owning 5.2's proof, since a second capture mechanism is a control and controls are proven.

Proof of the stage: a turn that forgets to save is still captured, and an empty review writes nothing.

Does not: route models, or hold credentials.


Stage 6 — Cost and retention: the ledger, and the routing rule

Delivers: the cost ledger, the two GO/ZEN instruments, and retention as an enforced routing input.

Must be here because: Mira's rule is the strongest in the tree — anything touching vault content, credentials, task text or the registry routes only to a zero-retention model. Sol's §4.4: the rule is correct and unenforced, which is V2's exact failure pattern — a declared policy no code reads.

#StepProof
6.1The ledger: billing_instrument (none · go_allowance · zen_payg), cost_state, window_stateevery model call records its instrument
6.2E3 — per-requester attribution and a spend cap at the boundaryspend is attributable per agent; the cap stops the call
6.3E12 — trace-id threading through the boundarya call is traceable end to end
6.4Retention as a routing input — a task classification (does this touch vault content?)a zero-retention task cannot route to a non-zero-retention model
6.5Window exhaustion ≠ spend exhaustionGO's windowed allowance and ZEN's PAYG float are distinguishable

Mira's four axes, adopted verbatim — each closes a specific V2 defect:

AxisValuesThe V2 defect it closes
billing_instrumentnone · go_allowance · zen_paygV2 merged two pools with different accounting into one "spend"
cost_statemeasured · unknown — never conflatedV2 recorded 30,721 attempts at $0.00000; unknown must never read as $0.00
window_stateok · approaching · blocked — a distinct axis from spendV2's ledger "reads green up to the halt"
result_classsuccess · refusal · empty · error — never one wordV2's prose-refusal defect: a refusal recorded as success

Owner: Mira

Proof of the stage — Mira's falsifier, adopted unchanged: a probe injects each of unpriced run, refusal, empty result, window exhaustion, and the ledger must report four different things. V2 recorded one thing ($0.00000) for all of them. If all four collapse into one, the stage has failed.

And the second falsifier: the §6.4 probe goes red when the classifier is removed. A rule that cannot be broken by deleting one function is a comment, not a control.

Note the rule that came from two directions independently: "limits on requests and output do not limit the cost." Mira found it in our design; Moyai's docs say it as a warning. A request limit is not a cost limit.

Does not: decide which model is smartest — free-first is the ladder.


Stage 7 — The executor: the harness boundary, and live steering

Delivers: the Arms boundary in the HarnessDefinition shape, with run-scoped credentials and live steering.

Must be here because: it depends on the task contract (Stage 3) for follow-up and on the registry (Stage 1) for the harness list. Sol gated core/arms/ as empty, and this is the one place V3 genuinely was reinventing something Moyai has.

#StepProof
7.1E4 — HarnessDefinition(name, label, module, class, live_steering=, durable_context=)the registry declares harnesses; 0 harness literals elsewhere
7.2E5 — the executor authenticates with a run-scoped token; the provider key stays at the boundarythe sandbox's environment contains no provider key
7.3E2 — a correction reaches a running agent through its native inboxa correction changes a running agent's behaviour
7.4E8 — project runtime configuration is untrusteda project-supplied config cannot alter the harness
7.5The permission fence — writes outside the workdir auto-reject; --auto is never passeda write outside the workdir fails, rc=1

Owner: Sol

Proof of the stage: the executor's environment contains no provider key, and a correction reaches a running agent.

Note: our Arms drives /home/lab/.opencode/bin/opencode directly, and its fence is verified. This stage formalises the boundary; it does not replace the executor with an abstraction.

Does not: own sessions, memory or skills — cortex does.


6. Why this order

Stage 0  root + the prover        ← nothing can be trusted before it
   │
Stage 3a states.yaml              ← NEW: a dependency of Stage 2's proof (F1)
   │
Stage 2  A2A transport            ← needs 3a's vocabulary to prove conformance
   │
Stage 1  the registry             ← CORRECTED: 1.3b's proof needs Stage 2 (F4)
   │
Stage 3  the task contract        ← the stuck rule, the wire mapping
   │
   ├── Stage 4  skills + tools    ← needs the registry for projection
   ├── Stage 5  memory            ← **BUILD, not adopt** (F5) — `cortex/memory/` does not exist
   ├── Stage 6  cost + retention  ← needs the registry for instruments
   └── Stage 7  the executor      ← needs the task contract for follow-up

Stages 4, 5 and 6 are independent of each other and can run in parallel across lanes. Stage 7 depends on 3 and 1.

🔴 Two corrections to this graph — found by verification, not by writing it

#WasNowFound by
F1Stage 3 entirely after Stage 2states.yaml comes BEFORE Stage 2 — Stage 2's proof ("all 9 states are spec values") has nothing to check against otherwiseMira
F4Stage 1 independent of Stage 2Stage 1.3b needs Stage 2 — the identity proof is an a2a_callSol

So Stage 1 first, then Stage 2 is wrong in both directions. The corrected order is: 0 → 3a → 2 → 1 → 3 → (4·5·6·7·8·9).

Stage 0 is now the only stage with no dependency at all — which is why it is still first, and why it is still the cheapest thing to build.


7. What still needs a ruling

Five decisions from OPEN-DECISIONS.md — the plan assumes the recommendation in each. Say if you disagree and the affected stage changes:

#DecisionPlan assumes
D1Where the registry livesa declarative file (it is data, not logic)
D2Where tasks livecortex owns the contract; a database is the store
D3How a skill is assignedplane by default, per-agent override
D4SDK migrationboth, prove with the Inspector, then migrate
D5Phase 0 firstyes — Stage 0 and 1 immediately

Three more, and these are genuinely yours:

QuestionWhy it matters
Does V3 get its own repo?V3 has no code. solos2 is 735 MB of V2 history
Who owns the V3 build — Sol, or Min?Sol owns engineering; Min coordinates. This is a full build, not a patch
spec/ (18 MB of official A2A repos)currently untracked in solos2 — one git clean from gone
.solos has no remoteversion-controlled locally, nowhere else — one disk failure loses it
Observability / backup / onboardingsee what-we-havent-touched.md §6 — no owner, no stage

7.1 ✅ Four rulings applied, 2026-10-10

Young ruled the four blocking questions. Full record: 03-decisions/young-rulings-2026-10-10-four.md.

QuestionRulingApplied
Stage 5's ownerMin (build), Sol (proof)register + stage-5-memory.md
a2a-sdk vs Hermes' pluginthe official a2a-sdk 1.2.2register + stage-2-a2a-transport.md
The stuck number300 s (corrected from 3600 s)register + stage-3-task-contract.md
GO $60 / GO-before-ZENGO exhaustion rolls over to ZENstage-6-cost-and-retention.md
Observability ownerSolStage 8
Backup / DR ownerMiraStage 9
GO $60 hard or softSOFT — falls back to ZEN "instead of blocking"; the $60 is per modelstage-6-cost-and-retention.md + 03-decisions/go-allowance-and-model-selection.md

And the trace question — "how do we track GO exhaustion?" — has a measured answer: GET https://opencode.ai/zen/go/v1/usage returns rolling / weekly / monthly, each with status, percent and resetsAt. The provider supplies our window_state axis. ZEN's balance is not readable by API, so it stays cost_state: unknown — never $0.00.


8. What this plan does not do

  • It writes no code. Every stage is a design until its proof runs.
  • It does not re-plan the spine. A2A, the registry, .solos/, GO/ZEN and the 11-agent organisation are Young's rulings and are assumed.
  • It does not fork. Thirteen essences are adopted as patterns and specifications, in our own lanes, with no new dependency.
  • It does not claim any stage is done. No probe has run. A phase is done when its proof runs — not when the code is written, and not when a test is green.
Copyright © 2026