Source live CLI v1

September 20, 2026 · View on GitHub

Audience: private host operators and reviewers of the checked source execution route.

Status: private host implementation with local recorded-transport integration tests. This CLI is a host adapter for the existing checked source driver and journal. It does not add a second replay engine, provider fallback, candidate publication path, or model-created authority.

Exact operator route

Only the unpublished semaprax-full binary admits:

semaprax-full source-live run CONFIG CHECKPOINT --opencode ABS --scratch EMPTY_ABS
semaprax-full source-live resume CONFIG CHECKPOINT --opencode ABS --scratch EMPTY_ABS
semaprax-full source-live migrate OLD_CONFIG OLD_CHECKPOINT NEW_CONFIG NEW_CHECKPOINT FUNCTION STEPS --opencode ABS --scratch EMPTY_ABS
semaprax-full source-live offline-repair
semaprax-full source-live repair run REPAIR_CONFIG REPAIR_CHECKPOINT
semaprax-full source-live repair resume REPAIR_CONFIG REPAIR_CHECKPOINT
semaprax-full source-live repair run REPAIR_CONFIG REPAIR_CHECKPOINT --opencode ABS --scratch EMPTY_ABS
semaprax-full source-live repair resume REPAIR_CONFIG REPAIR_CHECKPOINT --opencode ABS --scratch EMPTY_ABS

All operands are absolute except the stable migration function identity and positive checked-evaluator step limit. run requires a new, private checkpoint directory. resume requires its existing latest journal. migrate accepts a committed unpriced v2 Suspend or a committed priced v4 Suspend from the predecessor directory, and writes a fresh or same-claim destination journal. The private CLI performs one predecessor-to-destination handoff; it does not offer a general migration-chain command. A v3 predecessor is explicitly refused by this CLI version; the checked embedding migration API has a separate A→B→C gate. The executable is the one explicitly chosen OpenCode binary, and every process attempt uses the fixed opencode/muse-spark-1.3-contributor-free profile without a paid fallback. Scratch must be a new empty absolute directory for each CLI invocation.

offline-repair is a separate fixed, credential-free private demonstration. It accepts no operands and authenticates only the bundled examples/offline-repair-project Project. It uses the checked Direct Runtime v2 source loop with two scripted streaming attempts: the first creates a malformed ephemeral candidate and the second must carry the checked diagnostic feedback before it can create the bounded fixture.repair.value replacement preview. Its single JSON report contains the candidate digest, source review, semantic delta, impact summary, model/effect counters and the in-memory source journal. The command neither writes source nor persists a checkpoint, publishes a candidate, selects a network provider, accepts a target/path/model operand, or claims physical recovery. It is local demonstration evidence for the checked repair path, not a general offline repair interface.

repair is the durable, host-selected candidate-preview route for issue #116. Its canonical JSON configuration selects the retained Project, source Agent, target declaration, bounded typed effect contract and task; it never supplies a provider endpoint, credential, source-edit path, test command, publication grant, or Git authority. Version 1 (semaprax.source-live-cli.repair-config.v1) also contains exactly two bounded scripted fixture documents and accepts no OpenCode operands. It exists as the credential-free test seam: the second fixture turn can require the actual first rejection's checked effect-feedback bytes, proving that the correction did not proceed blind. Version 2 (semaprax.source-live-cli.repair-config.v2) has the same fields except turns, and requires the explicit --opencode ABS --scratch EMPTY_ABS operands. ABS names the operator-selected executable; EMPTY_ABS is a host-selected, non-symlink scratch directory that is empty or contains only the exact deny-all policy and private session directory left by an interrupted run-before-export boundary. The configured executable is retained as an exact bounded byte snapshot, copied to a create-new staged file for each attempt, and re-authenticated before spawn. Host construction removes a crash-left staged file only when its held inode and bytes match that snapshot, then clears the exact owned policy/private state; a completed dispatch removes all of it eagerly and refuses while preserving any foreign entry the provider leaves. This is binding inside the documented host boundary, not isolation from a hostile concurrent process running as the same operating-system principal. The only v2 provider profile is the fixed free opencode/muse-spark-1.3-contributor-free profile. There is no fallback model and no provider selection in source or configuration. The selected source Agent deployment must already admit that exact provider/model row; a V2 host binding against a source deployment that only admits another model fails before checkpoint or provider work.

