Young's Mind
Manual

02 Design

This is a COPY for the operator's reading room. The original (above) is the authority — if they ever disagree, the original wins and this file is regenerated. Generated 2026-10-12 18:27 by mira. Consolidated. This note's findings now live in ../MIN-STUDIES.md §1, 2, 12. The original is kept for its measurements; do not re-derive from it.

SolOS v3 — design of record

Status: DESIGN. No V3 code exists. Every V2 fact cited here was verified live on 2026-10-10. Every V3 item is a proposal.


1. Why a fresh start rather than a V2 patch

V2 works in parts — the A2A transport is conformant, the executor runs, the queue state machine is sound. But its defects are not local; they are structural, and patching them means fighting the structure that produced them.

V2 defectWhy a patch is the wrong fix
Roster in 5 placesRemoving one duplicate leaves four; the shape regenerates them
Two skill systemsNeither is wrong alone; they exist because no registry owns skills
Task state in core, identity in cortexOwnership is split by accident of history, not design
Hand-rolled A2AEvery spec change is ours to track; the SDK exists
cortex/registry built, never wiredIt was built as the answer and had no consumer

The last one is the tell: V2 already contained the fix and did not connect it. V3 starts from the registry outward rather than adding it later.


2. The design

2.0 The root — .solos/ IS the system

Young, 2026-10-10: "Root folder .solos. Don't start any coding, just design and architecture."

.solos/ is the root, not a folder inside a repo. It is the whole system.

The tree — flat, and this is the resolved form. Three of us drafted a structure and produced three different ones, which is the defect this system exists to prevent appearing in the structure itself. Reconciled here against §2.3, which is the authority on what cortex owns:

.solos/                     <- ROOT
  cortex/                   THE REGISTRY LAYER (owner: Min)
    registry/               identity, port, lane, skills, tools  ← agents.yaml
    personas/               generated FROM registry, never hand-edited
    tasks/                  states.yaml — the task CONTRACT, not the store
    memory/                 4 tiers: shortterm / longterm / durable / dna
    skills/                 one source → two projections
    standards/              code-quality, security-patterns, test-coverage
    verification/           the gate — promotes on evidence, never claims
    coordinator/            routing rules + lanes, derived FROM registry

  core/                     primitives everything imports (Min + Sol, split)
    paths.py                the root guard              ← Min
    queue.py                the task STORE              ← Sol

  protocol/                 the A2A spine on the official SDK   ← Sol
  runtime/                  the executor: opencode run         ← Sol
  spec/                     the four official repos, tracked    ← Sol

  config/                   providers, cost policy, budgets     ← Mira
  arms/                     GATED — no consumer yet (see §5)
  harness/                  probes, conformance, negative cases ← Sol
  tests/                    proofs                            ← unassigned
  ops/                      operational entry points          ← unassigned
  state/                    runtime state — gitignored, never committed
  docs/                     decisions and reports             ← Min

Why cortex/ is a layer and not a single directory. §2.3 already said cortex owns eight kinds of fact. A flat registry/ beside a flat skills/ scatters one owner's material across the root, which is how a registry stops being one place. Cortex is the owner's boundary; the kinds live inside it.

Why protocol/ and not a2a/. a2a/ names the protocol, not the role. V2 had both a protocol/ and an a2a/ and they drifted. One directory, named for its function.

Absent, deliberately — and the absence is load-bearing:

AbsentWhy
orchestrator/retired in V2 — duplicated routing done at submit, zero importers
agents/identity is data in cortex/registry, not a directory of markdown
workflows/V2 README-only husk. A directory is a claim something lives in it.

If a later session finds one of these missing and builds it, that session has re-introduced a husk.

The import constraint this resolves. Code under .solos/ as a subdirectory could never be imported — a leading dot is not a valid Python identifier ('.solos'.isidentifier() is False). As the root, the directory's name never appears in an import: packages are cortex, core, protocol, runtime, and import cortex works from the root.

Verified: cd /home/lab/.solos && python3 -c "import sys; sys.path.insert(0,'.'); import cortex" → OK.

The rule, stated precisely (it is narrower than "nothing may touch .solos/"):

In .solos/May be importedMay be read by path
cortex/registry/agents.yaml, other dataneveryes — via a loader accessor
lane-contract README.mdneveryes
Python sourceyes — as cortex.*, core.*—

