acta

module
v0.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 5, 2026 License: MIT

README

acta

Run coding agents. Keep the whole story.

Coding agents write code fast. Trusting that code is now the slow part.

A diff tells you what changed. It doesn't tell you what the agent read, what it assumed, what it tried and abandoned, or what it never looked at. For agent-authored changes, that is exactly what a reviewer needs, and today it evaporates the moment the terminal scrolls past.

Acta keeps it. It runs Codex or Claude Code noninteractively, streams the output live, and records the entire run as a portable local bundle: the raw agent stream, a normalized trajectory of what the agent did, the workspace diff, and per-write patches with explicit capture status. Review the run, not just the result.

Acta is plumbing, not porcelain. It works fine by hand, but it is built to run headless underneath other software: an issue-resolving orchestrator, a CI pipeline, an eval harness, an agent platform. The launcher decides what runs and why, hands Acta that context (repository, issue, task title), and Acta stamps it into the evidence at capture time. One invocation, one run, one bundle; nothing interactive. Acta disables agent session persistence, while the completed run bundle is intentionally durable local state.

Installation

Tagged releases provide prebuilt archives for Linux and macOS on amd64 and arm64, plus an experimental Windows amd64 build. Download the archive for your platform from GitHub Releases and verify it against the published checksums.txt file before installing the acta binary on your PATH.

Developers with the Go toolchain can install an exact release from source:

go install github.com/nobbettt/acta/cmd/acta@v0.1.0

Automated environments should pin an exact version rather than @latest.

Acta probes the selected agent before every run. The current tested ranges are Codex CLI >=0.147.0,<0.148.0 and Claude Code >=2.1.235,<2.2.0; prerelease versions are rejected. Acta fails closed outside these ranges until the command flags and provider event contract are requalified. The normalized agent version is recorded in every current run record and projection.

Codex sessions are always ephemeral. When a runtime bundle is supplied, Acta also ignores user config, enables strict config validation, and explicitly resolves every supported bundle capability. Claude sessions are not persisted and load project settings (including project instructions) but not user or local settings. Runtime bundles currently support Codex only.

Quick start

make build                  # build the ./acta binary
./acta doctor --agent codex # check Git, paths, and the selected CLI/version
./acta run --agent codex --cwd . --prompt "Summarize this repository"

Claude Code works the same way:

./acta run --agent claude --cwd . --prompt "Summarize this repository"

Or pipe a prompt in:

cat task.md | ./acta run --agent codex --cwd .

What you get

Every run lands in a self-contained bundle under .acta/runs:

.acta/runs/20260701T220000Z-codex-ab12cd34/
  run.json            # run metadata (incl. trace_id when OTLP export is on)
  codex-events.jsonl  # raw agent stream (claude-output.jsonl for claude)
  codex.stderr.log
  event-times.jsonl   # arrival timestamp per raw line (the streams carry none)
  workspace.diff      # staged+unstaged+non-ignored-untracked diff, when non-empty
  digest.json         # normalized timeline, metrics, files touched
  acta-events.jsonl  # stable product/replay events derived from the digest
  projection.json     # generation hashes after reconciliation/re-projection

The digest is the heart of it: a normalized timeline with token and tool metrics, the files the agent read (down to line spans) and edited, independent of which agent produced the run. See Run Bundle for the full format.

Don't Codex and Claude Code already do this?

They do export telemetry: Claude Code can emit OTel metrics, events, and optional traces; Codex has an opt-in [otel] exporter; both keep local session logs. If usage dashboards are all you need (cost, tokens, tool counts), use that and skip Acta.