The ordinary V2 CLI still has no candidate-test authority. An embedding host may instead call the public source_live_cli::run_repair_with_candidate_test entry with one opaque candidate-test capability together with a bounded observer. The observer receives the immutable exact ProjectCandidate (including its retained candidate source material) together with the candidate revision, base Project revision, source revision, and host-selected capability identity; it receives no candidate mutator, command, environment, process handle, publication grant, or Git authority. It returns one canonical semaprax.source-live-cli.candidate-test-observation.v1 data document with a bounded passed, failed, or refused outcome. The host rejects malformed, oversized, foreign-candidate, stale-base, stale-source, or wrong-capability documents. There is deliberately no JSON operand that selects a test runner.

When such an embedding observes a candidate, its canonical outcome is reduced to a deterministic typed i64 feedback code bound to the entire observation. That result is settled through the existing typed-effect/journal boundary before any later proposal request, so a failed candidate test becomes actual bounded feedback rather than a fixture-side annotation. The capability identity is also bound into the V2 model/journal binding: resuming with a different selected capability refuses before provider or test-observer dispatch. A terminal replay dispatches neither and does not fabricate a fresh observation. refused is an honest host observation, not a pass or an authorization to retry outside the checked loop. The callback returns fixed-size bounded observation storage; it cannot hand validation an unbounded allocation.

V2 interprets deadline_millis in the restart-stable Unix-epoch-millisecond clock domain. The process timeout is the smaller of 30 seconds and the time remaining when the host is constructed, while the runtime checks the same absolute deadline around every attempt and settlement. V1 keeps its fixed-zero fixture clock so its committed credential-free examples remain deterministic.

V2 adapts that same bounded ProcessOpenCodeRunner through the Provider Adapter SDK boundary used by Direct Runtime v2. The OpenCode process receives the runtime's compiler-derived canonical source-proposal request and schema, runs with the deny-all tool policy, and must pass the existing run/export receipt validation before its raw text reaches the compiler proposal decoder. A fresh adapter instance is created only after the checked journal acknowledges an attempt intent. Both V1 and V2 resume first rebind and recover their journal; terminal recovery occurs before V1 fixture-only target lookup or diagnostic derivation, and before V2 host, candidate, or typed-effect construction. A terminal checkpoint dispatches neither provider nor typed effect again, a changed source or executable/scratch host binding refuses, and an unresolved acknowledged delivery remains uncertain rather than being sent again. These facts make interruption/resume observable in the journal, not an exactly-once claim about an external service. The V1 scripted fixture is a deterministic terminal-receipt seam only; this document does not claim nonterminal V1 provider replay or exactly-once fixture work after a crash. V2 recovery retains the journal's explicit uncertainty/idempotency outcome for an acknowledged but unsettled provider attempt.

Candidate-test observation has the same fail-closed crash boundary. The host observer runs only after a durable effect intent exists and before its EffectObserved settlement. A returned observer error is settled as a refused handler result; if the process stops before either settlement, the journal remains uncertain. Resume does not redispatch the observer in either case. The observer's bounded detail field is included in the fresh public CLI receipt; embedding hosts must therefore return only review-safe, non-secret detail.

The repair receipt is always source-immutable and publication-authority-free. V1 retains the frozen semaprax.source-live-cli.repair-receipt.v1 projection: candidate digest, source review, semantic delta, impact summary, rejected- candidate count, checkpoint generation and dispatch counters. V2 emits the additive semaprax.source-live-cli.repair-receipt.v2 projection. On the invocation that produces a candidate it additionally carries journal_binding (invocation, chain, generation) and analysis.coverage / analysis.blind_spots for those review artifacts. It also prints selected_profile with the exact config schema, provider/model, adapter identity/version and provider profile, plus checked_prerequisites with the ProgramRoot, checked source revision, compiler-derived proposal-schema digest and deployment binding. These are the same identities used to bind the durable invocation, not independently authored receipt labels; terminal replay must reproduce them exactly. They are review evidence only and mint no provider, filesystem, test or publication authority. model_attempts is the bounded, replayable per-attempt projection of that same validated, binding-checked retained source journal: it reports intent and settlement stages, request / response digests and byte counts, decode/refusal outcome, and provider-reported usage only when the adapter actually recorded it. Missing usage remains null; the receipt does not manufacture zero tokens, cost, timing or delivery. Terminal replay recomputes the identical projection without starting the provider. The hash chain supplies integrity and causal shape, not freshness or external authentication; a storage controller can replay an older same-binding journal, so consumers must not treat this receipt as proof that it is the newest state. V2's candidate_test_execution.status: "not_run" is deliberate when the ordinary CLI route supplies no capability. A capability-bearing embedding can instead report the bounded canonical observation and its feedback code in the fresh receipt. A terminal replay carries the previously settled status and typed feedback with replayed: true; the full host observation document is not retained in the current journal, and no new observation is fabricated. Neither receipt is a cost proof, provider-delivery proof, source/Git mutation, or approval to publish the candidate.

Priced migration requires both predecessor and destination config v2 pricing with exactly matching work unit, currency, minor-unit exponent and integer rate. The destination money ceiling may narrow but cannot fall below carried reservations. The migrated v4 journal retains unknown exposure, observed charges, overage and the global money ordinal; it never reconstructs them from the CLI receipt. Unpriced-to-priced and priced-to-unpriced conversion are refused rather than silently shedding exposure or treating it as zero-priced.

A crash after fresh directory creation but before its first journal ACK can leave an empty directory. Both run (existing directory) and resume (no latest journal) refuse it. An operator may use a different new directory only after independently establishing that no journal or provider work began; the CLI does not infer that fact from an empty directory.

CONFIG is one canonical JSON object, at most 8192 bytes. Version 1 has exactly these keys: schema (semaprax.source-live-cli.config.v1), manifest, source_path, agent_id, step_id, task_path, task_budget, read_path, deadline_millis, ceiling, reservation_units, max_iterations, max_stages, max_steps_per_stage, max_total_steps, and response_limit. Unknown, duplicate, alternate-encoding, and negative or over-capacity fields are rejected before journal or provider work. JSON must use exact compact sorted-key serialization; a single final line feed is accepted. manifest, task_path, and read_path are absolute host selections; source_path is a relative Project .spx selector without ... The task and observation files are bounded to 65,536 bytes each. The observation file is an explicit fixed read snapshot, returned by the one injected AgentReadOperation; it is not a shell, test runner, candidate editor, or semantic validation tool.

Version 2 is an additive priced route. It uses schema semaprax.source-live-cli.config.v2, retains every v1 key, and requires one additional exact pricing object with currency (three uppercase ASCII letters), minor_unit_exponent (0..=9), positive integer price_per_work_unit_minor, and nonnegative integer money_ceiling_minor. These are an operator quote in bound integer minor units for the fixed source work unit, never a provider price lookup, currency conversion, float-cost parser, or invoice. A v1 document with pricing, or a v2 document with a missing, extra, malformed, zero-price, negative, or noncanonical pricing field, is refused before checkpoint or provider activity; it cannot downgrade to the unpriced route.

Version 3 is an additive priced-I/O route. It uses schema semaprax.source-live-cli.config.v3, retains every v2 key and requires one additional exact io_limits object with nonnegative integer max_request_bytes, max_total_request_bytes, and max_total_response_bytes. The per-attempt request field is capped at 65,536 bytes and may be zero; the cumulative fields are u64 ceilings. V3 does not widen v1/v2 keys, and a missing, extra, malformed, negative or noncanonical I/O field is refused before a checkpoint or provider call. Its exact reservation, recovery and migration semantics are in Source Live I/O v5.

The host authenticates the retained Project, selects and checks its Agent role closure, derives its actual ProgramRoot, and derives the proposal grammar from the compiled source. The read snapshot bytes and fixed model are hashed into the deployment binding; changing the snapshot on resume changes the invocation identity and fails journal recovery. The task bytes, budget, source revision, ProgramRoot, schema, fixed charge, bounds, clock and deadline are bound by the existing SourceInvocationBinding. Neither submitted model text nor the checkpoint document supplies those host facts. The unit is fixed_model_attempt_units.v1, charged once per acknowledged attempt intent; provider-reported counters remain optional observations, never billing proof. For v2, the paired price reservation is also acknowledged before dispatch. Current OpenCode cost JSON has no bound currency/minor-unit representation, so the host records explicit Unknown charge evidence instead of converting a float or manufacturing zero cost.

Latest store, clock and migration claim

The Unix host holds the checkpoint directory by file descriptor and a nonblocking exclusive advisory lock. It refuses symlinked/nonphysical path components and non-private directories. A read preflights regular-file type and byte limit on the opened descriptor; a FIFO or replaced symlink cannot turn the bounded read into an unbounded wait. Each canonical journal generation is written to a new file, synced, renamed over the latest document through the held directory, then the directory is synced before the store ACK. A failed or ambiguous commit poisons the writer. Recovery loads the latest authoritative document under the same exclusive lock, validates the exact independently derived source binding, and restores the existing cumulative ledger. A store replacement by its owner or a valid rollback of the latest document cannot be authenticated by hashes alone.

