Files
TORCH/docs/ARCHITECTURE_DECISIONS.md
thePR0M3TH3AN 6fdb88ea20 docs: specify proposed candidate attempt store
TASK-6e4ff870-a386-4a11-a59c-444ebc23732e
2026-10-01 09:20:52 -04:00

764 lines
42 KiB
Markdown

# TORCH architecture decisions
Status date: 2026-09-27
This ledger resolves the implementation decisions identified by
`PORTABLE_AGENT_FLEET_SPEC.md` section 31. A decision marked **accepted** is
the current portable-core contract. A decision marked **gated** identifies a
feature boundary TORCH must not cross until a follow-up decision and acceptance
evidence exist.
## ADR-001: SQLite state and retention
**Status:** accepted.
Machine-local structured state uses one SQLite database per installed project,
with WAL, foreign keys, and a bounded busy timeout. Messages, acknowledgements,
audit records, presence, leases, check receipts, integration requests, context
samples, schedule runs, and Fleet changes share the project identity but remain
separate tables. TORCH performs no automatic history deletion. Retention or
compaction must be an explicit, owner-authorized operation added with recovery
tests; until then, durable records are append-preserving.
## ADR-002: Control-plane transport
**Status:** accepted.
The service layer is an in-process API. The agent-facing transport is MCP over
stdio and the non-MCP fallback is the structured CLI. The Fleet Console exposes
a read-only loopback HTTP observation projection, plus only specifically
approved local owner actions documented in separate decisions. There is no
required daemon, Unix socket, network message queue, or hosted database. A
future socket transport must preserve identity binding and cannot become the
data model.
## ADR-003: Project identity and repository moves
**Status:** accepted for the first release; moves are gated.
Installation creates an opaque project UUID stored in tracked configuration,
the ownership manifest, and machine-local metadata. All three must agree.
Machine-local state also records the canonical absolute repository root, and a
different root fails closed. Moving a managed repository therefore requires a
future explicit migration command that verifies identity and rewrites only the
local root binding; TORCH must not infer a move from a matching directory name.
## ADR-004: Runtime process supervision
**Status:** accepted.
Runtime adapters own process-specific creation, resume, status, capture, and
stop behavior and declare every capability. TORCH persists stable Fleet
identity and runtime session IDs but does not require a resident supervisor.
Unsupported controls fail explicitly and durable messages remain available.
The optional system-schedule launcher uses user systemd on Linux; macOS and
Windows launchers are separate adapters and are not claimed by this release.
## ADR-005: Configuration and generated files
**Status:** accepted.
`.torch/torch.yaml`, approved decisions, and tracked backlog records are
authoritative. `roster.yaml` and prompts are reviewable materializations of an
approved organization. The ownership manifest records their exact hashes;
doctor reports drift, and generated prose never overrides configuration.
Changing organization requires the Fleet-change workflow rather than editing a
generated prompt and treating it as policy.
## ADR-006: Runtime capability negotiation
**Status:** accepted.
Every adapter implements the common operation surface and declares support for
each operation. Startup validates the declaration before planning a launch.
The minimum useful adapter can create or resume a session and exchange durable
messages through CLI/MCP; live steering, hooks, listing, and stopping may be
reported unsupported. TORCH never substitutes an unsupported native feature
silently.
## ADR-007: Check invalidation
**Status:** accepted for the first release.
A check pass is reusable only for the exact Git commit and the exact hash of its
configured definition. A dirty or changing worktree produces no transferable
pass. Additional non-Git inputs require a future declared input-fingerprint
schema; until then projects must encode them in the check definition or rerun
the check. Branch names alone never validate a receipt.
## ADR-008: Local canonical remote naming
**Status:** accepted.
The local bare canonical remote is named `torch-canonical`, never `origin`, and
lives under machine-local project state as `remote.git`. Creation refuses an
existing path, remote-name collision, or dirty checkout. The configured remote
and path are recorded in tracked configuration and the ownership manifest.
## ADR-009: Multi-machine security
**Status:** gated.
The first release is single-machine and exposes no network control-plane write
surface. Multi-machine transport is prohibited until a separate decision
defines mutual authentication, encryption, project isolation, replay defense,
revocation, and recovery, with adversarial acceptance tests. A forge remote is
not a substitute for control-plane authentication.
## ADR-010: Distribution and update signing
**Status:** gated.
Local candidate builds use exact tree hashing, versioned compatibility
metadata, isolated state, and atomic activation/rollback. They are not signed.
Publishing a package or enabling unattended remote updates is blocked until a
follow-up decision selects artifact signing, trusted-key rotation, provenance,
and offline rollback verification. Local `npm pack` qualification does not
claim supply-chain authenticity.
## ADR-011: Project-specific coordination hierarchy
**Status:** gated.
The installed organization config now accepts a versioned role graph, and
TORCH persists sustained-evidence proposals, exposes them for owner review,
and prepares a read-only pilot plan. A separate owner-confirmed CLI activation
now commits the approved graph, derived manager schedules, ownership-manifest
hashes, and pilot record after freshness checks. It does not launch an identity
or modify a system timer. Doctor reports graph roles bound to missing
identities, and the read-only Console renders approved roles, identity status,
reporting links, coordination links, and implementation ownership. Fresh,
resumed, and on-demand identity briefs now include the current role,
responsibility, authority, reporting, direct-report, coordination, and
implementation-ownership context from the active graph, bound into the prompt
digest. A new read-only hierarchy assessment measures recurring cross-domain
backlog, coordination-request, manager-approval, and handoff events in each
project's configured observation window; it recommends review but never creates
or activates a role. Real-fleet measurement, roster binding across every
activation path, pilot outcome measurement, adoption, and reversal remain
incomplete.
Do not represent the full adaptive organization as implemented or encode
COMBATRIG's proposed departments as the default organization.
The target design is an owner-approved, versioned organization graph generated
from each project's repository and specification. It separates
implementation ownership from coordination, fleet operations, program
priority, and independent review authority. The graph permits optional
coordination levels, one primary owner-facing role, and direct peer
communication. Reporting links do not confer worktree access, task
reassignment, or release authority. One shared backlog remains authoritative;
program and domain documents are summaries, not task queues.
A proposed lead must own a concrete integrated outcome. In this design,
headcount alone must never trigger an added management layer. Before this ADR
can be closed, TORCH still needs real fleet-pressure assessment, pilot outcome
measurement, adoption, and reversal. Owner-approved pilot activation is
available through the CLI and the local Console's fresh-plan, same-origin
preview/confirm flow. Active tasks, branches, messages, and implementation
ownership must remain preserved throughout.
## ADR-012: Per-identity runtime and model portability
**Status:** accepted as a product requirement; implementation gated.
TORCH's policy, identity, backlog, and inter-session communication are
provider-independent. Each persistent identity must be assignable an
owner-reviewed runtime profile, including adapter, model identifier, optional
reasoning settings, and launch policy. A single Fleet may mix Claude, Codex,
Pi, other local agent CLIs, and different model tiers at once. Provider/model
labels remain project configuration rather than TORCH-wide assumptions.
An adapter registry must combine built-in and explicitly installed local
plugins. Plugins declare lifecycle capabilities and unsupported settings;
TORCH may not silently substitute a runtime. Durable TORCH messages remain the
inter-session source of truth when a runtime lacks native messaging or live
steering. Runtime-profile changes preserve identity, backlog, worktree, and
resume state, and remain separate from explicit process startup.
The current implementation is partial: install/config, doctor, and lifecycle
startup share a capability-declaring registry; identities can select registered
adapters and model/reasoning profiles; unsupported settings block launch instead
of falling back. Built-in adapters include Claude, Codex, and Pi. The Pi adapter
uses explicit provider/model profiles, Pi's extension API, and an identity-bound
TORCH MCP server started only for individual tool calls; it runs one resumable
turn per process and stores Pi transcripts outside the repository. Pi's local
CLI contract and bridge are covered by regression tests, and installed Pi
0.80.3 passes an offline, no-session extension startup without a provider turn.
Other Pi versions and live provider resume remain qualification gates. Custom
adapter registration still requires an
embedding caller for programmatic use. Profile review and changes are available
through `torch profile show`, `torch profile
set`, and `torch profile defaults`; they validate and write only project-local
config and do not launch runtimes. A user-managed local plugin trust flow now
pins an absolute CommonJS entrypoint to an explicitly reviewed SHA-256 outside
the repository, validates it before registry loading, and lets profile changes
select newly trusted adapters without editing TORCH source. Project config
cannot grant plugin trust, `runtimes list` does not execute plugins, and
revocation does not affect an already-running process. Launch policy uses a
nested `launchPolicy` object in a runtime default (`runtimes.<adapter>`) and/or
identity (`session_manager` or `domains[]`). Identity keys override matching
default keys; omitted keys inherit. Adapters declare exact supported
field/value sets. Unknown fields/values, unsafe modes, and incompatible
combinations block doctor and startup planning before a runtime process is
executed. Custom adapters without `capabilities.launchPolicy.fields` accept no
overrides. `torch profile show`, `profile set`, and `profile defaults` expose
effective policy; edits require `--yes` and change future plans only.
The local Console also supports per-identity changes through a same-origin,
single-use owner preview followed by explicit confirmation. Preview runs the
same CLI validation path as profile edits; confirmation rechecks the exact
configuration digest before writing and records an owner audit event. The UI
shows current versus next-launch state and does not start or alter a running
session. Only built-in and verified, explicitly user-trusted adapters are
offered as available choices.
Codex supports `sandbox=read-only|workspace-write|danger-full-access` and
`approval=on-request|never|approve-for-me`. `approve-for-me` maps only to
`--approve-for-me` and conflicts with an explicit sandbox because the flag
implies workspace-write; other approval modes map to `--ask-for-approval`.
Fresh defaults preserve the previous invocation with `approval=approve-for-me`
and no redundant sandbox field. The historical generated flat pair
`sandbox=workspace-write`, `approval=approve-for-me` is interpreted as that same
effective mode and migrated by the profile CLI before policy edits. Claude
supports only `permissionMode=default|acceptEdits|auto|manual|dontAsk|plan`;
`bypassPermissions` is excluded. Pi exposes no policy overrides and retains
TORCH's fixed `--no-approve`, `--no-extensions`, and `--no-context-files`.
Remaining portability gates: supported-version Pi runtime matrix coverage and
live mixed-provider resume evidence.
Pi extensions execute with Pi's operating-system permissions, so only the
bundled reviewed bridge is loaded for TORCH sessions; project extension
discovery and project trust are disabled for these invocations.
Built-in starting profiles are Codex `gpt-6-luna` with high reasoning effort
and Claude `sonnet`. These are editable defaults, not fixed policy: after
install, change `runtimes.codex.model` / `runtimes.codex.reasoning` or
`runtimes.claude.model` in `.torch/torch.yaml`; set `model` and/or `reasoning`
on `session_manager` or an individual `domains[]` identity to override that
runtime's default. Profile edits do not start, stop, or replace sessions.
Codex receives the selected reasoning effort as an invocation-scoped
`model_reasoning_effort` override, leaving the user's global Codex config
untouched.
Runtime defaults and per-identity overrides are resolved consistently by
startup planning, doctor, the live identity/agent interfaces, and dashboard
snapshots. Agents can query their own effective profile without relying on a
stale prompt copy. Owners can use `torch profile show`, `torch profile set`, and
`torch profile defaults` to review or edit installed project-local profiles.
Each write requires explicit `--yes`, validates the full project config, and
only changes future launch plans; it never starts or reconfigures a running
session.
## ADR-013: Repository analysis snapshot and approval freshness
**Status:** accepted.
Repository analysis operates on the live working tree: tracked files plus
unignored, non-symlink regular files not yet tracked. It reports tracked and
untracked coverage separately and never executes project content. A domain
proposal binds Git history, `HEAD`, external specification hashes, and a
working-tree fingerprint over visible paths plus analysis-relevant source and
manifest contents. Installation recomputes this fingerprint; any relevant
working-tree drift invalidates approval even when the commit has not changed.
Ignored files are excluded, and the fingerprint is provenance only—it is not
an authorization token.
The deterministic baseline assigns a shared path to a domain only when the
repository graph shows that domain importing that shared component. Other
repository-level candidates remain visible for Session Architect and owner
review; they are not blanket-granted as editable shared paths to every domain.
## ADR-014: Local visual evidence and owner feedback
**Status:** accepted.
Visual review evidence is registered only against an existing backlog task owned
by the publishing Fleet identity, with a full local Git commit SHA and the
runtime session ID captured at publication time. TORCH accepts bounded PNG,
JPEG, WebP, and GIF files from that identity's worktree, rejects symbolic-link
paths, and copies image bytes into the project's private external local state.
The catalog stores content hashes and exposes images only through an
integrity-checking local Console route; project repositories do not gain image
blobs or a second artifact ledger.
Owner comments use the durable message stream as their source of truth. A
comment preview identifies the image, task, target identity, commit, and
message effect before an explicit owner confirmation creates the message. The
Console's loopback feedback endpoint requires a same-origin JSON request and a
single-use, five-minute token bound to the exact preview; confirmation
recomputes the plan, while stale, cross-origin, expired, or replayed requests
fail without creating a message. The snapshot API remains GET-only, and the
Console gains no general task, identity, or lifecycle write access. Private
model reasoning is never exposed. Missing or changed image bytes appear
unavailable, not as verified evidence.
## ADR-015: Browser-local saved backlog views
**Status:** accepted.
Named Console views are bounded, schema-versioned browser preferences scoped by
stable project identity. A view stores only a name and filters over fields
already present in the authoritative backlog snapshot. It does not duplicate
tasks, statuses, ownership, or queue ordering, and it never calls a write API.
Storage errors or malformed data fall back to the unfiltered backlog with an
explanatory status. View edits are explicit: owners may save a new view, update
the selected one, or delete it; each action affects only this browser's saved
presentation preferences.
## ADR-016: Backlog-derived feature and milestone progress
**Status:** accepted.
Backlog tasks may carry optional bounded `feature` and `milestone` labels.
The Session Manager may set labels at task creation or update them with a
revision-checked, reasoned classification action; task-local history and the
control-plane audit record preserve the change. Labels never change task state,
owner, dependencies, evidence, or queue order. The Console derives group status
and completion from the same task records and does not create initiative
records, another task ledger, or inferred project taxonomy. Canceled work is
reported but omitted from the completion denominator. Test, integration,
release, deployment, and live-verification gates remain distinct evidence and
cannot be inferred from progress percentages.
## ADR-017: Preview-confirmed owner priority changes
**Status:** accepted.
The loopback Console may expose one bounded owner control: change a task's
priority. Every change requires a same-origin loopback request, an explicit
reason, a revision-bound single-use preview, and separate confirmation. The
service records the owner, reason, prior/new values, timestamp, revision, and
audit event. The action cannot change state, ownership, evidence, dependencies,
integration, or release status. The snapshot endpoint remains GET-only and no
general-purpose Console mutation API is introduced.
## ADR-018: Preview-confirmed owner task proposals
**Status:** accepted.
The loopback Console may let the configured project owner propose one new
backlog task through an exact, same-origin, five-minute preview and a separate
confirmation. Confirmation creates the task as `proposed` and unassigned;
selected domains are routing suggestions only. The Session Manager retains
triage, readiness, dependency, and assignment authority. Creation records an
owner audit event and is idempotent for a repeated confirmation token. It does
not start, stop, or steer any runtime. The task remains in the existing
authoritative backlog and the observation endpoint remains GET-only.
## ADR-019: Serialized canonical integration
**Status:** accepted.
TORCH owns one serialized landing authority per project. Landing acquires a
cross-process project-state write lock before revalidating the request, target
branch, exact check receipts, authorization, and fast-forward eligibility. The
lock remains held through the canonical Git ref update and durable result
record. A competing lander fails closed with a retryable busy result and must
recompute its plan; after target advancement, stale candidates converge and
rerun required checks on the exact resulting commit. Authorized requests are
drainable in durable insertion order through one explicit `torch integrate
drain` invocation. After each landing, later requests are re-evaluated; sibling
tips that omit the new target become `needs_convergence` until their owner
merges the current target and records fresh exact-commit checks. The drain is
one-shot by default, requires explicit confirmation, and is not installed as a
daemon or timer. An owner may separately define an exact-config system schedule
to invoke the same drain no more frequently than once per minute; each ticket
still needs its own landing-authority authorization and fresh exact-commit
receipts. The schedule cannot merge/converge worktrees, push remotes, or start
AI runtimes. Specialists do not push directly to the canonical branch.
External forge credentials can bypass this local queue, so branch
protection/credential isolation remains optional defense in depth rather than a
false claim of local enforcement.
## ADR-020: Current instruction rehydration on every resume
**Status:** accepted.
Every fresh start and resume composes a digest-addressed instruction bundle
from the canonical project checkout. TORCH delivers that current bundle through
the selected adapter's supported resume mechanism and exposes the same text and
digest in the Fleet brief. Current instructions override historical conversation
content, and agents re-read the current brief before each new work item. The
bundle is never sourced from a potentially stale specialist worktree. If an
adapter cannot apply the bundle, TORCH must disclose or block rather than claim
the resumed agent has current instructions.
## ADR-021: Hierarchy-aware manager check-ins
**Status:** accepted; organization-aware inspection, durable queue, typed
system-schedule dispatch, default Session Manager cadence, and an opt-in
adapter-mediated wake with an atomic project-wide daily invocation ceiling are
implemented. Installing a persistent timer and live provider qualification
remain gated.
Periodic check-ins derive each manager's direct reports from the active
organization graph. They inspect durable presence/task state, unacknowledged
coordination messages, and explicit approval waits; uncertain natural-language
summaries are not treated as approval facts. Approval requests persist with a
named requester and approver, task/evidence context, state, revision, and
decision metadata. Named AI approvers receive durable inbox messages and may
decide only their own requests; the configured owner decides through explicit
CLI confirmation. Decisions are audited and returned durably to the requester.
Manager check-ins classify direct-report requests by whether the manager, a
peer AI, or the owner must act; they route but never decide on another
approver's behalf. The check-in creates a durable,
bounded request for the manager to inspect and advance its reports. It never
approves, reassigns, edits backlog state, creates identities, or starts provider
sessions by default. If the manager is offline, the event remains queued. An
optional adapter-mediated wake is disabled by default; owner-reviewed schedule
configuration plus a project-wide budget can enable it with an atomic maximum
invocation count shared across manager schedules per UTC day. TORCH uses the configured identity adapter/profile and skips active
managers. This count ceiling is not equivalent to a USD spend ceiling. No
persistent timer is installed without owner approval. New
deterministic project proposals include one 15-minute system schedule for the
Session Manager. Additional manager identities require their own explicitly
configured check-in schedule; project configuration validation rejects an
explicit hierarchy that leaves a manager identity without one. Automatic
schedule generation follows hierarchy planning and activation; activation
surfaces schedule additions and obsolete candidates but does not install or
reconcile a system timer.
## ADR-022: Explicit fast-forward-only forge publication
**Status:** accepted.
Integration and forge publication are separate operations. Specialists submit
verified commits to TORCH's local FIFO integration queue; the landing authority
updates local canonical main. Publishing that landed canonical commit to an
attached forge requires a separate owner-confirmed `torch forge sync --yes`
after a read-only `torch forge sync plan`. TORCH pushes only the exact current
canonical commit with normal Git fast-forward semantics and records the
owner's action and observed result. It never force-pushes, fetches/merges
implicitly, or publishes from the integration drain or its timer. A remote-ahead
or divergent result blocks and requires explicit owner convergence. The
remote's compare-and-update semantics reject a race that advances its ref
after planning. Local integration remains available during forge outages.
## ADR-023: Organization-aware lifecycle ordering
**Status:** accepted.
Startup is topologically ordered by the active reporting graph after role
relationships are projected onto persistent Fleet identities. Managers start
before direct reports. Wind-down reverses that identity order and routes each
active identity's final status to its direct manager identities, preserving
bottom-up reporting as the fleet grows. An identity-level cycle or missing
manager blocks both plans; an acyclic role graph is not sufficient if multiple
roles collapse into a cyclic identity graph. A partial startup leaves already
started managers available for coordination and reports the exact identities
that did start; TORCH does not silently stop them or continue launching after a
failure. Planning alone never invokes a provider. Legacy
`session_manager.start_last: true` remains accepted in existing configs but is
ignored in favor of the active organization graph.
## ADR-024: Runtime reservation schema compatibility
**Status:** accepted.
Runtime reservation leases require control-plane schema version 3 or later.
An engine may migrate state that predates reservations, but it must refuse a
reservation-bearing database relabelled by an older engine and must refuse a
database with a newer schema. Runtime and Release must keep the reservation-
capable engine active for any project that has reservation state; rollback to
an older launcher is unsupported until an explicit, recovery-tested migration
exists. This is a compatibility boundary, not permission to install, activate,
or start a runtime.
## ADR-025: Reviewed reservation-state recovery and writer isolation
**Status:** proposed; design-only pending independent QA and Session Manager
review.
This decision records the recovery design required by
`TASK-125dd886-6859-4e3c-a0c7-abbfde9eac24`. It does not authorize an
installation change, a runtime start, a database mutation, a migration retry,
or another qualification gate.
### Observed writer inventory
The clean candidate `be1ae9511e74e3c4c7d8f3d3a5f1212b25c24cc2` has the only
reservation-aware initializer: `src/control-plane/service.mjs` declares schema
version 3, defines the twelve-column `runtime_launch_reservations` table, and
creates both its active-identity and unique-attempt indexes. Its CLI
(`src/cli.mjs`), MCP server (`src/mcp/server.mjs`), and Console server
(`src/console/server.mjs`) all open that control-plane service.
The current canonical checkout and installed active runtime are incompatible
writers. Canonical `src/control-plane/service.mjs` and active
`~/.local/share/torch/runtime/active` (a link to
`versions/0.1.0-alpha.2`) unconditionally execute `PRAGMA user_version = 2`
after their shared-table initializer. The installed `torch` launcher resolves
to that active alpha.2 runtime. Its CLI, MCP server, and Console server also
open their version-2 control-plane service. Any of those writers can relabel a
reservation-bearing database after the candidate has initialized it. A
reservation-aware candidate alone therefore cannot safely repair the state.
### Staged qualification without production-state migration
There is no non-circular native qualification path today. Every normal
candidate-CLI check opens the registered production state root before executing
its check, so it correctly stops at the downgrade guard. That failure must not
be bypassed to create a real-project receipt.
The first implementation phase is therefore limited to an isolated fixture:
it uses a unique `XDG_DATA_HOME`, a fixture installation and state root, and a
candidate-source executable. It may construct version-2 and version-3 fixture
databases and exercise the recovery planner/backup verifier there. Its outputs
are scoped source evidence only; they cannot be stored as, substituted for, or
promoted into any receipt for this project. The existing test fixtures already
support a distinct `XDG_DATA_HOME`; the registered manifest/state-root checks
ensure that such a fixture is not the production installation.
Only after independent QA accepts that isolated implementation may Release
propose a separately reviewed reservation-aware runtime installation and writer
fence. That activation is not authorized by this ADR. Once the fence is live,
the real project may take a fresh read-only plan, a separately authorized
consistent backup, and—only after another review—an apply. Full native gates
remain unavailable until that real state has been safely recovered; this
sequence reports the circular dependency instead of hiding it.
### Required read-only preflight and backup contract
The future recovery planner must first emit a digest-bound, read-only plan. It
must record the project manifest and `project.json` identities, repository and
runtime source hashes, active-launcher target, candidate commit, database file
and WAL/SHM hashes, and the exact observed writer set. It must reject an
unresolved or untrusted writer rather than infer that a process is absent. A
plan does not create a backup or change the database.
The plan must validate all of the following before it can name a database
recoverable:
- `PRAGMA integrity_check` succeeds and `PRAGMA foreign_key_check` is empty.
- The reservation table SQL, all twelve columns, and both expected indexes
exactly match the schema-3 contract; a missing, extra, or substituted object
is an incomplete-schema refusal.
- The recorded schema version is 2, the table is present, and every
reservation-state count is zero. Any `starting`, `held`, `succeeded`,
`failed`, or otherwise unrecognized row refuses recovery; held rows are
never cleared or reclassified.
- Project identity, local-state path, and database provenance bind to the
requested installation. A copied, forged, newer-than-supported, or
cross-project database refuses recovery.
- The plan identifies the external backup destination and the source files to
fingerprint, but does not copy them. A later backup phase must produce a
consistent snapshot and immutable hash manifest before any future mutation.
Backup success is necessary but never itself an authorization to apply
recovery.
Writer isolation is a separate, independently verified prerequisite. The
future recovery implementation must verify a reservation-aware installed
engine and refuse while any alpha.2/canonical version-2 CLI, MCP, Console, or
arbiter-owned service can write this state root. A caller-supplied boolean or
free-text claim that executors are stopped is insufficient. Runtime/version
activation remains a separately owned and reviewed operation.
The required attestation is a Release-owned, installation-bound recovery fence,
not a process-list observation. Its verifier must bind the project and state
root, the approved reservation-aware runtime digest, retired writer digests,
the active launcher target, and an exclusive fence token. The fence begins
before plan-hash recheck, remains held through online backup and the apply
transaction, and remains enforced afterwards so an old writer cannot restart
and relabel the database. `BEGIN IMMEDIATE` protects the short database
transaction but is not a substitute for that lifetime fence. The apply phase
recomputes the source `state.db`/WAL/SHM hashes while the fence is held and
refuses if they differ from the plan. It then verifies a fresh immutable
snapshot rather than trusting volatile live file copies.
No such verifier or enforceable lifetime fence exists in the current alpha.2
installation. A state-root lock that the version-2 writers do not honor, a
stopped PID, or an unsigned attestation is not sufficient. Consequently, live
backup and live apply are deliberately unsupported until Release supplies and
independently validates this boundary.
### Proposed future interface (not implemented or invoked)
The supported path should be two explicit commands implemented only after
review:
```text
torch control-plane reservation-recovery plan --area project-kernel --target-schema 3 --json
torch control-plane reservation-recovery backup --plan <plan-digest> \
--writer-attestation <trusted-fence> --destination <external-path> --yes --json
torch control-plane reservation-recovery apply --plan <plan-digest> \
--backup-manifest <backup-digest> --writer-attestation <trusted-fence> --yes --json
```
`plan` is read-only. `backup` is a separate, explicit mutation: while the
trusted fence is held it must use Node's `node:sqlite` online `backup()` API to
write an external snapshot, then create an immutable manifest over the backup
and the fenced source inventory. Independent file copies of live
`state.db`/WAL/SHM are prohibited. `apply` must be unavailable until the plan,
immutable backup manifest, and a verifier-backed writer attestation all match
current state. In one immediate write transaction it must repeat every
validation, record an audit event, and only then advance the version label from
2 to 3. It must never create a missing reservation table, drop/rebuild an
index, delete a row, clear a lease, or accept a different schema shape as a
recovery shortcut. A changed hash, concurrent writer, or transaction contention
aborts without a partial relabel.
### Required deterministic scenarios before any implementation
- Empty, exact twelve-column version-2 reservation state plus a trusted
reservation-aware writer produces a plan, backup manifest, and one atomic
relabel only after a separately reviewed apply.
- A held row, active row, unknown state, nonzero data count, forged table SQL,
missing/altered index, newer schema, or version-3 state missing its table
refuses with no database change.
- A v2 writer attempt racing the plan/apply boundary invalidates the writer
attestation or transaction precondition; recovery does not relabel and no
duplicate runtime reservation is created.
- A forged caller evidence record cannot stand in for trusted writer isolation
or a verified backup manifest.
- Candidate-source recovery tooling operating under an isolated fixture state
root yields scoped evidence only; a normal candidate CLI pointed at the real
manifest remains blocked until a separately reviewed fence and live recovery
exist.
These scenarios are additive future QA work under the existing strict consent;
they do not relax ADR-024's fail-closed guard or change existing assertions.
## Consequences
These decisions keep the first usable release local-first and provider-
independent. They also make current boundaries explicit: repository moves,
multi-machine control, non-Linux persistent launchers, published update
channels, and measurement/adoption of live multi-level organization pilots
remain unavailable or owner-gated rather than silently approximated.
## ADR-026: Proposed candidate-source qualification contexts and receipt authority
**Status: proposed, nonnormative design contribution pending Work, Release, QA,
and owner review. This ADR makes no implementation, policy, gate, schema, or
receipt-validity change.**
### Context
Candidate checks must be able to qualify a clean, unlanded source commit
without initializing a live control plane. A single engine digest is not enough
to establish what was checked, whether it may write a receipt, or whether the
result describes candidate source versus an installed operational runtime.
Conversely, the schema-2-to-schema-3 refusal protects live registered state and
must not be bypassed by fixtures, alternate roots, or a source runner.
### Proposed direction
A future versioned result would bind three independent axes. **Engine
provenance (E)** is the exact engine source commit, module path,
dependency-resolution and source-tree digests, runtime/build bytes, and
interface version. **Subject scope (S)** is a tagged union:
`candidate-source` contains an authenticator-derived clean managed product
commit and root, while `operational-runtime` has its own authenticated
runtime/install identity and never uses a candidate-commit placeholder. Each
has immutable `subjectProductStorageSchemaDigest` (or explicit null) as subject
metadata.
**Receipt authority (A)** is the adapter identity/digest/interface, declared
compatible registered receipt schemas, `registeredReceiptSchemaVersion`, and
`observedControlPlaneSchemaVersion`. E is independently recorded and is not
presumed to equal S's product commit. The exact tuple would also include
protocol version, registered project/install identity, frozen definition and input snapshot digests,
guard/resource binding, durable attempt identity, sealed artifact-manifest
digest, and actual terminal outcome. Product-storage schema metadata is not a
registered receipt/control-plane schema change.
The proposed `CandidateAttemptStore/v1` is registered control-plane receipt
storage, not product storage. `AttemptRecord` would bind `id`, protocol and
tuple digests, `issued|executing|sealed|finalized` state, authenticated project
and area, E/S/A sets, frozen definition/input digests, guard and lease bindings,
artifact-manifest digest, observed terminal, process fence, and timestamps.
`ResultRecord` would bind `attemptId`, `PASS|FAIL|INCOMPLETE`, attributable
outcome proof, seal digest, and optional `receiptId`; `UNKNOWN` has no
`ResultRecord` and remains nonfinalized guarded evidence. A separately reviewed
and owner-authorized Kernel provisioner—not a source check, candidate engine,
or adapter—would have to create the store under a qualified registered receipt
schema. Until then, missing or incompatible storage refuses; it is not silently
created, migrated, or activated. `ReceiptAdapter/v1` opens an already-compatible
registered schema and performs DML only.
The Kernel would derive a native candidate context from the registered
manifest/root, registered area, and exact clean managed candidate, rather than
accepting caller paths, manifests, database handles, receipt adapters, commits,
or an initial expected commit as authority. The proposed candidate engine would
not initialize `ControlPlane`. A separately versioned registered adapter would
validate the tuple and compatible schema before performing receipt DML; the
adapter would not perform DDL, migration, schema relabel, or root redirection.
A future product-schema-3 subject can be considered with genuinely pure,
schema-agnostic engine bytes and an authenticated schema-2-compatible adapter
only when the reviewed tuple explicitly supports it. The observed Project
Kernel reservation candidate `be1ae951` actually initializes `ControlPlane`,
requires schema 3, and must continue to refuse against registered schema 2; it
does not imply every candidate CLI is schema-3-bound. This is tuple-specific:
valid schema-2 legacy source receipts remain valid under their recorded
compatible adapter contract and are not retroactively invalidated or promoted.
Fixture execution would use a separately authenticated fixture identity,
manifest/root, and XDG state. It may return a permanently nonpromotable
`SourceAttestation/v1`, never a registered receipt. It cannot be copied or
relabeled into registered receipt storage, and Integration, Backlog, and
Delivery would not consume it as a gate result.
That fixture nonpromotion does not make every source subject unusable. A future
explicitly reviewed candidate-source result may satisfy a prelanding integration
requirement for its exact unlanded subject; requiring an independently
authenticated post-landing operational subject for that same prelanding
condition would be circular. Operational gates still require their own
independently authenticated operational-runtime subject.
The proposed worker transport is an adapter-created opaque fresh single-use
attempt with a dedicated artifact directory. The worker would receive only a
verified immutable snapshot, frozen definition, bounded arguments, an explicit
environment allowlist, and per-attempt `HOME`, `TMPDIR`, and XDG homes; it
would receive neither registered root/manifest/database locations nor provider
credential variables. It would receive no registered root, manifest, database,
adapter identity, writable receipt path, or migration capability. A process
fence would record direct-child and process-group observation; cancellation
alone is `UNKNOWN`. The adapter-created attempt directory would yield only a
write-once `ArtifactManifestV1`: canonical sorted relative paths, SHA-256, and
byte lengths for regular files. Links, traversal, special files, overwrite, or
post-seal digest drift would refuse.
`FinalizeV1` would require exact tuple, active guard/lease bindings,
process-fence evidence, and sealed `ArtifactManifestV1`. One `BEGIN IMMEDIATE`
compare-and-swap transaction would finalize only the matching sealed open
attempt, insert `ResultRecord`, and insert a PASS receipt in that same
transaction only for PASS. Duplicate, wrong-tuple, inactive-binding, nonsealed,
or finalized attempts fail closed; no durable finalization exists without its
PASS receipt or non-PASS result record.
`PASS`, `FAIL`, and proven `INCOMPLETE` remain distinct terminal evidence;
non-PASS is durable observable evidence but cannot pass a gate. Error, signal,
timeout, ENOBUFS, malformed transport, drift, and zero-exit mutation are
INCOMPLETE only when proven. Unproven interruption, cancellation, ambiguous
commit, or uncertain persistence is `UNKNOWN`: no terminal ResultRecord, no
receipt, no replay/retry/release, and retained evidence, guards, leases, and
resources for exact-identity reconciliation. A proven FAIL or INCOMPLETE with
durable attribution and no live descendants may take only existing-policy scoped
cleanup, separately recorded from receipt eligibility; it never becomes PASS.
These withheld capabilities are an authority boundary, not an OS filesystem
sandbox. They stop the proposed interfaces from granting registered-root or
receipt-writing authority, but do not independently contain same-user code that
can discover ambient host state.
### Unresolved decisions
- The initial CLI subject rule is constrained: derive registered area and clean
managed subject internally; accept no caller selector or initial
expected-commit. A future reviewed expected-commit value could only reject a
mismatch against that derived subject. Release still must decide command
naming, presentation/refusal surface, and the primary `src/cli.mjs` boundary.
- QA must approve strict candidate, native, fixture-nonpromotion,
schema-incompatibility, single-use, replay, no-DDL, terminal-outcome, and
unknown-interruption scenarios before implementation.
- Durable store choice, guard/fence ownership, process isolation, environment
exceptions, artifact sealing/transport, credential treatment,
cancellation/recovery behavior, replay implementation, and adapter support
for historical receipt layouts remain unselected.
### Consequences
This proposal preserves ADR-024's fail-closed schema compatibility and
ADR-025's recovery boundaries. It does not authorize live recovery, a schema
change, an installation change, a provider start, automatic wake activation, or
an integration decision.