Skip to content

Repository files navigation

epicd

Epicd is being rebuilt as a persistent autonomous engineering lead. GPT-6 Astra chooses delivery strategy, coordinates agents, investigates failures, and requests actions from a deterministic Git and Beads safety kernel.

This branch has one orchestrator controller. There is no legacy phase dispatcher, compatibility mode, state conversion, or database migration. Current storage format is 43. Use a fresh state path; unsupported existing data is left intact.

Default state lives at $XDG_STATE_HOME/epicd/epicd.sqlite3, or ~/.local/state/epicd/epicd.sqlite3 when XDG_STATE_HOME is unset. --state overrides that path. An unsupported-format error prints the resolved filename, a command to use an unused fresh path, and explicit instructions to delete the old database and its SQLite sidecars after stopping its controllers. Deletion discards all runs in that file and does not release repository run reservations.

Persisted ownership fields must be explicit: missing workspace creation bindings, turn launches, native endpoint bindings or decision-attempt turn identities are invalid, not implicitly null. Reading incomplete records does not repair them or release their resources.

The CLI and controlled runtimes are wired, but autonomous epic delivery is not yet release-ready. Independent whole-epic verification, epic-scoped repair, guarded container/root closure, atomic run completion, isolated tracker export and tracker-only delivery commits are implemented. Restricted validation access to run-created PostgreSQL fixtures is implemented under separate operator grants. Host-fixture reset/cleanup, some recovery/resource-management capabilities, and end-to-end acceptance remain unfinished. Unavailable capabilities are reported to the orchestrator, not emulated by a legacy workflow.

Requirements

  • Linux x64, Node.js 22.12+, Git, Bubblewrap, util-linux unshare, and working unprivileged user/PID/mount namespaces.
  • Codex authentication and access to exactly gpt-6-astra. There is no coordinator model fallback.
  • br (Beads) and a repository with a local .beads/beads.db.
  • For native Herdr: a compatible running Herdr session, its Codex integration, the native codex executable, and invocation from a Herdr-managed pane.

The coordinator defaults to Astra/high. Supported Astra efforts are low, medium, high, xhigh, and max. Worker defaults are resolved once with the selected implementation account in an isolated discovery home unless supplied explicitly. Role preferences can change future assignments; existing agent contracts remain pinned. Official Astra model reference

Build and inspect

npm ci
npm run build
node dist/cli.js --help
node dist/cli.js doctor --repo /path/to/repository --runtime sdk

doctor checks executable/endpoint availability without starting a model turn. It does not certify authentication, model access, confinement, or successful delivery.

SDK mode uses the SDK-pinned native Codex binary through the supervised SDK transport. Herdr mode launches a real native Codex TUI in run-owned, unfocused tabs; it does not wrap SDK workers in decorative panes. The selected runtime, executable paths, private storage roots, and Herdr endpoint are persisted at creation. Resume does not silently switch runtimes.

When the selected codex is the current npm JavaScript entrypoint, bootstrap resolves that installation's native payload before confinement and freezes the resulting executable path. An incomplete selected installation is an error, not permission to substitute another installation or launch the shim inside the sandbox.

Confined validation, Codex, Beads and fixture commands use an independent PID-namespace lifetime supervisor outside Bubblewrap's command mounts. Cancellation remains effective during sandbox startup. A killed supervisor is an unknown stop, not permission to release a workspace, accept tracker completion or attest fixture-client termination. There is no unconfined fallback when this boundary cannot start.

Whole-epic verification has its own published-revision target. prepare_epic_delivery binds the latest publication, observed tracker scope and closed-task provenance to an epic-root validation plan. It retains required checks from delivered tasks and demands fresh results at the final SHA. run_review then starts an independent final_review conversation in an isolated verification copy, covering all descendant requirements and the diff from the run baseline. Findings survive replacement targets. This evidence does not itself close the tracker root or complete the run. Oversized complete review context is rejected, never silently shortened into approval.

After a final finding, the orchestrator can request start_agent with purpose epic_repair, the root task ID and latest captured root candidate, using a fresh implementation workspace at the latest private commit. The kernel binds the assignment to the open root and proven closed descendants, and supplies retained requirements, findings and checks to either runtime. No task is reopened and no root claim is fabricated. Repair commits use the normal independent pre-commit and exact-revision review gates. Every further repair extends the private tip; publication must be followed by a new whole-epic target and approval before root closure. Policy/reviewer requirements and committed repair checks remain binding even before publication; uncommitted draft checks stay editable.

Isolated tracker export

export_tracker takes no paths or replacement content from the orchestrator. The kernel creates a private, operation-owned SQLite snapshot, copies the pinned Beads configuration, and runs a strict confined export against that copy. SQLite's online backup API captures committed WAL state; this is an execution snapshot, not an old-format migration or compatibility backup.

The source database is opened read-only for the snapshot. Its dirty flags, user JSONL and base JSONL are not flushed, imported or cleared. The private export includes the tracker's full JSONL output, not just the current epic. The kernel checks count/hash, captured issue fields and relationships, stability of the copied epic graph, and agreement with a later live epic observation. A changed epic scope yields a conflict and retains the historical bytes. That comparison does not certify freshness of unrelated issues or grant authority to overwrite them later.

inspect_tracker_operation and status expose export identity and metadata; raw JSONL stays in the private journal for kernel commit construction. It can contain sensitive tracker content and is not a redacted diagnostic. Export uses a 4-MiB output limit, a 64-MiB database bound and a 120-second cooperative deadline. Existing operation directories are never reused or truncated. Recovery can inspect a known-stopped operation without exporting again; an unknown process stop remains unknown. Private copies, including incomplete ones, are retained in the completion resource inventory.

The orchestrator can request request_tracker_commit with a retained export operation and the current publication ID, then request_publish_tracker with that tracker commit ID and the expected previous revision. The kernel replaces only .beads/issues.jsonl in the committed tree and advances the actual local epicd/<run-id> delivery branch through the same guarded publisher. It never stages user files or changes either checkout or index. New implementation work uses the complete tracker-updated tip, not its older application ancestor.

Tracker commits retain explicit ancestry to the reviewed application object. They do not invent a review of their own SHA. A final review may inspect the tracker-updated SHA directly, or remain valid at its original revision through a proven tracker-only chain with unchanged requirements, evidence and writer generations. After root closure, completion requires another exported and published tracker descendant whose recorded scope matches the live closed epic. inspect_tracker_commit and reconcile_tracker_commit expose the durable object outcome; replay does not construct another object, and reconciliation requires independently confirmed I/O stop or a provably unused dispatch gate.

Export alone does not commit, publish, close an issue or approve completion. Those are separately requested capabilities with their own kernel checks; neither runtime substitutes a shell command or legacy workflow for them.

Interrupted delivery actions

interrupt_action({ actionId, reason }) requests cancellation of one running action owned by the current controller without pausing unrelated work. The kernel journals the request before signalling its exact in-memory handle. A successful acknowledgment does not prove process or remote SQL stop, roll back effects, release workspace/fixture exclusions, or satisfy a check. Inspect the original action and its resource evidence before reuse. Settled results remain unchanged; absent, foreign, indeterminate or previous-controller handles cannot be guessed or revived. interrupt_agent remains available for an exact worker turn. Interruption is allowed while publication or tracker work is unresolved, but does not authorize further delivery mutations.

reconcile_action({ actionId }) inspects an interrupted capture, implementation/review-copy creation, validation, review or application commit by its original action ID. The controller uses the same recovery code on restart. Neither path reruns the original command, copy, review turn or commit write, and the capability cannot interrupt a live action.

A retained result can settle its historical acknowledgement; current review and publication eligibility are checked separately. Failed checks remain failed. A stopped review without a durably recorded verdict needs a fresh independent review. Changed or unbound copies are preserved, not restored or adopted. Missing process or controller-I/O stop proof leaves the operation unresolved and its workspace excluded. Other resource types keep their dedicated reconciliation capabilities.

An interrupted interrupt_action acknowledgment can be settled as failed by reconcile_action or current-format restart recovery. The signal may already have been sent. Recovery never sends it again, changes the target's result, or clears its resource exclusions; the original target still needs its own outcome and stop evidence.

