# Architecture

## System boundary

OMS is a TypeScript-first coordinator around supported local CLI workflows. It
does not proxy model traffic, emulate a model API, or keep a custom long-running
daemon. Native CLI sessions, goals, hooks, and subagents remain the execution
primitives. OMS supplies validated local authorities, immutable evidence,
idempotent event handling, and operator-facing plans around those primitives.

The executable control plane is local and durable, not a daemon: a human starts
and remains attached to the read-only conductor, and accepted dispatches may
start policy-eligible interactive worker panes through OMS. A fixed observer
projects provider/GitHub events; a fixed watchdog performs recovery only. Both
run inside the attended tmux session and neither owns routing. Every merge
remains on human hold. No shipped test or artifact proves a live GitHub ticket
or an elapsed 12h/24h canary.

All runtime automation is TypeScript. The repository contains no tracked shell
automation scripts; the extensionless `bin/oms` file is a Node.js compatibility
shim for a source checkout.

## Sources of truth

Each field has exactly one authoritative owner:

| Concern | Authority |
| --- | --- |
| Agent definitions, skills, and safety policy | packaged `.agents/` files |
| User role and model policy | `~/.oms/config/oms-config.json` |
| Ticket content | immutable materialized file below `OMS_STATE_ROOT` |
| Ticket lifecycle and lease | one canonical versioned queue record per ticket |
| Session lifecycle | canonical session registry record |
| Quota freshness | hook-derived quota observation bound to a canonical session version |
| Events and delivery | immutable typed outbox event plus per-consumer receipt |
| Audit run | immutable manifest plus append-safe audit ledger |
| Conductor route | immutable opaque decision file |
| Goal lifecycle status | one canonical OMS goal record; native goal state is immutable checkpoint evidence |
| Pre-dispatch advice | canonical intent digest plus immutable protected advisor evidence and operation record |
| Verification and trust | deterministic evidence and ticket-bound immutable trust records |
| User schedule | exact generated plan plus user-scoped installation record |
| Release status | negative-only tracked release assessment |

Cached views must be reconstructible and must not compete with these
authorities. Native goal state, Team state, HUD state, and reports are projections or evidence only.
A leader may checkpoint a fresh native goal snapshot into evidence, but a mismatch with the canonical OMS goal record parks the goal and blocks mutation until explicit reconciliation.

## Runtime root

The CLI keeps four paths separate: its read-only package root, the user
configuration root at `~/.oms`, the caller's workspace root, and
`OMS_STATE_ROOT`. The state root is resolved once to an
explicit absolute path and passed to every worktree and generated service job.
`state.json` binds that root to one canonical workspace, so another checkout
cannot reuse its events, conductor, or authorities. Runtime state never depends
on the package installation directory.

For a single-checkout smoke run, the CLI defaults to the caller workspace's
ignored `.oms/` directory. Any multi-worktree or resumed run should set one
explicit absolute `OMS_STATE_ROOT`. Initialization rejects the filesystem root,
home directory, and workspace root. Read-only status surfaces do not initialize
a missing root. The user configuration root itself is also rejected as runtime
state, so global model choices cannot mix with a repository queue or ledger.

Interactive bare `oms` starts the conductor. When `OMS_STATE_ROOT` is absent, it
may reuse the single valid repository-authorized state root found at `.oms/` or
one immediate child; zero matches use `.oms/`, while multiple matches fail
closed. Discovery itself never creates live-delivery authorization. When either
required authority is absent, bare `oms` performs the one-time human terminal
confirmation before initialization or provider contact; it never widens a
running conductor.

Bare startup recognizes a workspace through its non-symbolic Git marker; local
`.agents/` content never overrides the user role policy. If the current
directory is not a Git workspace, a source-linked installation may use its OMS
Git checkout. If neither location is valid, startup fails before state
initialization or provider contact.

When the user policy does not exist yet, setup may use the current Git
workspace's legacy `.agents/config/oms-config.json` as its one-time migration
source. After creation, only the user-level file owns role and model choices.

The initialized runtime tree includes private directories for:

