Young's Mind
04 Structure Snapshot

SolOS V3 — structure of record

Status: DESIGN. No code. find . -name '*.py' returns 0. Root: /home/lab/.solosAuthority: Young, 2026-10-10

SolOS V3 — structure of record

Status: DESIGN. No code. find . -name '*.py' returns 0. Root: /home/lab/.solosAuthority: Young, 2026-10-10

Young's brief, in order:

"Root folder .solos. Don't start any coding, just design and architecture."

"Providers: Opencode / workspace: SolOS Go / Zen. Go $10 package. Zen $40/mth credit. Optimise free models on both Go/Zen then selected paid models. Use native A2A V1.0."

"cortex: 1. registry → this where agents define and live? 2. personas → update to agents. core: protocol, config, arms, harness. operation: mcp, test, ops, state, runtime. References → keep on vault or in repo: spec, docs."


0. Reconciliation — four drafts, one tree

Mira, Sol and Min each drafted a structure; they disagreed. That is the defect this system exists to prevent, appearing in the structure itself. This is the merged form, and it now follows Young's layering:

LayerQuestion it answersOwner
cortex/what is true — identity, contract, policyMin
core/what it is built from — protocol, config, arms, harnesssplit
operation/how it runs — mcp, test, ops, state, runtimesplit
references/what we read — the spec, notes. Not part of the systemread-only

Nothing was deleted from anyone's lane. Directories were moved, and every README moved with its directory.


1. The tree

/home/lab/.solos/                 ← V3 ROOT
│
├── README.md                     what V3 is, current status
├── STRUCTURE.md                  THIS FILE — the tree, owners, gates
│
├── cortex/                       OWNER: MIN — identity, contract, policy
│   ├── registry/                   agents.yaml — WHERE AGENTS DEFINE
│   ├── agents/                     per-agent prose, GENERATED from registry
│   ├── tasks/                      states.yaml — the task contract, not the store
│   ├── skills/                     one source → two projections
│   ├── standards/                  code-quality, security, test-coverage
│   ├── memory/                     shortterm / longterm / durable / dna
│   ├── verification/               the gate
│   └── coordinator/                routing rules + lanes, derived FROM registry
│
├── core/                         OWNER: split
│   ├── protocol/                   A2A v1.0 native, on the official SDK   ← SOL
│   ├── config/                     providers, workspaces, cost policy    ← MIRA
│   ├── arms/                       GATED — no consumer yet
│   └── harness/                    probes, conformance, negative cases   ← SOL
│
├── operation/                    OWNER: split
│   ├── mcp/                        the tool surface (GATED)
│   ├── test/                       proofs
│   ├── ops/                        operational entry points
│   ├── state/                      runtime state — gitignored, never committed
│   └── runtime/                    the executor: opencode run            ← SOL
│
└── references/                   READ-ONLY reference material
    ├── spec/                       the four official A2A repos
    └── docs/                       notes that are not vault-registered

This matches disk as of 2026-10-10. cortex/memory/, cortex/verification/, cortex/coordinator/ are declared but not yet created — they are created by their owner when that owner has content, not before. An empty directory is a claim something lives in it.


2. "registry → is this where agents define and live?"

Short answer: agents define in the registry. They live as a running process described by it. Those are two different things, and conflating them is how V2 got five roster sources.

What the registry holds — the DEFINITION

cortex/registry/agents.yaml holds the facts that are shared — the ones more than one component needs to agree on:

FieldWhy it is shared
id, name, planeidentity — the card, the prose and the logs must agree
port, endpointthe A2A address — this is what drifted in V2
rolewhat the agent is for
capabilitieswhat it may be given
boundarieswhat it may not do

The test for "does this belong in the registry?" is not "is it about an agent?" It is: does more than one component need this fact? If yes, it lives here and everything derives. If no, it lives with its owner.

What the registry does NOT hold

  • Behaviour prose — the "you are X, you do not Y" text. Authored, not derived. It lives in cortex/agents/.
  • Model assignment per task — routing decides at dispatch time.
  • Runtime state — alive/dead, busy, last heartbeat. That is operation/state/, and it is disposable by definition.

Why the distinction matters

V2 had one file trying to be both: cortex/personas/AGENT_IDENTITIES held identity and generated prose and fed four projections — while cortex/registry was built as the "real" answer and never wired. Two homes for one fact, and the wrong one was live.

V3 splits it by kind:

cortex/registry/agents.yaml     the FACTS — one home, derived everywhere
        │
        ├──► cortex/agents/*.md        generated prose
        ├──► A2A agent card            generated from the same facts
        ├──► Hermes a2a_agents config  generated
        └──► steward health ports      generated

cortex/agents/ is a projection of the registry, not a second source. Edit the registry; regenerate. Never hand-edit a generated file — V2's personas drifted because SOUL.md was authored while the rest was generated, and nothing warned when the two disagreed.


3. "personas → update to agents"

Done. cortex/personas/ is now cortex/agents/.

Not cosmetic. "Persona" described the prose; the directory holds the agent definitions projected from the registry, and the word "persona" made it sound like a source of truth. It never was.

In it: one file per agent, generated, the prose an agent reads about itself. Not in it: any fact that is also in the registry.


4. Providers and workspaces

ProviderOpencode
WorkspaceSolOS — ONE workspace
GO$10 subscription, allowance cap $60 credit on model usage
ZEN$40/mth pay-as-you-go credit
Policyoptimise free models on both pools, then selected paid

CORRECTED 2026-10-10 — this said "Workspace 1 / Workspace 2". It is ONE workspace with two POOLS. A workspace is the billing and routing boundary, so one workspace means one routing scope; GO and ZEN are pools inside it, not two places work is sent. See SolOSV3/03-decisions/young-corrections-2026-10-10.md §1.

The ladder is free-first, and it is not a preference — it is measured. In V2, one day: $10.45 spent, of which $10.34 (99%) was one paid model (qwen3.8-flash). On a correctness-controlled comparison all four models answered correctly; the paid one's entire justification was 4 seconds faster. The free model did 7× the work at $0.00.

Two accounting models in one scope. GO is a subscription with a $60 allowance cap; ZEN is PAYG. The ledger must record which pool a call drew on, and the two cannot share one dollar cap — a subscription allowance and a PAYG balance are not the same quantity. monthly_cap_usd stays 0.0; it cannot represent either.

Full policy, tier ladder, retention rules and ledger spec: core/config/COST-POLICY.md (Mira's lane).

A cost rule that is not about money

Retention is a routing input, and it outranks cost. Anything touching vault content, credentials, task text or the registry routes only to a zero-retention model, or to no model at all. A model that may train on our task text is not free — it is an undisclosed disclosure. The cost of that rule is a narrower catalogue, and it is accepted.


5. A2A — native v1.0

Native v1.0 means PascalCase method names, not the slash names. V2 declared PROTOCOL_VERSION = "1.0.0" while serving v0.3 names:

V2 servedA2A v1.0 native
message/sendSendMessage
message/streamSendStreamingMessage
tasks/getGetTask
tasks/cancelCancelTask
X-A2A-VersionA2A-Version

A client trusting the declared version dials SendMessage and gets -32601 MethodNotFound. The declaration and the implementation disagreed, and nothing checked one against the other.

V3 builds on the official a2a-sdk, which is v1.0-native. The SDK's v0.3 compat adapter accepts our current names — that is available as a migration path, not as the target. The target is native v1.0.


6. References — vault or repo?

Both, split by purpose:

WhatWhereWhy
Design, decisions, lessonsvault (SolOS/vault/Docs/SolOSV3/)the vault is the authority; it has history and two remotes
references/spec/repothe four official A2A repos — code we build against, pinned to a revision
references/docs/reponotes not yet argued for. Anything that survives review moves to the vault

The rule: a note in references/docs/ is a note that has not been argued for yet. The vault is where it goes when it has.

references/spec/ must be TRACKED. In V2 it was 18 MB of official repos, untracked — one git clean from gone, which is why the protocol got hand-rolled in the first place. Track it as a submodule pinned to a revision, so "which SDK did we build against?" is answerable.


7. Lanes

LaneOwnerDirectories
REGISTRY & CONTRACTMincortex/registry, cortex/agents, cortex/tasks, cortex/standards
TRANSPORT & EXECUTIONSolcore/protocol, core/harness, operation/runtime, references/spec
COST & PROVIDERSMiracore/config
GATED—core/arms, operation/mcp — until a consumer exists
UNASSIGNED—operation/test, operation/ops, references/docs

The rule: an agent does not write outside its lane. A finding in another lane goes to references/docs/ and names the owner — that is a complete contribution.

Why: two authors writing one fact is how V2 got five roster sources, and it is how SCOPE.md was overwritten in this folder's first hour.


8. Deliberately gated: core/arms/ and operation/mcp/

Both are in the tree, both empty and gated. Recording why, because an empty directory invites a session to fill it:

Fact (V2, measured)Value
Production importers of arms/mcp_server.py0
Measured usage0.03%

Min's position is architectural (A2A and MCP do different jobs). Sol's is empirical (no consumer; building it repeats built-never-wired). Not reconcilable by evidence; Young has not ruled.

So the directories exist as the declared home, hold nothing, and are not filled until either a real consumer appears, or Young rules MCP stays.


9. Absent, deliberately

AbsentWhy
orchestrator/retired in V2 — duplicated routing already done at submit, zero live importers
workflows/V2 README-only husk. A directory is a claim something will live in it
bin/replaced by operation/ops/ — a Unix convention with no owner implied
a solos_paths fallbackV2's resolver carried a fallback to a legacy DB, which is how two live DBs happened

Absence here is load-bearing. If a later session finds one missing and builds it, that session has re-introduced a husk.


10. The trap in core/paths.py — read before the first line

V2's core/solos_paths.py resolved the root with a guard and no fallback, because "a guessed root is how two live DBs happened." V3 keeps the guard.

But the root changes in V3 — /home/lab/.solos, while V2 is /home/lab/solos2 with its own databases under solos2/state/ and solos2/board/. Measured:

/home/lab/solos2/state/tasks.db      /home/lab/solos2/board/tasks.db
/home/lab/solos2/state/solos2.db     /home/lab/solos2/board/solos2.db
/home/lab/solos2/cortex/cortex.db

Five databases already. V3 will add a sixth under operation/state/. So the guard must fail closed on any undeclared root, and V3 must never fall back to a V2 path, in either direction.

This is why operation/state/ is in the tree and gitignored. It is the one directory whose contents must never be committed.


11. Status — DESIGN ONLY

No code. find . -name '*.py' returns 0.

The one data file is cortex/registry/agents.yaml — 11 agents, 3 planes, the roster extracted from V2's live sources. Design input, not code.

Not implemented: every phase. Open: the five decisions in SolOSV3/03-decisions/OPEN-DECISIONS.md, plus the MCP ruling in §8.

Copyright © 2026