This works through the shared kernel for both SDK and native Herdr runtimes. It is current-format restart recovery, not migration support. Some abandoned controller I/O and retained-resource cleanup remain unfinished; repository ownership has the separate recovery boundary below.

Private application and tracker commit construction each have one independently supervised lifetime covering source preflight, tree/object construction and the private retention-ref write. The shared worker binds an explicit application-or-tracker target and a one-use launch before dispatch; it cannot substitute one commit kind for the other. Each kind retains its own approval, export and lineage checks. Controller loss interrupts the worker's process namespace; recovery either reads its exact stop receipt or fences an unused launch gate. A missing receipt keeps the source excluded even if the expected Git ref exists. Recovery never repeats the writer. Only subsequent physical inspection can establish the retained commit and unchanged source; exact-revision validation, independent review and publication remain separate requirements.

If an application commit was reserved but its worker was never bound, recovery atomically fails that exact commitment and releases only its matching source exclusion. This needs no Git inspection and does not assert source integrity. Late binding and the fixed worker's own admission reject that cancelled intent. A bound worker still requires its original stop proof; a missing revision alone is not proof that no worker ran. reconcile_action cannot cancel a healthy live request. The orchestrator may capture and independently review a fresh candidate for a new request, but cannot replay the failed candidate's commit intent.

An unused tracker Git dispatch gate is not a substitute for a bound host worker's stop receipt. Cancelling that gate preserves existing supervisor evidence rather than overwriting it. reconcile_tracker_commit refuses to fence a write that the current kernel still owns; inspect or interrupt that exact operation first.

Candidate capture also has a complete supervised worker: its workspace exclusion and candidate intent are reserved in one transaction before preflight, scanning, object/tree/ref writes and snapshot retention. The worker durably retains the snapshot without making it available for review. Live or cold reconciliation promotes those exact bytes only after the original independent stop receipt; it never rescans the source or reconstructs a manifest from a surviving ref. Missing stop proof retains exclusion. Proven stop without a retained snapshot fails the capture and preserves any private objects. An unbound intent can be cancelled atomically, forbidding later binding or launch. Concurrent source edits are never restored by capture recovery, and a recovered snapshot is not review approval.

Workspace creation has one supervised lifetime covering source preflight, copying, readiness checks and the review binding. A creation record atomically reserves the destination and, for a managed source, its copy-source exclusion. The same independently observed worker stop settles both; a ready directory, retained review binding or finished nested Git command cannot release either lock alone. Baseline, candidate, diagnostic, retained-commit, coordinator and canonical-delivery copies use this path. Readiness, review binding and successful completion are retained in one transaction. If it fails, files stay preserved but no ready workspace or binding is exposed. A caller crash can recover the original completion without copying again. A never-bound creation can be cancelled atomically; delayed binding is forbidden. inspect_workspace exposes creation outcome and stop status without exposing its private launch control files.

Publication writing and subsequent inspection/owned-lock release now have separate fixed supervised workers. Each attempt binds its exact source/canonical exclusions, launch, retained result and independent stop proof. A controller crash can recover the original write or inspection acknowledgement without repeating the publication. A stopped inspection with no retained observations requires a new requested inspection; matching refs alone cannot settle unknown worker I/O. Live model-requested reconciliation cannot fence a healthy pending publication or inspection. Canonical-copy preparation uses its own supervised creation before the writer launches; neither worker's receipt substitutes for the other's. A new publication request may allocate fresh canonical custody after a proven failed initial copy, preserving the failed directory and its journal records.

inspect_publication shows bounded attempt previews. inspect_record retains the complete redacted history through paging; neither view exposes private execution controls. Publication outcome and current independent exact-revision approval remain separate.

Standalone materialization, application-commit and tracker-commit inspections now use fixed supervised workers too. Each read binds an exact target and source exclusion, retains its physical observations, and settles only with the original independent stop proof. Commit confirmation and inspection settlement commit atomically. A missing receipt keeps the exclusion even when an observation or Git ref exists. A stopped attempt without retained observations fails without a replacement read; another request may start a new inspection. Startup discovers pending inspections directly from their journal before recovering parent actions or selecting coordinator settings, including inspections with no parent action or agent. reconcile_workspace_inspection({ inspectionId }) provides the same recovery through a model-dispatched action while the run is active; inspect_workspace supplies the IDs. Recovery never depends on requesting the original target again. Materialization readiness always uses one new supervised read for each request, after settling any matching prior inspection with its original stop proof. A retained result never answers a new readiness question. Explicit inspection recovery and commit reconciliation retain their historical observations without rereading; current commit eligibility remains a separate check. Failed commit inspections remain failed across controller restarts; only a new model-dispatched reconcile_action or reconcile_tracker_commit request may retry the read. Copy recovery reads current bytes without recopying or clearing unrelated I/O. The adapter rejects concurrent reconciliation of its live read. Internal recovery inspections require current controller authority and exclusive custody, and remain available while delivery is paused, awaiting user input or blocked. Model-dispatched actions, including reconcile_workspace_inspection, require an active run. Completed runs reject new inspection reservations; retained records and idempotent recovery of settled inspections remain available. Inspection diagnostics are redacted and bounded at the journal boundary before validating the durable result. An explicit physical mismatch yields an observed incomplete copy; Git execution, permission, custody and unknown errors yield a failed inspection with retained diagnostics, never a negative observation. Each requested inspection has a 120-second worker deadline and performs no automatic retries. Delivery decision budgets govern model-requested work; historical inspections never exhaust a workspace's ability to recover.

inspect_workspace reads at most ten recent inspection records and reports the omitted count; inspect_record with recordKind: "workspace_inspection" pages the full redacted record. Summaries retain readiness, commit creation and source-integrity observations separately from inspection settlement: observed can mean an incomplete copy or an absent commit, while failed means the inspection could not settle an observation. Private execution controls are omitted. A retained physical observation is historical evidence, not fresh validation or review approval, and never authorizes restoring concurrent changes.

The inspection contracts explain custody versus content failures, cancellation after durable settlement, and recovery after quarantine. Workspace previews enforce one aggregate JSON budget, preserve the newest recovery IDs, and report omitted history or shortened diagnostics without changing retained records.

These boundaries share the command-lifetime mechanism across SDK and native Herdr runtimes. Fresh format-43 state is required, with no migration or legacy defaults. Missing or invalid stop proof still requires intervention rather than inference from an absent PID. Retained-resource cleanup, external ownership guards and full live acceptance remain unfinished.

Recoverable workspace disposal

dispose_workspace retires a stopped independent review, verification or diagnostic copy and moves its complete directory into run-private .disposed storage. Already-retired coordinator copies can also be disposed. Implementation and delivery repositories remain protected object sources. Every exact turn and launch must have confirmed stop, workspace I/O must be settled, and pending instructions must be resolved first. A native not_started receipt does not prove that its host shell stopped, so that case is refused. No Herdr pane, tab or session is closed.

Herdr 0.8.2/protocol 20 exposes pane/tab closure by ID without an atomic expected-terminal/process guard. Automatic endpoint cleanup remains unavailable: inspecting a terminal and then closing its ID would leave a reuse race with user-owned work.

The original workspace identity can never be reused. All contents—including ignored files, receipts and symlinks—remain recoverable; this is not permanent disk reclamation. inspect_workspace reports the retained location and disposal outcome. inspect_repo resolves the original retained copy by identity and keeps its usual redaction and read-only limits. Historical approval survives healthy retirement; later evidence revocation cannot reactivate a retired workspace. Retention is not new validation evidence.

The fixed operation requires GNU mv with --no-copy support (tested with coreutils 9.4). It uses pinned parent descriptors, no-clobber rename and no copy/delete fallback. GNU implementation A whole-operation supervisor supplies one-use dispatch and independent stop receipts. reconcile_action and cold bootstrap inspect the original stopped move without repeating it. Replaced directories, occupied destinations and unproven stop preserve their records and files. Any new occupant at the original name is not adopted or removed. Empty private directories allocated before rejected admission may remain; permanent retention cleanup and native endpoint cleanup are still separate unfinished work.

Repository ownership after a controller crash

