# Comet Runtime SDK The Comet Runtime SDK gives host Agent platforms a resumable workflow engine for Skills. The host remains responsible for model requests, Skill/MCP/tool calls, Hooks, Rules, sandboxing, and user interaction. The SDK pins workflow definitions, creates claimable Actions, records Outcomes and decisions, and restores a Run after a process restart. It does not replace the platform's native Agent loop or duplicate its model integration. Connect the capabilities the platform already provides through an explicit executor. Start with [SDK getting started](./sdk-getting-started.md). Use this reference for the full protocol, extension points, and application limits. Simple hosts should prefer `createRuntimeExecutor` and `runUntilBlocked`. ## Install and import ```bash npm install @rpamis/comet ``` Runtime is a dedicated ESM export with bundled TypeScript declarations: ```ts import { approval, tool, createFileRuntimeStore, createRuntime, skill, } from '@rpamis/comet/runtime'; ``` Existing package deep paths remain available. New SDK consumers should use `@rpamis/comet/runtime`, not source paths under `domains/engine`. ## Runnable example [runtime-sdk-example.mjs](../../scripts/lib/runtime-sdk-example.mjs) demonstrates collect → persistent approval → report writing with a local host adapter. It needs no Git repository, Native change, Comet initialization, model provider, or network access: ```bash pnpm build node scripts/lib/runtime-sdk-example.mjs --root-dir ./.tmp/runtime-example node scripts/lib/runtime-sdk-example.mjs --root-dir ./.tmp/runtime-example --approve ``` The first process collects the report and saves a pending Wait. The second process reopens the same Run. The report is written only after the explicit `--approve` decision. The output is `./.tmp/runtime-example/published/report.md`. ## Define a workflow ```ts const reportWorkflow = { id: 'report', version: '1', entry: 'collect', steps: { collect: skill({ ref: 'research.collect' }), approve: approval({ proposalFrom: 'collect' }), publish: tool({ ref: 'reports.write' }), }, transitions: [ { from: 'collect', to: 'approve' }, { from: 'approve', to: 'publish', on: 'approved' }, ], }; ``` `skill`, `tool`, `approval`, `childWorkflow`, and `evidence` are convenience builders; steps can also be declared by their `type`. A workflow contains steps, input bindings, and transitions—not a model client or platform runtime. It supports branches, bounded activations, parallel joins, child workflows, synchronous JSON Schema validation, and versioned business validators. To update business state after accepted events, define a `stateSchema` and a versioned `transitionHandler`, then register the corresponding `transitionHandlers` with `createRuntime`. If JSON Schema cannot express domain state constraints, the Workflow can also declare a versioned `stateValidator`; register a synchronous, side-effect-free `RuntimeStateValidator`. It runs at start, on recovery reads, and before every transition is committed. Put an invariant initial state in the Workflow's `initialState`; when multiple changes share one Workflow definition, pass `initialState` to `runtime.start` instead. Providing both is rejected, as is starting a workflow with `stateSchema` but no initial state. The handler receives an accepted Action Outcome, user decision, or evidence receipt and returns the new `state` and `next` steps. The SDK validates the state and declared transition edges, then commits them with the event result in one Run revision. Missing pinned handler or validator versions, or invalid state, prevent advancement. The Run saves an initial-state hash so an idempotent start cannot silently replace it. A string in `next` activates a declared step once; `{ stepId, input }` can activate the same step multiple times with different JSON inputs. Each activation creates a distinct Action. Its `input.activation` is persisted and included in the Action input hash. Duplicate activations and undeclared transitions are rejected. An explicit empty array adds no steps for that event, for example while previously dispatched parallel Actions remain outstanding. Activation input is also persisted and must not contain credentials. Native Supervisor uses this mechanism for the normal two-Child dependency path, including independent parent verification, delivery to the target branch, Archive, and temporary worktree cleanup. Parallel Child integration and integration checks advance serially in Run order; public `native next` returns all `pendingActions`. A failed integration check can become a host repair Action bound to the original check and commit, followed by a new check of the repaired commit. Cleanup rejects dirty worktrees or unmerged branches; if the host stops during cleanup, it can reconcile and complete the original Action in a new process. Drift before delivery or after partial cleanup is rejected; other failures, unknown results, and concurrent drift within a Git operation have not been fully accepted, so this does not establish complete Supervisor acceptance. Native's application exposes interrupted recovery as `comet native archive --recover`. It handles only the unique `unknown` Supervisor delivery, Archive, or cleanup Action, and requires the host to confirm that the original execution has stopped. Delivery recovery commits the original Action's result only when the target branch still points exactly to the verified integration commit. It rejects an undelivered or drifted target without repeating the fast-forward. Cleanup recovery checks the worktrees and branches before safely completing remaining work. Ordinary `archive` does not automatically redispatch an action whose result is unknown. After the user accepts verification, one `comet native archive ` completes the currently executable preparation, application, and finalization steps in order. Approved Supervisor delivery and cleanup follow their existing contracts as well. Each Action still persists its own checkpoint, revalidates evidence, and commits against the current revision. The command stops at a user decision, evidence wait, extension step, failure, or running/unknown Action and returns the current result. Resume from the original checkpoint after interruption; repeating a completed Archive is safe. `--dry-run` only previews whether the current step can execute and does not advance Actions. New SDK changes keep a `run_checkpoint` in `comet-state.yaml` at their original change path; historical Classic `.comet.yaml` files remain supported in place. Transfer the actual state file, authoritative documents, referenced artifacts, modified code, and project configuration together to a new checkout with matching configuration. An ordinary Native or Classic change can then restore the same Run even when local Run records are missing, preserving its saved phase, Actions, and approvals instead of restarting at Shape or Open. Recovery rejects mismatched checkpoint hashes, artifacts, or workspace bindings. An external Action claimed without a known result stays `unknown`: stop the original executor, inspect the actual side effects, and use the reconciliation protocol before continuing. It is not automatically replayed. Classic keeps historical `.comet.yaml` files in place for reads, writes, recovery, and archive. Explicit compat initialization still uses `.comet.yaml`. If both state filenames exist for one change, including a dangling symlink, Runtime rejects the conflict rather than choosing one or overwriting state. Preserve both and check their origins before resolving the conflict. Native and Classic retain separate directories and state schemas. A Supervisor Child's separate worktree and uncommitted code are not included in the parent's state file. During the Child Builder stage, after confirming that the original executors have stopped, explicitly transfer them with `comet native transfer export --output --confirmed-stopped` and `comet native transfer import --input `. The bundle contains temporary Git branches and non-ignored worktree changes and must be kept private; ignored files and external dependencies require separate preparation. Copying only the parent's state file cannot take over an already started Child. Only old state files without a checkpoint require explicit user approval to rebuild from preserved documents at Shape or Open; old approvals and check results cannot be reused. See the [Native/Classic integration boundaries (Chinese)](./runtime-sdk-native-classic-integration.zh.md) for supported stages and rejection conditions. For artifacts not produced directly by an Action Outcome, use an `await_evidence` step with an evidence kind and versioned validator. Register it in `evidenceValidators`, then call `recordEvidence({ runId, evidenceId, kind, ref, contentHash, submissionId, expectedRevision })`. The validator receives an isolated copy of the current Run, so it can check ownership before checking reference scope and the current content hash. The SDK records the receipt and advances only if validation succeeds and the revision is unchanged. External files can change again after validation; later dependent actions must recheck them against the recorded hash. If the workflow declares an `on: invalidated` transition for the Wait, the host can call `invalidateEvidence` after a receipt is rejected. The SDK revalidates the original receipt, and only if it is now invalid records the reason, closes that Wait, and follows the declared transition without replaying earlier successful Actions. The corresponding CLI operation is `invalidate-evidence`. Without an invalidation transition, or while the receipt is still valid, the Run remains waiting. The JSON CLI cannot register custom handlers and validators for arbitrary workflows; its built-in Native and Classic applications register their own. For user-requested changes during a workflow, declare an externally activated execution step with `commands: { revise: 'revise.step' }`, then call `dispatchCommand({ runId, expectedRevision, commandId, name: 'revise', input })`. To constrain the current stage, input, or external artifacts, use `{ revise: { stepId: 'revise.step', validator: { id, version } } }` and register the matching `commandValidators` with `createRuntime`. The validator runs before old work is canceled. A rejection leaves the Run unchanged, and recovery requires the same version. In one CAS commit, the SDK cancels unclaimed Actions and unresolved Waits, saves the command receipt, and creates a new Action. The same `commandId` and input can be replayed idempotently. A claimed Action or one with an unknown result cannot be interrupted this way; the host must reconcile it first. The command itself does not rewrite business state or external files. A versioned transition handler updates the Run only after the executor completes the side effect and records an accepted Outcome. The JSON CLI's `dispatch-command` operation requires explicit `expectedRevision`, `commandId`, `name`, and `input`. New built-in Native and Classic changes use their respective SDK Workflow Applications by default; existing changes remain owned by the Runtime that created them. Native `spec remove`, `spec disassociate`, `next --revise-requirements`, and `archive` submit commands or advance existing Actions according to SDK ownership; `doctor ` diagnoses the same Run. `native check` and `spec sync` remain compat-only entry points: checks for SDK changes run through Run Actions, while changes to references or acceptance requirements return to Shape for revision and confirmation. On the SDK path, `archive --dry-run` read-only displays pending Run Actions; it is not the compat Runtime's preflight hash check. Each Run pins hashes for its root workflow and transitive child workflows. To resume, register the same workflow ids, versions, and contents. If a definition changes under the same version, Runtime rejects further mutations with `WORKFLOW_CHANGED` instead of interpreting old state with new logic. If a Runtime-managed `child_workflow` Action becomes `unknown` after recovery, call `next` to reconcile the existing child Run. Runtime checks pinned definitions, parent-child ownership, input, and claim identity before accepting a persisted completed, failed, or cancelled child result. An unfinished child remains pending completion. Restore both parent and child checkpoints; a missing or mismatched child Run is not recreated automatically. Hosts cannot supply child results through generic `retry` or `recordOutcome`. Unknown external Actions still require separate reconciliation. ## Execute Actions in the host ### Common integration path `createRuntimeExecutor({ id, capabilities?, handlers })` binds step `ref` values to named handlers. `defineRuntimeHandler({ type, parseInput, execute })` checks the step type, parses `{ input, outputs, activation? }`, and calls the business function. The return type of `parseInput` determines the execution function's input type. The execution function returns `{ status, output, artifacts?, summary?, event? }`. The helper does not infer types across the entire workflow; cross-step JSON still needs validation. After registration, call `runtime.runUntilBlocked({ runId, executorId, maxActions?, context? })` to receive `{ reason, run, actionsExecuted }`. It executes pending Actions in the current Run in persisted order without a handwritten drive loop. It does not choose executors, approve proposals, submit evidence, or retry unknown Actions automatically. `maxActions` defaults to 100 and limits execution attempts in this call, not the duration of an individual tool call. `context.signal` follows the existing execution protocol; it cannot guarantee termination of a host tool or undo its side effects. | reason | Host next step | | ------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- | | `completed` / `failed` / `cancelled` | Handle the terminal state and actual result | | `approval-required` | Read the current proposal from `run.waits`, obtain the user's decision, then explicitly call `resolveWait` | | `evidence-required` | Check and submit real evidence; do not fabricate receipts | | `execution-unknown` | Stop or contact the original executor, inspect external effects, then submit the original result or evidence of non-execution | | `action-in-flight` | Wait for the original executor; the host schedules child workflows using `run.children`, without implicitly executing child Runs recursively | | `executor-required` | The first Action's type, reference, or capabilities are unsupported; leave it unclaimed rather than skipping it | | `action-limit` | This call reached its limit; call again within the host's budget without repeating successful Actions | | `idle` | No work is executable and no recognized wait exists; inspect the Run and definition | Any existing branch with unknown execution, in-flight work, or pending approval stops the call instead of executing other parallel Actions. Once a child Run completes, advancing the parent again accepts its result. Rejected Outcomes, configuration errors, and concurrency conflicts still throw the existing `RuntimeProtocolError` with its `recovery` guidance; they are not converted to success. Input parsing runs after claiming and before the business function. A parsing failure does not execute the business function, but the existing protocol conservatively retains unknown execution ownership. ### Manual dispatch and result submission For Actions executed in another process or platform, continue using the low-level `claim` / `recordOutcome` protocol: ```ts const runtime = createRuntime({ store: createFileRuntimeStore({ rootDir: '.comet/runtime' }), workflows: [reportWorkflow], }); let run = await runtime.start({ runId: 'report-2026-09', workflow: { id: 'report', version: '1' }, input: { topic: 'runtime design' }, }); const action = run.actions.find((item) => item.status === 'pending')!; run = await runtime.claim({ runId: run.runId, actionId: action.id, attempt: action.attempt, inputHash: action.inputHash, executorId: 'my-agent-platform', }); // Call the platform's existing Skill/tool capability here. const claimedAction = run.actions.find((item) => item.id === action.id)!; const result = await invokePlatformSkill(claimedAction, run); run = await runtime.recordOutcome({ runId: run.runId, outcome: { actionId: action.id, attempt: action.attempt, inputHash: action.inputHash, claimToken: run.actions.find((item) => item.id === action.id)!.claim!.token, outcomeId: result.requestId, status: 'succeeded', output: result.output, }, }); ``` The Action id, attempt, input hash, and claim token bind the returned Outcome to the current execution. Replaying the same Outcome is safe; stale attempts, conflicting reuse of an outcome id, unclaimed results, and incomplete results are rejected. Alternatively, register a `RuntimeExecutor` and call `runtime.execute`. Each executor explicitly declares its id, capabilities, supported Actions, and execution function. `supports` runs before the claim is committed and should be a pure check. `execute` runs only after the claim is committed; if it throws or returns a result that cannot be committed, Runtime records an indeterminate Action and does not dispatch it again automatically. Runtime dispatches only through host-provided adapters; it does not discover or call platform tools itself. ## Waits, approval, and recovery Rebuilding a new Run from documents when an old state file has no `run_checkpoint` does not handle Archive, unresolved external execution, or an already started Supervisor. Recover the original Run records and reconcile the results instead. This restriction does not apply to restoring an ordinary SDK change from a valid checkpoint as described above. An `ask_user` step persists a Wait with a `proposalHash`. After showing the proposal to the user, submit their choice: ```ts run = await runtime.resolveWait({ runId: run.runId, waitId: wait.id, proposalHash: wait.proposalHash, decisionId: 'user-decision-001', choice: 'approved', }); ``` Use `reviseWait` to submit an edited proposal; a decision using the old hash is rejected. `inspect` revalidates the Run structure, Action ownership, receipt hashes, step sequences, and proposal hashes. When workflow definitions are registered, it also verifies the content of every pinned definition. A read-only `inspect` without definitions can show the state, but does not establish that the Run is ready to continue. If JSON Schema or a business validator rejects an Outcome, Runtime persists the original result, its identity, and the rejection reason in the same Run snapshot before returning `OUTPUT_INVALID` or `OUTCOME_REJECTED`. The Action stays claimed and is not dispatched again automatically. Replaying the same content with the same ID returns the original rejection; changing content under that ID returns `OUTCOME_CONFLICT`. After reconciling and correcting the result, the host must use a new `outcomeId` with the original attempt and claim token. An attempt with a recorded result cannot be retried as "not executed", even if it is subsequently marked unknown. If an Outcome is bound to an Action but a validator throws or workflow advancement fails, Runtime likewise persists the original Outcome before returning `OUTCOME_PROCESSING_ERROR`. The failed advancement does not leave partial outputs or new steps. The host should check the error and external side effects before submitting a corrected result under a new `outcomeId`. If an executor returns a value that cannot be serialized, Runtime cannot persist that value and marks the Action unknown for host reconciliation. `createFileRuntimeStore` keeps a fixed format marker and a content-validated current checkpoint for each Run. Writers compare revisions under a recoverable cross-process lock before atomic publication; revisions remain monotonic without retaining a complete history file for every update. Legacy numbered snapshots must pass continuity and content checks and have their old write path fenced before conversion to the current checkpoint. `createMemoryRuntimeStore` is useful for tests or short single-process runs. FileStore requires supported local filesystem atomic operations and is not a distributed lock for shared network filesystems. See [local Runtime storage](./runtime-local-storage.zh.md) for layout and recovery conditions. Runs persist their input, Action inputs, Outcome outputs, and proposal content. Do not put access tokens, API keys, or other credentials in these fields. Inject secrets at the execution boundary through the host's secret-management mechanism; `RuntimeInvocationContext` is not written to the Run snapshot. An external side effect and local state cannot be committed as one atomic transaction. `execute` commits the claim before calling the host. If the executor disconnects, throws, or the process stops, the Action remains indeterminate and Runtime does not dispatch it again automatically. The host must inspect the external system and use `retry` only with explicit evidence that the effect did not happen. Idempotency keys and reconciliation are contracts between the host and target system; local CAS does not provide exactly-once side effects. To resume, recreate Runtime with the same FileStore and workflow definitions, then call `inspect` or `next`. Continue without changing the pinned definition. If the definition is unavailable or changed, Runtime refuses execution; keep the old definition available or explicitly migrate the Run. ## Extension points - `RuntimeStore`: replace persistence by implementing `read` and revision-based `compareAndSwap`. - `RuntimeExecutor`: route Actions to the platform's existing Skills, MCP, CLI, or API capabilities. - `RuntimeValidator`: apply versioned business acceptance before an Outcome advances the workflow; an Agent reporting success is not the same as acceptance. A validator can be called again after a CAS conflict, so keep it side-effect-free or repeatable. - `RuntimeStateValidator`: check domain state at initialization, on recovery reads, and before transition commits; use synchronous, side-effect-free checks. - `WorkflowTransitionHandler`: compute business state and subsequent steps from an accepted event; keep it pure and do not execute tools or write the Run directly. - `RuntimeEvidenceValidator`: verify external evidence references and content hashes; concurrent retries may invoke it more than once, so do not rely on one-time side effects. - Workflow definitions: compose steps, transitions, joins, schemas, and child workflows without forking the scheduler. The Store, Executor, and Validator are explicit dependencies. The SDK does not expose an observer or hook that can arbitrarily mutate a Run. A host can log command calls for observability, but cannot bypass CAS or outcome validation to change authoritative state. ## JSON CLI For a platform Agent that can run commands but cannot import JavaScript: ```bash comet runtime dispatch --request ./start.json --workflow ./report.workflow.json --root-dir ./.comet/runtime --json ``` Example `start.json`: ```json { "operation": "start", "requestId": "host-request-001", "runId": "report-2026-09", "workflow": { "id": "report", "version": "1" }, "input": { "topic": "runtime design" } } ``` Each command processes one structured request and returns JSON containing `protocolVersion`, `requestId`, and either the Run or a machine-readable error. `inspect` can read a Run from a new process without a workflow file; provide the pinned definitions to verify their content or change state. If an external execution disconnects after claim, use `mark-unknown` to preserve the indeterminate fact. `retry` creates a new attempt only when given `reconciliation: { "resolution": "not-executed", "evidence": ... }`. Relative request, workflow, and root paths use the CLI invocation directory; `--project-root` is passed as explicit host context to executor-related interfaces. A cancel request `{"operation":"cancel","runId":"","expectedRevision":,"reason":""}` stops scheduling and cancels pending Actions, decisions and evidence waits. Existing code and external effects remain. Repeating cancellation does not increment the revision or resume progress; ordinary late Outcomes are still rejected. Native CLI cancellation responses retain the complete Run in `data` and add sibling `cancellation` and `continuation` fields, distinguishing completed cancellation from outstanding stop confirmation. Native reports `cancelling` while original execution stop or current selection release remains unconfirmed, and `cancelled` once both are complete. With no claimed work and no cleanup blocker, cancellation immediately releases the current selection and workspace, without requiring Shape, verification or Archive. The cancellation request or `comet native doctor --repair` stops registered Runtime checks only when their original process creation identity can be verified. Unknown or mismatched processes are never assumed stopped. Terminating a process group does not establish that detached background processes stopped. Every claimed task, including Runtime checks, still requires acknowledgement under its original claim that the task and all child tasks stopped before its workspace is released. SDK selection cleanup also uses the Native root-move mutation lock, retaining its existing fifteen-minute protection before explicit takeover of an unknown owner. Blocked cleanup stays `cancelling` and reports the owner, age, remaining protected time, and diagnosis/repair commands; read-only status/inspect does not wait on that lock. On Windows, an exited check supervisor alone is not sufficient to release the workspace or retry a check. Automatic recovery requires termination evidence bound to the original registered process instance. For a legacy or missing receipt, first confirm on the original machine that the execution and all descendants have stopped. Inspect `comet native doctor --json`, then submit genuine stop evidence with `comet native doctor --repair --confirmed --stopped-actions `. SDK entries retain the original `actionId`, `attempt`, `inputHash`, `claimToken`, and `evidence`. For Portable and Supervisor checks, copy the doctor-provided `checkStopTargets[].confirmation` entries into a JSON array, preserving `kind`, `operationId`, and `executionHash` and replace only the evidence placeholder with the actual stop facts. The hash binds the original execution snapshot; a changed execution requires a fresh diagnosis. Recovery retains the original snapshot and evidence, and does not replay a check or certify it passed. Unknown or live registered processes remain blocked; ordinary doctor repair does not invent a receipt. The original host for an external Builder, Verifier or custom Skill must stop the execution and all its child tasks, then submit actual stop evidence using the template at `status.cancellation.outstandingActions[].acknowledgementRequest`: ```json { "operation": "cancel", "runId": "", "expectedRevision": 12, "reason": "The task was cancelled and the original host has now stopped execution", "stoppedActions": [ { "actionId": "", "attempt": 1, "inputHash": "", "claimToken": "", "evidence": "" } ] } ``` `stoppedActions` acknowledges that execution stopped; it does not claim that effects never happened, results passed or files were cleaned up. The Runtime verifies the original claim, attempt, inputHash and CAS revision. Repeating the same evidence is idempotent; conflicting evidence and duplicate Actions are rejected. This statement cannot bypass a registered check whose process is live, unknown or whose record is invalid. Cancellation retains the original domain state, code, history and receipts: the Run's `cancelled` status and Native cancellation projection identify termination, while retained domain `state` describes the last committed phase before cancellation. Native application CLI `claim` and `record-outcome` requests return `data.schema: "comet.native.dispatch-result.v1"` by default. This keeps the Run identity, revision, complete `state`, current complete `action` (including input, claim, and outcome receipts), and `continuation`, without repeating other Action history. Claim results also include `outcomeRequest`; fill in the actual execution status, output, and unique outcome ID before submitting with the original identity. The template does not imply success. CLI consumers that require the previous complete Run should add `--details`. `inspect` always returns the complete Run, and the compact result's `inspection.request` can be submitted directly. The JavaScript SDK and programmatic `runtimeDispatchCommand` still return the complete Run in `response.data`. After the built-in Native CLI receives the current independent Verifier's final passing outcome, it executes the sole pending report-generation step and stops at the user's acceptance decision. It does not approve or archive automatically. Custom applications and the JavaScript SDK retain single-operation semantics. If report generation fails, the saved Verifier outcome remains successful and the response includes a `NATIVE_REPORT_PENDING` warning. Inspect the current state and resume the report; do not treat the accepted outcome as unsubmitted or repeat independent verification unnecessarily. Native and Classic commands also accept explicit `--json --compact`, for example `comet native status --json --compact` or `comet state next --json --compact`. Default `--json` fields are unchanged. Compaction is best-effort: when it does not reduce output bytes, the original response is returned byte-for-byte without `outputView`. The view uses `outputView.schema: "comet.cli.compact.v1"`; `outputView.references` maps omitted JSON Pointers to equal values in the same response (RFC 6901). Resolve references recursively to restore the full response. Submittable Request templates, execution identity, and approval fields remain in place. Omit `--compact` for the existing protocol; never hash or submit reference text instead of the actual input. For recovery, follow the returned reason and continuation command. If the check environment changes before Native report acceptance, Native schedules the necessary checks and independent verification again. Implementation changes still require the user's revision decision. Classic validates Build configuration combinations before claiming work. A rejected built-in configuration proposal can be corrected and resubmitted using the current recovery instructions, retaining its original Action binding and requiring a new configuration approval. Do not declare a rejected outcome unexecuted to bypass validation. The built-in Native and Classic Workflow Applications can be registered with `--application native|classic-full|classic-hotfix|classic-tweak`. Their Runs use fixed user-level workspace paths, `~/.comet/workspaces//runtime/sdk-runs/native` and the corresponding `classic` directory; `COMET_DATA_DIR` can replace the user data root. Both workflows may use the same change name, but callers cannot select another `--root-dir` or combine `--application` with `--workflow`. Legacy local directories are migrated after validation, while project state files remain portable; see [Local Runtime storage (Chinese)](./runtime-local-storage.zh.md) for the boundaries. Pass the matching `--application` whenever advancing that Run. In addition to the generic operations above, `execute` claims and runs an application-registered Executor, `record-evidence` submits declared evidence waits, and `invalidate-evidence` resumes evidence that has been verified invalid and has a declared recovery transition. The host still supplies real initial state, artifacts, and external Agent results. Native `new` and Classic `state init` now create SDK Runs by default and keep `comet-state.yaml` at the original change path; historical Classic `.comet.yaml` files remain supported in place. Existing compat changes continue under their original Runtime. ## How Comet uses the SDK Native Verifier and Classic `comet check` also use the SDK Action lifecycle. Both use Actions to bind execution inputs, claim identity, and returned results. After an interruption, the saved attempt provides the evidence needed to decide whether to reconcile or rerun. Classic persists a claimed Action in its existing trajectory before starting a check command, then records the result on that same Action. Reusing a check across scopes creates a separate Action for the target scope. Older Classic trajectories without Actions remain readable. For SDK-owned Classic full/hotfix/tweak changes, `comet guard build` performs a read-only preflight. `--apply -- [args...]` then completes the Build Action and runs a real check. If exactly one project build command can be inferred, `--apply` may omit the program arguments. Checks and receipts enter the same SDK Run, which advances to Verify only on success. Previous failed checks, Actions with unknown results, and stale inputs are not inferred away. For those Classic changes, `comet guard verify --report ` performs a read-only report check. `--apply -- [args...]` submits report evidence, runs a real verification check, and records its receipt; only success advances to Archive. Invalid report content or language, changes to an accepted report, failed checks, and unknown results block Archive. If only the receipt submission was interrupted after a successful check, `--apply` without a program can recover it without rerunning the check. In Archive, SDK-owned Classic changes can use `comet state propose-archive` to record a delivery proposal and `decide-archive` to submit a user decision bound to its proposal hash. After approval, `comet guard archive --apply` or `comet archive ` revalidates the branch and Verify evidence, then archives OpenSpec files once. The Agent remains responsible for the authorized Git commit, push, or PR; `comet state complete-delivery` verifies the actual commit and corresponding remote result before completing the Run. Archive commands do not create commits or expand user authorization on their own. If inputs change after Archive approval, or an accepted Verify report needs revision, run `comet state reopen-verification --expected-revision `. This keeps current files and prior approvals/evidence, cancels pending work, and starts a new Verify round in the same Run. Passing verification requires a new Archive proposal and approval. Only built-in SDK Runs with an active change directory, no executed Archive action, and no running/unknown Action are eligible. Once Archive has executed or its outcome is uncertain, reconcile the original execution instead of replaying it. The delivery commit must include the exact verification report bound to this acceptance, with content matching its receipt. Its path may be outside the change directory, but only that exact path is allowed; neighboring unrelated files remain excluded. Missing or changed reports and out-of-scope commits identify the affected paths. Correct the commit scope or repeat verification as needed instead of broadening the allowlist. Native ordinary changes and Classic full/hotfix/tweak each have a distinct SDK Workflow Application accessible through `comet runtime dispatch --application`. They retain separate stages and acceptance rules while the SDK handles general scheduling and commits. New changes use the SDK path by default, while existing compat changes continue under their original Runtime. Uncovered compat command entry points and real platform execution still require their own acceptance evidence; SDK unit tests do not establish it. ## Scope See the [SDK release contract](./sdk-release-contract.md) for public interface compatibility, TypeScript consumer support, declaration checks, and error recovery guidance. Runtime is a reusable deterministic orchestration core, not another Agent platform. It provides the Run/Action/Outcome/Wait protocol, workflow scheduling, persistence and recovery, approval, and acceptance extension points. The host continues to provide the Agent loop, system prompts, Skills, Hooks, Rules, MCP, sandbox, tool authorization, and model context. Restrictions expressed in platform prompts, Hooks, or tool policy must still be implemented and verified by that platform. ### Advancing long histories and reading current views The default `inspect`, `execute`, and `runUntilBlocked` methods still return a complete `WorkflowRun`, including each historical Action's original input, result, and receipts. Returned snapshots and their historical inputs remain independently mutable without changing committed evidence. A serial chain of different steps retains cumulative `outputs` in each Action input, so its full JSON size can grow quadratically with the number of steps. Hosts that need current work and final outputs can use `inspectCurrent(runId)`, `executeCurrent(command)`, or `runUntilBlockedCurrent(command)`. These return a separate `RuntimeCurrentRun` type, not a recoverable `WorkflowRun`: pending, running, unknown, and cancelled-but-not-confirmed-stopped Actions, unresolved waits, current `outputs`, and history counts. They omit completed Actions' cumulative inputs and `actionContexts`. Use `inspect` for complete evidence. `inspectCurrent` currently still reads, validates, and expands the entire stored snapshot; it reduces the returned object, not cold-read cost, and is not a paginated history API. Executors that do not need the complete Run argument can explicitly set `includeRun: false`, also accepted by `createRuntimeExecutor`. Their `preflight` and `execute` callbacks receive `undefined` as the third argument; the Action and its cumulative upstream input remain unchanged. The default still provides the complete Run. For large serial batches, combine this option with `runUntilBlockedCurrent({ runId, executorId, maxActions })` to avoid copying all history into every executor call. The batch reuses validated immutable snapshots internally, while each transition still commits through CAS. Concurrent cancellation or competing claims cannot bypass persisted-state checks. These optimizations do not remove evidence, change existing checkpoint formats, or promise linear total execution time for arbitrarily long chains. Cumulative step inputs, current outputs, and checkpoints still grow with history. Validators and transition handlers that consume complete Runs, and full Run returns, still incur expansion costs. Capacity measurements should report actual executed Actions, duration, memory, and checkpoint size. Constructed history seeds or `inspect` timings alone do not demonstrate end-to-end execution performance.