Log in
docs/plan-2026-06-accuracy-and-insight.md 259 lines · 14.2 KB

Plan: Accuracy & Insight Push (June 2026)

Six tasks to take the suite from "instrumented" to "publishable evidence" for oak's core pitch. Ordering is driven by one shared dependency: remote plumbing. Tasks 1, 3, and 5 all need the harness to stand up disposable remotes (oak server-side, git local-bare or GitHub). Build that once in oakbench, then the lanes are thin.

Repos required (blocking β€” request before Phase 1)

RepoWherePurposeLifecycle
oak/bench-fleet-tmpoak orgBranch storms from the agent-fleet lane (N workers Γ— M commits Γ— runs). Kept separate from oak/oak-benchmarks-tmp so fleet garbage never pollutes the mount lane's timing repo.Disposable; deletable/recreatable any time
oak/bench-sync-tmpoak orgPush-divergence / pull-with-upstream-changes scenarios (task 5) and the full task-loop pushes (task 3).Disposable
oak/bench-large-mirroroak orgLarge-repo fixture for mount-vs-clone (task 2). Seeded once from a generated fixture; read-mostly afterward.Long-lived, read-only after seeding
bench-large-mirrorGitHubByte-identical mirror of the above, the git side of mount-vs-clone.Long-lived, read-only after seeding
bench-task-loop-tmpGitHubOptional but recommended: git's agent-default push target for the task-loop lane, so git pays real git push like oak pays real oak push. Local-bare remains the core-equivalent track.Disposable

Large-mirror fixture spec (generated by a new scripts/make_large_fixture.py, deterministic seed so both mirrors are identical): ~30k files across a realistic tree (src/, docs/, vendored deps), ~300 MB working tree including a handful of 20–100 MB binaries, ~500 commits of synthetic history. Big enough that full git clone visibly pays for bytes an agent never reads; small enough to re-seed in minutes.

Phase 0 β€” shared remote plumbing (oakbench/remotes.py)

Lift what already exists in bench.py (OAK_BENCH_REMOTE resolution at bench.py:400, disposable branch namespacing at bench.py:431-449, local-bare git remote setup at bench.py:566-576) into a shared module:

  • resolve_oak_remote(purpose) β†’ owner/repo from env (OAK_BENCH_FLEET_REPO, OAK_BENCH_SYNC_REPO, falling back to OAK_BENCH_REMOTE), or a structured skip reason. Skips stay returncode-77 rows naming the env var (skips are work items).
  • make_git_bare_remote(tmpdir) β€” untimed local bare repo (core-equivalent track) and resolve_git_github_remote() from GIT_BENCH_REMOTE env (agent-default track).
  • disposable_branch(bench_id, scenario, subject, run) β€” the existing collision-safe namescheme, shared by all lanes.
  • Transport honesty: every row touching a remote records transport: local_file | network and the server identity. Network rows are never aggregated with local-file rows (AGENTS.md already mandates this for remote.net.*; the new lanes inherit the rule).

Tests: tests/test_remotes.py (resolution, skip reasons, branch-name uniqueness). Run row_parity.py against a pre-change bench.py run to prove the lift is measurement-identical (ADR-0005 discipline).

Task 1 β€” agent-fleet lane: oak workspace-per-task

Today: parallel_contention.py:425-439 hard-skips non-git subjects in workspace_per_task mode. Git mode already emits setup_ms, merge_ms, marginal_workspace_disk_kb, commit throughput, lost-update detection, and integrity (parallel_contention.py:535-554).

Build:

  1. Untimed setup: push the contention fixture to oak/bench-fleet-tmp on a disposable branch (one per run).
  2. Per worker i: timed oak mount/space creation of workspace i β†’ record per-workspace setup_ms and a new run-level time_to_nth_workspace_ms curve (cumulative wall time until N workspaces are usable β€” this is the fleet-spinup headline number). Git comparator gets the same curve computed from its existing worktree setup timings, so the metric lands on both subjects simultaneously (ADR-0005: new metric, additive, no renames).
  3. Marginal disk: reuse the mount lane's disk.usage probe (mount_probe.py:439-511 β€” visible vs allocated bytes, entry count) so lazy hydration's near-zero marginal disk is measured the same way in both lanes. Git worktrees keep workspace_disk_kb as-is.
  4. Commit storm: same disjoint-file edit/commit loop; oak workers commit and push their workspace branch.
  5. Merge throughput: merge N branches back (git: existing merge step; oak: its merge/integrate flow against the remote), emit merge_ms, merges_clean, merge_conflicts for both.
  6. Integrity pass: payload survival in the merged result + oak's closest fsck analog, measurement_source named accordingly.