Acquiring and releasing refs/epicd/run-owner now run as whole trusted operations under a detached supervisor and the existing PID-namespace lifetime boundary. The supervisor detects loss of the controller's private pipe, stops the namespace, waits for its descendants to be reaped, and retains an operation-specific stop receipt. Git guards still require the original lease, repository binding and exact ownership intent; release still requires completed-run control.

A replacement controller can acknowledge that receipt and inspect the actual ref without repeating the original write. If dispatch never started, it can atomically prevent that generation from starting. Neither a matching ref, an absent process nor a replacement lease proves termination. Missing or invalid receipts, replaced control directories and killed namespace monitors preserve uncertainty and the repository reservation. A failed operation can be retried as a new generation only after its stop and physical outcome have been settled.

Private control directories live beside the state file under <state-path>.repository-io; that location must be outside the checkout and Git metadata. The journal binds their filesystem identity before dispatch and records the operation and receipt identities. Control files are retained, not automatically deleted. Worker attachment cannot create missing state, initialize empty state or adopt a replaced state file. This boundary is shared by SDK and native Herdr; it does not yet supervise all controller-side Git, workspace, tracker or fixture I/O.

Repository policy

You can start with epicd without creating a configuration file. When .epicd/policy.json is absent, starting an epic creates it with the built-in defaults: at most four workers, 64 task decisions and 128 epic decisions, with no declared fixtures, additional scratch paths or autonomous worker-setting changes. No extra repository checks are declared by default; normal kernel review and evidence requirements still apply. The generated JSON includes all defaults so you can inspect and customize them. Browsing alone does not create the file.

To customize these defaults, edit .epicd/policy.json before starting a new run. You can also provide this file yourself. An existing file must be readable and valid; configuration errors do not silently fall back to defaults. The JSON declaration uses schema version 1. Include the checks that actually establish your repository's acceptance requirements. For example:

{
  "schemaVersion": 1,
  "requiredChecks": [
    {
      "id": "unit-tests",
      "command": "npm",
      "args": ["test"],
      "cwd": ".",
      "timeoutMs": 120000,
      "stage": "both"
    }
  ],
  "budgets": {
    "maxWorkers": 4,
    "taskDecisions": 64,
    "epicDecisions": 128
  }
}

Commands and dependencies must be available inside the isolated validation environment; host installation alone is not sufficient. Repository commands cannot access arbitrary host services, home directories, or network endpoints. There is no full-host-access bypass. Host fixture declarations do not themselves grant service authority. Explicit grants allow catalog inspection, absent-database creation and separately authorized restricted validation access; host-fixture reset and cleanup are not implemented. Separately declared check-scoped PostgreSQL services can also supply an isolated database for validation.

The effective policy is frozen when a run is created. Editing the repository file does not change an existing run's permissions or required checks.

To let the orchestrator change worker settings itself, list exact permitted model/effort pairs in autonomousWorkerSettings, for example [{"model":"YOUR_WORKER_MODEL","reasoningEffort":"high"}]. An empty or omitted list does not grant unrestricted model choice. Operator-selected initial settings remain usable; this list bounds autonomous changes, not explicit operator settings commands.

Start and operate a run

In an interactive terminal, epicd opens the epic browser. You can also invoke epicd browse explicitly:

epicd browse --repo /path/to/repository --state /path/to/private-state/current.sqlite3

To diagnose slow loading, run epicd --trace-discovery. It writes discovery stage names, durations and success/failure to stderr. These timing lines contain no search text, tracker payloads or error causes. Load errors identify the failed stage and suggest a next step; failed navigation keeps the previous page visible.

Use arrows or j/k to navigate, [/] to page, and a to show nested epics. The browser loads 50 epics at a time. / opens a search across the tracker; Enter submits it, Escape cancels editing, and c clears the search. r reloads the current page. If navigation fails, the previous page stays visible; reload or navigate again to retry. Selecting a retained choice first reloads the list and requires a new confirmation. Search matches IDs, titles, descriptions and comments, including epics on unloaded pages. Search terms are passed to br as process arguments and can be visible to other local processes. Avoid secrets; display redaction does not make searching private. Epics too large to load cannot start new runs; their saved runs can still resume or open controls. Bounded tracker metadata preserves their actual priority and status; the list and confirmation show tracker status separately from run status. Unloaded titles and hierarchy are shown as unknown. Each page uses one metadata read and at most 16 detail reads. If that budget is exhausted, the remaining epics stay visible; search for one epic's ID to load its details separately. Other epics and pages stay accessible. Enter opens a start/resume preview; Enter or y confirms it. q closes the browser without starting work. Existing runs retain their recorded runtime and settings. A live run or one with an unanswered question opens the operator console, including a paused run whose question is still pending. Normal journal updates do not prevent opening the console; its commands obtain their own current versions. Other epics remain unavailable while a run owns the repository. Browsing does not claim tracker work or start a model; launch checks run after confirmation. Without a terminal, use the explicit commands below. Explicit run and resume commands retain automatic terminal/CI rendering; sessions launched from the browser use interactive rendering. If a launched controller fails, the browser keeps a nonzero exit status even if you return to the picker and quit. A later successful controller attempt replaces that status: 0 for normal completion, or 2 when blocked or awaiting user input.

The browser discovery contract explains metadata, saved-run authority, and the regression coverage for partial tracker reads.

Use an explicit state path outside the target repository for this experimental branch:

node dist/cli.js run EPIC_ID --repo /path/to/repository \
  --state /path/to/private-state/current.sqlite3 --runtime sdk --headless

# From a Herdr-managed pane:
node dist/cli.js run EPIC_ID --repo /path/to/repository \
  --state /path/to/private-state/native.sqlite3 --runtime herdr

Choose Codex accounts

In epicd, select a new epic with Enter to open Accounts directly. The screen shows the epic title, repository, and runtime. The run starts only after you review your account choices and confirm Start run. epicd run EPIC_ID opens the same setup in a terminal; use --headless for unattended execution.

The Accounts screen lets you choose source homes for orchestrator, implementation, and review. Use Up/Down and Enter to open a visible home picker with labels and paths. Up/Down or Tab selects a discovered ~/.codex / ~/.codex-* home or configured path; Enter applies it. Choose Enter another path (or press e) for a custom path. Left/Right and Home/End move the cursor; Backspace/Delete edits at the cursor, and Ctrl+U clears the path. An empty path or Inherit clears the override.

Each choice shows its source, such as From CODEX_HOME, Saved default, or Override. Unsaved changes means the choices differ from saved defaults; they still apply to this run. a shows advanced classes and d saves defaults. Local path and credential checks show errors beside the affected field. A failed start keeps your edits open so you can correct the problem and retry.

s opens Review choices. This final summary includes the epic, repository, runtime, and primary accounts, with inherited advanced roles collapsed (a expands them). Enter starts the run. Escape goes back one screen, including from the editor to the epic list. Ctrl+C exits setup and the browser.

Headless starts use the same resolution rules:

epicd run EPIC_ID --repo /path/to/repository --headless \
  --codex-home ~/.codex-main \
  --agent-codex-home implementation=~/.codex-build \
  --agent-codex-home review=~/.codex-review

--agent-codex-home accepts distinct classes; review=inherit clears that override. verification and final_review inherit review; epic_repair inherits implementation. A specialist override applies to either specialist role; otherwise specialists use their implementation or review role's account. These settings do not change models.

Defaults live in $XDG_CONFIG_HOME/epicd/accounts.json, or ~/.config/epicd/accounts.json. --accounts-config <path> selects another file; an explicitly selected missing file is an error. For example:

{
  "schemaVersion": 1,
  "defaultCodexHome": null,
  "classes": {
    "implementation": { "codexHome": "~/.codex-build" },
    "review": { "codexHome": "~/.codex-review", "label": "Review" }
  }
}

A null default uses the invoking CODEX_HOME, falling back to ~/.codex. CLI overrides take precedence over the file; subsequent editor changes take precedence over both. Saved relative paths resolve beside the configuration file; CLI/editor relative paths resolve from the invoking directory. Only leading ~/ is expanded, with no shell interpolation. The config file must be an owned private regular file (0600); Save defaults creates an owner-only parent directory (0700).