Acta exists for the moment after the run: review, audit, replay. The native exporters describe a run in counts, durations, names, and decisions. A bundle holds the data that never reaches them:

  • The raw stream, with arrival times. The CLI retains up to 1 GiB combined stdout/stderr by default (or every byte with an explicit --max-raw-output-bytes 0). Within that budget Acta retains every byte the agent emitted on stdout and stderr, not a curated event vocabulary, plus a per-line arrival timestamp for the stdout event stream (the streams themselves carry none). A launcher may set an explicit raw-output budget; exceeding it fails and stops the run rather than silently truncating success.
  • Full tool-call arguments and outputs plus surfaced messages. Provider reasoning/thinking channels are treated differently from assistant text deliberately surfaced to the user. Private reasoning stays local-only in raw and normalized streams, is excluded from OTLP, digest.json, and evaluation-facing summaries, and can be removed from bundles entirely with --redact-reasoning.
  • The workspace diff (staged + unstaged + non-ignored untracked): the net uncommitted change the run left in the workspace. Commits made during the run show up as base-to-head commit movement in the run metadata instead. Line counters exist natively; the actual diff does not.
  • Per-write patches when capture succeeds. What each individual write changed between its write boundaries, with explicit unavailable/partial status when size, file type, timing, or capture errors prevent exact evidence.
  • Files read, down to line spans, with the exact text returned to the agent when it can be safely attributed. What the agent looked at, and what it never looked at, is precisely what a reviewer needs.
  • The task basis. Repository, issue, and prompt provenance, declared by the launcher and stamped into the bundle at capture time. Telemetry is keyed to a session id; a bundle is keyed to the work item.
  • The invocation and outcome, recorded from outside. The exact command Acta ran (prompt redacted), working directory, the git context (base commit, branch, and dirty state at start; head commit at completion), timing, exit code, timeout, termination reason. Self-reported telemetry ends when the process dies; Acta is the thing that stopped the platform process container and knows why. Process environment variables are deliberately not captured: they are a secret minefield, and Acta scrubs its own token variable from the agent's environment.
  • The awkward events. Permission denials, rate limits, subagent tasks, todo updates, orphaned tool results, and unknown provider events preserved as agent.event.unsupported instead of silently dropped. Within the tested agent-version range, an unsupported event makes the run degraded/non-successful until Acta's parser explicitly supports it.

And the how matters as much as the what: native session logs live in agent-writable directories, so an agent can rewrite its own transcript. Acta records from outside the process, stages output beyond the agent's writable roots, and publishes the bundle after platform cleanup. Windows uses a Job Object. POSIX uses a process group; a child that creates a new session or process group is outside the portable guarantee.

Two guarantees follow from recording this way. The bundle is crash-safe: every rewrite commits as one atomic generation behind the run record, so a kill or power loss mid-write can never leave a torn bundle or a moment without the authoritative run.json, and concurrent readers always see one consistent generation. And the bundle is truthful and verifiable: the projection manifest pins every artifact by content hash, so a consumer can check that this diff, this event stream, and this record belong together and are unmodified — and recorded states never overstate reality. If a telemetry flush fails after the run, the record, digest, and events all say so; a redaction label reflects what was actually removed, and content Acta cannot verify is withheld rather than mislabeled.

Both agents' events are digested into one normalized timeline. By default the raw streams are preserved byte-identical so old runs can be re-digested with better parsers (acta digest); --redact-reasoning instead rewrites the raw stdout stream to remove private reasoning text. A span the exporter dropped is gone; a bundle can be re-projected from its raw evidence. Live-only per-write patches are preserved from the prior digest and regeneration fails explicitly when that evidence cannot be validated.

Watch runs live

Stream any run as OpenTelemetry traces to Jaeger, Grafana Tempo, or any other OTLP/HTTP backend you already have (OTEL_EXPORTER_OTLP_* env vars are honored too):

./acta run --agent codex --prompt "..." --otlp-endpoint http://localhost:4318

Configured OTLP export is best-effort by default: setup or flush failure is recorded without changing the agent outcome. Use --otlp-export-failure-policy required when delivery is an operational requirement. Required mode still finishes and preserves the bundle and its semantic result before Acta exits with the documented telemetry-only code 86. Launchers must validate the successful Acta-owned run.json as well as the code before treating it as an operational warning. Required mode rejects startup configurations that make delivery impossible, including a missing endpoint, OTEL_SDK_DISABLED=true, OTEL_TRACES_EXPORTER=none, and an unconditionally disabled sampler. If parent sampling or another runtime decision still drops the root, Acta records not_sampled, preserves the bundle, and exits with telemetry-only code 86. The deprecated --otlp-best-effort alias still selects best-effort with a warning. Combining it with --otlp-export-failure-policy required is a startup error because those flags request conflicting delivery policies.

When TRACEPARENT contains valid W3C Trace Context, the invoke_agent span joins that trace as a child of the supplied remote parent; valid TRACESTATE is carried with it. Missing or malformed parent context is ignored and Acta starts a standalone root trace as before. Pass --otlp-force-root to ignore both variables even when the parent is valid. Run IDs remain opaque and are never used to derive trace context.

