# open traces > Open-source CLI for repo-local agent trace capture, dataset authoring, and publication to Hugging Face Hub. Local bucket store with optional private-remote sync, VCS-anchored Trace Trails, executable dataset workflows, and a structured JSONL schema. ## Links - Documentation: https://opentraces.ai/docs - GitHub: https://github.com/JayFarei/opentraces - Explorer: https://opentraces.ai/explorer - Schema: https://opentraces.ai/schema ## Full Documentation --- # opentraces Open schema + CLI for capturing agent traces into a private bucket, searching and slicing them locally, and publishing workflow-projected dataset rows to Hugging Face Hub. opentraces has four separate surfaces: 1. **Trace workflow:** capture agent sessions into a portable bucket, then use Trace Discovery, Trace Trails, and Context Tree to inspect the retained environment. 2. **Dataset workflows:** skill-format row builders that use trace discovery, context, and trail evidence to project purposeful rows. 3. **Datasets:** local HuggingFace-shaped row stores that can be reviewed, scheduled, and published. 4. **Clients:** evaluation jobs, training loops, dashboards, context warmup, and manual trace-capsule patterns that consume either rows or bucket evidence. ## Trace Workflow ```bash opentraces setup # install capture hooks and optional integrations opentraces init # enroll a repo explicitly (optional under global tracking) opentraces init --agent pi # enroll Pi explicitly (optional; auto under global tracking) opentraces status # fleet-wide bucket safety dashboard (safe-to-sync verdict) opentraces bucket list # bounded, paginated per-trace inventory opentraces trace query --since 7d # search retained traces opentraces trace query --skill opentraces --json # rank observed skills by usage opentraces trace map --bursts # deterministic edit/intent map opentraces trace get --run-intel # resteer/recovery/loop signals opentraces trace slice --by change-burst opentraces trail blame commit # which traces contributed to a commit opentraces ctx # what the agent saw across the trace opentraces workflow templates # choose a row projection template opentraces dataset new my-dataset --workflow my-workflow opentraces dataset run my-dataset # synthesize dataset rows from retained traces opentraces dataset new skill-episodes --from-skill opentraces opentraces dataset run skill-episodes --executor script --json opentraces dataset review approve my-dataset --all opentraces dataset publish my-dataset # upload reviewed rows to the active remote ``` `init` writes the committable project marker at `.opentraces.json`. Captured traces, bucket state, and upload bookkeeping stay machine-local under `~/.opentraces/`. Global tracking (the default) auto-enrolls every agent, including Pi, the first time a capture hook or the Pi extension fires in a repo, private + review-required. Capture is opt-out: `opentraces config tracking-mode manual` or a per-project `excluded` marker turns it off. ## What You Get **For individual developers.** A private trace bucket, deterministic local search, and dataset publishing only when you choose to project and approve rows. **For teams.** Shared bucket remotes for retained evidence, per-dataset HuggingFace remotes for curated rows, and reproducible workflow templates. **For dataset consumers.** Schema-valid row streams for training, evaluation, teacher/student reinforcement learning, analytics, and attribution. ## Start Here | Section | What's inside | |---------|---------------| | **[Installation](/docs/getting-started/installation)** | Install, verify, upgrade, uninstall | | **[Authentication](/docs/getting-started/authentication)** | OAuth, PATs, `HF_TOKEN`, auth precedence | | **[Quick Start](/docs/getting-started/quickstart)** | Capture into a bucket, search traces, build and publish a dataset | | **[Commands](/docs/cli/commands)** | Current `opentraces` command reference | | **[Portable Bucket](/docs/workflow/bucket)** | Raw trace envelopes, companions, manifests, sync, replay | | **[Trace Discovery](/docs/workflow/trace-discovery)** | `trace query` (including `--skill`), `trace get`, `trace map`, `trace slice`, plus `--waste` / `--run-intel`; the Trace Index (BM25 + semantic) self-maintains behind `query` | | **[Trace Trails](/docs/workflow/blame)** | Git anchors, survival states, blame, graph, PR body generation | | **[Context Tree](/docs/workflow/context-tree)** | the bare-noun `ctx [:|:last]` read and OTLP capture for what the agent saw | | **[Dataset Workflows](/docs/workflow/workflow-templates)** | Build deterministic row projections from bucket traces | | **[Dataset Rows](/docs/workflow/datasets)** | Local HF-shaped datasets, review states, remotes, schedules | | **[Publish](/docs/workflow/pushing)** | Publication gates, shards, visibility, bucket-vs-dataset split | | **[Security Tools](/docs/security/tiers)** | Optional default-off security/privacy tool registry | | **[Schema](/docs/schema/overview)** | `TraceRecord` and schema `0.9.0` field semantics | | **[Clients](/docs/clients/overview)** | How consumers use rows or bucket evidence | | **[Agent Workflows](/docs/clients/agent-workflows)** | Context warmup and progressive session-history discovery | | **[Trace Capsule](/docs/clients/trace-capsule)** | Share a single trace as a reproducible capsule; use `opentraces capsule` for the full CLI surface | --- # Installation ## pipx ```bash pipx install opentraces ``` ## brew ```bash brew install JayFarei/opentraces/opentraces ``` ## skills.sh ```bash npx skills add jayfarei/opentraces ``` Installs the opentraces skill via [skills.sh](https://skills.sh) so your coding agent can drive the init, review, and push workflow conversationally. `opentraces init` also installs the bundled skill into the current project. ## Copy to your agent Paste this into your coding agent (Claude Code, Codex CLI, or Pi): ``` {{AGENT_PROMPT}} ``` The agent runs this as an interview: it asks you how to configure opentraces, one decision at a time, and waits for your answer before applying it. You choose the tracking mode (global auto-enroll vs manual per-project opt-in), which capture hooks to install, whether to install the shared skill, whether to authenticate with Hugging Face now, and any optional security passes. The prompt is tool-agnostic, so agents with a structured question UI render it as choices while others simply ask in chat. After hook or skill installation, start a fresh agent session before expecting the new capture hooks or skill to be available. ## From Source ```bash git clone https://github.com/JayFarei/opentraces cd opentraces python3 -m venv .venv source .venv/bin/activate pip install -e packages/opentraces-schema pip install -e ".[dev]" ``` ## Verify Installation ```bash opentraces --version opentraces --help ``` ## System Requirements | Platform | Status | |----------|--------| | macOS (ARM64, x86_64) | Supported | | Linux (x86_64, ARM64) | Supported | | Windows (WSL) | Supported via Linux binary | Python 3.10 or later is required. ## Upgrading From inside an initialized project, the preferred path is: ```bash opentraces upgrade ``` This detects whether you installed via `pipx`, Homebrew, pip, or source, upgrades the CLI, re-renders every installed integration glue file (watcher shim, git post-commit hook, Claude Code and Codex CLI hooks, OTLP settings and autostart) re-stamped to the new version, and refreshes the project skill and hook. Use `opentraces upgrade --integrations-only` to re-render the installed glue without a CLI bump, or `--skill-only` to refresh just the skill and hook. (`opentraces setup upgrade` still works as a hidden alias.) Outside a project context, upgrade with the package manager you originally used: ```bash pipx upgrade opentraces # or brew upgrade JayFarei/opentraces/opentraces # or pip install --upgrade opentraces ``` ## Uninstalling `opentraces uninstall` is the symmetric inverse of `setup` — one command that reverses the whole multi-surface install (capture hooks, the OTLP receiver + its `~/.claude/settings.json` env keys, the watcher daemon, the skill, shell completions, per-repo git post-commit hooks, security-tool flags) and stops every opentraces process. It is data-safe by default — prefer it over `rm -rf ~/.opentraces`, which leaves hooks, daemons, git refs, and completions behind. (`opentraces setup uninstall` still works as a hidden alias.) ```bash opentraces uninstall --dry-run # recommended first run: prints the plan, changes nothing opentraces uninstall # default: reverse every install-time patch + daemon, PRESERVE all captured data ``` The default (`--integrations-only`) tier preserves every captured trace, dataset, bucket, and Git ref (`refs/opentraces/*`, `refs/notes/opentraces`); you can re-`setup` later and pick up where you left off. To also delete the captured data: ```bash opentraces uninstall --purge # ALSO delete captured data + git refs — UNRECOVERABLE (typed confirmation, or --yes) ``` `--purge` deletes the captured corpus (bucket, datasets, projects, staging) and the opentraces Git refs in one shot — both the canonical Trail event log and its only local replay source (the bucket) — so it requires a typed confirmation. A configured remote bucket is not deleted (local-only teardown) and is reported in the residue summary. Finally, remove the package itself with the command for your install method (printed by `uninstall`): ```bash pipx uninstall opentraces # or brew uninstall jayfarei/opentraces/opentraces # or pip uninstall opentraces ``` Your HuggingFace login (`~/.cache/huggingface/token`) is never touched by uninstall. --- # Authentication opentraces publishes to HuggingFace Hub. You need an HF account. Auth and sync are separate from egress security. What a dataset's rows are allowed to carry before publication is governed by that dataset's manifest policy (`opentraces dataset security `), distinct from bucket auth and sync. ## Browser Login ```bash opentraces auth login ``` By default, `opentraces auth login` starts Hugging Face's OAuth device flow in your browser. This is the simplest path on a developer machine and supports the normal dataset workflow. The CLI requests the scopes it needs to read, create, push, delete, and change visibility on datasets in namespaces you belong to. When a coding agent is driving setup for you, use a bounded wait so the session does not hang if you cannot complete the browser step: ```bash opentraces auth login --device-timeout 180 ``` If that times out, complete auth outside the agent session with `opentraces auth login`, `opentraces auth login --token`, or `HF_TOKEN`, then verify with `opentraces --json auth whoami`. ## Token Login ```bash opentraces auth login --token ``` Use a personal access token when you are headless, on CI, or cannot complete the browser flow. Generate the token at [huggingface.co/settings/tokens](https://huggingface.co/settings/tokens). ## Environment Variable ```bash export HF_TOKEN=hf_... ``` The CLI checks for `HF_TOKEN` automatically. Useful in CI pipelines where interactive login isn't available. ## Auth Precedence 1. `HF_TOKEN` environment variable 2. Stored credentials from `opentraces auth login` ## Verify ```bash opentraces auth whoami ``` Shows your authenticated HuggingFace username. ## Logout ```bash opentraces auth logout ``` Clears stored credentials from `~/.opentraces/credentials`. --- # Quick Start From local capture to a published Hugging Face dataset row stream. ## 1. Install ```bash pipx install opentraces ``` ## 2. Set Up The Machine ```bash opentraces setup ``` `setup` is the machine-wide wizard. It can configure: - **tracking mode** (`global` by default), so Claude/Codex sessions can auto-enroll projects private + review-required the first time capture fires. Pi remains explicit-consent per repo. - **capture hooks** for Claude Code and Codex CLI, plus Pi package checks. - **shared agent skill install**, which links the opentraces skill into supported harnesses such as Claude Code, Codex CLI, and Pi. - **git hook** and **watcher**, which mature Trace Trails after commits land. - **bucket remote**, optional private HuggingFace sync for raw retained evidence, configured with `bucket connect`. It requires `opentraces auth login` first and prompts for a bucket security policy before configuring remote sync. - **HuggingFace login**, needed for bucket sync and dataset remotes. - **optional security tools**, such as TruffleHog, privacy-filter, and LLM review. Per-record tools default off until a workflow or config enables them. You can run specific setup commands non-interactively: ```bash opentraces setup claude-code opentraces setup codex-cli opentraces setup pi opentraces setup git opentraces bucket connect opentraces setup capture-otlp opentraces setup skill opentraces setup trufflehog opentraces setup privacy-filter ``` For Codex, install and authenticate Codex CLI first. `setup codex-cli` wires terminal Codex CLI hooks only; it does not cover Codex Desktop. For Pi, install `opentraces-pi` with `pi install npm:opentraces-pi`; `setup pi --dry-run --json` shows the local package/checklist plan without doing Python/service/auth setup. Inside Pi, `/ot-capture-status` and `/ot-setup` expose the same local-first checklist. ## 3. Enroll A Project Under global tracking (the default) this is optional for every agent, including Pi, which auto-enrolls each project on first capture. Running `init` explicitly is still useful when you want to import existing sessions, be explicit about the connected agent, or are running in `manual` tracking mode. ```bash opentraces init opentraces init --agent claude-code --import-existing opentraces init --agent codex-cli opentraces init --agent pi ``` `init` writes `.opentraces.json` and registers machine-local state under `~/.opentraces/`. `--import-existing` currently backfills Claude Code sessions for the current repo. Codex CLI and Pi capture start with sessions run after their runtime setup and project enrollment are in place. ## 4. Inspect The Portable Bucket Captured traces land in the private bucket first. This is not a public dataset. ```bash opentraces bucket status opentraces bucket list --json opentraces bucket verify --sample 100 ``` To sync the raw bucket to a private remote, authenticate first. `bucket connect` exits with a `run 'opentraces auth login'` hint until you do, and it prompts for a bucket security policy that protects raw captured evidence before remote sync: ```bash opentraces auth login opentraces bucket connect opentraces bucket security policy --policy recommended opentraces bucket sync push opentraces bucket sync status ``` ## 5. Search, Map, And Slice Traces ```bash opentraces trace query --since 7d --cwd opentraces trace query --skill my-skill --json opentraces trace map --bursts opentraces trace slice --template bursts opentraces trace get ``` `trace query` returns bounded candidates. `trace map` exposes the trace's deterministic evidence graph and edit bursts. `trace slice` creates bounded packets that workflows can turn into rows. For deterministic signals about how a run went (no LLM, nothing persisted): ```bash opentraces trace get --waste --json # where context burned opentraces trace get --run-intel --json # resteer/recovery/loop signals opentraces trace compare --json # compare two runs ``` For commit-level provenance: ```bash opentraces trail blame commit opentraces trail pr render --base main opentraces trail graph opentraces trail track ``` For model context at a decision point: ```bash opentraces ctx opentraces ctx :7 opentraces ctx :7 --layer messages opentraces ctx resume ``` ## 6. Create A Dataset Workflow Datasets are projected rows, not raw trace uploads. Start from a template or a custom workflow package. ```bash opentraces workflow templates opentraces workflow create my-workflow --template skill-command-trajectory-eval-v1 opentraces dataset new my-dataset --workflow ./workflows/my-workflow/ opentraces dataset new opentraces-episodes --from-skill opentraces ``` Ad-hoc seeding is also available: ```bash opentraces dataset new my-import --rows-file rows.jsonl --schema schema.json ``` ## 7. Run And Review ```bash opentraces dataset run my-dataset --dry-run --limit 5 opentraces dataset run my-dataset opentraces dataset run opentraces-episodes --executor script --json opentraces dataset status my-dataset opentraces dataset review my-dataset --json opentraces dataset review approve my-dataset opentraces dataset review approve my-dataset --all ``` The legacy `--web` and `--tui` review clients currently return decommission notices. Use the CLI row review surface until the dataset-scoped UI lands. ## 8. Publish Reviewed Rows ```bash opentraces dataset remote create my-dataset owner/team-traces --private opentraces dataset publish my-dataset --check-only opentraces dataset publish my-dataset ``` `dataset publish` uploads approved rows and contract files to the bound remote as new shards. It does not publish the raw bucket unless you separately run `bucket sync push`. ## Next Steps - [Portable Bucket](/docs/workflow/bucket), raw retained evidence and sync - [Trace Discovery](/docs/workflow/trace-discovery), query/map/slice/get - [Trace Trails](/docs/workflow/blame), Git anchors and survival - [Context Tree](/docs/workflow/context-tree), what the agent saw - [Dataset Workflows](/docs/workflow/workflow-templates), row projection packages - [Dataset Rows](/docs/workflow/datasets), review states and schedules - [Clients](/docs/clients/overview), consumers and bucket evidence - [Agent Workflows](/docs/clients/agent-workflows), context warmup and session-history search - [Trace Capsule](/docs/clients/trace-capsule), privacy-bounded agent usage episodes - [Security Tools](/docs/security/tiers), optional default-off tools --- # Commands Reference for the current `opentraces` CLI. Use `--json` on commands that offer it when another agent or script needs structured output; under an explicit `--json`, stdout is pure JSON, with no `---OPENTRACES_JSON---` sentinel line, and interactive commands refuse to prompt (a structured `INTERACTIVE_REQUIRED` error, exit code 2, zero prompt bytes) rather than hang under `--json` or a non-TTY. ```bash opentraces [--json] ... ``` A bare `opentraces [:|:last|:-]` root-dispatches to `opentraces trace get ` when `` is not an existing command name and is address-shaped: a full trace-id UUID, a `t:`/`tu:`/`tmn:`/`ot://` typed id, or a bare hex prefix (8+ chars) that uniquely resolves to one trace, the same trace address grammar `ctx` and `trail` resolve, so one token names the "resume triple" (action / context / world) across all three. Known commands always win, so no verb can ever be shadowed. ## Public Root Surface ### Global setup | Command | What it does | |---------|---------------| | `setup` | Wire opentraces into your system | | `upgrade` | Upgrade the CLI, re-render installed integration glue, and refresh the project skill (root peer of `setup`, not a subcommand) | | `uninstall` | Reverse the opentraces install, the symmetric inverse of `setup` (root peer of `setup`, not a subcommand) | | `auth` | HuggingFace identity (`login`, `logout`, `whoami`) | | `config` | Show or set configuration | | `completions` | Print or install shell completion scripts | ### Project setup | Command | What it does | |---------|---------------| | `init` | Enroll the current project and connect selected agent hooks | | `status` | Fleet bucket safety dashboard: is my private trace bucket safe to sync? | | `doctor` | Report integration and security tool health | | `remove` | Remove opentraces from the current project | ### Trace, Trail, Context, Bucket | Command | What it does | |---------|---------------| | `trace query` | Search retained traces and return bounded candidate packets | | `trace get` | Resolve a trace, trace unit, map node, or `ot://` resource; resolves `:` / `:last` / `:A-B` | | `trace map` | Show a deterministic Trace Map or burst projection | | `trace slice` | Extract bounded Trace Slices, or tile the whole trace into Trajectories with `--by` | | `trail blame commit` | Resolve commit-to-trace or trace-to-commit attribution | | `trail pr render\|create\|update` | Render/create/update PR bodies from branch lineage (lifted from `trail blame pr`) | | `trail graph` | Render commit + trace history | | `trail track` | Walk trace patch survival through Git history | | `ctx` | Navigate the Context Tree: what the LLM saw at each step, via the bare-noun `ctx [:\|:last]` | | `bucket` | Inspect, list, verify, repair, reclaim, connect, and sync the private trace bucket | The CLI collapsed from 8 visible `trace` verbs to 4 (`query` / `get` / `map` / `slice`) and lifted `trail blame pr` to top-level `trail pr`; every demoted verb (`trace skills`, `trace index`, `trace partition`, `trace compare`, `trace teleport`, `trace discover`, `bucket status`, `bucket manifest`, `bucket remote`, `bucket rebuild`, `bucket replay`, `bucket prune`, `bucket prefetch`, `setup bucket`, `setup upgrade`, `setup uninstall`, `setup llm-review`, `ctx tree`/`show`/`step`/`reads`/`writes`/`list`/`info`) stays callable and `--json`-scriptable, hidden from `--help`, never removed. ### Dataset Workflow, Dataset, Capsule, Security, Capture | Command | What it does | |---------|---------------| | `workflow` | Manage local dataset workflow skill packages | | `dataset` | Manage local executable datasets and row publication | | `capsule` | Seal, resolve, share, and replay a Trace Capsule (an immutable, URL-addressed episode) | | `security` | Optional privacy/security utilities | | `capture-otlp` | Run and control the OTLP receiver capture source | | `git-backfill` | Retroactively correlate old commits to retained traces | ## Setup ```bash opentraces setup opentraces setup claude-code opentraces setup codex-cli opentraces setup pi opentraces setup git opentraces setup watcher install opentraces setup watcher status opentraces bucket connect opentraces auth login opentraces setup capture-otlp opentraces setup trufflehog opentraces setup privacy-filter opentraces setup skill opentraces upgrade opentraces uninstall ``` `upgrade` and `uninstall` are root peer verbs beside `setup` (the in / update / out triad), not subcommands of it: `opentraces upgrade` / `opentraces uninstall`. `opentraces bucket connect` (a thin wrapper over the same configure logic) is the rename of `setup bucket`; the old `setup bucket` / `setup upgrade` / `setup uninstall` spellings stay callable, hidden. `setup doctor` aliases the root `opentraces doctor`. The former `setup llm-review` is hidden; the canonical LLM row-review surface is `opentraces dataset review` / `opentraces dataset publish`, not a setup step. Security setup commands only enable the tools you choose. Regex, entropy, TruffleHog, privacy-filter, LLM PII, business-logic signals, path anonymization, capsule scope, and classifier are not on by default for per-record sanitization. `opentraces setup codex-cli` supports: | Flag | Meaning | |------|---------| | `--dry-run` | Print the hook-copy and `hooks.json` update plan without writing files | | `--remove` | Remove opentraces Codex hook commands and copied scripts | | `--hooks-file TEXT` | Override the Codex hooks file, default `~/.codex/hooks.json` | | `--hooks-dir TEXT` | Override the copied hook script directory, default `~/.codex/hooks/opentraces/` | It registers native Codex hook commands for future Codex CLI sessions. Use `opentraces doctor` for install health; there is no `setup codex-cli --status` flag. `opentraces setup pi --dry-run --json` reports the Pi package/checklist plan without writing. Use `pi install npm:opentraces-pi` for the primary package install path. Under global tracking (the default) Pi auto-enrolls each repo on first capture like Claude/Codex; opt out with `opentraces config tracking-mode manual` or a per-project `excluded` marker, or run `opentraces init --agent pi` to enroll a repo explicitly. `opentraces setup pi` supports: | Flag | Meaning | |------|---------| | `--dry-run` | Report the package/checklist plan without writing | | `--project` | Write/check project-local `.pi/settings.json` instead of global Pi settings | | `--settings-file TEXT` | Use an explicit Pi `settings.json` path | | `--local` | Use the repo-local `packages/opentraces-pi` package path when present | | `--remove` | Remove the OpenTraces package entry instead of installing | | `--json` | Emit machine-readable JSON, accepted for Pi tool callers | Inside Pi, the package provides `/ot-capture-status`, `/ot-setup`, `/ot-search `, `/ot-trace `, `/ot-standup`, `/ot-capsule [trace-id]`, and `/ot-dataset`. The matching model tools are `ot_capture_status`, `ot_search`, `ot_trace`, `ot_standup`, `ot_capsule`, and `ot_dataset`. `opentraces setup skill` installs the shared opentraces skill into `~/.agents/skills/opentraces/` and links it into supported harness skill directories: ```bash opentraces setup skill --harness claude-code opentraces setup skill --harness codex-cli opentraces setup skill --harness pi ``` Omit `--harness` to refresh every supported harness link. Use `opentraces doctor` to verify the canonical skill copy and per-harness symlinks. A bare `opentraces upgrade` does the full stack: it upgrades the CLI via the detected install method (`pipx`, Homebrew, or pip; a `source`/editable install skips the package upgrade), re-renders every installed integration glue file (watcher shim, git post-commit hook, Claude Code and Codex CLI hooks, OTLP settings and autostart) re-stamped to the new version, and refreshes the project skill and hook if opted in. Two mutually exclusive flags narrow that scope: | Flag | Meaning | |------|---------| | `--integrations-only` | Re-render only already-installed integration glue to the current CLI version, with no CLI bump and without enabling new integrations | | `--skill-only` | Refresh only the skill file and hook, skip the CLI upgrade | `opentraces uninstall` is the symmetric inverse of `setup`: one command that reverses the whole multi-surface install (Claude Code / Codex / Pi capture hooks, the OTLP receiver + its `~/.claude/settings.json` env keys, the watcher daemon, the skill, shell completions, per-repo git post-commit hooks, security-tool flags) and stops every opentraces process. It is **data-safe by default** and emits the frozen `opentraces.setup_uninstall.v1` JSON envelope (`removed_names`, `skipped_names`, `keys_removed`, `refs_preserved`, `refs_purged`, `daemon_stopped`, `unit_unloaded`, `package_uninstall_command`, `residue`). | Flag | Meaning | |------|---------| | `--integrations-only` | Default. Reverse every install-time patch + daemon; PRESERVE all captured data (bucket, datasets, projects, staging, and every `refs/opentraces/*` + `refs/notes/opentraces`) | | `--purge` | Also DELETE the captured corpus + opentraces git refs. UNRECOVERABLE (the canonical Trail event log and its only local replay source, the bucket, both die); requires a typed confirmation | | `--project PATH` | Scope per-repo reversal (and, under `--purge`, ref-purge) to one repository instead of every registered repo | | `--prune-unflushed` | Also delete un-flushed raw bodies + OTel staging (`raw-bodies/`, `staging/otel/`); destructive opt-in under the default tier | | `--dry-run` | Resolve and print the plan; change nothing. The recommended first run | | `--yes` | Skip the `--purge` confirmation (required for `--purge` in non-interactive use); does not bypass `--dry-run` | It quiesces first (sets each repo's `excluded` marker, resets `tracking_mode` to `manual`, stops the daemons) so a mid-uninstall agent touch cannot re-enroll the repo, then reverses the surfaces, reusing every existing per-surface reverser. The package itself is never self-uninstalled; the correct command for the detected install method is printed, and `~/.cache/huggingface/token` is never deleted. A configured remote bucket survives a local `--purge` and is named in `residue`. Unlike `opentraces remove` (one cwd repo), `uninstall` fans out across every registered repo. ## Project Commands ```bash opentraces init opentraces init --agent claude-code --import-existing opentraces init --agent codex-cli opentraces init --agent pi opentraces status opentraces status --short opentraces status --project my-repo opentraces doctor opentraces doctor --security opentraces remove opentraces remove --all ``` `opentraces status` is the fleet bucket safety dashboard (`opentraces.bucket.status.v1`): an O(1) read of the local bucket, never a full scan, reporting how many traces are captured, how many are cleared for sync, and, the load-bearing signal, how many are not yet cleared. The green "safe to sync" verdict is **structurally impossible** while any trace is unscanned; the wording is a PROCESS state ("not cleared for sync"), never a content claim. Flags: `--short` (one-line porcelain summary), `--full` (adds debugger detail the default render drops), `--project SLUG` (scope every count and the verdict to one project), `--json`. The former per-project capture inbox (counts by stage, active remote, most recent traces) moved to the hidden `opentraces status-inbox`; the lower-level per-field bucket-health readout `status`'s verdict points at for deeper debugging is the hidden `opentraces bucket status`. `opentraces doctor` also reports CLI freshness and integration version drift. Under `--json`, `doctor.cli` carries `installed_version`, `latest_version`, `upgrade_available`, `check_state` (`fresh`, `cached`, `stale-cache`, `unavailable`, `disabled`, or `unknown`), `source`, and `cache_path`. The latest-version lookup is an offline-safe, 24h-cached PyPI check; set `OPENTRACES_DISABLE_VERSION_CHECK=1` to disable it. `doctor.integrations` (`cli_version`, `items`, `drift`) reports each deployed integration glue file against the running CLI. Doctor emits a top-level `next_command` / `next_steps` directive for agents: `opentraces setup upgrade` when a newer CLI is available, or `opentraces setup upgrade --integrations-only` when only deployed glue has drifted. An available upgrade alone stays exit 0, but `doctor` exits 3 when an installed integration has version drift (alongside the existing broken-hook and invalid-trail exit-3 conditions). The human view shows a warn row "v\ available; run 'opentraces setup upgrade'" and per-integration "drift: ...; run 'opentraces setup upgrade --integrations-only'". Under `--json`, `doctor.trace_index.state` and `rebuild_advice` describe the **live search snapshot** that `trace query` actually serves (`state` is `ok` / `stale` / `missing` / `error`, mapped from the snapshot state: ok→ok, stale→stale, missing→missing, wrong_schema→stale, unreadable/error/unknown→error; `rebuild_advice` is always `opentraces trace index rebuild`). The deprecated legacy `index.db` compatibility cache is reported separately as `doctor.trace_index.legacy_index_state`, so its absence is not a current failure when the snapshot is healthy. Both `opentraces --json doctor` and the command-local `opentraces doctor --json` emit this payload. Under `--json`, `doctor.runtime_provenance` reports install **provenance** — not just versions — so a machine with multiple opentraces runtimes (editable checkout, pipx, Homebrew) that all report the same version is still distinguishable. It carries `current` (the foreground command's python + `module_file` + `source_kind` + git root/commit), `discovered_installs` (the distinct code roots behind the configured integration runners, with real `module_file`/`dist_version` from a bounded probe), `integration_runners` (codex/claude/git/watcher/otlp, each with `matches_current`), and a `state` of `single_runtime` or `mixed_runtimes`. Mixed runtimes are a `warning` (never an error — they never change doctor's exit code); the advice is informational and never deletes or rewrites anything. `init --agent` accepts `claude`, `claude-code`, `codex`, `codex-cli`, or `pi`. `--import-existing` currently imports existing Claude Code traces for the current repo. Codex CLI and Pi capture start with future sessions after their runtime setup and project enrollment are in place. Pi project enrollment is an explicit consent gate even when tracking mode is global. ## Trace Discovery ```bash opentraces trace query --lex "bug fix failing test" --cwd --limit 20 --json opentraces trace query --skill opentraces --include-slice intent opentraces trace map --bursts --json opentraces trace slice --template bursts --json opentraces trace slice --by user-turn --json opentraces trace slice --by milestone --judge agent --answers answers.json --json opentraces trace get --bursts --json opentraces trace get : --json opentraces trace get :last --json opentraces trace get --remote owner/private-bucket ``` The visible `trace` surface is four verbs: `query` (search) -> `get` (pull up) -> `map` (dissect) -> `slice` (extract). Two verbs from the previous CLI folded into that loop and stay callable, hidden from `--help`: `trace skills` is now `trace query --skill ` (same compact snapshot's `skill_invocations` table, same `telemetry.duration_ms` + `search_diagnostics` so agents can tell whether a raw scan happened); `trace partition --by s1|s2|s3|s4` is now `trace slice --by user-turn|change-burst| milestone|subgoal` (see below). `trace index`, `trace compare`, `trace discover`, and `trace teleport` are likewise hidden-but-callable; the search index now self-maintains invisibly behind `trace query` rather than needing an explicit rebuild step. Common `trace query` filters include `--lex`, `--semantic`, `--skill`, `--tool`, `--files`, `--signal`, `--survival`, `--since`, `--candidate-kind`, `--project`, and `--cwd`. `trace query` is read-only against the local search snapshot: it neither rebuilds the index nor pulls remote data. Under active capture the snapshot can be marked stale by a concurrent write; rather than dead-ending with `maintenance_needed`, the default query serves the last-known-good snapshot and attaches a `freshness` object (`stale`, `stale_reason`, `built_at`, `source_hash`, `rebuild_recommended`). Pass `--fresh` for strict freshness: it returns `maintenance_needed` instead of serving a stale snapshot when freshness cannot be proven. To search remote traces, first `opentraces bucket sync pull` and then refresh the snapshot with the hidden `opentraces trace index --json`. `trace get --remote owner/repo` is the per-trace remote-read path, a uniform `--remote ` flag that `trace query`, `trace get`, and `ctx` all take to read from a HuggingFace bucket ad hoc; the old `--remote-bucket` / `--force-remote-bucket` flags on `trace query` / `trace get` are hidden deprecated aliases for it. `bucket sync` moves the whole bucket against the one remote configured by `bucket connect`, so its verbs don't take a per-call `--remote`. ### `trace slice --by` — the Trace Slicer Library `opentraces trace slice --by --json` runs one operation, `slice(trace, slicer) -> Trajectory[]`, that decomposes a captured session into an array of trajectories which **tiles** the whole trace (every step in exactly one trajectory). It absorbs the former `trace partition --by s1|s2|s3|s4`, which stays callable, hidden from `--help`, sharing the exact same slicing engine byte-for-byte (its `s1`-`s4` ids map onto the `--by` names below). It emits the frozen `opentraces.slicing.v1` envelope (`trace_id`, `slicer`, `tier`, `total_steps`, `trajectories[]`, `tiling`); a `Trajectory` is `{start, end, kind, label}` with an end-inclusive step-index span. It is derive-on-demand (no captured-schema change, no `SCHEMA_VERSION` bump), like `--waste` / `--run-intel` / `trace compare`. | Slicer | `--by` | former `--by` (`trace partition`) | Tier | What it isolates | |---|---|---|---|---| | user-turn | `user-turn` | `s1` | deterministic | one genuine human ask plus the work serving it | | change-burst | `change-burst` | `s2` | deterministic | a coherent code-change burst (reuses the burst gap, 35) with its read/verify | | milestone | `milestone` | `s3` | cheap-LLM | a span ending on a verified outcome (no over-segmenting same-artifact successes) | | subgoal | `subgoal` | `s4` | cheap-LLM | one coherent sub-goal with one deliverable | The cheap-LLM slicers (`milestone`, `subgoal`) resolve their judgment through a pluggable `--judge deterministic|agent|provider|human` (default `agent`). With the agent judge, if judgments are needed and none are supplied the command computes the `JudgmentRequest`s, prints them with an instruction, and exits `rc=10` (`opentraces.slicing.needs_judgment.v1`). Answer each request, write `{"answers": [{"id": "...", "decision": "...", "confidence": 0.9}]}` to a file, and re-run with `--answers ` for the final tiling at `rc=0`. `slice` is a pure function of `(trace, answers)` — same inputs, byte-identical output. The `provider` judge resolves the same requests unattended via the detected LLM backend. Reading the trace from a HuggingFace bucket ad hoc (`--remote `) is available today only on the hidden `trace partition --by s3 --remote ` form; `trace slice --by` does not yet expose that flag. Each milestone / subgoal trajectory is given a short, outcome-first **label** so a sliced session reads as a legible map. When a model is reachable (the detected `claude` CLI / API) the labels are model-authored in one batched call per trace; with no model they fall back to the agent's own narration. Labels never affect the boundaries — tiling stays deterministic. Explicit maintenance commands that can run for minutes accept a shared `--progress auto|plain|json|never` flag (starting with the hidden `opentraces trace index rebuild`; the search index now self-maintains behind `trace query`, so this is callable plumbing rather than a step you run by hand). Progress and heartbeat events go to **stderr** so the final `--json` payload on stdout stays clean; `--progress json` emits a stable JSONL event stream (`{"event":"progress","command":...,"stage":...,"elapsed_ms":...}`) that lets an agent tell "working" from "hung". Default is `auto` (quiet on a non-TTY/CI, human-readable on a terminal); read commands like `trace query` stay quiet and never gain hidden rebuild/progress behavior. `--semantic` expands a small static dictionary of service/library concepts observed in trace evidence (for example Hugging Face or MongoDB); it is not an embedding search. A query that matches no concept falls back to lexical matching where every token must match, so natural-language paraphrases can miss traces that concrete keywords would find. ## Trace Intelligence Deterministic, derive-on-demand signals about how a run went, layered on top of the Trace surface. No LLM, no schema change, nothing persisted; each is a frozen JSON envelope. Three capabilities: context waste, run signals, run compare. ```bash opentraces trace map --waste --json # also: trace get --waste opentraces trace get --run-intel --json # also: trace map --run-intel opentraces trace compare --json opentraces trace compare --no-quality --json ``` - **Context waste** — `--waste` emits `opentraces.context_waste.v2`: `large_output` (>= 12000 chars), `repeated_file_read` (same file 3+ times in 20 min), and `repeated_search` (`rg|grep|find|ag|ack` 5+ times in 10 min) findings plus a `summary` count block. Override thresholds with `--large-output-chars`, `--file-read-window-min`, `--search-window-min`. - **Run signals** — `--run-intel` emits `opentraces.run_intel.v1` with deterministic `resteer` / `recovery` / `loop` / `failure` annotations. Recovery only fires after an uncleared prior failure; failure prefers structured tool errors over substring matches; a repeated command is one `loop` signal carrying `evidence.repeat_count`. - **Run compare** — `trace compare ` (hidden from `--help`, still callable and `--json`-scriptable; it isn't one of the four job verbs) emits `opentraces.trace_compare.v1` with top-level `{schema_version, status, trace_a, trace_b, fidelity: {a, b}, burst_gap, quality_included, delta}`. `delta` holds `{a, b, delta}` triples over `metrics`, deterministic quality persona scores under `quality` (skip with `--no-quality`, which sets `quality_included: false`), burst/signal counts under `bursts` and `signals`, and `security`. Both traces are pinned to the same burst gap (`--burst-gap`, default 35). `--waste` and `--run-intel` are mutually exclusive with `--bursts` and with each other; on `trace get` they are also mutually exclusive with `--resume`. The `trace get` and `trace map` surfaces emit byte-identical payloads for matching flags. Each detector reports a `fidelity` of `record` or `otel`, preferring full wire fidelity when the trace was captured via the OTLP receiver. An unresolved trace ref exits with code 6. ## Trace Trails ```bash opentraces trail opentraces trail : opentraces trail blame commit opentraces trail blame commit c: src/main.py --lines opentraces trail blame commit t: --include-overlapping opentraces trail pr render --base main opentraces trail pr create --base main opentraces trail pr update --base main opentraces trail graph --limit 50 opentraces trail graph --trace opentraces trail track opentraces trail track --patch opentraces trail track --anchor opentraces trail track --since 12h --json opentraces trail track --all --json --limit 50 ``` The bare-noun `trail ` returns a bounded, survival-free lineage overview and `trail :` returns the world (outcome + hunk) at that step, the same `trace:step` address `trace get` and `ctx` resolve. `trail blame` is a group scoped to commit/trace attribution: use `trail blame commit` for that. PR-body rendering is lifted to the top-level `trail pr render|create|update` (the old `trail blame pr` path stays callable, hidden). `trail teleport` is dropped from `--help` (hidden but callable). ## Context Tree ```bash opentraces ctx opentraces ctx : opentraces ctx :last opentraces ctx : --layer system opentraces ctx : --layer messages opentraces ctx : --full --json opentraces ctx diff --json opentraces ctx compactions --json opentraces ctx resume --json opentraces ctx prune --source-jsonl opentraces ctx resolve ot://context-node/ --json opentraces ctx anchor-for-step ``` `ctx` is bare-noun addressable: `ctx ` renders the context overview (shape + capture method), `ctx :` the model input at that step, and `ctx :last` the final/active step. `--layer system|messages| tools|runtime` renders one layer readably (default is the bounded four-layer card); `--full` inlines the full hydrated model input (system + messages + tools + runtime), the fork/eval-row packet; `--with-dropped` includes compaction-dropped content; `--remote ` reads from a remote bucket and `--offline` fails rc=4 instead of fetching a blob not already cached. `:` is the same universal address `trace get` and `trail` resolve, so a `trace:step` ref piped in on stdin (`trace query --json | ctx --json`) is hydrated too. The former `ctx tree` / `ctx show` / `ctx step` subcommands are superseded by the bare noun and stay callable, hidden; `ctx reads` / `ctx writes` (per-step decomposition, now `ctx : --json | jq`), `ctx list` / `ctx info` (manifest-only reads, superseded by `bucket list`), and `ctx diff` / `ctx compactions` / `ctx resume` / `ctx prune` / `ctx resolve` / `ctx anchor-for-step` (Context Tree plumbing) remain hidden-but-callable and resolve Context Tree nodes and layers from local retained evidence. ## Bucket ```bash opentraces bucket list --json opentraces bucket list --unscanned --json opentraces bucket list --project my-repo --count opentraces bucket verify --sample 100 --json opentraces bucket verify --full --json opentraces bucket repair --json opentraces bucket reclaim --json opentraces bucket reclaim --apply --json opentraces bucket connect --repo me/opentraces-bucket opentraces bucket connect --local-only opentraces bucket sync status --json opentraces bucket sync diff --json opentraces bucket sync push --dry-run --json opentraces bucket sync push --json opentraces bucket sync pull --json opentraces bucket security status opentraces bucket security policy --policy recommended opentraces bucket security policy --tool regex --enable opentraces bucket security policy --tool entropy --disable opentraces bucket security status --json ``` `bucket status`, `bucket manifest`, `bucket remote` (`status`/`diff`/`push`/ `pull`), `bucket rebuild`, `bucket replay`, `bucket prune`, `bucket prefetch`, and `bucket security` are hidden from `--help` in v7 (hidden != removed): the visible bucket surface is `bucket list` / `bucket connect` / `bucket sync` / `bucket verify` / `bucket repair` / `bucket reclaim`. `bucket manifest` folded into `bucket list` (the read side) and `bucket repair` (the `--heal` write side, plus what `bucket rebuild` used to cover for per-trace envelopes and anchors); the hidden `bucket rebuild --substrate context-tree|trail|traces| all` remains for substrate-targeted rebuilds beyond what `bucket repair` covers. `bucket prune` / `bucket prefetch` stay callable for that narrower maintenance need, but `verify` / `repair` / `reclaim` are the documented path. `opentraces bucket connect` (the rename of `setup bucket`, which stays callable, hidden) configures the private bucket remote target: `--remote` / `--local-only`, `--provider huggingface|fake`, `--repo`, `--sync-policy daemon|manual`. `bucket list` is the bounded, paginated per-trace inventory (issue #162) that supersedes the hang-prone `bucket manifest`: it reads the plan-087 per-row status accelerator once and applies `--unsynced` / `--unfiltered` / `--security-stale` / `--unscanned` / `--project` / `--since` before projecting rows, so a real fleet bucket returns a page in an interactive budget. `--count` returns only the matching count; `--limit` / `--cursor` page through the rest (default 50, hard cap 1000). `bucket sync {push,pull,diff,status}` is the bidirectional mirror of the whole raw bucket (traces, blobs, events, manifest) against the one remote `bucket connect` configured, distinct from `dataset publish`, the gated one-way egress of reviewed rows. `bucket sync push` is the gated egress SEAL: it computes an auditable `pushed[]` / `withheld[]` partition and **refuses** (zero bytes egressed, non-zero exit) if any trace is not yet cleared for sync; `--dry-run` previews that partition without egressing. `bucket sync status --json` carries an additive `security_gate` block so a single command answers "why can't this bucket sync, and what makes it eligible?". It reports `configured`, `unfiltered_count`, `security_stale_count`, `eligible`, `blocking_reasons` (in order: `setup bucket` first when the remote is unconfigured, then `bucket security policy --policy basic` → `bucket security run --all`), and `remediation`. `doctor` and `bucket sync status` derive this from the same persisted-manifest counts via one shared helper, so they agree on counts and remediation; `bucket security status` shares the remediation. The gate reads the persisted bucket manifest (no corpus scan); a missing manifest yields `security_gate` state `unknown`. `bucket list` and `bucket verify` are side-effect-free reads: they never write under the bucket. Self-heal (regenerating `manifest.json` and every per-trace envelope from the canonical event log + blob store) is `bucket repair`, the idempotent crash-recovery primitive; `bucket reclaim` separately finds (and, with `--apply`, removes) leaked Trace Trails cruft under `.git/**/opentraces/`, dry-run by default, never touching the live event log or accelerator. The bucket is the private capture-time store. It is separate from datasets: `bucket sync` moves raw retained evidence, while `dataset publish` moves approved workflow rows. ## Dataset Workflows ```bash opentraces workflow templates opentraces workflow templates --json opentraces workflow create my-workflow opentraces workflow create my-workflow --template skill-command-trajectory-eval-v1 opentraces workflow list opentraces workflow list --digest opentraces workflow remove my-workflow --yes ``` Workflow packages are skill-format row builders. `dataset run` invokes them to project bucket traces into dataset rows. ## Datasets ```bash opentraces dataset list --json opentraces dataset new my-dataset --workflow ./workflows/my-workflow/ opentraces dataset new opentraces-episodes --from-skill opentraces opentraces dataset new my-import --rows-file rows.jsonl --schema schema.json opentraces dataset run my-dataset --dry-run --limit 5 --json opentraces dataset run opentraces-episodes --executor script --json opentraces dataset run my-dataset --scope trace --trace opentraces dataset status my-dataset --json opentraces dataset security my-dataset --json opentraces dataset security my-dataset --tool business_logic --enable opentraces dataset security my-dataset --tool regex --disable --unsafe-override --reason "synthetic fixtures" opentraces dataset review my-dataset --json opentraces dataset review approve my-dataset opentraces dataset review reject my-dataset opentraces dataset review reset my-dataset opentraces dataset review approve my-dataset --all opentraces dataset remote create my-dataset owner/team-traces --private opentraces dataset remote list my-dataset --verbose opentraces dataset remote visibility my-dataset owner/team-traces --public opentraces dataset publish my-dataset --check-only opentraces dataset publish my-dataset opentraces dataset verify my-dataset --json opentraces dataset schedule add my-dataset --every 1h --approve-new --publish-check-only opentraces dataset schedule list opentraces dataset remove my-dataset --yes ``` The legacy interactive `dataset review --web` and `--tui` flags are still accepted but return decommission notices. `dataset run --privacy-tier off|low|medium|high` remains a publication compatibility field for dataset row envelopes. It is not the security tool selection mechanism; use `opentraces security sanitize --tools ...` or `--use-config` in a workflow when explicit sanitization is required. `dataset new --from-skill ` creates a `skill-episodes-v1` dataset and stores the selected skill in the dataset's `candidate_query`. Pair it with `dataset run --executor script --json` for deterministic local row materialization from the snapshot-backed skill invocation surface. `dataset security ` inspects or edits that dataset's resolved security policy, which is seeded from the workflow's front-matter security contract at `dataset new --workflow` and stored in the dataset manifest. It is scoped to one dataset, not a global config toggle, and there is no `--policy` form. Optional tools toggle with `--tool --enable|--disable`; a required tool can only be disabled with `--unsafe-override` (and an optional `--reason`) when the workflow contract permits it. `dataset publish --check-only` blocks rows when a dataset's required security tools are not satisfied. `dataset remote visibility [] --private|--public` flips a bound HuggingFace dataset remote between private and public. `dataset verify [--json]` replays the bound workflow (with recorded judgment answers) in a side-effect-free mode and byte-compares the re-run against the stored rows, classifying the outcome as `reproduces` / `bucket-advanced` / `integrity-failure` (non-zero exit). It never appends rows or advances a cursor. See [Dataset Rows: Verify](/docs/workflow/datasets#verify) for the full contract. `dataset run --answers ` re-runs after a workflow exits `rc=10` needs-judgment (schema `opentraces.workflow.needs_judgment.v1`), the same handshake shape `trace slice --by` uses. See [Dataset Workflows: The Judgment Handshake](/docs/workflow/workflow-templates#the-judgment-handshake-rc10). ## Capsule ```bash opentraces capsule create [:|:-] --json opentraces capsule preview --product opentraces capsule get --json opentraces capsule get --summary opentraces capsule import --json opentraces capsule share --publish --yes opentraces capsule issue --publish --yes opentraces capsule replay --against HEAD --json opentraces capsule test --against HEAD --json opentraces capsule verdict --state fixed --close opentraces capsule watch --timeout 300 --json ``` A capsule is a privacy-bounded, redacted, self-contained record of one agent episode: the immutable, URL-addressed seal in the [Seal Family contract](https://github.com/JayFarei/opentraces/blob/main/docs/adr/0008-seal-family-contract.md) (ADR-0008). `create` seals it (the v7-address form; the pre-v7 `export` spelling stays callable, hidden, with a richer flag set). `get` is the read-only resolve verb (file / `https://` / `hf://` ref); `import` is the explicit opt-in write into the local bucket. `share` mints/publishes the shareable URL; `issue` renders/files the GitHub issue embedding it. `replay` builds a re-pose packet for a maintainer agent; `test` runs the captured repro as an executable test; `verdict` posts the outcome back to the issue; `watch` polls the issue for resolution. Every verb works identically whether or not the session had a failing command (`test=null` capsules are first-class). Full command reference, the redaction floor, the `capsule_replay.v1` four honesty properties (`reproducible`/`gradable`/`scoped`/`sandboxed`) plus the derived `verdict_trust`, and the capsule-worker rendered URL are documented at [Trace Capsule](/docs/clients/trace-capsule). ## Security Tools ```bash opentraces security tools list opentraces security tools list --json opentraces security tools info regex --json printf '%s\n' '{"text":"OPENAI_API_KEY=sk-demo"}' | opentraces security sanitize --tools regex printf '%s\n' '{"row":{"path":"/Users/alice/project"}}' | opentraces security sanitize --tools path_anonymizer printf '%s\n' '{"record":{...}}' | opentraces security sanitize --use-config ``` `security sanitize` requires either `--tools` or `--use-config`. `--tools` runs the named tools in canonical order. `--use-config` runs only tools explicitly enabled in the loaded config. Registered tools: `regex`, `entropy`, `trufflehog`, `privacy_filter`, `llm_pii`, `business_logic`, `path_anonymizer`, `capsule_scope`, and `classifier`. `security tools list|info` and `security sanitize` are the generic registry surface for introspection and explicit sanitization. `bucket security` is the scoped bucket policy front-end over the same `cfg.security..enabled` flags, where `--policy` accepts only `off|basic|recommended|strict`. ## OTLP Capture ```bash opentraces setup capture-otlp opentraces capture-otlp start opentraces capture-otlp status --json opentraces capture-otlp flush --session --project --trace-id opentraces capture-otlp flush --from-raw-bodies --session --project --trace-id opentraces capture-otlp restart opentraces capture-otlp stop ``` The receiver feeds Claude Code OTel events and raw API bodies into Context Tree events. If the receiver is down, agent traffic is not blocked. `flush` lands the captured context in the bucket and joins it to the trace spine, so `ctx show` / `ctx step` can read it. You usually do not need to run it by hand: the watcher tick auto-flushes a project's active OTel sessions once each goes idle (zero-touch). `--from-raw-bodies` reconstructs an already-captured session per-step straight from the raw request/response bodies with no live receiver running — the full-fidelity, retroactive path. `capture-otlp status` lists the captured session ids you can flush. ## Advanced ```bash opentraces git-backfill --max-commits 2000 --window-hours 48 --json opentraces completions install zsh --alias ot opentraces capabilities --json opentraces introspect --json ``` `capabilities` and `introspect` are machine-readable metadata surfaces for agents and tests. They are intentionally less prominent than the workflow commands above. --- # Supported Agents 0.4 separates live capture from import support. ## Current Support | Mode | Identifier | Status | Notes | |------|------------|--------|-------| | Live capture | `claude-code` | Supported | Installed via `opentraces init` or `opentraces setup claude-code`; supports snapshot-backed `--at-step` resume | | Live capture | `codex-cli` | Supported | Installed via `opentraces setup codex-cli` plus `opentraces init --agent codex-cli` inside each repo | | Live capture | `pi` | Supported | Install `opentraces-pi` with `pi install npm:opentraces-pi` or `opentraces setup pi`; auto-enrolled under global tracking (default), opt out via `config tracking-mode manual` or per-project exclusion | | Context capture source | `capture-otlp` | Supported for Claude Code | Installed via `opentraces setup capture-otlp`; feeds Context Tree events from OTel/raw API-body capture | | Dataset import | `hermes` | Supported | Registered `FormatImporter`. Invoked from dataset workflows or via the schema package's serializers | Planned adapters can follow the same contracts without changing the bucket, workflow, review, publish, or schema layers. ## Live Capture vs Import Live capture adapters discover and parse session files on disk. Import adapters read external datasets or files and map them into `TraceRecord`. That distinction matters in the public CLI: ```bash opentraces init --agent claude-code opentraces init --agent codex-cli opentraces init --agent pi opentraces setup capture-otlp opentraces dataset new my-import --rows-file rows.jsonl --schema schema.json ``` `init --agent` accepts `claude`, `claude-code`, `codex`, `codex-cli`, and `pi`. `codex` is an alias for the canonical `agent.name = codex-cli`. ## Codex CLI Details Codex CLI support is for the terminal Codex CLI, not Codex Desktop. Install and authenticate Codex CLI first, then run: ```bash opentraces setup codex-cli opentraces init --agent codex-cli ``` The setup command registers native Codex hooks in `~/.codex/hooks.json` and copies hook scripts to `~/.codex/hooks/opentraces/`. Future sessions write sidecar events under `.opentraces/codex-cli/hooks/` in the active repo. Hooks cover session start, user prompt submission, tool boundaries, permission requests, compactions, and Stop; they observe permission prompts but do not approve or deny them. Codex traces participate in the shared substrates: - **Trace**: Codex rollout JSONL is normalized into `TraceRecord` with `agent.name = codex-cli`. - **Trail**: pre/post tool sidecars feed Trace Trails when hook evidence is present; git correlation remains shared. - **Context Tree**: Codex uses `capture_method = transcript_reconstruction` and session-level approximated layers. It is not a Codex-specific raw Context Tree capture method and is not equivalent to Claude Code OTLP raw body capture. - **Bucket**: captured Codex traces land in the same private bucket layout as other supported agents. - **Resume**: native handoff uses `codex resume ` through `opentraces trace get --resume`; snapshot-backed `--at-step` materialization is unsupported for Codex. Codex encrypted reasoning is not decrypted. When a rollout contains encrypted reasoning without a plaintext summary, opentraces records an explicit redaction marker instead of inventing hidden chain-of-thought content. ## Pi Details Pi support is extension-backed. Under global tracking (the default), an installed Pi extension auto-enrolls each project on first capture, the same way Claude/Codex do, into a private + review-required bucket; capture is opt-out (switch to `opentraces config tracking-mode manual`, or set a per-project `excluded` marker / run `opentraces remove`). Capture still requires the `opentraces` CLI on PATH. The primary package install is: ```bash pi install npm:opentraces-pi opentraces init --agent pi ``` `opentraces setup pi --dry-run --json` reports the same package/checklist plan without writing; use `--project`, `--settings-file`, `--local`, and `--remove` for project-local settings, explicit settings files, repo-local packages, and uninstall. The package exposes slash commands (`/ot-capture-status`, `/ot-setup`, `/ot-search`, `/ot-trace`, `/ot-standup`, `/ot-capsule`, `/ot-dataset`) and high-level tools (`ot_capture_status`, `ot_search`, `ot_trace`, `ot_standup`, `ot_capsule`, `ot_dataset`) inside Pi. Pi traces participate in the same shared substrates as Claude/Codex: - **Trace**: native Pi JSONL under `~/.pi/agent/sessions/----/*.jsonl` plus `.opentraces/pi/events/` sidecars normalize to `TraceRecord` with `agent.name = pi`. The exported TraceRecord follows the active branch; tree transitions and inactive branch summaries remain in `metadata.pi` and Context Tree events in v1. - **Trail**: Pi pre/post tool sidecars map to the existing `hook_pre_tool_use` / `hook_post_tool_use` vocabulary. - **Context Tree**: provider/context sidecars use `capture_method = live_capture` when available; transcript-only fallback records explicit limitations. - **Bucket**: Pi traces write the same private bucket v2 layout and mixed-agent manifest rows as other agents. - **Resume**: native handoff uses `pi --session ` through `opentraces trace get --resume`; snapshot-backed `--at-step` materialization is unsupported for Pi v1. Raw provider bodies are default-off and local/security-gated when explicitly enabled. Opt in with `OPENTRACES_PI_RETAIN_RAW_PROVIDER_BODIES=1` (or `true`/`yes`) or an explicit sidecar opt-in; retained blob refs stay local under `.opentraces/pi/blobs/` before any bucket/workflow sync. Extension/bridge failures are fail-open and must not block Pi. Under `manual` tracking mode, or for a project with an `excluded` marker, the bridge returns `capture_disabled` without writing sidecars; under global tracking (default) it auto-enrolls the project and captures. `opentraces init --agent pi` still enrolls a repo explicitly. ## Adapter Contracts The capture layer exposes small protocols: - `SessionParser` for live agent session parsing - `ProjectSessionDiscoverer` / `SessionPathIdentifier` optional capabilities for repo-scoped discovery and native session ids - `AgentResumer` for native resume handoff - `FormatImporter` for file or dataset imports - `HookInstaller` for external integrations like Claude Code, Codex, Pi, and git This is why review, optional security tools, bucket storage, and dataset publication stay consistent even as new sources are added. ## What Gets Normalized All supported sources are normalized into the same schema with: - trace-level metadata - steps and plaintext or summarized reasoning content when the source provides it - tool calls and observations when the source provides them - outcomes and metrics - attribution and git links when available --- # Troubleshooting ## First Checks Start with: ```bash opentraces status opentraces doctor opentraces doctor --security ``` `status` is the fleet bucket safety dashboard: scanned/unscanned trace counts and a "safe to sync" verdict that is structurally impossible while any trace is unscanned (`--short` / `--full` / `--project SLUG`). `doctor` tells you whether required integrations are misconfigured. The old per-project capture inbox view is now the hidden `opentraces status-inbox`. ## Common Problems ### Not Initialized If the CLI says the repo is not initialized, run: ```bash opentraces init ``` The current repo marker is `.opentraces.json`, not `.opentraces/config.json`. ### No Traces Showing Up Check: ```bash opentraces status opentraces trace query --cwd --since 1d opentraces setup claude-code opentraces setup codex-cli --dry-run opentraces setup pi --dry-run --json opentraces capabilities --json ``` If you are using Claude Code, make sure the capture hooks are installed and that the repo has actual Claude Code session files under `~/.claude/projects/`. If you are using Codex CLI, make sure Codex itself is installed and authenticated, then run both `opentraces setup codex-cli` and `opentraces init --agent codex-cli` in the repo. Codex sessions are captured after hooks are installed; `--import-existing` is not a Codex backfill path. Sidecar hook files should appear under `.opentraces/codex-cli/hooks/` after a future Codex session runs. If you are using Pi, install `opentraces-pi` with `pi install npm:opentraces-pi`, then run `opentraces init --agent pi` in the repo. `/ot-capture-status` and `opentraces setup pi --dry-run --json` show the local checklist. Pi sidecars should appear under `.opentraces/pi/events/`; raw provider bodies are default-off. ### Codex Hooks File Is Corrupt `opentraces setup codex-cli` validates `~/.codex/hooks.json` before writing. If it reports `CORRUPT_HOOKS`, fix the JSON or move the file aside, then rerun: ```bash opentraces setup codex-cli opentraces doctor ``` The installer is idempotent and preserves unrelated hooks, but it will not try to repair malformed JSON automatically. ### Stale Trace Trails If `trail blame`, `trail graph`, or `dataset run` complain about stale projections, the watcher or backfill may need to catch up: ```bash opentraces setup watcher status opentraces setup watcher tick opentraces backfill opentraces git-backfill ``` `git-backfill` is particularly useful after a first-time install of the post-commit hook or after a period where the hook failed silently. `backfill` (the attribution-cache refresh) is a hidden maintenance verb: it runs when typed but does not appear in `--help`. ### Blocked Rows in a Dataset Inspect the dataset state: ```bash opentraces dataset status my-dataset opentraces dataset review my-dataset ``` Then approve, reject, or reset rows directly from the CLI. The legacy `--tui` and `--web` flags currently return decommission notices. ```bash opentraces dataset review reset my-dataset opentraces dataset review reject my-dataset opentraces dataset review approve my-dataset ``` ### Publish Fails Common causes: - no Hugging Face auth (`opentraces auth login`) - no remote bound to the dataset (`opentraces dataset remote create ...`) - survival-state filter excluded every row (`--min-retention` too high, or `--exclude-state` covered everything) - a configured integration is broken Useful commands: ```bash opentraces auth whoami opentraces dataset remote list my-dataset opentraces dataset publish my-dataset --check-only --json opentraces doctor ``` ### Bucket Sync Diverged If `bucket sync status` reports a diverged or remote-ahead bucket: ```bash opentraces bucket sync status opentraces bucket sync diff opentraces bucket sync pull --force # remote wins opentraces bucket sync push --force # local wins ``` `bucket sync` mirrors the whole raw bucket substrate (traces, blobs, events, manifest) against the configured remote; `bucket sync push` additionally refuses (zero bytes, non-zero exit) if any trace is not yet cleared for sync, so a diverged push may mean traces are still unscanned rather than a real conflict — check `opentraces status` first. The old `bucket remote push|pull|diff|status` spellings still work but are hidden from `--help`. Use `--force` carefully: it overwrites the slower side. ### TruffleHog Enabled But Missing If `doctor` reports that TruffleHog is enabled but unavailable: ```bash opentraces setup trufflehog # or opentraces setup trufflehog --disable ``` ### LLM Review Unreachable First check what `doctor` sees: ```bash opentraces doctor --security ``` The LLM trace review row shows the configured backend and model, the endpoint URL, whether the `api_key_env` variable is set, and the probe result against that endpoint. Common signals: - `probe: ... not found`, the configured model is not in the endpoint's catalog (pull it or update the model name) - `probe: UNREACHABLE ...`, the endpoint did not respond (start the local server, check the URL) - `api key env: $VAR (unset)`, remote backend needs an API key that is not exported in this shell Re-test or reconfigure: ```bash opentraces setup llm-review --test opentraces setup llm-review opentraces setup llm-review --disable ``` `setup llm-review` is now hidden from `--help` (it configures a publication gate, not a per-record sanitize step) but is still callable exactly as above. The row-level surface that consumes it day to day is `opentraces dataset review` / `opentraces dataset publish`. ### Command Hangs Or Exits 2 Under `--json` If a command that normally prompts interactively (for example an unconfirmed `setup` step or a review action) instead exits immediately with a structured error and code `2` when run with `--json` or from a non-TTY agent shell, that is expected: interactive commands refuse to prompt under `--json`/non-TTY and emit an `INTERACTIVE_REQUIRED` error with zero prompt bytes rather than hanging on stdin. Re-run interactively, or pass the flags needed to answer the prompt non-interactively (check `--help` for the command's non-interactive form). ### Resetting A Repo To remove opentraces from the current repo cleanly: ```bash opentraces remove opentraces remove --all ``` To clear the stored Hugging Face credential: ```bash opentraces auth logout ``` --- # Capture Capture turns agent sessions into retained trace evidence. The retained output lands in the private bucket first; dataset rows are produced later by workflows. ## Capture Path When capture hooks fire, opentraces: 1. discovers the agent session artifact; 2. parses it into a `TraceRecord` spine; 3. extracts tool calls, observations, steps, metrics, and environment data; 4. records Trace Patch and Context Tree events where available; 5. writes the trace envelope and companion files into the private bucket; 6. updates search projections used by `trace query` (including `trace query --skill `), `trace map`, and `trace slice`. The raw bucket is local-only unless you run `opentraces bucket connect` and sync with `opentraces bucket sync push`. ## Supported Live Sources ```bash opentraces setup claude-code opentraces setup codex-cli opentraces setup pi opentraces init --agent claude-code opentraces init --agent codex-cli opentraces init --agent pi ``` Claude Code, Codex CLI, and Pi are live capture sources. Hermes remains an import adapter consumed by workflows/schema tooling, not a direct public capture command. Codex CLI capture requires both machine setup and repo enrollment: ```bash opentraces setup codex-cli opentraces init --agent codex-cli ``` `setup codex-cli` registers native Codex hook commands in `~/.codex/hooks.json` and copies hook scripts under `~/.codex/hooks/opentraces/`. The hooks write sidecar JSONL under `.opentraces/codex-cli/hooks/` in the active repo and trigger a bounded ingest on Stop. They are passive observers; permission requests are recorded but not approved or denied. Pi capture is extension-backed: `pi install npm:opentraces-pi` loads the package, `opentraces init --agent pi` enrolls the repo, and native sessions live under `~/.pi/agent/sessions/----/*.jsonl`. Sidecars land under `.opentraces/pi/events/`. Provider/context sidecars use `capture_method = live_capture` when available; transcript fallback records explicit limitations. The exported TraceRecord follows the active branch in v1; inactive branch/tree transition details remain in `metadata.pi` and Context Tree events. Raw provider bodies are default-off and local/security-gated; opt in with `OPENTRACES_PI_RETAIN_RAW_PROVIDER_BODIES=1` only when the retained local blobs are acceptable. Codex Context Tree capture is reconstructed from rollout JSONL plus hook sidecars with `capture_method = transcript_reconstruction`. It produces useful session-level Context Tree nodes, but it is not the Claude Code OTLP raw-body capture path and it does not decrypt encrypted Codex reasoning. Snapshot-backed `--at-step` resume remains Claude Code only. ## Optional OTLP Context Capture For higher-fidelity Claude Code Context Trees: ```bash opentraces setup capture-otlp opentraces capture-otlp start opentraces capture-otlp status --json opentraces capture-otlp flush --session --project --trace-id opentraces capture-otlp flush --from-raw-bodies --session --project --trace-id ``` The OTLP receiver feeds OTel events and raw API-body files into the same Context Tree substrate. If the receiver is down, agent traffic is not blocked. `flush` lands the captured context in the bucket and joins it to the trace spine, so `ctx show` / `ctx step` can read it; the watcher tick auto-flushes a project's active OTel sessions once each goes idle (zero-touch), so you usually need not run it by hand. `--from-raw-bodies` reconstructs an already-captured session per-step from the raw bodies with no live receiver. ## Enrichment | Enrichment | Where it surfaces | |------------|-------------------| | Git signals | `TraceRecord.git_links`, `patches[].anchor`, Trace Trails | | Trace patches | `TraceRecord.patches[]`, `trail.jsonl.gz` | | Context Tree | `steps[].context_node_id`, `context_tree_summary`, `context.jsonl.gz` | | Dependencies | `TraceRecord.dependencies`, Trace Index facets | | Metrics | `TraceRecord.metrics` | | Security metadata | `metadata.security.tools_applied` when tools are run | Security tools are optional. Capture does not imply automatic sanitization. A bucket flow or workflow can run `opentraces security sanitize --tools ...` or `--use-config` when it needs redaction. ## Existing Sessions ```bash opentraces init --agent claude-code --import-existing ``` `--import-existing` currently imports existing Claude Code sessions for the current repo. Other sources should be handled by their capture adapter or by a workflow/import path. ## Next Step ```bash opentraces bucket status opentraces trace query --since 1d opentraces trace map --bursts opentraces dataset run my-dataset --dry-run ``` --- # Portable Bucket The bucket is the portable environment for captured trace evidence. It is local by default, can sync to a private remote, and is self-sufficient enough for another machine to inspect trace records, replay Trail events, and lazy-load Context Tree blobs without access to the original workstation. Buckets are distinct from datasets: | Layer | Contents | Egress | |-------|----------|--------| | Bucket | raw trace envelopes, patch history, Trail events, Context Tree events, source events, blobs, manifest | `opentraces bucket sync push` | | Dataset | workflow-projected rows over one or more traces | `opentraces dataset publish` | ## Principles - **Raw evidence stays private first.** Capture writes to the local bucket, not to a public dataset. - **The bucket is replayable.** Trail events and manifests are enough to rebuild derived projections and restore the canonical Git event ref. - **Large evidence is lazy.** Context and raw blobs are content-addressed, so readers can inspect the inventory first and fetch only what they need. - **Datasets are projections.** Publishing a dataset row does not publish the bucket unless you separately sync the bucket remote. - **Egress is a gated seal, reads are pure.** `bucket sync push` is one of the few commands in the CLI that writes anywhere outside the local bucket, and it refuses outright — zero bytes egressed, non-zero exit — while any trace is not cleared for sync. Every inspection command below (`status`, `list`, `verify`) is a derive-on-demand read that never persists anything. ## Layout The bucket lives under `~/.opentraces/bucket/` and is organized around deterministic, replayable pieces: ```text bucket/ traces/v1/// trace.json trace_history/ trail.jsonl.gz context.jsonl.gz sources.jsonl.gz blobs/v1// context//.json.gz raw//.blob events/v1/ batches/-.jsonl.gz index.json manifest.json ``` `trace.json` is the `TraceRecord` spine. The companion files carry the large or evolving evidence needed by Trace Trails, Context Tree, and replay. ## Fleet Safety Dashboard ```bash opentraces status opentraces status --short opentraces status --full opentraces status --project my-repo opentraces status --json ``` `opentraces status` is the top-level, O(1) answer to "is my private trace bucket safe to sync?" — scanned/unscanned counts across the whole fleet and a green "safe to sync" verdict that is structurally impossible while any trace is still unscanned. `--short` prints a one-line, stable, scriptable summary; `--full` adds debugger-level detail; `--project` scopes every count and the verdict to one project slug. The envelope is `opentraces.bucket.status.v1`. For the lower-level per-bucket health readout that this dashboard's verdict points back to, use `opentraces bucket status`. ## Inspect ```bash opentraces bucket status opentraces bucket status --json opentraces bucket list --json opentraces bucket list --unscanned --json opentraces bucket list --unsynced --unfiltered --security-stale --json opentraces bucket list --project my-repo --since 2026-06-01 --json opentraces bucket list --count --json opentraces bucket verify --sample 100 --json opentraces bucket verify --full --json ``` `bucket status` is the lower-level, per-bucket health readout (avoids expensive blob enumeration). `bucket list` is the bounded, paginated, accelerator-backed per-trace inventory — it reads the security/sync status accelerator once and applies filters before projection, so it stays fast even on a large fleet bucket; use `--limit`/`--cursor` to page, `--count` to get just a match count without materializing rows, and the facet flags (`--unsynced`, `--unfiltered`, `--security-stale`, `--unscanned`) to slice by sync/security state. `bucket verify` recomputes blob hashes and checks for dangling references. `bucket status` and `bucket list` are side-effect-free reads: they never write under the bucket. A full rebuild of the manifest and per-trace envelopes from canonical state is explicit via `bucket repair`. ## Repair And Replay ```bash opentraces bucket repair --json opentraces bucket replay --repo /path/to/git-clone --json ``` `bucket repair` re-projects envelopes and the manifest from canonical events and blobs — the documented crash-recovery primitive, idempotent and safe to re-run. `bucket replay` reconstructs the canonical Trace Trails Git event ref in another Git repository from bucket-exported events. These are the operations that may consume the historical bucket events mirror as a fallback. Ordinary live session ingest does not use the aggregate mirror to backfill one trace when its live Trail slice is empty; it preserves the last authoritative per-trace companion and anchor projection instead. ## Remote Sync ```bash opentraces bucket connect opentraces bucket sync status --json opentraces bucket sync diff --json opentraces bucket sync push --dry-run --json opentraces bucket sync push --json opentraces bucket sync pull --json ``` Sync order is substrate-aware: blobs, then events, then envelopes, then the manifest. A configured bucket remote does not publish dataset rows. `bucket sync push` is the gated egress seal: it computes an auditable `pushed[]`/`withheld[]` partition and refuses — zero bytes egressed, non-zero exit — if any trace is not cleared for sync. Run `--dry-run` first to preview the partition without egressing anything; if traces are withheld, clear them with `opentraces bucket security run --all` and re-check. `bucket connect` requires authentication: run `opentraces auth login` first, or it exits with a `run 'opentraces auth login'` hint. The wizard then prompts for a bucket security policy (recommended / basic / strict / off / custom) before configuring remote sync. ## Bucket Security Policy Bucket security protects raw captured evidence before `bucket sync push`. The policy is a named bundle over the same `cfg.security..enabled` flags that `setup ` and `config set security..enabled` flip, scoped to the bucket. ```bash opentraces auth login opentraces bucket connect opentraces bucket security status opentraces bucket security policy --policy recommended opentraces bucket security policy --tool regex --enable opentraces bucket security policy --tool entropy --disable opentraces bucket security status --json ``` `bucket security status` is a read-only inspector: it prints the active policy and enabled tools without writing config. `bucket security policy --policy` applies an exact bundle and accepts only `off|basic|recommended|strict`. `bucket security policy --tool ... --enable` or `--tool ... --disable` (repeatable, needs exactly one of enable/disable) edits one tool at a time. `bucket security run [--all | --trace ]` applies the configured filter to existing records. `bucket security policy --json` emits `{status, security:{enabled, tools, scope:"bucket", policy, available_policies}, changes:{enabled,disabled}}`. Policy bundles: | Policy | Tools | |--------|-------| | `off` | (nothing) | | `basic` | regex, entropy | | `recommended` | regex, entropy, business_logic, path_anonymizer, classifier | | `strict` | regex, entropy, trufflehog, privacy_filter, business_logic, path_anonymizer, classifier | Bucket security flags are machine-global (the same `cfg.security..enabled` flags capture-time sanitization reads), so applying a policy can turn OFF a tool you enabled for another purpose; the CLI prints a warning naming any tool it disables. When `bucket connect` runs non-interactively (for example with `--json`, in CI, or any non-TTY), it applies the `recommended` policy by default so a remote-syncing private bucket is never left with zero redaction; pass explicit `--enable-security-tool` / `--disable-security-tool` flags to override. ## Cleanup ```bash opentraces bucket reclaim --json opentraces bucket reclaim --apply --json ``` `bucket reclaim` lists (and, with `--apply`, removes) leaked Trace Trails cruft under `.git/**/opentraces/` — stray temp files and orphan accelerator pickles. It is dry-run by default and never touches events or `trace.json`. Orphan-blob cleanup and single-trace blob warming (the old `bucket prune` / `bucket prefetch`) are hidden-but-callable advanced ops now superseded by `verify` / `repair` / `reclaim` for everyday maintenance. --- # Trace Discovery Trace discovery is the deterministic search layer over the private bucket. It lets workflows find the right trace evidence without loading full transcripts. The four visible trace verbs form a loop: `query` (search) -> `get` (pull up) -> `map` (dissect) -> `slice` (extract). All four are pure derive-on-demand reads over the Trace Index — nothing here persists or bumps the schema version. ## Trace Index The Trace Index is the underlying projection: retained traces compiled into bounded search documents with lexical, semantic, file, tool, skill, dependency, and survival facets. It self-maintains behind `trace query`; the standalone `trace index` command that used to rebuild/inspect it directly is now hidden (still callable, off `--help`). ## Query ```bash opentraces trace query --lex "bug fix failing test" --cwd --json opentraces trace query --skill opentraces --since 7d opentraces trace query --files "src/**/*.py" --signal failing-test opentraces trace query --survival alive_on_path --candidate-kind trace opentraces bucket sync pull --force --json # sync remote traces before querying opentraces trace query --cwd --json ``` `trace query` returns `CandidatePacket` rows. Use `--include-slice intent` or `--include-slice evidence` when the consumer needs a bounded packet attached to the search hit. The `--semantic` facet is a lightweight concept layer, not embeddings: it expands a small static dictionary of service/library concepts (for example Hugging Face, MongoDB, OpenAI) and joins on concept ids when the query matches one. Anything else degrades to lexical full-text search where every token must match, so paraphrased queries can return zero hits even when related traces exist. Prefer query terms that actually appear in the trace. ## Skill Usage ```bash opentraces trace query --skill opentraces --json opentraces trace query --skill opentraces --since 7d --json opentraces dataset new opentraces-episodes --from-skill opentraces opentraces dataset run opentraces-episodes --executor script --json ``` `--skill` is a facet on `trace query` (the old standalone `trace skills` command is now hidden, folded into this filter). It matches the exact `skill.name` facet against the compact search snapshot's `skill_invocations` table, returning candidates ranked by relevance with agent/source/project breakdowns and telemetry. Use `dataset new --from-skill ` when the next step is a reviewable `skill-episodes-v1` dataset for one observed skill. ## Map ```bash opentraces trace map --json opentraces trace map --bursts --json opentraces trace map --waste --json opentraces trace map --run-intel --json opentraces trace map --around s42 --depth 2 ``` `trace map` returns a deterministic evidence graph. `--bursts` groups nearby file edits into `change_burst` nodes with structured intent: ```json { "intent": { "trigger": "...", "most_substantive_spec": { "text": "...", "step": 12 }, "spec_chain": [], "burst_commit_sha": "abc123...", "commit_subject": "fix: parser edge case" } } ``` ## Slice ```bash opentraces trace slice --template bursts --json opentraces trace slice --around-step 7 --radius 3 opentraces trace slice --around-patch opentraces trace slice --from-step 5 --to-step 12 opentraces trace slice --by user-turn --json opentraces trace slice --by change-burst --json opentraces trace slice --by milestone --json opentraces trace slice --by subgoal --json ``` Trace Slices are bounded packets for workflows. They are not dataset rows by themselves; workflow templates decide how to project them. `--by user-turn|change-burst|milestone|subgoal` tiles the WHOLE trace into a `Trajectory[]` array (the frozen `opentraces.slicing.v1` envelope) — this absorbs the former standalone `trace partition --by s1|s2|s3|s4` command, which is now hidden. `user-turn`/`change-burst` are deterministic; `milestone`/`subgoal` are cheap-LLM resolved via `--judge` (default `agent`: when judgments are needed the command exits `rc=10 needs-judgment` with `JudgmentRequest`s to answer and re-run with `--answers`). ## Get ```bash opentraces trace get --json opentraces trace get --bursts --json opentraces trace get --waste --json opentraces trace get --run-intel --json opentraces trace get --remote owner/private-bucket opentraces trace get ot://trace//patches//trail --json opentraces trace get :7 --json # step 7 of the trace opentraces trace get :last --json # the last step ``` `trace get` is the full retrieval and resolver surface: the "pull up" step after `query` points to the exact trace/unit/resource a workflow needs. It also resolves the full `[:step | :last | :A-B]` address grammar — the same `trace:step` token that `ctx` (`:step` / `:last`) and `trail` (`:step`) resolve, so the three substrates share one address for the "resume triple" (action / context / world) at a given step (`ctx`'s `:A-B` span and `trail`'s `:last`/`:A-B` are reserved for a future release). `--remote` names the HF bucket repo to read from (replaces the old `--remote-bucket` flag, which still works but is hidden). ## Intelligence Deterministic, derive-on-demand signals about how a run actually went, layered on the same trace surface. Nothing is persisted; each is computed on read and emitted as a frozen JSON envelope. `--waste` and `--run-intel` are accepted on both `trace map` and `trace get` (byte-identical payloads), and are mutually exclusive with `--bursts` and each other. ```bash opentraces trace get --waste --json # context-waste findings opentraces trace get --run-intel --json # resteer/recovery/loop/failure opentraces trace compare --json # two-run delta (hidden, still callable) ``` - `--waste` emits `opentraces.context_waste.v2`: oversized tool outputs (>= 12000 chars), the same file read 3+ times in 20 minutes, and search commands repeated 5+ times in 10 minutes, with a `summary` count block. Override the thresholds with `--large-output-chars`, `--file-read-window-min`, and `--search-window-min`. - `--run-intel` emits `opentraces.run_intel.v1`: deterministic `resteer` / `recovery` / `loop` / `failure` signals plus `counts`. Recovery only fires after an uncleared failure; a repeated command is one `loop` signal carrying `evidence.repeat_count`. ## Compare `trace compare` is not one of the four visible job verbs (query / get / map / slice) — it's hidden in v7 but still callable and `--json`-scriptable: ```bash opentraces trace compare --json opentraces trace compare --no-quality --json opentraces trace compare --burst-gap 50 --json ``` `trace compare` emits `opentraces.trace_compare.v1`: per-side `fidelity` plus `{a, b, delta}` triples over token/cost metrics, deterministic quality persona scores (skip with `--no-quality`), and burst/error/security signals. Both traces are pinned to the same burst gap (`--burst-gap`, default 35) so the deltas are comparable. See [Commands](/docs/cli/commands) for the full envelope shapes. --- # Trace Trails Trace Trails answer: what did this trace change, and where does that change live in Git now? They are the Git evidence layer for traces. The trace says what happened during the agent session. Trails preserve the patch history of that trace against Git history: the original patch, the commit anchor, and whether the authored content still survives at `HEAD`. ## Principles - **Trace patches are the unit of authorship.** A patch is one tool-produced change or hunk, not one whole file and not one final diff. - **Git anchors are evidence, not guesswork.** The post-commit path matches trace patches into commits and records the confidence of that match. - **Survival can change over time.** A patch may be alive, transformed, reverted, moved, partially preserved, or lost as Git history evolves. - **The log is canonical.** Snapshot refs and CLI projections are rebuildable from append-only Trail events. The canonical substrate is an append-only event log under: ```text refs/opentraces/local/events/v1 ``` Bucket exports mirror that event log in `bucket/events/v1/`, which makes the bucket portable across machines. Live capture keeps that portability path out of the one-trace hot path. It builds patch rows from the current generation's emitted or reconciled events and reads only the trace's live Trail slice; an empty live slice does not trigger an aggregate mirror fallback or clear prior companion/anchor evidence. The mirror remains the source for explicit repair, replay, and cross-machine reconstruction. ## Mental Model | Concept | Meaning | |---------|---------| | Trace Patch | One tool-produced change/hunk from a trace | | Git Anchor | Evidence that a Trace Patch landed in a commit | | Survival State | Current status of that patch at `HEAD` | | Trail Event | Append-only fact used to rebuild projections | | `ot://` resource | Stable resolver path for a trace patch, Git anchor, file line, or context node | Schema `0.7.0` stores compact patch refs in `TraceRecord.patches[]`. Full patch history and survival observations live in the Trail companion (`trail.jsonl.gz`) and the Git event log. ## Commands ```bash opentraces trail blame opentraces trail blame commit opentraces trail blame commit c: src/main.py --lines opentraces trail blame commit t: --include-overlapping opentraces trail pr render --base main opentraces trail pr create --base main opentraces trail pr update --base main opentraces trail graph opentraces trail graph --trace opentraces trail track opentraces trail track --patch opentraces trail track --anchor opentraces trail track --since 12h --json opentraces trail opentraces trail : ``` `trail blame` is a group; a bare `trail blame ` dispatches to `trail blame commit` directly, which resolves attribution. `trail pr` — render/create/update — was lifted to a top-level `trail` verb since it is the family's one gated GitHub write, separate from the pure read verbs (`blame` / `track` / `graph`); it consumes branch-context workflow rows and renders a PR body. The old `trail blame pr render|create|update` path stays callable but hidden from `--help`. A bare `trail ` (and `trail :`) resolves a per-trace lineage card / step world without naming a subcommand, mirroring `ctx`'s addressing. ## Survival States | State | Meaning | |-------|---------| | `alive_on_path` | Authored content survives at the anchored path | | `alive_transformed` | Content survives with formatter/refactor divergence | | `reverted` | The change was explicitly reverted | | `lost` | The anchored content no longer survives | | `unknown` | The walker cannot prove current state | | `alive_moved` | Content survived after a move/rename | | `partially_preserved` | Some authored lines survive | | `repaired` | A later change repaired/touched the anchored range | ## Common Reads ```bash opentraces trail blame commit ac019172 opentraces trail blame commit ac019172 src/auth.py --lines opentraces trail blame commit t: --include-overlapping opentraces trail track --json ``` Commit mode answers which traces contributed to a commit. Trace mode answers which commits carry a trace's output. `trail track` follows one trace, patch, or anchor through the current Git state. ## PR Consumer ```bash opentraces trail pr render --base main opentraces trail pr create --base main opentraces trail pr update --base main ``` The PR consumer is workflow-based. It runs the bundled `pr-intent-summary-v1` workflow over the branch context and renders one row per commit with deterministic trace lineage and intent summaries. --- # Context Tree The Context Tree answers: what did the model see at this point in the trace? It is the sibling substrate to Trace Trails. Trace Trails tracks what changed; Context Tree tracks the visible context used to make that change: messages, system instructions, tool registry, runtime state, compaction boundaries, and resume points. Schema joins: - `Step.context_node_id` - `TraceRecord.context_tree_summary` ## Principles - **Context is geometry.** A trace is not just a transcript; it is a tree of model-visible context nodes across turns, compactions, forks, and resumes. - **Layers are content-addressed.** Context nodes point to layer blobs, so the bucket can move the environment without embedding every byte into each row. - **The bare noun is the read.** `ctx ` renders the overview; there is no separate lookup step for "what did the model see" — reads and writes are a `--json | jq` decomposition of that same view, not their own subcommands. - **Resume is a first-class output.** `ctx resume` creates a structured packet for an agent to restart from a context node. ## Commands ```bash opentraces ctx # overview: shape + capture method opentraces ctx : # model input at that step opentraces ctx :last # the final / active step opentraces ctx : --layer system|messages|tools|runtime opentraces ctx : --full # inline the full hydrated model input opentraces ctx --json ``` `ctx [:|:last]` is the bare-noun context read (`opentraces.ctx.view.v1`): no subcommand needed, the ref IS the command. `:` is the same universal address that `trace get` and `trail` resolve, so the identical ref can be piped between them (`opentraces trace query --json | opentraces ctx --json`). `--layer` renders one layer readably instead of the bounded four-layer card; `--full` inlines the complete hydrated model input (the fork/eval-row packet). `ctx list` and `ctx info ` (manifest-only inventory reads, no blob loads) and the old subcommand forms (`ctx tree`, `ctx show`, `ctx step`, `ctx reads`, `ctx writes`, `ctx diff`, `ctx compactions`, `ctx resume`, `ctx prune`, `ctx resolve`, `ctx anchor-for-step`) remain callable and `--json`-scriptable; the bare-noun ref is the documented entry point going forward. ## Layers Context nodes reference content-addressed layers. `ctx : --layer ` takes the human-facing alias on the left; it maps to the substrate's layer type on the right: | `--layer` alias | Layer type | Meaning | |-----------------|------------|---------| | `system` | `system` | System prompt and instruction context | | `messages` | `messages` | Conversation messages in scope | | `tools` | `tool_registry` | Tools and schemas visible to the model | | `runtime` | `runtime_state` | Captured runtime settings and state hints | ## Capture Sources The substrate accepts additive capture sources, and `ctx ` is honest about which one produced a given node — a two-rung fidelity ladder, `jsonl` (structure-only) or `otel` (full wire): | Source | `capture_method` | Fidelity | Notes | |--------|-------------------|----------|-------| | JSONL reconstruction | `transcript_reconstruction` | `jsonl` | Works from agent session logs; structure-only — message content sizes are not stored, so `ctx ` draws no size chart, and some views are session-level approximations | | Pi extension sidecars | `live_capture` | `jsonl` | Provider/context sidecars use fuller messages/tool registry/runtime state when available; transcript fallback records limitations | | OTLP receiver | `otel` | `otel` | Byte-perfect wire capture for Claude Code — full API body, tool schema, and sampling parameter evidence; `ctx ` reports `fidelity: otel` and draws a token-size sparkline from it | | HTTP proxy | `proxy` | — | Reserved/deferred; historical prototype, not the current path | ## OTLP Operations ```bash opentraces setup capture-otlp opentraces capture-otlp start opentraces capture-otlp status --json opentraces capture-otlp flush --session --project --trace-id opentraces capture-otlp flush --from-raw-bodies --session --project --trace-id opentraces capture-otlp stop ``` `flush` reconstructs the session into Context Tree nodes (one node per llm-request), lands them in the bucket companion, and joins them to the trace spine via `Step.context_node_id` and `TraceRecord.context_tree_summary`, so `ctx show` / `ctx step` can hydrate the captured context. `--from-raw-bodies` reconstructs an already-captured session per-step straight from the raw request/response bodies (paired by `session_id` and the message chain) with no live receiver — the full-fidelity path. `capture-otlp status` lists the captured session ids available to flush. You usually do not need to run `flush` by hand: the watcher tick auto-flushes a project's active OTel sessions into the bucket once each session goes idle (zero-touch, watermark-gated, at most once per session), so live captures land on the trace automatically. Run `flush` explicitly only to land a session immediately or to reconstruct an already-captured session retroactively with `--from-raw-bodies`. If the receiver is down, agent traffic is not blocked. Claude Code's OTel emission is fire-and-forget. ## Use Cases - resume from a specific context node; - compare two steps after compaction; - build training rows that include the visible context; - audit whether a tool write was made with enough surrounding information; - warm a new session by pulling only the relevant prior trace context. --- # Dataset Workflows Dataset workflows are skill-format packages that turn bucket traces into typed row streams. They are the boundary between retained evidence and the dataset you actually want: eval rows, SFT examples, branch summaries, bug capsules, or custom training objectives. This is separate from the trace workflow. Capture fills a bucket; dataset workflows read that bucket and decide what row shape to emit. ## Principles - **A workflow is purposeful.** It encodes the row schema and training or evaluation objective. - **Discovery stays deterministic.** Workflows use `trace query`, `trace map`, `trace slice`, `trail`, and `ctx` commands to find and bound evidence. - **Rows are projections.** A row may contain summaries, references, scores, or a small evidence closure; it is not the raw trace. - **Security is explicit.** Workflows opt into the security tools they require. ## Manage Workflows ```bash opentraces workflow templates --json opentraces workflow create my-workflow --template skill-command-trajectory-eval-v1 opentraces workflow create my-workflow --template default --description "Curate bug fixes" opentraces workflow list --json opentraces workflow remove my-workflow --yes ``` Generated workflows live under the local workflows directory and can be bound to datasets: ```bash opentraces dataset new my-dataset --workflow ./workflows/my-workflow/ opentraces dataset run my-dataset --dry-run --limit 5 opentraces dataset run my-dataset ``` For a dataset around one observed skill, use the built-in skill episodes workflow directly: ```bash opentraces trace query --skill opentraces --json opentraces dataset new opentraces-episodes --from-skill opentraces opentraces dataset run opentraces-episodes --executor script --json ``` ## Runtime Contract The script executor runs: ```bash /scripts/build_rows.py ``` with: | Env var | Meaning | |---------|---------| | `OT_RUN_PACKET` | JSON packet describing scope, trace candidates, dataset, and workflow metadata | | `OT_DATASET_OUTPUT` | JSONL path the script must write | The dataset-free primitive is `execute_workflow(workflow_name, scope, output_path)`. Dataset runs wrap that primitive with manifest, cursor, review, and publication state. This is the single execution seam: both the dataset-bound runner (`dataset run`) and the dataset-free primitive go through `execute_workflow`, so a workflow's judgment handshake, isolation, and sanitization behave identically whether or not a dataset manifest is involved. ## The Judgment Handshake (rc=10) Per [ADR-0008](https://github.com/JayFarei/opentraces/blob/main/docs/adr/0008-seal-family-contract.md), judgment is an INPUT to a projection, not an exception to it. A workflow's `scripts/build_rows.py` that needs a judgment call (for example, grading whether an episode succeeded) does not resolve it inline: it writes structured `JudgmentRequest`s to the sidecar file named by `OT_JUDGMENT_SIDECAR` and exits with `RC_NEEDS_JUDGMENT` (10). The script executor detects the sidecar + `rc=10` and propagates the handshake up: ```bash opentraces dataset run my-dataset --json # exits rc=10, prints: # [judgment] : ``` Answer each request, write them to a file, and re-run with `--answers`: ```bash opentraces dataset run my-dataset --answers answers.json --json ``` The answers travel into the run packet (`packet["answers"]`) and are recorded in row provenance's `answers` block (see [Row Provenance: The Contract Triple](datasets.md#row-provenance-the-contract-triple)), so re-running with the same answers against the same bucket state is byte-identical: the projection stays a pure function of `(workflow, bucket state, answers)`. The `JudgmentRequest`/`JudgmentAnswer` shapes are reused verbatim from the `trace partition` slicing judgment handshake (schema `opentraces.slicing.judgment.v1`), one answer shape across the whole system. ## Evidence Inputs A workflow can use the trace substrates directly: ```bash opentraces trace query --lex "fix failing test" --cwd --json opentraces trace map --bursts --json opentraces trace slice --template bursts --json opentraces trail track --json opentraces ctx :7 --json opentraces ctx resume --json ``` Typical row builders do a progressive read: query for candidates, map the candidate trace, slice the relevant span, then attach Trail and Context evidence only when the row schema needs it. ## Built-In Templates | Template | Purpose | |----------|---------| | `default` | Minimal scaffold for custom row builders | | `skill-episodes-v1` | Skill episode rows for one observed skill, normally created with `dataset new --from-skill ` | | `skill-command-trajectory-eval-v1` | Compact eval rows for command/skill trajectory attribution | | `pr-intent-summary-v1` | Branch-context rows consumed by `opentraces trail pr render/create/update` | ## Everything-Style Workflows The general pattern is: choose a schema, choose a trace scope, choose the evidence closure, and emit rows. A workflow can be broad enough to support an "everything" dataset for one objective, while still keeping the raw bucket private. For example, a command-trajectory workflow may include: - the user intent summary from `trace map --bursts`; - the bounded step window from `trace slice`; - patch survival from `trail track`; - visible context from `ctx :` or `ctx resume`; - security metadata from an explicit `security sanitize` pass. ## Security Contract A workflow declares the security posture of the rows it projects in its `SKILL.md` / `WORKFLOW.md` YAML front matter, under a `security:` block: ```yaml security: required_tools: [regex, entropy] optional_tools: [business_logic, path_anonymizer] default_enabled_tools: [business_logic] disallowed_tools: [] allow_disable_required: false ``` | Key | Meaning | |-----|---------| | `required_tools` | MUST run; cannot be disabled unless the contract allows it | | `optional_tools` | MAY be toggled per dataset | | `default_enabled_tools` | On when a dataset is first seeded (subset of required ∪ optional) | | `disallowed_tools` | Never run | | `allow_disable_required` | Whether a downstream dataset may disable a required tool at all | A dataset security contract may only reference the tools that can actually run over a projected row: `regex`, `entropy`, `privacy_filter`, `business_logic`, and `path_anonymizer`. The remaining registry tools (`trufflehog`, `llm_pii`, `capsule_scope`, `classifier`) operate on full `TraceRecord` structure and cannot sanitize a row dict, so a contract that lists them is rejected at `opentraces dataset new`. Unknown tool names are also rejected. When you bind a workflow to a dataset with `opentraces dataset new --workflow `, this contract seeds the dataset's resolved manifest policy and is pinned to the workflow digest. After that, the policy is managed per-dataset with `opentraces dataset security `. ## Security In Workflows Security tools are optional and default off. A workflow can invoke them explicitly: ```bash printf '%s\n' '{"row": {...}}' \ | opentraces security sanitize --tools regex,entropy,path_anonymizer ``` or use the loaded config: ```bash printf '%s\n' '{"record": {...}}' \ | opentraces security sanitize --use-config ``` This keeps the dataset contract explicit: the workflow decides what row shape and what sanitization are required for its objective. --- # Dataset Rows A dataset is a local HuggingFace-shaped row store produced by a workflow. It is not the raw trace bucket. Per the [Seal Family contract](https://github.com/JayFarei/opentraces/blob/main/docs/adr/0008-seal-family-contract.md) (ADR-0008), a dataset is one of exactly two things that "seal": a **growing, reviewed seal**, appended to under review gates, with every row carrying the full contract triple back to its source. (The other is a [capsule](../clients/trace-capsule.md), an immutable, URL-addressed seal of one scope.) A PR body, a standup, or a dashboard built from dataset rows is a *rendering* of the projection, not a seal. ## Create ```bash opentraces workflow create my-workflow --template default opentraces dataset new my-dataset --workflow ./workflows/my-workflow/ ``` Skill-episode datasets can start from observed skill usage without writing a custom workflow: ```bash opentraces trace query --skill opentraces --json opentraces dataset new opentraces-episodes --from-skill opentraces ``` Ad-hoc row seeding is available when you already have JSONL: ```bash opentraces dataset new my-import --rows-file rows.jsonl --schema schema.json ``` ## Run ```bash opentraces dataset run my-dataset --dry-run --limit 5 --json opentraces dataset run my-dataset opentraces dataset run opentraces-episodes --executor script --json opentraces dataset run my-dataset --scope trace --trace opentraces dataset run my-dataset --since-last-run opentraces dataset run my-dataset --answers answers.json --json ``` `dataset run` invokes the workflow and appends rows locally. It can read from Trace Index candidates, a project scope, the current working directory, a specific trace, or the saved skill query from `dataset new --from-skill`. The `script` executor runs the workflow package's deterministic `scripts/build_rows.py` without a live agent. ## Row Provenance: The Contract Triple Per ADR-0008, a projection is explainable iff it is a pure function of content-addressed inputs: `projection = f(scope_ref, workflow@digest, bucket_state@digest, answers)`. Every appended row records this as its provenance (`.opentraces/row_provenance.jsonl` inside the dataset directory, schema `opentraces.dataset.row_provenance.v2`): ```json { "ref": ":120-148", "workflow": {"skill": "skill-command-trajectory-eval-v1", "digest": "sha256:…"}, "bucket": {"manifest_digest": "sha256:…", "snapshot_digest": "sha256:…"}, "answers": {"digest": "sha256:…", "recorded": {}}, "contract_triple": { "workflow_digest": "sha256:…", "bucket_digest": "sha256:…", "answers_digest": "sha256:…" }, "reconstructable": true, "isolation": {"sandbox_tier": "none"} } ``` `reconstructable` is an honesty label on the run that produced the row: `true` for the `script` executor (a recorded, re-runnable transform) and `dry-run`, `false` for `current-agent` (raw agent emission is not a recorded input, so it is never claimed reconstructable). `contract_triple` is the convenience roll-up of the three content digests that pin the row; `answers` carries the fourth input, recorded judgment answers from a `--answers`-driven run (see [Dataset Workflows: the judgment handshake](workflow-templates.md#the-judgment-handshake-rc10)), digested even when empty so the triple is never partially populated. ## Verify ```bash opentraces dataset verify my-dataset --json ``` Re-executes the bound workflow against the bucket (with the recorded answers, from the same run packet the contract triple points at) in a side-effect-free mode, projects the re-run through the same sanitize/validate/dedup/canonical transform an append would apply, and byte-compares it against the stored `data/train.jsonl` rows. Classifies the outcome into exactly three honest verdicts: | Verdict | Meaning | Exit code | |---------|---------|-----------| | `reproduces` | The re-run rows are byte-identical to the stored rows | 0 | | `bucket-advanced` | Stored rows are a strict subset of the re-run and the bucket watermark moved past the recorded one (an explained delta) | 0 | | `integrity-failure` | A stored row was hand-mutated (its bytes no longer hash to the recorded `payload_hash`), or the rows differ with no watermark explanation | 7 | `--json` emits the frozen `opentraces.dataset.verify.v1` envelope (`verdict`, `stored_row_count`, `reproduced_row_count`, `byte_identical`, `delta`, `mutated_rows`, `recorded_watermark`, `current_watermark`, `detail`). The command never appends rows or advances a cursor/watermark; it is a pure read that enforces the integrity invariant on the transform (digested bytes == installed bytes == executed bytes). ## Dataset Security Policy Each dataset carries its own resolved security policy in the manifest (`DatasetManifest.security`). It is seeded from the source workflow's front-matter `security:` contract at `dataset new --workflow ` and pinned to that workflow's digest (`source_workflow_digest`). The resolved `enabled_tools` start as the contract's required tools plus its `default_enabled_tools`, in canonical registry order. **The reader's security floor is non-overridable.** Every dataset row is sanitized by at least `regex`, `entropy`, `business_logic`, and `path_anonymizer` — the `DATASET_ROW_FLOOR` — regardless of what the source workflow's contract declares. A third-party workflow contract can only *add* tools to this floor, never narrow below it; the floor runs even when a contract enables a smaller set and even at `privacy_tier: off` (which therefore no longer ships rows verbatim). Because the floor is unioned in as the last resolution step, no policy flag — including an authored `allow_disable_required` + `--unsafe-override` — can drop a floor tool from what actually runs (an override on a floor tool is recorded but does not weaken sanitization). Each row records its author-declared `requested_tools` distinctly from the floor-resolved `effective_tools`, and `dataset status --json` surfaces `security.reader_floor` + `security.floor_satisfied` so the guarantee is inspectable. This enforces the promise that the redaction rules that run are the consumer's, not the author's. The policy is per-dataset, not a global config toggle. Toggling a tool on one dataset never affects another dataset or the bucket egress policy. ```bash opentraces dataset security my-dataset opentraces dataset security my-dataset --json ``` `--json` emits the resolved policy under a `security` block: `source`, `source_workflow_digest`, `required_tools`, `optional_tools`, `enabled_tools`, `disallowed_tools`, `overrides`, `scope` (always `dataset`), `required_satisfied`, and `missing_required_tools`. Toggle an optional tool on a single dataset: ```bash opentraces dataset security my-dataset --tool business_logic --enable opentraces dataset security my-dataset --tool path_anonymizer --disable ``` `--tool` is repeatable and requires `--enable` xor `--disable`. Only optional tools can be toggled this way. A required tool can be disabled only when the workflow contract sets `allow_disable_required: true` and you pass `--unsafe-override` (optionally with `--reason ""`); the opt-out is recorded in the manifest as an override. If the contract forbids it, the command exits 2. ```bash opentraces dataset security my-dataset --tool regex --disable --unsafe-override --reason "rows are synthetic fixtures" ``` This is distinct from `opentraces bucket security`, which governs the machine-wide bucket egress policy over global tool flags. Dataset security governs what a dataset's rows carry before dataset publication. ## Review States | State | Meaning | |-------|---------| | `inbox` | Row needs review | | `approved` | Row is publishable | | `published` | Row was uploaded upstream | | `rejected` | Row is kept local only | | `blocked` | Row needs action before approval | ```bash opentraces dataset status my-dataset --json opentraces dataset review my-dataset --json opentraces dataset review approve my-dataset opentraces dataset review reject my-dataset opentraces dataset review reset my-dataset opentraces dataset review approve my-dataset --all ``` ## Remotes ```bash opentraces dataset remote create my-dataset owner/team-traces --private opentraces dataset remote create my-dataset owner/existing-traces # idempotent: binds an existing HF dataset too opentraces dataset remote list my-dataset --verbose opentraces dataset remote visibility my-dataset owner/team-traces --public opentraces dataset remote remove my-dataset owner/team-traces ``` Dataset remotes are independent of bucket remotes. A private bucket remote can hold raw evidence while a dataset remote holds only approved projected rows. ## Schedules ```bash opentraces dataset schedule add my-dataset --every 1h --approve-new --publish-check-only opentraces dataset schedule list opentraces dataset schedule pause my-dataset opentraces dataset schedule resume my-dataset opentraces dataset schedule remove my-dataset ``` Schedules rerun workflows over retained evidence. They do not bypass review or publication gates unless you explicitly pass approval/publish flags. --- # Dataset Row Review Review applies to dataset rows, not to raw bucket traces. A workflow projects trace evidence into rows; then those rows are approved, rejected, reset, or published. ```bash opentraces dataset status my-dataset opentraces dataset review my-dataset --json opentraces dataset review approve my-dataset opentraces dataset review reject my-dataset opentraces dataset review reset my-dataset opentraces dataset review approve my-dataset --all ``` The legacy `--web` and `--tui` flags currently return decommission notices. Use the CLI row review surface until the next dataset-scoped UI lands. ## Row States | State | Meaning | |-------|---------| | `needs_review` | Awaiting a review decision | | `publishable` | Approved and ready for publish | | `published` | Uploaded upstream | | `rejected` | Kept local only | | `blocked` | Security gate not satisfied; needs action before approval | ## What To Check - residual secrets or PII that the workflow did not sanitize; - internal hostnames, repository paths, or customer identifiers; - rows that are too short, low quality, or unrelated to the dataset objective; - stale Trace Trail survival state if the row depends on live code evidence; - context windows that are too broad or too narrow for the training/eval task. ## Security Tools Security tools are optional and default off. Workflows can run them before rows reach review: ```bash printf '%s\n' '{"row": {...}}' \ | opentraces security sanitize --tools regex,entropy,path_anonymizer ``` Review is still the final human or workflow gate before publication. --- # Dataset Publish `opentraces dataset publish ` uploads approved workflow rows and contract files for a named dataset to its active HuggingFace remote. It never appends to an existing shard in place. ```bash opentraces dataset review approve my-dataset --all opentraces dataset remote create my-dataset owner/team-traces --private opentraces dataset publish my-dataset --check-only opentraces dataset publish my-dataset ``` ## Options ```bash opentraces dataset publish my-dataset opentraces dataset publish my-dataset --to owner/team-dataset opentraces dataset publish my-dataset --check-only opentraces dataset publish my-dataset --min-retention 0.5 opentraces dataset publish my-dataset --exclude-state lost --exclude-state never_committed ``` | Flag | Description | |------|-------------| | `--to TEXT` | Remote name or `owner/name` override | | `--check-only` | Run gates and stage without upload | | `--resume TEXT` | Resume a previous publication run id | | `--min-retention FLOAT` | Drop rows whose mean patch retention is below the threshold | | `--exclude-state TEXT` | Drop rows containing a patch with this survival state; repeatable | | `--json` | Emit structured JSON | ## One Clearance Predicate Per [ADR-0008](https://github.com/JayFarei/opentraces/blob/main/docs/adr/0008-seal-family-contract.md) §3, exactly one predicate decides whether a trace's bytes may leave the private bucket. `bucket sync push`, `dataset publish`, and `capsule share --publish` / `capsule issue --publish` all evaluate the SAME three-way clearance (`cleared` / `not_cleared` / `unknown`) instead of each re-implementing their own lock, and egress is never on by default for any of them. The check is evaluated against a push-time snapshot, not a check-then-copy race: a publish run indexes one manifest snapshot up front and every row in that run is authorized against it, so a trace cannot slip from cleared to not-cleared mid-run. Absence of a recorded clearance (`unknown`) is never coerced to "safe to leave", only a positive `cleared` state permits egress. A refusal moves zero bytes. ## Bucket Sync Is Separate ```bash opentraces bucket sync push opentraces bucket sync pull opentraces bucket sync status ``` Bucket sync moves raw retained evidence. Dataset publish moves approved projected rows. A private bucket remote can exist even when no dataset has been published. `opentraces bucket sync push` is the gated egress seal for the bucket: it recomputes the push-time pushed/withheld partition and, if any trace is not cleared for sync, REFUSES outright — zero bytes egressed, non-zero exit — rather than pushing a partial or unscanned bucket. Preview the partition first with `opentraces bucket sync push --dry-run`, which reports the same `pushed[]` / `withheld[]` (each withheld entry carries a `reason`/`sub_reason`) split without touching the remote. Run `opentraces status` beforehand as the pre-egress safety gate — it is the fleet-wide scanned/unscanned dashboard, and its "safe to sync" verdict is structurally impossible to show green while any trace remains unscanned. (`bucket remote push|pull|diff|status` still work as the old spelling; `bucket sync` is the current one.) ## Security And Publication Gates Publication gates operate on dataset rows. If a workflow requires sanitization or LLM review, it should run those steps before approving rows. ```bash opentraces security tools list opentraces security sanitize --tools regex,entropy opentraces dataset review my-dataset --json opentraces dataset review approve my-dataset --all opentraces dataset publish my-dataset --check-only ``` LLM-assisted row review now runs through `dataset review` / `dataset publish`, not a standalone `setup llm-review` step (that command still works but is hidden — the canonical surface for clearing rows before publish is the review/approve/publish lifecycle above). `dataset publish --check-only` also blocks any row that does not satisfy the dataset's required security tools (block reason `required_security_tools_missing`), alongside the existing review, security-version, and privacy gates. This check is keyed on per-row execution evidence: each row records the tools that actually ran over it (`tools_applied`, in row provenance), and the gate blocks the row if that set does not cover the required tools. So a row appended while a required tool was disabled stays blocked even if the tool is re-enabled afterward. The dataset's required tools come from its manifest policy; inspect or adjust them with `opentraces dataset security `. Rows without an approval state are filtered out. Gate failures surface in the CLI output and, in JSON mode, in the publication payload. ## Upload Shape Each publish creates a new shard: ```text data/ rows_20260521T142300Z_a1b2c3d4.jsonl rows_20260521T151500Z_e5f6a7b8.jsonl ``` The dataset card and schema contract files are regenerated from the local models and row manifest. --- # Clients & Use Cases Clients consume either published dataset rows or private bucket evidence. The important distinction is whether the client needs curated rows or the retained trace environment. | Client / use case | Primary input | Start here | |-------------------|---------------|------------| | Dataset consumers | Published dataset rows | [Dataset Consumers](/docs/clients/dataset-consumers) | | Private bucket clients | Remote bucket evidence | [Private Bucket Clients](/docs/clients/private-bucket) | | Agent workflows | Trace discovery + context + Trail evidence | [Agent Workflows](/docs/clients/agent-workflows) | | Trace capsule | Privacy-bounded agent usage episode ("Agent Experience Report") | [Trace Capsule](/docs/clients/trace-capsule) | Published rows are for evaluation jobs, training loops, analytics, dashboards, and sharing. Bucket evidence is for agents or teammates that need to inspect a trace environment: raw trace records, Trail anchors, Context Tree nodes, or source blobs. The same captured trace can feed multiple clients. One workflow can publish small eval rows, another can warm an agent with relevant prior context, and a third can package a capsule — a privacy-bounded usage episode — for sharing or filing as a GitHub issue. --- # Dataset Consumers Published datasets are workflow-projected rows on Hugging Face Hub. Use this path for evaluation jobs, teacher/student training, SFT/RL pipelines, dashboards, and public or private dataset sharing. ## Load Rows ```python from datasets import load_dataset ds = load_dataset("owner/team-traces", split="train") print(ds[0]) ``` For streaming: ```python from datasets import load_dataset ds = load_dataset("owner/team-traces", streaming=True) for row in ds["train"]: print(row) ``` Rows are workflow-specific. A command-trajectory eval dataset and a PR intent summary dataset will not have the same row schema, even if they came from the same bucket traces. A dataset is a growing, reviewed seal (ADR-0008): each row was appended under review gates and carries its own provenance record back to the workflow digest, bucket state digest, and any recorded judgment answers that produced it (see [Row Provenance: The Contract Triple](../workflow/datasets.md#row-provenance-the-contract-triple)). Consumers relying on a dataset for grading or replay can check `opentraces dataset verify ` upstream of publication to confirm the published rows still reproduce from the recorded inputs. ## File-Oriented Access For published Hugging Face datasets, the third-party `hf-mount` tool can expose shards as files: ```bash hf-mount start repo datasets/your-org/agent-traces /mnt/traces ls /mnt/traces/data/ head -n 1 /mnt/traces/data/*.jsonl hf-mount stop /mnt/traces ``` `hf-mount` is an external tool, not part of opentraces; install it separately and check its own documentation for platform support. For private or gated datasets, authenticate with Hugging Face first. ## Resolve Back To Evidence Rows should carry enough trace references for a consumer to retrieve bucket evidence when it is allowed: ```bash opentraces trace get --remote owner/private-bucket --json opentraces trail track --json opentraces ctx : --json ``` That lookup is separate from dataset loading. A public dataset can reference a private bucket without exposing the bucket itself. --- # Private Bucket Clients Private bucket clients need retained trace evidence rather than curated row streams. Typical clients are teammate agents, local dashboards, replay tools, debugging scripts, and workflow builders that need raw context. ## Read From A Remote Bucket The bucket is self-sufficient local-or-remote: connect it once with `bucket connect`, then every read verb below (`bucket sync`, `trace`, `trail`, `ctx`) works the same whether the evidence is already local or still on the configured remote. ```bash opentraces bucket connect --repo owner/private-bucket # one-time: bind the remote target opentraces bucket sync pull --json # sync remote traces before querying opentraces trace query --cwd --json opentraces trace get --remote owner/private-bucket --json opentraces trail blame commit --json opentraces ctx --json ``` `trace query` starts with bounded candidates. `trace get`, `trail`, and `ctx` then load the precise trace units, Git evidence, and Context Tree nodes the client needs. `opentraces status` is the safety gate to check first, its green "safe to sync" verdict is structurally impossible while any trace in the bucket is unscanned. ## Portable Environment Pattern A bucket is portable because it carries: - `trace.json`, the compact `TraceRecord` spine; - Trail event mirrors for Git evidence replay; - Context Tree companions and content-addressed layer blobs; - raw/source blobs referenced by the trace; - a manifest that makes remote sync and lazy fetch deterministic. That means a client can pull a bucket remote, inspect a trace, and replay Trail events into another Git clone without the original machine. ```bash opentraces bucket sync pull --json opentraces bucket sync pull --eager --json # eager: fetch every referenced blob upfront, not just envelopes opentraces bucket replay --repo /path/to/git-clone --json ``` For warming a single trace's blobs ahead of a cold-cache read, the advanced `opentraces bucket prefetch --remote owner/private-bucket` verb is still callable (hidden from `--help`); `bucket sync pull --eager` is the visible, whole-bucket equivalent. Bucket evidence can be large and private. Do not treat a bucket remote as a public dataset unless you intentionally made that storage public. --- # Agent Workflows Agent workflows are clients that use opentraces as working memory. They search prior session history, progressively load relevant trace evidence, and hand a small packet to the next agent step. This is not dataset publication by default. It is an agent using the trace bucket to orient itself. ## Context Warmup Natural-language prompt: ```text Find prior traces in this repo related to the current task. Start with a broad trace query, inspect only the most relevant candidates, then load the smallest context and Trail evidence needed to brief the next agent step. Do not publish anything. ``` Commands: ```bash opentraces trace query --cwd --lex "auth middleware regression" --json opentraces trace map --bursts --json opentraces trace slice --template bursts --json opentraces ctx :7 --json opentraces ctx resume --json opentraces trail track --json ``` The output is a compact working packet: the relevant intent, bounded trace slice, context resume packet, and Trail survival evidence. ## Progressive Discovery Use the cheapest read first: 1. `trace query` for candidates. 2. `trace map --bursts` for intent and edit clusters. 3. `trace slice` for the bounded step window. 4. `ctx :` (or `ctx resume`) for model-visible context. 5. `trail track` for whether the trace output still survives. The agent only loads full trace content after the candidate is clearly useful. ## Session History Search ```bash opentraces trace query --cwd --since 30d --lex "database migration" --json opentraces trace query --skill opentraces --semantic "huggingface push failing" --json opentraces trace get --bursts --json ``` `--semantic` matches a small concept dictionary (services and libraries seen in trace evidence); queries outside it fall back to all-tokens-must-match lexical search, so keep query terms concrete. This lets an agent ask, "Have we solved something like this before?" without turning the answer into a dataset row. --- # Trace Capsule A **Trace Capsule** is a privacy-bounded record of how one agent used one consumed product: an "Agent Experience Report". It packages a redacted, self-contained slice of a captured session so a maintainer agent or teammate can resolve the episode with a single command. A runnable repro test is **optional evidence, not the point**. Capsules with `test=null` are fully first-class: every verb (`create`, `preview`, `get`, `import`, `share`, `issue`) works identically whether or not the session had a failing command. The seal/consume/write verbs are `capsule create` (seal), `capsule get` (read-only resolve), and `capsule import` (the explicit opt-in write into the local bucket). The pre-v7 spellings `capsule export` and `capsule open` still work (hidden from `--help`, kept callable so existing issue-embedded commands and scripts never break) — this doc uses the current names throughout. The underlying object stays `opentraces.capsule.v1` throughout. The presentation reframe (command noun, `` wire markers, issue idempotency) is unchanged. Per the [Seal Family contract](https://github.com/JayFarei/opentraces/blob/main/docs/adr/0008-seal-family-contract.md) (ADR-0008), a capsule is one of exactly two things that "seal": an **immutable, URL-addressed seal** over one scope, byte-stable under re-seal, with a deterministic `capsule_id`. (The other is a [dataset](../workflow/datasets.md), a growing, reviewed seal of workflow-projected rows.) Everything else that carries capsule content, a rendered issue body, the capsule-worker HTML page, is a *rendering* of that seal, not a seal itself. ## What a Capsule Carries A capsule assembles four signals that make an episode replayable by intent, not by bytes: | Signal | Capsule field | |--------|---------------| | What the agent tried to do | `intent` (deterministic `Burst.intent`) | | What it knew and saw | `context_resume_packet` (4 inlined layers) | | Where it got stuck or finished | `failing_step` + bounded `slice` | | The repo state it ran against | `repo_pin` (remote + sha-pinned commit + changed files) | Optional additive keys (null-tolerant, absent on older capsules without error): - `product` — the consumed product/dependency this episode is bound to (grouping anchor + product-episode slice) - `summary.outcome_taxonomy` — derived outcome: `completed` / `abandoned` / `unclear` - `privacy_scope` — structural egress declaration (bools/ints only; never a classifier verdict) ## Verbs ### `capsule create` — seal a local capsule ```bash opentraces capsule create \ [--from-step --to-step ] \ [--project ] \ [--product ] \ [--include-prompts] \ [--repo-url ] \ [--progress auto|plain|json|never] \ [--out ] \ [--bundle] \ [--json] ``` `` is a v7 address: a whole ``, a point `:`, or a span `:A-B`. The address selects the scope directly — no step/radius flag soup; `--from-step`/`--to-step` is the equivalent explicit span seam. Writes `capsules/v1//capsule.json` + `capsule.md` under `/.opentraces/`. Primary stdout is the `capsule.json` path. `--json` prints the full envelope. - `--product ` binds the capsule to one consumed product and scopes the slice to steps that reference it. - `--include-prompts` opts prompt-bearing fields (system prompt + per-step reasoning) IN; they are excluded by default (see [Layered Redaction](#layered-redaction)). - `--bundle` embeds a hermetic `git archive` at the pinned commit so a later `capsule test --from-bundle` runs even if the commit is later unreachable; the shipped bundle bytes are secret-scanned before `share --publish` (see [Bundle Safety and Sandbox](#bundle-safety-and-sandbox)). - `--progress auto|plain|json|never` reports stage/heartbeat to **stderr** during the slow seal phases (slice / context / trail-anchor projection); the `--json` envelope on stdout stays clean. `auto` is quiet on a non-TTY. `create` is the v7-address seal path; it does not (yet) declare a runnable test command, an explicit `--consume` spec, or seal from a live in-flight session. For those, the pre-v7 `capsule export` spelling stays callable (hidden from `--help`) with its full original flag set: `--test-command`, `--expect-error`, `--setup-command`, `--consume`, `--from-session [--from-agent claude|codex]`, and `--product-full-span`. Both verbs write the same `opentraces.capsule.v1` object; `export` is the richer flag surface, `create` is the v7-address one. ### `capsule preview` — inspect egress before anything leaves ```bash opentraces capsule preview \ [--project ] \ [--product ] \ [--include-prompts] \ [--json] ``` Runs the full redaction pipeline, then prints: - redaction manifest by field path - `business_logic` findings (internal hostnames, collab-tool URLs, DB strings, AWS account ids) - the `privacy_scope` block - the destinations a publish WOULD reach Writes and publishes **nothing**. This is the developer-approval checkpoint. ### `capsule get` — the read-only consume verb ```bash opentraces capsule get [--json/--no-json] [--summary] ``` Resolves a capsule from a file path, `https://` URL, or `hf://` ref. `--json` (the default) prints the frozen `opentraces.capsule.v1` envelope so a maintainer agent can parse it with zero bespoke code. `--summary` prints the human markdown instead. Read-only: no `~/.opentraces`, bucket, or project state is created, so a maintainer in a brand-new environment can `get` a capsule and read it. The `--json` flag is the default and is accepted explicitly so the command embedded in the issue body runs verbatim. ### `capsule import` — the explicit opt-in write ```bash opentraces capsule import [--source-layer