Each source must contain a supported file-based managed ChatGPT auth.json with account and identity-token metadata. Epicd projects only access credentials into its existing private agent homes. It imports neither source configuration nor refresh tokens. The agent's actual CODEX_HOME remains its private runtime home.

New runs pin all selected accounts at creation and retain them through resume, replacement, and runtime handoff. Same-account token rotation is supported; changing the account or replacing its home directory requires a new run. Machine-default and environment changes do not redirect saved runs. Run records use version 4 with required account snapshots. Older run records and direct credential-file selectors are unsupported; there is no migration or compatibility mode.

Account usage/status inspection and quota handling are deferred; this screen selects credentials without querying usage. Save defaults validates paths and credentials first. Symlinked config ancestors are supported; the final preferences file must remain a private regular file rather than a symlink.

Worker-model discovery uses an isolated account home and supports neither /etc/codex/config.toml nor /etc/codex/managed_config.toml. If either exists, discovery reports an explicit error; --worker-model selects a model without that discovery step. System requirements.toml, when present, remains mounted and enforced. Probe storage is deleted after confirmed shutdown. A timed-out probe is retained until its shutdown is proven; a late result or a later discovery can then reclaim it.

A missing .epicd/policy.json is initialized after startup validation, immediately before saving the new run. Rejected account, model, tracker, or runtime checks do not create that file. Policy publication and SQLite persistence are separate operations; a database failure after publication can still leave the policy file.

The SDK and Herdr examples are alternatives. Before invoking either runtime, the controller acquires refs/epicd/run-owner in the repository's physical common Git directory. Separate state files and linked checkouts therefore contend for the same run reservation. Creating another state file does not bypass ownership. The reservation binds the run, a unique owner identity and the state file's canonical path/device/inode; copying, moving or replacing state cannot borrow it.

Pausing, detaching or quarantining a run does not release its reservation. Inspect its diagnostic metadata with git -C /path/to/repository cat-file -p refs/epicd/run-owner to locate the recorded owning run and state file. Treat those bytes as metadata, not takeover authority. Do not delete the ref to bypass an interrupted run or unsupported state. Automatic takeover and abandoned-resource disposal are not implemented.

Acquisition and release have durable intents and recorded I/O stop evidence. Lost acknowledgements can be inspected without repeating a write; matching refs or a replacement lease do not prove an unknown old operation stopped. Ownership is checked before coordinator calls and action dispatch, with periodic checks during waits. Verified completion permits exact-owner compare-and-swap release, and a completed run can finish that cleanup on resume without initializing a model. Delivered branches, user checkouts and indexes are retained. Other resource cleanup remains unfinished.

The command prints the new run ID. Subsequent commands target that exact ID:

node dist/cli.js status RUN_ID --state /path/to/private-state/current.sqlite3 --json
node dist/cli.js pause RUN_ID --control-version VERSION --state /path/to/private-state/current.sqlite3
node dist/cli.js resume RUN_ID --state /path/to/private-state/current.sqlite3 --headless

In the status UI, p or q pauses admission, interrupts owned work, and exits after shutdown handling. Headless mode streams journal events. A pause request is not proof that an external process stopped.

Answer a pending escalation using the question ID and control version returned by status:

node dist/cli.js respond RUN_ID ESCALATION_ID "Your instruction" \
  --control-version VERSION --state /path/to/private-state/current.sqlite3
node dist/cli.js resume RUN_ID --state /path/to/private-state/current.sqlite3

Responses are durable, correlated instructions, not environment or destructive-action grants. A stale response is rejected. Pausing does not dismiss an unanswered question.

Change future-thread settings while no live controller owns the run:

node dist/cli.js settings RUN_ID --role review --model WORKER_MODEL \
  --reasoning high --state /path/to/private-state/current.sqlite3

The coordinator model must remain gpt-6-astra. A changed coordinator effort takes effect through a new assignment after the prior turn has stopped; existing worker assignments retain their contracts.

During a run, the orchestrator can invoke change_agent_settings within frozen policy. A coordinator effort change creates a fresh conversation before the next decision, retaining journaled memory, findings and budgets. It does not use provider-specific in-place effort updates or silently change models.

replace_agent requires the old worker's confirmed stop and a separately created workspace. It preserves the old copy, retires that generation and reserves a fresh one with the same task/purpose and current permitted settings. The orchestrator supplies handoff instructions and chooses when to continue; independent reviewers still run through run_review. Replacement does not erase findings, reuse a contaminated copy or count as completed work.

create_diagnostic_workspace gives specialists a writable private copy for experiments. With candidate and revision both null it copies the frozen epic baseline, even while implementation is active. A candidate identity selects its captured snapshot; an explicit revision must also identify that candidate's kernel-recorded exact commit. Candidate copying requires its source workspace to be stopped. The orchestrator then chooses start_specialist, follow-up, inspection or replacement through the selected SDK/native Herdr driver. Diagnostic edits and reports cannot satisfy delivery validation or independent review. Restart can recover a lost creation acknowledgement only from an intact recorded copy with confirmed I/O stop; it never recreates an uncertain copy or discards its delta.

run_diagnostic_check runs a model-selected, bounded command on an existing immutable candidate review/verification copy. Supply its workspace and candidate identities/generations, validation plan ID, and a check containing id, command, args, cwd, timeoutMs and environmentBindings; the kernel derives the revision stage. Command metadata is limited to 16 KiB. The same source protection, private network namespace, workspace exclusion, declared service bindings and separate fixture SQL grants apply as for run_validation. Ordinary worker commands gain no host-network access.

A worker can stop and request kernel execution when its own command environment cannot run a useful check. The orchestrator chooses whether to capture that source, create a copy, run a diagnostic, inspect its evidence and return the result in a follow-up. SQLite retains the exact command, purpose, target and observed outcome, including process-stop evidence. A deliberately omitted service binding can diagnose a missing environment, but never substitutes for required validation. Diagnostic success cannot satisfy or shadow a required check or supply independent review approval, even when its command or ID matches. Required checks still run through run_validation; subsequent worker turns require a fresh candidate capture. Cold recovery inspects a retained outcome without rerunning the command or promoting it to delivery evidence.

Bounded coordinator conversations

continue_agent can ask an existing independent reviewer a diagnostic question after its previous turn has confirmed stop. The kernel keeps the same immutable workspace and provider conversation and requests a diagnostic result (kind, summary, observations, uncertainties), not a review verdict. Queued messages, interruption and replay use the ordinary durable turn journal. This cannot resolve findings or satisfy validation. Any later reviewer turn supersedes its prior approval, so use run_review for fresh independent reassessment before relying on that reviewer again. Revoked or quarantined reviewers cannot be continued; their retained records remain available for inspection.

Every run_review request includes references (use [] when none are needed). Select up to 32 bounded pages: { kind: "action", actionId, offset, limit } for a settled action's redacted request/result, { kind: "artifact", artifactId, offset, limit } for an immutable retained diagnostic, or { kind: "record", recordKind, recordId, offset, limit } for a settled domain record. Offsets count UTF-16 characters in the redacted view; limits are 1–4000. Pages together still must fit 32 KiB, and the complete review context retains its 48-KiB bound. The higher reference count permits more small records, not more content. The kernel resolves references within this run, freezes their digest at admission, and supplies the primary records directly to the reviewer. Unsettled, missing/foreign and out-of-range references are rejected. Gaps, truncation and diagnostic provenance remain explicit. These references cannot satisfy required checks or resolve findings: the current review context's validation/finding arrays still control those result fields.

inspect_record({ recordKind, recordId, offset, limit, expectedDigest }) reads the same retained record view directly, without rerunning an inspection or nesting its output inside another action envelope. Record kinds are validation, review, agent_turn, commit, publication, tracker_operation, fixture_creation, fixture_access, fixture_grant and fixture_sql_grant. Start at offset zero with expectedDigest: null; continue with the returned digest and nextOffset. Changed views require restarting at zero. Reads can diagnose unfinished records, but review references require settled ones. Content is redacted before paging; private control leases/nonces and worker prompts/launch manifests are excluded, and coordinator turns are unavailable. This is retained history, not a new SQL observation, current grant, passing-check claim or approval calculation.

