# Radial — runtime architecture # Render: d2 --sketch --layout elk docs/architecture.d2 docs/architecture.svg vars: { d2-config: { sketch: true pad: 40 } } classes: { actor: { shape: person style: {fill: "#f4f1ea"; stroke: "#4b4b4b"} } proc: { style: {fill: "#ffffff"; stroke: "#333333"; stroke-width: 2} } store: { shape: cylinder style: {fill: "#f0f4f8"; stroke: "#33556b"} } ephemeral: { style: {fill: "#fff8e6"; stroke: "#8a6d1f"; stroke-dash: 4} } ext: { style: {fill: "#f5f0f6"; stroke: "#6b4b78"} } note: { shape: text style: {font-size: 13; italic: true} } } human: Human {class: actor} # ───────────────────────────────────────────────────────────── ui: "Web UI · @radial/ui" { style: {fill: "#fbfbfd"; stroke: "#333"; stroke-width: 2} tooltip: Browser SPA (SvelteKit, static adapter). Materializes independently of the daemon. auth: "atproto OAuth\n(browser client)" {class: proc} fold: "materialize()\n@radial/core" {class: proc} views: "goals · artifact cards\nin-flight · awaiting\nSystem section" {class: proc} write: "write.ts\nrecord writes" {class: proc} auth -> write fold -> views views -> write: "request buttons" {style.font-size: 12} } # ───────────────────────────────────────────────────────────── net: "atproto · the bus" { style: {fill: "#eef4ef"; stroke: "#3d6b4c"; stroke-width: 2} tooltip: No central coordinator. Every actor writes only to its own repo. hrepo: "Human PDS repo" {class: store} arepo: "Agent PDS repo" {class: store} ns: "com.disnetdev.radial.*\nspace · addMember/removeMember · join\nproject · goal · artifactType\nartifactRequest · claim · artifact\nreview · checkrun · message · agent" {class: note} } # ───────────────────────────────────────────────────────────── daemon: "Daemon · radiald (operator's machine)" { style: {fill: "#fdfbf7"; stroke: "#333"; stroke-width: 2} ingest: "@radial/ingest\nRepoPoller · SpaceIngestor" {class: proc} db: "SQLite\nrecords + checkpoints" {class: store} fold: "materialize()\n@radial/core\n→ MaterializedIndex" {class: proc} dispatch: "TurnDispatcher\nclaim · assignee match" {class: proc} checks: "CheckDispatcher\nCheckRunner" {class: proc} autorev: "auto-review\n(single terminal hop)" {class: proc} mergepoll: "merge-poll\nForgeAdapter" {class: proc} sock: "turn-socket\nunix socket" { style: {fill: "#ffe9e9"; stroke: "#8a3324"; stroke-width: 2} } writer: "validate → sign → PDS write\n@radial/atproto XRPC" { style: {fill: "#ffe9e9"; stroke: "#8a3324"; stroke-width: 2} } keys: "signing keys\napp password / OAuth" {class: store; style.fill: "#ffe9e9"} ingest -> db db -> fold fold -> dispatch fold -> checks fold -> autorev fold -> mergepoll sock -> writer autorev -> writer checks -> writer: checkrun keys -> writer bound: "protocol credentials never\nleave this boundary" {class: note} } # ───────────────────────────────────────────────────────────── turn: "Turn container (ephemeral · one request)" { class: ephemeral bundle: "goal bundle\ngoal · basedOn ancestry · findings\ncheckruns · system artifacts · thread" {class: store; style.fill: "#fff8e6"} harness: "harness\nClaude Code / Codex" {class: proc} sidecar: "sidecar · radial CLI\n@radial/sidecar" {class: proc} checkout: "git checkout" {class: store; style.fill: "#fff8e6"} secrets: "forge token +\nspend-capped model key" {class: note} bundle -> harness checkout -> harness harness -> sidecar: "artifact submit\nreview post\nmessage" {style.font-size: 12} } check: "Check container\nproject checks · no secrets" {class: ephemeral} # ───────────────────────────────────────────────────────────── forge: "Forge · GitHub" { class: ext pr: "branch · commit · PR" {class: proc; style.fill: "#f5f0f6"} merge: "human merges" {class: actor; style.fill: "#f5f0f6"} merge -> pr } lex: "@radial/lexicons\n20 NSIDs → generated types" {class: note} # ── edges ──────────────────────────────────────────────────── human -> ui: "define goals,\npress request buttons" ui.write -> net.hrepo: "OAuth write" {style.stroke: "#3d6b4c"} net.hrepo -> ui.fold: "listRecords poll" {style.stroke-dash: 3} net.arepo -> ui.fold: {style.stroke-dash: 3} net.hrepo -> daemon.ingest: "getRepo / listRecords\n(per-author order)" {style.stroke-dash: 3} net.arepo -> daemon.ingest {style.stroke-dash: 3} daemon.writer -> net.arepo: "agent records" {style.stroke: "#8a3324"} daemon.dispatch -> turn: "launch: bundle + checkout\n+ per-turn creds" {style.stroke-width: 2} turn.sidecar -> daemon.sock: "the only harness → protocol path" {style.stroke: "#8a3324"; style.stroke-width: 2} turn.harness -> forge.pr: "push branch, open PR\n(operator's gh token)" daemon.checks -> check: "run on every\nimplementation artifact" check -> forge.pr: "clone @ commit" {style.stroke-dash: 3} forge.pr -> daemon.mergepoll: "PR state\n(observe only)" {style.stroke-dash: 3} lex -> net.ns: "defines" {style.stroke-dash: 3; style.font-size: 12} principle: |md **Humans initiate every step · agents produce artifacts · reviews annotate them.** No state machine, no autonomous chaining. The UI and the daemon are both materializers folding the same record streams into the same view — neither is privileged. Radial never merges. | { near: bottom-center style: {fill: "#fbfbfd"; stroke: "#999"} }