feat(trace): add Pi provider support

Merged
WA
1d ago
+1819-123

Summary

Completes Pi support for Chainloop Trace under issue #3520 and its approved spec:

  • registers Pi in the shared provider registry and init wizard;
  • adds trace init --pi and trace run --pi while preserving Claude Code as the no-flag default and existing multi-provider behavior;
  • owns the project-local .pi/extensions/chainloop-trace.ts lifecycle, including safe upgrades, collision and symlink checks, uninstall, rollback, and trace-run restoration;
  • bridges Pi session, prompt, tree, write, edit, and bash events into the existing Trace hook pipeline;
  • maps Pi toolCallId to the existing tool_use_id correlation key for concurrent file snapshots;
  • bounds direct child processes with per-stream output caps, timeout, SIGTERM grace, SIGKILL fallback, and failure absorption;
  • completes the Pi E2E adapter, generated CLI reference, provider guide, compatibility matrix, and project-trust guidance; and
  • supports the approved Pi 0.80.10 JSONL v3 baseline in both TUI and JSON-headless operation.

Together with merged provider/parser PR #3547, this covers R-001 through R-009. This PR directly implements R-001, R-002, R-003, R-004, R-008, and R-009, plus the extension-side transcript-path behavior in R-005.

Compatibility and safety

  • Claude Code remains the default provider when no provider flag is selected.
  • Existing Claude Code, Cursor, and OpenCode behavior is unchanged except for the approved shared file-snapshot correlation change when a provider supplies ToolUseID; events without one retain the existing fallback key.
  • Pi project trust is not bypassed. Interactive Pi asks before loading project files, and non-interactive Pi needs saved trust or --approve.
  • The extension executes chainloop directly, never through a shell, and runs each child from ctx.cwd.
  • Spawn errors, malformed responses, output overflow, timeouts, and non-zero exits are absorbed without blocking Pi tools or corrupting stdout.
  • No control-plane API, protobuf, evidence schema, or attestation schema changes are included.
  • The merged behavior from #3547 keeps the last usable private Pi transcript when a refresh fails. That accepted behavior differs from the stale invalidation wording in R-005 and is preserved here.

Verification

  • go test -race ./cmd ./internal/trace/providers ./internal/trace/e2e ./internal/trace/pi ./pkg/action with CHAINLOOP_TOKEN and CODEX_HOME unset
  • full make -C app/cli test passes every package except the current-main Codex storage-root tests; the identical transcript outside Codex storage failure was reproduced on untouched upstream 72bc01d9 in the same environment
  • go build ./... and go vet ./... from app/cli
  • golangci-lint v2.14.0 run --new-from-rev upstream/main: 0 issues
  • go generate ./documentation, with no stale generated CLI reference
  • LSP diagnostics: no errors in the changed command, provider, Pi, or E2E packages
  • compiled CLI/manual QA: init/run help, default selection, multi-provider selection, install, repeat init, uninstall, and trust guidance
  • Pi 1.3.0 live E2E matrix with openrouter/anthropic/claude-haiku-5.5: 25 checks passed; unsupported and control-plane-only cells remain No or Unknown
  • Pi 0.80.10 targeted TUI and JSON-headless smokes: session start, write, edit, bash, commit attribution, and session shutdown all observed; TUI banner observed
  • git merge-tree --write-tree HEAD upstream/main: clean
  • final immutable gate review: APPROVE

Pi 1.3.0 matrix result: /tmp/pr3704-postmerge-pi-e2e-results/runs/20261023T135451Z.json.

Provider checklist

Provider package (app/cli/internal/trace/pi/)

  • Name constant and var _ trace.Provider = (*Provider)(nil).
  • InstallHooks: a second call changes nothing and user files stay intact.
  • InstallHooksForTraceRun: all hooks except session end.
  • SettingsFile: returns the installed extension path.
  • UninstallHooks: removes only the marked Chainloop extension.
  • HooksInstalled: true only for the marked Chainloop extension.
  • Hook events: session start, user prompt, selected session shutdown reasons, and session-tree changes.
  • Hook events: before and after Pi write, edit, and bash; Pi skills tracking is intentionally unsupported and documented.
  • ReadHookInput: typed, size-limited input with absolute paths, cwd, transcript path, tool-use ID, command, version, and model fields where available.
  • IsFileWritingTool and IsCommandTool: Go slices generate the extension tool lists.
  • Snapshot attribution strategy implemented and cleaned up.
  • Shell tool sends paired hooks with the same ToolUseID.
  • CopySessionData: repeatable, path-checked, atomic, and non-blocking for missing sessions.
  • ParseSession: reads only the Trace-owned copy.
  • Copy and parse paths require no control plane or network.
  • Trace state flows only through *state.Store, including trace run --local outside a repository.
  • Evidence includes agent identity, session timing, model/provider, and models used.
  • Evidence includes input, output, total, cache-read, and cache-write tokens.
  • Evidence uses Pi's persisted cost.
  • Evidence includes tools, conversation counts, and documented root-session-only behavior.
  • RawSession["main"] is non-nil and contains JSON objects.
  • Invalid or missing optional data produces sanitized warnings.
  • Session-start, prompt-submit, and post-tool channels emit one document or no output.
  • Supports* methods match Pi UI and headless behavior.
  • AnnounceToUser uses Pi UI or stderr as appropriate.
  • Optional skill, spec-copy, and pasted-image interfaces were evaluated; unsupported features are documented as No.
  • Unit coverage includes hooks, parsing, messages, copies, concurrency, failure bounds, and legacy recovery.

CLI wiring

  • Registered in providers/registry.go.
  • traceProviderFlags field, any(), and names().
  • --pi in trace init, wizard/help, and trace run.
  • Existing chainloop trace hook pi ... subcommands retain skipActionOptsInit.
  • Pi pre-tool handlers always return undefined and never block tools.
  • providers/capabilities_test.go updated.
  • Init provider, default, multiple-provider, next-step, and local-mode tests updated.
  • Generated CLI reference refreshed.

End-to-end tests and documentation

  • e2e/pi.go implements e2e.Harness.
  • Pi is registered in the E2E harness map.
  • Pi 1.3.0 and its OpenRouter model are pinned in e2e/config.yaml.
  • The Pi column was run and its results reviewed.
  • README.md has a Pi cell in every feature table with measured values.
  • Every non-Yes Pi cell has an explanation.
  • The hook table and provider list include Pi.
  • The approved spec is present and requirement IDs are cited here.
  • Changed source files retain the 2026 license header.
  • Changed-code lint, affected race tests, build, vet, generated docs, manual QA, and live E2E pass; the full CLI suite's only failure is the identical pre-existing Codex storage-root failure reproduced on current main.

Refs #3520

AI assistance

Vibhav's Pi assisted with implementation, testing, and review. Every affected commit carries Assisted-by: Vibhav's Pi and a DCO sign-off, and is GPG-signed.