Each formal review also receives a complete retained-evidence packet at /epicd-evidence/review.json, outside its source copy. reviewContext.primaryEvidence gives its digest, byte length and record count. The packet expands selected references to their full redacted retained views; selected page windows still govern the small prompt previews. It automatically includes current validation records and settled non-coordinator worker results for the review's task scope. Pending worker turns are explicitly listed, not represented as final results. Original observation truncation and omission markers remain: a complete retained record cannot recover missing original output.

Whole-epic packets additionally include each actual descendant closure's commit, publication, independent candidate/exact reviews and their validation records, plus prior epic-repair commit/review/validation history. The closedTasks.historicalRecords index remains available for navigation. Preexisting closed tasks do not acquire invented run-owned proof. Historical evidence does not become current validation, resolve findings, grant permissions or instruct approval; only current evidence IDs and independent judgment can qualify the reviewed revision.

SQLite snapshots each packet once and binds it to the review, prompt and launch. The shared SDK/native Herdr launcher verifies the private file and sends its bytes through a private descriptor into a read-only mount; neither the journal nor the launch directory is exposed. Packet integrity is checked again before recording successful process stop. A later independent review takes a new snapshot. Packets are bounded to 2,048 unique records and 16 MiB; overflow fails admission without clipping evidence or launching a reviewer. Unsupported earlier state is preserved and refused, not upgraded or deleted. This transport does not establish live-model delivery acceptance.

Validation preflight, the confined check, service handling and postinspection run in one independently supervised kernel worker. Its one-use launch and exact stop receipt are journal-bound. A replacement controller can reconcile that original execution without replay; a stopped worker with no retained check outcome becomes interrupted, never a synthetic pass or command result. Approval still requires the actual result, exact source/environment evidence and successful complete-worker stop. The required check keeps its exact timeout; the enclosing worker has a bounded additional two minutes for admission and postinspection. Local stop never substitutes for current fixture authority or remote PostgreSQL quiescence.

Structured action and domain-record views redact values before JSON serialization. This preserves adjacent fields and multiline output after credential lines; applying text redaction to the serialized object could consume that evidence along with a secret. Redaction remains best effort, not a secret detector, and retained diagnostic text is not independently verified evidence.

Each decision snapshot stays within 64 KiB. Observations are delivered as an ordered prefix with an explicit backlog flag; admission acknowledges only the delivered cursor. Large event metadata causes another page, not skipped events or an increased limit. Shortened observations remain in SQLite and can be retrieved with the read-only, run-scoped inspect_observation capability. Its pages contain retained JSON text with UTF-16 offsets and nextOffset; reading every retained character does not turn diagnostic claims into verification evidence. Current authority/evidence summaries and the latest action outcome remain in context. When request arguments are too large, latestActionOutcome retains the action identity and result while explicitly omitting those arguments from the preview; the journaled request is unchanged. A mandatory snapshot that cannot fit still fails explicitly.

Orchestrator context and inspect_run use one read-only SQLite snapshot. Complete turn history is validated once and reused only during that synchronous read. The temporary records are immutable; later reads and action admission validate fresh state. Review approval and authority are never cached across actions. Both SDK and native Herdr use this shared journal boundary.

The run outlives any one Astra conversation. Between decisions, the controller retires a confirmed-stopped coordinator when its recorded history reaches 12 turns, 512 KiB of serialized prompts/schemas/results, or 196,608 reported SDK input tokens. These are conservative rollover thresholds, not exact context occupancy or monetary limits. SDK usage is retained against the exact acknowledged launch; native Herdr uses the same byte/turn guards without inventing token counts from terminal text.

Rollover starts a fresh gpt-6-astra conversation in the selected runtime with unchanged settings and current journal context. Memory, assignments, evidence, findings, policy and consumed budgets remain intact. Pending instructions and unknown stop states prevent retirement; a frozen decision is reconciled before its conversation can be retired. Old workspaces and provider records are retained, not deleted or copied into the new conversation. Unexpected runtime failures still require diagnosis; this does not introduce a generic retry or model fallback.

Codex exposes automatic compaction settings, but this safeguard does not depend on compaction succeeding or assume that the Astra API context window is the installed Codex runtime's effective limit.

Explicit runtime handoff

resume always uses the recorded runtime. To switch deliberately, pause the run and wait for its controller to detach, then inspect status again for the current control version:

node dist/cli.js handoff RUN_ID --state /path/to/private-state/run.sqlite3 \
  --runtime herdr --control-version VERSION

Use --runtime sdk to switch back. --codex-path selects a native executable; otherwise SDK uses its pinned binary and Herdr resolves native codex. Herdr selection requires a managed caller and read-only discovery of that caller's exact named session and workspace, never the focused pane. --herdr-path can select the Herdr executable. Handoff does not create layout, submit prompts or start a model. Inspect status and explicitly resume afterward; an unanswered escalation still requires its correlated response.

The handoff holds a controller lease, verifies physical repository ownership, and rechecks the observed control version before one atomic journal transaction. All turns, launchers, workspace I/O and delivery/fixture operations must have recorded stop and settlement. Pending agent instructions are not discarded. If work is uncertain, reconcile it in its recorded runtime first; a dead controller is not stop proof.

Stopped conversations are retired without copying provider session IDs into another runtime. Existing workspaces, native endpoint identities, exact evidence, findings, memory, policy, grants and budgets remain intact. The next coordinator starts a fresh Astra conversation using durable context. Retirement does not revoke valid historical evidence, but later contamination can still revoke it. No retired conversation can take another turn. Switching runtime never changes repository identity, private storage, authentication paths, worker defaults or permission grants, and does not imply cleanup of old resources.

Fixture authority, inspection and creation

Review the frozen fixture declarations in status RUN_ID --json before granting access. A declaration identifies its canonical local PostgreSQL socket directory, port, existing role, exact database and expected owner. Grant only the operations you intend, with an ISO-8601 UTC expiry in the next 24 hours:

node dist/cli.js grant-fixture RUN_ID FIXTURE_ID --state STATE_PATH \
  --control-version VERSION --operations inspect \
  --expires-at EXPIRY_ISO8601 --psql-path /absolute/path/to/native/psql

node dist/cli.js revoke-fixture-grant RUN_ID GRANT_ID --state STATE_PATH \
  --control-version VERSION

Use the canonical native psql executable, not a shell wrapper such as pg_wrapper. The grant pins the run, policy, declaration, executable contents and filesystem/socket identities. A changed socket or provider requires a new grant. Replacing a grant revokes the old identity but preserves its history; revocation never deletes a database. Grant changes do not answer pending escalations or resume paused runs. Ordinary respond messages cannot issue grants.

The orchestrator chooses when to invoke inspect_fixture. Its fixed read-only catalog query connects to the declared local server's postgres maintenance database using the declared role; startup files, inherited PostgreSQL environment and password files are not loaded. Only the trusted inspection process receives the exact socket, inside a separate PID/network sandbox. The psql options reference documents the startup-file and error-stop controls used here.

Inspection distinguishes a missing socket, an absent database, a present database and a failed query. Matching database ownership does not establish epicd ownership. Successful local authentication is not reported as proof of peer authentication, and neither observation grants service access to repository commands.

To authorize creation too, use --operations inspect,create with a declaration that allows create. The orchestrator may then request provision_declared_fixture with operation: "create" and the observed expectedGeneration (initially 0). The kernel creates only an absent exact database, using the existing declared role. It does not install/start PostgreSQL, change roles or authentication rules, or adopt an existing database. The real provider contract is tested against PostgreSQL 18 with a non-superuser CREATEDB role; repository commands never receive that role's socket.

Before mutation, SQLite records the generation, planned database OID, operation marker and exact creation backend. A one-use dispatch gate prevents mutation replay. The new database starts with connections disabled; a locked transaction verifies its identity before installing the ownership marker and enabling connections. Completion requires a separate observation proving that backend has stopped and the resource's OID, name, owner and marker match. PostgreSQL's CREATE DATABASE reference describes the explicit OID, ownership and transaction constraints.

reconcile_fixture_creation inspects a recorded creation without repeating SQL mutations. It requires an inspection grant after dispatch. An unmarked, changed or possibly still-running creation remains uncertain and is preserved. A replaced socket cannot prove that the old backend stopped. Only a never-dispatched intent, or confirmed backend stop followed by an absent resource, permits a new creation generation. Reset/cleanup remain unavailable. Successful creation alone does not make the fixture accessible to repository validation; SQL access requires the separate declaration and grant below.

