All notable changes to aet are recorded here.
billing.METERED_PROVIDERSnow lists canonicalopenai— an OpenAI API-key row (the metered Codex path) was previously classified assubscriptionand silently dropped from reported spend.
- Codex-CLI importer + live recorder (
aet import --format codex) — ingests acodex exec --jsonstdout JSONL stream (verified against codex-cli 0.147.0) into a canonicalRunTrajectory.aet.trajectory.codexis a lossless, structural normalizer (dispatch on the dottedtype, no regex; unknown events and non-JSON lines kept verbatim), shared by the batch importer (aet.trajectory.importers.codex) and a streamingCodexTrajectoryRecorder(aet.trajectory.codex_recorder) fed one timestamped line at a time — one normalization path, so a run killed mid-turn re-imports to the same trajectory. Token buckets follow the subset rules (cached/cache-write ⊆ input, reasoning ⊆ output; absent bucket staysNone, never0) and are kept non-overlapping socum_totalnever double-counts. Idempotent; raw-vs-normalized event counts reconcile. - Reasoning tokens + cache read/write split on the trajectory —
TrajectoryPoint,RoundBoundary,RunTrajectorysummaries, andtoken_series()now carry reasoning output and the per-round cache read/write split (schema1.2; older1.0/1.1files load unchanged). - Nullable, provenanced cost (
aet.trajectory.cost.CostRecord) —final_cost_usdis nowfloat | None;Nonemeans unpriced (unknown), which is never rendered/aggregated as$0(genuinely free). ACostRecordcarrieskind(metered / subscription_notional / unpriced),source, and price-table provenance (id + sha256 + url). - Versioned OpenAI price snapshot (
aet.trajectory.price_snapshot) — a hashable, frozen rate map with averifiedflag (the bundledopenai-2026-08-17rates are placeholders, TODO-verify) that copies into an experiment root for reproducibility and prices Codex usage under the subset semantics. - Codex reconciliation reports (
aet.trajectory.reconcile) — raw-events vs imported-events, token-ledger vs trajectory (with subset-invariant checks), calculated cost vs admin/invoice cost, and a missing/unpriced-fields report; plus per-turn token and tool ledger rows for the run bundle. - Tests-passing CLIMB mined from oracle invocations (
aet.trajectory.oracle) — reconstructs the tests-over-time progression from the transcript itself: each./run.sh(testbench) invocation the agent runs becomes ak/Nmilestone at its wall time. Retroactive (works on existing runs), no harness change. Rejects a different testbench by suite-size mismatch.import_transcript(oracle_markers=…)+classify.spec_to_rtl_config()(verilator/run.sh→ tool-wait band) reproduce the referencerate-panelsfigure for abc-testing spec-to-rtl. aet plot-sessions— point at raw Claude session transcripts (files or dirs) and render the comparison figures in one step; no prioraet importneeded.k/Nterminal grade + per-lane facet scaling —import_transcript(n_passed=, n_total=)records a real fraction (e.g.182/182 cases), andplot_tests_facetsscales each lane to its own suite size so heterogeneous arms aren't dwarfed.- Adaptive
$-label precision — figure cost labels show cents below $10, so small-per-session sweeps don't collapse to$2. - Sandboxed agent runner (
aet run/aet.runner) — a single Claude Code invocation launched inside a deny-by-defaultaet.isolationbwrap sandbox, streamed to a transcript, recorded as aRunTrajectory, and materialized into a canonical aet run (manifest +logs/+metrics/trajectory.json) thataet runs/aet show/aet plotread directly. Closes sandbox-run → record → plot for any project.--sandbox noneneeds--allow-unsandboxed;--agent-cmdoverrides theclaudecommand for custom launchers / dummy runs. - Rate-limit watchdog + auto-resume (
aet.ratelimit,aet run --resume) — unattended runs survive the Claude five-hour usage limit: on a rejected-with-no-work invocation the runner checkpoints, waits to the exactresetsAt(or polls every ~20 min up to a 5h20m cap when the epoch is missing) and resumes the same session — never burning the attempt. On the weekly limit (or an exhausted wait budget) it stops honestly: writesUNFINISHED.md+ sets manifeststatus: rate_limited_unfinishedwith aresume_cmd, surfaced byaet runs, so a person or another session picks it up withaet run --resume <run>. Daemon-free (checkpoint + relaunch), and fully testable (injectable spawn/sleep/now) with no realclaude,bwrap, or 5-hour wait. - Generic transcript importer (
aet import --source transcript) — the repo-agnostic default: ingests one or many Claude Code*.jsonlfiles into one trajectory with zero project-specific code. Handles both on-disk shapes — CLIstream-json(billed cost, exact) and desktop/app session logs (noresultevent → provisional list-price cost) — orders multiple session files by first timestamp, and records an optional terminal--pass/--failverdict + milestone (e.g. abc-testing'sfunctional_pass). Verified on the recovered abc9/abc11 desktop sessions and the abc4 CLI arms. - Presentation comparison figures (
aet plot --kind,aet.viz.comparison) — the polished N-arm views, consuming only the data-model:rate-panels(per-arm token-rate panels, each on its own time scale with a below-axis fixed-duration ruler, activity bands, gold milestones, corner chip),cost-vs-time(one labeled cumulative-spend line per arm), andtests-facets(small-multiple tests-passing step-lanes; degrades gracefully to the final verdict when there's no over-time signal).series_styles(n)gives repo-agnostic per-arm colour/marker/dash identity.--outwrites a.pngand its sibling.svg;compare --plotsrenders the full set. claude_streamrobustness — the parser now tolerates string content-blocks (desktop/app session logs carry ausermessage'scontentas a plain string), so real session logs import without crashing.- Agentic trajectory recording (
aet.trajectory) — canonical, repo-agnostic record of what an agent did over time: cumulative tokens (input/output/cache), cumulative cost, an activity timeline (thinking / reading / writing / bash / long tool-waits), and external-oracle test-pass milestones.RunTrajectoryis pure-stdlib and built the same way from a completed run or a live stream viaappend_round(one code path). Timing comes fromclaude_stream.parse_timestamped_stream(real per-tool offsets, superseding the old within-round weighting). The activity classifier is pluggable config (ActivityConfig/LongWaitRule; the verilator/CIRCT long-wait rule is data viacapsule_bench_config, never hardcoded), so the core stays generic.- Native recording —
EvalRunLogger.log_trajectory_point/log_test_milestone/log_round_boundaryemit the trajectory through the existing tracking primitives, so it is reconstructable from canonicallogs/(RunTrajectory.from_run_dir), plus ametrics/trajectory.jsonfast-path artifact. - Importer —
aet import --source capsule-bench --raw <dir> [--into <run>]ingests existing agentic runs (transcripts + qa verdicts + selfcheck log) into a canonical trajectory;--intomaterializes a full aet run so old data is queryable viaaet runs/aet show/aet plot. Handles per-roundwall_offset_sresets in the self-check log (cumulative-clock reconstruction). - Live monitor —
aet monitor --attach <transcript>tails an in-flightstream-jsontranscript, updating the same data-model incrementally; cost is~$…(provisional)until the terminal result event, then flips to the billed number. Headless-first (one rewriting status line).
- Native recording —
- Visualization (
aet.viz, optional[viz]extra) — house-style trajectory plots consuming only the data-model.aet plot <run|json> [--comparison …]andcompare --plotsrender per-run and stacked comparison figures (cumulative tokens on a log axis, spend twin-axis, activity-share background bands, gold test-pass milestones). matplotlib/numpy stay behind the extra with a friendlypip install 'aet[viz]'hint;import/monitorwork without it. claude_streamcorrectness (for full session-log transcripts) — the parser now dedups re-emitted assistant messages by id (session logs emit the same message 2–3× with identical usage) so tokens are counted once, and consumers can split a transcript at eachresultevent (a file may concatenate several invocations). Together these make imported token/cost totals match the authoritative per-model billing exactly.TurnUsage.has_thinkingadded.- Isolation & integrity (
aet.isolation) — reusable, project-agnostic filesystem isolation for agentic runs.SandboxSpec/bwrap_argv/wrap_commandbuild a deny-by-default bubblewrap allow-list (agent sees only granted files + tools; answers, sibling runs, and other projects masked; per-file/dev/nullmasking; DNS + nested-session-env handling; permission-safe on locked dirs).AuditPolicy/audit_runis a post-run allow-list transcript check (hard cheats vs soft out-of-scope vs review-warnings).file_access_ledgerenumerates every file the agent touched and what it did. Seedocs/isolation.md. Extracted from the gemmini agentic A/B harness. - Multi-run statistics —
compare()now writesstatistical_comparison.mdwith Welch's t-test, 95% confidence intervals, and Cohen's d effect size for every key metric across methods. Significance markers (***/**/*/ns) included. - Structured rubric scoring —
RubricCriteriondataclass,compute_weighted_score, andvalidate_rubricinaet.core.rubric.EvalRunLogger.log_rubric_score()fans out to local JSONL and MLflow. - Trajectory similarity —
jaccard_similarityandsequence_edit_distance(Levenshtein) inaet.core.metrics.compare()writestrajectory_similarity.mdpairwise Jaccard matrix whentool_sequenceis present in run summaries. - Context window utilization —
turn.context_pct_usedstep metric,aet.context.max_pct_usedsummary metric, andaet.context.high_utilization_warningevent (>80% threshold). OTel inference spans carryaet.turn.context_pct_usedattribute. - Baseline / regression detection —
aet baseline set/showCLI subcommand stores a reference run'ssummary_metrics.jsonunderbaselines/<suite>/baseline.json. Subsequentcompare()calls writeregression_report.mdflagging runs where cost >1.2× baseline or score <baseline−0.05. aet runsandaet showCLI subcommands for listing and inspecting recorded runs.
- Ray execution backend — it was a
NotImplementedErrorskeleton advertising a feature that didn't run. Removed the backend, the[ray]extra, the--execution raychoice, and its docs/test. Only the local executor ships (ship only what's real).
- Lint-clean + enforced: repo is
ruff-clean;[tool.ruff]/[tool.pytest.ini_options]pinned inpyproject.toml; theallextra now composes the other extras (single source of truth) + a new[docs]extra. - De-branded viz API:
use_merlin_style()→use_house_style()(deprecated alias kept). - CLI de-godded:
cli/main.py(1379 LOC) split into a thin argparse table +cli/_common.py+cli/commands/{lifecycle,reporting,trajectory}.py. No behavior change. - Logging: tracking warnings emit via
logging(silent by default) instead of rawprint. - Report writers extracted: the run-report serialization (
run_record.json,summary_metrics.json,eval_report.json,metrics.json) moved fromEvalRunLoggerinto free functions intracking/reports.py(the facade delegates) — testable in isolation.
- Rewritten
README.md(the real record→plot / sandboxed-run surface), a rootAGENTS.md(architecture map + "how to add X" recipes + Definition-of-Done),docs/ARCHITECTURE.md, ADRs underdocs/adr/, and an auto-generated API site (mkdocstrings).tests/test_docs.py+mkdocs build --strictin CI keep docs from drifting.
- Initial release:
defaultandtargetgensuites,EvalRunLoggerwith local / MLflow / OTel backends,aet init-project,aet init-run,aet validate,aet compare,aet run-suite. - SigNoz observability stack (
docker-compose.observability.yml) with OTel Collector, Jaeger, Prometheus, and Grafana. - Per-turn token / cost / cache breakdown; per-tool-call timing spans with GenAI semconv attributes; OpenLLMetry auto-instrumentation.
ClaudeStreamResultparser for Claude Code JSONL stream output.- Ray backend for parallel sweep execution.