```text
$OMS_STATE_ROOT/
  locks/                 queue/                 tickets/
  worktrees/             worktree-records/      trace-operations/
  sessions/              quota/                 workers/
  conductor-{activity,notifications,waiters}/   outbox/{events,receipts}/
  audit/                 run-manifests/         run-decisions/
  run-start-intents/     run-completion-proofs/ run-aborts/
  goals/                 goal-artifacts/        team-plans/
  service-plans/         schedule-installations/ evidence/             tmp/
  live-contacts/         review-deliveries/     review-proofs/         secret-scan/
  dispatch-advice/       codex-structured-results/
```

User configuration contains no secrets. Runtime records use restrictive
permissions and reject unsafe path, symlink, authority-copy, version, and schema
conditions at their mutation boundaries.

## Ticket lifecycle and local tracer

The implemented queue states are:

```text
pending -> leased -> running -> verifying -> completed
             |          |          |
             +------> retryable <---+
             +------> parked
             +------> blocked
```

Legal edges are enforced by the lifecycle service rather than inferred from a
diagram. Every mutation supplies the opaque ticket ID, expected version, state,
attempt, actor, operation nonce, and lease token when ownership is required.
Replaying an accepted operation returns the existing outcome; stale or
conflicting authority fails closed. The attempt budget is bounded.

The one-ticket tracer composes ticket materialization, a dedicated git
worktree, executor result, deterministic verification, and supplemental grader
evidence. Its shipped fixture uses a local no-op executor and grader, performs
no network or provider call, changes no tracked source, and is non-certifying.
A successful fixture remains `verifying`; only the protected live result gate
can complete a ticket. Passing the fixture is not evidence of a live ticket or
provider run.

GitHub Issues supply untrusted ticket intent only. The named queue probe first
persists and emits a contact/spend disclosure and requires explicit
confirmation before invoking `gh`. Queue lifecycle authority stays local; sync
cannot overwrite active execution state.

Normal GitHub sync is projection-only: it materializes open `oms:ready` issues
as pending tickets and assigns an opaque conductor notification. It never
freezes the plan, creates a worktree, leases a ticket, or selects a route.
After a persisted human goal, the conductor selects one exact ticket version
and calls `prepare_dispatch`. OMS first writes an immutable request journal,
then freezes the plan, creates or replays the `oms-ticket-*` worktree from
`main`, leases the ticket, validates the server-derived intent, and writes its
immutable result. Exact replay can recover a crash between those writes;
changed or stale requests fail before advice.

## Pre-dispatch advice boundary

The conductor passes the exact intent returned by `mcp__oms__prepare_dispatch`
through `mcp__oms__advise_dispatch`. OMS re-reads current goal, ticket, lease, role,
policy, budget, and worktree authority, canonicalizes every executor-affecting
field including the operation nonce, and computes the intent SHA-256.

Before contacting Codex, OMS requires the separate human-confirmed
pre-dispatch-advice authorization, writes the live-contact disclosure, and
records a `prepared -> contacting` operation. A fresh schema-bound Codex exec
may return only the intent digest, `approve | revise | veto`, summary,
recommendations, and risks. OMS—not the model—adds provider request, fresh
session, contact, and authorization identities and writes protected immutable
advisor evidence. The contacting record also stores the owning OMS Linux
process identity (PID, boot ID, and process start ticks). Another OMS process
leaves a live owner untouched; a dead, restarted, or PID-reused owner makes the
contact ambiguous, blocks the operation, and never launches a second provider
session. While one intent is prepared or contacting, another intent for the
same ticket attempt and lease is rejected before provider contact.

`mcp__oms__propose_dispatch` then recomputes the exact intent digest and accepts
only an `approve` evidence reference for that unchanged ticket and digest before
creating a dispatch, worker reservation, or delivery event. Worker delivery
independently revalidates the same evidence before any provider probe or pane
creation. A veto is terminal for that ticket attempt and lease. A revision
requires a substantive canonical-intent change and fresh advice; changing only
the operation nonce cannot reopen advice. Missing, stale, tampered,
cross-ticket, veto, or revision evidence fails closed.

## Atomic records and immutable artifacts

Mutable records are protected by exclusive locks, schema validation, expected
versions, and same-filesystem atomic replacement. Append-style history uses
serialized, hash-linked records or immutable one-file-per-event artifacts.
Digest-addressed evidence is written once and a replay must match its original
bytes. Stable-directory checks and no-follow reads prevent a path or symlink
swap from silently changing the authority being read.

## Event, hook, session, and quota flow

The typed event bus accepts only registered event kinds and exact schema-valid
payloads. Immutable outbox events are processed per consumer through
idempotent receipts. The event set covers pinned provider observations, typed
GitHub changes, native-hook observations, schema-bound Claude grader/dreamer
completion, due ticket resumes, audit heartbeats, watchdog reports, and Slack
notification requests. A structured-role completion event carries only its
exact result-gate, dispatch, ticket, fresh-session, and result-digest bindings;
it has no grade/reflection content and no terminal authority.
Projection is serialized per structured request and recovery isolates failures,
so one malformed or live-locked request cannot prevent another waiting gate
from being repaired. A dead projector lock is recovered only for its exact
generation after its owner process is no longer alive. Already committed
outbox, notification, and worktree replays bypass mutation locks, keeping the
60-second polling path from growing lock history for unchanged state.

`hook ingest` reads one bounded JSON object from stdin. It computes its source
digest, publishes a typed event, and projects the event into canonical session,
quota, and ticket authorities. Session identity binds role, engine, mode, run,
ticket, native-session reference, pane ID, version, and lifecycle state. Live
quota gates can be created only from the canonical bus event and matching
session registry; stale, ambiguous, cross-policy, copied, or unrelated evidence
is rejected. A quota-parked ticket resumes only from newer, fresh availability
evidence for the same session.

OMS injects only pinned, fixed-argument native hooks into the interactive Claude
and Codex launch settings. Hook JSON arrives on bounded stdin; raw prompt,
completion, quota, and GitHub content is discarded before event publication.
Each GitHub poll durably stages one validated snapshot batch before projection.
Restart recovery replays that exact generation before any refetch, advances the
watermark only after projection, and then removes the stage.

## Reconciliation and watchdog

The minute reconciler is a single idempotent pass. It:

- consumes pending resume and run-heartbeat events;
- emits due ticket-resume events using the original ticket authority;
- requires a current time or quota gate before resuming parked work;
- records an audit heartbeat against the expected hash-chain head;
- reports overdue, conflicting, failed, or superseded lanes without converting
  them into success.

The watchdog is a separate recovery pass. It recovers expired ticket leases,
marks sessions stalled, reports stalled internal delivery, supersedes dead
waiters, repairs a persisted structured-role result whose completion event,
receipt, or notification was interrupted, and may resume the same conductor
without attaching. It sends only one opaque notice ID to a proven idle pane and
never interrupts `working` state or performs ordinary routing.

The tracked policy specifies a 60-second minute interval, a 15-minute watchdog
interval, a 15-minute session heartbeat horizon, and bounded quota freshness.
These policies and deterministic tests do not constitute elapsed endurance
evidence.

## Schedule boundary

`schedule generate|install|status|remove` manages one exact user schedule for
cron, systemd, or launchd. It contains only GitHub polling (60 seconds by
default) and recovery watchdog reconciliation (900 seconds). Installation is
user-scoped, fixed-argv, drift-detecting, idempotent, and recorded only after
native activation succeeds. Legacy digest-addressed service plans remain
internal evidence for non-certifying audit runs.

## Audit run and conductor boundary

`run start` first persists an immutable start intent, then freezes each admitted
ticket's ID, version, attempt, state, source digest, content digest, policy, and
verification gate together with the service plan, run kind (`until-empty`,
`12h`, or `24h`), workspace, and state root. A crash can replay that intent
without recapturing a changed queue. Every manifest fixes
`provider_started: false`, `manual_conductor_start_required: true`,
`certifying: false`, `release: not_stable`, and `merge_disposition: human_hold`.
It generates service files but neither installs them nor launches a provider.

