paperclip/doc/run-log-events.md

7.8 KiB

Run-Log Events

Run-log events write to the heartbeat_run_events table (packages/db/src/schema/heartbeat_run_events.ts:6-20). They are not Paperclip Telemetry events, and they are not OpenTelemetry exports. A run-log event needs no operator endpoint.

Native PRP Run-Log Events

The hidden native coordinator writes each validated PRP event to the bound run's existing event stream before it acknowledges the runner. The row keeps the PRP eventType, source instance, source event ID, source sequence, protocol schema version, and a SHA-256 digest of the canonical source envelope. Its payload is { "prpEvent": <canonical PRP event> }.

The writer locks the native heartbeat_runs row and allocates the existing per-run seq cursor. A byte-equivalent retry reuses the first row; a changed retry or source-sequence gap is rejected. Company, issue, agent, run, session, and runner-source bindings must match the persisted native run. Bootstrap tickets, reconnect leases, authentication proofs, encryption keys, and raw credential material are never written to the run log.

These records remain run-log events. They do not create an OpenTelemetry or Paperclip Telemetry export, and legacy adapters do not use this writer.

Native Restart Recovery Run-Log Event

Paperclip writes a native.recovery.transition event for every native restart classification and for graceful restart suspension. This immutable run-log record lets operators reconstruct recovery decisions without exporting data to Paperclip Telemetry or OpenTelemetry.

The payload contains the restart kind, recovery request id when one exists, runner disposition, and the controller generation and provider attempt for a claimed recovery. Live-runner adoption also records the runner PID, process group, and process-start fingerprint. A non-claim disposition records a bounded reason instead. Graceful suspension records the signal and confirms that it did not create a retry run.

The event never includes bootstrap tickets, reconnect leases, authentication proofs, encryption keys, environment variables, provider credentials, command arguments, or an unsanitized stderr stream. Detailed failed-attempt diagnostics remain in the bounded native_run_finalizations.recovery_history ledger.

Sandbox Startup Run-Log Event

Paperclip writes one run.startup.step event to the run log for each bring-up step. This event is a run-log record, not a first-party telemetry event. The generated telemetry contract does not cover it, so this section is its canonical contract.

The event payload carries only three fields.

Field Type Meaning
step string The bring-up step name, for example stage.sync.
durationMs number The wall time of the step. A skipped step reports 0.
outcome string The step outcome (ok, skipped, or failed).

The event no longer carries the per-step round-trip count or the provider duration fields. It dropped roundTrips, providerExecMs, providerGetMs, createRuntimeMs, and ensureSessionMs. The startup spans in doc/observability.md carry that detail now. The sandbox.exec child spans hold the round-trip and provider durations. The acp.handshake step span holds the create-runtime and ensure-session sub-times.

To read the detailed timing, use the startup spans. The spans need an OTLP endpoint. A run with no endpoint keeps only the three run-log fields above.

Run Phase Timing Run-Log Event

Paperclip writes one run.phase.timing event to the run log for each run-lifecycle phase. This event is a run-log record, not a first-party telemetry event. The generated telemetry contract does not cover it, so this section is its canonical contract. The producer is emitRunPhaseTiming in packages/adapter-utils/src/acpx-engine/startup-timing.ts.

The event payload carries only three fields.

Field Type Meaning
phase string The run-lifecycle phase name from the closed allowlist below.
durationMs number The wall time of the phase. A negative or a non-finite value clamps to 0.
outcome string The phase outcome (ok or failed).

The phase field is one member of a closed, low-cardinality allowlist. The producer drops any event whose phase name is outside this allowlist, so a free-form label never reaches the run log. The allowlist has twelve phase names.

Phase Meaning
place_workspace Place the run workspace.
start_transport Start the agent transport.
create_runtime Create the agent runtime.
ensure_session Ensure the agent session exists.
configure_session Configure the agent session.
prepare_turn Prepare the turn.
turn Run the turn.
end_session End the agent session.
settle_reuse Settle the session for reuse.
stop_transport Stop the agent transport.
sync_back Sync the workspace back.
release_staging_lease Release the staging lease.

The payload never carries a command, an argument, a path, an environment value, or a raw identifier. The event rides the ctx.onEvent run-event bridge and is run-log-only. It needs no OTLP endpoint.

Sandbox performance batches

When full sandbox diagnostics are enabled by OTEL_EXPORTER_OTLP_ENDPOINT, the host writes sandbox.performance.batch system events after measured execution. These rows stay in the instance database; they are neither first-party Telemetry events nor a replacement for inspecting actual OpenTelemetry exports. No batch is produced with the endpoint unset.

The payload schema is paperclip.sandbox-performance.v1, with a hashed runHash, records (at most 250 per event), and the run's dropped count. Each record contains a fixed operation name, span id, optional parent/trace id, start time, duration, operation outcome, and closed safe attributes. Host times use epoch milliseconds and monotonic durations. Records with clock: "remote_relative" instead contain offsets from a remote command's start and must not be placed on the host timeline as absolute timestamps. The root has numeric retained-record and dropped-record counts.

The default buffer holds 20,000 records per run; additional records increment the drop count. A missing root or a nonzero drop count means the local record set is incomplete. Event persistence happens in bounded batches after the operation timings end, not synchronously for each file. A sink failure stops batch persistence without changing the original task outcome. A successful task alone therefore does not prove that all timing records were saved.

The sandbox duplex transport also writes one run-log event as one of its three sinks. See the Sandbox Duplex Transport Instrumentation section in the Observability contract.

Native Process Rotation

native.session.process_rotation records a controller-initiated close of a settled warm session before opening the next run. Its system-stream payload contains reason (run_scoped_github_capability or configuration_changed), previousRunId (nullable), runId, companyId, agentId, nativeSessionId, and runnerInstanceId. It contains no token, environment, path, or credential value. The event records rotation intent after the prior process closes; the new run must still succeed to establish successful continuation.

A subsequent run uses a fresh process for its own GitHub capability while preserving the durable conversation. Warm qualification accepts a changed process fingerprint only with a matching system rotation event for that exact run transition and a process start inside the new run. Unexpected restarts, configuration changes, and conversation, runner-instance or sandbox changes remain failures. Same-process warm reuse remains required without a rotation.