Design note — agentic-OS root layer (#479)
One-page sketch of the cross-project layer that sits above project_init.
Decision recorded in ADR-025;
this note is the buildable picture for when it is greenlit. Not built yet.
Shape (one diagram)
┌─────────────────────────────────────────┐
│ agentic-os (SEPARATE tool — stateful, │
│ may call an LLM; NOT project_init) │
│ │
│ registry │ memory-aggregate │ MCP-inv │
│ │ tier-3 RAG (=#495) │
└───────▲──────────────────▲───────────────┘
│ reads descriptor │ (pull-only, non-mutating)
┌───────────────┴───┐ ┌──────────┴────────┐ ┌─────────────────┐
│ repoA/.agents/ │ │ repoB/.agents/ │ │ pkgs/c/.agents/ │
│ config.yaml(memory)│ │ config.yaml │ │ config.yaml │
│ MEMORY.md, CAPS.md │ │ MEMORY.md, CAPS.md│ │ ... │
└────────────────────┘ └───────────────────┘ └─────────────────┘
multirepo: registry of roots monorepo: subtree glob
▲
│ project_init scaffolds each (one-way: produce → consume)
(In the diagram CAPS.md is the generated .agents/CAPABILITIES.md, abbreviated
to fit the box; config.yaml(memory) is its memory: descriptor block.)
project_init only produces the descriptor; agentic-os only consumes it.
The arrow never reverses — no callback/registration hook back into a child repo.
The aggregation contract (what the root reads per project)
Source of truth = .agents/config.yaml memory: block (#498 / ADR-024);
.agents/CAPABILITIES.md is the human mirror. All anchors invariant across tiers:
| Field | Always? | Meaning to the orchestrator |
|---|---|---|
tier |
yes (if memory) | 0–3 — selects the retrieval path (degrade-by-tier) |
stack |
yes | auto / obsidian-only / obsidian-graphify / obsidian-graphify-rag |
memory_path |
yes | .agents/memory — grep anchor; MEMORY.md is the index |
vault_path |
tier ≥ 1 | .agents/vault — human notes |
graph_path |
tier ≥ 2 | graphify-out/graph.json — code structure |
rag_endpoint |
tier 3 | per-project RAG engine, or empty (seam only — #495/#505) |
Degrade-by-tier read (the one rule every cross-project skill follows):
tier >= 3 and rag_endpoint set → query RAG, then confirm against anchors
tier >= 2 → query graph_path before grep
tier >= 0 → grep memory_path; MEMORY.md first
no memory: block → project opted out; skip retrieval
A reader written against tier 0 keeps working at tier 3 (higher tiers only add
surfaces). Discovery = glob for .agents/config.yaml — its presence is the
registration breadcrumb; no daemon, no write-back.
MVP (three read-only capabilities, then RAG)
- Registry — list of roots (multirepo) or subtree glob (monorepo).
- Memory aggregate — union of each
MEMORY.mdinto a global view. - MCP/tool inventory — union of each
CAPABILITIES.md. - (later) Tier-3 RAG (#495) — one cross-corpus index over all registered
projects' memory/vault/code; reads
rag_endpointto reuse a per-project engine if present. Same hard constraints as ADR-024 §4 (upstream tool, no key on the default path, derived/gitignored index).
Aggregate-by-tier (the orchestrator-side mirror of degrade-by-tier)
Each recall tier has a tier-appropriate cross-project aggregation strategy. This
is the producer→consumer mirror of the per-project degrade-by-tier read rule:
the descriptor's tier + paths (already shipped, #498) tell the orchestrator
exactly which strategy to apply per project.
| Tier | Per-project artifact | OS cross-project strategy | Cost | Engine |
|---|---|---|---|---|
| 0 auto | .agents/memory/MEMORY.md (flat facts) |
direct-merge — read + union each MEMORY.md; may cross-link facts between projects |
cheapest | deterministic, no LLM |
| 1 obsidian | .agents/vault/ (human prose) |
grep for cheap recall, or agent-synthesis to summarize the "why" across projects | grep cheap / synthesis costly | LLM only for synthesis |
| 2 graphify | graphify-out/graph.json |
federate — read each project's graph_path, union the code graphs |
moderate | graphify CLI (per-repo) |
| 3 rag | rag_endpoint (present at tier 3; may be empty until a tool is wired) |
federated (query each project's engine, merge) or central (one OS index over all corpora) | highest | a RAG tool (#495) |
Mixed-tier fleet. Projects sit at different tiers, and the strategies are
additive, not exclusive — exactly like the per-project ladder where higher
tiers only add surfaces. Every project with memory contributes its tier-0
MEMORY.md merge (the universal baseline); a tier-≥2 project also contributes
its graph, and a tier-3 project also its RAG surface. The fleet view is the
union of all those layers across all projects, not "highest tier only." Tier 1 is
high-value but high-cost (the vault holds the why, but synthesizing prose
needs an LLM) — not "least useful"; fall back to grep when you don't want to pay
for synthesis. Build order follows cost: deterministic
first (L0 direct-merge, L2 graph federation), then the LLM-assisted layers
(L1 synthesis, L3 RAG).
Engines vs scaffolder. project-init only scaffolds the seam for each tier
(the graphify/RAG setup_*.sh + the descriptor field); it never runs an index.
The orchestrator may invoke project-init to add a seam to a project that lacks
one (a delegated write), but the graphify CLI / RAG tool build the indices
and the OS federates/aggregates them.
Connection / transport model (already thought through)
Three distinct edges, three different answers — only one is a network protocol:
- OS ↔ child projects — filesystem pull, no live connection. The OS globs for
.agents/config.yaml, reads the descriptor +MEMORY.md/graph.json/vault. Projects are passive file trees: no daemon, no server, no write-back in a child (ADR-025 pull-only). The lone exception is tier-3 federated RAG, where a project may expose arag_endpointthe OS queries (that endpoint is the project's own RAG tool, not a project-init service). - OS ↔ harnesses (Claude Code / Cowork / Codex / …) — client-server via MCP. The OS is the MCP server; harnesses are clients. stdio local for the MVP; a separately-gated HTTP/streamable adapter (own auth/threat model) only if remote access is ever needed. This is the agnostic seam.
- OS ↔ other agents/orchestrators — A2A, deferred. A2A (agent-to-agent) is the right protocol if you later run specialized agents or federate across machines/users that must coordinate. Premature for a single-user local OS; the headless core + MCP doesn't preclude adding an A2A adapter later.
- p2p — out of scope. Only relevant for multi-machine/multi-user federation (several people's agentic-OSes sharing), which conflicts with the small/local/ single-user charter. If ever needed it's a sync layer over the registry, not a core concern.
So between the OS and its projects there is deliberately no client-server or p2p — it's reads. The only client-server is MCP to the harnesses; A2A and p2p are future seams, not MVP needs.
Boundary (carried from ADR-025)
Do not build the orchestrator, a registry format, a daemon, RAG, or any
write-back hook on the strength of the spike. The only project_init-side
follow-up unblocked is finishing #498 (the rag_endpoint field shipped with
505; plus a short "contract a root project reads" doc). The (a) infrastructure-OS
surface (scheduling/isolation/runtime) is explicitly out of scope.
Tier-3 default topology + engine (provisional — #495 research)
A 2026 deep-research pass (logged on #495) provisionally resolves two of the
below: default tier-3 topology = central (one OS-level index over all repos,
project-filtered — better-supported and more maintainable than federated for a
solo dev; federated stays the fallback for a project that wired its own engine via
rag_endpoint). Provisional engine = codebase-memory-mcp (on-device,
no-key, MCP-native, dual AST-graph + vectors), with LEANN as the uv-native
fallback. Because that engine is dual, the A-vs-B question leans B (one
tool could replace the Graphify rung, collapsing tiers 2+3) — but that supersedes
ADR-009 and rests on first-party quality claims, so it is gated on a hands-on
bake-off (incl. comparing its AST-graph depth against Graphify) before any build.
Open questions for the build decision (not now)
- Registry format: a hand-maintained
~/.agents/projects.tomlvs. discovery-only glob. - Does the orchestrator ship as a CLI, an MCP server, or both?
- RAG A-vs-B (#495): provisionally B (one tool replaces Graphify) — confirm on the bake-off.