Hybrid and standalone upload pin an immutable bundle snapshot before sending it. Remote snapshots remove provider-private reasoning by default while the local bundle remains full fidelity. The explicit --allow-unredacted-remote-reasoning flag opts a remote upload back into that content. Artifact labels are content-derived in this mode: detected reasoning or content that cannot be verified is unredacted, while verified-clean artifacts are not_required. Declared structured artifacts (Acta events, digests, and provider streams) use their schema-specific privacy passes. Opaque text is handled only line by line: standalone JSON lines are redacted, but an unparseable brace/bracket-opening line or multiline JSON continuation makes the artifact unverified and local-only for the default upload. The uploaded terminal artifact manifest retains that reference with status: withheld, a machine-readable reason, and redaction_state: unverified, so remote completeness checks can distinguish privacy withholding from a missing upload. The explicit unredacted opt-in uploads such an artifact as unredacted. Ordinary plain-text diagnostics still upload. The total snapshot budget defaults to 1 GiB; use --max-upload-bytes 0 only when an explicit unlimited upload is intended. Reasoning redaction bounds each JSONL record to 8 MiB by default; use --max-redaction-line-bytes to set a different explicit bound.

By default spans carry only structural metadata (tool names, ids, exit codes, tokens, timing). Content that can hold secrets or local paths stays out of the export unless you opt in with --otlp-include-output. That opt-in applies to surfaced messages and tool content, never provider reasoning/thinking text. Without --redact-reasoning, private reasoning is retained only in local raw streams and agent.reasoning normalized events; run.json records reasoning_redaction_state: retained_local. Redact mode removes its text from both persisted streams and records reasoning_redaction_state: redacted. If redaction fails before replacement, Acta retains the completed unredacted bundle locally and records reasoning_redaction_state: failed. An ambiguous post-replacement commit is hash-checked and recorded as partial unless the original bytes are verified unchanged. Both states refuse default remote upload. Remote upload redaction is independent and never rewrites local files.

Current writers emit schema v3 for run records, digests, and Acta events. It adds the not_sampled OTLP status, explicit redaction/publication metadata, and regenerated-event provenance without changing the closed v2 contract; readers accept both versions and reject v3-only fields or values when an artifact still declares v2.

Where it's headed

Acta is the recording layer for a larger idea: agent-authored changes deserve a review surface built on evidence (the issue, the trajectory, and what was verified), not just a diff. Report rendering and the PR-style review UI are on the roadmap; today Acta focuses on capturing everything they will need, with explicit evidence and projection limits at run time.

Contributing

Want to build from source, run the tests, or integrate Acta into a launcher? Start with the contributors guide, then dig into Architecture and Development. Security reports follow the private process in SECURITY.md. Maintainers can follow the release process when preparing versioned distribution artifacts.

License

Acta is available under the MIT License.

Directories

Path Synopsis
cmd
acta command
internal
actaevents
Package actaevents renders Acta's normalized digest into a stable product-facing JSONL event stream.
Package actaevents renders Acta's normalized digest into a stable product-facing JSONL event stream.
cli
contextio
Package contextio adds context cancellation checks to I/O operations.
Package contextio adds context cancellation checks to I/O operations.
digest
Package digest normalizes raw agent output (codex exec --json events, claude --print stream-json) into a versioned, reporting-oriented digest: a timeline of what the agent did, metrics, and the files it looked at.
Package digest normalizes raw agent output (codex exec --json events, claude --print stream-json) into a versioned, reporting-oriented digest: a timeline of what the agent did, metrics, and the files it looked at.
gitdiff
Package gitdiff captures the full workspace diff after an agent run: staged + unstaged changes plus non-ignored untracked files.
Package gitdiff captures the full workspace diff after an agent run: staged + unstaged changes plus non-ignored untracked files.
reasoning
Package reasoning identifies provider-private reasoning blocks by their exact discriminator and position in supported provider event shapes.
Package reasoning identifies provider-private reasoning blocks by their exact discriminator and position in supported provider event shapes.
schemaversion
Package schemaversion owns the field-level compatibility boundary between Acta's published v2 and v3 JSON documents.
Package schemaversion owns the field-level compatibility boundary between Acta's published v2 and v3 JSON documents.
securefile
Package securefile creates sensitive run artifacts with owner-only access.
Package securefile creates sensitive run artifacts with owner-only access.
tracing
Package tracing exports agent runs as OpenTelemetry traces over OTLP/HTTP: one trace per run, a root invoke_agent span, one execute_tool child span per tool call or command, and span events for assistant messages.
Package tracing exports agent runs as OpenTelemetry traces over OTLP/HTTP: one trace per run, a root invoke_agent span, one execute_tool child span per tool call or command, and span events for assistant messages.
Package schemas exposes validation at runtime for Acta's published JSON contracts.
Package schemas exposes validation at runtime for Acta's published JSON contracts.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL