mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-27 02:44:50 +01:00
docs(runner): revise phase 6 native integration design
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
f8a584ac9e
commit
3a38c8f931
5 files changed
+534
-138
No files matched your search
@@ -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/<generated>.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?
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
@@ -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).
|
||||
```
|
||||
@@ -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.
|
||||
Reference in new issue
Block a user