One Ouroboros across Main and project rooms: each root may publish a short authored focus (update_focus) that rides the durable task result and the [INDEPENDENT_ROOTS] tail, so concurrent foci can see one another without a shared chat, a new wake or any widened authority; explicit cross-room journal/workpad reads are honoured instead of silently redirected. Dialogue consolidation summarizes each source room of a logical chunk from its own bytes (Light draft + source-grounded Light correction), assembles the typed room sections deterministically into one shared block and carries them through era compression; a failed room withholds its whole chunk while earlier complete chunks stay published; legacy mixed blocks keep unknown provenance. Room labels resolve against the canonical registry root even on a forked task drive. BIBLE P1 states the principle in three sentences. Version-neutral contribution: release carriers untouched. Co-authored-by: Ouroboros <311266734+ouroboros-agent@users.noreply.github.com>
157 KiB
1. High-Level Architecture
This chapter is the structural index of the running system: the module tree from the desktop launcher down through the supervisor to every core package, the gateway and CLI boundaries, the two-role process topology of launcher and server, the platform substrate their locks and fingerprints rest on, and the on-disk data layout. It exists so a reader can find the owner of any behaviour by name before reading its code, and so a renamed module or a moved durable file surfaces as a documentation change instead of silent drift. A tree row is an address — what the module does, the typed codes, events, state files and constants to grep for, and the section that owns its mechanism and rationale; a WHY stays in a row only where no section carries it.
User
│
▼
launcher.py (desktop/native) ← shared lifecycle; desktop keeps its immutable packaged shell, Android runs source with an immutable seed (Runtime topology and Android host below; §2)
│
│ spawns subprocess
▼
server.py (Starlette+uvicorn) ← HTTP + WebSocket on configurable host:port (default localhost:8765; Docker/non-loopback via OUROBOROS_SERVER_HOST=0.0.0.0); the lifespan applies boot provider normalization in-process and persists no provider decision (§7). Startup is a read, with one exception inside the read seam: `load_settings()` runs `context_mode_compat.normalize_and_persist_context_mode_compat`, rewriting only the `OUROBOROS_CONTEXT_MODE`/`OUROBOROS_CONTEXT_MODE_AUTO_LOW` pair left by the RETIRED persistent auto-Low mechanism — the mechanism retired, not the keys (§7 Default settings)
│
├── web/ ← Web UI (SPA with ES modules in web/modules/; §3)
│ ├── ui.css + modules/ui_primitives.js ← The one palette/control stylesheet for the SPA, onboarding and optional author pages; self-contained safe-field, escaping and tone/status functions (§3 Navigation and shared UI contracts)
│ ├── modules/page_header.js, ui_interactions.js, scroll_fade.js ← Header/tab-strip binder; dialog-focus, menu and popup-geometry binders; overflow-driven scroll-edge fade — each with owned teardown (§3 Navigation and shared UI contracts)
│ ├── modules/chat_decision.js, question_presentation.js, chat_render_batch.js, task_phase_chip.js, lifecycle_card.js ← Chat helpers: the typed decision (quiz) cards, one form for a Project question in its room and in its Main mirror, with one pure lifecycle projection shared by both (its Python twin: `project_dialogue.QUESTION_STATUS`); keyed timeline items, DOM patches, history-control presentation and reading anchors; pure desired-phase chip projection where terminal truth wins; skill lifecycle card state with best-effort polling
│ ├── modules/chat_history.js, chat_history_replay.js ← Per-chat history pages with bounded retention and exact return handles; source-keyed replay merging without live-task authority (§3 Timeline ownership and ordering)
│ ├── modules/project_answer.js ← Main's Project lifecycle rows: the fold of a mirrored final answer and the Project reference under every row (§3 Main rows)
│ ├── modules/project_reference.js ← the one control that points at a Project, and the only raiser of `ouro:open-project` (DESIGN "References and actions")
│ ├── modules/project_work_pointer.js ← Project-room pointer to an already loaded root card; no execution or history authority (§3 Project rooms)
│ ├── modules/model_wait.js ← Model-wait views and owner actions inside existing chat cards, through the shared decision ingress (§6 Quota and auth waits)
│ ├── modules/dashboard.js, logs.js, costs.js, files.js ← Dashboard tab host; Logs (backfill plus live-stream duplicate guard); Costs (an open zero is never shown as free); Files browser over `/api/files/*` (§3 Dashboard, Files)
│ ├── modules/skills.js, marketplace.js, skill_review_card.js, skill_publish_flow.js ← Installed-skills UI; the ClawHub marketplace inside Skills; Skill Review chat cards; publish-dialog detail rows (§3 Skills and Widgets)
│ ├── modules/settings_ui.js, settings_catalog.js, settings_controls.js, settings_local_model.js, mcp_settings.js ← Settings page (Accounts → Secrets → Models → Agents); model-catalog refresh with a 25-second bound and a sequence guard; control binders; the local-model form; MCP cards that keep masked tokens until edited (§3 Settings and onboarding)
│ ├── modules/model_roles.js, model_chooser.js ← The Models editor shared by Settings and onboarding; the editable chooser shared with the route editors — catalog arrival never assigns a value (§3 Navigation and shared UI contracts)
│ ├── modules/subagents_settings.js, subagent_status_primitives.js, reviewer_slots.js, route_editor_primitives.js, harness_accounts.js, harness_login_cards.js, claudexor_status_store.js ← Agents surfaces: the Available-subagents editor; one card's pure status/meta projection; Review lanes rows; neutral route-editor primitives; Agent accounts; host-neutral login cards; the ONE client store over `GET /api/claudexor/status` (`facetReadState`) (§3 Agent accounts, Review lanes and Available subagents)
│ ├── modules/onboarding_agents_step.js, onboarding_overlay.js, project_create.js, utils.js ← The first-run "Connect your accounts" step; the framed wizard's sandbox policy, kept in one place because it is a security boundary; the New Project dialog; shared escaping/formatting utilities (§2; §3 Project rooms)
│ ├── modules/review_presentation.js, review_dom_patch.js, harness_presentation.js ← Read-side only: Review Checkpoint grouping, keyed DOM reconciliation, neutral harness identity (`executorIdentityMarkup`, the one markup owner) (§3 Child cards and executor presentation)
│ └── modules/widgets.js + widget_module.js + widget_frame.js + widget_card.js + widget_reorder.js + widget_chart.js + widget_list.js + masonry.js ← Widgets page host (`mountTab`, card registry, declarative renderer); the two framed mounts (extension-route iframe; module `srcdoc` iframe with its CSP/sandbox constants, bridge and disposer); the child-side module bootstrap; framed card chrome (launch policy incl. `retain`, Start/Stop); reorder handles; chart/table helpers; pure list helpers (change signatures, keyed patch plan); the masonry that writes only `--masonry-*` properties (§3 Skills and Widgets)
│
├── supervisor/ ← Background thread inside server.py (§5)
│ ├── active_activity.py ← Process-local owner of in-flight native chat actors (`DirectActivityRegistry`): private handles for controls and the writer drain, public snapshots for `/api/state` `active_direct_turns` and WS typing frames; no queue records (§3 Direct turns and the activity block)
│ ├── message_bus.py ← Queue-based local message bus (Web UI + reviewed transport skills)
│ ├── workers.py ← Multiprocessing worker pool (forkserver on Linux, spawn on macOS/Windows; never fork from the multi-threaded supervisor)
│ ├── worker_assignment.py, worker_chat_lane.py, worker_health.py, worker_pool_lifecycle.py, worker_process.py, worker_promotion.py ← The pool's leaves: assignment and its refusals; the direct chat lane (restart resume; admission during a managed update — DEVELOPMENT "Managed Update Rule"); health-owned crash detection and terminal-file recovery; pool lifecycle and `kill_worker_tree`, the ONE worker tree-kill (daemon roots always spared); the worker child process; promotion of a chat turn or project scope into a queued task (single refusal writer `_persist_promote_rejection`) (§5; §6 Owner routing verbs)
│ ├── worker_owner_wait.py ← Queue-owned active-capacity transfer for a required owner wait: the task stays RUNNING and custodied while another worker takes the active slot (§5)
│ ├── state.py ← Persistent state (state/state.json) with file locking
│ ├── queue.py ← Task queue (PENDING/RUNNING) + activity-based timeout enforcement; the ONE task-state authority — the lifecycle/publication/transition modules below extend it without becoming second authorities (§5)
│ ├── queue_schedules.py, queue_snapshot.py, queue_timeouts.py ← Queue leaves re-exported through `supervisor.queue`: recurring schedules and the skill sync; the durable queue snapshot and what a restart may restore; activity-based liveness (§5)
│ ├── cognitive_operations.py ← Typed in-memory LLM/review/VLM operation leases for the idle rail; no scheduler or durable timing ledger (§6)
│ ├── task_model_wait.py ← Live model-wait event projection and forwarding, with quota-clock reads for queue liveness (§6 Quota and auth waits)
│ ├── task_admission.py ← Token-owned admission reservations fence duplicate user-ingress ids before Project/workspace/attachment side effects; queue.py stays the state authority (§5)
│ ├── task_lifecycle.py ← Cancellation custody — the ONE settle owner of durable cancel intents — plus the `sweep_cancel_intents` watchdog and the queue-owned root-budget admission fence (flow: §5; rules: §10 invariant 14)
│ ├── cancel_publication.py ← Cancellation settlement publication for `task_lifecycle.py`: typed CANCEL_* outcomes, artifact-honest cancelled result fields, ledger cost reconstruction, salvage, owed-before-settle registration, capture-miss terminalization (§5)
│ ├── queue_transitions.py ← Queue-owned transitions outside cancellation custody: acceptance-fence open/inspect/seal, explicit budget resume, typed `stop_evolution_tasks` (an incomplete stop leaves the campaign OPEN under the durable `evolution_owner_stopped` flag, cleared only by an owner start ingress) and fenced Project deletion (lineage ROOTS only; tombstone after provable quiescence); imports nothing from task_lifecycle (§5)
│ ├── terminal_delivery.py ← Durable terminal-answer delivery seam for final answers, cancel salvage, cascade digests and non-retry reaps: restart-surviving `delivery_id` dedupe (the id digests only the stable part of the answer, so a rebuilt replay dedups) + the bounded PENDING outbox `state/terminal_deliveries.json`; typed `terminal_delivery_exhausted` and `terminal_delivery_handoff`; per-origin projection `host_salvage` / `host_notice` / `custody_notice` / `model_final` (§5; §6 Task lifecycle; §10 invariant 15)
│ ├── task_reaper.py ← Single-owner off-loop reaper for timeout teardown and health-prepared terminal-file/crash jobs; an unconfirmed death keeps the slot reaping with `task_reaper_wedged`; mints no cancel intents (§5)
│ ├── owner_stop.py ← Owner graceful stop: the `finalize_then_cancel` policy axis on the SAME durable cancel intent (monotonic hardening), one typed `finalize_now` control (`owner_requested_finalization`), grace bounded by `OWNER_STOP_OUTER_CAP_SEC`; `running_owner_stop_tasks` bypasses only the idle/finalization-grace rails (§5)
│ ├── schedule_time.py ← Cron/timezone schedule time parsing helpers
│ ├── evolution_lifecycle.py ← Evolution campaign state + transaction lifecycle: campaign file IO, start/pause, cycle outcomes, deterministic worktree cleanup, owner cycle reports, idle dispatch, auto-restart request (§6 Background consciousness and Evolution)
│ ├── events.py ← Worker→supervisor event dispatcher with exact attempt/execution/round/call correlation for the active main-LLM row; composes the frozen subagent task text (`_compose_subagent_text`: its `[WRITE SURFACE]` block states only the write-root boundary — actor identity comes from the immutable configured snapshot, startup/wake facts from bootstrap); an event type absent from `EVENT_HANDLERS` is DROPPED into a truncated `unknown_worker_event` row, so `tests/test_worker_event_registry.py` pins the registry by a shape-bounded AST scan — outside its reach the discipline is code review
│ ├── subagent_task_truth.py ← Delegation-truth enrichment of the subagent `task_done` transport frame (`enrich_task_done_event`; §11.1)
│ ├── event_taxonomy.py, events_budget.py, events_chat_delivery.py, events_coop_checkpoint.py, events_evolution_done.py, events_project_routing.py, events_runtime_controls.py, events_schedule_task.py, events_subagent_admission.py, events_task_done.py, events_worker_reports.py ← The handler leaves merged into `EVENT_HANDLERS`, one owner per family: the declared disposition of every event kind on `EVENT_Q` (`event_taxonomy.py`, the registry the AST scan reads); budget/usage reports; owner-facing chat delivery; cooperative checkpoints at tree quiescence; evolution task terminals; chat-turn promotion and project-scope binding (the one publication boundary around every promote outcome); runtime posture controls; the `schedule_subagent` admission gates — deliberately no semantic duplicate gate — and the admission facts they read (census, caps, constraint); terminal resolution into durable truth; worker self-reports (§5; §6 Owner routing verbs)
│ ├── task_dispatch.py ← Pure admitted-event→worker-payload construction, including the depth and configured-route projections workers consume
│ ├── log_addressing.py ← Audience of task-scoped live log events: `address_task_event` (project binding wins; an explicit chat_id of 0 is `HIDDEN_CHAT_ID`, the hidden partition), the admission-time `ingress_chat_id`, `make_server_log_sink`, `address_handler_push`; A2A frames are suppressed at the `push_log` choke, not by dishonest addressing (§4 WebSocket protocol; §5; §12)
│ ├── steering.py ← Steering delivery keyed on the host-minted `issuer` fact: an OWNER turn writes owner text, a TASK writes a `task_message` row with `independent_task` provenance (one `task_message_routed` Logs row); mailbox routing to the drive the worker drains; refused typed while a cancel intent is pending — the fence behind the owner-stop single-turn rail (§6 Owner routing verbs; §5)
│ ├── plan_obligation.py ← The Swarm planning obligation follows the work: a promoting root with an unmet `force_plan` hands it to the new root inside the admission transaction (§6 Owner routing verbs)
│ ├── direct_roots.py ← The off-lock `state/direct_roots.json` fragment of live direct-chat roots (one aggregate `incomplete` fact; queue init hands the roster to snapshot restore and clears it), so a worker can list them without the server's registry; root-authored focus rides the same projection
│ ├── telemetry_events.py ← Durable handlers for RARE typed telemetry-only worker events: a verbatim passthrough into events.jsonl beside the `task_message_injected` sibling; every dispatch is a durable append, so high-rate narration never joins this registry
│ ├── git_ops.py ← Git operations (clone, checkout, rescue, rollback, push, credential helper) and the shared bounded local-Git process runner
│ ├── git_ops_remotes.py, git_ops_rescue.py, git_ops_reset.py, git_ops_updates.py ← Git-operation leaves re-exported through `git_ops.py`: the personal `origin` remote and push; the rescue/snapshot machinery destructive tree movement takes first (§2); checkout/reset admission, dependency sync and safe restart; managed-update status, official tags and preparation
│ ├── update_source.py ← Official update-source selection + network policy through the bounded Git runner
│ ├── update_recovery.py ← Exact owner Restore/promotion: pinned prior HEAD, rescue-before-reset, one captured SHA for local/remote promotion
│ ├── update_merge.py ← Managed-update engine: exact-target 3-way plan, clean fast-forward, stash-first reviewed assisted merge, the transaction (`m0_tree`, `tests_evidence`, `stash_sha`/`local_work_carrier`, `failed_update_ref`), verified rollback/smoke, boot recovery; disclosed residual: M0 is a pin-once forensic baseline in the resolver-writable tx marker — review discloses it and does not re-verify (DEVELOPMENT "Managed Update Rule")
│ ├── update_candidate.py ← Candidate/carrier primitives: private-index tree serialization, rerere-neutral merges (no silent rr-cache replay), failed-update preservation branches, marker-guarded stash restore; `tests_evidence` is forensics, never reuse authority (§6 Hermetic preflight proof)
│ ├── update_carriers.py, update_merge_plan.py ← Carrier-aware conflict resolution (the version/contract carriers a merge may not resolve by hand), and merge planning with live materialization
│ └── update_merge_policy.py ← Presentation-only doc/code/hot conflict labels; every conflict uses the same reviewed assisted path
│
└── ouroboros/ ← Agent core (runs inside worker processes)
├── config.py ← SSOT: paths, settings defaults, load/save, PID lock (§7)
├── settings_defaults.py, settings_scales.py, model_slots.py, review_model_routes.py, runtime_limits.py ← The settings vocabularies `config.py` re-exports, one owner each: keys, shipped defaults and `RETIRED_SETTING_KEYS`/`RETIRED_COMMA_LIST_SETTING_KEYS`; closed scales and `IMMEDIATE_SETTINGS` / `RESTART_REQUIRED_SETTINGS`; model-slot resolution (`ResolvedModelTarget`); the API-pinned reviewer model lists; numeric knobs with clamps — a new key belongs to the leaf, never the facade (§7; §10 invariant 3)
├── version.py ← Version string from the VERSION file with importlib.metadata fallback
├── secret_masking.py ← Exact Settings/MCP wire-placeholder emitters/recognizers + top-level secret repair before env overlay and persistence (§7)
├── settings_integrity.py ← Task-local in-memory settings read view and strict settings-snapshot integrity pin; `OUROBOROS_SETTINGS_SHA256` enables the trust root (§7)
├── credential_shapes.py ← Credential leaf names plus physical owner credential locations (§6 Credential fence and byte masking)
├── update_channels.py ← Closed Stable/QA/Development channel mapping and update-network defaults (§8)
├── update_letter.py ← The update letter: `base..target` commit material plus README history rows (only bodies and the oldest row texts are bounded, and disclosed), one accounted LIGHT-slot call, `state/update_letter.json`, one projection shared by the Updates payload and the Runtime-context `official_update` fact (§7; §3 Updates)
├── colab_bootstrap.py ← Google Colab source-mode bootstrap: official update source, stable local `ouroboros` branch, Drive-backed settings/data, personal origin, no-UI server command, native Telegram setup
├── cli.py ← Source/headless CLI over gateway tasks, logs, settings, skills, marketplace, local-model, and MCP wrappers (CLI / Headless Boundary below)
├── packaged_cli.py ← Packaged desktop CLI bridge: resolves bundle roots, bootstraps the launcher-managed repo, delegates to cli.py
├── packaged_cli_install.py ← Packaged CLI installer planning/execution for user-local command shims
├── agent.py ← Task orchestrator; the dispatch-note pair lives in `subagent_dispatch_notes.py`. `_task_exception_terminal` projects a loop crash: a lost capture stays explicitly unknown (never zero counters or unverified checkpoint bytes), and a `task_exception` is `failure.kind = "runtime"`, never a fabricated provider failure (§6 Task lifecycle)
├── focus.py ← Bounded authored focus/source-reference normalization shared by live-root projections; rejects raw dialogue, attachments and path escapes
├── agent_startup_checks.py ← Worker-boot verification: dirty repo, version sync, budget, memory files, health checks (warning-only: §2) and generation-bound native-host adoption for self-restart
├── agent_task_pipeline.py ← Task execution pipeline: result and artifacts, the frozen non-final cost snapshot and task-local owner/verification inputs for summary/reflection, the review lens those prompts get — commit/advisory review plus the task's own acceptance-panel projection, an absence statement naming the lens it describes — and the root-only post-task work (§6 Task lifecycle, Budget tracking)
├── agent_dispatch.py, post_task_synthesis.py ← The agent's delegated-child dispatch seam, and the post-task synthesis workers (§6 Post-task reflection)
├── task_finalization.py ← Early final-answer delivery (one `delivery_id` for the live and buffered copy) + the sealed final package for summary/reflection — a prompt input, never a validator (§6 Post-task reflection); owns the `swarm_efficiency` rollup (`lanes_requested`: pre-dispatch events cannot know effective lanes; `planned` stays null; Swarm intent is the typed `force_plan_source == "swarm"`; `no_fanout_observed`) and the root `depth` block (`requested_depth`/`permitted_depth`/`attempted_depth`/`achieved_depth`, status `host_visible_only`) (§6 Budget tracking)
├── mutation_attribution.py ← Root-task baseline capture; clean-at-baseline candidates plus exact explicit predecessor adoption without erasing original dirt; terminal content fingerprints and committed interval delta (§6 Git and commit review)
├── process_interpreters.py ← Interpreter resolvers for the process launch surfaces: the pre-guard unversioned-Python resolver and the post-gates Node ladder — its probe EXECUTES a candidate, so it runs only after the dispatch gates (§2; CLI / Headless Boundary below)
├── post_task_checkpoint.py ← Durable root post-task phase/final-cost checkpoint shared by task finalization and Project naming recovery
├── presence_profile.py ← Strict reviewed `presence:` behavior-profile parser (instructions, context topics, runtime defaults, portable capability requests)
├── presence_runtime.py ← Symbolic `main`/`light` defaults; owner-local overrides clamped to the global round limit
├── presence_capabilities.py ← Host-owned installation selections for portable Presence capability requests, including an optional owner-selected working folder
├── presence_authority.py ← Immutable positive capability ceiling from one reviewed profile, its selected targets and the cognitive-memory baseline (`tool_capabilities.COGNITIVE_MEMORY_TOOL_NAMES`) (§12)
├── presence_bindings.py ← Owner-created revocable transport-room → behavior-skill bindings with exact origin/destination identity
├── presence_admission.py ← Fresh review/enablement/profile/state admission + immutable per-turn Presence snapshot
├── presence_context.py ← Presence instructions, exact event facts, declared knowledge-topic projection
├── presence_runner.py ← Fresh-agent Presence turns: cross-process installation cap, per-conversation serialization, idempotency, typed result, dialogue provenance (§12)
├── presence_delivery.py ← Provider receipts in canonical chat history; Host-context deduplication projection (§12)
├── dialogue_provenance.py ← Shared exact transport-provenance rendering for history, memory, and consolidation
├── extension_companion.py ← Host-supervised companion processes for transport skills (§12)
├── extension_reconcile_queue.py ← Durable worker→server extension reconcile markers + server pickup loop
├── event_bus.py ← Typed in-process event bus for skill subscriptions
├── evolution_checkpoints.py ← Append-only campaign/eval checkpoint ledger for evolution progress
├── evolution_fingerprint.py ← Canonical fingerprint for evolution-campaign objectives; SSOT for repeat gating
├── improvement_backlog.py ← Durable advisory improvement backlog: recurrence-counted dedup (never drop), priority+recurrence+recency ranking, `close_backlog_items`, size-triggered `groom_backlog`; parser-safe locked writer
├── loop.py ← High-level LLM tool loop; finalization nudges and the latest typed `FINAL ANSWER:` candidate (§6 Task lifecycle). A harness child with zero durable start attempts gets one nanny nudge; PENDING ≠ FAILED, so a started-but-unsettled run gets a wait reminder instead of a failure accusation that invites a duplicate run. Disclosures, never gates: `nanny_finalized_after_nudge_without_delegation`, `CONFIGURED_ACTOR_INCOMPLETE`/`CONFIGURED_ACTOR_UNKNOWN`, `NANNY_METERED_OVERRUN` (§6 Delegated subagents)
├── acceptance_settlement.py ← A paid acceptance panel that outlives the answer it reviewed: the quorum/completion mailbox wake, delivery under a running panel (`previous_revision_accepted`), the post-terminal `late_settlement` supplement (§6 Task acceptance)
├── loop_acceptance.py, loop_acceptance_review.py ← Acceptance machinery (externally used names re-exported from `loop`): the fence and its obligations (`ACCEPTANCE_DECISION_REASONS`, the sole decision writer `_set_acceptance_decision`); the run — host evidence packet, the one substantive panel (`_execute_task_acceptance_panel`), `acceptance_dialogue_history`, paid identity and the free replay `_refuse_identical_acceptance`; `acceptance_retrieving_work_order` for retrieving rows (§6 Task acceptance)
├── loop_llm_call.py ← Single-round LLM call + usage accounting
├── transcript_prefix.py ← Append-only transcript invariant between the sends of one loop execution: `unsent_in_previous_send` allows tail merging only when the last observed send proves that row absent; without that knowledge producers append a new row; content digests cover role, plain text and tool-call identity (not cache markers or block shape), and a break is the `prompt_prefix_break` checkpoint fact (`kind` = system_rewritten | tail_replaced | rewritten | shrunk, plus `sanctioned_by` stamped through `sanction_rewrite`; a context-fit reprojection after a real overflow is an ordinary break). It RECORDS, never blocks — OpenAI-family caches reuse a request only when it is a byte-prefix of the next, so a transient tail or an in-place rewrite discards the whole conversation cache (§6 Task lifecycle)
├── loop_transport.py ← Transport-outage wait episodes and provider-failure terminal text; bounded backoff with free redial (§6 Context fitting, retry, and compaction)
├── loop_delivery.py ← Delivery candidates and the delivery-control protocol: the candidate with inherited host-control provenance, the control prompt and parsers, child-result dispositions, acceptance bindings, candidate publish/replace/degrade, the no-tool final (§6 Task lifecycle)
├── loop_budget.py, loop_forced_finalization.py, loop_messages.py, loop_model_call.py, loop_nudges.py, loop_round_limits.py ← The rest of the round driver, one rail per leaf, re-exported from `loop`: budget rails and soft landing; forced finalization (the absorption gate, the one forced model call); owner-message text plumbing; the per-round model call (context fit, dispatch, overflow retry, the cross-model fallback chain); mid-task steering notes and nudges; round-limit and terminal-drain handling
├── task_pacing.py ← Task-pacing SSOT: deadline/cost milestones, finalization reserve, BudgetSnapshot, the typed `CostCeiling`, `main_loop_wire_options` (the one owner of the payload-shaping options a main-loop send and its priced wrap-up copy share), and separate critic-launch and ordinary author-work rails `review_launch_allowed` / `improvement_pass_allowed`, no review-duration prediction (§6 Task acceptance, Budget tracking)
├── vision_routing.py ← Send-time image routing SSOT: inline vision vs generic captions vs placeholders on a per-send message copy (`OUROBOROS_IMAGE_INPUT_MODE`, `OUROBOROS_MODEL_VISION`)
├── fallback_cooldown.py ← Per-process 429-aware cooldown for the `OUROBOROS_MODEL_FALLBACKS` chain: a transiently-failed model is parked for a short window; advisory, fail-soft, passive heal — honestly not a swarm-wide governor
├── model_concurrency.py ← Per-(model,use_local) `BoundedSemaphore` capping concurrent provider calls (`OUROBOROS_MODEL_MAX_CONCURRENCY`, default 3) so one task's loop, subagent threads and status pings cannot self-DoS a model's rate limit; waits are deadline-bounded; per-process only
├── project_naming.py ← SSOT for LLM-first project naming: bounded light-model title with deterministic fallback, shared by admission naming (`admission_names`, no model call), card conversion, `ensure_project_scope` and the lazy turn namer (`spawn_turn_namer`, never for a greeting)
├── loop_tool_execution.py ← Tool dispatch and tool-result handling
├── deadline_utils.py ← Shared deadline parsing/remaining-time helpers + the transport-vs-logical wait seam for loop milestones and process-tool/review timeouts
├── observability.py ← Private forensic execution ledger: redaction, gzip CAS blobs, call manifests, trace refs
├── model_send_seal.py ← The runtime invariant `model-visible ⟺ logged` for `model_send`: a reconstruction mismatch is a typed durable fact, and the call is NOT blocked — dispatch authority stays with the pre-existing in-memory identity re-check
├── cancel_intents.py ← Durable cancel-intent projection: locked `state/cancel_intents.json` of ACTIVE intents (claim owner/pid + claim GENERATION fencing every mutation, `scope` single-vs-cascade) + forensic `cancel_intent` ledger rows; the ONE ingress `request_cancel`; strict fail-closed reads (`CancelIntentProjectionCorrupt`; a malformed row is disclosed once per row content, so the ~20 s watchdog cannot repeat it forever); owns `claim_is_abandoned` and `allow_settled_target` (§5; §10 invariants 14–15)
├── owner_hurry.py ← Owner "hurry": a typed TASK-LOCAL acceleration latch, never a chat message; its durable `owner_hurry` projection is written by `update_json_locked` on its own keys only — never `write_task_result`, whose status-regression guard could drop concurrent terminal fields; effects `acceptance_skip_applied`, zero improvement passes via `effective_budget_profile`, advisory force-plan; dies with the attempt (`retry_reset`; `not_applied_before_terminal`) (§5)
├── owner_quiz.py ← Owner-quiz lifecycle projection: `record_asked`, request-id-idempotent first-answer-wins `record_answered` (index validated against the STORED labels), structural `reconcile_terminal` (open → expired_terminal, closing the PAIRED `owner_wait`), `quiz_states` replay (§11.1)
├── owner_wait.py ← Native owner-answer continuation: completed-tool source checkpoint, original-process sleep, and same-task recovery only through an acknowledged planned-restart handoff (§5)
├── routing_wait.py ← SSOT of the durable routing-receipt waits (`wait_for_promotion_admission`, `wait_for_routing_annotation`), so the gateway picker confirms clicks through the SAME receipts the LLM routing tools poll
├── outcomes.py ← Typed task-outcome and acceptance-decision authority keeping the lifecycle/execution/objective/review/artifact/verification/child-absorption axes separate; policy denials never masquerade as tool failures; an oversized verification ledger rides as a stub whose `summary` is re-projected from the artifact file, never a source for entries or axes (§6 Task lifecycle; §10.1)
├── outcome_receipt_store.py ← Durable verification-receipt path/append/read authority + exact-row union of forked-child and canonical replicas; owns the zero-run WRITE enum (`incomplete`/`unknown` only — a zero-run "complete" is unverifiable self-report)
├── depth_evidence.py ← Pure requested/permitted/attempted/achieved depth projection for root acceptance; missing admitted permission stays evidence-unknown rather than reconstructed from live config
├── _outcome_receipts.py ← Receipt parsing and the ONE canonical receipt identity (`receipt_canonical_identity` → `ReceiptIdentity`; §10 invariant 16) from three components: `criterion_id`; the canonical `check` text PAIRED with its `check_rendering` stamp (the stored string alone cannot say which renderer wrote it); the raw-sorted `canonical_path_set` (a leading space is a legal filename byte). Per-kind normalization lives in the closed `IDENTITY_KINDS`/`KIND_NORMALIZES_COMMAND_TEXT` table; outstanding sets `unreconciled_failed`/`unreconciled_masked`; `receipt_identity_projection`/`disclosed_list_projection` carry exact omitted counts plus a hash; `verification_receipt_ledger_row` splats that projection, so a new receipt key is dropped unless added there
├── _outcome_tool_errors.py ← Leaf SSOT for tool-trace status vocabularies and execution-axis classification; outcomes.py re-exports
├── code_intelligence.py ← Internal code inventory: derived-only file facts, hashes, polyglot symbol/import/call extraction via tree-sitter with Python on stdlib `ast`, a visible `structural_unavailable` fallback when a grammar is missing, and an incremental JSON cache (no raw source)
├── code_intelligence_architecture.py ← Architecture facts over the pinned domain/contract/persistence carriers: `owner_of`, the domain quotient, and the facade inventory (`docs/inventories/FACADE_INVENTORY.md`)
├── code_search_rg.py ← Optional ripgrep-backed search for search_code; every match is post-filtered through the protected/secret gates
├── pricing.py ← Exact-route best-effort provider-catalog lookup with nullable estimates; no static model tariffs (they go stale) and not the monetary ledger (§6 Budget tracking)
├── usage_accounting.py ← Physical-model-attempt monetary authority: reserved→dispatched→settled|unresolved (or reserved→released), short cross-process check+append+fsync lock, global/root admission, validated replay, compatibility projections; candidates carry exact raw/context identities + a pre-dispatch manifest on the same attempt id (§6 Budget tracking)
├── _usage_response.py ← Pure provider-response usage normalization for physical accounting — the one NORMALIZER of a provider's usage block. Not the only READER of that block: every provider adapter reads the raw `usage` dict itself (§6 Usage ledger substrate vs. accounting policy)
├── _usage_rows.py ← Pure row arithmetic (summaries, limit/integrity decoration, physical-call counts, breakdown buckets, the Skill Review wave/slot projection); no I/O or locks
├── _usage_rows_memo.py ← Validated-rows memo + fingerprint-keyed render cache + in-lock warm read cache; any doubt falls back to the authoritative locked read, and only a display reader (`allow_stale`) rides the last validated snapshot past a contended lock
├── _usage_cache_splits.py ← process-local `(task, provider, route identity, review surface)` last-observed prompt-cache split; non-durable — a lost entry only re-prices a full cache write (§6 Budget tracking)
├── skill_review_usage.py ← Read-only cached projection of final physical-attempt rows for one exact `(review_skill, review_wave_id)`; no second ledger or persisted totals
├── usage_ledger.py ← Durable append-only ledger substrate: cross-process locking, atomic append+fsync, row/transition validation, torn-tail quarantine; one-way seam — accounting imports it, never the reverse (§6 Usage ledger substrate vs. accounting policy)
├── usage_compaction.py, usage_legacy_import.py ← Seq-preserving compaction of that monetary ledger, BESIDE the substrate rather than inside it, and the one-time legacy usage-telemetry import (§6 Budget tracking)
├── cost_projection.py ← The ONE projection of task cost for every producer: `accounted_upper_bound_usd`, null as None (never $0.00), `COST_OPENNESS_FIELDS` beside every amount; the retired `cost_usd[_with_children]` spellings are read-only tolerance (a diverged stored pair resolves deprecated-wins) (§6 Budget tracking)
├── delegate_custody.py ← Durable custody for delegated (Claudexor) runs: the `delegate_run_*` rows in the canonical event log plus the compact incident projection `<drive_root>/logs/containment_faults.jsonl`; OWNED/FOREIGN/UNKNOWN replay, the per-intention `Idempotency-Key` (`retry_of`), typed cancel vocabulary (confirmed | requested | failed | containment_fault_run_may_still_be_live), the one `daemon_says_absent` predicate, patch-apply intent rows (`delegate_run_patch_apply_started`/`_resolved`), `run_not_owned`/`run_ownership_unknown` refusals (§6 Delegated subagents)
├── delegate_custody_reconcile.py, delegate_state_sweep.py ← Reconciliation sweeps of delegated runs (a review-owned row is cancelled only behind an owner cancellation; a review invocation is never re-posted) and the terminal-plus-age sweep of leftover recovery/supervision state (§6 Delegated subagents)
├── delegate_custody_usage.py ← Usage and terminal-state projections over custody rows; the cross-process custody usage lock; the reviewer usage observers (one `llm_usage` row per ledger attempt)
├── delegate_hold.py ← Unknown-provider hold: parks the task in supervised_wait, waits for the leaf wake, never resends (§6 Context fitting, retry, and compaction)
├── delegate_source_coverage.py ← Oversized-work-order source custody: interval union/completeness, durable receipt bounds, replay-safe start binding — incomplete source cannot authorize a terminal PASS/apply; no alternate store
├── delegate_evidence.py ← Read-side execution evidence over custody rows (`task_execution_evidence`; `delegate_start_attempted` counts blocked and uncustodied attempts), the stamp writers `record_nanny_nudge_stamp`/`record_start_blocked`, `applied_access_profiles`, `acceptance_patch_dispositions` (cap 20, `unreviewed_delegated_apply`); an unreadable log is `evidence_read_failed`, never clean (§6 Delegated subagents; §11.1)
├── synthesis_cost_text.py ← Synthesis-prompt renderers for the pre-synthesis cost/outcome snapshot over the SSOT `cost_display`
├── llm.py ← Multi-provider LLM routing (OpenRouter/OpenAI/compatible/Cloud.ru/MiniMax/DeepSeek/GigaChat/Anthropic); canonical conversations stay function-shaped while the physical-send seam delegates exact-route request adaptation to the request-wire leaves below
├── llm_routing.py, llm_attempt.py, llm_messages.py, llm_capability_policy.py, llm_fallback.py, llm_pricing.py, llm_openai_compatible.py, llm_anthropic.py, llm_gigachat.py, llm_local.py, llm_claudexor.py, llm_substitution.py ← The client's leaves behind that facade: target resolution, client construction and route affinity; physical-attempt candidates and send-time prompt-cache policy; wire transcript shaping and the reasoning-artifact contract; capability metadata and the learned parameter/effort policy; the recovery ladder; live price catalogs (OpenRouter, Cloud.ru); the wire lanes — OpenAI-compatible, native Anthropic, GigaChat, local llama.cpp, caller-owned Claudexor model operations; which account a route must not prefer next and the refusal of a round ANOTHER model answered (§6 Context fitting, retry, and compaction; Caller-owned subscription model calls; §7 Direct-provider routes)
├── llm_stream.py ← Complete Chat Completions and native Messages SSE assembly inside one physical attempt; private wire/partial evidence and terminal framing (§6 Streams and transport waits)
├── net_transport.py ← Shared httpx transport construction for remote LLM clients; TCP-keepalive socket options (§6 Context fitting, retry, and compaction)
├── model_wait.py ← Quota/auth waits bound to the existing task or phase owner: same-call continuation, role overrides, quota-aware clocks, typed stop/deadline propagation (§6 Quota and auth waits)
├── transport_custody.py ← Typed transport facts for the physical-attempt custody seam
├── openrouter_attribution.py ← Canonical OpenRouter application attribution, centralized so forks do not compete under the same external application identity (§7)
├── openai_chat_custom.py ← Pure direct-OpenAI Chat function→custom codec: compact schemas, exact catalog binding, tool-choice projection, prior-call replay, canonical response normalization; no Responses transcript or second stored history
├── openai_chat_dispatch.py ← Direct-OpenAI Chat policy leaf: custom+requested reasoning first, exact-dialect fallback with the same reasoning, then task-local explicit `none` only when the physical-attempt rail still permits it; owns the bounded schema-error continuation
├── request_wire_contract.py ← Provider-neutral exact-route request profile: closed `set_value`/`drop_field`/registered `replace_dialect` actions, 14-day success-only evidence store `data/state/request_wire_compatibility.json`; task-local explicit `none` can never become durable dispatch authority — a task-local availability fallback must not teach the route (§6 Context fitting, retry, and compaction; §10 invariant 13)
├── request_wire_resolution.py ← Deterministic request-profile composition with source-predicated effort bounds/transitions; contradictions fail open to the requested effort with `conflict=True`
├── request_wire_receipts.py ← Factory-bound wire candidates + semantic-success receipts (exact serializer digests; tool-choice semantics cannot change)
├── request_wire_attempt.py ← Physical-attempt validation split by authority: `validate_wire_attempt_identity` is the exact candidate/attempt identity every consumer shares, `validate_physical_wire_attempt` composes it with the settled-state requirement for the compatibility receipt, and the public `usage.request_wire` disclosure uses identity alone
├── request_wire_custom_validation.py ← Custom-call validation; a validation failure may prove wire acceptance but cannot authorize tool execution (`allows_execution` gate)
├── request_wire_recovery.py ← One sync/async wire-recovery machine: durable evidence applied immediately before send, reactive evidence committed only after terminal semantic success, ordered terminal disclosures
├── anthropic_native_custody.py ← Whole-block replay custody for Anthropic native reasoning (same provider/endpoint/API/model); opaque type/order/size/digest projections
├── reasoning_artifacts.py ← Sealed-vs-portable reasoning-artifact classification, shape-first and fail-closed; only sealed artifacts pin an endpoint; the `SIGNED_PORTABLE` roster is a decaying external provider fact, extended only by a fresh cross-provider replay probe (inventory: docs/DEVELOPMENT.md)
├── llm_observability.py ← Persists public call projections; strips private sidecars from durable records while returning them in-process
├── llm_probe.py ← Oversized-context evidence probe + Provider Test transport with physical accounting; no retry, fallback, or learning
├── mcp_client.py ← MCP client: parses MCP_SERVERS, validates transports, masks tokens, prefixes tools `mcp_<server>__<tool>`; MCP descriptions/results stay untrusted data (§6 MCP and browser-facing external tools)
├── safety.py ← Safety Supervisor call with a bounded newest-first context budget; typed non-verdict `⚠️ SAFETY_UNAVAILABLE` (a 429 is an infrastructure fact, not a verdict: one retry, a storm latch, `safety_check_rate_limited`) and the fail-closed `⚠️ SAFETY_SUBJECT_TOO_LARGE_BLOCKED` over the 250k-char `_SAFETY_SUBJECT_CHAR_BUDGET` — never truncated, because anything past a cut would run unreviewed (§6 Safety Supervisor outcomes, fail-open cases included)
├── consciousness.py ← The alarm clock of Background Consciousness: no thread — the supervisor pass calls `tick(now)`, and a launch is `supervisor.workers.handle_wake_direct`, an ordinary Main direct turn; `notify(reason)` pulls the next wake forward; a legacy observation inbox is moved once to the archive unread (§6 Background consciousness and Evolution)
├── consciousness_wake.py ← The wake-up MESSAGE (`prompts/CONSCIOUSNESS.md` rendered as the turn's USER message, cuts disclosed as `(+N more)`) and the origin/authority envelope `wake_task_metadata`
├── consciousness_authority.py ← The three autonomy levels of a wake (observe/act/full) and their two consequences — `disabled_tools`, bound at dispatch only so the prompt prefix matches an owner turn's, and `runtime_mode_cap=light` below Full (§6 Background consciousness and Evolution)
├── consciousness_allowance.py ← Rolling-24h consciousness spend read off the usage ledger; typed `allowance_unknown` on a read failure; read by the alarm and the single admission door in `supervisor/queue.py`
├── room_consolidation.py ← Per-room memory summaries: one Light draft per source room plus a source-grounded correction pass, then deterministic assembly of typed room sections into one shared block/era — no cross-room LLM recombine, no guessed labels on legacy mixed blocks (§6 Durable memory and project focus)
├── consolidator.py ← Dialogue consolidation with a generation-aware cursor; an unfindable generation appends a loud `[MEMORY GAP]` block, never a silent offset reset; `last_consolidation_error` / `last_unpublished_nominations` in `dialogue_meta.json` (§6 Durable memory and project focus)
├── memory.py ← Scratchpad, identity, chat history
├── knowledge.py ← `ouroboros/knowledge.py`: linked-Markdown note addressing, exact source reads, generated shelf indexes for global and project knowledge, and revision-checked writes, so concurrent cognition cannot silently overwrite a newer note (§6 Durable memory and project focus)
├── memory_journal_compaction.py ← Digest-only compaction of old memory-journal snapshots: the digest replaces the snapshots it summarizes, never a silent drop
├── project_facts.py ← project_id resolution (explicit `--project-id` or workspace-path hash); per-project knowledge dir `projects/<id>/knowledge` isolated from `memory/knowledge`; journal/workpad helpers
├── task_tree_ledger.py ← Append-only `data/task_trees/<root>/blackboard.jsonl`: EPHEMERAL typed swarm coordination (`tree_note`/`tree_read`), mirrored into the durable project journal at root completion; pruned on root terminal
├── projects_registry.py ← Durable `data/state/projects.json`: 80-char names, `active|deleting|tombstoned`; deletion preserves bindings/history/folder/memory; a tombstone blocks resurrection; reconcile NEVER prunes (§6 Project registry and lease)
├── project_dialogue.py ← Read-only chat lens + append-only `logs/chat_annotations.jsonl`; the sidecar owns no routing except the token-bound `needs_manual_target` decision card; `build_owner_message_ref`, `routing_refusal_cause` (the owner-facing `cause` sentence), `room_membership` (§3 Chat and Projects)
├── project_lease.py ← One-writer-per-project lease in `assign_tasks`; same-project subagent swarms exempt; `""` is no lane
├── context.py ← Main agent context assembly; the subagent catalog under `## Available subagents`; the knowledge index always carries each note's authored summary, and a missing shared overview renders as a visible gap line
├── main_context_authority.py ← Deep-copies the context authority; replaces only oversized raw result strings with source-resolvable narrative or a typed gap
├── client_surface.py ← Closed-key bounded client-surface normalizer; surface identity excludes viewport/narrow_layout (§4 WebSocket protocol)
├── context_fit.py ← Deterministic Max/Low/Nano context projections from one immutable core with labelled measurement + typed reclaim deficit; owns the ONE message-side transcript cache seal; no routing/retry/global-mode authority (§6 Context fitting, retry, and compaction)
├── context_budget.py ← Context budget vocabulary + typed reclaim SSOT (owner-Low 200K economy target, Nano bounded horizon); `estimate_message_chars` (images at `IMAGE_BLOCK_CHAR_EQUIVALENT`), the basis of the local compaction proxy
├── context_mode_compat.py ← One-window compatibility shim for the retired persistent context auto-Low state
├── capability_evidence.py ← Sourced capability and token-density evidence in `data/state/capability_evidence.json`; windows size sends and grant no review authority. `observe_token_density` records measured witnesses; `cold_start_density_probe` supplies one bounded exact-model witness when the triad packet cannot fit a cold route (§6 Prompt size, density and windows)
├── context_layout.py ← Doc-layout SSOT: tier-0 always full; `book_navigation` is the compact view of a reference book (authored introductions + heading index with ranges into the PHYSICAL chapter file); ARCHITECTURE composed in Max, navigated in Low/Nano; reduction by relocation with a visible pointer, never silent truncation (§6 Context fitting, retry, and compaction)
├── reference_books.py ← `ouroboros/reference_books.py`: the ordered Architecture/Development reader over explicit chapter membership — overviews, physical source/range views, legacy single-file composition; `book_path_role`/`book_entrypoint_for`; `validate_reference_books` (DEVELOPMENT "Documentation contract")
├── local_model_server.py ← Read-only local formatter measurement and serving-process probe, without generating a reply or a second accounting attempt
├── context_compaction.py ← Atomic-unit compaction: exact checkpoint, gap-free map/fold, provenance capsules, transactional apply; an unfinished Anthropic native unit is ineligible (§6 Context fitting, retry, and compaction)
├── context_health.py ← Health invariants for the reading task (`build_health_invariants`, ONCE per task attempt — a task-start snapshot); delegated-run obligations stay globally visible — a preserved-and-invisible result is how work rots on disk — while the instruction is ownership-aware (`delegate_shared.orphan_apply_target_ok`) (§6 Context fitting, retry, and compaction; Delegated subagents)
├── context_runtime_facts.py ← The runtime section's FACT builders: what the host can honestly say it knows about this turn
├── headless.py ← Child-drive isolation, workspace patch artifacts, memory export helpers; typed `sensitive_blocked` exclusions (§6 Headless finalization and workspace patch capture)
├── headless_status.py ← Artifact and task lifecycle vocabulary shared by the headless owners
├── workspace_patch_rules.py ← Pure patch-exclusion rules (env/cache sets, junk regex, lockfiles, credential-shaped names); the I/O checks + `untracked_capture_veto_reason` stay in headless
├── workspace_patch_capture.py ← Workspace patch capture: the patch artifact, its manifest, and its git plumbing
├── coop_checkpoint.py ← Quiescent checkpoint commits of cooperative trees, off the drain thread: only a MUTATIVE child's `write_root` qualifies (owner-attached folders never); credential-shaped files excluded + disclosed; a root mid merge/rebase/cherry-pick/revert is SKIPPED, because staging an interrupted operation consumes its MERGE_HEAD and commits a half-resolved tree (§5)
├── delegate_output.py ← Atomic staged full outputs under `delegated_runs/<run>.json` (sha256+length); once-per-run `delegate_run_output_consumed` row — a disclosure, never a gate
├── delegate_directory.py ← Directory-result custody and explicit apply/discard projection for delegated file outputs through the existing artifact owner
├── workspace_file_outputs.py ← Capture and apply owner for ordinary-folder file results: exact before/after identities, binary artifacts, retained unknown-preimage custody; no second workspace store
├── delegate_containment.py ← Engine-derived isolation facts: a home-isolation breach is exactly two facts (`harness_home_isolated: false`, or applied home == the operator's own); a home nested under `$HOME` is disclosed-unconfined (`home_nested_under_operator_home`); absence is reported unproven (§6 Delegated subagents)
├── delegate_progress.py ← Transport read bound + transient Git-object retry (`poll_bound`); publishes event-local executor observations, not a current-executor authority
├── nanny_pacing.py ← Metered-silence pacing: only `BASELINE_RESET_TOOLS` (`delegate_start`/`schedule_subagent`) reset the burn — coordination never buys metered silence
├── delegate_interactions.py ← Child-interaction custody: typed `waiting_on_user` with an immutable spill file, strict `_delegate_answer` validation with typed rejections (`subscription_window_exhausted` carries `reset_at`; transport death/5xx is `delivery_unknown`)
├── delegate_shared.py ← Shared delegate leaf: `_fail`/`_emit`/`_owned_run` (OWNED/FOREIGN/UNKNOWN from durable rows; governs wait/cancel/answer and is deliberately NOT widened) plus `orphan_disposition_status`, the disposition-only upgrade for a terminal owner's orphan
├── route_spec.py ← Neutral route primitive: kind/target/pin normalization + effort validation; semantic owners keep their own spelling
├── configured_subagents.py ← Canonical `OUROBOROS_SUBAGENTS` parser/serializer: strict validation, stable ids, fingerprinting; owner free text is never host-parsed
├── subagent_runtime.py ← Immutable task-start subagent snapshots, exact `subagent_id` selection, typed alternatives, bounded legacy-input seam
├── subagent_route_health.py ← Route health: the ONE manifest reader behind every delegated dispatch
├── subagent_work_order.py ← Complete chosen work-order compiler and normalized host authority without arbitrary admission cuts
├── subagent_bootstrap.py ← Host pre-start of the exact snapshotted leaf BEFORE the first metered round, through the same wrapper as `delegate_start(prompt="")`; the host never waits (`configured_session_started`); only a definite typed refusal ends unrun at $0 — everything ambiguous wakes the model (§6 Delegated subagents)
├── delegate_supervision.py ← Event-only sleeping-nanny loop: quiet windows renew without a model call; a meaningful event (or one reasoned checkpoint) triggers a durable wake with read-only coordination context (`time.state = "not_set"` rather than a latched anchor) (§6 Delegated subagents; `usage_attempts.lock` recovery: Platform substrate below)
├── delegate_start_instructions.py ← Stable host start instructions + a complete separately-hashed coordination appendix; host pre-start sends no appendix
├── delegate_target_drift.py ← Read-only authority-tree drift evidence for delegated capture; records changed paths without attributing them to the child or blocking a normal no-change disposition (§6 Delegated subagents)
├── delegate_recovery.py ← Narrow exact-leaf recovery for proven crash + planned self-restart; vetoes every no-resume cause
├── delegate_registration_policy.py ← `persistent_registration` + the STARTED-row field tables
├── delegate_pending.py ← Durable pending-invocation replay preserving the original idempotency key + canonical start body
├── delegate_custody_memo.py ← Process-local memo of the custody rows (`custody_rows`): an ordered `(st_dev, st_ino, consumed, st_mtime_ns)` fingerprint of the rotated events chain prefix plus a hash of the live file's consumed bytes, advanced by folding only appended bytes, refolded on any doubt, bypassed (never cached) while the chain is unreadable; inline legacy request bodies replaced by a re-readable locator; a warm cache with an exact fallback, not a durable projection
├── delegate_terminal.py ← Terminal reconciliation + custody-audit persistence: counters stay a frozen snapshot while `actual_substrate` and the envelope mirror follow live custody; audit-only in both directions; the typed `terminal_custody_notice` card row; `refresh_recently_settled_terminals` over the byte-offset cursor `state/delegate_terminal_refresh_cursor.json` (5 MB per tick) (§6 Delegated subagents)
├── subagent_dispatch_notes.py ← Dispatch-time executor notes for delegated children (configured-nanny charter note); agent.py keeps re-exports
├── subagent_messages.py ← Bounded durable child-message identity shared by the final frame, recovery, persistence and replay; `executor_observation_meta` validates task-bound progress actor facts
├── subagents.py ← Subagent envelopes + bounded legacy compatibility; `configured_subagent` snapshots dispatch through subagent_runtime
├── subagent_history.py ← Existing compact helper receipt: dated API attempts, session settlement/recovery and typed unrun starts; dynamic context and owner UI only, never admission (§6 Route health)
├── subagent_worktrees.py ← Worktree lifecycle + durable registry `state/subagent_worktrees.json`; `provision_genesis_project` (never registry/GC); execution snapshots pinned by `refs/ouroboros/delegated/`; standalone payload snapshots; removal only explicit or custody-cross-checked startup GC, fail-closed on an unreadable custody log (§6 Delegated subagents)
├── artifacts.py ← Attachment staging into `artifact_store/attachments/`; artifact records; scratch fingerprints (`.scratch_manifest.json`) that gate patch exclusion only while content matches; the undeclared-output guard; `delegated_capture_read_target`; partial tool evidence first reads its verified actor source, then a matching redacted observability projection; recovered text uses the existing exact-text source writer best-effort, and a later budget cut without a verified handle remains source-unavailable
├── retention.py ← Unified GC retention SSOT: clamp/age-cutoff + legacy-key seed picker
├── workspace_preflight.py ← Read-only external-workspace git/manifest/toolchain snapshot used by gateway task creation
├── project_sources.py ← Folder attach validation (realpath, not the home root, no repo/data overlap); opt-in `init_git`, NEVER auto-init; atomic server-side clone with `GIT_TERMINAL_PROMPT=0` and typed `auth_required`; attaching IS the trust grant (`trusted_at`)
├── promotion_source.py ← Promoted-task source admission off the event-drain loop, only after an executor/id reservation
├── workspace_admission.py ← Shared admission for `/api/tasks` + promotion: disjoint git root, Project binding, `workspace="none"`, bounded preflight; typed `workspace_provisioning_failed` — never a fallback onto the system repo; `workspace_repair_hint` (§6 Owner routing verbs; CLI / Headless Boundary below)
├── local_model.py ← Local LLM lifecycle (llama-cpp-python); normalized launch settings become applied only after owned-process health (§3 Settings)
├── local_model_autostart.py ← Local model startup helper
├── deep_self_review.py ← Whole-system review on the configured `deep_review` row: every API row runs native inspection and every session row delegates retrieval; BIBLE, standing disclosures and memory arrive inline (§6 Deep self-review). Reports retain provenance and diagnostic reading gaps; typed failures leave `memory/deep_review.md` intact and `BudgetExceeded` reaches the budget-pause rail
├── review.py ← Shared size inventory, code collection, complexity and informational headroom; official CI enforces the shrink-only ceilings while local findings remain warnings (§6 Structural gates)
├── size_ratchet_manifest.py ← Generated data-only size-debt manifest (regenerated by scripts/regenerate_size_ratchet.py)
├── review_execution_projection.py ← Same-slot requested/observed progress and read-side reviewer-execution projection: bounded rows, 2000-char post-redaction string bound; kinds `api` | `harness` | `native`, so the owner can tell a retrieving review from a packet review on the same model
├── preflight_runner.py ← Hermetic pre-commit test runner: ONE hardened raw-bytes `git diff --binary … HEAD` capture; typed `PREFLIGHT_CANDIDATE_ASSEMBLY` block, never a test verdict; node lane, then the two-pass parallel/serial pytest split (`LANE_EXCLUSION_EXPR`); a dead xdist worker or missing plugin is a named block, never a silent serial fallback (§6 Hermetic preflight proof)
├── preflight_node.py ← Content-keyed Node test lane: bundled signed node first then PATH, floor 20.11; typed `PREFLIGHT_NODE_MISSING`/`PREFLIGHT_NODE_TOO_OLD` blocks and `NODE_TESTS_FAILED`, never a silent skip; both CI jobs mirror `cd web && node --test tests/*.test.js` (§6 Hermetic preflight proof; §8)
├── review_substrate.py ← Review slot coordinator (re-exports the row-identity mint and paid stamp of `review_dispatch.py`, the slot builders of `reviewer_slot_config.py`, the reducers of `review_verdict.py`): independent slots, per-actor records keeping transport/parse/verdict/coverage/quorum distinct, adaptive quorum, one substantive interaction per actor; a session's narrative is canonicalized by the surface's output SHAPE (`triad_review.review_output_shape`) (§6 Review stack, Task acceptance)
├── review_custody.py ← Process-local physical review custody: independent deadlines, late-result settlement, stable retry identity, duplicate-dispatch suppression; not durable scheduling (§6 Physical custody)
├── review_owner_custody.py ← Paid attempts record `(server session, pid)`; owner loss is proven by pid death, never elapsed time (§6 Paid stamp and owner custody)
├── review_execution.py ← Shared review-delivery seam: `delivery_retrieves(route, subagent_id)` classifies ordinary rows; scope sets `ReviewSlot.native_retrieval_override` and deep review binds native delivery directly, preserving route identity without fabricated subagent ids (§6 Review delivery); immutable `ReviewAssignment` → typed `ReviewAttemptResult`, no cross-transport fallback (`ReviewRouteUnavailable`). `AgentSessionReviewExecutor` sends `outputSchema` only when the live manifest declares it and trusts `outputConformance == "passed"`; otherwise strict parsing and light extraction disclose `capability_delta`. `review_output_contract` is shared governance; `ROUTE_OWNED_POLICY_KEYS` (`output_contract`, `native_data_root`) stay outside API Policy JSON. Structured slots own routes; shared session-env fallback (`REVIEW_SESSION_ROUTE_ENV`): §6 Review delivery
├── review_native_episode.py ← NativeToolRoundReviewExecutor: read-only inspection for configured-subagent API rows and every scope, deep-review and advisory API row. `review_native_transcript_bound` is min(owner ceiling, calibrated route capacity); mandatory reading uses successive views, never raises the bound; owner deadline and paid ledger, no round cap. Exact host-observed read coverage is diagnostic (§6 Native tool-round episode)
├── review_session_reads.py ← Completed harness-journal tool calls folded over a declared source manifest as weaker `harness_observed` reading diagnostics; unsupported or unavailable extents remain unobserved, candidate drift stays a source gap, and neither changes verdicts, quorum or retry (§6 Review delivery)
├── review_verdict_extraction.py ← Session/native verdict canonicalization: strict parse first, then light-model extraction, branching on the output SHAPE — `array` keeps the findings ladder, `object` (task acceptance) keeps the WHOLE verdict object, `report` (deep self-review) passes through verbatim
├── review_session_custody.py ← Exact delegated-review recovery validation + pre-POST durable invocation checkpoint; no scheduler or state store
├── review_slot_cancel.py ← Slot-cancel honesty: a cancel outcome reports only what it PROVED; a succeeded run whose result read fails is typed `ReviewSessionSucceededResultUnavailable`, never "may still be live"
├── review_actor_aggregation.py ← Contract aggregation for completed review actor rows; demotes non-contract-valid responses
├── review_session_usage.py ← UsageScope-to-custody attribution for delegated review sessions
├── review_thread_continuity.py ← Thin Claudexor thread operations for delegated plan reviewers
├── commit_admission.py ← Deterministic commit-admission SSOT: release checks + auto-sync, staged-Python syntax compile, `run_tests_preflight_with_proof`; the advisory and commit gates both delegate here; the disclosure-only `preflight_test_proof` row carries `pass_seconds` and `budget_sec` (§6 Hermetic preflight proof)
├── reviewer_slot_config.py ← Structured reviewer-slot SSOT: stable ids, route targets, per-slot effort, disclosure-only execution records, save/runtime validation (`reviewer_slot_save_check`; the one-time `acceptance_delivery_disclosure`); a row is EITHER an inline route OR a `subagent_id`; `is_session` is transport while `retrieves` is delivery class; the optional `deep_review` singleton (`deep_review_slot()`); malformed config refuses EVERY surface; `triad_delivery_slots` is THE triad-row builder plan, skill/commit (`commit_triad_delivery`) and task-acceptance review all read (§7 Reviewer slots)
├── review_state.py ← Durable advisory pre-review state (`state/advisory_review.json`)
├── review_state_model.py, review_state_records.py, review_state_custody.py, review_records.py, review_verdict.py, review_projection.py, review_evidence_sections.py ← The review ledger and its vocabularies, re-exported through `review_state.py`: the in-memory ledger and its transitions; record types and shaping rules; durable custody of in-flight invocations; the typed panel records and hardness vocabulary every surface shares; the pure reducers from actor rows to a verdict, a tier and a capsule; panel identity and the compact redacted run projection; the provenance-tagged sections of the task-acceptance evidence packet
├── review_cycles.py ← Shared paid-cycle cap SSOT (`OUROBOROS_REVIEW_MAX_CYCLES`, positive int or `unlimited`); the four per-gate meanings: §6 Review stack (§10 invariant 17)
├── review_dispatch.py ← Review row-identity mint + the write-ahead PAID stamp: typed pre-start refusals stay $0 while a late worker or crash cannot race away the paid fact; acceptance binds one strict exact-hash tree-wallet claim per panel to that stamp (§6 Paid stamp and owner custody, including the compatibility positive-capture residual)
├── reviewer_window.py ← Typed per-route window resolution and scaled reserves, used only for sizing; unknown routes retain a disclosed assumption. Metadata probing is per-route locked and evidence-TTL limited, never a process-lifetime memo or review-authority floor (§6 Prompt size, density and windows)
├── triad_review.py ← Shared review primitives: JSON-array extraction, per-actor records, quorum/degraded accounting; `REVIEW_JSON_ARRAY_CONTRACT`/`REVIEW_JSON_MATRIX_CONTRACT` — a clean verdict is the WHOLE response `[]` (± one fence, ± `NO_FINDINGS`), because a refusal cannot be told from a benign preamble by structure; `REVIEW_OUTPUT_SHAPES` / `review_output_shape(surface)`, the ONE form fact (`array` | `object` | `report`)
├── onboarding_wizard.py ← Shared desktop/web onboarding bootstrap + validation (§2)
├── subscription_install_presets.py ← Pure sibling install compilers from one normalized draft + one discovery snapshot; output is linear, unpinned, exact-discovery-backed, all-or-nothing (§2)
├── settings_setup_contract.py ← SSOT for the setup contract, derived bootstrap state, payload validation, and the `TOTAL_BUDGET` resolver authority `resolve_total_budget_usd`
├── owner_mailbox.py ← Per-task user message mailbox (compat module name); revocation-aware drain and proven-empty peek; the closed task-message provenance set (`ancestor_task`, `peer_via_ancestor`, `system`, `descendant_task`, `independent_task`)
├── peer_roster.py ← Host-listed independent roots as a worker reads them (pooled roots from `state/queue_snapshot.json`, direct roots from `direct_roots.json`, hidden-partition roots included): the addressability gate for `forward_to_worker`, the grouped `[INDEPENDENT_ROOTS]` TAIL note (40 rows shown, the cut disclosed), and the stable paginated `live_roots` catalogue; authored focus is a bounded source reference, never dialogue or authority (§6 Owner routing verbs)
├── launcher_bootstrap.py ← Bundle-to-repo bootstrap, launch-option parsing, managed sync and selected native-host artifact synchronization (used by launcher.py; §2)
├── launcher_onboarding.py ← First-run onboarding as the desktop launcher presents it (serves the gateway /onboarding page; §2)
├── launcher_server_reaper.py ← POSIX same-install server discovery, pre-signal descendant capture, root-first termination, live identity revalidation; PID-lock-owning launcher only (Runtime topology below)
├── launcher_windows_runtime.py ← Windows-only pythonnet/pywebview runtime preparation
├── provider_models.py ← Model-ID helpers; the `ACTIVE_MODEL_SETTING_KEYS` vs `LEGACY_MODEL_SETTING_KEYS` split keeps Heavy out of startup/Provider Test/new consumers while migration/history still read it
├── runtime_mode_policy.py ← Protected-path policy (safety-critical files, frozen contracts, release/managed invariants) shared by the registry, git tools, and gateway guards (§6 Safety and runtime mode)
├── schedule_contract.py ← Schedule id, 5-field cron, IANA timezone validation SSOT
├── reflection.py ← Execution reflection and pattern capture (§6 Post-task reflection)
├── post_task_evolution.py ← The worker writes a durable promotion signal; only the supervisor idle tick applies it via the gated enqueuer; never from evolution/subagent tasks (§6 Background consciousness and Evolution)
├── repo_remotes.py ← Role-based remotes: `managed` is the read/update-only official source; `origin` is the personal target auto-configured from the GitHub token
├── review_evidence.py ← Same-execution commit-review evidence (redacted browser/vision calls, canonical source handles, exact pending reuse; §6 Commit review evidence) and the bounded provenance-tagged task-acceptance packet (ingress claims win over a closed plan wave); `substrate_execution` and `delegated_patch_dispositions` are VISIBILITY ONLY — acceptance judges quality, never the execution route (§6 Task acceptance)
├── review_evidence_refs.py ← Leaf SSOT of the evidence-ref vocabulary + exact-membership resolver; unsupported claims cannot certify (`CLAIM_ID_UNSUPPORTED`)
├── review_status_projection.py ← Leaf commit-review status projection over `review_state` records (`build_review_projection` / `build_review_status_payload`); re-exported by `review_evidence`
├── semantic_dedup.py ← LLM-first semantic dedup, fail-open None; consumed by improvement_backlog + review_state
├── betterleaks_runtime.py ← Pinned Betterleaks runtime resolver (six platform artifacts, packaged-resource-first)
├── skill_loader.py ← Skill discovery over `data/skills/{native,clawhub,ouroboroshub,external}` + `OUROBOROS_SKILLS_REPO_PATH`; `.self_authored.json` marker; per-skill state under `data/state/skills/<name>/` (§13)
├── skill_readiness.py ← Execution readiness and phase-specific next actions from review, hash, enablement, grants, dependencies and peer conflicts
├── skill_dependencies.py ← Shared dependency-spec resolution and installed-readiness probe for skills
├── skill_repair_admission.py ← Selected-skill development admission: immutable `base_content_hash` and observed `expected_content_hash`, verified before each payload operation; no long shell lock or rollback (§6 Skills and extensions)
├── skill_owner_attestation.py ← Owner attestation lane: the owner may skip the expensive LLM skill review for skills they authored themselves
├── skill_publish_snapshot.py ← Immutable captured-byte authority for publication
├── skill_publish_scanner.py ← Exact-byte Betterleaks evidence; publication applies the mode policy in §6 Skill publication
├── skill_publish_result.py ← Typed publish attempt/receipt + finalization veto
├── skill_publish_github.py ← GitHub publication transport after the local gates
├── skill_publish_eligibility.py ← Shared current Advisory author publication authority; passive publish visibility + `task_start_allowed`
├── skill_review_status.py ← Verdict aggregation → `executable_review` (anchors the §13 readiness statuses); an Advisory author acceptance may stay valid while the critic hash is stale, while Blocking requires fresh critic authority
├── skill_review_passes.py ← One multi-model pass or chunked quorum; reserves the complete operation roster before dispatch and rejoins exact logical waves from lifecycle history
├── skill_review.py ← Skill review orchestration: preflight + advisory critic via `run_advisory_critic`; tri-model gate against the Skill Review Checklist (docs/CHECKLISTS.md) + docs/CREATING_SKILLS.md
├── skill_review_prompt.py, skill_review_packs.py, skill_review_output.py, skill_review_rebuttals.py ← The skill reviewer's leaves: prompt contract, governance context and waves; the reviewable payload; parsed findings, aggregate verdict and rendering; the review-history evidence a reviewer reads before re-judging
├── skill_review_history.py ← Write-ahead review-history marker (`physical_attempt_v1`) and idempotent `state/skill_review_root_tasks.jsonl` projection; append failures emit typed `skill_review_history_append_failed`
├── skill_review_cycles.py ← Paid skill-review cycle counting, exact prior-wave selection, $0 replay and typed exhaustion; shared cap SSOT is review_cycles.py
├── extension_loader.py ← Extension loading: in-process pure-Python via `PluginAPIImpl`, child-process proxies for isolated-dep/native extensions (§13)
├── extension_process_runner.py ← Extension child processes: scrubbed env, per-skill deps, timeouts, graceful host errors
├── extension_route_stream.py ← Portable stdio response frames and ASGI relay for out-of-process extension routes (§3 Out-of-process extension responses)
├── extension_ui_validation.py ← The one host-owned declarative-schema-v1 widget validator
├── extension_isolated_deps.py ← Legacy/forced in-process bridge for isolated-dependency extensions
├── extension_health.py ← Durable process-qualified per-skill health at `data/state/skills/<name>/health.json`; server observation is authoritative, worker observation is a handoff-qualified view
├── extension_plugin_api.py, extension_registry_state.py, extension_liveness.py, extension_child_catalog.py, extension_import_staging.py, extension_surface_names.py ← The extension runtime's leaves: the `PluginAPI` object handed to `register(api)`; the process-wide registries of live extension surfaces; the liveness authority for one extension; host-side validation of child-catalog surface descriptors; staged import trees and their reclamation; provider-safe naming and syntax rules for extension surfaces
├── skill_token.py ← Opaque Host Service token minting/validation (§12)
├── marketplace/ ← ClawHub + OuroborosHub: `clawhub.py`, `ouroboroshub.py` (hub update via adopt transaction with VERIFIED `rolled_back`/`rollback_errors`, `.pre-adopt` retention disclosure), `fetcher.py`, `adapter.py`, `install.py`, `install_specs.py` (normalized third-party dependency install metadata for bounded per-skill prefixes), `isolated_deps.py`, `provenance.py` (+ publication receipt `state/skills/<n>/ouroboroshub.json`)
├── skill_lifecycle_queue.py ← Single FIFO skill-mutation lane + event snapshot (§13)
├── skill_lifecycle_actions.py ← Shared grant/toggle effects and owner-action admission for UI, launcher, CLI and task adapters; exact target/revision, no new permission store
├── skill_uninstall_state.py ← Marketplace-uninstall tombstones and explicitly authorized local payload/state deletion; separate operations with separate retention contracts
├── skill_review_runner.py ← Writes `review_job.json` + `skill_review_*` events; separate review, dependency and extension outcomes; unchanged verdict replay can resume dependencies without another panel or overriding an owner disable
├── server_auth.py ← Non-localhost network gate via `OUROBOROS_NETWORK_PASSWORD` (warns when unset; see §8 packaging note)
├── server_control.py ← `restart_current_process` + `execute_panic_stop`
├── server_entrypoint.py ← CLI parsing + port binding helpers
├── server_runtime.py ← Startup/onboarding wiring + WS liveness
├── server_web.py ← `NoCacheStaticFiles`, web-dir resolver and fixed-source `read_author_kit_assets(repo_dir)` for optional author-owned routes; no endpoint or cache
├── server_process.py, server_liveness.py, server_maintenance.py, server_restart.py, server_owner_routing.py, server_routing_context.py ← Server leaves the composition root calls: the facts one server process shares with every leaf, including applied startup settings; wedge detection for the supervisor generation; the upkeep a generation owes the drive; restart operations (shutdown, the checkout-first manual Restart, the planned restart's engine-pin daemon stop; §9); where one owner message goes; the bounded facts one owner turn may address
├── task_continuation.py ← Durable review continuation state
├── task_results.py ← Durable task results `task_results/<id>.json`; the locked `task_acceptance_review_accounting` claim (minted at first physical reviewer dispatch; a claim without a recoverable terminal host run is UNKNOWN, never permission to re-dispatch); the read-only root review-capacity projection is WALLET and cancellation only (`root_task_id`, `cap_cycles`, `claimed_cycles`, `remaining_cycles`, `binding_seen`, `dedupe`, `state`, `reason`), no time axis (§6 Task acceptance)
├── task_result_schema.py ← Task-result schema admission: the `_schema_version` stamp, the classifier, and the quarantine an unstamped, future, malformed or retired-key row lands in
├── task_status.py ← Effective-status SSOT, lineage, bounded waits; worker-side `task_has_live_queue_ownership` (§10 invariant 14); the DESTRUCTIVE orphan predicate fails open toward liveness — an in-process direct actor or a missing/stale queue snapshot can never prove a task dead
├── git_shell_policy.py ← Structural git argv classifiers for the shell guards
├── protected_artifacts.py ← Execute-only black-box policy for protected artifacts
├── shell_parse.py ← Shared command/argv normalization and POSIX wrapper grammar; observed targets, not semantic permission judgments (§6 Safety and runtime mode)
├── argv_budget.py ← Argv admission counts encoded bytes of argv PLUS environment (ARG_MAX charges both; per-arg `MAX_ARG_STRLEN`, Windows unit limit); asked by skill_exec before exec
├── workspace_executor.py ← Workspace process backends: `local` and network-none `docker_exec`
├── deliverables_paths.py ← Lexical + case-folded deliverables path views
├── tool_capabilities.py ← SSOT for the core/parallel-safe/untruncated/stateful-browser tool sets and the cognitive-memory tool class every Presence ceiling carries
├── tool_access.py ← ToolProfile × ResourceRoot × Operation matrix, affordance map, closed-enum `required_capabilities` check
├── tool_access_types.py, tool_access_roots.py, tool_access_paths.py, tool_access_user_files.py ← The access matrix behind that facade: the closed access vocabulary and policy matrix; who is acting and where each resource root physically lives; the physical path primitives; the `user_files` confinement with its secret-name policy
├── tool_policy.py ← Round-one tool visibility (the sets live in tool_capabilities)
├── browser_policy.py ← The browser tool's target and control-request policy: task-granted concrete origins, metadata/private/reserved refusals, the three-valued Ouroboros control-service identity (`runtime_service_kind`: proven kind / unknown / none); `tools/browser.py` keeps the Playwright lifecycle (§6 MCP and browser-facing external tools)
├── skill_payload_binding.py ← Skill payload targeting: `.seed-origin` distinguishes native vs external; read/list/search only for read profiles; bounded manifestless skill_publish recovery
├── utils.py ← SSOT for atomic JSON, timestamps, hashes, sanitization, subprocess helpers, `truncate_review_artifact`
├── jsonl_tail.py ← The one bounded rotation-aware filtered tail reader (window-doubling live tail, newest-first archive backfill bounded to three files, coverage facts + `coverage_line`) behind the history/logs/routing endpoints (`gateway/_helpers.py` wrappers keep the gateway parser seam) and the per-task recent-activity context sections
├── markdown_source.py ← `ouroboros/markdown_source.py`: byte-preserving Markdown structure shared by books and knowledge notes; physical LF/UTF-8 ranges tied to source SHA; `MarkdownSourceError` keeps missing grammars or malformed YAML visible without replacing original bytes
├── world_profiler.py ← Generates WORLD.md
├── contracts/ ← Frozen ABI package (§11)
│ ├── tool_context.py ← ToolContextProtocol
│ ├── tool_abi.py ← ToolEntryProtocol + GetToolsProtocol
│ ├── chat_id_policy.py ← SSOT for human-visible vs synthetic chat ids (§12)
│ ├── task_contract.py ← Frozen task-contract normalization and effective acceptance-claim binding (semantics: §11.1)
│ ├── task_constraint.py ← `VALID_WRITE_SURFACES` + surface/write_root validation, fail-closed
│ ├── skill_payload_policy.py ← Payload path resolution/confinement/sidecar detection
│ ├── skill_manifest.py ← Unified skill manifest parser (`VALID_SKILL_TYPES`: instruction|script|extension)
│ ├── schema_versions.py ← Opt-in `_schema_version` stamping helpers (§11.2)
│ └── plugin_api.py ← PluginAPI, ExtensionRegistrationError, FORBIDDEN_SKILL_SETTINGS, VALID_EXTENSION_PERMISSIONS, VALID_EXTENSION_ROUTE_METHODS
├── gateways/ ← Thin outbound transport adapters; no business logic
│ └── claudexor.py ← Loopback daemon client: discovery (`discover_daemon_at` reads `<config_dir>/daemon/control-api.json`), handshake, runs, cached quota GET + explicit quota POST; the token stays inside; prefers the OWNED daemon via `claudexor_daemon.owned_daemon_provisioned` (§6 Delegated subagents)
├── claudexor_runtime.py ← Reviewed engine pin (version/SHA/URL/SHA-256/size/protocol/Node/entrypoints, nullable CLI): seed-or-download, verify + staged extract + probe + atomic promote under `data/state/cx`; the reviewed pin IS the next-spawn selection — no mutable `current` pointer or background updater; `OUROBOROS_CLAUDEXOR_BIN` stays an explicit operator override (§6 Delegated subagents)
├── claudexor_daemon.py ← Installation-owned Claudexor lifecycle over `data/claudexor`: lazy first use, authenticated attach, purpose-bound startup custody, `stop_outcome` (explicit same-home CLI shutdown with a measured/Popen fallback), atomic ownership-marker publication, provisioned warmup, the start-failure spawn latch, `install_missing_harness_cli` (§9)
├── claudexor_startup_failure.py ← Typed vocabulary of a failed owned-daemon start: `ExitFact` (`failed_without_control` is the spawn-latch predicate), the diagnostic-only log classification (`heap_exhausted` | `writer_lease_contended` | `engine_floor` | `unclassified`), the latch record and the two supervisor-row shapes; stdlib only (§9)
├── gateway/ ← Gateway Boundary v1: browser-facing route ownership + frontend contract SSOT (see below)
│ ├── contracts.py ← Active WS/HTTP envelope contract owner
│ ├── decision_contracts.py ← Typed request/response contracts for decision families, re-exported by contracts.py; each ingress owns runtime validation
│ ├── endpoint_index.py ← `HTTP_ENDPOINTS` index (re-exported by contracts.py); routers own the Route objects
│ ├── schema.py, task_list_scan.py ← The executable gateway contract — JSON Schema derived from the TypedDicts, validating ingress — and the stat-invalidated compact result facts shared by list ordering, SSE discovery and Main routing
│ ├── router.py ← Starlette route collector for /api/* and /ws (§4)
│ ├── ws.py ← WS manager, extension WS dispatch, broadcast (§4 WebSocket protocol)
│ ├── state.py ← /api/health + /api/state
│ ├── tasks.py ← Headless task create/list/get/cancel/events; cancel accepts `stop_policy` (empty = immediate; `finalize_then_cancel` → 202 + open intent → supervisor/owner_stop.py; unknown → 400)
│ ├── task_events.py ← Task-event SSE endpoint: legacy GET ranks plus read-only POST v2 physical-chain cursors (§3 History reads and the SSE v2 transport)
│ ├── task_hurry.py ← POST hurry ingress: exact one-field `{request_id}` body — extra fields refused, because hurry carries no text by design and a smuggled field must not become a side channel; queue-owned admission initializes only an absent pooled lifecycle (`write_task_result(create_only=True)`), direct turns excluded (semantics: owner_hurry.py)
│ ├── task_decision.py ← ONE `POST /api/decisions` ingress with family-parsed ids (`quiz:` here, `routing:` → routing_decision.py, `interaction:` reserved); writes `KIND_QUIZ_ANSWER`, broadcasts `quiz_state` (lifecycle: owner_quiz.py; ABI: §11.1)
│ ├── task_model_wait.py ← Shared model-wait decision effects over the existing mailbox, with live-owner/revision checks and optional role persistence
│ ├── routing_decision.py ← Validates a click against the durable `needs_manual_target` row, recovers the original text, dispatches the existing `steer_task`/`promote_chat_to_task`, confirms through routing_wait receipts
│ ├── logs.py ← Read-only runtime log tail
│ ├── onboarding.py ← `POST /api/onboarding/complete`: install-time latch, shared validation, live engine read, preset compile, one settings write under lock; a typed 503 persists nothing — except 503 `settings_save_timeout` (`saved: null`), the unknown outcome (§2)
│ ├── onboarding_host.py ← GET /onboarding: side-effect-free wizard page served as ES modules
│ ├── owner_settings.py ← Settings-lock-as-precondition + `CommitBoundary` (Gateway Boundary v1 below)
│ ├── settings.py ← /api/settings + /api/owner/*; `GET /api/reviewer-slots` with row limits (triad 10 / scope 4 / advisory 1 / deep_review 1) and typed `config_error`, never a 500; the deep self-review singleton rides the response (saved, or labeled `synthesized_from`), beside a `config_error` only as a repair placeholder, never an effective row
│ ├── presence_settings.py ← Owner-facing runtime overrides and working-folder selection for reviewed Presence behavior skills
│ ├── control.py ← /api/reset, /api/command, /api/git/*, /api/update/*, /api/evolution-data HTTP handlers
│ ├── update_progress.py ← Process-local stages owned by the synchronous update executor; status projection and WS invalidation, never recovery authority
│ ├── schedules.py ← Cron schedule HTTP surface
│ ├── files.py ← File Browser + chat upload
│ ├── ui_preferences.py ← `state/ui_preferences.json`: widget order, per-card start-mode overrides (`widget_start_mode`, values from `extension_ui_validation.WIDGET_START_MODES`), nested subagent expansion
│ ├── models.py ← Model catalog + provider probes + local-model lifecycle
│ ├── extensions.py ← extensions/skills HTTP surface (GET /api/extensions, GET /api/extensions/<skill>/manifest, GET /api/extensions/<skill>/module/<entry:path> — reviewed module sources served from the live loader registration, ALL /api/extensions/<skill>/<rest:path>, POST /api/skills/<skill>/toggle, POST /api/skills/<skill>/delete, POST /api/skills/<skill>/review, POST /api/skills/<skill>/grants)
│ ├── extension_receipts.py ← Process-qualified extension index/toggle/reconcile receipt projection
│ ├── widgets.py ← GET /api/widgets: the Widgets card list projected from the in-memory extension snapshot (live UI tabs + the skill's live `content_hash` as `revision`), with no discovery, reconcile, hashing or writes on the read path; homes the `WidgetTab`/`WidgetsResponse`/`ExtensionLiveSnapshot` TypedDicts `gateway/contracts.py` re-exports, importing no transport at module level
│ ├── skill_publish.py ← Read-only publish preflight with scan cache; one five-state response; no task or GitHub effect (§6 Skill publication)
│ ├── marketplace.py ← ClawHub + OuroborosHub HTTP surface
│ ├── mcp.py ← MCP HTTP surface backed by the shared MCPManager
│ ├── claudexor_accounts.py ← Agent accounts HTTP surface: six thin proxies (by handler) over the owned daemon, with zero auth logic or vendor recipes and no browser exposure of its token. GET /api/claudexor/status[?include=models] stamps facet read states; `unified_accounts` follows the engine catalog's `get:account-pools` capability (unreadable catalog retains legacy rendering). POST /api/claudexor/wake and /api/claudexor/login, login-job actions, and /api/claudexor/credential-profiles preserve the opaque `{job, cursor, sequence, deviceCode?}` envelope. Complete routes: §4; Connect/install/retry/custody: §3 Agent accounts
│ ├── claudexor_quota.py ← Explicit owner quota refresh: POST /api/claudexor/quota/refresh discovers the already-owned daemon, handshakes (60 s control-plane read bound) and delegates exactly once to the engine's quota POST (90 s foreground bound); no lifecycle start, retry, policy or daemon token crosses this boundary; GET /api/claudexor/status stays passive
│ ├── host_service.py ← Loopback-only Host Service API (§12)
│ ├── history.py ← Shared Chat room/quiz/media/review/terminal projection + cost breakdown factories
│ ├── history_contracts.py ← Descriptive paged Chat history response, re-exported by contracts.py
│ ├── history_paging.py ← Physical range selection over retained chat/progress JSONL chains, frozen room-bound page/continuation cursors and read gaps; no stored history copy
│ ├── cost_breakdown.py ← Ledger-derived dashboard buckets and root-task detail breakdown over the same physical-attempt authority
│ ├── projects.py ← GET/POST /api/projects, /from-task, /update, /delete
│ └── _helpers.py ← Shared request-root/coercion/JSON error envelope and `run_sync_to_completion`, the settled worker wait for request-owned blocking work
├── tools/ ← Auto-discovered tool plugins (registry.py owns discovery; frozen module list for packaged builds)
│ ├── registry.py ← Tool registry SSOT: loads tool modules, exposes schemas, executes safely; owns the shell-guard/process-tool membership sets
│ ├── core.py ← File/data tools (read_file, write_file, list_files) + code search and digest helpers
│ ├── core_file_tools.py, core_secret_paths.py, core_artifacts.py ← Core-tool leaves: the read/list file tools with the shared resource-access helpers; the restricted-subagent physical read-denial policy (owner secrets/control state, data roots, repository credential locations, listing redaction); the verbs that put something in front of a human (§6 Credential fence and byte masking)
│ ├── shell.py ← Process tools `run_command`/`run_script` (in-process `_active_subprocesses` tracking; §9)
│ ├── shell_guards.py ← Shared process-path inspection helpers and retained target extractors; process admission is owned by registry_guard_process (§6)
│ ├── registry_core.py, registry_guards.py, registry_guard_process.py, tool_context.py ← The registry's leaves: the execution authority (load, schemas, safe dispatch); the host-owned pre-dispatch guards (capability/resource, managed-update and skill-payload constraints); process admission over the prepared target with post-execution observations (§6 Safety and runtime mode); and `ouroboros/tools/tool_context.py`, the concrete `ToolContext` + `BrowserState` (its protocol is `contracts/tool_context.py`)
│ ├── git.py ← Git/write tools with the advisory, triad, and scope review commit gates (§6 Git and commit review)
│ ├── git_plumbing.py, git_repo_edit.py, git_vcs_ops.py, git_review_cycle.py, git_evolution.py ← The git tool's leaves, re-exported by the facade: shared low-level plumbing; the uncommitted repo write and exact-match edit surface; generic VCS inspection and rollback; staging plus the advisory/triad/scope review and reviewed-material binding; evolution-campaign authority at the reviewed-commit and publication boundaries
│ ├── search.py ← Web search tool (OpenAI Responses API, LLM-first overridable defaults)
│ ├── browser.py ← Playwright browser tools with per-ToolContext lifecycle and thread affinity (§6 MCP and browser-facing external tools)
│ ├── vision.py ← Vision LLM tools for browser screenshots and uploaded images
│ ├── vision_process.py ← Tracked vision-child IPC: validated model/physical-attempt receipts, result recovery and parent-owned cancellation
│ ├── knowledge.py ← Persistent topic-based knowledge files with an auto-maintained index
│ ├── memory_tools.py ← Memory registry tools for tracking data sources, gaps, and trust
│ ├── health.py ← Codebase health tool: complexity metrics and self-assessment
│ ├── compact_context.py ← LLM-requested tool-history compaction trigger; stores the pending request for the next round
│ ├── control.py ← Control tools: restart, timeout settings, scheduling, review, chat history, model switching; publishes the strict `subagent_id`+objective `schedule_subagent` contract and the compact `wait_task` projections (§6 Delegated subagents)
│ ├── control_delegation.py ← Delegation-budget and in-task project-scoping affordances (`ensure_project_scope` handler; §6 In-task project scoping)
│ ├── control_events.py, control_routing.py, control_runtime.py, control_scheduling.py, control_subagent_spec.py, control_task_results.py ← The control tools' leaves: emitting one control event and waiting for its durable handler outcome; routing real work out of a conversation lane into a supervised task; runtime self-control (restart, promotion, evolution, memory, model); scheduling one live subagent; the published `schedule_subagent` parameter surface and its validation; absorbing a child — reading one result or waiting on a batch
│ ├── tool_discovery.py ← Tool-discovery meta-tools: confirm registration, no delayed capabilities
│ ├── tool_result.py, tool_catalog.py, tool_resolution.py ← Dispatch-side vocabulary: the typed internal tool result with its byte-compatible legacy text adapter; the intrinsic tool descriptors; argument normalization with physical target binding
│ ├── arg_feedback.py ← What a tool says about an argument it did not obey: the one-line "ignored" disclosure and the typed refusal naming field, value and repair (DEVELOPMENT "LLM-first affordances")
│ ├── review_response.py ← Pure response-envelope projection for multi-model review rows
│ ├── shell_process.py, shell_effects.py, shell_outputs.py ← The command-running substrate: shared process execution; what a command did to its working tree and which of it was throwaway; declared process outputs (resolution, fingerprints, artifact registration, per-path export eligibility over the workspace-patch credential-shape SSOT)
│ ├── plan_review_artifacts.py ← Exact plan-review waves and separate current author subjects in existing source handles, bounded successor index, reviewer-continuation inputs
│ ├── evolution_stats.py ← Generates evolution.json metrics from sampled git history
│ ├── owner_delivery.py ← Owner event delivery: live queue XOR `pending_events` fallback with sticky deferral, preserving narrative order after the first live failure
│ ├── deliverables_shell.py ← cp/mv/ln into deliverables with symlink checks
│ ├── shell_audit.py ← Post-exec custody audit for process tools
│ ├── process_facts.py ← Per-call selected environment, secret egress masking and typed process/runtime facts consumed by loop_tool_execution; the regex harvest stays a read fallback
│ ├── write_shape.py ← Retained interpreter/non-interpreter syntax helpers; process permission is owned by the task/resource and Supervisor contract (§6 Safety and runtime mode)
│ ├── extension_dispatch.py ← Extension tool dispatch (contracts preserved; discovery stays in registry.py)
│ ├── release_sync.py ← `sync_release_metadata` (version carriers) used by commit-admission preflight; `_preflight_check` uses `check_history_limit`; the carrier-span SSOT (`VERSION_CARRIER_SPANS`, `substitute_carrier_spans`, the `carrier_only_change` predicate) shared by the managed-update resolver and the commit-triad pack cut (§10 invariant 2)
│ ├── review_synthesis.py ← Shared synthesis helpers; the parser/aggregator lives in plan_spec.py
│ ├── ci.py ← CI trigger/monitoring
│ ├── claude_advisory_review.py ← `preflight_review` (callable `advisory_review` alias) and its admission policy: api_chat rows ride review_native_episode, agent_session rows the AgentSessionReviewExecutor; a native episode ending on its transcript bound is the typed non-blocking `ADVISORY_SKIPPED: native_transcript_bound_exceeded` (keyed on the structured `native_transcript_cap_exceeded` code), and every episode exception keeps `failure_custody()` as the advisory `usage`; the MANDATORY FULL READ corpus is measured at prompt build (`preflight_review_prompt._mandatory_read_corpus_chars`) and declared to the episode, which never raises the send bound — a shortfall shows in the prompt's MANDATORY READ budget and the usage as `native_multiple_windows_required` (§6 Commit advisory cycle, Native tool-round episode)
│ ├── preflight_review_prompt.py, preflight_review_run.py ← The preflight (advisory) pre-review: prompt assembly with its git captures, and the run with its result parsing; the pack applies the shared span-only release-carrier cut with the same `PACK EXCLUSION NOTE`
│ ├── recent_tasks.py ← Read-only context recovery
│ ├── commit_gate.py ← Commit gate: `_record_commit_attempt` (LLM claim synthesis), `classify_review_block`/`attempt_block_class`, `check_identical_verdict_refusal`, `count_paid_review_cycles`/`check_review_cycles_ceiling`, `commit_review_contract_fingerprint`
│ ├── git_rollback.py ← Wraps `git_ops.rollback_to_version`
│ ├── git_pr.py ← Five PR tools (non-core)
│ ├── github.py ← Issue + PR tools (frozen tool module): the shared process binding selects the active Project and an explicit repo flows through every subcall; discovery reads token sources or native CLI configuration without an authentication probe; `_gh_run` is the structured transport read `_gh_cmd` projects onto the string ABI; only its own target refusals (`GH_TARGET_INVALID`/`GH_TARGET_REQUIRED`) enter the tool-result sidecar
│ ├── parallel_review.py ← Prepares the triad packet and scope briefs before dispatch; wave money admission, scope-first hold and executor usage-scope propagation; responding reviewers count independently of diagnostic reading coverage (§6 Surfaces and money admission)
│ ├── plan_review_references.py ← Reference projection that also writes its own provenance rows (`logs/progress.jsonl`), never a second plan authority
│ ├── plan_review.py ← `plan_task` engine: evidence, packet, review-substrate fan-out, `plan_review_state` v2, shared paid-cycle cap and free identical replay; no scouts, assembled repository pack or plan_class (§6 Plan construction and review)
│ ├── plan_review_runtime.py ← Plan-review deadline rail, `ReviewSlot` rows, `plan_slot_fit` + `preflight_oversize`, health snapshot + `plan_wave_replay_decision`, `plan_review_advisory_open` emitter
│ ├── plan_review_collect.py ← The event route's collection side: the per-slot settlement progress line, the ONE system mailbox frame the last released slot writes, and the $0 collection of an open wave (drain window 0; reconcile-before-supersede); no wave state of its own
│ ├── plan_spec.py ← Pure plan-spec parsing/aggregation (`resolve_constitutional`); no I/O
│ ├── plan_evidence.py ← Bounded plan-evidence manifest; the runtime data plane is denied
│ ├── plan_packet.py ← Reviewer packet; the governance pack inlines BIBLE + ARCHITECTURE in full for self-modification plans, nav maps otherwise
│ ├── plan_render.py ← Wave view + `PLAN_REVIEW_CONTROL_JSON` footer; no independent behaviour
│ ├── review.py ← Acceptance review + multi-review adapters
│ ├── review_multi_model.py, review_file_pack.py, review_prompt_text.py ← Commit-triad fan-out, working-tree file packs and prompt vocabulary; `span_only_release_carriers` shares the advisory/triad carrier cut and its disclosure, `triad_pack_exclusions` avoids byte-identical inline governance duplicates, and a managed subject retains full texts (§6 Guaranteed-fit ladder)
│ ├── review_context_atlas.py ← `repository_index`: compact tracked-path map plus touched-file and direct-importer facts; no file bodies, admission, budget or writes, and no restriction on what a reviewer can read (§6 Scope review by retrieval)
│ ├── governance_context.py ← Shared triad/scope/advisory/deep governance tiers: stable inline rules, bounded change-class rules and physical-source navigation; every omitted inline body has an explicit disposition (§6 Governance delivery)
│ ├── query_code.py ← Read-only code intelligence; `root=user_files` with path guards, denied to subagents
│ ├── edit_ops.py ← `apply_patch` + `edit_batch` with shared `_syntax_check`/`_unified_diff` backing write_file
│ ├── media.py ← `ocr_pdf`, `youtube_transcript`, `extract_video_frames` (dependency-optional, typed capability envelopes; frames under `artifact_store/video_frames`)
│ ├── verify.py ← Independent check execution through the SAME pre-exec guards (deliberately NOT a process-command tool); receipts append to `<drive_root>/task_results/artifacts/<task_id>/verification_receipts.jsonl`; `expected_match`: substring (default) · exact · exact_line · json_equals · bytes_equal; the exit-masking sensor also lets `run_command`/`run_script` append ONE advisory note plus `exit_masking_reasons` to a masked GREEN envelope without changing status or writing a receipt (§6 Tool capability and execution)
│ ├── review_helpers.py ← Shared review helpers: governance-doc loading, checklist section slicing, the prompt-size SSOT, the density-calibrated input cap and the probe sample `density_probe_sample`
│ ├── review_binary_context.py ← Staged/parent Git object metadata for review packs
│ ├── review_subject.py ← `ManagedReviewSubject`; `capture_review_diff` stays byte-identical for non-managed callers
│ ├── review_admission.py ← Fits the triad packet, prepares scope briefs and their final source manifests, discloses bare-scope delivery migration once, and admits paid seats together through `review_wave_budget_gate`; cold-start density probing belongs to triad packet fit (§6 Surfaces and money admission)
│ ├── review_revalidation.py ← Review-contract fingerprint revalidation
│ ├── scope_review.py ← Whole-repository intent/scope/coupling review by retrieval on every row and in every context mode; source manifests and diagnostic coverage ride both transports, substantive findings follow configured enforcement (§6 Scope review by retrieval)
│ ├── scope_review_session.py ← One brief builder for both scope deliveries: intent, governance tiers, compact index, touched paths, task evidence and exact inline/paged subject; final source manifest and pre-run delivery disclosures
│ ├── scope_window.py ← Scope window sizing and five-way evidence provenance; designated-default and conservative fallbacks stay disclosed, never review authority
│ ├── scope_review_contract.py ← Pure scope-item parser (`normalize_scope_items`); also consumed by scripts/validate_scope_receipt.py
│ ├── scope_required_sources.py ← Change-relative protected/prompt/contract sources, derived families and twins; exact candidate identities, deleted preimages and paged diffs, inline satisfaction and unavailable-source diagnostics; final manifest hash and policy version bind review replay
│ ├── services.py ← Service mini-manager with process-group cleanup
│ ├── skill_exec.py ← list_skills/skill_review/toggle_skill/skill_owner_action/skill_exec; runtime allowlist python/python3/bash/node/deno/ruby/go; gated by enablement + fresh review + hash
│ ├── skill_publish.py ← Thin publish transaction over the four leaves; success is PR-receipt-gated
│ ├── skill_preflight.py ← Read-only skill preflight; a module widget's `render.entry` is checked for containment inside the skill directory and parsed with classic-script grammar, because the widget frame runs it as an inline script
│ ├── project_journal.py ← journal_write/read, workpad_read/write, `update_focus`, journal_tail_digest (over-limit rejected); root-only foreign reads honor explicit project scope, foreign scoped writes refuse; owns `mirror_tree_coordination_to_journal`
│ ├── presence.py ← configure_presence, initiate_presence, typed completion/cancel
│ ├── task_tree.py ← tree_note/tree_read (storage SSOT: task_tree_ledger.py)
│ ├── followup.py ← One deferred follow-up into `state/scheduled_tasks.json`: exactly one trigger (`once` ISO or 5-field cron+tz), cap 2 pending; preserves the SOURCE address (`project_id` plus the originating `chat_id`) instead of defaulting to the global owner chat; a one-shot is consumed on the typed tombstoned-Project refusal, while a deleting Project and transient refusals stay retryable
│ ├── join_ledger.py ← Child-result absorption: validates lineage + exact hashes; dispositions integrated/irrelevant/deferred; `CHILD_RESULT_STALE`; keeps peek_task/discard_child_result
│ ├── delegate.py ← Delegation facade verbs `delegate_start` (with `retry_of`), `delegate_wait`, `delegate_cancel`, `delegate_answer`; the host pre-start rides the same wrapper and the shared `subagent_runtime.exact_start` (§6 Delegated subagents)
│ ├── delegate_integration.py ← Delegated-patch integration: `_mutation_authority`, `_provision_snapshot`, retry-binding validation, `_capture_terminal_patch`; the skill-payload cluster (`_payload_mutation_authority`, `_write_payload_patch_artifacts`; reserved paths refuse the WHOLE apply as `blocked_reserved_paths`) and `integrate_payload_patch` (CAS, index-free git apply in NO-REPOSITORY mode under `GIT_CEILING_DIRECTORIES`, typed `INTEGRATE_APPLY_NO_OP`; a successful apply QUEUES the extension reconcile request via `request_extension_reconcile`) (§6 Delegated subagents)
│ ├── delegate_payload_patch.py, delegate_terminal_evidence.py, subagent_integration_delegated.py ← Delegation leaves: the skill-payload patch pipeline (capture artifacts, guards, the live apply); the terminal story of ONE delegated run as the parent reads it; `integrate_delegated_patch`, the delegated-run disposition seam
│ ├── subagent_integration.py ← integrate_subagent_patch (sha256, 3-way --index, protected-path gated, genesis refused), external-workspace audited verdict, `coop_already_in_tree` no-op, compare_subagent_patches
│ └── patch_verdict.py ← The ONE verdict writer for both patch pipelines (`_write_verdict`): subjects are minted by the writer (`run_<rid>`), never prefix-matched by readers; each decision lands twice — artifact + typed `delegate_run_patch_verdict` custody row
├── delegate_start_claims.py ← One short pre-transport transaction serializing the zero-run/custody recheck + `START_REQUESTED` append; transport and waiting stay outside claim locks
├── process_containment.py ← Env-token container membership (`OURO_PROC_CONTAINER_*`; /proc environ on Linux, `ps -E` on macOS, kill-on-close Job Object on Windows — spawned suspended-then-adopted so a child cannot run before Job membership) with live-state read at reap; an alive-or-undeterminable member is an honest hard-block answer, never a kill guarantee; unreadable strangers are warnings, never members by uid/start-time alone (a detached descendant that hides its token is a disclosed detection gap); `process_group_has_live_members` excludes zombie-only groups (§6 Delegated subagents)
├── process_custody.py ← `spawn_supervised` + durable process_ledger.jsonl; `reap_orphaned_processes` (strict identity, `retained_purposes` across generations); `start_parent_lifeline`; `quiesce_custodied_services`; `live_daemon_root_pids`/`live_kept_service_pids` select teardown exclusions; `process_stop_snapshot` binds the stop fallback to the observed rows, and `stop_ledgered_processes` requires measured identity and confirmed exit (Runtime topology below; §9)
├── platform_layer.py ← Cross-platform process helpers, the descendant-enumeration seam, the Windows Job Object ABI (Platform substrate below)
├── verified_download.py ← Shared exact-size/digest verification and atomic cached byte delivery; consumers retain their own transport timeout and error vocabulary
└── node_runtime.py ← Execution-probed Node runtime health (`node_runtime_health`, memoized by path/mtime/size — a missing binary is never cached), `select_skill_node_runtime`, `skill_node_emergency_path_dir`
Devtools boundary
devtools/ (including devtools/benchmarks/cybergym/) lives outside the runtime and package discovery: no runtime imports, normal review, artifacts in an external output root; a sentinel-marked isolated root suppresses rotation warnings. CyberGym keeps budget/result settlement in cybergym_adapter; cybergym_custody._CustodyMixin owns gateway admission, normal and cancelled waits, and their shared terminal-custody transfer, while cybergym_lifecycle._LifecycleMixin owns startup, official-verifier delivery and cleanup. Both wait paths retain their distinct bounds around the same attempt identity rather than creating another scheduler. devtools/e2e_live/ is the live E2E stand — K staggered isolated real servers running the owner-shaped scenarios SM1, SW1 and SK1, accepted over durable artifacts and a browser probe, admitted through the same seed gate and manifest seams as the benchmark launchers. Its operator manual is devtools/e2e_live/README.md; the one rule binding runtime changes is DEVELOPMENT "Live E2E stand".
devtools/benchmarks/cowork_bench/ runs a clean seed inside the pinned upstream task containers, preserving MCP process state through a local proxy; its launcher owns campaign spending, resource limits and result ledgers, while its offline audit preserves the official evaluator as scoring authority (see its METHODOLOGY.md).
Gateway Boundary v1
ouroboros/gateway/ is the single inbound browser/CLI boundary (ouroboros/gateways/ holds the thin outbound adapters): contracts.py owns the envelopes, with the endpoint index in endpoint_index.py, router.py collects the routes, and files.py/host_service.py stay separate trust boundaries. The contract is EXECUTABLE — gateway/schema.py validates ingress against JSON Schema derived from those TypedDicts. Domain handlers translate transport into calls on existing runtime owners and must not acquire a second copy of queue, review, settings, or lifecycle policy. The facade exists for dependency direction: the UI evolves without importing the agent body, and the runtime evolves without ad-hoc browser contracts.
gateway/owner_settings.py is the ONE owner-scoped settings WRITE seam (the generic POST, the single-decision endpoints, and onboarding; membership = calling _owner_update_settings, directly with a transform or through _owner_write_settings with a whole document). The settings lock is a precondition — a timed-out acquisition refuses before any precondition or write — and CommitBoundary marks the commit instant so a later-step failure is reported as that step, with saved a field on BOTH sides of the boundary: an envelope that merely omits the field would be ambiguous. Invariant prose: the module docstring; the read-change-write and digest rule: §7 Reading and writing the settings document.
Frontend calls go through web/modules/api_client.js with the JSDoc mirror web/modules/api_types.js; gateway parity tests pin the mirror. Extension HTTP lives under /api/extensions/<skill>/… with namespaced WS dispatch.
CLI / Headless Boundary
ouroboros.cli is a client of the same gateway/queue — no second task engine. Its parser is the command-surface SSOT (server, run, tasks, chat, logs, evolve, schedule, settings, skills, marketplace, local-model, MCP); streaming commands reserve stdout for the final answer/patch/result/JSONL and send progress to stderr.
POST /api/tasks creates an ordinary managed root; GET /api/tasks is a non-materializing list; GET /api/tasks/<id> returns the effective durable result; /events is the archive-aware SSE stream (§3 History reads and the SSE v2 transport); /artifacts/<name> serves simple filenames confined to data/task_results/artifacts/<task_id>/ — a stored arbitrary path is not a download capability. The CLI refuses any delegation_role other than root, the gateway rejects caller lineage/subagent labels, and only schedule_subagent creates children; reserved service metadata is written after caller metadata. Admission reserves the task id plus a worker-pool slot under one queue lock, and a failure rolls back only the token-owned row with a loud typed refusal; blocking admission and materialization run off the HTTP event loop (gateway._helpers.run_sync_to_completion), so a cancelled HTTP waiter never cancels the admitted task. Attachments are copied into the effective task drive before enqueue; artifact-store references are not host-path authority.
Workspace tasks default memory_mode=forked; shared is rejected for an external workspace and materialized on a forked child drive for project scope — the stored memory_mode reports what was requested while drive_root reports where the task executes, so isolation does not depend on relabelling the request. The mode isolates the execution drive and the knowledge seed; identity and scratchpad writes still land on the canonical root the next context reads.
--detach returns only after durable admission; --no-stream polls to completion; waiters treat a result as terminal only after the artifact state leaves pending/finalizing, and an explicitly-partial cost gets a bounded 60-second finality wait before partial flags stay visible. ouroboros run exits 0 only for a completed lifecycle, a clean execution axis, no failed/degraded objective, and a finished artifact bundle — strict exit semantics keep shell automation from interpreting "the model answered" as "the requested workspace deliverable exists"; --patch/--patch-out are stricter still (failed/missing patch, no-change, empty payload, or unfinished finalization is an error). workspace_patch.json is written for EVERY workspace finalization (including no-change and failed) and is the truth source for CLI strict-patch — it distinguishes omitted vs no-op vs failed; workspace.patch exists only for ready_with_changes. The comparison base, capture rules, file-reference manifest, child-drive copy-back and startup prune are §6 Headless finalization and workspace patch capture; canonical results, artifacts, genesis repos and memory exports survive that prune.
CLI schedules and skill-manifest schedules enqueue ordinary supervisor tasks — no parallel scheduler. resync_skill_schedules() mirrors manifests (executable skills with supervised-task permission only) into the same table after lifecycle changes and on ticks; a blank timezone means the DST-aware system zone, with a fixed-offset fallback only when the zone is unrecoverable; the active schedule digest rides task/consciousness context.
Packaged CLI artifacts are a tiny wrapper + installer, not a second PyInstaller runtime: packaged_cli locates repo.bundle, its manifest, and python-standalone, bootstraps the launcher-managed repo, and invokes the same ouroboros.cli under the embedded interpreter with canonical env. Packaged server is refused — it would bypass launcher-owned bootstrap, process identity, restart, and cleanup. run --start is loopback-only, starts the desktop app when no ready gateway answers, follows data/state/server_port, and waits for /api/health + supervisor_ready. Nested AppImage extract-and-run gets a private TMPDIR because the type-2 runtime keys extraction by TMPDIR + digest; a marker-gated AppRun custodian removes the verified extracted child after the launcher exits.
Release builds also carry Node and ripgrep. Skill-side Node resolves bundled-first via node_runtime.select_skill_node_runtime() (falling back to a healthy PATH node when the bundled candidate fails the health probe), while the four generic process launch surfaces run the opposite policy (process_interpreters.resolve_process_node): a PATH candidate that passes the execution health probe stays byte-identical in argv and child env, and the bundled runtime substitutes only when that candidate is missing or probe-dead, attesting the child-env PATH prepend — never inside a non-local executor backend. The Node downloader verifies the official archive against published SHASUMS and the macOS signing pass re-signs it under the hardened runtime; ripgrep is archive-hash verified, and search_code still pre-enumerates policy-approved files before invoking it, so bundling a faster binary does not widen search authority. Every bundled consumer searches bundled_resource_bases(): OUROBOROS_BUNDLE_DIR → frozen root → interpreter-ancestor roots → source checkout — server/CLI children run from the managed repo with neither _MEIPASS nor an in-bundle module path, and ancestor recovery covers older launchers starting newer checkouts.
The embedded interpreter must never write into the signed application (codesign seal): entry processes suppress bytecode before project imports, and embedded_python_env() redirects bytecode to data/state/pycache and user installs to data/state/python-userbase (pip_install_target_args() adds --user for direct python-standalone invocations and aliases). The pip helper first recognizes an invoked virtual environment from pyvenv.cfg beside its standard bin/Scripts directory, before resolving the executable symlink, so venv installs stay inside that environment. Disclosed residual: the userbase outranks bundle site-packages and nothing prunes or versions it — recovery is manual (remove the directory, relaunch).
Workspace binding changes the contextual repo, never the system repo for BIBLE/prompts/review governance. /api/tasks and project-room promotion share workspace_admission.validate_workspace_root() (exists, ordinary directory or exact Git worktree root, disjoint from the system repo and data drive, resolved/bidirectional/case-folded); an empty Project binding is idempotently provisioned as a standalone git repo unless workspace="none". Ordinary folders support direct file/process work; Git-specific operations require a Git worktree. Binding changes the default file/process/VCS target plus memory/lease/preflight/finalization; it does not remove top-level tools or downgrade the Architecture context in Max mode (root=system_repo stays; root=skill_payload takes bucket+skill_name). The workspace executor is a process-routing boundary, not a sandbox: executor_ref is host-owned, mappings must cover the workspace without overlapping system repo/data, network=none only when the backend implements it, and executor processes enter durable custody records. Preflight snapshots git state (bounded porcelain rows), manifests/scripts and tool availability into the full workspace_preflight.json artifact with a bounded summary in metadata; tools_on_path/tools_missing_from_path are named that way because shutil.which measures PATH presence, not executability, the keys stay frozen because they ride durable, replaying task metadata, and a collection failure is a disclosed error summary, never a fictitious full artifact.
Ordinary-directory delegated sessions negotiate mutability.workspaceKinds from the engine catalog and submit execution.workspaceKind=directory on the existing agent mode. The parent selects directory_strategy=direct|copy and optional scope_paths: copy requires an explicit input footprint, direct may name future output paths for capture. Geometry is a WRITE-side request, so copy or a non-empty scope_paths for a read-only child is a typed schedule-time refusal that states the repair, while direct with no selected paths is the documented default and means exactly what omitting both means. The engine owns copy materialization, per-file CAS, apply and discard; the host preserves the complete before/after artifact closure through the existing custody path — no full-tree direct baseline copy, second file engine, scheduler or retention timer. Complete means the selected footprint, not the whole source folder; partial selections keep undisposed changes in engine custody, and an explicit discard records rejection without undoing applied effects. A missing capability on an older serving engine is an explicit refusal, and file or GUI work is never judged from an empty text diff.
Forked/empty task state lives under data/state/headless_tasks/<task_id>/data: a forked drive copies identity.md, WORLD.md, registry.md (a project fork carries memory/knowledge/patterns.md and omits global knowledge; an empty drive starts blank); dialogue, scratchpad, mailbox, and history never cross. memory_export.json is an explicit artifact, never merged automatically.
Complete input sets are preserved in the existing artifact store: task_contract.attachment_manifest is a preview of at most 25 rows, and an additive attachment_manifest_ref names the full immutable JSON under source_handles/context_checkpoints with count, size and SHA; inheritance, owner-mailbox reads, physical retries and copy-back resolve the complete closure through artifacts, and a preview never substitutes for an unreadable full reference. Immutable artifacts keep their captured size/SHA identity through collection, rebasing and manifest refresh and disclose changed bytes; a failed artifact or input copy stays a per-file failure plus a pending child_ref_promotion ref that is retried, and no artifact observation rewrites task lifecycle, price or objective. Native-image bounds and transport-specific Telegram limits remain separate from complete work-order delivery.
The two router continuation tools require an explicit predecessor_task_id ("" = fresh; omission or null refused before lookup, enqueue, or spend); predecessor source metadata survives snapshot/restore, and Main receives a defensive provider-only copy (inline threshold, persisted narrative, bounded legacy row, or an explicit gap — never a raw head/tail substitute), while exact reads and work orders stay full.
System self-modification, external workspace, and genesis remain distinct task classes. A genesis child gets an inspectable deliverable_manifest.json listing with streamed sizes and hashes for readable, stable regular files and explicit gap rows otherwise (complete, gap_count; symlinks stay un-followed): the listing is discovery, so its gaps never fail the task, while an actual copy/ZIP capture requires stable source bytes, descriptor and path identity and any expected digest, and growth beyond the initial regular-file size fails immediately instead of waiting for EOF. Global/system installs stay runtime-policy reviewed, and sudo is always non-interactive (sudo -n).
Runtime topology
Two continuity roles: launcher.py owns the PID lock, bundle bootstrap, the server process, presentation, the restart signal, and cleanup (desktop launchers run outside the managed repo; Android source-host mode retains a separate immutable seed); server.py is the self-editable inner runtime. Native packages ship an opt-in systemd user unit as an alternate ingress, not a third role — deliberately without a restart policy, because the launcher owns managed restart, the crash fuse, and panic-to-complete-stop (KillMode=control-group).
Spawn custody: POSIX children start in a new session/process group; Windows creates the server suspended, assigns a kill-on-close Job, then resumes — failure to establish Job custody refuses to run. The launcher Job permits explicit breakaway; only the shared daemon requests it, while ordinary generation children remain covered by Job close. Lazy worker and direct-server starts use the same platform helper. An old packaged or external Job that cannot confirm breakaway retains the earlier working spawn with a disclosed survive-close limitation; desktop managed source updates cannot replace their immutable launcher. The launcher records data/state/server_process.json (PID, pgid, server/repo paths, requested and actual ports, argv, creation time) and re-proves identity before cleanup. Forced tree/group cleanup excludes the shared daemon subtree; existing listener sweeps retain their separate scope.
Same-install reaper (launcher_server_reaper.py): holding the PID lock licenses the reap, which runs at main() preflight and at the top of every launcher generation. A PID is proven only on three live facts — the exact <REPO_DIR>/server.py argv token, OUROBOROS_DATA_DIR, and OUROBOROS_MANAGED_BY_LAUNCHER=1 — revalidated immediately before the signal, with descendants captured before the root signal and the whole pass bounded to three rounds. Kills require the byte-exact /proc environment: ps -E output never authorizes a kill, because argv is indistinguishable from an env assignment there, so non-/proc hosts stay report-only. Server selection does not require a custody row — missing server records are the defect being repaired. Caller-supplied retained daemon roots filter descendants before final root revalidation. POSIX-only; Windows generation children die with the launcher Job while the shared daemon breaks away; never on panic or window-close. The startup stray check is report-only and annotates same_install/foreign.
Durable process custody (ouroboros/process_custody.py): spawn_supervised() records every long-lived child in data/state/process_ledger.jsonl — {pid, pgid, fingerprint{start_time, cmd_sha256}, purpose, scope task|session|daemon, owner_task, session_id}. The custody reaper runs at server startup and on the 10-minute supervisor tick and kills only entries whose generation or task owner is gone, by STRICT fingerprint — never by command-line class, so dev and packaged instances can coexist; a recorded bare tick never authorizes a kill (the downgrade-safe start-time token: Platform substrate below). Current-generation session processes take a cheap non-zombie liveness check that only ever KEEPS; daemon entries and explicitly retained legacy installation-process records are kept, with skill companions the exception — reaped on owner-uninstall or foreign generation, log-only by default (process_would_reap), fail-safe keep-all on an unknown live-skill set. Every worker tree-kill (supervisor/worker_pool_lifecycle.kill_worker_tree) and custody reaping of a stale session ancestor preserve retained daemon subtrees; _active_subprocesses, existing port sweeps, and generation Job Objects complement durable custody.
start_parent_lifeline() gives our python entrypoints a watchdog that group-suicides when the spawning parent dies: inside a multiprocessing child it waits on the spawner's parent sentinel — under forkserver the ppid is the forkserver, which outlives a dead supervisor, so a ppid watch would never fire — while a plain subprocess falls back to its ppid. Windows uses native creation FILETIME plus canonically quoted psutil argv for new fingerprints and one PID/PPID snapshot for selective tree termination; it never applies taskkill /T to an ancestor of a retained branch, and legacy empty-birth Windows rows keep their liveness-only retention but cannot authorize forced signals (§9).
Launcher lifecycle: the lifecycle thread removes stale port state, starts the server, follows the actual port file, and waits for health. Exit 42 requests a managed restart (refresh bundle metadata/remotes, sync dependencies; the one visible install retry: §2). Five ordinary crashes within 120 seconds stop automatic restart; a panic exit performs full cleanup and terminates the outer process rather than the retry loop.
Presentation: the Linux browser fallback checks DISPLAY/WAYLAND_DISPLAY before touching pywebview; GTK needs a live Gdk.Display.get_default(), while Qt is trusted on env alone — probing it constructs a QGuiApplication that can itself abort, so the probe would cause the crash it exists to avoid. On probe failure the same launcher supervises the same server, prints the authoritative URL, and opens the system browser best-effort — the browser is the owner's application, deliberately outside process custody and teardown. A repeated launch that loses the PID lock soft-polls the port file (~10 s) and opens the last-read loopback URL best-effort. Browser-mode SIGINT/SIGTERM handlers only set the shutdown event; sys.exit paths leave PID-lock release to the registered atexit owner, because a second release could unlink a newer launcher's lock.
Extension children, delegated runtimes, services, the local model, and companions all sit beneath these roles: every long-lived process enters the custody ledger or a process group. Disclosed residual: shutdown admission is not atomic with publishing a spawned child (a signal can land between Popen and the record) — tracked, not a reason for a second launcher.
Standalone preflight_review and its advisory_review alias use a finite outer settlement envelope: resolved hermetic test budget plus max(task absolute ceiling, LLM transport bound + finalization grace) plus finalization grace, following the existing plan-review wrapper. Inner test, critic, owner deadline and cancellation rules remain separate; commit_reviewed retains its existing terminal-wait behavior when calling the same preflight handler.
Android host (experimental)
android/ is a platform of the same repository, Python core, SPA and official update channels. It runs the full core in an ARM64 GNU/Linux chroot on an already Magisk-rooted Android device; LLM APIs and subscriptions remain remote. There is no Pixel selector: physical qualification is limited to Pixel 10a, and the manifest's API-26 floor is not a tested compatibility matrix. Installation and recovery instructions live in docs/ANDROID_INSTALL.md.
android/install.py verifies the common source-release archive, inspects USB/root/ABI prerequisites and provisions the canonical /data/local/ouroboros-phone/rootfs. android/provision/ owns pinned upstream inputs, package/toolchain preparation and the installed dependency record, rather than distributing a copy of someone's runtime. Inside Linux, /opt/ouroboros/{repo,data,venv,tools,signing,launcher} separates mutable source, personal state, tools, personal APK key and the immutable seed. A successful installation rerun preserves source/data/key; an interrupted one resumes its original source archive. No installer rerun substitutes for ordinary managed updates.
bootstrap/enter-linux creates private mounts while retaining Android's network namespace and sets its own oom_score_adj to 0 before spawning descendants, preserving root privileges while allowing kernel OOM recovery. It forwards explicit OUROBOROS_PREFLIGHT_TEST_WORKERS and OUROBOROS_PREFLIGHT_TIMEOUT_SEC, defaulting this Android entry to 2 workers and a 3600-second total test budget; the existing preflight owner still enforces its two-worker floor and full test coverage. core-control is a one-shot start/status entry, never another restart loop. It starts the common source launcher with --no-ui, --launch-intent owner|automatic, --host-update and --seed-bundle. Automatic entry preserves the existing Panic marker; an explicit owner Start resumes normally.
The seed directory retains its original VERSION, repo.bundle and manifest. BootstrapContext.app_version validates that seed version independently of the running source's APP_VERSION. Exit 42 synchronizes source metadata/dependencies, releases the PID lock and re-executes the same launcher process from the current repository. Its next generation adopts the selected native host before starting the core; the immutable seed is neither rebuilt from personal edits nor relabelled as a newer official release.
bootstrap/update-host builds source with host/build.py and the installation's persistent signing/host.keystore plus host-password. Native inputs include host source/resources, VERSION, the icon, desired platform group digests and android-sdk/installation.json; a release-version change rebuilds the APK, while an unrelated core edit without a version change does not. PackageManager replacement streams the APK through android-exec; installed bytes and signing certificate are read back. Local versionCode increases on every installation, including rebuilding older source for rollback. The data/state/android_host.json receipt records source/input/artifact/certificate facts; android_host.lock serializes native installs, and data/android-builds/ retains candidates. Source-selected bootstrap scripts are copied and verified at their existing outer-bin/tools destinations even when no APK rebuild is needed. Key creation belongs only to provisioning; loss requires restoring that identity, never silent replacement.
Before native compilation, the same hook calls current-source provision/runtime.py::ensure_platform, also used by first installation. It compares tracked package/snapshot recipes, Java SDK pins, native AAPT/vendor pins and patches, the common Node pin, and Playwright pins/runtime lock against completed group digests in android-sdk/installation.json. Unchanged groups reuse their installation; a Java SDK-only change does not compile AAPT. The existing downloader verifies and retains cached inputs. That SDK record marks groups platform_preparing before mutation and replaces them with completed inputs plus actual SDK output hashes only after success; interruption or source drift cannot turn a restored old Git tree into a false dependency PASS. The first upgrade from a legacy receipt prepares all groups once, reusing downloads. Node selection refreshes the common manager's tool links without starting or stopping a daemon; Playwright owns browser extraction. The Ubuntu Base archive remains immutable seed provenance, separate from supported same-Noble apt package/snapshot updates. Ordinary apt semantics do not promise package removal/downgrade on Git rollback, and major distribution migration is not implemented.
launcher_bootstrap.update_external_host contains the native build process and requires a complete artifact proof. Its EXTERNAL_PLATFORM_UPDATE_TIMEOUT_SEC bound is 3600 seconds, owned by runtime_limits.py and re-exported through config.py, independently of ordinary tool or harness timeouts. The launcher starts its HTTP readiness window only after the lifecycle has spawned the core; native preparation is not server startup time or health evidence. Shutdown interrupts the hook through the existing event and waits for its owned ProcessContainer cleanup before launcher exit. Each new core inherits OUROBOROS_EXTERNAL_HOST_UPDATE and the one generation's OUROBOROS_EXTERNAL_HOST_RESULT; worker restart verification uses that fact and its source SHA without certificate subprocesses under the campaign lock. Failed/missing/stale native evidence leaves both ordinary claimed and markerless evolution adoption unresolved. Ordinary startup retains the available core with native_update_failed in launcher logs; core health alone makes no native-success claim. Managed-update post-boot finalization independently invokes the same hook's read-only --check; mismatch uses the existing update transaction and rollback. The next launcher generation rebuilds the restored native source with a higher local installation number.
Delegated access belongs to the common actor and delegation contracts in Agent Core, not to the Android host. The same configured snapshot, trust, capture, explicit integration and retry owners apply on every platform. A requested access profile is not proof that a particular Android kernel can execute its sandbox; qualify the observed route and retain any failure.
The APK's MainActivity hosts the ordinary SPA and Android document/media callbacks. It also declares ACTION_ASSIST and exposes an in-app RoleManager consent flow on API 29+, so a normal user can choose Ouroboros as the assistant without a root-only role command. This is an Activity entry, not a VoiceInteractionService: it provides no hotword, voice session or assist context, and full WebView/private data still waits for an unlocked keyguard. CoreService and BootReceiver live in the separate :native process at the same app UID, so a WebView renderer replacement does not own the SDK bridge. Sticky service restoration and MY_PACKAGE_REPLACED request status/bridge only; BOOT_COMPLETED uses automatic core entry after unlock. android-call may start that native service on a connection failure before sending request bytes, then retry the connection; a lost mutation response stays unknown and is never resent. There is no second core supervisor.
AndroidBridge exposes generic package/provider discovery, typed PackageInstaller staging (packages.install, packages.install.status, and packages.sessions) with serialized same-key admission and a persisted pre-commit receipt; pending user consent travels through a notification holding Android's original confirmation Intent and remains incomplete until a terminal callback. The bridge also exposes typed Intents and ContentResolver operations, plus bounded location.state and location.get requests through a root/host-UID abstract socket; location.get returns a current fix on API 30+ or a last-known result on older releases and never starts background tracking. The same adapter exposes optional accessibility.state/windows/perform and notifications.state/list capabilities when the owner has enabled the corresponding Android services. The wallpaper and Quick Settings components are opt-in surfaces; their presence does not imply that the owner enabled them. DirectBootReceiver writes only a device-protected boot marker; it does not start the core before unlock. The installed rootfs lives under /data/local, accessible to an authorized root before unlock; post-unlock startup is a lifecycle policy, not a claim that the rootfs uses app credential-encrypted storage. android-exec retains arbitrary owner-root argv capability. Initial access setup uses the manifest's dangerous permissions and ordinary Android dialogs; declaring a permission does not grant it. WebView media/location grants bind to the exact local runtime origin. Its runtime UI uses loopback HTTP; the network security config denies cleartext by default and permits only localhost/127.0.0.1. The first distribution is GitHub sideloading, with QUERY_ALL_PACKAGES and the declared personal-data capabilities retained. Official publisher APK/signatures attest the reference artifact; locally built, personally signed installed APKs have their own hashes and are not covered by that unchanged publisher-artifact attestation.
Platform substrate
platform_layer.py owns OS observations and lock/process primitives; their callers own policy. kernel_file_locks_enforced probes once per real directory under a module lock, using a scratch file rather than a failed live acquisition. Unsupported-lock errors and ENOLCK select a recorded name-only tier; an unprobeable directory stays enforced for that call and is retried later. Contention retries, other kernel errors fail closed, and callers may refuse specific name-tier causes. The name tier uses O_EXCL plus identity recheck/unlink without kernel exclusion; monetary compaction refuses it while appends retain their existing policy.
A won file lock must still have a readable descriptor/path inode identity; a creator evicted before acquiring its hold recontends, and unreadable identity is not proof. Owner-aware recovery reaches the existing kernel/inode check immediately after confirmed owner death and never evicts a live writer. Unknown metadata and callers without owner-aware recovery retain the stale-age grace; permission denial is not proof of death. POSIX judges and unlinks the stale inode under its flock; Windows unlocks/closes the probe before identity-checked deletion and relies additionally on open-handle deletion refusal (CPython omits FILE_SHARE_DELETE), not POSIX's single-reclaimer guarantee. Refresh returns ownership, not a courtesy heartbeat: losing it requires abandoning protected work. Release unlinks only the held identity, before close on POSIX and after unlock/close on Windows. Windows contenders can transiently hold the path open, so unlink retries within its bound; swallowing that refusal would strand a stamp bearing a live owner's PID. LockFileEx covers one fixed byte beyond the short owner stamp, keeping the stamp readable despite mandatory locking and supporting empty files. Its OVERLAPPED is rebuilt, not cached per recyclable fd. Violation means contention; invalid-function/not-supported map to unsupported-lock errnos; other Win32 errors preserve their classification.
Birth identity is separate from PID presence. Linux prefers ticks plus boot id, then the ps wall-clock token, then separator-qualified bare ticks as a disclosed cross-boot collision limit; recovered observation capability can change the token form. The custody ledger keeps the legacy start_time spelling and the optional boot-qualified sibling so an older reader does not silently discard every owned row after rollback — a rollback meeting an unknown token would prune every row WITHOUT a kill, orphaning processes. Windows uses exact creation FILETIME; full command identity remains independent. Presence uses non-signalling process handles on Windows and retains access-denied/unknown presence rather than authorizing cleanup. Win32 calls declare full-width HANDLE arguments/results and snapshot last-error at the call; omitted declarations truncate 64-bit handles. Job termination/close checks false BOOL returns as failures, not just exceptions, so survivors remain disclosed.
Bundled resources use the CLI / Headless Boundary lookup order rather than assuming the managed server runs inside the frozen app. Node health/selection stays in node_runtime; the platform's lazy PEP 562 re-exports avoid its eager import cycle while preserving existing caller names. Platform TCP keepalive is specified with the shared transport in §6 Context fitting, retry, and compaction; kernel dead-peer detection does not shorten a cognitive operation's deadline.
Data layout (~/Ouroboros/)
~/Ouroboros/ is the default application root; APP_ROOT, DATA_DIR, and SETTINGS_PATH are independently env-overridable (ouroboros/config.py, §7).
~/Ouroboros/
├── repo/ ← the self-modifying git repository; launcher-managed git clone keeps server.py in sync (never copied per launch — §2)
│ ├── ouroboros/ ← core package (module map above)
│ ├── supervisor/ ← supervisor package
│ ├── web/ ← Web UI; ES-module pages under web/modules/
│ ├── docs/ ← ARCHITECTURE.md + architecture/ (this map: entrypoint + chapters), DEVELOPMENT.md + development/ (engineering handbook: entrypoint + chapters), CHECKLISTS.md (review checklists SSOT), CHECKLISTS_ARCHIVE.md, CREATING_SKILLS.md, DESIGN.md, DEPLOYMENT.md
│ └── prompts/ ← SYSTEM.md, SAFETY.md, CONSCIOUSNESS.md (the wake-up message template, not a second system prompt)
├── data/
│ ├── settings.json ← user settings (API keys, models, budget; §7)
│ ├── task_results/ ← durable task results (task_results/<id>.json, every write stamped `_schema_version: 1`; an inadmissible row is QUARANTINED and keeps its id occupied — `task_result_schema.py`); artifacts/<task_id>/ holds .artifact_manifest.json (private metadata) + artifact files; .scratch_manifest.json declares ephemeral scratch {abs_path: sha256} excluded from patch capture only while content matches
│ │ └── artifact_versions/<task_id>/ ← artifact recovery history, last 5 versions per name (`artifacts.py`)
│ ├── task_drives/<task_id>/ ← task-scoped scratch, including live per-call manifests; startup prunes terminal tasks after the headless retention window
│ ├── task_trees/<root>/blackboard.jsonl ← append-only swarm blackboard + beacons; tree-scoped and ephemeral (task_tree_ledger.py), pruned on root terminal
│ ├── state/
│ │ ├── state.json ← runtime state + compatibility cost projection; never the monetary authority
│ │ ├── queue_snapshot.json ← durable PENDING/RUNNING recovery projection + worker counts + explicit worker_pool_disabled_reason (§5)
│ │ ├── usage_attempts.jsonl ← append-only monetary authority: per-attempt id + state transitions; a settled attempt with cost=None and a numeric reservation bound counts at the bound (§6 Budget tracking)
│ │ ├── skill_review_root_tasks.jsonl ← append-only compact index derived from per-skill `review_history.jsonl` (writer `skill_review_history.append_history_once`, bounded-tail reader `skill_readiness._skill_names_from_review_history`); 20 MB warning at `context_budget.SKILL_REVIEW_ROOT_TASKS_WARN_BYTES`
│ │ ├── usage_attempts.quarantine.jsonl ← loud quarantine of a proven-corrupt final row; the validated prefix stays readable
│ │ ├── usage_import_watermark.json ← resumable idempotent legacy-import watermark
│ │ ├── request_wire_compatibility.json ← cross-process locked, schema-versioned 14-day exact-route wire evidence (request_wire_contract.py)
│ │ ├── capability_evidence.json ← sourced model-capability evidence (capability_evidence.py)
│ │ ├── process_ledger.jsonl ← durable process-custody ledger (process_custody.py; Runtime topology)
│ │ ├── server_port ← active HTTP port for launcher/browser handoff
│ │ ├── server_port.bindings.json ← informational endpoint snapshot owned by `server_process.py`: the main, Host Service and local-model owners publish their bound host/port with pid and process fingerprint while they hold it (`record_service_binding`/`clear_service_binding`, compare-and-remove); a browser identity fact, never a grant or a custody ledger (§6 MCP and browser-facing external tools)
│ │ ├── server_process.json ← launcher-owned server identity record for relaunch cleanup
│ │ ├── advisory_review.json ← durable advisory/review ledger (runs, attempts, obligations, commit-readiness debts)
│ │ ├── scope_delivery_migration.json ← one-time bare-scope inspection-delivery disclosure marker (review_admission.py)
│ │ ├── code_intel/<repo_key>/inventory.json ← code-inventory facts; no raw source cache
│ │ ├── evolution_metrics_cache.json ← per-tag metrics cache regenerated by /api/evolution-data
│ │ ├── evolution_campaign.json ← campaign objective/progress/history/budget
│ │ ├── evolution_checkpoints.jsonl ← append-only per-cycle checkpoints
│ │ ├── post_task_evolution_request.json ← worker-written one-shot promotion signal; consumed + deleted by the supervisor idle tick; dropped while evolution_owner_stopped
│ │ ├── post_task_evolution_counter.json ← per-drive every_n counter
│ │ ├── scheduled_tasks.json ← cron (5-field + tz) and one-shot {type:"once", run_at} schedules; consumed one-shot receipts age out past the unified GC retention
│ │ ├── claudexor_rotation_provisioning.json ← receipt of the last rotation-reconcile settings POST
│ │ ├── subagent_last_delegation.json ← bounded dated helper observations owned by subagent_history.py, with the compatible latest receipt; never live health or dispatch authority
│ │ ├── update_letter.json ← the last update letter (key = base/target/channel/ref, state, text, `last_good`); kept after apply and projected against the live HEAD (update_letter.py)
│ │ ├── projects.json ← Project registry: immutable id/chat identity, working folder, lifecycle/routing fence, revision; tombstones are durable and never age-pruned
│ │ ├── project_task_bindings.json ← schema v1 root↔Project bindings with REQUIRED typed origin; one-way enrichment; tombstoning never removes a binding
│ │ ├── ui_preferences.json ← owner-local layout preferences + monotonic project_seen_revision ACKs
│ │ ├── cancel_intents.json ← compact locked projection of ACTIVE cancel intents; the forensic trail is typed cancel_intent rows in logs/supervisor.jsonl, never read back (cancel_intents.py)
│ │ ├── terminal_deliveries.json ← bounded delivered-dedupe + PENDING terminal-answer outbox (terminal_delivery.py)
│ │ ├── extension_companions.json ← runtime snapshot of live companion processes
│ │ ├── extension_reconcile/ ← worker-written markers consumed by the server lifespan pickup task
│ │ ├── review_continuations/ ← durable blocked-review continuations (+ corrupt/ quarantine; archived/ holds settled un-resumed rows ≥7 days, never deleted)
│ │ ├── workspace_executor_processes/ ← durable local/docker executor cleanup records
│ │ ├── headless_tasks/<task_id>/data ← forked/empty child execution drives whose live per-call manifests are promoted at terminal; until then the canonical reader cannot resolve their refs (issue #805) (CLI / Headless Boundary above)
│ │ ├── pycache/ ← embedded-interpreter bytecode (packaged builds; CLI / Headless Boundary above)
│ │ ├── python-userbase/ ← embedded-interpreter user installs (packaged builds)
│ │ ├── betterleaks/ ← versioned scanner runtime + archive cache, created only by the explicit source-checkout installer
│ │ ├── cx/ ← managed Claudexor store: immutable <version>-<sha12>/ trees each with managed-runtime.json + node/, cache/ of verified archives, install.lock
│ │ └── skills/<name>/ ← per-skill state plane (§13): review.json (content_hash, findings, reviewer_models, raw actor records, advisory_result — findings stay authoritative), owner_attestation.json (owner-issued marker; removal invalidates, a content edit stales it, the agent can never forge it), review_history.jsonl (append-only terminal history; raw reviewer text never exposed to chat), accepted_rebuttals.json (injected into later review prompts), deps.json (isolated-dependency install fingerprint), auto_repair.json (marketplace auto-repair dedup by payload hash), ouroboroshub.json (publication receipt), health.json (server-authoritative health plus worker qualifier), auth_token.json (content-hash-bound Host Service token), enabled.json ({"enabled": bool, "updated_at": iso_ts, "actor": host_actor}), extension_calls/ (transient per-call child-process payloads), __extension_imports/<pid>-<uuid>/skill/ (staged import trees)
│ ├── claudexor/ ← Ouroboros-owned Claudexor home (CLAUDEXOR_CONFIG_DIR): daemon descriptor/token, credential profiles, runs, ouroboros-owned.json, daemon.log; never the operator's ~/.claudexor
│ ├── memory/
│ │ ├── identity.md ← durable identity
│ │ ├── scratchpad.md ← auto-generated from scratchpad_blocks.json (rendered newest-first; FIFO eviction of the oldest blocks until BOTH the 10-block count cap and the SCRATCHPAD_MAX_CONTENT_CHARS content cap hold)
│ │ ├── dialogue_blocks.json ← consolidated dialogue memory blocks (dialogue_summary.md remains a read-only legacy fallback when present)
│ │ ├── dialogue_meta.json ← consolidation cursor/metadata for the dialogue blocks
│ │ ├── WORLD.md ← host profile generated on first run
│ │ ├── knowledge/ ← topic files + auto-maintained index; patterns.md (Pattern Register), improvement-backlog.md (backlog SSOT), *_journal.jsonl + *history.jsonl provenance
│ │ ├── deep_review.md ← written by the deep-self-review task
│ │ ├── registry.md ← memory awareness map
│ │ └── owner_mailbox/ ← per-task user message files
│ ├── projects/<id>/knowledge/ ← per-project facts + provenance sidecars; logs/task_reflections.jsonl holds full reflections with a bounded pointer row in the canonical log
│ ├── observability/ ← canonical private forensic ledger: blobs/<sha256>.json.gz compressed CAS payloads (0600) + terminal-promoted calls/<task_id>/<call_id>.json manifests
│ ├── services/<task_id>/<service>.log ← service runner logs; public tool output is bounded redacted tails + private blob refs
│ ├── logs/
│ │ ├── chat.jsonl ← canonical chat: one logical message stored once, projected into Main/Project lenses
│ │ ├── chat_annotations.jsonl ← compact routing status by client_message_id; presentation-first, with the token-bound `needs_manual_target` decision card as the one routing-authority exception
│ │ ├── progress.jsonl ← runtime ledger: progress/thinking stream
│ │ ├── events.jsonl ← runtime ledger: lifecycle, llm_round/llm_usage, errors
│ │ ├── tools.jsonl ← runtime ledger: tool calls
│ │ ├── supervisor.jsonl ← runtime ledger: workers/supervisor
│ │ ├── task_reflections.jsonl ← canonical reflection log
│ │ └── containment_faults.jsonl ← append-only compact projection of containment incidents (delegate_custody.py)
│ ├── archive/ ← rotated logs, rescue snapshots, archived managed repos
│ └── uploads/ ← chat file attachments (paperclip)
├── Deliverables/ ← bare user_files filenames land here (OUROBOROS_DELIVERABLES_ROOT; sibling of projects/, outside repo/ and data/, never GC-pruned)
└── ouroboros.pid ← launcher PID lock; platform lock auto-released on crash
The generated docs/inventories/DATA_LAYOUT_INVENTORY.md probes every entry of this tree: its last literal path segment must be a tracked repo path or directory, or a literal in the runtime sources. A durable file renamed in code while its row here survives therefore turns red, not silent — but this is a basename-and-substring check and proves nothing stronger about an entry.