The CLI uses Unix epoch milliseconds as one restart-stable clock domain, with origin zero for a fresh v2 run and an absolute deadline_millis supplied in CONFIG. A v3 migration's origin is the authenticated predecessor latest checkpoint's last checked clock floor; repeating the same handoff derives the same origin from that predecessor terminal. Recovery does not reset that deadline. A regressed or expired continuation refuses; an already committed terminal is a read-only receipt and can be retrieved after expiry with zero model/effect dispatches. The OpenCode process timeout is no greater than 30 seconds or the invocation time remaining when this CLI traversal begins. The source clock is checked again before each attempt and after each settlement; a later child may cross the absolute deadline, in which case its result is withheld and its committed reservation remains charged.

Before a v2→v3 migration can evaluate or run the destination, the predecessor store persists a single handoff claim under its held lock. The claim binds the checked handoff digest, destination directory and new invocation. The same destination/handoff may reopen its latest journal; another destination is refused. A crash after the claim but before destination settlement can leave the handoff unavailable pending explicit operator reconciliation. This is a cooperating-CLI single-destination rule, not a distributed transaction or proof against hostile owner rollback. The destination uses prepare_source_live_migration and the same source journal/driver; checked migration fuel is acknowledged before the pure evaluator, and the migrated State is schema-checked before first Observe. No Initialize is repeated.

Output and scope

A completed unpriced run returns the v1 bounded JSON receipt with terminal status, invocation, generation, chain, acknowledged model units and stage fuel, and this traversal's model/effect dispatch counts. Every receipt version also carries iterative_evidence: the compiled reducer's own semaprax.agent-iterative-evidence.v2 document (policy, invocation digest, status, iteration/effect counts, per-stage role/function/outcome/step rows, authorization bindings, and a terminal-value digest) when this traversal dispatched fresh work, or null on a pure terminal-checkpoint replay that redispatched nothing. It does not include raw model text, credentials, provider stderr, or a publication grant. A failure reports its selected status and last acknowledged counters; the journal remains the reviewable causal artifact. The CLI never rewrites authoritative .spx source or Git state. The separate source-live repair run|resume route described above now provides the host-selected failed-check observation seam, semantic candidate preview (source diff, semantic impact and blind spots), checked repair feedback, and durable journal binding for issue #116. It still provides no approval-bound publication. The general run/resume/migrate route remains a domain-agnostic Agent-lifecycle host adapter and is not itself a repair command. No operator-approved live repair run, hosted CI, durable power-loss, or exactly-once physical delivery claim follows from the local injected tests.

A completed priced run returns semaprax.source-live-cli.receipt.v2 with the same top-level status, invocation, generation, chain, committed_model_units, committed_stage_fuel, model_dispatches, and effect_dispatches, plus a money object containing exactly currency, minor_unit_exponent, reserved_minor, observed_charge_minor, unknown_charge_reservation_minor, observed_over_reservation_minor, and remaining_admission_minor. These values are replay-derived bound reservations and provider observations. They are neither a reconciled invoice nor a refund or payment authorization. A priced failure reports the same acknowledged monetary counters in its error detail. Terminal recovery returns the bound receipt without another provider call.

The focused source_live_cli tests exercise local recorded execution. The retained-Project fixture sends a recorded OpenCode run/export through the actual source adapter, then checks terminal resume and changed read, task, or policy refusal with zero further calls. A second fixture executes checked Suspend, pure StateB migration, destination completion, terminal recovery, and competing-destination claim refusal. Store tests cover exclusive locks, held-directory rename, poisoned writes, symlink/FIFO input refusal, and an empty fresh directory that neither mode silently resumes. These are local fixture results, not a live provider or power-loss test. The repair-specific local fixture additionally injects a bounded candidate-test observer: it proves failed-test feedback reaches a later recorded provider request, malformed/oversized/withheld observation data is refused, terminal resume does not redispatch either provider or observer, and capability-binding drift fails closed. Its V2 hostile-recovery corpus additionally covers malformed settled response hex, unknown settled-response fields, a settled-response sequence mismatch, an unknown envelope field, stale checked source, and changed task binding. The V1 terminal-replay corpus separately replaces the configured candidate target with a valid-but-missing declaration and alters the fixture diagnostic shape; recovery still returns its retained receipt because neither fixture-only action is constructed. These mutations have exact local stable refusal or replay assertions before a provider, effect, or candidate-test handler can be constructed. The implementation also maps its other closed journal recovery classes (clock, capacity, uncertain delivery, or unavailable store) to diagnostics, but this corpus does not claim a hostile fixture for each of them. The clean terminal V2 replay remains an exact positive control, so rejection alone cannot satisfy the corpus. These labels are diagnostic-only and do not authenticate freshness, grant provider authority, or approve a candidate. This is local injected-host evidence only; it is neither a real test-command execution claim nor the operator-approved live-provider smoke required by issue #116.