Honesty constraint: git worktrees are local; oak workspaces hit the network. Rows carry transport; the summary presents the comparison with the asymmetry stated, and optionally adds a clone_per_task git mode (clone from local bare per worker) as the transport-comparable third column. Do not bury this β€” it is the first question a skeptical reader asks.

Flags/env: OAK_BENCH_FLEET_REPO=oak/bench-fleet-tmp, existing --workers 2,8,32 --modes workspace_per_task.

Deliverable claim shape: "N agents Γ— isolated workspaces: time-to-32nd-workspace X s vs Y s; marginal disk per workspace X MB vs Y MB; merge throughput X/s vs Y/s (oak network-backed, git local β€” see methodology)."

Task 2 β€” mount-vs-clone with a git comparator

Today: mount lane is oak-only (scenarios/mount.yaml:45-174, no git scenarios); docs/benchmark-coverage.md lists no mount-vs-clone plan.

Build:

  1. Seed oak/bench-large-mirror and GitHub bench-large-mirror from the same generated fixture (Phase 0 fixture script). Verify byte-identity with a manifest hash committed to both.
  2. New lane scenarios β€” new names, not edits to existing mount ops (ADR-0005): acquire.cold with subjects:
    • oak mount oak/bench-large-mirror β†’ first targeted file read
    • git clone <github-url> (stock) β†’ same file read
    • git clone --filter=blob:none (partial clone, tuned-git credibility)
    • git clone --depth 1 (shallow)
    • optionally git clone + sparse-checkout set <task-path>
  3. Metrics per subject: time_to_first_read_ms (command start β†’ target file bytes in hand), bytes_transferred (oak: existing bytes_hydrated parsing at mount_probe.py:327-347; git: pack bytes from clone stderr Receiving objects / .git size delta), disk_total_bytes, time_to_task_ready_ms (tree listable + task path readable).
  4. Follow-up scenario acquire.task_scoped: agent needs 3 files in one subdirectory β€” oak hydrates ~those bytes; git pays the whole clone (or the partial-clone fetch chatter). This is the marketing picture.
  5. Network honesty: GitHub's CDN vs oak.space are different networks. Mitigate: interleave reps, record server + timestamps, report medians of β‰₯5 reps, and state the caveat in the summary. The claim is about the acquisition model (lazy vs eager), which dominates network variance at this repo size β€” but say so rather than assume it.

Deliverable claim shape: "Start working on a 300 MB repo: oak mount X s / Y MB hydrated vs git clone X s / 300 MB (and partial clone X s) β€” time-to-first-read and bytes-to-first-read, 5 reps, medians."

Task 3 β€” full oak task-loop end-to-end

Today: workflow_ab.py workflows are single-checkout, no remote, no mount; the signature motion (mount β†’ edit β†’ commit β†’ push β†’ desc β†’ end β†’ remount for follow-up) is only exercised piecemeal by the mount lane's task_lifecycle_loop.

Build: new workflow-ab-style lane (likely its own script, scripts/task_loop.py, sharing oakbench policy) with one scenario per subject family, agent-default track:

  • oak native: oak mount (from oak/bench-sync-tmp) β†’ edit β†’ oak commit β†’ oak push β†’ oak desc β†’ oak end β†’ remount for a follow-up task β†’ second edit/commit/push.
  • git native: git clone (GitHub bench-task-loop-tmp) or git worktree add from an existing checkout β†’ branch β†’ edit β†’ commit β†’ push β†’ PR-less desc analog (commit message / git push output) β†’ git worktree remove β†’ new worktree for follow-up.
  • Core-equivalent variant runs git against a local bare remote so the mechanics claim survives without GitHub network in the loop.