Restricted validation against a run-created fixture

Add a fixtureValidation entry alongside the corresponding fixture in the frozen policy. For example, these fields declare one disposable database and its dedicated SQL role:

{
  "fixtures": [
    {
      "id": "browser-db",
      "provider": "postgresql",
      "socketDirectory": "/run/postgresql",
      "port": 5432,
      "role": "fixture_manager",
      "database": "browser_fixture",
      "expectedOwner": "browser_role",
      "operations": ["create"],
      "environmentBinding": "browser",
      "cleanup": "retain"
    }
  ],
  "fixtureValidation": [
    {
      "fixtureId": "browser-db",
      "validationRole": "browser_role",
      "listenPort": 55432,
      "connectionVariable": "DATABASE_URL",
      "pgbouncerExecutable": "/usr/bin/pgbouncer"
    }
  ]
}

The existing PostgreSQL service, management role, dedicated validation login, authentication configuration and native PgBouncer executable must already be prepared by the operator. Use canonical paths. Epicd does not install packages, create roles, change HBA/peer mappings or adopt an existing database. The management role needs authority to create the declared database for its expected owner; it is never exposed to repository commands. The validation role must have no superuser, role/database-creation, replication, RLS-bypass, membership or outside-object authority. The kernel also rejects unsafe callable functions, privileged parameter grants, foreign-data access and enabled event triggers. These deliberately conservative checks can reject an extension-enabled database. A broad peer-authenticated account is not a substitute for the dedicated role.

After reviewing the frozen declaration, grant SQL access separately from creation:

node dist/cli.js grant-fixture-validation RUN_ID browser-db --state STATE_PATH \
  --control-version VERSION --expires-at EXPIRY_ISO8601 \
  --psql-path /absolute/path/to/native/psql

node dist/cli.js revoke-fixture-validation RUN_ID GRANT_ID --state STATE_PATH \
  --control-version VERSION

The grant authorizes repository SQL in that disposable database under its dedicated role, including changes that PostgreSQL permits to the role's own password/settings. It does not authorize broader host administration. The provider/socket and broker identities are pinned; expiry is at most 24 hours. Granting access does not query, create or adopt a database. An inspection/creation grant, ordinary user response or model request cannot mint this permission.

Reference "browser" in a check's environmentBindings. The orchestrator chooses provision_declared_fixture and run_validation as separate actions. Validation requires this run's successful creation record, exact database OID/owner/marker, a current SQL grant, fresh role/catalog checks and no unresolved earlier access. The unchanged check receives DATABASE_URL for one private TCP database/role mapping. Its nested sandbox cannot see the upstream or admin sockets, broker configuration or broker processes. A check can use at most one host fixture and four total environment bindings; local-service ports and URL variables must be distinct.

SQLite binds each access to its validation operation, workspace evidence, creation generation and grant. Local process exit does not prove a PostgreSQL query stopped. The kernel accepts environment evidence only after both local stop and a fresh exact-resource observation with no other database connections. A timeout or revoked grant can therefore release the stopped local workspace while preserving the database exclusion. The orchestrator can use inspect_fixture_access and reconcile_fixture_access to inspect it without replaying SQL. A renewed grant permits a new read, not acceptance of an observation begun under the old grant. Reconciliation never changes failed or unverified evidence into a pass.

If an interrupted validation reserved SQL access but never dispatched its transport, reconcile_fixture_access can atomically close that unused gate, including after controller replacement or grant revocation. The original action must no longer be running, and the recorded validation, creation and grant must still match. This performs no database query or mutation and prevents delayed callbacks from dispatching SQL. It does not settle the parent validation, release its workspace I/O exclusion, or supply process-stop or passing-check evidence.

For dispatched fixture commands, a separate trusted supervisor retains an exact local namespace-stop receipt outside repository mounts. It interrupts on controller loss, waits for namespace teardown, and publishes the receipt through a one-use private file gate. Reconciliation can adopt that receipt after journal acknowledgment loss or atomically prevent a never-started launch. A killed supervisor/namespace monitor without a receipt remains unknown; database quiescence or an absent PID cannot replace physical proof. Local proof requires no SQL grant, but a database observation still needs current permission. The original validation outcome and its workspace I/O exclusion remain unchanged.

Unknown local stop, a replaced socket or unresolved remote work prevents reuse, runtime handoff and run completion. Retained command-control directories are evidence, not automatically deleted scratch. No automatic backend termination, fixture reset or cleanup is implemented. Both runtimes use this shared kernel, but the real PostgreSQL contract tests do not establish authenticated model-led browser recovery.

Check-scoped PostgreSQL validation

When a check needs a fresh database rather than a shared host fixture, add a separate validationServices declaration to policy:

{
  "validationServices": [
    {
      "id": "e2e-postgres",
      "provider": "postgresql",
      "lifetime": "check",
      "binDirectory": "/usr/lib/postgresql/18/bin",
      "database": "browser_test",
      "role": "fixture_owner",
      "port": 55432,
      "connectionVariable": "DATABASE_URL"
    }
  ]
}

Reference "e2e-postgres" in the required check's environmentBindings array. The orchestrator still chooses when to call run_validation; the kernel initializes the declared service before executing that check's unchanged command/arguments. Setup stays inside the validation profile already authorized for the run. It does not consume or expand a host-fixture grant, install PostgreSQL, or connect to a host database. Service IDs cannot alias host fixture bindings.

Each invocation creates a fresh private PostgreSQL cluster and database, exposes its URL only inside that check, and discards its data when the sandbox stops. TCP uses the sandbox's private loopback interface; no host socket, host account file or credential is mounted. Even PostgreSQL-superuser operations remain inside the same filesystem/process/network confinement. The existing CREATEDB host role is never passed through SET ROLE as a substitute for isolation; PostgreSQL permits resetting that role.

SQLite records the instance ID, definition digest and native executable fingerprints before launch. Binding and its audit write commit together. Setup failure cannot start the check; a changed runtime cannot supply passing environment evidence. Replaying the same action returns its stored result, while a new invocation gets a new instance. There is no database state shared between checks, and no automatic substitution for a declared host fixture. Use a deliberately matching check/plan; application dependencies and browser binaries still need to exist in the validation environment.

The live contract currently uses PostgreSQL 18. Native initdb, pg_ctl, postgres and psql must reside in the declared canonical /usr directory. The validation user must be non-root. These services are visible in status --json and in the orchestrator's frozen policy context.

Safety and recovery

The model chooses the next useful capability. The kernel validates control versions, leases, policy, workspace ownership, and evidence before executing it.

  • Only the kernel claims/closes Beads and stages/commits/publishes Git changes.
  • Implementation and review use private repository copies. The user's checkout and index are not a scratch workspace.
  • Review judgments and validation evidence are tied to exact candidate/workspace/turn identities; actual-SHA verification is separate from pre-commit review.
  • inspect_review explains approval through a current kernel-derived assessment: the latest evidence ID, first unsatisfied condition and bounded references. The same evaluator guards commits. A review's requiredChecks lists outstanding demands, not already-satisfied tests; earlier demands remain binding after later reports or reviewer replacement.
  • A task close requires a current verified publication and claim. The installed Beads close interface lacks atomic expected-owner/parentage comparison; fresh before/after checks detect conflicts but cannot eliminate that race. This remains a release blocker.
  • Container closure requires proven closure of its own descendants; unrelated root tasks may remain open. Root closure additionally requires current independent whole-epic verification at the published SHA or its proven tracker-only ancestor. Neither a model verdict nor a successful Beads exit code substitutes for those proofs.
  • complete_run inspects the live tracker graph and both publication refs, verifies this run's root-closure markers, published closed-scope tracker export and stopped work, then records completion and the successful action result in one SQLite transaction. Interrupted inspections can be reconciled without repeating a close. Workspaces, agent sessions, publication artifacts, tracker export copies and owned fixtures are explicitly retained for inspection, not silently deleted.
  • Unknown stop state stays unknown. Restart reconciles recorded work; it does not replay an uncertain external mutation.
  • Epicd creates local commits/refs, never pushes.