2.1 Shape

                          ┌────────────────────────────────────────┐
                          │  HERMES  (Brain)                       │
                          │  native A2A client                     │
                          │  a2a_call · a2a_discover · orchestrate │
                          │  Arms: opencode CLI, direct            │
                          └────────────────┬───────────────────────┘
                                           │  A2A / JSON-RPC
                                           │  (the one interface)
              ┌────────────────────────────┼────────────────────────────┐
              ▼                            ▼                            ▼
        ┌──────────┐                 ┌──────────┐                 ┌──────────┐
        │   ace    │                 │  atlas   │                 │  alpha   │  HEART
        └────┬─────┘                 └────┬─────┘                 └────┬─────┘
             └────────────────────────────┼────────────────────────────┘
                                          │
                          ┌───────────────▼────────────────┐
                          │  ARMS                          │
                          │  opencode run (direct)         │
                          │  MCP server (agent↔tools)      │
                          └───────────────┬────────────────┘
                                          │
                          ┌───────────────▼────────────────┐
                          │  CORTEX  —  THE REGISTRY       │
                          │  identity · port · lane ·      │
                          │  skill · tool · task vocab     │
                          │  memory                        │
                          └────────────────────────────────┘

Two interfaces, deliberately different:

A2AMCP
betweenagents (Brain ↔ Heart)an agent and its tools
transportJSON-RPC over HTTP, one port per agentstdio, spawned by the client
unita task with a lifecyclea tool call

They are not redundant and must not be merged. A2A carries work; MCP carries a single agent's access to its tools. Merging them loses the task lifecycle.

2.2 The registry is the load-bearing change

Every shared fact has exactly one home. Everything else derives at runtime.

cortex/registry/agents.yaml     ← THE registry (one file, all 11 agents)
        │
        ├──► A2A fleet (ports, endpoints)
        ├──► agent cards (name, skills, capabilities)
        ├──► Hermes a2a_agents config
        ├──► steward health ports
        ├──► coordinator lanes
        └──► profile projections (personas, skills)

Nothing restates it. A drift test per projection: mutate the registry, watch each projection fail. That is what makes "one home" enforceable rather than aspirational.

2.3 Cortex owns

KindHomeNotes
identitycortex/registryname, role, plane, pronouns, capabilities
portcortex/registryassigned, never discovered
lanecortex/registrywhich agent may touch which path
skillscortex/skillsone source, projected twice (§2.5)
toolscortex/registrygenerated into the MCP tool list
task vocabularycortex/tasksthe contract, not the store (§2.4)
memorycortex/memory4 tiers, carried from V2 unchanged
standardscortex/standardscarried from V2's context/standards

2.4 Task state — the contract, not the store

V2 has two state machines with no bridge: core/queue.py (8 states) and the A2A wire (9 spec states). Three local states have no wire equivalent.

V3 owns the vocabulary and the mapping in cortex, as one declared thing:

cortex/tasks/states.yaml
    submitted   → submitted          (identity)
    working     → working            (identity)
    verified    → working            (INTERNAL — not a wire state)
    completed   → completed
    failed      → failed
    cancelled   → canceled           (spec spelling, one L)
    uncertain   → working            (interrupted, not terminal)
    blocked     → input-required     (a human must act)
    rejected    → rejected

Decision (proposed, needs Young): the store stays a database; the contract lives in cortex. Rewriting a working state machine to relocate it would risk the one component V2 got right.

2.5 Skills — one source, two projections

V2's defect: the agent card advertised 3-5 differentiated skills per agent, while every profile received all 67. Two systems, no shared source.

cortex/skills/<skill>/SKILL.md     ← the single source
        │   each declares: planes, capabilities
        │
        ├── filter by plane ──► A2A agent card      (per-agent, differentiated)
        └── filter by plane ──► profile skills/     (per-agent, differentiated)

Plane as the default, with a per-agent override for exceptions.

2.6 A2A adoption — SDK, CLI, Inspector

ToolRole
a2a-sdkBuild on it. Replaces the hand-rolled JSON-RPC layer
a2a-cliOperate with it. Manual dispatch and debugging
a2a-inspectorStay compliant with it. The gate

Hand-rolling means owning JSON-RPC parsing, SSE streaming, card serving, error-code mapping, content-type negotiation (application/a2a+json) and version negotiation. The SDK owns all of it — and V2's hand-rolled version already produced 3 non-spec state values, which is the class an official implementation does not have.

The Inspector is the durable part. Run it against the fleet as a check, and "are we compliant?" stops being a question.

Hermes already speaks A2A natively (a2a_call, a2a_discover, a2a_orchestrate, a2a_list, a2a_history). The Brain's A2A client exists; V3 only has to point it at the right ports — which V2 did not.

2.7 V2 is speaking a dialect, not v1.0 — CORRECTED

This section was wrong in the first draft of this document. Sol found it; I verified it against the SDK's own dispatcher. Recording the correction because the error is instructive.