Metrics: per-phase operation rows plus a task_loop.total row: wall time, tool-call count (envelope-priced β€” this lane is where oak's fewer-calls-per-motion compounds), estimated cost-weighted tokens, and followup_warm_delta_ms (second task on warm caches vs first β€” the remount story). Oracle: payload present on the remote branch, workspace cleanly ended, follow-up edit visible.

Why a separate lane: the command sequences are not semantically equivalent step-by-step (desc/end have no git twin), so this is explicitly agent-default-track, whole-task-cost evidence β€” exactly the framing docs/command-semantics.md exists to police.

Task 4 β€” real-agent runs

Today: adapters exist (agent_workflow.py:570-642), token extraction handles all provider field variants (agent_workflow.py:737-786), but only mock has ever run; stream formats for current CLI versions are unverified.

Build (no new repos needed):

  1. Adapter shakeout: one manual run per installed CLI (--agents available), single rep, bugfix_test_loop, both subjects. Verify: stream parses, usage events found (provider-reported tokens non-null), turn_metrics populated, oracle passes. Fix adapter drift against current CLI flags before burning tokens on reps.
  2. Token-extraction goldens: capture one real transcript per CLI into tests/ golden fixtures (scrubbed) so adapter drift is caught by test_stream_adapters.py instead of mid-campaign.
  3. The campaign: --agents claude[,codex,cursor_agent] --subjects git,oak_installed --workflows bugfix_test_loop --runs 5 --agent-environment minimal --randomize-agent-order --randomize-subject-order --instruction-level zero-shot then the same at cheat-sheet. Never aggregate across levels (existing contract). Budget estimate before launch: runs Γ— agents Γ— subjects Γ— levels β‰ˆ 40 tasks; cap with --timeout-seconds 900 and a per-campaign dollar estimate printed up front.
  4. The story metrics: zero-shot vs cheat-sheet deltas in success rate, help_calls_total, unknown_command_failures_total, turns_to_recovery / tokens_to_recovery β€” the "git is in pretraining, oak isn't, and AGENTS.md closes the gap" measurement.
  5. Stretch (post-campaign): run task 5's divergence workflow and task 3's loop under real agents β€” recovery-cost numbers from real transcripts are the credibility ceiling for the whole drop.

Transcripts/JSONL stay out of the repo (data-hygiene rules); keep run artifacts under an external/ignored dir with the run manifest.

Task 5 β€” sync & divergence recovery

Today: vcs_error_recovery covers only empty-commit and unknown-subcommand (workflow_ab.py:413-433). No remote plumbing exists in workflow_ab. ADR-0005 forbids changing vcs_error_recovery's meaning β€” these are new workflows, not extensions.

Build: new workflow vcs_sync_recovery (uses Phase 0 remotes; git β†’ local bare for core-equivalent + optional GitHub for agent-default; oak β†’ oak/bench-sync-tmp):

  • sync.push_divergence: checkout A and B from the same remote; A edits + pushes; B edits + pushes β†’ rejected. Then the recovery arc: whatever the error output tells you to do (git: pull/rebase or --force-with-lease decision; oak: its divergence flow β€” the exact trap hit three times in real use on 2026-06-11). Steps carry expected_returncodes admitting the failure; recovery steps follow.
  • sync.pull_upstream_changes: clean pull with upstream commits, and dirty pull (local uncommitted edits) variant.
  • sync.non_ff_after_amend: amend/rewrite local tip after pushing, push again β€” the second-most-common agent trap.

Metrics (this is the "error output is a prompt" lane): per-failure error_output_tokens (the rejected push's stderr, cost-weighted as model input), recovery_steps_count, recovery_tokens_total, and an error_actionability check: does the error text literally contain the command that fixes it? (boolean per subject, measurement_source: stderr_contains_recovery_command). Real-agent turns_to_recovery already exists for task 4 to consume on these same workflows.

Task 6 β€” pre-publish calibration & tuned-git (gate, runs last)

  1. Calibration: capture real per-style outputs from a standard run (status, diff, log, push-rejection stderr β€” per subject), run token_calibration.py with tiktoken available (optional import, allowed under ADR-0001) per style group, plus --envelope to validate the envelope constants. Output: calibration factors table committed to docs/ and cited next to every published token delta; if any factor moves a published cross-subject delta's sign or order, the table uses calibrated numbers, not char/4.
  2. Tuned-git everywhere public: every public table includes git_untracked_cache AND git_fsmonitor rows (verify fsmonitor--daemon health on the darwin runner; it is the load-bearing comparator for wide-tree claims). Wire --git-modes untracked_cache,fsmonitor into the canonical publish-run invocation.
  3. Publish checklist: small docs/publish-checklist.md (or scripts/publish_gate.py that greps result sets): calibration run βœ“, tuned modes present βœ“, no aggregation across instruction levels or transports βœ“, nulls rendered as unmeasured βœ“, every skip row accounted for βœ“, tail-percentile sample-count honesty βœ“.

Sequencing

Phase 0  oakbench/remotes.py + fixture generator + repo creation   ← blocks 1,2,3,5
Phase 1  Task 1 (fleet lane)        Task 5 (sync recovery)         [parallel]
Phase 2  Task 2 (mount-vs-clone)    Task 3 (task loop)             [parallel]
Phase 3  Task 4 (real agents: shakeout β†’ goldens β†’ campaign,
                 then rerun tasks 3/5 workflows under real agents)
Phase 4  Task 6 (calibration + tuned-git + publish gate)

Each phase lands with instrument tests (python3 -m unittest discover -s tests) and, where existing lanes are touched, a row_parity.py proof.