`run tick` records a continuity observation, projects admitted ticket state, and
may complete an `until-empty` audit only after every admitted ticket has
deterministic and grader evidence. A new or drifted unresolved ticket produces
a human hold. Completion requires a digest-bound immutable proof. `run close`
applies the same proof gate to a fully elapsed same-boot 12h/24h audit; it does
not certify a canary. `run abort` records an immutable reason, while
`run recover-controller` is an exact-generation, dead-owner-only recovery for a
crashed global controller lock. `run status` is read-only and revalidates the
start intent, service artifacts, manifest/ledger binding, and any terminal
proof on every read.

`conductor next` reads the canonical run and queue, chooses the next legal
opaque route, and persists a decision containing IDs, versions, role IDs, and
evidence file paths. Raw ticket text is excluded. The decision can say that a
human start, native hook, resume wait, human hold, or empty queue is next. It
does not invoke Claude, Codex, tmux, or a shell.

The bounded campaign loop's internal result service is the only review-delivery
entry point. A canonical completed result triggers pinned secret scanning,
fresh remote branch/draft-PR readback, one exact non-force push, and at most one draft
PR. Ambiguous push or PR outcomes recover by readback only. The delivery remains
waiting until existing typed PR/check events bind the exact verified commit;
then an immutable proof reports `READY_FOR_HUMAN`. No merge, ready-for-review,
tag, publish, release, or positive-stability operation exists.

Worker launch is a nonce-bound two-phase transition. OMS first stages an inert
tmux placeholder whose process arguments prove the physical nonce. Under the
kernel lock it journals the launch completion and binds the exact task, pane,
session, launch identity, and nonce before recording activation intent. Only
then may the adapter replace the placeholder with the provider. Activation
completion is journaled separately, so a crash after binding re-enters
`activation_pending` and resumes the same physical launch rather than staging a
second worker. A per-run/task/attempt creation lock spans physical creation and
the durable created receipt; campaign stop acquires the same lock before it
decides that an intent-only launch is absent. Every terminal path replays all
launch intents and requires either the exact bound-worker stop proof, a
non-ambiguous failed launch, or the creation-lock-certified unbound stop proof.

## Goal, trust, and Team authorities

One canonical OMS goal record owns `active`, `parked`, or `completed` lifecycle
state. The CLI can create a goal from bounded JSON and read its status. Native
goal snapshots, Team checkpoints, Ultragoal checkpoints, and regression
requests are immutable evidence. Stale, inconsistent, or mismatched evidence
parks the goal; only matching terminal native and executor-group checkpoint
evidence can complete it through the internal authority API.

Trust runs require ticket, attempt, result, role-policy identity, and
verification-gate identity from one protected live deterministic verdict. OMS
ships that writer only inside the result gate: installed checks run with the
candidate worktree and isolated temp writable, OMS state and installed code
protected, and one fresh opposite-engine grade bound to its durable request and
session. The local trace fixture cannot create canonical trust evidence. Daily trust reports cover one closed UTC
interval and are review-only; their output grants no merge, queue, or execution
authority.

`team start --dispatch-id ID ...` writes one immutable executor-group manifest
over already accepted dispatches and sends every member through the same OMS
worker delivery path. The manifest contains no task text, provider command,
model choice, verifier lane, or release authority.

## HUD and release truth

The HUD reconstructs read-only counts from queue, session, quota, and outbox
authorities and always displays `release=not_stable`. It is not a dashboard
server and has no mutation authority.

`release status` is offline, read-only, and negative-only. Its schema can express
only `not_stable`, human hold, automatic merge disabled, and the canonical list
of unproven obligations: live GitHub ticket, elapsed 12h canary, fresh elapsed
24h canary, and executor-group evidence. There is no positive
release or automatic-merge parser in the shipped design.

## Verification boundary

`doctor` performs offline executable, version, JSON, path, permissions, git,
exact Gitleaks-version, and native verification-sandbox prerequisite checks. `oms verify` validates
shipped assets and the user role policy plus the offline doctor. `make verify` runs
strict TypeScript typecheck, a clean TypeScript build, Vitest, and the installed
offline gate.

Pre-dispatch advice runs before execution. Deterministic project verification
runs after execution and before the supplemental grader; neither model advice
nor grading can override tests. Local tests do
not prove network access, provider authentication, billing policy, current
remote GitHub state, an actual provider session, or elapsed endurance.