V2 declares PROTOCOL_VERSION = "1.0.0" and serves message/send. A2A v1.0 JSON-RPC uses PascalCase method names matching the gRPC service:

V2 servesA2A v1.0 JSON-RPCSource
message/sendSendMessagespec §9.1; SDK jsonrpc_dispatcher.py:115
message/streamSendStreamingMessagesame
tasks/getGetTasksame
tasks/cancelCancelTasksame
tasks/resubscribeSubscribeToTasksame

Spec §9.1: "Method Naming: PascalCase method names matching gRPC conventions (e.g., SendMessage, GetTask)."

Verified in the SDK's code, both directions:

src/a2a/server/routes/jsonrpc_dispatcher.py:115   'SendMessage': SendMessageRequest,
src/a2a/client/transports/jsonrpc.py:73           method='SendMessage',

So a client that trusts V2's declared version dials SendMessage and receives -32601 MethodNotFound. The declared version and the implemented dialect disagree — which is worse than declaring the old version honestly.

Two further mismatches from the same check:

V2A2A v1.0
Version string"1.0.0""1.0"
Version headerX-A2A-VersionA2A-Version

Why this matters for V3. It is not a bug to patch — it is the reason the SDK adoption in §2.6 is load-bearing rather than a convenience. A hand-rolled transport let the declared version drift from the implemented one, and nothing caught it because the only consumer was itself.

Phase 1's proof must include this: after migration, the Inspector validates the card, and a2a-cli — which speaks real v1.0 — completes a task end to end.


3. What V3 inherits from V2

Inherited deliberately, because it was right:

ItemWhy
The 11-agent, 3-plane organisationthe organisation was never the problem
The port assignmentsassigned, not discovered; already canonical
The queue state machinecore/queue.py is sound — atomic claim, separation of duties
Memory: 4 tiersshortterm/longterm/durable/dna, live, 40,826 rows
The executoropencode run, permission boundary verified
The separation-of-duties rulesan agent may never verify its own work
The standards layercontext/standards — code-quality, security, test-coverage
The two failure patternsbuilt-never-wired · reports-something-untrue

4. What V3 leaves behind

Left in V2Why
The hand-rolled A2A serverthe SDK is the spec's own implementation
Five roster sourcesone registry, everything derives
Two skill systemsone source, two filtered projections
The dual state machineone contract with a declared mapping
cortex/registry as an orphanit becomes the registry and gets consumers
The retired components (orchestrator, G5, brain workers, fleet.json)already retired in V2; V3 never builds them

5. Implementation plan

Each phase has a proof — the live probe that says it worked. Nothing is done until its proof runs.

Phase 0 — the registry (nothing else is safe before this)

#StepProof
0.1Write cortex/registry/agents.yaml — 11 agents, all fieldsfile parses; 11 agents
0.2Derive the A2A fleet from ita2a_fleet reads the registry, no literals
0.3Derive the Hermes a2a_agents config from ita2a_call ace returns ace's identity
0.4Derive the steward's ports, coordinator's lanesno port or lane literal outside the registry
0.5Drift test per projectionmutate the registry; each projection fails

0.3 is the V2 defect V3 must not reproduce. In V2, all 8 Hermes peers pointed at wrong ports and three formed a rotation — a call reached a different agent and returned success.

Phase 1 — adopt the SDK

#StepProof
1.1Install a2a-sdkimport a2a succeeds
1.2Implement AgentExecutor over the V3 dispatchone agent serves via the SDK
1.3Run the Inspector against itInspector passes
1.4Migrate all 8 LAB agentsInspector passes 8/8
1.5Confirm the state vocabulary against the specall 9 values are spec values

Phase 2 — skills, one source

#StepProof
2.1Declare plane/capability per skillevery skill declares a plane
2.2Generate card skills from cortex/skillscard matches the declared set
2.3Filter profile projection by planea heart agent receives no brain skills

Phase 3 — tools

#StepProof
3.1Move the MCP tool list into the registryMCP server reads it
3.2Declare per-agent tool permissiona refused tool call fails by declaration

Phase 4 — task contract

#StepProof
4.1Write cortex/tasks/states.yamlevery local state maps to a wire state
4.2Wire the mapping into the A2A layertasks/get returns spec values

6. Not established

  • V3 has no code. No solos3 repo exists. This is a design.
  • The SDK has not been installed or run; §2.6 is read from spec/sdk and the SDK's README.
  • The Inspector has not been run against anything. Its role is described from its README.
  • The Phase 0-4 plan is a proposal; ordering and scope need Young's ruling.
  • Five decisions are open — see 03-decisions/OPEN-DECISIONS.md.
Copyright © 2026