Status is derived from the journals: current control, assignments, actions, evidence, diagnostics, pending questions, and controller ownership. SQLite also retains strategy, hypotheses, findings, and run-local operational knowledge.

Runtime diagnostics retain bounded, best-effort-redacted content. Native terminal excerpts are partial; clipping and omissions are explicit. Diagnostic hashes establish content identity, not truth or approval.

Inspection results in action history are compact previews. inspect_action retrieves the retained request and result, including historical handoff instructions and output beyond a preview. Start with an action ID, offset: 0, limit up to 4000 and expectedDigest: null; continue at nextOffset using the returned digest. Offsets count UTF-16 characters in the fully redacted view. If the action changes, continuation fails and must restart at offset zero; pages from different outcomes cannot be silently combined. The latest requested action/observation page remains intact in decision context, while historical pages and status summaries stay compact. Inspection cannot replay a command, grant permissions, promote diagnostic evidence, or replace current eligibility checks.

Both SDK and native Herdr runtimes also observe selected tool calls, tool results, and command records from the pinned Codex 0.153.4 private transcript format. Capture requires the exact submitted prompt and session/workspace identity, rejects linked or replaced transcript files, and excludes provider reasoning and base instructions. Byte-offset source identities allow cold replay without duplicate observations or budget charges. Missing transcripts, unmatched turns, incomplete final records, and absent completion markers produce explicit observation gaps. This is a bounded diagnostic adapter, not a public transcript-completeness guarantee: tool output can be assembled or summarized by the agent, and transcript markers never certify execution, validation, approval, or process stop.

The transcript regression checks are npm test -- test/codex-transcript.test.ts test/controlled-transcript.integration.test.ts test/codex-native-state.test.ts test/diagnostic-artifacts.integration.test.ts. After building, authenticated adapter checks are EPICD_LIVE_ORCHESTRATOR=1 EPICD_LIVE_HERDR=1 npm test -- test/controlled-sdk.integration.test.ts test/controlled-herdr.integration.test.ts. The latter requires Codex authentication and a managed Herdr caller; its native harness owns a separate named test session. These tests include actual failed-command diagnostic capture, native conversation continuity and independent process-stop checks, not complete epic delivery.

If a controller lease must be fenced, first inspect its exact PID and lease ID:

node dist/cli.js unlock RUN_ID --owner-pid PID --lease-id LEASE_ID --force \
  --state /path/to/private-state/current.sqlite3

Unlocking does not kill processes or prove they stopped. A replacement controller must reconcile their recorded identities. Do not use it as routine resume.

quarantine RUN_ID --force --state PATH preserves an invalid current-format run's raw records; it does not infer cleanup actions or delete external resources. Unsupported whole database formats require a different state path, not quarantine or migration.

Development and verification

npm run build
npm run typecheck
npm test
npm run format:check

Build before testing: supervised-process and CLI tests exercise the compiled entrypoints. Do not rebuild or edit source while those tests are running.

Normal integration tests use owned temporary repositories and scripted provider results. They exercise real journaling, Git operations, confinement, and process stop; they do not prove model judgment. Authenticated model/native checks are opt-in and recorded separately. See the implementation plan for remaining acceptance scenarios and their live-test commands.

The unscripted SDK delivery acceptance uses the existing Codex authentication cache, installed Beads CLI and a generated one-task epic:

EPICD_LIVE_DELIVERY=1 npm test -- test/model-led-delivery.integration.test.ts

It allows up to 20 minutes of actual Astra work and always retains its printed private /var/tmp/epicd-live-delivery-* directory for diagnosis. It does not use the project's tracker or modify the user's checkout. The 2026-09-08 run passed complete SDK delivery, including nine context rollovers, three independent reviews, task/epic closure, final tracker publication, repository ownership release and preservation of the original checkout/index. This is one bounded acceptance case, not full release certification.

From a Herdr-managed caller, the native equivalent creates its own named server, private session registry and caller pane, then runs the same model-led acceptance through real native Codex TUIs:

EPICD_LIVE_HERDR_DELIVERY=1 npm test -- test/model-led-herdr-delivery.integration.test.ts

The native run allows 40 minutes. The harness retains its printed /var/tmp/epicd-native-delivery-* logs and child report as well as the run artifacts. It stops only its owned server after the child actually exits; an unknown child outcome retains the session for inspection. Neither terminal idle state nor a passed native-startup check proves whole-epic delivery.

The 2026-09-08 native acceptance passed in 26½ minutes: 72 decisions, ten Astra/high conversations, three approved independent reviews, task/epic closure and final tracker publication. All 60 turns had stop evidence; 58 had actual native endpoints and two were cancelled with never-started receipts. The run exercised two byte-bounded observation pages and preserved the original checkout, index, README and concurrent source edits. This live run preceded the subsequent large-action preview hardening; its exact evidence and follow-up verification are recorded in the plan. Receipt/browser recovery scenarios and the remaining resource/authority release audit are still open.

Set EPICD_LIVE_DELIVERY_SCENARIO=receipts before either live delivery command to exercise receipt contamination recovery. The test appends two receipts to a stopped private review copy, explicitly labeled as host-test fault injection. It requires model-chosen inspection, fresh independent exact-revision evidence, completed delivery and preserved user work; it does not demonstrate an immutable reviewer writing, active-turn intervention or knowledge reuse on a second task. Terminal results and remaining incident requirements are recorded in the plan.

The SDK receipt case passed on 2026-09-08 in 17½ minutes: 90 decisions, twelve Astra/high conversations, preserved contaminated evidence, three subsequent independent approvals and completed delivery. All 79 turns stopped. The native injected-receipt case subsequently passed at f632686 in 38¾ minutes: 83 decisions, twelve Astra/high conversations, all 77 turns with native endpoints and stop evidence, three fresh approvals, final tracker publication, ownership release and preserved original work. These are bounded post-stop injection scenarios, not live wrapper-denial or browser recovery.

The opt-in fixture contract uses real PostgreSQL binaries but creates and stops its own Unix-socket-only cluster; it never uses an existing host database service:

EPICD_TEST_PG_BINDIR=/absolute/path/to/postgresql/bin npm test -- test/fixture-postgresql.integration.test.ts test/fixture-creation-postgresql.integration.test.ts test/validation-services.integration.test.ts test/delivery.integration.test.ts

For restricted host-fixture validation, also supply a native PgBouncer binary:

EPICD_TEST_PG_BINDIR=/absolute/path/to/postgresql/bin \
EPICD_TEST_PGBOUNCER=/absolute/path/to/pgbouncer \
  npm test -- test/fixture-bridge.test.ts test/fixture-bridge.integration.test.ts test/fixture-validation-policy.test.ts test/fixture-validation.integration.test.ts

These tests create their own roles and peer mappings only in owned temporary clusters. They cover actual SQL, privilege rejection, grant revocation, journal rollback, remote-stop exclusion and cold reconciliation; they do not configure the operator's PostgreSQL service.

The unused SQL-gate recovery change passed 166 tests across 11 fixture, delivery-recovery and runtime-handoff suites with no skips. Seven new cases cover interruption before dispatch, preflight, controller replacement, revoked grants, provenance checks, audit rollback and a still-executing original handler. They use real PostgreSQL/PgBouncer and controlled boundary faults, not autonomous model judgments. Already-dispatched SQL with missing local-stop acknowledgment remains excluded. This is bounded recovery verification, not release certification.

Targeted action interruption passed an initial 199-test regression run (one existing optional Beads test skipped), then 167 tests with no skips after acknowledgment recovery was added. Coverage includes real target-only process cancellation, unchanged unrelated work, durable audit failure, replay, foreign/stale/cold handles, publication/tracker admission fences, and lost acknowledgments before and after signalling. These are kernel and controlled-driver tests, not proof that a live model chooses this capability correctly. The persisted action schema is a hard cut to format 34; unsupported retained state is not migrated or deleted.

The real browser fixture needs a local npm project containing @playwright/test and its matching Playwright headless Chromium directory. It bundles those dependencies and the current native Node executable into the temporary test repository; it does not expose a host browser/cache directory to agents or validation. The mechanical browser contract is:

EPICD_TEST_PG_BINDIR=/absolute/path/to/postgresql/bin \
EPICD_TEST_PGBOUNCER=/absolute/path/to/pgbouncer \
EPICD_TEST_PLAYWRIGHT_ROOT=/absolute/path/to/playwright-project \
EPICD_TEST_BROWSER_DIRECTORY=/absolute/path/to/chrome-headless-shell-linux64 \
  npm test -- test/browser-fixture.integration.test.ts

The authenticated browser acceptance has not yet passed. Its first SDK attempt failed after 24 minutes: independent review could not obtain the original browser-command execution record. The implementer's command had stopped at the helper's hardcoded /tmp path, before authentication. The helper now honors private TMPDIR, but a real confined probe then reached Playwright's local server and failed with listen EPERM. Ordinary worker commands cannot open network listeners; kernel validation uses a separate private network environment. The transcript reader recovers the original missing tool result from that retained SDK run, and fresh authenticated SDK/native tests exercise failed-command capture. That diagnostic evidence does not retroactively approve the failed run or demonstrate browser recovery.

Both SDK and native Herdr browser harnesses now probe the intended execution boundary before creating a run, database or Herdr test server: ordinary workers must have private scratch and denied network listeners, while the kernel executor must have private scratch and a working private local server. A failed prerequisite fails the acceptance explicitly; it is not a skip or success. Verify this boundary with EPICD_CODEX_CONFINEMENT=1 npm test -- test/browser-worker-preflight.integration.test.ts. This local-command probe is not proof of native TUI delivery. The mechanical browser contract records a real authentication failure through run_diagnostic_check without the database binding, then uses separately authorized fixture creation and required validation to obtain a green browser result.

Set EPICD_LIVE_DELIVERY_SCENARIO=browser with the same four toolchain variables and either authenticated delivery command above. The worker stops and requests kernel execution; the lead chooses a diagnostic and supplies its result in a follow-up. Acceptance requires a real completed-code/failed-browser report citing that kernel evidence from the same assignment and source snapshot, further diagnostic follow-up, configuration inspection, observed database absence, authorized creation, green kernel browser evidence before review, verified delivery, and preserved test/oracle/dependency files. The initial claim shape and execution boundary are documented scenario instructions; no provider report or recovery strategy is injected. SDK/native browser deadlines remain 30/60 minutes, and test-owned database data and run artifacts are retained. Passing the prerequisites does not establish full model-led recovery.

The SDK attempt at b1b1b08 exercised the kernel handoff and genuine authentication failure, authorized fixture creation, a reviewed private commit, and green browser checks at both candidate and actual commit revisions. It still failed the 30-minute deadline after 109 decisions: exact-revision review required primary historical records that were difficult to retrieve through clipped inspection previews. It paused with all 110 turns stopped. A later SDK attempt at e0dadb8 reached exact approval and publication but paused after 99 decisions without task/epic closure; all 101 turns stopped. Their test-owned PostgreSQL servers are stopped and data/run journals are retained. Neither was completed delivery, and later acceptance assertions were not reached.

inspect_action provides bounded original-record retrieval. Diagnostic reviewer follow-ups are now available through continue_agent, and run_review.references supplies kernel-resolved historical records without coordinator transcription. These capabilities preserve current validation, finding and exact-revision requirements. The reference/review/recovery and broader controller/delivery regressions passed 178 tests across 12 files; provider judgments in those tests are scripted.

The native browser attempt at 45117d4 failed after 18 minutes and 35 decisions: a new coordinator shell did not acknowledge launch setup. All 37 turns have stop evidence, the owned Herdr/PostgreSQL servers are stopped, and the failed run is retained. It reproduced the browser authentication failure but reached no review, commit or closure. A controlled experiment reproduced an open-before-write readiness-marker race consistent with that trace; the exact rejected bytes were not retained. Startup now publishes the complete marker atomically, preserving the exact-generation and timeout checks. The combined branch passed its 14 focused reference/startup tests and two selected real native Astra/reviewer checks (two other native cases were filtered out). These are bounded checks, not a passing full browser rerun. The browser evaluator and deadlines remain unchanged; full recovery and remaining release requirements are still open.

The subsequent native browser attempt at 85fc42d also failed, reaching the unchanged 60-minute deadline after 105 decisions. It did exercise genuine authentication failure, fixture recovery, green candidate/committed browser checks, both native task reviews with primary record references, publication and task closure. Whole-epic checks passed, but final review blocked on missing historical failure/recovery and descendant proof records. The lead gathered records without obtaining a new final verdict or completing the epic. All 104 turns have stop evidence; its owned Herdr/PostgreSQL servers are stopped and artifacts are retained. The completion assertion failed before later acceptance assertions ran. No evaluator or deadline was relaxed, and this is not a passing browser acceptance.

The ordinary full suite at ee9eb30 recorded 923 passes, one failure and 24 skips. Its sole failure was a diagnostic-reopen test still expecting storage format 30 instead of the then-current 33; the correction passed the diagnostic and hard-cut state suites (36 tests). That narrow correction does not replace the retained failed full-suite result. Current storage changes remain hard cuts without migration or deletion of unsupported data.

The subsequent full suite at 047404b passed 943 tests with 24 skips and no failures across 77 files. It predates the typed primary-record change. That change separately passed 274 tests across 16 files with no skips, covering original record paging, reference binding and tampering, whole-epic proof indexing, real fixture/grant history, current-evidence gates, cold recovery, context bounds and SDK/Herdr interface contracts. Its first regression run exposed the serialized-redaction data loss described above; the failing assertions remain and pass after the product fix. These results do not establish live native browser recovery or complete release readiness.

The fresh SDK browser attempt at d13079c completed delivery in about 24 minutes: 112 decisions, 20 Astra/high coordinator conversations, three independent approvals, real authentication failure and authorized fixture recovery, task/root closure, final tracker publication and repository ownership release. All 108 turns and seven complete validation workers have stop evidence; the test-owned PostgreSQL server is stopped and its data retained. Source, evaluator and compiled output stayed frozen throughout.

That attempt still failed the unchanged acceptance test at model-led-delivery.integration.test.ts:460: the lead did not issue another implementer continuation after receiving the completed-code/failed-browser report. It used the kernel evidence to diagnose and repair the environment. Postmortem inspection also found no direct orchestrator read of playwright.config.cjs, which a later, unexecuted assertion requires. Completed delivery does not replace those prescribed-action requirements; changing them needs an explicit contract decision. No evaluator or deadline was relaxed, no live native parity is implied, and full acceptance remains open.

The fresh native Herdr browser attempt at cb15e85 failed after about 60 minutes with awaiting_user, not completed delivery or decision-budget exhaustion. It reproduced the genuine kernel authentication failure, created the declared fixture, passed candidate/committed/final-epic browser checks, obtained both task approvals, published the application commit and closed the child task. It missed the required kernel-failure handoff and completed-code/failed-browser report before fixture creation. Final review blocked on that historical reporting gap and incomplete supplied primary records. The kernel refused a late continuation of the closed task; a diagnostic reviewer follow-up confirmed that a new report cannot prove the original sequence. Astra then requested an explicit requirement amendment without closing the epic or inventing the missing history.

That native run used 112 decisions and 22 Astra/high conversations. All 112 turns have stop evidence; 111 have native endpoints and no eligible turn lacks one. The seven complete validation workers and all three fixture SQL uses have stop evidence. The owned Herdr/PostgreSQL servers are stopped; data, reports, workspaces and the incomplete run's repository reservation are retained. Source, evaluator and compiled output remained unchanged. The completion assertion failed before later acceptance checks ran; postmortem observations are not replacement passes. Evidence is retained at /var/tmp/epicd-native-current38-j3DxvY and /var/tmp/epicd-native-delivery-s0cgYW. Neither the evaluator nor its deadlines were changed, and browser acceptance remains open.

The original dispatcher and its dedicated tests have been removed. Their history remains in Git; old state, user repositories, and user-owned Herdr resources are not deleted by this hard cut.

License

MIT

About

Durable Codex orchestration for dependency-safe Beads epics with independent review, exact-commit verification, and resumable SDK or Herdr sessions.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages