From 3a38c8f931284b2feea827e023f4d5b2e0b4a47a Mon Sep 17 00:00:00 2001 From: Dotta Date: Sun, 9 Aug 2026 02:29:18 +0000 Subject: [PATCH] docs(runner): revise phase 6 native integration design Co-Authored-By: Paperclip --- .../design/phase-6-thin-paperclip-adapter.md | 516 ++++++++++++++---- packages/paperclip-runner/docs/index.md | 2 +- .../phase-06-thin-paperclip-adapter.md | 73 ++- .../journal/2026-08-09-phase-06-design.md | 72 ++- packages/paperclip-runner/knowledge/log.md | 9 +- 5 files changed, 534 insertions(+), 138 deletions(-) diff --git a/packages/paperclip-runner/docs/design/phase-6-thin-paperclip-adapter.md b/packages/paperclip-runner/docs/design/phase-6-thin-paperclip-adapter.md index d0915493356..c7d08613d24 100644 --- a/packages/paperclip-runner/docs/design/phase-6-thin-paperclip-adapter.md +++ b/packages/paperclip-runner/docs/design/phase-6-thin-paperclip-adapter.md @@ -2,7 +2,8 @@ Status: proposed for CTO approval Date: 2026-08-09 -Decision scope: the first feature-flagged Paperclip tracer only +Decision scope: the first feature-flagged Paperclip native run, including the +normative native finalizer and server-owned Section 18 status arbitration ## Decision @@ -37,8 +38,11 @@ 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 decision deliberately limits Phase 6 to a tracer. It does not enable -native execution by default, migrate legacy runs, or add browser UI. +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. Phase 6 +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 @@ -88,23 +92,106 @@ immediately before `adapter.execute(...)` today: ```ts const mode = resolveNativeRuntimeMode({ settings, agent, run, issue, target }); -await persistResolvedMode(run.id, mode); +await persistResolvedNativeRunEnvelope({ run, issue, mode, contract, target }); const adapterResult = mode.kind === "native" - ? await paperclipNativeRuntimeAdapter.execute(existingExecutionContext) - : await adapter.execute(existingExecutionContext); + ? await nativeSessionRuntime.execute( + buildNativeExecutionInput({ run, issue, mode, contract, target, session }), + ) + : await adapter.execute(existingLegacyExecutionContext); ``` -Everything before the branch remains Paperclip-owned preparation. Everything -after it remains Paperclip-owned finalization: workspace-finalize barrier, -usage/cost accounting, terminal run write, issue liveness evaluation, session -state, agent state, audit/live events, environment lease release, runtime -service release, and scratch cleanup. +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 -`nativeFinalization` discriminator. Existing adapters omit that field. Native -run terminal state is taken only from that validated discriminator; exit code -is retained as a diagnostic and is never a native fallback heuristic. +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 + +Phase 6 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 @@ -160,13 +247,83 @@ The real `ControlPlanePort` is constructed inside the server with a bound authoritative rows. Every method compares incoming identity fields with that binding and rejects generically before persistence on any mismatch. -The runner or harness never receives: +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): -- `PAPERCLIP_API_KEY`, a board session, or a board API key; +```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. @@ -179,6 +336,7 @@ Threats and required responses: | 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 | @@ -187,7 +345,9 @@ Threats and required responses: PRP events and `reportedWorkDisposition` are reports, not organizational commands. The Phase 6 tracer does not add a runner-accessible issue status API. -- Existing issue routes/services remain the only issue-status mutation path. +- 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 @@ -198,18 +358,28 @@ commands. The Phase 6 tracer does not add a runner-accessible issue status API. - Existing activity logging records flag changes, runtime selection, run terminal state, cancellation, and any later governed mutation. -The native result is persisted with `nativeFinalization` and its reported -disposition. In the first tracer, status application is **shadow-only**: the -result can be inspected and compared, but it cannot close, block, review, or -cancel an issue. Existing liveness/recovery handling observes the unchanged -issue state. Broader rollout requires the separately specified immutable -completion contract, work assessment, status arbiter, atomic liveness effects, -and reconciliation gates in the normative spike specification. The tracer must -not implement a smaller model-authoritative shortcut. +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`. -This is the safest thin boundary: Phase 6 proves execution, durability, and -Paperclip lifecycle integration without smuggling the much larger status- -authority migration into an adapter. +Shadow computation remains mandatory at MIG-04/MIG-05, but it is a rollout +stage, not the Phase 6 scope or exit state. Phase 6 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, Phase 6 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 @@ -223,9 +393,9 @@ authority migration into an adapter. | 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 `nativeFinalization.runTerminalState` | Thin adapter + heartbeat | -| Issue status | No native mutation in tracer; existing authority remains | Existing issue/governance services | -| Approvals/interactions | Existing services only; unsupported native requests fail closed | Existing governance 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 | @@ -253,26 +423,49 @@ source_payload_sha256 protocol_schema_version ``` -Required partial unique indexes: +Required unique indexes (the source identities are partial): ```text -(company_id, run_id, source_event_id) where source_event_id is not null -(company_id, run_id, source_instance_id, source_seq) +(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 ``` -Append behavior is transactional: +Every writer uses one server-owned `appendHeartbeatRunEvent` transaction. +`nextRunEventSeq(max(seq)+1)` is removed. The transaction: -1. validate the PRP schema and bound identity; -2. canonicalize and hash the complete event payload; -3. insert with its server timeline sequence and native source identity; -4. on conflict, load the canonical row and accept only a byte-equivalent hash; -5. compute the highest contiguous committed source sequence; -6. acknowledge only after commit. +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 -server timeline sequence. Legacy rows have null native-source fields and are +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. @@ -322,6 +515,12 @@ Required shared cases: - 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 @@ -341,14 +540,24 @@ Required shared cases: | 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 | Credential redaction | yes | yes | No key in model context, event, log, or digest | -| P6-16 | Reported `done` | yes | yes | Stored as claim; issue status not changed | -| P6-17 | Approval/status payload forgery | yes | yes | No approval/decision/status mutation | -| P6-18 | Unsupported runtime request | yes | yes | Declined; no auto-approval | -| P6-19 | Missing/invalid native finalization | yes | yes | Native run fails with stable code | -| P6-20 | Cost/usage/session projection | yes | yes | Existing field meanings unchanged | -| P6-21 | Activity and live events | — | yes | Selection/terminal/cancel audited once | -| P6-22 | Full legacy targeted regression | — | yes | Byte-equivalent result/events for fixture | +| 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 @@ -356,21 +565,38 @@ Required shared cases: 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. Add nullable native-source columns and partial unique indexes to - `heartbeat_run_events`; generate the migration and prove legacy reads. -4. Implement the server-bound `PaperclipControlPlanePort` with constructor - binding, transaction-safe append/ACK, replay, and terminal idempotency. -5. Add the default-off instance flag, parse the per-agent profile, and test the - pure resolver/kill-switch precedence. -6. Add `nativeFinalization` to `AdapterExecutionResult` and the thin native - executor/converter service. -7. Add the one heartbeat branch, persisted mode, native cancel registry, and - existing-finalizer mapping. Do not rearrange preparation or cleanup. -8. Run mock/real conformance, governance/security, workspace/finalization, - budget/cancellation, and legacy regression tests. -9. Add the package-local tracer/tutorial/evidence and run one safe local Codex - task only after deterministic tests pass. -10. Stop for Security and CTO implementation review before QA or wider opt-in. +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 @@ -402,33 +628,72 @@ packages/paperclip-runner/knowledge/evidence/index.md packages/paperclip-runner/knowledge/log.md ``` -### Minimal Paperclip adapter seam +### 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/index.ts (only if a new export is required) +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) +packages/db/src/migrations/meta/* (generated only) server/src/services/instance-settings.ts -server/src/services/native-runtime/runtime-mode.ts (new) -server/src/services/native-runtime/paperclip-control-plane-port.ts (new) -server/src/services/native-runtime/paperclip-native-runtime-adapter.ts (new) -server/src/services/native-runtime/index.ts (new) +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/__tests__/native-runner-phase6.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/routes/issues.ts +server/src/routes/agents.ts +server/src/routes/openapi.ts +server/src/__tests__/native-runner-phase6.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. In particular, Phase 6 must not change a -concrete legacy adapter, issue status route, approval route, UI file, workspace -service, budget service, or activity-log implementation merely to make the -tracer pass. A discovered need outside this list returns to CTO review. +No other file is pre-approved. The migration owns the universal +`status_version` increment (a database trigger on an actual status change), so +Phase 6 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, Phase 6 must not change a +concrete legacy adapter, approval authority, browser UI, workspace policy, +budget policy, or activity semantics merely to make the tracer pass. ## Commands the implementation must make runnable @@ -438,6 +703,8 @@ do not exist yet in this design-only task. Deterministic package/mock proof: ```sh +pnpm check:runner-phase5-spec + pnpm --filter @paperclipai/paperclip-runner exec vitest run \ src/conformance/control-plane-port.test.ts \ src/backends/harness-driver-backend.test.ts @@ -467,7 +734,22 @@ Targeted real integration proof: pnpm --filter @paperclipai/server exec vitest run \ src/__tests__/native-runner-phase6.integration.test.ts \ src/__tests__/heartbeat-native-runner-selection.test.ts \ - src/__tests__/heartbeat-native-runner-cancellation.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 "emits one result for every Section 18.13 fixture and consumer" ``` Legacy fallback proof after disabling the flag: @@ -482,10 +764,14 @@ pnpm --filter @paperclipai/server exec vitest run \ ``` The tracer must print a stable JSON summary containing `resolvedMode`, -`runStatus`, `reportedWorkDisposition`, `issueStatusBefore`, -`issueStatusAfter`, `nativeEventCount`, `highestContiguousSourceSeq`, -`workspaceFinalizeStatus`, and `legacyAdapterInvocationCount`. It must never -print a bearer token, secret value, raw environment, or private host path. +`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 @@ -496,22 +782,53 @@ The implementation fills in the package-local 2. mock conformance first; 3. flag and one-agent opt-in; 4. one safe local native task; -5. event replay and finalization inspection; -6. cancellation and workspace-finalization checks; -7. kill-switch disablement; -8. a new legacy task proving fallback; -9. cleanup and expected stable summaries. +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. +## Evidence plan + +`knowledge/evidence/2026-08-09-phase-06-verification.md` is the human-readable +index and records git revision, migration revision, exact commands/exit codes, +flag/profile scope, and links to 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 UI changes are required for the Phase 6 tracer. Existing run and -issue APIs plus the package tracer provide inspectable evidence. The flag is -server/API-only and default off. Any later request to expose native mode, -reported disposition, replay cursors, or status-arbitration state in the board -UI requires a separate UX review task before implementation. +No browser component changes are required for the Phase 6 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 @@ -520,9 +837,10 @@ 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` preferable to a second event store for - the tracer? -4. Is shadow-only issue-status handling the correct Phase 6 limit, with full - native arbitration remaining separately gated? +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 Phase 6 gate? 5. Are the allowed files, conformance matrix, and exact tracer/fallback commands sufficient to begin implementation? diff --git a/packages/paperclip-runner/docs/index.md b/packages/paperclip-runner/docs/index.md index 27b702679e2..6c225bfb448 100644 --- a/packages/paperclip-runner/docs/index.md +++ b/packages/paperclip-runner/docs/index.md @@ -13,7 +13,7 @@ Each tutorial starts with an explanation of what the phase is and what the phase - [Phase 4b: run the protocol demo server](tutorials/phase-04b-protocol-server.md) — exercise the server-only Codex boundary, canonical replay, typed controls, and reconnect with `curl`. - [Phase 4b: run the live Codex protocol console](tutorials/phase-04b-live-console.md) — chat with a live session in the browser, steer it, stop it three ways, answer its requests, change its goal, break its connection, and replay the record. - [Phase 5: run the SDK console and mini consumer](tutorials/phase-05-sdk-console.md) — exercise the versioned public browser/React surface in two independent consumers against fake and real drivers. -- [Phase 6: thin Paperclip adapter tutorial outline](tutorials/phase-06-thin-paperclip-adapter.md) — planned mock/real conformance, one local native task, replay/finalization inspection, kill switch, and legacy fallback proof. Design only until CTO approval. +- [Phase 6: thin Paperclip adapter tutorial outline](tutorials/phase-06-thin-paperclip-adapter.md) — planned mock/real conformance, canonical sequencing, durable mode/result recovery, Section 18 arbitration, credential isolation, kill switch, and legacy fallback proof. Design only until CTO approval. - [Cumulative end-to-end tutorial](tutorials/end-to-end.md) — the shortest complete workflow available at the current phase. ## Reference diff --git a/packages/paperclip-runner/docs/tutorials/phase-06-thin-paperclip-adapter.md b/packages/paperclip-runner/docs/tutorials/phase-06-thin-paperclip-adapter.md index c842ee41b4d..606471470c5 100644 --- a/packages/paperclip-runner/docs/tutorials/phase-06-thin-paperclip-adapter.md +++ b/packages/paperclip-runner/docs/tutorials/phase-06-thin-paperclip-adapter.md @@ -11,8 +11,11 @@ runner package still owns session and protocol behavior. ## What the runnable proof will establish The proof will run the same contract against the mock core and a real local -Paperclip service. It will store and replay native events. It will then turn the -flag off and prove that a new task uses the old adapter path. +Paperclip service. It will store and replay native events through one canonical +sequence allocator, preserve the typed terminal result, and apply the complete +Section 18 server-owned finalization/arbitration flow. It will then turn the +flag off, prove persisted native recovery still completes as native, and prove +that a new task uses the old adapter path. ## Planned prerequisites @@ -33,6 +36,8 @@ pnpm --filter @paperclipai/paperclip-runner exec vitest run \ src/conformance/control-plane-port.test.ts \ src/backends/harness-driver-backend.test.ts +pnpm check:runner-phase5-spec + pnpm --filter @paperclipai/paperclip-runner trace:phase6 -- \ --target mock --scenario happy-path ``` @@ -46,11 +51,19 @@ sequence, replay parity, and no Paperclip server import. pnpm --filter @paperclipai/server exec vitest run \ src/__tests__/native-runner-phase6.integration.test.ts \ src/__tests__/heartbeat-native-runner-selection.test.ts \ - src/__tests__/heartbeat-native-runner-cancellation.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 ``` Expected: company/auth denials, workspace/finalization ordering, cancellation, -budget, audit, event replay, status non-authority, and legacy regression pass. +budget, audit, canonical event sequencing, terminal replay, typed input +isolation, Section 18 arbitration, and legacy regression pass. ### 3. Enable one isolated agent @@ -76,11 +89,17 @@ nativeEventCount>0 highestContiguousSourceSeq=nativeEventCount workspaceFinalizeStatus=succeeded legacyAdapterInvocationCount=0 -issueStatusAfter=issueStatusBefore +reportedWorkDisposition=done +authoritativeDecision=done +issueStatusBefore=in_progress +issueStatusAfter=done +statusVersionAfter=statusVersionBefore+1 ``` -The last equality proves that the runner report did not seize issue-status -authority. +The separate claim and decision fields prove that the runner report did not +seize issue-status authority; the server-owned arbiter applied the transition +only after the completion contract, evidence, workspace barrier, and legal +liveness effects passed. ### 5. Inspect persistence, replay, and finalization @@ -93,17 +112,35 @@ curl -fsS \ | jq '[.[] | select(.sourceEventId != null)] | {count: length, events: map({sourceSeq, sourceEventId, eventType})}' ``` -Repeat the read with an `after` cursor. Expected: no duplicate semantic event, -stable IDs, increasing source sequence, and a persisted workspace-finalize -barrier before clean run success. +Repeat the read with an `after` cursor and inspect the run finalization and +issue status-decision read models. Expected: no duplicate semantic event, +stable IDs, unique canonical `seq`, increasing source sequence, a persisted +`native_run_results` digest, a committed coordinator/decision, and a workspace- +finalize barrier before assessment or clean run success. ### 6. Prove cancellation Run the deterministic cancellation scenario through the focused server test. -Expected: one native cancel effect, one terminal run outcome, unchanged issue -status, and the normal environment/runtime/scratch cleanup path. +Expected: one native cancel effect, one terminal run outcome, only the +server-authorized cancellation/resume scope from Section 18, and the normal +environment/runtime/scratch cleanup path. The focused concurrency case also +appends lifecycle, cancellation, native, and log events at once and proves one +unique replay-stable `(run_id, seq)` order. -### 7. Disable the kill switch and prove fallback +### 7. Prove credential and recovery boundaries + +Run the typed-input boundary test with unique canaries in the local agent JWT, +Paperclip API key, managed MCP gateway credentials, wake payload, skill +instructions, raw env, and legacy context. Expected: no canary key/value in the +package launch input, model request, event, terminal result, log, or digest. + +Then persist a native terminal result, simulate process/server disconnect, and +disable the flag before reconciliation. Expected: recovery reads the persisted +`heartbeat_runs.runtime_mode`, `native_run_finalizations.result_id`, and +`native_run_results` row and completes without re-running selection or invoking +the legacy adapter. + +### 8. Disable the kill switch and prove fallback Set `experimental.enableNativeRunner=false` through the existing board- authorized settings API, then run a fresh task: @@ -125,7 +162,7 @@ nativeEventCount=0 legacyAdapterInvocationCount=1 ``` -### 8. Record evidence and clean up +### 9. Record evidence and clean up The implementation record will save only redacted summaries and test results under `knowledge/evidence/`. It will restore the test agent profile and leave @@ -134,6 +171,8 @@ the global flag off. It will not delete a shared database or workspace. ## Stop conditions Stop and fail the proof if mode changes during a run, a native failure invokes -the legacy adapter, a credential appears in output, a workspace-finalization -failure reports success, a PRP claim changes issue status, or replay changes a -canonical event. +the legacy adapter, a forbidden legacy-context field or credential reaches the +package/model, a workspace-finalization failure reports success, a PRP claim +bypasses the arbiter, a non-terminal status lacks its atomic liveness path, a +concurrent writer duplicates canonical `seq`, or replay changes canonical +event/result/effect bytes. diff --git a/packages/paperclip-runner/knowledge/journal/2026-08-09-phase-06-design.md b/packages/paperclip-runner/knowledge/journal/2026-08-09-phase-06-design.md index 14e6ccee1cb..533c623c3d5 100644 --- a/packages/paperclip-runner/knowledge/journal/2026-08-09-phase-06-design.md +++ b/packages/paperclip-runner/knowledge/journal/2026-08-09-phase-06-design.md @@ -28,16 +28,24 @@ reversible tracer. 4. Disabling the instance flag is the new-run kill switch. A selected native run fails or recovers as native and never silently retries through legacy. 5. Native events extend the existing heartbeat event timeline with nullable - source identity, sequence, digest, and schema fields plus partial unique - indexes. Acknowledgement occurs only after commit; conflicting replay fails - closed. -6. Runner/model data has no company, approval, interaction, budget, or issue- - status authority. The tracer stores reported disposition in shadow-only - form; full automatic status arbitration remains separately gated. -7. A package-exported conformance suite runs unchanged against the mock and + source identity, sequence, digest, and schema fields. All writers share a + per-run transactional allocator backed by `heartbeat_runs.next_event_seq` + and unique `(run_id, seq)`; acknowledgement occurs only after commit. +6. First-class `heartbeat_runs.runtime_mode` fields, `native_run_results`, and + `native_run_finalizations` make selection and terminal recovery independent + of the current flag, agent profile, or producer process. +7. Runner/model data has no company, approval, interaction, budget, or issue- + status authority. A strict native input allowlist excludes local JWT/API + keys, managed MCP credentials, wake/skill context, raw env, and every other + legacy-only execution field. +8. The additive native finalizer applies the complete Section 18 contract: + immutable contract/result/assessment, evidence classification, pure arbiter, + CAS status/effect commit, native interaction bridge, and reconciliation. + Shadow comparison is only MIG-04/MIG-05; Phase 6 proves MIG-06 application. +9. A package-exported conformance suite runs unchanged against the mock and database-backed real ports. Real Codex is a smoke proof, not the deterministic authority. -8. No browser UI is required. A later native-status UI needs a separate UX +10. No browser UI is required. A later native-status UI needs a separate UX review before implementation. The complete decision record is @@ -54,7 +62,8 @@ The complete decision record is - Confirmed `heartbeat_run_events` is the existing company/run-scoped operator timeline and identified the missing source identity/dedup columns required for durable PRP acknowledgement and replay. -- Recorded the proposed files, 22-case test matrix, exact tracer commands, +- Recorded the revised storage/finalizer file allowlist, 32-case test matrix, + allocator/credential/recovery commands, legacy fallback proof, and tutorial outline in package-local documentation. - Design-only verification command: `pnpm --filter @paperclipai/paperclip-runner docs:validate`. @@ -73,18 +82,47 @@ that implementation must resolve explicitly rather than hide: - The Phase 6 tracer does not expose a public runner WebSocket. - It supports only an issue-bound local `codex_local` native profile. -- It declines unsupported native runtime requests and cannot approve governance - actions. -- It does not apply model-reported issue status. Full completion-contract and - status-arbiter rollout remains subject to its existing migration gates. +- It materializes only strictly typed supported interaction requests through + existing governance services and cannot approve governance actions. +- It applies only server-owned Section 18 decisions; model-reported issue + status remains advisory. - Exact API request examples for toggling the flag depend on the local instance auth mode and will be filled in after implementation proves both modes. # Follow-up questions -- CTO: approve extending `heartbeat_run_events` for the tracer, or require a - dedicated native event table now? -- CTO: approve shadow-only issue status for the thin tracer, or expand Phase 6 - into the separately specified status-authority implementation? +- CTO: approve extending `heartbeat_run_events` with the shared per-run + allocator, or require a dedicated native event table now? +- CTO: approve the restored complete Section 18 finalization/arbitration scope, + with shadow comparison only as its MIG-04/MIG-05 rollout stage? - Security: is the in-process, server-bound port sufficient for Phase 6 before a one-time runner lease and outbound WebSocket are introduced? + +# Revision verification + +Self-review against the design-revision gate completed on 2026-08-09: + +- restored typed native finalization and complete Section 18 server-owned + arbitration; shadow comparison is only MIG-04/MIG-05 and the Phase 6 proof + proceeds through the MIG-06 internal canary; +- named every first-class run-mode, contract, result, coordinator, assessment, + decision, effect, issue-version, and event-sequence field plus restart/flag- + off/process-loss recovery reads and canonical byte-equivalence rules; +- replaced source-dedup-only ordering with the shared row-locked per-run + allocator, unique `(run_id, seq)`, deterministic migration repair/backfill, + and concurrent lifecycle/cancel/native/log cases; +- replaced generic legacy context handoff with strict native/package/model + allowlists and explicit negative coverage for JWT/API/MCP credentials, + wake/skill content, raw env, and legacy-only context; +- revised the file allowlist, implementation order, 32-case matrix, runnable + commands, tutorial, and evidence artifact plan; +- confirmed documentation-only scope with `git diff --check`; no implementation + source was added by this revision. + +Required validation passed: + +```text +pnpm --filter @paperclipai/paperclip-runner docs:validate +Documentation link validation passed (55 files). +OKF v0.2 validation passed (23 concepts, 4 indexes). +``` diff --git a/packages/paperclip-runner/knowledge/log.md b/packages/paperclip-runner/knowledge/log.md index 88bb44469b4..a7f16c06a14 100644 --- a/packages/paperclip-runner/knowledge/log.md +++ b/packages/paperclip-runner/knowledge/log.md @@ -36,7 +36,8 @@ ## 2026-08-09 * **Phase 6 integration design**: Specified the default-off thin Paperclip - adapter seam, public port reconciliation, persisted runtime selection and - kill switch, native event deduplication/replay, company/auth/governance threat - boundary, shadow-only status contract, exact file allowlist, conformance test - matrix, and mock/native/legacy tutorial commands before production code. + adapter seam, public port reconciliation, first-class persisted mode/result + recovery, canonical per-run event allocation, typed credential-free native + input, complete Section 18 finalization/arbitration, kill switch, exact file + allowlist, conformance matrix, and mock/native/legacy tutorial commands before + production code.