# Standalone Thin Paperclip Adapter Boundary > **Scope correction — 2026-08-09:** The board superseded real Paperclip > installation and live-instance execution for the current checkpoint. Active > Standalone work is limited to the standalone demo runner and demo page under > `packages/paperclip-runner/`. Product/server/database sections below are > retained as historical design context, not current execution instructions. Status: design approved; remediation 8 documentation reconciliation complete; final CTO gate pending Date: 2026-08-09 Decision scope: the first feature-flagged Paperclip native run, including the normative native finalizer and server-owned Section 18 status arbitration ## Decision Paperclip will integrate the native runner through two public package ports: - `NativeSessionBackend` owns a normalized native session and hides the package's concrete `HarnessDriver` and Codex implementation. - `ControlPlanePort` accepts validated PRP events and terminal results from the package and exposes durable acknowledgement and replay cursors. The two ports are complementary, not two core implementations. The runner package supplies the `NativeSessionBackend` implementation; Paperclip supplies the server-bound `ControlPlanePort` implementation. The core adapter only composes those public contracts and translates the final result into existing Paperclip types. It does not implement a native backend or contain runner behavior. The dependency direction is one way: ```text server heartbeat orchestration -> PaperclipNativeRuntimeAdapter (core seam) -> @paperclipai/paperclip-runner public contracts -> NativeSessionBackend -> package-owned driver/runner logic -> ControlPlanePort -> server-bound Paperclip implementation ``` `packages/paperclip-runner/` never imports `server/`, `packages/db/`, `packages/shared/`, or a Paperclip service. The server may import the public package. The core seam may translate company-scoped Paperclip records into the public input types, persist protocol records, and convert one terminal native result into the existing `AdapterExecutionResult`. It must not contain driver, provider, reducer, outbox, reconnect, or process-supervision logic. This remains a narrow, default-off tracer: it does not enable native execution by default, migrate legacy runs, or add browser UI. “Thin” limits where runner behavior lives; it does not remove the normative completion contract. Standalone is complete only when the additive native finalizer applies the complete Section 18 arbitration contract required by the spike exit criteria and NR-006. ## Why the current sketches need one package-local reconciliation The original `ControlPlanePort` and `NativeRunEvent` sketch proves the Conformance lifecycle, while the accepted Replay-5 path uses `PrpEvent`, `PrpStructuredRunResult`, durable source sequences, and replay. Before the real adapter is added, the package contract must be reconciled without importing Paperclip types: ```ts interface ControlPlanePort { openRun(input: OpenControlPlaneRunInput): Promise; appendEvent(event: PrpEvent): Promise<{ highestContiguousSourceSeq: number; disposition: "committed" | "duplicate"; }>; replayEvents(input: { runId: string; sourceInstanceId: string; afterSourceSeq: number; limit: number; }): Promise<{ events: PrpEvent[]; highestContiguousSourceSeq: number; }>; completeRun(input: { result: PrpStructuredRunResult; terminal: PrpTerminalState; }): Promise; } ``` The exact exported names may stay source-compatible through overloads, but the observable contract above is required. The Conformance fixture may be adapted inside the package; no production adapter may invent a second event type. `NativeSessionBackend` remains the control-plane-facing session contract. A package-local backend adapts `HarnessDriver`/`HarnessSession` into it, including the accepted semantic result stored in the harness snapshot. Paperclip core must not construct `CodexAppServerDriver` or inspect provider notifications. ## Core branch point The sole execution branch belongs in `server/src/services/heartbeat.ts` after the existing environment lease and workspace realization have succeeded and immediately before `adapter.execute(...)` today: ```ts const mode = resolveNativeRuntimeMode({ settings, agent, run, issue, target }); await persistResolvedNativeRunEnvelope({ run, issue, mode, contract, target }); const adapterResult = mode.kind === "native" ? await nativeSessionRuntime.execute( buildNativeExecutionInput({ run, issue, mode, contract, target, session }), ) : await adapter.execute(existingLegacyExecutionContext); ``` Everything before the branch remains Paperclip-owned preparation. Shared cleanup after either branch still owns usage/cost accounting, session and agent state, audit/live events, environment lease release, runtime service release, and scratch cleanup. Native mode replaces only the legacy terminal heuristic: after the existing `workspace_finalize` barrier, `NativeRunFinalizer` preserves the typed result, classifies evidence, builds an immutable assessment, calls the pure `StatusArbiter`, and commits the run/issue/liveness effects atomically. Legacy mode retains the current finalizer byte-for-byte. The native adapter returns through `AdapterExecutionResult` with an additive, strictly validated `nativeFinalization` discriminator. Existing adapters omit that field. Native run terminal state is taken only from the persisted run profile plus that typed discriminator; exit code and model-authored fields in `resultJson` are diagnostics and are never native fallback heuristics. ## Durable native run envelope and recovery boundary Standalone uses first-class columns and the Section 18 tables, not keys hidden in `context_snapshot` or `result_json`. `heartbeat_runs` gains these exact fields: ```text runtime_mode text not null default 'legacy' runtime_mode_resolver_version text null runtime_mode_reason text null runtime_mode_resolved_at timestamptz null runner_profile_json jsonb null -- redacted resolved snapshot runner_instance_id uuid null native_session_id uuid null driver_kind text null driver_version text null completion_contract_id uuid null references completion_contracts completion_contract_sha256 text null next_event_seq bigint not null default 1 native_phase text null native_phase_updated_at timestamptz null ``` The selection transaction locks the run row and writes `runtime_mode`, the resolver version/reason/timestamp, redacted profile, realized target identity, and the immutable completion-contract reference before any native provider, runner, or harness invocation. `runtime_mode='native'` without a valid contract/profile is a fail-closed native setup/finalization error; it never means legacy. Terminal claims are stored in the normative append-only `native_run_results` table (`company_id`, `issue_id`, `run_id`, `turn_id`, `completion_contract_id`, caller IDs, `server_fingerprint`, `schema_status`, `rejection_code`, the safe original `result_json`, and `canonical_sha256`). One row-locked `native_run_finalizations` coordinator per run records `phase`, `attempt`, lease, `result_id`, `assessment_id`, `decision_id`, and redacted failure/retry fields. Receipt of a terminal result is acknowledged only after the result row and coordinator `result_id` commit. The rest of the finalization storage uses the exact Section 18.7 field sets: | Durable record | Exact fields added/used | |---|---| | `completion_contracts` | `id`, `company_id`, `issue_id`, `revision`, `schema_version`, `policy_version`, `risk`, `completion_authority`, `incomplete_criteria_policy`, `contract_json`, `canonical_sha256`, `created_by_actor_type`, `created_by_actor_id`, `created_at`, `supersedes_contract_id` | | `native_run_results` | `id`, `company_id`, `issue_id`, `run_id`, `turn_id`, `completion_contract_id`, `caller_result_id`, `caller_dedupe_key`, `server_fingerprint`, `schema_status`, `rejection_code`, `result_json`, `canonical_sha256`, `created_at` | | `native_run_finalizations` | `run_id`, `company_id`, `issue_id`, `phase`, `attempt`, `lease_owner`, `lease_expires_at`, `result_id`, `assessment_id`, `decision_id`, `failure_code`, `failure_detail`, `next_attempt_at`, `created_at`, `updated_at` | | `work_assessments` | `id`, `company_id`, `issue_id`, `run_id`, `turn_id`, `contract_id`, `result_id`, `trigger_kind`, `trigger_ref`, `trigger_capability`, `trigger_actor_company_id`, `prior_issue_status`, `prior_status_version`, `prior_decision_id`, `policy_version`, `assessment_json`, `input_digest`, `supersedes_assessment_id`, `created_at` | | `status_decisions` | `id`, `company_id`, `issue_id`, `assessment_id`, `decision_version`, `policy_version`, `from_status`, `to_status`, `reason_code`, `decision_json`, `decision_digest`, `application_state`, `supersedes_decision_id`, `applied_at`, `created_at` | | `status_decision_effects` | `id`, `company_id`, `issue_id`, `decision_id`, `ordinal`, `effect_kind`, `target_type`, `target_id`, `idempotency_key`, `payload`, `delivery_state`, `attempt_count`, `next_attempt_at`, `last_error`, `delivered_at`, `created_at`, `updated_at` | | `issues` | new `status_version bigint not null default 0` and `last_status_decision_id uuid null`; existing status/liveness/lock fields remain projections | Their unique keys, company foreign keys, immutable/supersession rules, and effect-ledger semantics are unchanged from Section 18.7. `heartbeat_runs` also gets only a compact result/finalization read projection in `result_json`; that JSON is never the source of truth. Byte equivalence means equality after the Section 18.8 canonical serializer: UTF-8 JSON, recursively sorted object keys, protocol-order arrays, UTC RFC 3339 timestamps with millisecond precision, lowercase UUIDs, absent optionals omitted, and bounded numeric serialization. Prose is preserved byte-for-byte after schema normalization with no locale/Unicode compatibility folding. The hashed material includes the complete validated envelope and server-bound run/session/turn/contract identity, but excludes transport-only delivery metadata. The server stores SHA-256 of those bytes. Reusing a source event ID, source sequence, caller result ID, or caller dedupe key with another hash is a replay conflict with no acknowledgement or effects. Equivalent retries, including fresh caller keys with the same server fingerprint, return the already committed canonical row and cursor. Recovery never re-runs the resolver. Startup and periodic reconciliation read `heartbeat_runs.runtime_mode`; for native rows they follow `native_run_finalizations.result_id` to `native_run_results` and resume the first missing durable phase. This remains true when the instance flag or agent profile has since been disabled, the server restarted, or the runner process disconnected. A disabled flag affects only runs whose mode has not yet been resolved (`runtime_mode_resolved_at is null`). ## Feature flag, selection, and kill switch The global gate is `instance.experimental.enableNativeRunner`, default `false`. It is server/API configurable in the first slice; there is no UI control. The per-agent opt-in is stored in the existing company-scoped `agents.runtime_config` JSON: ```json { "nativeRunner": { "mode": "native", "backend": "codex_app_server", "protocolVersion": 1 } } ``` The resolver is pure and versioned. Its inputs are the instance flag, the persisted agent profile, the authenticated run/agent/issue records, and the realized execution target. It does not read model output or PRP events. | Global flag | Agent profile | Eligibility | New run mode | |---|---|---|---| | off | any | any | legacy | | on | absent or `legacy` | any | legacy | | on | `native` | eligible | native | | on | `native` | ineligible before selection | rejected before invocation | Initial eligibility is intentionally narrow: an issue-bound standard run, a same-company active `codex_local` agent, protocol version 1, and a local execution target with an already-realized workspace. Remote environments, unscoped timer wakes, skill tests, task bridges, low-trust review, and other drivers remain legacy or are rejected when explicitly misconfigured as native. The resolved mode, resolver version, and non-secret reason code are persisted on the heartbeat run before invocation. An active run never changes modes. Turning `enableNativeRunner` off is the kill switch: - queued and future runs resolve to legacy; - an already-selected native run remains native until terminal, drained, or cancelled through the existing run-cancellation path; - replays and recovery use the persisted mode even while the flag is off; - no native error may retry the same run through `adapter.execute`. This gives rollback without dual execution or ambiguous event history. ## Company, authentication, and threat boundary The real `ControlPlanePort` is constructed inside the server with a bound `companyId`, `runId`, `issueId`, `agentId`, and `sourceInstanceId` loaded from authoritative rows. Every method compares incoming identity fields with that binding and rejects generically before persistence on any mismatch. The native branch does not spread or pass the legacy `context`, `runtimeConfig`, `AdapterInvocationMeta`, or `process.env`. `buildNativeExecutionInput` is the only constructor and returns this closed, strict schema (`unknown` keys are rejected at the server/package boundary): ```ts interface NativeExecutionInputV1 { schema: "paperclip.native-execution-input.v1"; binding: { companyId: string; runId: string; issueId: string; agentId: string; executionWorkspaceId: string; }; task: { identifier: string; title: string; description: string | null; workMode: "standard"; }; workspace: { cwd: string; repoUrl: string | null; repoRef: string | null; branchName: string | null; }; session: { normalizedSessionId: string | null; driverKind: "codex_app_server"; protocolVersion: 1; }; completionContract: { id: string; sha256: string; schemaVersion: string; contract: StrictCompletionContractInput; }; interactionResponses: NativeInteractionResponseEnvelope[]; credentialBindings: NativeCredentialBindingRef[]; } ``` `credentialBindings` contains only opaque binding IDs, service/destination policy, expiry, and non-secret display metadata. A broker or trusted launch boundary may resolve a binding outside the model process; neither gateway tokens nor secret values are fields in this input. Likewise, interaction responses are the typed, destination-bound response envelopes from the native interaction bridge, not a wake payload. The package then builds a still smaller `NativeModelEnvelopeV1` from only `task`, the safe workspace location, the completion schema, and applicable typed interaction responses. Binding IDs, credential binding refs, runner profile, and driver/session control data stay outside model-visible content. There is no escape hatch such as `extra`, `metadata`, arbitrary `context`, or arbitrary `env` in either schema. The runner, package driver, and model/harness never receive: - the local agent JWT, `PAPERCLIP_API_KEY`, a board session, or a board API key; - managed MCP gateway credentials, runner-lease/bootstrap credentials, or credential-broker secret material; - `PAPERCLIP_WAKE_PAYLOAD_JSON`, rendered Paperclip wake text, Paperclip skill instructions, the Paperclip API manual, or run-scoped skill material; - raw `process.env`, agent/project/routine env maps, `runtimeConfig.env`, or the legacy adapter's generic execution context; - authority to choose a company, issue, agent, policy, approval, or status; - database access or a public issue-mutation endpoint; - resolved secret values in events, snapshots, diagnostics, or digests. The native constructor never reads the already-built legacy wake/context object, and the native branch occurs before local-agent JWT construction, managed-MCP gateway materialization, and legacy adapter invocation. Tests seed unique canaries into every forbidden source, recursively inspect the backend launch input and captured model request, and assert that no key, value, serialized canary, or object reference crosses the native boundary. The server-side port is an in-process capability for the first tracer. A future runner WebSocket must authenticate a one-time runner lease and bind to the same server-owned values; that transport is not part of this issue. Threats and required responses: | Threat | Required response | |---|---| | Event names another company/run/session | Generic rejection, no lookup disclosure, no write | | Same source ID or sequence with different bytes | `native_event_replay_conflict`; fail native run closed | | Caller supplies status, approval, or policy outcome | Preserve as untrusted payload at most; never apply | | Event/result contains a known credential | Redact before diagnostics and reject persistence of the unsafe payload | | Legacy context or raw env reaches native constructor | Validation/test failure before provider invocation | | Native port or event store is unavailable | Do not acknowledge; runner keeps its durable outbox; fail/recover without legacy fallback | | Flag is disabled after a run started | Finish/cancel the persisted native mode; never splice legacy execution into it | ## Governance and status authority PRP events and `reportedWorkDisposition` are reports, not organizational commands. The Standalone tracer does not add a runner-accessible issue status API. - Existing board/user issue routes remain authorized organizational command paths. For native finalization, `StatusDecisionCommitter` is the only server-internal status writer and it applies only a `StatusArbiter` decision. - Existing execution-policy participants remain the only execution-decision authority. - Existing approval and interaction services remain the only materialization and decision paths. A native request cannot approve itself or translate an interaction into a formal approval. - Existing budget checks decide whether dispatch is allowed and existing budget hard-stop cancellation remains authoritative. - Existing activity logging records flag changes, runtime selection, run terminal state, cancellation, and any later governed mutation. The native result and reported disposition are preserved as claims. The additive native finalizer then applies the complete normative Section 18 flow: immutable `CompletionContractService` revision, authenticated `NativeResultIngestor`, exactly-once workspace finalization, `EvidenceClassifier`, immutable `WorkAssessmentService` snapshot, pure versioned `StatusArbiter`, serialized `StatusDecisionCommitter`, durable effect ledger, and `NativeFinalizationReconciler`. Shadow computation remains mandatory at MIG-04/MIG-05, but it is a rollout stage, not the Standalone scope or exit state. Standalone evidence must proceed through MIG-06 for one allowlisted internal company/agent and prove an applied decision. Rollout can then be disabled again; already-selected native rows still reconcile as native. If the complete corpus or application gate cannot pass, Standalone stays blocked rather than shipping a contradictory shadow-only substitute. The arbiter may choose `done`, `in_review`, `blocked`, continued `in_progress`, or preserve-on-finalization-failure. Non-terminal decisions atomically create the named reviewer/approval/interaction/delegated-review/monitor, blocker owner and action, continuation, or recovery action required by Section 18. A reported `done` never directly closes an issue. Board/user status races increment `issues.status_version`; the losing native finalizer reloads and appends a superseding assessment instead of overwriting organizational authority. ## Existing lifecycle mapping | Paperclip concern | Standalone mapping | Owner | |---|---|---| | Checkout and issue execution lock | Unchanged; resolved before native selection | Existing issue/heartbeat services | | Budget and pause gate | Unchanged pre-dispatch check | Existing budget/invokability services | | Workspace preparation | Unchanged; native receives the realized cwd/target only | Environment/workspace orchestrators | | Session execution | `NativeSessionBackend` through package-owned runtime loop | Runner package | | Event validation/reducer semantics | Accepted PRP schemas and reducer | Runner package | | Event commit/ACK/replay | Bound `ControlPlanePort` implementation | Thin server adapter | | Cancellation | Existing cancel route invokes a registered native cancel handle, then existing terminal cleanup | Heartbeat + package session | | Workspace finalization | Existing `workspace_finalize` barrier after native execute returns | Heartbeat/workspace services | | Run terminal state | Validated discriminator after workspace finalization, coordinated with native finalization | Thin adapter + native finalizer | | Issue status | Section 18 assessment, arbitration, CAS, and atomic liveness effects | Native finalizer + existing issue/governance services | | Approvals/interactions | Typed native bridge materializes through existing services; unsupported/forged requests fail closed and cannot self-approve | Native interaction bridge + existing governance services | | Usage/cost/session state | Converted to existing `AdapterExecutionResult` fields | Existing heartbeat finalizer | | Audit/live events | Existing activity and heartbeat event publication | Existing services | | Lease/runtime/scratch release | Existing `finally` path | Existing heartbeat/environment services | ### Cancellation seam The heartbeat service already cancels process-backed adapters through `runningProcesses`. Native sessions need one additional run-scoped registry of idempotent cancel functions. `cancelRunInternal`, agent pause, budget pause, and shutdown drain call the same registry before their current status/cleanup writes. The package implementation performs `session.cancel`/`interrupt` and `close`; provider process escalation remains package-owned. Registering a cancel handle does not grant status authority. ## Native event persistence and replay The first slice extends `heartbeat_run_events` rather than creating a parallel operator timeline. Nullable native-source columns keep legacy rows unchanged: ```text source_instance_id source_event_id source_seq source_payload_sha256 protocol_schema_version ``` Required unique indexes (the source identities are partial): ```text (run_id, seq) (run_id, source_event_id) where source_event_id is not null (run_id, source_instance_id, source_seq) where source_instance_id is not null and source_seq is not null ``` Every writer uses one server-owned `appendHeartbeatRunEvent` transaction. `nextRunEventSeq(max(seq)+1)` is removed. The transaction: 1. validates the event and bound company/run/agent identity; 2. locks the owning `heartbeat_runs` row `FOR UPDATE`; 3. checks source-event/source-sequence deduplication before allocating a canonical sequence; 4. canonicalizes and hashes the complete event payload; 5. inserts with `seq = heartbeat_runs.next_event_seq`, then increments `next_event_seq` in the same transaction; 6. on a dedup conflict, loads the canonical row and accepts only an equivalent hash without consuming a sequence; 7. computes the highest contiguous committed native source sequence; and 8. publishes/acknowledges only after commit. The same allocator is mandatory for lifecycle, stdout/stderr, cancellation, workspace, adapter, recovery, and native PRP events. Publication happens from the committed row/outbox, never between allocation and commit. Concurrent cancel/lifecycle/native appends therefore serialize on the per-run row and the database unique `(run_id, seq)` constraint is the final guard. The expand migration changes event `seq` and run `next_event_seq` to `bigint`. For each existing run it first preserves the earliest row for every existing sequence; if historical duplicates exist, it deterministically moves only the later duplicates (ordered by `created_at, id`) to fresh values above that run's old maximum and records duplicate counts in migration verification output. It then sets `next_event_seq = max(seq) + 1` (or `1` for an empty run), installs the unique constraint, and only then deploys the shared writer. Legacy event payloads/source-null behavior are unchanged; no synthetic native source fields or finalization history are backfilled. Replay reads the same rows by the bound run/source instance, ordered by `source_seq`, after an exclusive cursor. A gap is reported and never hidden by canonical server sequence. Legacy rows have null native-source fields and are unaffected. Existing `/api/heartbeat-runs/:runId/events` remains the operator read path; no new public mutation route is required. ## Failure behavior 1. Failures before mode persistence use the existing setup-failure path. 2. A native eligibility/configuration error never invokes a provider. 3. After native mode is persisted, every error stays native and uses a stable `native_*` error code; there is no legacy retry for that run. 4. Missing, inconsistent, or invalid `nativeFinalization` fails the run with `native_finalization_missing` or `native_finalization_invalid`. 5. Workspace-finalization failure wins over a successful reported result. The report remains persisted for inspection, the run is failed, and issue status is preserved. 6. Cancellation is idempotent. Late native completion cannot overwrite a cancelled heartbeat run because the existing conditional terminal write remains authoritative. 7. Event replay conflict, company mismatch, auth mismatch, unsafe payload, or acknowledgement ambiguity fails closed and never mutates issue governance. 8. A native runtime request that the tracer does not support is declined with `native_runtime_request_unsupported`; it is not auto-approved. ## Mock-versus-real conformance strategy The package exports one table-driven `ControlPlanePort` conformance suite. It runs unchanged against: 1. `MockControlPlaneAdapter`, with deterministic in-memory storage; and 2. `PaperclipControlPlanePort`, with a real test database, heartbeat run, issue, agent, and company binding. Both adapters produce a normalized snapshot containing canonical PRP events, source cursors, duplicate dispositions, terminal result, and failure code. The suite compares snapshots while excluding database IDs and wall-clock fields. The real suite adds authorization and transaction assertions that a mock cannot prove. A separate deterministic fake backend drives both ports. Real Codex is a smoke proof only and does not replace the deterministic matrix. Required shared cases: - open, append, terminal happy path; - ordered replay after every cursor; - identical duplicate event and terminal replay; - conflicting duplicate ID and conflicting source sequence; - source gap and recovery after missing event arrives; - wrong run/session/company/agent identity; - terminal before result and event after terminal; - cancellation before turn, during turn, and after terminal; - connection loss before and after commit acknowledgement. - concurrent lifecycle, cancellation, native, and log appends with one gap-free canonical server sequence and stable replay after restart; - terminal-result replay before/after acknowledgement with one canonical `native_run_results` row and one finalization coordinator; - process loss and flag disablement after result persistence, proving recovery uses only the persisted native run envelope. ## Test matrix | ID | Concern | Mock | Real Paperclip | Legacy assertion | |---|---|---:|---:|---| | P6-01 | Default flag off | — | yes | `adapter.execute` called exactly once; no native rows | | P6-02 | Per-agent opt-in absent | — | yes | Same as current path | | P6-03 | Eligible native selection | yes | yes | Legacy adapter not called | | P6-04 | Persisted mode survives flag change | yes | yes | No mid-run mode switch | | P6-05 | Kill switch before dispatch | — | yes | New run uses legacy once | | P6-06 | Workspace cwd/branch/identity | yes | yes | Same realized workspace and finalize barrier | | P6-07 | Workspace finalize failure | yes | yes | Failed run; result preserved; issue unchanged | | P6-08 | Budget blocked before dispatch | — | yes | Neither native nor legacy provider starts | | P6-09 | Budget hard stop during native run | yes | yes | One cancel, normal lease/resource cleanup | | P6-10 | Manual/agent-pause cancellation | yes | yes | Idempotent native cancel and terminal race guard | | P6-11 | PRP append/ACK/replay | yes | yes | Legacy event rows unchanged | | P6-12 | Duplicate replay | yes | yes | One semantic event, stable cursor | | P6-13 | Conflicting replay | yes | yes | Native fails closed; no legacy fallback | | P6-14 | Cross-company/run/agent forgery | — | yes | Generic denial, zero rows, no disclosure | | P6-15 | Typed input/credential isolation | yes | yes | JWT, API key, MCP credentials, wake/skills, raw env/context canaries absent from package and model | | P6-16 | Durable mode/result recovery | yes | yes | Restart/flag-off/process-loss resumes persisted native result and coordinator | | P6-17 | Canonical allocator concurrency | yes | yes | Lifecycle/cancel/native/log writers produce unique `(run_id, seq)` and stable replay | | P6-18 | Migration/backfill | — | yes | Legacy duplicate repair, `next_event_seq`, bigint, uniqueness, and null native fields proven | | P6-19 | Terminal result idempotency/conflict | yes | yes | One canonical result/finalization; changed bytes fail closed | | P6-20 | Reported `done` with satisfied contract | yes | yes | Claim stored; server assessment/arbiter/committer applies authoritative `done` once | | P6-21 | Incomplete/rejected evidence | yes | yes | Arbiter preserves or creates a valid blocker/review/continuation path; never trusts prose | | P6-22 | Governance/status forgery | yes | yes | Caller status/policy/approval fields rejected; zero authoritative effects | | P6-23 | Native interaction bridge | yes | yes | Durable materialization/typed response, no invalid issue status or credential exposure | | P6-24 | Board/cancel/dependency race | yes | yes | `status_version` CAS and superseding assessment; no reopen/duplicate effect | | P6-25 | Missing/invalid native finalization | yes | yes | Native run fails with named recovery; no legacy heuristic | | P6-26 | Workspace finalization ordering | yes | yes | Workspace success precedes assessment; failure prevents `done` and preserves claim | | P6-27 | Non-terminal liveness effects | yes | yes | Reviewer/blocker/continuation/recovery entity commits atomically with status | | P6-28 | Kill switch during arbitration | yes | yes | Existing native coordinator finishes/reconciles; new run selects legacy | | P6-29 | Cost/usage/session projection | yes | yes | Existing field meanings unchanged | | P6-30 | Activity/live/effect ledger | — | yes | Selection/result/decision/effects/cancel audited and published once | | P6-31 | Section 18.13 corpus | yes | yes | Every SD/TC/ATT/LIVE/REC/COMP/MIG fixture emits a joinable consumer result | | P6-32 | Full legacy targeted regression | — | yes | Byte-equivalent result/events/UI-read snapshot; zero native history rows | ## Exact implementation sequence 1. Reconcile `ControlPlanePort` with accepted PRP events/results and add the shared mock conformance suite. No core files change in this step. 2. Add the package-local `HarnessDriver` to `NativeSessionBackend` adapter and a deterministic fake-backend executor. Keep provider/process logic packaged. 3. Expand storage: add first-class run-mode/contract/sequence fields, native result/finalization/assessment/decision/effect tables, `issues.status_version`, and nullable native event-source columns. Run production-shaped migration and legacy snapshot tests before enabling any writer. 4. Replace every heartbeat event writer with the shared per-run transactional allocator, backfill `next_event_seq`, add unique `(run_id, seq)`, and prove concurrent lifecycle/cancel/native/log appends. 5. Implement the server-bound `PaperclipControlPlanePort` with constructor binding, canonical-byte deduplication, commit-before-ACK replay, and terminal result idempotency. 6. Add the default-off instance flag, parse the per-agent profile, materialize immutable completion contracts, and persist the resolved native run envelope before invocation. Test resolver/kill-switch precedence and restart recovery. 7. Add strict `NativeExecutionInputV1`/`NativeModelEnvelopeV1` validators and the explicit constructor. Prove forbidden legacy context and credential canaries cannot reach package, driver, model, event, result, log, or digest. 8. Add `nativeFinalization` to `AdapterExecutionResult`, the package executor, native cancel registry, and the single heartbeat branch. Keep legacy JWT, MCP, prompt/context, and adapter invocation construction entirely on the legacy branch. 9. Implement the Section 18 services: result ingestion, workspace-first native finalization, evidence classification, assessment, pure arbiter, serialized decision/effect commit, interaction bridge, and reconciler. Version every existing issue-status writer before enabling native application. 10. Execute MIG-01 through MIG-05 with the unchanged Section 18.13 fixture corpus. Resolve every unexplained shadow divergence; do not waive cases. 11. Enable MIG-06 only for one isolated company/agent, run mock and database conformance, governance/status, workspace, budget/cancel, disconnect, terminal replay, and legacy regression proofs, then disable new dispatch. 12. Add the package-local tracer/tutorial/evidence and run one safe local Codex task only after deterministic tests pass. Stop for mandatory Security and CTO implementation review before QA or wider opt-in. ## Exact files that may change ### Package-owned contract, implementation, tests, and evidence ```text packages/paperclip-runner/package.json packages/paperclip-runner/src/index.ts packages/paperclip-runner/src/contracts/control-plane-port.ts packages/paperclip-runner/src/contracts/native-session-backend.ts packages/paperclip-runner/src/contracts/types.ts packages/paperclip-runner/src/mock-core/mock-control-plane-adapter.ts packages/paperclip-runner/src/mock-core/mock-control-plane-adapter.test.ts packages/paperclip-runner/src/backends/harness-driver-backend.ts (new) packages/paperclip-runner/src/backends/harness-driver-backend.test.ts (new) packages/paperclip-runner/src/conformance/control-plane-port.ts (new) packages/paperclip-runner/src/conformance/control-plane-port.test.ts (new) packages/paperclip-runner/src/cli/paperclip-adapter.ts (new) packages/paperclip-runner/protocol/fixtures/standalone/* (new) packages/paperclip-runner/docs/standalone-thin-paperclip-adapter.md (new) packages/paperclip-runner/docs/tutorials/standalone-thin-paperclip-adapter.md packages/paperclip-runner/docs/tutorials/end-to-end.md packages/paperclip-runner/docs/index.md packages/paperclip-runner/docs/architecture.md packages/paperclip-runner/.paperclip-local/log.md ``` ### Paperclip storage, adapter, finalizer, and read seam ```text server/package.json packages/adapter-utils/src/types.ts packages/shared/src/types/instance.ts packages/shared/src/types/native-finalization.ts (new) packages/shared/src/validators/instance.ts packages/shared/src/validators/native-finalization.ts (new) packages/shared/src/constants.ts packages/shared/src/feature-catalog.ts packages/db/src/schema/heartbeat_runs.ts packages/db/src/schema/heartbeat_run_events.ts packages/db/src/schema/issues.ts packages/db/src/schema/completion_contracts.ts (new) packages/db/src/schema/native_run_results.ts (new) packages/db/src/schema/native_run_finalizations.ts (new) packages/db/src/schema/work_assessments.ts (new) packages/db/src/schema/status_decisions.ts (new) packages/db/src/schema/status_decision_effects.ts (new) packages/db/src/schema/index.ts packages/db/src/migrations/.sql packages/db/src/migrations/meta/* (generated only) server/src/services/instance-settings.ts server/src/services/heartbeat-run-events.ts (new) server/src/services/native-runtime/runtime-mode.ts (new) server/src/services/native-runtime/native-execution-input.ts (new) server/src/services/native-runtime/paperclip-control-plane-port.ts (new) server/src/services/native-runtime/native-session-runtime.ts (new) server/src/services/native-runtime/completion-contracts.ts (new) server/src/services/native-runtime/native-result-ingestion.ts (new) server/src/services/native-runtime/native-run-finalizer.ts (new) server/src/services/native-runtime/evidence-classifier.ts (new) server/src/services/native-runtime/work-assessments.ts (new) server/src/services/native-runtime/status-arbiter.ts (new) server/src/services/native-runtime/status-decision-committer.ts (new) server/src/services/native-runtime/native-interaction-bridge.ts (new) server/src/services/native-runtime/native-finalization-reconciler.ts (new) server/src/services/native-runtime/index.ts (new) server/src/services/issue-thread-interactions.ts server/src/services/issues.ts server/src/services/heartbeat.ts server/src/routes/issues.ts server/src/routes/agents.ts server/src/routes/openapi.ts server/src/__tests__/native-runner-standalone.integration.test.ts (new) server/src/__tests__/heartbeat-native-runner-selection.test.ts (new) server/src/__tests__/heartbeat-native-runner-cancellation.test.ts (new) server/src/__tests__/heartbeat-run-event-sequencing.test.ts (new) server/src/__tests__/native-runner-input-boundary.test.ts (new) server/src/__tests__/native-run-finalizer.test.ts (new) server/src/__tests__/native-status-arbiter-corpus.test.ts (new) server/src/__tests__/native-finalization-recovery.test.ts (new) server/src/__tests__/native-finalization-migration.test.ts (new) server/src/__tests__/legacy-finalization-regression.test.ts (new) ``` No other file is pre-approved. The migration owns the universal `status_version` increment (a database trigger on an actual status change), so Standalone does not scatter versioning edits across unrelated writers. The native committer calls transaction-aware existing issue, blocker, review, interaction, recovery, wake, activity, and live-event entry points; if one of those files must change to expose such an entry point, implementation pauses for an allowlist amendment and CTO review. In particular, Standalone must not change a concrete legacy adapter, approval authority, browser UI, workspace policy, budget policy, or activity semantics merely to make the tracer pass. ### Remediation allowlist amendment (2026-08-09) The implementation review found four seam-local paths whose names differed from the provisional list above. They are added without expanding authority: ```text packages/paperclip-runner/src/native-session-runtime.ts packages/paperclip-runner/src/native-session-runtime.test.ts packages/paperclip-runner/src/contracts/native-execution.ts packages/paperclip-runner/src/contracts/native-execution.test.ts packages/paperclip-runner/src/backends/codex-native-backend.ts packages/paperclip-runner/spec/fixtures/status-authority-sdk.json packages/adapter-utils/package.json packages/shared/src/index.ts packages/shared/src/types/index.ts packages/shared/src/validators/index.ts server/src/services/native-runtime/native-session-executor.ts server/src/services/native-runtime/native-session-executor.test.ts server/src/services/native-runtime/evidence-classifier.test.ts server/src/services/native-runtime/paperclip-control-plane-port.test.ts server/src/services/native-runtime/status-arbiter.test.ts server/src/services/recovery/service.ts packages/paperclip-runner/docs/design/standalone-thin-paperclip-adapter.md ``` The package runtime, closed native-input contract, and Codex backend files own persisted provider-session recovery and construction. The adapter-utils metadata declares the package dependency used by the approved native finalization result type; the shared barrels only expose the already-reviewed types and validators. The recovery service touch reuses the existing source-scoped recovery action and wake path rather than creating new authority. The corpus edit moves two existing coverage labels between fixtures without changing any fixture input or expected outcome, eliminating an unjoinable fixture. The server executor files own only the run-scoped cancellation handle and coordinator lease around that package runtime. The colocated port and arbiter tests exercise the same approved database and pure-policy seams exposed through the named `server/src/__tests__` matrix entry points. This amendment does not authorize changes to a concrete legacy adapter, approval authority, UI, workspace policy, budget policy, or runner/provider/session behavior outside the package. ### Remediation 2 test-seam amendment (2026-08-09) The following already-listed files receive one provider-boundary test seam for the persisted recovery proof: ```text server/src/services/heartbeat.ts server/src/services/native-runtime/native-session-executor.ts server/src/__tests__/native-session-resumption.test.ts server/src/__tests__/native-status-arbiter-corpus.test.ts ``` `heartbeatService` may receive an optional native-backend factory, and the native session executor may receive the resulting `NativeSessionBackend`. Production supplies neither option and therefore still constructs the package-owned Codex backend exactly as before. The embedded-PostgreSQL test uses this seam only at the external provider boundary; it still executes the production orphan reaper, database lease claim, `executeRun`, package session recovery, control-plane port, workspace barrier, evidence classifier, status committer, finalizer, and terminal heartbeat projection. No package contract, provider/session behavior, legacy adapter, approval authority, workspace policy, or UI surface changes. ### Remediation 3 production-policy and corpus amendment (2026-08-09) The following production files were already listed in the approved authority seams above. This amendment explicitly permits corpus-conformance vocabulary and return-shape corrections inside those seams; it grants no new authority: ```text server/src/services/native-runtime/status-arbiter.ts server/src/services/native-runtime/status-decision-committer.ts server/src/services/native-runtime/native-run-finalizer.ts server/src/services/native-runtime/native-interaction-bridge.ts server/src/services/native-runtime/native-session-executor.ts server/src/services/native-runtime/native-finalization-reconciler.ts server/src/services/native-runtime/runtime-mode.ts ``` The arbiter remains the versioned server-owned policy boundary. The finalizer, attention, cancellation, reconciliation, compatibility, and migration modules expose trigger-specific production consumers of that policy. The committer continues to materialize decisions and effects atomically through existing issue, interaction, wake, recovery, and activity services. This amendment does not authorize changes to package contracts, provider/session behavior, a concrete legacy adapter, approval authority, workspace or budget policy, or UI. The corpus test remains in its pre-approved matrix path. It creates a fixture-specific database shape and dispatches the fixture to the responsible production consumer. All eleven expected fields are derived from consumer return values and persisted production rows: run status, status/preserve action, reason, required and forbidden effects, live-path kind, claim preservation, native-record behavior, decision count, maximum wake count, and maximum notification count. A separate mutation check changes every expected field for every fixture and must fail. Every one of the 70 matrix row IDs maps to a named finalizer, terminal projection, attention, cancellation, committer, reconciliation, compatibility, or migration consumer, and the row fails if that consumer did not execute or returned different semantics. No hand-written test policy table supplies observed outcomes and no row is satisfied by a coverage-label join to a generic observation. ### Remediation 4 live-consumer and effect-materialization amendment (2026-08-09) The fixture-keyed scenario arbiter is removed. Finalization now calls the same fact-based arbiter as the heartbeat finalizer, while attention, cancellation, reconciliation, compatibility, and migration decisions accept canonical facts and each has a non-test production caller. The corpus constructs those durable facts, invokes the production consumer, and uses that return plus persisted issue, run, decision, interaction, wake, recovery, workspace-operation, and contract rows as its observation. `StatusDecisionCommitter` handles every `NativeStatusEffect` explicitly. Each case creates or changes its named target before its delivered ledger row is written. Reconciliation acknowledges a pending effect only after its existing company- and issue-bound target is verified. An unknown effect or target type throws inside the transaction, leaving the decision, effect ledger, issue status/version, and finalization coordinator unchanged. Corpus replay calls the committer twice and requires one decision identity and exactly one delivery attempt per target; audit-only attention and replacement-turn paths likewise assert their real target state. The 52-fixture/70-row proof therefore depends on runtime-reachable consumer returns and materialized state. Removing a production consumer invocation, changing its facts or decision, suppressing its target mutation, duplicating a delivery, or restoring a synthetic `issue_checkout` fallback fails the focused database gate. ### Remediation 5 live-entrypoint proof amendment (2026-08-09) The remediation stays within the approved server authority seams and adds one owning workspace service: ```text server/src/services/heartbeat.ts server/src/services/native-runtime/native-session-executor.ts server/src/services/native-runtime/native-interaction-bridge.ts server/src/services/native-runtime/native-finalization-reconciler.ts server/src/services/native-runtime/native-workspace-finalizer.ts server/src/services/native-runtime/status-decision-committer.ts server/src/services/native-runtime/runtime-mode.ts server/src/__tests__/native-status-arbiter-corpus.test.ts ``` Policy resolvers remain pure and may be tested directly, but their return value is not operational proof. Cancellation fixtures enter through `cancelNativeSession`, which commits the decision or an explicit audit-only replacement-turn outcome. Attention fixtures enter through the persisted accepted-result ingress, which calls `routeNativeAttention` internally, resolves a same-company eligible delegate, and uses the company-scoped issue service. REC-04/06/07/08 fixtures enter through `reconcileNativeFinalizations`; REC-04 invokes the workspace operation recorder and observes the actual finalizer result. REC-06/07/08 reclassify the persisted result and contract into a new append-only assessment before an explicitly authorized superseding commit; REC-07 cannot be selected by an unrelated work product. MIG-08 uses the instance-global flag through the production heartbeat selector: persisted native mode wins for an active run and a fresh unresolved run selects legacy while the agent profile remains unchanged. `resolveNativeFinalizerStatus`, `resolveNativeAttentionStatus`, `resolveNativeCancellationStatus`, `resolveNativeReconciliationStatus`, the compatibility/migration resolvers, and their read models are policy evidence only when called directly. A fixture claiming an operational effect must also contain the named live-entrypoint receipt and the owning service's durable target. Pending-effect replay remains acknowledgement-only and is restricted to the original company, issue, decision, and still-existing target. Negative tests deliberately remove each live entrypoint/action and the replay target; the mapped fixture fails even though the corresponding pure resolver still returns its expected label. ### Remediation 6 persisted-attention and global-kill-switch amendment (2026-08-09) Accepted native attention now enters through the runtime finalization call graph, not through a corpus-owned call to `routeNativeAttention`: ```text PaperclipControlPlanePort.completeRun -> native_run_results (accepted immutable package result) -> finalizeNativeRun -> routePersistedNativeResultAttention -> recordNativeAttentionAssessment -> routeNativeAttention -> StatusDecisionCommitter -> issueService / issueThreadInteractionService / recovery + activity owners ``` The persisted-result ingress derives company, issue, run, agent, result, and contract identity from database bindings. A same-company eligible delegate is materialized by `issueService`; a human-authority request is materialized as a typed issue-thread interaction; an explicit cross-company target is rejected into an immutable decision, recovery action, failed native finalization, and activity receipt. The corpus mutates the accepted result row and calls `finalizeNativeRun`, which owns the persisted-result ingress. The database integration test starts one layer earlier at `PaperclipControlPlanePort.completeRun` and then calls the same production finalizer. `routePersistedNativeResultAttention` and `routeNativeAttention` remain internal helpers and are no longer accepted as the operational proof. Audit-only duplicate and stale requests are a terminal success even though they intentionally create no status decision. The finalizer accepts that shape only when every request has an `attention_duplicate_suppressed` receipt bound to the exact durable interaction target. It commits the coordinator with a null decision, projects the successful heartbeat, and preserves issue status and version. A committed zero-decision coordinator is replayable only while those durable receipts remain present; replay does not update the named interaction a second time. Missing or cross-company interaction bindings fail closed into named finalization recovery. MIG-08 now represents the actual instance-global transition. The production heartbeat selector reads the persisted mode first: an already-selected native run remains native and can finish or reconcile from its coordinator after `experimental.enableNativeRunner` turns off. A fresh `paperclip_runner` start is rejected with `paperclip_runner_rollout_disabled`; direct adapters remain on their existing legacy paths and are never selected into native execution by an old runtime profile. The agent configuration is not rewritten, and no second rollback control is persisted. Re-enabling the instance flag therefore restores normal fresh-run selection without an operator profile repair. Sabotage changes the production inputs rather than the policy labels: removing the persisted-result ingress breaks the attention fixtures, and changing the global flag input from off to on breaks MIG-08 while `resolveNativeAttentionStatus` and `resolveNativeMigrationStatus` continue to return their pure labels. The full recovery test observes both durable outcomes: the original native rows reach committed terminal state and the fresh runner start is rejected before creating native result/finalization rows. This amendment does not add an attention UI, a public native-attention route, external-system execution, credential delegation, or auto-approval. The Codex v1 structured-output surface still emits the compact `kind`/`summary` form; the canonical PRP result can carry richer target metadata, which the server validates as untrusted routing hints. Richer provider authoring UX and additional resolver kinds remain deferred. ### Remediation 7 authoritative exact-file reconciliation (2026-08-09) The provisional list and seam amendments above remain the decision history. For final diff auditing, their authoritative union is the following exact 89-file allowlist. It matches the Standalone branch diff from the parent of the authorization commit through this remediation, including the package README, the Section 18.13 checker, package/runtime tests, `canonical.ts`, and `runtime-mode.test.ts`. No file outside this list is authorized as Standalone work. ```text packages/adapter-utils/package.json packages/adapter-utils/src/types.ts packages/db/src/migrations/0211_famous_guardsmen.sql packages/db/src/migrations/meta/0211_snapshot.json packages/db/src/migrations/meta/_journal.json packages/db/src/schema/completion_contracts.ts packages/db/src/schema/heartbeat_run_events.ts packages/db/src/schema/heartbeat_runs.ts packages/db/src/schema/index.ts packages/db/src/schema/issues.ts packages/db/src/schema/native_run_finalizations.ts packages/db/src/schema/native_run_results.ts packages/db/src/schema/status_decision_effects.ts packages/db/src/schema/status_decisions.ts packages/db/src/schema/work_assessments.ts packages/paperclip-runner/README.md packages/paperclip-runner/docs/design/standalone-thin-paperclip-adapter.md packages/paperclip-runner/docs/index.md packages/paperclip-runner/docs/standalone-thin-paperclip-adapter.md packages/paperclip-runner/docs/tutorials/standalone-thin-paperclip-adapter.md packages/paperclip-runner/.paperclip-local/log.md packages/paperclip-runner/package.json packages/paperclip-runner/spec/fixtures/status-authority-sdk.json packages/paperclip-runner/src/backends/codex-native-backend.ts packages/paperclip-runner/src/backends/harness-driver-backend.test.ts packages/paperclip-runner/src/backends/harness-driver-backend.ts packages/paperclip-runner/src/cli/standalone-paperclip.ts packages/paperclip-runner/src/conformance/control-plane-port.test.ts packages/paperclip-runner/src/conformance/control-plane-port.ts packages/paperclip-runner/src/contracts/control-plane-port.ts packages/paperclip-runner/src/contracts/native-execution.test.ts packages/paperclip-runner/src/contracts/native-execution.ts packages/paperclip-runner/src/contracts/native-session-backend.ts packages/paperclip-runner/src/index.ts packages/paperclip-runner/src/mock-core/mock-control-plane-adapter.ts packages/paperclip-runner/src/native-session-runtime.test.ts packages/paperclip-runner/src/native-session-runtime.ts packages/shared/src/feature-catalog.ts packages/shared/src/index.ts packages/shared/src/types/index.ts packages/shared/src/types/instance.ts packages/shared/src/types/native-finalization.ts packages/shared/src/validators/index.ts packages/shared/src/validators/instance.ts packages/shared/src/validators/native-finalization.ts scripts/check-runner-sdk-spec.mjs server/package.json server/src/__tests__/heartbeat-native-runner-cancellation.test.ts server/src/__tests__/heartbeat-native-runner-selection.test.ts server/src/__tests__/heartbeat-run-event-sequencing.test.ts server/src/__tests__/legacy-finalization-regression.test.ts server/src/__tests__/native-finalization-migration.test.ts server/src/__tests__/native-finalization-recovery.test.ts server/src/__tests__/native-interaction-bridge.test.ts server/src/__tests__/native-run-finalizer.test.ts server/src/__tests__/native-runner-input-boundary.test.ts server/src/__tests__/native-runner-standalone.integration.test.ts server/src/__tests__/native-session-resumption.test.ts server/src/__tests__/native-status-arbiter-corpus.test.ts server/src/services/heartbeat-run-events.ts server/src/services/heartbeat.ts server/src/services/instance-settings.ts server/src/services/issues.ts server/src/services/native-runtime/canonical.ts server/src/services/native-runtime/completion-contracts.ts server/src/services/native-runtime/evidence-classifier.test.ts server/src/services/native-runtime/evidence-classifier.ts server/src/services/native-runtime/index.ts server/src/services/native-runtime/native-execution-input.ts server/src/services/native-runtime/native-finalization-reconciler.ts server/src/services/native-runtime/native-interaction-bridge.ts server/src/services/native-runtime/native-result-ingestion.ts server/src/services/native-runtime/native-run-finalizer.ts server/src/services/native-runtime/native-session-executor.test.ts server/src/services/native-runtime/native-session-executor.ts server/src/services/native-runtime/native-workspace-finalizer.ts server/src/services/native-runtime/paperclip-control-plane-port.test.ts server/src/services/native-runtime/paperclip-control-plane-port.ts server/src/services/native-runtime/runtime-mode.test.ts server/src/services/native-runtime/runtime-mode.ts server/src/services/native-runtime/status-arbiter.test.ts server/src/services/native-runtime/status-arbiter.ts server/src/services/native-runtime/status-decision-committer.ts server/src/services/native-runtime/work-assessments.ts server/src/services/recovery/service.ts ``` ## Commands the implementation must make runnable These commands are the acceptance contract for the implementation issue. They do not exist yet in this design-only task. Deterministic package/mock proof: ```sh pnpm check:runner-sdk-spec pnpm --filter @paperclipai/paperclip-runner exec vitest run \ src/conformance/control-plane-port.test.ts \ src/backends/harness-driver-backend.test.ts pnpm --filter @paperclipai/paperclip-runner trace:standalone -- \ --target mock --scenario happy-path ``` First real Paperclip tracer and inspection (against an isolated local dev instance with the five `PAPERCLIP_*` identifiers/auth variables already set): ```sh pnpm --filter @paperclipai/paperclip-runner trace:standalone -- \ --target paperclip --scenario happy-path PAPERCLIP_API_BASE="${PAPERCLIP_API_URL%/}" PAPERCLIP_API_BASE="${PAPERCLIP_API_BASE%/api}" curl -fsS \ -H "Authorization: Bearer $PAPERCLIP_API_KEY" \ "$PAPERCLIP_API_BASE/api/heartbeat-runs/$PAPERCLIP_RUN_ID/events?after=0&limit=200" \ | jq '[.[] | select(.sourceEventId != null)] | {count: length, events: map({sourceSeq, sourceEventId, eventType})}' ``` Targeted real integration proof: ```sh pnpm --filter @paperclipai/server exec vitest run \ src/__tests__/native-runner-standalone.integration.test.ts \ src/__tests__/heartbeat-native-runner-selection.test.ts \ src/__tests__/heartbeat-native-runner-cancellation.test.ts \ src/__tests__/heartbeat-run-event-sequencing.test.ts \ src/__tests__/native-runner-input-boundary.test.ts \ src/__tests__/native-run-finalizer.test.ts \ src/__tests__/native-status-arbiter-corpus.test.ts \ src/__tests__/native-finalization-recovery.test.ts \ src/__tests__/native-finalization-migration.test.ts \ src/__tests__/legacy-finalization-regression.test.ts pnpm --filter @paperclipai/server exec vitest run \ src/__tests__/heartbeat-run-event-sequencing.test.ts \ -t "serializes concurrent lifecycle cancel native and log writers" pnpm --filter @paperclipai/server exec vitest run \ src/__tests__/native-status-arbiter-corpus.test.ts \ -t "executes all 52 fixtures in their production consumers" ``` Legacy fallback proof after disabling the flag: ```sh pnpm --filter @paperclipai/paperclip-runner trace:standalone -- \ --target paperclip --scenario legacy-fallback pnpm --filter @paperclipai/server exec vitest run \ src/__tests__/native-runner-standalone.integration.test.ts \ -t "uses the unchanged legacy path when the kill switch is off" ``` The tracer must print a stable JSON summary containing `resolvedMode`, `runtimeModeResolverVersion`, `runStatus`, `reportedWorkDisposition`, `nativeResultId`, `nativeResultSha256`, `finalizationPhase`, `assessmentId`, `decisionId`, `authoritativeDecision`, `issueStatusBefore`, `issueStatusAfter`, `statusVersionBefore`, `statusVersionAfter`, `nativeEventCount`, `highestContiguousSourceSeq`, `nextEventSeq`, `workspaceFinalizeStatus`, and `legacyAdapterInvocationCount`. It must never print a bearer token, credential binding value, wake payload, skill instruction, raw environment, or private host path. ## Tutorial outline The implementation fills in the package-local [Standalone tutorial](../tutorials/standalone-thin-paperclip-adapter.md) with: 1. prerequisites and an isolated local instance; 2. mock conformance first; 3. flag and one-agent opt-in; 4. one safe local native task; 5. canonical event replay, persisted mode/result, and coordinator inspection; 6. Section 18 assessment/decision/effect and `status_version` inspection; 7. cancellation, concurrent append, disconnect recovery, and workspace-first finalization checks; 8. explicit credential-boundary canary proof; 9. kill-switch disablement while persisted native recovery still succeeds; 10. a new legacy task proving fallback and zero native rows; 11. cleanup and expected stable summaries. No production credentials or destructive cleanup command belongs in the tutorial. ## Acceptance evidence The package verification run records the git revision, migration revision, exact commands and exit codes, flag/profile scope, and these redacted machine artifacts: - one Section 18.13 result per `{corpusRevision, gitRevision, fixtureId, consumer}` with observed digests and semantic row/effect counts; - migration before/after counts, repaired duplicate event IDs, per-run `next_event_seq`, and unique-constraint verification on production-shaped legacy data; - concurrent-writer attempt order plus final canonical event order and the byte-equivalent replay snapshot after a simulated restart; - typed-input canary inventory and negative package/model/event/result/log/ digest observations (names of forbidden categories, never secret values); - terminal replay/recovery snapshots before ACK, after ACK, after disconnect, with the flag disabled, and after reconciliation; - one safe real Codex trace showing workspace finalize, preserved claim, assessment, applied server decision/effects, and zero legacy invocation; - one new post-kill-switch legacy trace with byte-equivalent legacy projection and zero native contract/result/finalization/decision rows. The evidence index must call out any expected shadow divergence by fixture ID. An unexplained divergence, missing consumer result, duplicate semantic effect, credential-canary hit, or unjoinable aggregate “suite passed” result blocks the implementation handoff. ## Browser UI decision No browser component changes are required for the Standalone tracer. The Section 18 company-authorized read models/routes for completion contracts, native finalization, assessments/decisions, and compact run/issue summaries plus the package tracer provide inspectable evidence. The flag is server/API-only and default off. Rendering those read models in the board UI remains bound to the separately approved Section 18.12 operator UX gate. ## Approval questions The CTO can approve or reject this record by deciding these five points: 1. Is the one-way package dependency and single heartbeat branch narrow enough? 2. Is the default-off instance flag plus per-agent opt-in an acceptable first rollout and kill-switch contract? 3. Is extending `heartbeat_run_events` with the shared per-run allocator and unique canonical sequence preferable to a second event store? 4. Does the restored Section 18 native finalizer/arbitration scope, including the MIG-04/MIG-05 shadow stages and MIG-06 internal application proof, satisfy the normative Standalone gate? 5. Are the allowed files, conformance matrix, and exact tracer/fallback commands sufficient to begin implementation?