Viewing files at commit 186ae84b74bdCommit detailsView latest

Browse files
scripts/task_loop.py 3177 lines · 128.5 KB · python
1
#!/usr/bin/env python3
2
"""The full task loop, end to end — the product's signature motion.
3
4
oak:  mount -> edit -> commit -> push -> desc -> end, then REMOUNT for a
5
      follow-up task (the warm-cache story) and repeat edit/commit/push/end.
6
git:  worktree add -> branch edit -> add/commit -> push -> worktree remove,
7
      then a second worktree for the follow-up. The task description lives in
8
      the commit message git already paid for; oak pays a separate `desc`
9
      call. That asymmetry is the measurement, not a harness gap
10
      (docs/command-semantics.md): this is agent-default-track, whole-task
11
      evidence, never a step-by-step equivalence claim.
12
13
Transport honesty: oak rows always cross the network to a real oak server.
14
git runs against GitHub (--git-remote github, transport-comparable) or a
15
local bare repo (--git-remote local, the core-equivalent track). Rows carry
16
remote_transport/remote_server; latency comparisons across transports are
17
forbidden — cross-subject evidence at unlike transports is tool calls and
18
tokens only.
19
20
Setup:
21
  OAK_BENCH_SYNC_REPO=oak/bench-sync-tmp \\
22
  [email protected]:oakdotspace/bench-task-loop-tmp.git \\
23
  python3 scripts/task_loop.py --runs 3
24
25
Unconfigured remotes emit skip rows (returncode 77), never silent absence.
26
"""
27
28
from __future__ import annotations
29
30
import argparse
31
import hashlib
32
import json
33
import os
34
import platform
35
import random
36
import re
37
import shutil
38
import socket
39
import sqlite3
40
import stat
41
import subprocess
42
import tempfile
43
import threading
44
import time
45
import urllib.error
46
import urllib.parse
47
import urllib.request
48
from datetime import datetime, timezone
49
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
50
from pathlib import Path
51
from typing import Any
52
53
from oakbench import remotes as oakbench_remotes
54
from oakbench import tokens as oakbench_tokens
55
from oakbench import merge_history_fixture, shallow_refresh
56
from oakbench.byteproxy import CountingProxy
57
from oakbench.gitserver import GitHTTPServer
58
from oakbench.httpcount import RequestCountingProxy
59
from oakbench.diskprobe import bounded_tree_usage
60
from oakbench.environment import ENV_ISOLATION_VERSION, base_env as oakbench_base_env
61
from oakbench.environment import command_display
62
from oakbench.execution import run_timed, stop_owned_process
63
from oakbench.results import ResultsStore
64
from oakbench.rows import row_returncode
65
from oakbench.runlock import measurement_lock
66
from oakbench.subjects import (
67
    DEFAULT_OAK_REPO,
68
    Subject,
69
    binary_sha256,
70
    load_subjects,
71
    source_metadata,
72
    subject_details,
73
    subject_version,
74
    subject_versions,
75
)
76
77
ROOT = Path(__file__).resolve().parents[1]
78
DEFAULT_WORKDIR = Path(tempfile.gettempdir()) / "oak-task-loop"
79
SKIP_RETURNCODE = 77
80
VIRTUAL_BRANCH_RE = re.compile(r"virtual branch ([^\s)]+)")
81
PHASES = ("task", "followup")
82
TASK_LOOP_SCENARIO = "task_loop"
83
AGENT_TASK_SCENARIO = "agent_task_baseline_v1"
84
AGENT_TASK_OAK_LOCAL_SCENARIO = "agent_task_oak_local_v1"
85
INTERRUPTED_PUBLICATION_SCENARIO = "interrupted_publication_after_request_v1"
86
LEGACY_SELECTED_MATERIALIZATION_SCENARIO = "legacy_selected_clone_materialization_v1"
87
SHALLOW_REFRESH_SCENARIO = "shallow_worker_branch_refresh_v1"
88
SHALLOW_REFRESH_DEFAULT_FIXTURES = "1000:1,2000:2"
89
SHALLOW_REFRESH_TASK_BRANCH = "task"
90
SHALLOW_REFRESH_STEP_TIMEOUT_S = 900.0
91
COMMITS_INFO_SUFFIX = "/commits/info"
92
INTERRUPTED_PUBLICATION_REQUEST_TIMEOUT_S = 2.0
93
INTERRUPTED_PUBLICATION_PROCESS_TIMEOUT_S = 30.0
94
INTERRUPTED_PUBLICATION_MAX_REQUEST_BYTES = 64 * 1024 * 1024
95
LOCAL_OAK_SERVER_READY_TIMEOUT_S = 10.0
96
97
98
def legacy_selected_receipt_evidence(receipt: dict[str, Any]) -> dict[str, Any]:
99
    result = receipt.get("result") if isinstance(receipt.get("result"), dict) else {}
100
    observed = receipt.get("observed") if isinstance(receipt.get("observed"), dict) else {}
101
    phases = observed.get("pull_phases") if isinstance(observed.get("pull_phases"), list) else []
102
    phase_names = [phase.get("name") for phase in phases if isinstance(phase, dict)]
103
    materialization = (
104
        observed.get("materialization")
105
        if isinstance(observed.get("materialization"), dict)
106
        else {}
107
    )
108
    return {
109
        "legacy_route_observed": phase_names == ["initial", "legacy_selected_branch"],
110
        "pull_phase_names": phase_names,
111
        "pull_phases": phases,
112
        "materialization_passes_observed": materialization.get("passes_observed"),
113
        "written_paths": materialization.get("written_paths"),
114
        "snapshot_bound": result.get("snapshot_bound"),
115
        "head": result.get("head"),
116
        "manifest": result.get("manifest"),
117
    }
118
119
120
def legacy_selected_receipt_contract(evidence: dict[str, Any]) -> dict[str, Any]:
121
    phases = []
122
    for phase in evidence.get("pull_phases", []):
123
        if not isinstance(phase, dict):
124
            continue
125
        phases.append(
126
            {
127
                key: value
128
                for key, value in phase.items()
129
                if key not in {"materialization_passes"}
130
            }
131
        )
132
    return {
133
        "legacy_route_observed": evidence.get("legacy_route_observed"),
134
        "pull_phases": phases,
135
        "snapshot_bound": evidence.get("snapshot_bound"),
136
        "head": evidence.get("head"),
137
        "manifest": evidence.get("manifest"),
138
        "written_paths": evidence.get("written_paths"),
139
    }
140
141
142
def normalized_cache_contract(snapshot: dict[str, Any] | None) -> list[dict[str, Any]] | None:
143
    if not isinstance(snapshot, dict) or not isinstance(snapshot.get("rows"), list):
144
        return None
145
    return [
146
        {key: row.get(key) for key in ("path", "size", "blob_hash")}
147
        for row in snapshot["rows"]
148
        if isinstance(row, dict)
149
    ]
150
151
152
def normalized_status(value: dict[str, Any] | list[Any] | None) -> dict[str, Any] | None:
153
    if not isinstance(value, dict):
154
        return None
155
    return {
156
        key: value.get(key)
157
        for key in ("branch", "parent", "branch_status", "head", "working_changes", "branch_changes")
158
    }
159
160
161
def materialization_timing_controls_met(
162
    metadata: dict[str, Any], runs: int, randomize_order: bool, controls_requested: bool
163
) -> bool:
164
    return bool(
165
        controls_requested
166
        and metadata.get("measurement_lock") == "held"
167
        and randomize_order
168
        and runs >= 10
169
    )
170
171
172
def isolated_oak_fixture_env(task_config_root: Path) -> dict[str, str]:
173
    task_config_root.mkdir(mode=0o700, parents=True, exist_ok=True)
174
    retained = {
175
        "PATH",
176
        "TMPDIR",
177
        "TMP",
178
        "TEMP",
179
        "HOME",
180
        "USER",
181
        "USERNAME",
182
        "SHELL",
183
        "SYSTEMROOT",
184
        "WINDIR",
185
        "COMSPEC",
186
        "PATHEXT",
187
        "SSL_CERT_FILE",
188
        "SSL_CERT_DIR",
189
    }
190
    env = {key: value for key, value in os.environ.items() if key in retained}
191
    env.update(
192
        {
193
            "GIT_AUTHOR_NAME": "Oak Materialization Bench",
194
            "GIT_AUTHOR_EMAIL": "[email protected]",
195
            "GIT_COMMITTER_NAME": "Oak Materialization Bench",
196
            "GIT_COMMITTER_EMAIL": "[email protected]",
197
            "GIT_CONFIG_GLOBAL": os.devnull,
198
            "GIT_CONFIG_NOSYSTEM": "1",
199
            "OAK_AUTHOR": "oak-materialization-bench",
200
            "OAK_NO_UPDATE_CHECK": "1",
201
            "OAK_BENCH_ENV_ISOLATION_VERSION": ENV_ISOLATION_VERSION,
202
            "NO_COLOR": "1",
203
            "CLICOLOR": "0",
204
            "LC_ALL": "C",
205
            "LANG": "C",
206
            "TZ": "UTC",
207
            "GIT_AUTHOR_DATE": "2026-01-01T00:00:00Z",
208
            "GIT_COMMITTER_DATE": "2026-01-01T00:00:00Z",
209
        }
210
    )
211
    return env
212
213
214
def normalized_worktree_snapshot(root: Path) -> list[dict[str, Any]]:
215
    rows: list[dict[str, Any]] = []
216
    for path in sorted(root.rglob("*")):
217
        relative = path.relative_to(root)
218
        if relative.parts and relative.parts[0] in {".oak", ".git"}:
219
            continue
220
        info = path.lstat()
221
        if stat.S_ISDIR(info.st_mode):
222
            continue
223
        row: dict[str, Any] = {
224
            "path": relative.as_posix(),
225
            "mode": stat.S_IMODE(info.st_mode),
226
        }
227
        if stat.S_ISLNK(info.st_mode):
228
            row.update({"kind": "symlink", "target": os.readlink(path)})
229
        else:
230
            row.update({"kind": "file", "sha256": hashlib.sha256(path.read_bytes()).hexdigest()})
231
        rows.append(row)
232
    return rows
233
234
235
def json_command(
236
    command: list[str], cwd: Path, env: dict[str, str]
237
) -> dict[str, Any] | list[Any] | None:
238
    result = run_bounded_harness_command(command, cwd, env=env)
239
    if result.returncode != 0:
240
        return None
241
    try:
242
        value = json.loads(result.stdout)
243
    except (TypeError, ValueError):
244
        return None
245
    return value if isinstance(value, (dict, list)) else None
246
247
248
def normalized_history(value: dict[str, Any] | list[Any] | None) -> list[dict[str, Any]] | None:
249
    if not isinstance(value, list):
250
        return None
251
    rows: list[dict[str, Any]] = []
252
    for item in value:
253
        if not isinstance(item, dict):
254
            return None
255
        rows.append(
256
            {
257
                key: item.get(key)
258
                for key in (
259
                    "hash",
260
                    "timestamp",
261
                    "branch",
262
                    "description_or_subject",
263
                    "files_changed",
264
                )
265
            }
266
        )
267
    return rows
268
269
270
def normalized_oak_artifacts(root: Path) -> list[str] | None:
271
    oak_dir = root / ".oak"
272
    if not oak_dir.is_dir():
273
        return None
274
    return sorted(
275
        path.relative_to(oak_dir).as_posix() + ("/" if path.is_dir() else "")
276
        for path in oak_dir.rglob("*")
277
    )
278
279
280
def stat_cache_snapshot(root: Path) -> dict[str, Any] | None:
281
    db = root / ".oak/oak.db"
282
    if not db.is_file():
283
        return {"rows": None, "all_metadata_valid": False, "error": "oak_db_missing"}
284
    try:
285
        connection = sqlite3.connect(str(db))
286
        connection.execute("PRAGMA query_only = ON")
287
        raw_rows = connection.execute(
288
            "SELECT path, mtime_ns, ctime_ns, size, blob_hash FROM stat_cache ORDER BY path"
289
        ).fetchall()
290
    except sqlite3.Error as error:
291
        return {"rows": None, "all_metadata_valid": False, "error": str(error)}
292
    finally:
293
        if "connection" in locals():
294
            connection.close()
295
    rows: list[dict[str, Any]] = []
296
    valid = True
297
    for path, mtime_ns, ctime_ns, size, blob_hash in raw_rows:
298
        file_path = root / path
299
        try:
300
            info = file_path.stat()
301
        except OSError:
302
            valid = False
303
            continue
304
        expected_ctime = info.st_ctime_ns if os.name == "posix" else 0
305
        metadata_valid = (
306
            int(size) == info.st_size
307
            and int(mtime_ns) == info.st_mtime_ns
308
            and int(ctime_ns) == expected_ctime
309
        )
310
        valid = valid and metadata_valid
311
        rows.append(
312
            {
313
                "path": str(path),
314
                "size": int(size),
315
                "blob_hash": str(blob_hash),
316
                "metadata_valid": metadata_valid,
317
            }
318
        )
319
    return {"rows": rows, "all_metadata_valid": valid}
320
321
322
def base_env() -> dict[str, str]:
323
    return oakbench_base_env(
324
        author_name="Oak Task Loop",
325
        author_email="[email protected]",
326
        oak_author="oak-task-loop",
327
    )
328
329
330
def parse_args() -> argparse.Namespace:
331
    parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
332
    parser.add_argument("--subjects", help="Comma-separated subject names from config/subjects.toml")
333
    parser.add_argument("--config", type=Path, default=ROOT / "config" / "subjects.toml")
334
    parser.add_argument("--git-bin", type=Path)
335
    parser.add_argument("--oak-installed-bin", type=Path)
336
    parser.add_argument("--oak-local-bin", type=Path)
337
    parser.add_argument(
338
        "--oak-serve-bin",
339
        type=Path,
340
        help="Immutable Oak binary used only as the fixed loopback server and fixture writer.",
341
    )
342
    parser.add_argument(
343
        "--oak-serve-source",
344
        type=Path,
345
        help="Read-only source checkout that produced --oak-serve-bin.",
346
    )
347
    parser.add_argument(
348
        "--oak-serve-source-head",
349
        help="Exact 64-hex Oak source head that produced --oak-serve-bin.",
350
    )
351
    parser.add_argument(
352
        "--materialization-baseline-subject",
353
        help="Named control subject required by the legacy materialization scenario.",
354
    )
355
    parser.add_argument(
356
        "--materialization-timing-controls-met",
357
        action="store_true",
358
        help=(
359
            "Record that harness-verifiable controls are met only with a held measurement lock, "
360
            "randomized subject order, and at least 10 runs. This is not a speedup claim: "
361
            "separate A/A noise and externally verified quiet-host evidence remain required."
362
        ),
363
    )
364
    parser.add_argument(
365
        "--shallow-refresh-fixtures",
366
        default=SHALLOW_REFRESH_DEFAULT_FIXTURES,
367
        help=(
368
            "shallow_worker_branch_refresh_v1 merge-history fixtures as COMMITS:SEED[,...]; "
369
            "the first is primary, the rest are held-out scaling cases."
370
        ),
371
    )
372
    parser.add_argument(
373
        "--injected-request-delay-ms",
374
        type=float,
375
        default=0.0,
376
        help=(
377
            "shallow_worker_branch_refresh_v1: fixed sleep the loopback proxy adds before each "
378
            "HTTP request (labeled injected_request_delay_ms; not a shaped network)."
379
        ),
380
    )
381
    parser.add_argument("--runs", type=int, default=3)
382
    parser.add_argument(
383
        "--scenarios",
384
        default=TASK_LOOP_SCENARIO,
385
        help=(
386
            "Comma-separated scenarios: task_loop, agent_task_baseline_v1, "
387
            "agent_task_oak_local_v1, legacy_selected_clone_materialization_v1, "
388
            "shallow_worker_branch_refresh_v1, "
389
            "and/or interrupted_publication_after_request_v1. "
390
            "The interruption "
391
            "scenario is a loopback-only Oak fixture and never contacts a remote service."
392
        ),
393
    )
394
    parser.add_argument(
395
        "--git-remote",
396
        choices=("github", "local"),
397
        default="github",
398
        help=(
399
            "github: agent-default track, real push to GIT_BENCH_REMOTE "
400
            "(transport-comparable to oak). local: core-equivalent track, "
401
            "local bare remote (no network in git's loop)."
402
        ),
403
    )
404
    parser.add_argument("--workdir", type=Path, default=DEFAULT_WORKDIR)
405
    parser.add_argument("--results", type=Path, default=ROOT / "results" / "task-loop")
406
    parser.add_argument("--oak-repo", type=Path, default=DEFAULT_OAK_REPO)
407
    parser.add_argument("--admitted-output-chars", type=int, default=20_000)
408
    parser.add_argument("--keep-workdirs", action="store_true")
409
    parser.add_argument(
410
        "--randomize-subject-order",
411
        action="store_true",
412
        help="Shuffle subject order independently in each run using --random-seed.",
413
    )
414
    parser.add_argument("--random-seed", type=int, default=20260908)
415
    return parser.parse_args()
416
417
418
def run_untimed(command: list[str], cwd: Path | None, timeout: float = 300.0) -> subprocess.CompletedProcess[str]:
419
    return subprocess.run(
420
        command,
421
        cwd=cwd,
422
        env=base_env(),
423
        text=True,
424
        stdout=subprocess.PIPE,
425
        stderr=subprocess.PIPE,
426
        timeout=timeout,
427
        check=False,
428
    )
429
430
431
def step_row(
432
    subject: Subject,
433
    scenario: str,
434
    operation: str,
435
    command: list[str],
436
    cwd: Path,
437
    run_index: int,
438
    metadata: dict[str, Any],
439
    admitted_output_chars: int,
440
    *,
441
    kind: str = "vcs",
442
    expected_returncodes: tuple[int, ...] = (0,),
443
    preserve_stdout: bool = False,
444
    preserve_stderr: bool = False,
445
    env: dict[str, str] | None = None,
446
    timeout_seconds: float | None = None,
447
) -> dict[str, Any]:
448
    capture = run_timed(
449
        command,
450
        cwd,
451
        env or base_env(),
452
        admitted_output_chars,
453
        timeout_seconds=timeout_seconds,
454
    )
455
    expected = not capture.timed_out and capture.returncode in expected_returncodes
456
    is_vcs = 1 if kind == "vcs" else 0
457
    row = {
458
        **metadata,
459
        "subject": subject.name,
460
        "subject_kind": subject.kind,
461
        "subject_label": subject.label,
462
        "scenario": scenario,
463
        "run": run_index,
464
        "operation": operation,
465
        "step_kind": kind,
466
        "elapsed_ms": round(capture.elapsed_ms, 3),
467
        "returncode": 0 if expected else (capture.returncode or 1),
468
        "process_returncode": capture.returncode,
469
        "timed_out": capture.timed_out,
470
        "expected_returncodes": list(expected_returncodes),
471
        "command": command,
472
        "tool_call_count": 1,
473
        "terminal_tool_call_count": 1,
474
        "vcs_tool_call_count": is_vcs,
475
        **oakbench_tokens.interaction_token_fields(
476
            command_display(command),
477
            capture.stdout_text,
478
            capture.stderr_text,
479
            capture.stdout_bytes,
480
            capture.stderr_bytes,
481
            capture.stdout_truncated,
482
            capture.stderr_truncated,
483
        ),
484
    }
485
    if not expected:
486
        row["stderr"] = capture.stderr_text[-4000:]
487
    if preserve_stdout:
488
        row["stdout"] = capture.stdout_text
489
    if preserve_stderr:
490
        row["stderr"] = capture.stderr_text
491
    return row
492
493
494
def skip_row(
495
    subject: Subject,
496
    scenario: str,
497
    run_index: int,
498
    metadata: dict[str, Any],
499
    reason: str,
500
) -> dict[str, Any]:
501
    return {
502
        **metadata,
503
        "subject": subject.name,
504
        "subject_kind": subject.kind,
505
        "subject_label": subject.label,
506
        "scenario": scenario,
507
        "run": run_index,
508
        "operation": "task_loop.skipped",
509
        "step_kind": "summary",
510
        "elapsed_ms": 0.0,
511
        "returncode": SKIP_RETURNCODE,
512
        "command": [],
513
        "skipped": True,
514
        "skip_reason": reason,
515
        "tool_call_count": 0,
516
        "terminal_tool_call_count": 0,
517
        "vcs_tool_call_count": 0,
518
    }
519
520
521
def unmeasured_step_row(
522
    subject: Subject,
523
    scenario: str,
524
    operation: str,
525
    run_index: int,
526
    metadata: dict[str, Any],
527
    reason: str,
528
) -> dict[str, Any]:
529
    row = skip_row(subject, scenario, run_index, metadata, reason)
530
    row["operation"] = operation
531
    return row
532
533
534
def harness_step_row(
535
    subject: Subject,
536
    scenario: str,
537
    operation: str,
538
    run_index: int,
539
    metadata: dict[str, Any],
540
    elapsed_ms: float,
541
    *,
542
    returncode: int = 0,
543
    harness_command_count: int = 0,
544
) -> dict[str, Any]:
545
    return {
546
        **metadata,
547
        "subject": subject.name,
548
        "subject_kind": subject.kind,
549
        "subject_label": subject.label,
550
        "scenario": scenario,
551
        "run": run_index,
552
        "operation": operation,
553
        "step_kind": "harness",
554
        "elapsed_ms": round(elapsed_ms, 3),
555
        "returncode": returncode,
556
        "command": [],
557
        "tool_call_count": 0,
558
        "terminal_tool_call_count": 0,
559
        "vcs_tool_call_count": 0,
560
        "harness_command_count": harness_command_count,
561
        "stdout_bytes": 0,
562
        "stderr_bytes": 0,
563
        "retry_count": 0,
564
        "human_action_count": 0,
565
    }
566
567
568
def phase_total_row(
569
    rows: list[dict[str, Any]],
570
    subject: Subject,
571
    scenario: str,
572
    run_index: int,
573
    metadata: dict[str, Any],
574
    operation: str,
575
) -> dict[str, Any]:
576
    failures = [row for row in rows if row_returncode(row) not in (0, SKIP_RETURNCODE)]
577
    unmeasured = [
578
        str(row["operation"])
579
        for row in rows
580
        if row_returncode(row) == SKIP_RETURNCODE
581
    ]
582
    phase_elapsed_ms: dict[str, float] = {}
583
    for row in rows:
584
        phase = str(row.get("operation", "unknown")).split(".", 1)[0]
585
        phase_elapsed_ms[phase] = phase_elapsed_ms.get(phase, 0.0) + float(
586
            row.get("elapsed_ms", 0.0)
587
        )
588
    return {
589
        **metadata,
590
        "subject": subject.name,
591
        "subject_kind": subject.kind,
592
        "subject_label": subject.label,
593
        "scenario": scenario,
594
        "run": run_index,
595
        "operation": operation,
596
        "step_kind": "summary",
597
        "elapsed_ms": round(sum(float(row.get("elapsed_ms", 0)) for row in rows), 3),
598
        "returncode": 1 if failures else 0,
599
        "coverage_complete": not unmeasured,
600
        "unmeasured_operations": unmeasured,
601
        "command": [row["command"] for row in rows],
602
        "tool_call_count": sum(oakbench_tokens.int_or_zero(row.get("tool_call_count")) for row in rows),
603
        "terminal_tool_call_count": sum(
604
            oakbench_tokens.int_or_zero(row.get("terminal_tool_call_count")) for row in rows
605
        ),
606
        "vcs_tool_call_count": sum(oakbench_tokens.int_or_zero(row.get("vcs_tool_call_count")) for row in rows),
607
        "agent_tool_call_count": sum(
608
            oakbench_tokens.int_or_zero(row.get("tool_call_count")) for row in rows
609
        ),
610
        "harness_command_count": sum(
611
            oakbench_tokens.int_or_zero(row.get("harness_command_count")) for row in rows
612
        ),
613
        "retry_count": sum(oakbench_tokens.int_or_zero(row.get("retry_count")) for row in rows),
614
        "human_action_count": sum(
615
            oakbench_tokens.int_or_zero(row.get("human_action_count")) for row in rows
616
        ),
617
        "phase_elapsed_ms": {
618
            phase: round(elapsed, 3) for phase, elapsed in sorted(phase_elapsed_ms.items())
619
        },
620
        **oakbench_tokens.summed_token_fields(
621
            rows, "sum_of_task_loop_steps_command_plus_admitted_output_chars_div_4"
622
        ),
623
        "summarized_operations": [row["operation"] for row in rows],
624
    }
625
626
627
def has_lane_failures(rows: list[dict[str, Any]]) -> bool:
628
    return any(row_returncode(row) not in (0, SKIP_RETURNCODE) for row in rows)
629
630
631
def enforce_oracle_result(total: dict[str, Any], oracle_ok: bool | None) -> dict[str, Any]:
632
    if oracle_ok is False:
633
        total["returncode"] = 1
634
        total["stderr"] = "oracle_payload_missing_from_remote_branch"
635
    return total
636
637
638
def interrupted_publication_fields(
639
    *,
640
    request_count: int,
641
    request_bytes: int | None,
642
    declared_request_bytes: int | None,
643
    request_framing_valid: bool,
644
    request_body_timed_out: bool,
645
    local_head_before: str | None,
646
    local_head_after: str | None,
647
    status_after: dict[str, Any] | None,
648
    worktree_payload_before: str | None,
649
    worktree_payload_after: str | None,
650
    cli_observation: dict[str, str],
651
) -> dict[str, Any]:
652
    """Evidence fields for a single interrupted mutation attempt.
653
654
    The fixture's receipt of a full request is ground truth for the harness,
655
    not a server durability receipt available to the CLI.  A disconnect after
656
    that point is therefore unknown to the client and must never imply replay.
657
    """
658
    request_complete = bool(
659
        request_count == 1
660
        and request_framing_valid
661
        and declared_request_bytes is not None
662
        and request_bytes == declared_request_bytes
663
    )
664
    if request_count == 0:
665
        delivery_state = "no_request_observed"
666
        fixture_outcome = "not_sent_observed"
667
    elif not request_framing_valid:
668
        delivery_state = "invalid_request_framing"
669
        fixture_outcome = "indeterminate_after_invalid_request_disconnect"
670
    elif request_complete:
671
        delivery_state = "complete_request_observed"
672
        fixture_outcome = "unknown_after_complete_request_disconnect"
673
    else:
674
        delivery_state = "partial_request_observed"
675
        fixture_outcome = "indeterminate_after_partial_request_disconnect"
676
    payload_preserved = bool(
677
        worktree_payload_before
678
        and worktree_payload_before == worktree_payload_after
679
    )
680
    unpublished_state_preserved = bool(status_after and status_after.get("needs_push"))
681
    return {
682
        "publication_outcome": cli_observation["reported_outcome"],
683
        "cli_observation": cli_observation["observation"],
684
        "fixture_delivery_state": delivery_state,
685
        "fixture_outcome_knowledge": fixture_outcome,
686
        "fixture_received_complete_request_before_disconnect": request_complete,
687
        "fixture_request_framing_valid": request_framing_valid,
688
        "fixture_request_body_timed_out": request_body_timed_out,
689
        "fixture_declared_request_bytes": declared_request_bytes,
690
        "mutation_requests_observed": request_count,
691
        "blind_replay_requests": max(0, request_count - 1),
692
        "network_request_bytes_observed": request_bytes,
693
        "local_head_preserved": bool(local_head_before) and local_head_before == local_head_after,
694
        "worktree_payload_preserved": payload_preserved,
695
        "unpublished_state_preserved": unpublished_state_preserved,
696
        "local_work_preserved": payload_preserved and unpublished_state_preserved,
697
        "working_tree_dirty_after": None if status_after is None else status_after.get("dirty"),
698
    }
699
700
701
def cli_publication_observation(row: dict[str, Any], *, json_requested: bool) -> dict[str, str]:
702
    """Classify only what the CLI reported, never what the fault fixture knows."""
703
    if row.get("timed_out"):
704
        return {"reported_outcome": "unreported", "observation": "process_timeout"}
705
    stdout = str(row.get("stdout") or "")
706
    stderr = str(row.get("stderr") or "")
707
    value = _parse_json_object(stdout) if json_requested else None
708
    if value is not None and row.get("process_returncode") == 0 and value.get("published") is True:
709
        return {"reported_outcome": "published", "observation": "json_success_receipt"}
710
    if value is not None and isinstance(value.get("error"), dict):
711
        message = str(value["error"].get("message") or "").lower()
712
        if any(marker in message for marker in ("unconfirmed", "unknown", "may have")):
713
            return {"reported_outcome": "unknown", "observation": "json_error_unknown"}
714
        return {
715
            "reported_outcome": "unreported",
716
            "observation": "json_error_without_publication_outcome",
717
        }
718
    message = f"{stdout}\n{stderr}".lower()
719
    if any(marker in message for marker in ("unconfirmed", "unknown", "may have")):
720
        return {"reported_outcome": "unknown", "observation": "text_error_unknown"}
721
    return {"reported_outcome": "unreported", "observation": "text_error_without_publication_outcome"}
722
723
724
def file_sha256(path: Path) -> str | None:
725
    try:
726
        return hashlib.sha256(path.read_bytes()).hexdigest()
727
    except OSError:
728
        return None
729
730
731
class _DisconnectAfterPushHandler(BaseHTTPRequestHandler):
732
    """Minimal loopback fixture for a complete request with a lost reply."""
733
734
    server_version = "oakbench-interruption/1"
735
736
    def log_message(self, _format: str, *_args: object) -> None:
737
        return
738
739
    def _json(self, status: int, payload: dict[str, Any]) -> None:
740
        encoded = json.dumps(payload).encode()
741
        self.send_response(status)
742
        self.send_header("Content-Type", "application/json")
743
        self.send_header("Content-Length", str(len(encoded)))
744
        self.end_headers()
745
        self.wfile.write(encoded)
746
747
    def do_GET(self) -> None:  # noqa: N802 - BaseHTTPRequestHandler API
748
        self._json(404, {"error": "absent synthetic repo"})
749
750
    def do_POST(self) -> None:  # noqa: N802 - BaseHTTPRequestHandler API
751
        declared_size: int | None = None
752
        framing_valid = False
753
        body = b""
754
        body_timed_out = False
755
        raw_sizes = self.headers.get_all("Content-Length", failobj=[])
756
        try:
757
            raw_size = raw_sizes[0].strip(" \t") if len(raw_sizes) == 1 else ""
758
            parsed_size = int(raw_size) if raw_size.isascii() and raw_size.isdecimal() else -1
759
            framing_valid = (
760
                0 <= parsed_size <= INTERRUPTED_PUBLICATION_MAX_REQUEST_BYTES
761
                and self.headers.get("Transfer-Encoding") is None
762
            )
763
            if framing_valid:
764
                declared_size = parsed_size
765
                self.connection.settimeout(INTERRUPTED_PUBLICATION_REQUEST_TIMEOUT_S)
766
                while len(body) < parsed_size:
767
                    try:
768
                        chunk = self.rfile.read1(parsed_size - len(body))
769
                    except (socket.timeout, TimeoutError):
770
                        body_timed_out = True
771
                        break
772
                    except OSError:
773
                        break
774
                    if not chunk:
775
                        break
776
                    body += chunk
777
        except ValueError:
778
            framing_valid = False
779
        server = self.server
780
        if self.path == "/api/repos":
781
            self._json(200, {"name": "repo"})
782
            return
783
        if self.path == "/api/oak/repo/push":
784
            with server.receipt_lock:  # type: ignore[attr-defined]
785
                server.push_request_count += 1  # type: ignore[attr-defined]
786
                server.push_request_bytes += len(body)  # type: ignore[attr-defined]
787
                server.push_declared_request_bytes = declared_size  # type: ignore[attr-defined]
788
                server.push_request_framing_valid = framing_valid  # type: ignore[attr-defined]
789
                server.push_request_body_timed_out = body_timed_out  # type: ignore[attr-defined]
790
                server.request_complete_before_disconnect = (  # type: ignore[attr-defined]
791
                    framing_valid and len(body) == declared_size
792
                )
793
            self.close_connection = True
794
            try:
795
                self.connection.shutdown(socket.SHUT_RDWR)
796
            except OSError:
797
                pass
798
            self.connection.close()
799
            return
800
        self._json(404, {"error": "unexpected synthetic path"})
801
802
803
def start_interrupted_publication_server() -> tuple[ThreadingHTTPServer, threading.Thread]:
804
    server = ThreadingHTTPServer(("127.0.0.1", 0), _DisconnectAfterPushHandler)
805
    server.receipt_lock = threading.Lock()  # type: ignore[attr-defined]
806
    server.push_request_count = 0  # type: ignore[attr-defined]
807
    server.push_request_bytes = 0  # type: ignore[attr-defined]
808
    server.push_declared_request_bytes = None  # type: ignore[attr-defined]
809
    server.push_request_framing_valid = False  # type: ignore[attr-defined]
810
    server.push_request_body_timed_out = False  # type: ignore[attr-defined]
811
    server.request_complete_before_disconnect = False  # type: ignore[attr-defined]
812
    thread = threading.Thread(target=server.serve_forever, name="oakbench-interrupted-push", daemon=True)
813
    thread.start()
814
    return server, thread
815
816
817
def _parse_json_object(text: str | None) -> dict[str, Any] | None:
818
    try:
819
        value = json.loads(text or "")
820
    except (TypeError, json.JSONDecodeError):
821
        return None
822
    return value if isinstance(value, dict) else None
823
824
825
def run_interrupted_publication(
826
    subject: Subject,
827
    run_root: Path,
828
    run_index: int,
829
    metadata: dict[str, Any],
830
    admitted: int,
831
) -> list[dict[str, Any]]:
832
    """Run one real Oak push against a loopback server that drops its reply.
833
834
    The fixture observes request delivery but deliberately has no durable
835
    remote state. That makes the client's outcome unknown and lets the lane
836
    prove work preservation and absence of blind replay without production
837
    mutations or a fake success claim.
838
    """
839
    scenario = INTERRUPTED_PUBLICATION_SCENARIO
840
    metadata = {
841
        **metadata,
842
        "benchmark_track": "failure-evidence",
843
        "remote_transport": "loopback_fault_fixture",
844
        "remote_server": "127.0.0.1",
845
    }
846
    if subject.kind != "oak":
847
        return [
848
            skip_row(
849
                subject,
850
                scenario,
851
                run_index,
852
                metadata,
853
                "interrupted publication fixture currently exercises the actual Oak HTTP client only",
854
            )
855
        ]
856
857
    workspace = run_root / f"interrupted-{subject.name}-r{run_index}"
858
    workspace.mkdir(parents=True)
859
    rows: list[dict[str, Any]] = []
860
    vcs = str(subject.bin)
861
    rows.append(
862
        step_row(subject, scenario, "setup.init", [vcs, "init", "."], workspace, run_index, metadata, admitted)
863
    )
864
    if row_returncode(rows[-1]) != 0:
865
        return rows
866
    rows.append(
867
        edit_step(
868
            subject,
869
            scenario,
870
            "setup.edit",
871
            workspace,
872
            f"interrupted publication run={run_index}",
873
            run_index,
874
            metadata,
875
            admitted,
876
        )
877
    )
878
    rows.append(
879
        step_row(subject, scenario, "setup.checkpoint", [vcs, "commit"], workspace, run_index, metadata, admitted)
880
    )
881
    if row_returncode(rows[-1]) != 0:
882
        return rows
883
884
    before = run_untimed([vcs, "hash"], workspace)
885
    head_before = before.stdout.strip() if before.returncode == 0 else None
886
    payload_path = workspace / "task-notes.md"
887
    payload_before = file_sha256(payload_path)
888
    before_usage = bounded_tree_usage(workspace)
889
    push_help = run_untimed([vcs, "push", "--help"], workspace)
890
    push_supports_json = "--json" in (push_help.stdout + push_help.stderr)
891
    server, thread = start_interrupted_publication_server()
892
    host, port = server.server_address[:2]
893
    remote = f"http://{host}:{port}"
894
    fixture_env = base_env()
895
    fixture_env.pop("OAK_API_KEY", None)
896
    fixture_env.pop("OAK_REMOTE", None)
897
    fixture_env["OAK_NO_UPDATE_CHECK"] = "1"
898
    try:
899
        push_command = [vcs, "push", "--repo", "oak/repo", "--remote", remote]
900
        if push_supports_json:
901
            push_command.append("--json")
902
        publication_attempt = step_row(
903
            subject,
904
            scenario,
905
            "publication.attempt",
906
            push_command,
907
            workspace,
908
            run_index,
909
            metadata,
910
            admitted,
911
            expected_returncodes=tuple(range(1, 128)),
912
            preserve_stdout=True,
913
            preserve_stderr=True,
914
            env=fixture_env,
915
            timeout_seconds=INTERRUPTED_PUBLICATION_PROCESS_TIMEOUT_S,
916
        )
917
        rows.append(publication_attempt)
918
    finally:
919
        server.shutdown()
920
        server.server_close()
921
        thread.join(timeout=2.0)
922
923
    state_row = step_row(
924
        subject,
925
        scenario,
926
        "reconcile.local_state",
927
        [vcs, "agent", "state", "--json", "--compact"],
928
        workspace,
929
        run_index,
930
        metadata,
931
        admitted,
932
        preserve_stdout=True,
933
        env=fixture_env,
934
    )
935
    rows.append(state_row)
936
    after = run_untimed([vcs, "hash"], workspace)
937
    head_after = after.stdout.strip() if after.returncode == 0 else None
938
    payload_after = file_sha256(payload_path)
939
    status_after = _parse_json_object(state_row.get("stdout"))
940
    with server.receipt_lock:  # type: ignore[attr-defined]
941
        request_count = int(server.push_request_count)  # type: ignore[attr-defined]
942
        request_bytes = int(server.push_request_bytes)  # type: ignore[attr-defined]
943
        declared_request_bytes = server.push_declared_request_bytes  # type: ignore[attr-defined]
944
        request_framing_valid = bool(server.push_request_framing_valid)  # type: ignore[attr-defined]
945
        request_body_timed_out = bool(server.push_request_body_timed_out)  # type: ignore[attr-defined]
946
    cli_observation = cli_publication_observation(
947
        publication_attempt,
948
        json_requested=push_supports_json,
949
    )
950
    evidence = interrupted_publication_fields(
951
        request_count=request_count,
952
        request_bytes=request_bytes if request_count else None,
953
        declared_request_bytes=declared_request_bytes,
954
        request_framing_valid=request_framing_valid,
955
        request_body_timed_out=request_body_timed_out,
956
        local_head_before=head_before,
957
        local_head_after=head_after,
958
        status_after=status_after,
959
        worktree_payload_before=payload_before,
960
        worktree_payload_after=payload_after,
961
        cli_observation=cli_observation,
962
    )
963
    evidence["workspace_usage_before"] = before_usage
964
    evidence["workspace_usage_after"] = bounded_tree_usage(workspace)
965
    evidence["remote_transport"] = "loopback_fault_fixture"
966
    evidence["remote_server"] = str(host)
967
    evidence["human_action_count"] = 0
968
    evidence["compatibility_probe_calls"] = 1
969
    evidence["harness_probe_calls"] = 3
970
    evidence["push_json_supported"] = push_supports_json
971
    total = phase_total_row(rows, subject, scenario, run_index, metadata, "interrupted_publication.total")
972
    evidence["harness_command_count"] = total["harness_command_count"] + 3
973
    total.update(evidence)
974
    if not (
975
        evidence["fixture_received_complete_request_before_disconnect"]
976
        and request_count == 1
977
        and evidence["local_head_preserved"]
978
        and evidence["local_work_preserved"]
979
        and not publication_attempt["timed_out"]
980
        and row_returncode(rows[-1]) == 0
981
    ):
982
        total["returncode"] = 1
983
        total["stderr"] = "interrupted_publication_evidence_incomplete"
984
    rows.append(total)
985
    return rows
986
987
988
def edit_step(
989
    subject: Subject,
990
    scenario: str,
991
    operation: str,
992
    workspace: Path,
993
    payload: str,
994
    run_index: int,
995
    metadata: dict[str, Any],
996
    admitted: int,
997
) -> dict[str, Any]:
998
    command = ["/bin/zsh", "-c", f"echo {payload!r} >> task-notes.md"]
999
    return step_row(
1000
        subject, scenario, operation, command, workspace, run_index, metadata, admitted, kind="edit"
1001
    )
1002
1003
1004
def oak_virtual_branch(subject: Subject, mount_dir: Path) -> str | None:
1005
    proc = run_untimed([str(subject.bin), "status"], mount_dir)
1006
    match = VIRTUAL_BRANCH_RE.search(proc.stdout + proc.stderr)
1007
    return match.group(1) if match else None
1008
1009
1010
def run_oak_loop(
1011
    subject: Subject,
1012
    remote: oakbench_remotes.RemoteResolution,
1013
    run_root: Path,
1014
    run_index: int,
1015
    metadata: dict[str, Any],
1016
    admitted: int,
1017
) -> list[dict[str, Any]]:
1018
    scenario = "task_loop"
1019
    meta = {**metadata, **remote.row_fields()}
1020
    rows: list[dict[str, Any]] = []
1021
    branches: dict[str, str | None] = {}
1022
    payloads: dict[str, str] = {}
1023
    mounts: list[Path] = []
1024
    vcs = str(subject.bin)
1025
    try:
1026
        for phase in PHASES:
1027
            workspace = run_root / f"tl-{subject.name}-r{run_index}-{phase}"
1028
            payload = f"payload {phase} run={run_index} bench={metadata['bench_id']}"
1029
            payloads[phase] = payload
1030
            acquire = step_row(
1031
                subject, scenario, f"{phase}.acquire", [vcs, "mount", remote.repo or "", str(workspace)],
1032
                run_root, run_index, meta, admitted,
1033
            )
1034
            rows.append(acquire)
1035
            if int(acquire["returncode"]) != 0:
1036
                return rows
1037
            mounts.append(workspace)
1038
            branches[phase] = oak_virtual_branch(subject, workspace)
1039
            rows.append(edit_step(subject, scenario, f"{phase}.edit", workspace, payload, run_index, meta, admitted))
1040
            rows.append(step_row(subject, scenario, f"{phase}.snapshot", [vcs, "commit", "--no-verify"], workspace, run_index, meta, admitted))
1041
            rows.append(step_row(subject, scenario, f"{phase}.publish", [vcs, "push"], workspace, run_index, meta, admitted))
1042
            if phase == "task":
1043
                rows.append(
1044
                    step_row(
1045
                        subject, scenario, "task.describe",
1046
                        [vcs, "desc", f"bench task loop {metadata['bench_id']} r{run_index}"],
1047
                        workspace, run_index, meta, admitted,
1048
                    )
1049
                )
1050
            end = step_row(
1051
                subject, scenario, f"{phase}.end", [vcs, "mount", "end", str(workspace)],
1052
                run_root, run_index, meta, admitted,
1053
            )
1054
            rows.append(end)
1055
            if int(end["returncode"]) == 0 and workspace in mounts:
1056
                mounts.remove(workspace)
1057
1058
        # Untimed oracle: each phase's payload must be on its pushed branch.
1059
        verifier = run_root / f"tl-verify-{subject.name}-r{run_index}"
1060
        clone = run_untimed([vcs, "clone", remote.repo or "", str(verifier)], run_root)
1061
        oracle_ok: bool | None = None
1062
        if clone.returncode == 0:
1063
            oracle_ok = True
1064
            for phase in PHASES:
1065
                branch = branches.get(phase)
1066
                if not branch:
1067
                    oracle_ok = None
1068
                    break
1069
                switch = run_untimed([vcs, "switch", branch], verifier)
1070
                content = (verifier / "task-notes.md")
1071
                seen = content.read_text() if content.exists() else ""
1072
                if switch.returncode != 0 or payloads[phase] not in seen:
1073
                    oracle_ok = False
1074
        total = {
1075
            **phase_total_row(rows, subject, scenario, run_index, meta, "task_loop.total"),
1076
            "oracle_payload_on_remote_branch": oracle_ok,
1077
            "workspace_branches": branches,
1078
            "measurement_source": "verifier_clone_switch_grep; null oracle means branch name unparsed",
1079
        }
1080
        rows.append(enforce_oracle_result(total, oracle_ok))
1081
        return rows
1082
    finally:
1083
        for mount_dir in mounts:
1084
            run_untimed([vcs, "mount", "end", str(mount_dir), "-f"], run_root)
1085
1086
1087
def ensure_git_remote_seeded(subject: Subject, remote: oakbench_remotes.RemoteResolution, scratch: Path) -> str | None:
1088
    """Untimed: make sure the git remote has a main with a seed commit."""
1089
    vcs = str(subject.bin)
1090
    probe = run_untimed([vcs, "ls-remote", str(remote.repo), "refs/heads/main"], None)
1091
    if probe.returncode == 0 and probe.stdout.strip():
1092
        if remote.transport == oakbench_remotes.TRANSPORT_LOCAL_FILE:
1093
            head = run_untimed([vcs, "symbolic-ref", "HEAD", "refs/heads/main"], Path(str(remote.repo)))
1094
            if head.returncode != 0:
1095
                return f"git remote HEAD setup failed: {head.stderr.strip()[:200]}"
1096
        return None
1097
    seed = scratch / "git-seed"
1098
    if seed.exists():
1099
        shutil.rmtree(seed)
1100
    seed.mkdir(parents=True)
1101
    (seed / "README.md").write_text("Task-loop benchmark repo (disposable).\n")
1102
    (seed / "task-notes.md").write_text("# task notes\n")
1103
    for command in (
1104
        [vcs, "init", "-q", "-b", "main"],
1105
        [vcs, "add", "."],
1106
        [vcs, "commit", "-q", "-m", "seed"],
1107
        [vcs, "push", "-q", str(remote.repo), "main:main"],
1108
    ):
1109
        proc = run_untimed(command, seed)
1110
        if proc.returncode != 0:
1111
            return f"git remote seed `{' '.join(command[1:3])}` failed: {proc.stderr.strip()[:200]}"
1112
    if remote.transport == oakbench_remotes.TRANSPORT_LOCAL_FILE:
1113
        head = run_untimed([vcs, "symbolic-ref", "HEAD", "refs/heads/main"], Path(str(remote.repo)))
1114
        if head.returncode != 0:
1115
            return f"git remote HEAD setup failed: {head.stderr.strip()[:200]}"
1116
    return None
1117
1118
1119
def run_git_agent_task_baseline(
1120
    subject: Subject,
1121
    remote: oakbench_remotes.RemoteResolution,
1122
    run_root: Path,
1123
    run_index: int,
1124
    metadata: dict[str, Any],
1125
    admitted: int,
1126
    remote_create_ms: float = 0.0,
1127
) -> list[dict[str, Any]]:
1128
    """Measure a cold clone and a warm isolated worktree on one local remote.
1129
1130
    This is the core-equivalent, local-file cell. Hosted CI is deliberately a
1131
    skip row: a local commit oracle is not relabelled as provider CI.
1132
    """
1133
    scenario = AGENT_TASK_SCENARIO
1134
    meta = {
1135
        **metadata,
1136
        **remote.row_fields(),
1137
        "benchmark_track": (
1138
            "core-equivalent"
1139
            if remote.transport == oakbench_remotes.TRANSPORT_LOCAL_FILE
1140
            else "agent-default"
1141
        ),
1142
    }
1143
    vcs = str(subject.bin)
1144
    rows: list[dict[str, Any]] = [
1145
        harness_step_row(
1146
            subject,
1147
            scenario,
1148
            "setup.remote_container",
1149
            run_index,
1150
            meta,
1151
            remote_create_ms,
1152
            harness_command_count=1,
1153
        )
1154
    ]
1155
    setup_start = time.perf_counter()
1156
    seed_error = ensure_git_remote_seeded(subject, remote, run_root)
1157
    rows.append(
1158
        harness_step_row(
1159
            subject,
1160
            scenario,
1161
            "setup.remote_seed",
1162
            run_index,
1163
            meta,
1164
            (time.perf_counter() - setup_start) * 1000,
1165
            returncode=1 if seed_error else 0,
1166
            harness_command_count=6,
1167
        )
1168
    )
1169
    if seed_error:
1170
        rows[-1]["stderr"] = seed_error
1171
        return rows
1172
1173
    base = run_root / f"agent-task-{subject.name}-r{run_index}-cold"
1174
    cold_branch = oakbench_remotes.disposable_branch(
1175
        str(metadata["bench_id"]), "agent-task-cold", subject.name, run_index
1176
    )
1177
    warm_branch = oakbench_remotes.disposable_branch(
1178
        str(metadata["bench_id"]), "agent-task-warm", subject.name, run_index
1179
    )
1180
    cold_payload = f"cold task run={run_index} bench={metadata['bench_id']}"
1181
    warm_payload = f"warm task run={run_index} bench={metadata['bench_id']}"
1182
1183
    rows.append(
1184
        step_row(
1185
            subject,
1186
            scenario,
1187
            "cold.acquire",
1188
            [vcs, "clone", "-q", str(remote.repo), str(base)],
1189
            run_root,
1190
            run_index,
1191
            meta,
1192
            admitted,
1193
        )
1194
    )
1195
    if row_returncode(rows[-1]) != 0:
1196
        return rows
1197
    rows.append(
1198
        step_row(
1199
            subject, scenario, "cold.branch", [vcs, "switch", "-q", "-c", cold_branch],
1200
            base, run_index, meta, admitted,
1201
        )
1202
    )
1203
    rows.append(
1204
        step_row(
1205
            subject, scenario, "cold.inspect", [vcs, "status", "--porcelain=v1"],
1206
            base, run_index, meta, admitted,
1207
        )
1208
    )
1209
    rows.append(edit_step(subject, scenario, "cold.edit", base, cold_payload, run_index, meta, admitted))
1210
    rows.append(step_row(subject, scenario, "cold.checkpoint.add", [vcs, "add", "."], base, run_index, meta, admitted))
1211
    rows.append(
1212
        step_row(
1213
            subject, scenario, "cold.checkpoint.commit",
1214
            [vcs, "commit", "-q", "-m", f"agent task cold {metadata['bench_id']} r{run_index}"],
1215
            base, run_index, meta, admitted,
1216
        )
1217
    )
1218
    rows.append(
1219
        step_row(
1220
            subject, scenario, "cold.publish", [vcs, "push", "-q", "-u", "origin", cold_branch],
1221
            base, run_index, meta, admitted,
1222
        )
1223
    )
1224
    rows.append(
1225
        unmeasured_step_row(
1226
            subject, scenario, "cold.ci.observe", run_index, meta,
1227
            "local-file transport has no hosted CI provider; exact CI observation is unmeasured",
1228
        )
1229
    )
1230
    rows.append(
1231
        step_row(
1232
            subject, scenario, "cold.review_handoff",
1233
            [vcs, "show", "--format=%H%n%s", "--stat", "--oneline", "HEAD"],
1234
            base, run_index, meta, admitted,
1235
        )
1236
    )
1237
1238
    warm = run_root / f"agent-task-{subject.name}-r{run_index}-warm"
1239
    rows.append(
1240
        step_row(
1241
            subject, scenario, "warm.acquire",
1242
            [vcs, "worktree", "add", "-q", str(warm), "-b", warm_branch, "origin/main"],
1243
            base, run_index, meta, admitted,
1244
        )
1245
    )
1246
    if row_returncode(rows[-1]) == 0:
1247
        rows.append(
1248
            step_row(
1249
                subject, scenario, "warm.inspect", [vcs, "status", "--porcelain=v1"],
1250
                warm, run_index, meta, admitted,
1251
            )
1252
        )
1253
        rows.append(edit_step(subject, scenario, "warm.edit", warm, warm_payload, run_index, meta, admitted))
1254
        rows.append(step_row(subject, scenario, "warm.checkpoint.add", [vcs, "add", "."], warm, run_index, meta, admitted))
1255
        rows.append(
1256
            step_row(
1257
                subject, scenario, "warm.checkpoint.commit",
1258
                [vcs, "commit", "-q", "-m", f"agent task warm {metadata['bench_id']} r{run_index}"],
1259
                warm, run_index, meta, admitted,
1260
            )
1261
        )
1262
        rows.append(
1263
            step_row(
1264
                subject, scenario, "warm.publish", [vcs, "push", "-q", "-u", "origin", warm_branch],
1265
                warm, run_index, meta, admitted,
1266
            )
1267
        )
1268
        rows.append(
1269
            unmeasured_step_row(
1270
                subject, scenario, "warm.ci.observe", run_index, meta,
1271
                "local-file transport has no hosted CI provider; exact CI observation is unmeasured",
1272
            )
1273
        )
1274
        rows.append(
1275
            step_row(
1276
                subject, scenario, "warm.review_handoff",
1277
                [vcs, "show", "--format=%H%n%s", "--stat", "--oneline", "HEAD"],
1278
                warm, run_index, meta, admitted,
1279
            )
1280
        )
1281
1282
    usage_before_cleanup = {
1283
        "cold": bounded_tree_usage(base),
1284
        "warm": bounded_tree_usage(warm),
1285
    }
1286
    oracle_ok = True
1287
    for branch, payload in ((cold_branch, cold_payload), (warm_branch, warm_payload)):
1288
        show = run_untimed([vcs, "--git-dir", str(remote.repo), "show", f"{branch}:task-notes.md"], run_root)
1289
        if show.returncode != 0 or payload not in show.stdout:
1290
            oracle_ok = False
1291
1292
    if warm.exists():
1293
        rows.append(
1294
            step_row(
1295
                subject, scenario, "cleanup.warm",
1296
                [vcs, "worktree", "remove", str(warm)], base, run_index, meta, admitted,
1297
            )
1298
        )
1299
    cleanup_start = time.perf_counter()
1300
    shutil.rmtree(base, ignore_errors=True)
1301
    rows.append(
1302
        harness_step_row(
1303
            subject, scenario, "cleanup.cold", run_index, meta,
1304
            (time.perf_counter() - cleanup_start) * 1000,
1305
        )
1306
    )
1307
1308
    total = phase_total_row(rows, subject, scenario, run_index, meta, "agent_task.total")
1309
    total.update(
1310
        {
1311
            "oracle_payload_on_remote_branch": oracle_ok,
1312
            "acquisition_models": {
1313
                "cold": "fresh_full_clone",
1314
                "warm": "shared_object_worktree",
1315
            },
1316
            "repository_cache_state": {
1317
                "cold": "fresh_destination_no_shared_object_store",
1318
                "warm": "explicit_shared_object_store",
1319
            },
1320
            "os_cache_state": "unknown",
1321
            "os_cache_state_reason": "page_cache_not_purged_or_instrumented",
1322
            "workspace_usage_before_cleanup": usage_before_cleanup,
1323
            "network_bytes": None,
1324
            "network_bytes_reason": "local_file_transport_not_observed_by_byte_proxy",
1325
            "partial_clone_comparator": None,
1326
            "partial_clone_reason": "local file transport does not establish a network partial-clone comparison",
1327
            "human_action_count": 0,
1328
            "parallel_evidence_lane": "parallel_contention.py/workspace_per_task",
1329
            "oracle_probe_calls": 2,
1330
            "harness_command_count": total["harness_command_count"] + 2,
1331
        }
1332
    )
1333
    if not oracle_ok:
1334
        total["returncode"] = 1
1335
        total["stderr"] = "agent_task_remote_payload_oracle_failed"
1336
    rows.append(total)
1337
    return rows
1338
1339
1340
def reserve_loopback_port() -> int:
1341
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as listener:
1342
        listener.bind(("127.0.0.1", 0))
1343
        return int(listener.getsockname()[1])
1344
1345
1346
def wait_for_loopback_port(
1347
    process: subprocess.Popen[Any], port: int, timeout_s: float = LOCAL_OAK_SERVER_READY_TIMEOUT_S
1348
) -> bool:
1349
    deadline = time.monotonic() + timeout_s
1350
    while time.monotonic() < deadline:
1351
        if process.poll() is not None:
1352
            return False
1353
        try:
1354
            with socket.create_connection(("127.0.0.1", port), timeout=0.2):
1355
                return True
1356
        except OSError:
1357
            time.sleep(0.05)
1358
    return False
1359
1360
1361
def harness_command_row(
1362
    subject: Subject,
1363
    scenario: str,
1364
    operation: str,
1365
    command: list[str],
1366
    cwd: Path,
1367
    run_index: int,
1368
    metadata: dict[str, Any],
1369
) -> tuple[dict[str, Any], subprocess.CompletedProcess[str]]:
1370
    started = time.perf_counter()
1371
    result = run_bounded_harness_command(command, cwd)
1372
    row = harness_step_row(
1373
        subject,
1374
        scenario,
1375
        operation,
1376
        run_index,
1377
        metadata,
1378
        (time.perf_counter() - started) * 1000,
1379
        returncode=0 if result.returncode == 0 else result.returncode,
1380
        harness_command_count=1,
1381
    )
1382
    if result.returncode != 0:
1383
        row["stderr"] = str(result.stderr)[-4000:]
1384
    return row, result
1385
1386
1387
def run_bounded_harness_command(
1388
    command: list[str], cwd: Path, *, env: dict[str, str] | None = None
1389
) -> subprocess.CompletedProcess[str]:
1390
    capture = run_timed(
1391
        command,
1392
        cwd,
1393
        env or base_env(),
1394
        admitted_output_chars=4_000,
1395
        timeout_seconds=INTERRUPTED_PUBLICATION_PROCESS_TIMEOUT_S,
1396
    )
1397
    return subprocess.CompletedProcess(
1398
        command,
1399
        capture.returncode,
1400
        capture.stdout_text,
1401
        capture.stderr_text,
1402
    )
1403
1404
1405
def remote_branch_head(remote: str, repo_spec: str, branch: str) -> str | None:
1406
    owner, repo = repo_spec.split("/", 1)
1407
    branch_segment = urllib.parse.quote(branch, safe="")
1408
    url = f"{remote}/api/{owner}/{repo}/branches/{branch_segment}"
1409
    try:
1410
        with urllib.request.urlopen(url, timeout=2.0) as response:
1411
            value = json.loads(response.read())
1412
    except (OSError, ValueError, urllib.error.URLError):
1413
        return None
1414
    head = value.get("head") if isinstance(value, dict) else None
1415
    return head if isinstance(head, str) else None
1416
1417
1418
def run_oak_local_agent_task_baseline(
1419
    subject: Subject,
1420
    run_root: Path,
1421
    run_index: int,
1422
    metadata: dict[str, Any],
1423
    admitted: int,
1424
) -> list[dict[str, Any]]:
1425
    """Exercise two fresh Oak checkouts against an owned loopback Oak server."""
1426
    run_root = run_root.resolve()
1427
    scenario = AGENT_TASK_OAK_LOCAL_SCENARIO
1428
    meta = {
1429
        **metadata,
1430
        "benchmark_track": "oak-local-loopback",
1431
        "remote_transport": "loopback_http",
1432
        "remote_server": "127.0.0.1",
1433
    }
1434
    rows: list[dict[str, Any]] = []
1435
    vcs = str(subject.bin)
1436
    server_root = run_root / f"oak-local-server-{subject.name}-r{run_index}"
1437
    seed = run_root / f"oak-local-seed-{subject.name}-r{run_index}"
1438
    cold = run_root / f"oak-local-cold-{subject.name}-r{run_index}"
1439
    followup = run_root / f"oak-local-followup-{subject.name}-r{run_index}"
1440
    verifier = run_root / f"oak-local-verifier-{subject.name}-r{run_index}"
1441
    repo_spec = f"qa/task-loop-{subject.name}-r{run_index}"
1442
    port = reserve_loopback_port()
1443
    remote = f"http://127.0.0.1:{port}"
1444
    server_root.mkdir(parents=True)
1445
    server_started = time.perf_counter()
1446
    server: subprocess.Popen[Any] | None = None
1447
    server_start_error: str | None = None
1448
    try:
1449
        server = subprocess.Popen(
1450
            [vcs, "serve", "--dir", str(server_root), "--host", "127.0.0.1", "--port", str(port)],
1451
            cwd=run_root,
1452
            env=base_env(),
1453
            stdout=subprocess.DEVNULL,
1454
            stderr=subprocess.DEVNULL,
1455
            start_new_session=os.name == "posix",
1456
        )
1457
    except OSError as error:
1458
        server_start_error = type(error).__name__
1459
    ready = server is not None and wait_for_loopback_port(server, port)
1460
    rows.append(
1461
        harness_step_row(
1462
            subject,
1463
            scenario,
1464
            "setup.server",
1465
            run_index,
1466
            meta,
1467
            (time.perf_counter() - server_started) * 1000,
1468
            returncode=0 if ready else 1,
1469
            harness_command_count=1,
1470
        )
1471
    )
1472
    branch: str | None = None
1473
    expected_head: str | None = None
1474
    expected_payload = "# task notes\n"
1475
    failure: str | None = None if ready else (
1476
        f"local_oak_server_start_failed:{server_start_error}"
1477
        if server_start_error else "local_oak_server_not_ready"
1478
    )
1479
    try:
1480
        if failure is None:
1481
            seed.mkdir(parents=True)
1482
            (seed / "task-notes.md").write_text(expected_payload)
1483
            for operation, command in (
1484
                ("setup.seed_init", [vcs, "init", "."]),
1485
                ("setup.seed_commit", [vcs, "commit"]),
1486
                ("setup.seed_push", [vcs, "push", "--repo", repo_spec, "--remote", remote]),
1487
            ):
1488
                row, result = harness_command_row(
1489
                    subject, scenario, operation, command, seed, run_index, meta
1490
                )
1491
                rows.append(row)
1492
                if result.returncode != 0:
1493
                    failure = f"{operation}_failed"
1494
                    break
1495
1496
        def agent_step(operation: str, command: list[str], cwd: Path, *, preserve: bool = False) -> dict[str, Any]:
1497
            row = step_row(
1498
                subject,
1499
                scenario,
1500
                operation,
1501
                command,
1502
                cwd,
1503
                run_index,
1504
                meta,
1505
                admitted,
1506
                preserve_stdout=preserve,
1507
                timeout_seconds=INTERRUPTED_PUBLICATION_PROCESS_TIMEOUT_S,
1508
            )
1509
            rows.append(row)
1510
            return row
1511
1512
        if failure is None:
1513
            row = agent_step(
1514
                "cold.acquire",
1515
                [vcs, "clone", repo_spec, str(cold), "--remote", remote, "--allow-legacy-scope"],
1516
                run_root,
1517
            )
1518
            if row_returncode(row) != 0:
1519
                failure = "cold.acquire_failed"
1520
        if failure is None:
1521
            status = agent_step("cold.inspect", [vcs, "status", "--json"], cold, preserve=True)
1522
            status_value = _parse_json_object(status.get("stdout"))
1523
            branch_value = status_value.get("branch") if status_value else None
1524
            branch = branch_value if isinstance(branch_value, str) else None
1525
            if not branch:
1526
                failure = "cold.branch_identity_missing"
1527
        if failure is None:
1528
            cold_payload = f"cold task run={run_index} bench={metadata['bench_id']}\n"
1529
            expected_payload += cold_payload
1530
            rows.append(edit_step(subject, scenario, "cold.edit", cold, cold_payload.strip(), run_index, meta, admitted))
1531
            if row_returncode(agent_step("cold.checkpoint", [vcs, "commit"], cold)) != 0:
1532
                failure = "cold.checkpoint_failed"
1533
            elif row_returncode(
1534
                agent_step("cold.publish", [vcs, "push", "--repo", repo_spec, "--remote", remote], cold)
1535
            ) != 0:
1536
                failure = "cold.publish_failed"
1537
            rows.append(
1538
                unmeasured_step_row(
1539
                    subject, scenario, "cold.ci.observe", run_index, meta,
1540
                    "owned local oak serve has no configured CI provider",
1541
                )
1542
            )
1543
            rows.append(
1544
                unmeasured_step_row(
1545
                    subject, scenario, "cold.review_handoff", run_index, meta,
1546
                    "owned local oak serve does not provide hosted review semantics",
1547
                )
1548
            )
1549
        if failure is None and branch is not None:
1550
            row = agent_step(
1551
                "followup.acquire",
1552
                [
1553
                    vcs, "clone", repo_spec, str(followup), "--remote", remote,
1554
                    "--branch", branch, "--allow-legacy-scope",
1555
                ],
1556
                run_root,
1557
            )
1558
            if row_returncode(row) != 0:
1559
                failure = "followup.acquire_failed"
1560
        if failure is None:
1561
            agent_step("followup.inspect", [vcs, "status", "--json"], followup)
1562
            followup_payload = f"followup task run={run_index} bench={metadata['bench_id']}\n"
1563
            expected_payload += followup_payload
1564
            rows.append(
1565
                edit_step(
1566
                    subject, scenario, "followup.edit", followup,
1567
                    followup_payload.strip(), run_index, meta, admitted,
1568
                )
1569
            )
1570
            if row_returncode(agent_step("followup.checkpoint", [vcs, "commit"], followup)) != 0:
1571
                failure = "followup.checkpoint_failed"
1572
            else:
1573
                head_result = run_bounded_harness_command([vcs, "hash"], followup)
1574
                expected_head = head_result.stdout.strip() if head_result.returncode == 0 else None
1575
                if row_returncode(
1576
                    agent_step(
1577
                        "followup.publish",
1578
                        [vcs, "push", "--repo", repo_spec, "--remote", remote],
1579
                        followup,
1580
                    )
1581
                ) != 0:
1582
                    failure = "followup.publish_failed"
1583
            rows.append(
1584
                unmeasured_step_row(
1585
                    subject, scenario, "followup.ci.observe", run_index, meta,
1586
                    "owned local oak serve has no configured CI provider",
1587
                )
1588
            )
1589
            rows.append(
1590
                unmeasured_step_row(
1591
                    subject, scenario, "followup.review_handoff", run_index, meta,
1592
                    "owned local oak serve does not provide hosted review semantics",
1593
                )
1594
            )
1595
1596
        payload_ok = False
1597
        verifier_head: str | None = None
1598
        advertised_head: str | None = None
1599
        if failure is None and branch is not None:
1600
            clone = run_bounded_harness_command(
1601
                [
1602
                    vcs, "clone", repo_spec, str(verifier), "--remote", remote,
1603
                    "--branch", branch, "--allow-legacy-scope",
1604
                ],
1605
                run_root,
1606
            )
1607
            if clone.returncode == 0:
1608
                payload_path = verifier / "task-notes.md"
1609
                payload_ok = payload_path.exists() and payload_path.read_text() == expected_payload
1610
                verifier_result = run_bounded_harness_command([vcs, "hash"], verifier)
1611
                verifier_head = verifier_result.stdout.strip() if verifier_result.returncode == 0 else None
1612
                advertised_head = remote_branch_head(remote, repo_spec, branch)
1613
            else:
1614
                failure = "verifier_clone_failed"
1615
        head_ok = bool(expected_head and expected_head == verifier_head == advertised_head)
1616
        if not payload_ok:
1617
            failure = failure or "local_oak_payload_oracle_failed"
1618
        if not head_ok:
1619
            failure = failure or "local_oak_head_oracle_failed"
1620
    finally:
1621
        if server is not None:
1622
            stop_owned_process(server)
1623
        server_stopped = server is None or server.poll() is not None
1624
        cleanup_started = time.perf_counter()
1625
        for path in (seed, cold, followup, verifier, server_root):
1626
            shutil.rmtree(path, ignore_errors=True)
1627
        rows.append(
1628
            harness_step_row(
1629
                subject,
1630
                scenario,
1631
                "cleanup.local_server_and_checkouts",
1632
                run_index,
1633
                meta,
1634
                (time.perf_counter() - cleanup_started) * 1000,
1635
            )
1636
        )
1637
1638
    total = phase_total_row(rows, subject, scenario, run_index, meta, "agent_task_oak_local.total")
1639
    total.update(
1640
        {
1641
            "oracle_payload_on_remote_branch": payload_ok and head_ok,
1642
            "oracle_payload_matches": payload_ok,
1643
            "oracle_branch_head_matches": head_ok,
1644
            "server_process_stopped": server_stopped,
1645
            "acquisition_models": {
1646
                "cold": "fresh_full_clone",
1647
                "followup": "second_fresh_full_clone",
1648
            },
1649
            "repository_cache_state": {
1650
                "cold": "fresh_destination_no_shared_object_store",
1651
                "followup": "no_shared_object_store",
1652
            },
1653
            "os_cache_state": "unknown",
1654
            "os_cache_state_reason": "page_cache_not_purged_or_instrumented",
1655
            "network_bytes": None,
1656
            "network_bytes_reason": "loopback transport not observed by byte proxy",
1657
            "hosted_ci_configured": False,
1658
            "oracle_probe_calls": 4,
1659
            # The expected-head hash plus verifier clone/hash/HTTP head probe
1660
            # are oracle/setup commands, not agent-facing tool calls.
1661
            "harness_command_count": total["harness_command_count"] + 4,
1662
        }
1663
    )
1664
    if failure or not server_stopped:
1665
        total["returncode"] = 1
1666
        total["stderr"] = failure or "local_oak_server_cleanup_failed"
1667
    rows.append(total)
1668
    return rows
1669
1670
1671
def run_legacy_selected_materialization_campaign(
1672
    subjects: list[Subject],
1673
    server_bin: Path | None,
1674
    server_source: Path | None,
1675
    server_source_head: str | None,
1676
    baseline_subject_name: str | None,
1677
    run_root: Path,
1678
    runs: int,
1679
    metadata: dict[str, Any],
1680
    admitted: int,
1681
    randomize_order: bool,
1682
    random_seed: int,
1683
    timing_controls_requested: bool,
1684
) -> list[dict[str, Any]]:
1685
    """Measure one legacy selected-branch clone against one fixed Serve fixture."""
1686
    scenario = LEGACY_SELECTED_MATERIALIZATION_SCENARIO
1687
    oak_subjects = [subject for subject in subjects if subject.kind == "oak"]
1688
    if not oak_subjects:
1689
        return []
1690
    if (
1691
        server_bin is None
1692
        or not server_bin.is_absolute()
1693
        or not server_bin.is_file()
1694
        or not os.access(server_bin, os.X_OK)
1695
    ):
1696
        return [
1697
            unmeasured_step_row(
1698
                subject,
1699
                scenario,
1700
                "scenario.skip",
1701
                0,
1702
                metadata,
1703
                "--oak-serve-bin must name an absolute executable used only as the fixed Serve fixture",
1704
            )
1705
            for subject in oak_subjects
1706
        ]
1707
    if server_source_head is None or re.fullmatch(r"[0-9a-f]{64}", server_source_head) is None:
1708
        return [
1709
            unmeasured_step_row(
1710
                subject,
1711
                scenario,
1712
                "scenario.skip",
1713
                0,
1714
                metadata,
1715
                "the fixed Serve binary's exact 64-hex --oak-serve-source-head is required",
1716
            )
1717
            for subject in oak_subjects
1718
        ]
1719
    baseline_subject = next(
1720
        (subject for subject in oak_subjects if subject.name == baseline_subject_name), None
1721
    )
1722
    if baseline_subject is None:
1723
        return [
1724
            unmeasured_step_row(
1725
                subject,
1726
                scenario,
1727
                "scenario.skip",
1728
                0,
1729
                metadata,
1730
                "--materialization-baseline-subject must name one enabled Oak subject",
1731
            )
1732
            for subject in oak_subjects
1733
        ]
1734
    git_bin = shutil.which("git")
1735
    if git_bin is None:
1736
        return [
1737
            unmeasured_step_row(
1738
                subject,
1739
                scenario,
1740
                "scenario.skip",
1741
                0,
1742
                metadata,
1743
                "git is required only to create the deterministic main-history fixture",
1744
            )
1745
            for subject in oak_subjects
1746
        ]
1747
1748
    server_bin = server_bin.resolve()
1749
    resolved_server_source: Path | None = None
1750
    if server_source is not None:
1751
        if not server_source.is_absolute() or not server_source.is_dir():
1752
            return [
1753
                unmeasured_step_row(
1754
                    subject,
1755
                    scenario,
1756
                    "scenario.skip",
1757
                    0,
1758
                    metadata,
1759
                    "--oak-serve-source must name an absolute read-only source checkout",
1760
                )
1761
                for subject in oak_subjects
1762
            ]
1763
        resolved_server_source = server_source.resolve()
1764
        source_head_result = run_bounded_harness_command(
1765
            [str(server_bin), "hash"],
1766
            resolved_server_source,
1767
            env=isolated_oak_fixture_env(run_root / "source-check"),
1768
        )
1769
        if source_head_result.returncode != 0 or source_head_result.stdout.strip() != server_source_head:
1770
            return [
1771
                unmeasured_step_row(
1772
                    subject,
1773
                    scenario,
1774
                    "scenario.skip",
1775
                    0,
1776
                    metadata,
1777
                    "fixed Serve source checkout does not match --oak-serve-source-head",
1778
                )
1779
                for subject in oak_subjects
1780
            ]
1781
    baseline_binary_sha256 = binary_sha256(baseline_subject.bin)
1782
    timing_controls_met = materialization_timing_controls_met(
1783
        metadata, runs, randomize_order, timing_controls_requested
1784
    )
1785
    fixture_root = run_root / "legacy-selected-materialization-fixture"
1786
    server_root = fixture_root / "server"
1787
    git_seed = fixture_root / "git-seed.git"
1788
    seed = fixture_root / "seed"
1789
    task_config_root = fixture_root / "config"
1790
    for path in (server_root, git_seed, task_config_root):
1791
        path.mkdir(parents=True, exist_ok=True)
1792
    fixture_env = isolated_oak_fixture_env(task_config_root)
1793
    fixture_meta = {
1794
        **metadata,
1795
        "benchmark_track": "oak-local-loopback-materialization",
1796
        "remote_transport": "loopback_http",
1797
        "remote_server": "127.0.0.1",
1798
        "serve_binary": str(server_bin),
1799
        "serve_binary_sha256": binary_sha256(server_bin),
1800
        "serve_source": str(resolved_server_source) if resolved_server_source else None,
1801
        "serve_source_checkout_verification": (
1802
            "exact_head_verified" if resolved_server_source else "unavailable_binary_hash_and_head_supplied"
1803
        ),
1804
        "serve_source_head": server_source_head,
1805
        "serve_version": subject_version(Subject("serve_fixture", "oak", "Serve fixture", server_bin)),
1806
        "baseline_subject": baseline_subject.name,
1807
        "baseline_binary_sha256": baseline_binary_sha256,
1808
        "fixture_id": "legacy-selected-materialization-192x16k-v1",
1809
        "fixture_regular_files": 192,
1810
        "fixture_regular_file_bytes": 16 * 1024,
1811
        "fixture_empty_files": 1,
1812
        "fixture_executable_files": 8,
1813
        "fixture_selected_only_files": 1,
1814
        "os_cache_state": "warm_or_unknown_after_required_fixture_probe",
1815
        "materialization_timing_controls_met": timing_controls_met,
1816
        "materialization_timing_controls_reason": (
1817
            "harness_controls_met_aa_and_quiet_host_evidence_still_required"
1818
            if timing_controls_met
1819
            else "correctness_only_or_harness_controls_not_met"
1820
        ),
1821
    }
1822
    rows: list[dict[str, Any]] = []
1823
    setup_subject = baseline_subject
1824
1825
    def setup_command(operation: str, command: list[str], cwd: Path) -> bool:
1826
        started = time.perf_counter()
1827
        result = run_bounded_harness_command(command, cwd, env=fixture_env)
1828
        rows.append(
1829
            harness_step_row(
1830
                setup_subject,
1831
                scenario,
1832
                operation,
1833
                0,
1834
                fixture_meta,
1835
                (time.perf_counter() - started) * 1000,
1836
                returncode=0 if result.returncode == 0 else result.returncode,
1837
                harness_command_count=1,
1838
            )
1839
        )
1840
        if result.returncode != 0:
1841
            rows[-1]["stderr"] = result.stderr[-4000:]
1842
        return result.returncode == 0
1843
1844
    server: subprocess.Popen[Any] | None = None
1845
    proxy: CountingProxy | None = None
1846
    failure: str | None = None
1847
    expected_artifacts: list[str] | None = None
1848
    baseline_cache: dict[str, Any] | None = None
1849
    baseline_receipt_contract: dict[str, Any] | None = None
1850
    baseline_network_contract: dict[str, Any] | None = None
1851
    expected_status_contract: dict[str, Any] | None = None
1852
    selected_branch = "selected-materialization"
1853
    repo_spec = "qa/materialization-bench"
1854
    try:
1855
        for index in range(192):
1856
            directory = git_seed / f"group-{index % 12:02d}"
1857
            directory.mkdir(parents=True, exist_ok=True)
1858
            digest = hashlib.sha256(f"oak-materialization-{index}".encode()).digest()
1859
            content = (digest * ((16 * 1024 + len(digest) - 1) // len(digest)))[: 16 * 1024]
1860
            path = directory / f"file-{index:03d}.bin"
1861
            path.write_bytes(content)
1862
            if index < 8:
1863
                path.chmod(0o755)
1864
        (git_seed / "empty.txt").write_bytes(b"")
1865
        for operation, command in (
1866
            ("setup.git_init", [git_bin, "init", "-q", "-b", "main"]),
1867
            ("setup.git_add", [git_bin, "add", "."]),
1868
            ("setup.git_commit", [git_bin, "commit", "-q", "-m", "fixture main"]),
1869
        ):
1870
            if not setup_command(operation, command, git_seed):
1871
                failure = f"{operation}_failed"
1872
                break
1873
1874
        port = reserve_loopback_port()
1875
        direct_remote = f"http://127.0.0.1:{port}"
1876
        if failure is None:
1877
            server = subprocess.Popen(
1878
                [
1879
                    str(server_bin),
1880
                    "serve",
1881
                    "--dir",
1882
                    str(server_root),
1883
                    "--host",
1884
                    "127.0.0.1",
1885
                    "--port",
1886
                    str(port),
1887
                ],
1888
                cwd=fixture_root,
1889
                env=fixture_env,
1890
                stdout=subprocess.DEVNULL,
1891
                stderr=subprocess.DEVNULL,
1892
                start_new_session=os.name == "posix",
1893
            )
1894
            if not wait_for_loopback_port(server, port):
1895
                failure = "setup.server_not_ready"
1896
        if failure is None:
1897
            for operation, command, cwd in (
1898
                ("setup.oak_import", [str(server_bin), "clone", str(git_seed), str(seed)], fixture_root),
1899
                ("setup.branch", [str(server_bin), "switch", "-c", selected_branch], seed),
1900
            ):
1901
                if not setup_command(operation, command, cwd):
1902
                    failure = f"{operation}_failed"
1903
                    break
1904
        if failure is None:
1905
            (seed / "selected.txt").write_text("selected branch\n")
1906
            for operation, command in (
1907
                ("setup.oak_commit", [str(server_bin), "commit"]),
1908
                (
1909
                    "setup.oak_push",
1910
                    [str(server_bin), "push", "--repo", repo_spec, "--remote", direct_remote],
1911
                ),
1912
            ):
1913
                if not setup_command(operation, command, seed):
1914
                    failure = f"{operation}_failed"
1915
                    break
1916
1917
        expected_head_result = (
1918
            run_bounded_harness_command([str(server_bin), "hash"], seed, env=fixture_env)
1919
            if failure is None
1920
            else None
1921
        )
1922
        expected_head = (
1923
            expected_head_result.stdout.strip()
1924
            if expected_head_result is not None and expected_head_result.returncode == 0
1925
            else None
1926
        )
1927
        expected_history = (
1928
            normalized_history(json_command([str(server_bin), "log", "--json"], seed, fixture_env))
1929
            if failure is None
1930
            else None
1931
        )
1932
        expected_worktree = normalized_worktree_snapshot(seed) if failure is None else None
1933
        if not expected_head or expected_history is None or expected_worktree is None:
1934
            failure = failure or "setup.fixture_oracle_failed"
1935
1936
        if failure is None:
1937
            proxy = CountingProxy("127.0.0.1", port).start()
1938
            measured_remote = f"http://127.0.0.1:{proxy.address[1]}"
1939
            probe = fixture_root / "baseline-probe"
1940
            probe_env = isolated_oak_fixture_env(fixture_root / "baseline-probe-config")
1941
            proxy.reset_counts()
1942
            result = run_bounded_harness_command(
1943
                [
1944
                    str(baseline_subject.bin),
1945
                    "clone",
1946
                    "--json",
1947
                    "--allow-legacy-scope",
1948
                    "--remote",
1949
                    measured_remote,
1950
                    "--branch",
1951
                    selected_branch,
1952
                    repo_spec,
1953
                    str(probe),
1954
                ],
1955
                fixture_root,
1956
                env=probe_env,
1957
            )
1958
            receipt = _parse_json_object(result.stdout)
1959
            evidence = legacy_selected_receipt_evidence(receipt or {})
1960
            baseline_proxy_stats = proxy.snapshot()
1961
            baseline_cache = stat_cache_snapshot(probe)
1962
            expected_artifacts = normalized_oak_artifacts(probe)
1963
            baseline_receipt_contract = legacy_selected_receipt_contract(evidence)
1964
            baseline_network_contract = {
1965
                "connections": baseline_proxy_stats.connections,
1966
                "receipt": baseline_receipt_contract,
1967
            }
1968
            expected_status_contract = normalized_status(
1969
                json_command([str(baseline_subject.bin), "status", "--json"], probe, probe_env)
1970
            )
1971
            probe_checks = {
1972
                "returncode": result.returncode == 0,
1973
                "legacy_route": evidence["legacy_route_observed"],
1974
                "passes_two": evidence["materialization_passes_observed"] == 2,
1975
                "head": evidence["head"] == expected_head,
1976
                "manifest": bool(evidence["manifest"]),
1977
                "artifacts": expected_artifacts is not None,
1978
                "cache": normalized_cache_contract(baseline_cache) is not None,
1979
                "status": expected_status_contract is not None,
1980
                "network": baseline_proxy_stats.connection_errors == 0,
1981
            }
1982
            if not all(probe_checks.values()):
1983
                failed_checks = ",".join(key for key, passed in probe_checks.items() if not passed)
1984
                failure = f"baseline_probe_failed:{failed_checks}"
1985
                if isinstance(baseline_cache, dict) and baseline_cache.get("error"):
1986
                    failure += f":{baseline_cache['error']}"
1987
            shutil.rmtree(probe, ignore_errors=True)
1988
1989
        if failure is None and proxy is not None:
1990
            measured_remote = f"http://127.0.0.1:{proxy.address[1]}"
1991
            order_rng = random.Random(random_seed)
1992
            for run_index in range(runs):
1993
                run_subjects = list(oak_subjects)
1994
                if randomize_order:
1995
                    order_rng.shuffle(run_subjects)
1996
                for subject in run_subjects:
1997
                    destination = run_root / f"materialization-{subject.name}-r{run_index}"
1998
                    subject_env = isolated_oak_fixture_env(
1999
                        run_root / f"materialization-config-{subject.name}-r{run_index}"
2000
                    )
2001
                    proxy.reset_counts()
2002
                    row = step_row(
2003
                        subject,
2004
                        scenario,
2005
                        "legacy_selected.acquire",
2006
                        [
2007
                            str(subject.bin),
2008
                            "clone",
2009
                            "--json",
2010
                            "--allow-legacy-scope",
2011
                            "--remote",
2012
                            measured_remote,
2013
                            "--branch",
2014
                            selected_branch,
2015
                            repo_spec,
2016
                            str(destination),
2017
                        ],
2018
                        fixture_root,
2019
                        run_index,
2020
                        {
2021
                            **fixture_meta,
2022
                            "run_subject_order": [item.name for item in run_subjects],
2023
                        },
2024
                        admitted,
2025
                        preserve_stdout=True,
2026
                        env=subject_env,
2027
                        timeout_seconds=INTERRUPTED_PUBLICATION_PROCESS_TIMEOUT_S,
2028
                    )
2029
                    receipt = _parse_json_object(row.get("stdout"))
2030
                    evidence = legacy_selected_receipt_evidence(receipt or {})
2031
                    proxy_stats = proxy.snapshot()
2032
                    cache_before_status = stat_cache_snapshot(destination)
2033
                    artifacts = normalized_oak_artifacts(destination)
2034
                    trial_meta = {
2035
                        **fixture_meta,
2036
                        "run_subject_order": [item.name for item in run_subjects],
2037
                    }
2038
                    status_row = step_row(
2039
                        subject,
2040
                        scenario,
2041
                        "legacy_selected.first_status",
2042
                        [str(subject.bin), "status", "--json"],
2043
                        destination,
2044
                        run_index,
2045
                        trial_meta,
2046
                        admitted,
2047
                        preserve_stdout=True,
2048
                        env=subject_env,
2049
                        timeout_seconds=INTERRUPTED_PUBLICATION_PROCESS_TIMEOUT_S,
2050
                    )
2051
                    status = normalized_status(_parse_json_object(status_row.get("stdout")) or {})
2052
                    commit_row = step_row(
2053
                        subject,
2054
                        scenario,
2055
                        "legacy_selected.first_noop_commit",
2056
                        [str(subject.bin), "commit"],
2057
                        destination,
2058
                        run_index,
2059
                        trial_meta,
2060
                        admitted,
2061
                        env=subject_env,
2062
                        timeout_seconds=INTERRUPTED_PUBLICATION_PROCESS_TIMEOUT_S,
2063
                    )
2064
                    history = normalized_history(
2065
                        json_command([str(subject.bin), "log", "--json"], destination, subject_env)
2066
                    )
2067
                    head_result = run_bounded_harness_command(
2068
                        [str(subject.bin), "hash"], destination, env=subject_env
2069
                    )
2070
                    local_head = head_result.stdout.strip() if head_result.returncode == 0 else None
2071
                    worktree = normalized_worktree_snapshot(destination) if destination.exists() else None
2072
                    advertised_head = remote_branch_head(direct_remote, repo_spec, selected_branch)
2073
                    subject_sha256 = binary_sha256(subject.bin)
2074
                    expected_passes = 2 if subject_sha256 == baseline_binary_sha256 else 1
2075
                    receipt_contract = legacy_selected_receipt_contract(evidence)
2076
                    network_contract = {
2077
                        "connections": proxy_stats.connections,
2078
                        "receipt": receipt_contract,
2079
                    }
2080
                    cache_contract = normalized_cache_contract(cache_before_status)
2081
                    baseline_cache_contract = normalized_cache_contract(baseline_cache)
2082
                    expected_cache_valid = subject_sha256 != baseline_binary_sha256
2083
                    cache_valid = bool(
2084
                        isinstance(cache_before_status, dict)
2085
                        and cache_before_status.get("all_metadata_valid") is expected_cache_valid
2086
                    )
2087
                    status_ok = status == expected_status_contract
2088
                    artifacts_ok = artifacts == expected_artifacts
2089
                    network_ok = network_contract == baseline_network_contract
2090
                    oracle_ok = bool(
2091
                        row_returncode(row) == 0
2092
                        and row_returncode(status_row) == 0
2093
                        and row_returncode(commit_row) == 0
2094
                        and evidence["legacy_route_observed"]
2095
                        and evidence["snapshot_bound"] is False
2096
                        and evidence["materialization_passes_observed"] == expected_passes
2097
                        and evidence["head"] == expected_head == local_head == advertised_head
2098
                        and evidence["manifest"] == baseline_receipt_contract["manifest"]
2099
                        and evidence["written_paths"] == len(expected_worktree)
2100
                        and receipt_contract == baseline_receipt_contract
2101
                        and history == expected_history
2102
                        and worktree == expected_worktree
2103
                        and status_ok
2104
                        and artifacts_ok
2105
                        and cache_contract == baseline_cache_contract
2106
                        and cache_valid
2107
                        and network_ok
2108
                        and proxy_stats.connection_errors == 0
2109
                    )
2110
                    row.update(
2111
                        {
2112
                            **evidence,
2113
                            "network_connections_observed": proxy_stats.connections,
2114
                            "network_client_to_server_bytes_observed": proxy_stats.client_to_upstream_bytes,
2115
                            "network_server_to_client_bytes_observed": proxy_stats.upstream_to_client_bytes,
2116
                            "network_connection_errors": proxy_stats.connection_errors,
2117
                            "normalized_network_work": network_contract,
2118
                            "oracle_network_work_matches_baseline": network_ok,
2119
                            "oracle_head_matches": expected_head == local_head == advertised_head,
2120
                            "oracle_manifest_matches": evidence["manifest"]
2121
                            == baseline_receipt_contract["manifest"],
2122
                            "oracle_receipt_contract_matches": receipt_contract
2123
                            == baseline_receipt_contract,
2124
                            "oracle_history_matches": history == expected_history,
2125
                            "oracle_worktree_bytes_and_modes_match": worktree == expected_worktree,
2126
                            "oracle_clean_status": status_ok,
2127
                            "oracle_local_artifact_names_match": artifacts_ok,
2128
                            "oracle_stat_cache_contract_matches": cache_contract
2129
                            == baseline_cache_contract,
2130
                            "stat_cache_metadata_valid_before_status": (
2131
                                cache_before_status.get("all_metadata_valid")
2132
                                if isinstance(cache_before_status, dict)
2133
                                else None
2134
                            ),
2135
                            "stat_cache_rows_before_status": (
2136
                                len(cache_before_status.get("rows", []))
2137
                                if isinstance(cache_before_status, dict)
2138
                                and isinstance(cache_before_status.get("rows"), list)
2139
                                else None
2140
                            ),
2141
                            "stat_cache_expected_valid_before_status": expected_cache_valid,
2142
                            "oracle_clone_residue_absent": artifacts_ok,
2143
                            "oracle_passed": oracle_ok,
2144
                            "repository_cache_state": "fresh_destination_no_shared_object_store",
2145
                        }
2146
                    )
2147
                    for followup_row in (status_row, commit_row):
2148
                        followup_row.update(
2149
                            {
2150
                                "materialization_timing_controls_met": timing_controls_met,
2151
                                "stat_cache_expected_valid_before_first_status": expected_cache_valid,
2152
                                "oracle_passed": oracle_ok,
2153
                            }
2154
                        )
2155
                    if not oracle_ok:
2156
                        row["returncode"] = 1
2157
                        row["stderr"] = "legacy_selected_materialization_oracle_failed"
2158
                        status_row["returncode"] = 1
2159
                        commit_row["returncode"] = 1
2160
                    total = phase_total_row(
2161
                        [row, status_row, commit_row],
2162
                        subject,
2163
                        scenario,
2164
                        run_index,
2165
                        trial_meta,
2166
                        "legacy_selected_clone_materialization.total",
2167
                    )
2168
                    total.update(
2169
                        {
2170
                            key: row[key]
2171
                            for key in (
2172
                                "materialization_passes_observed",
2173
                                "network_connections_observed",
2174
                                "network_client_to_server_bytes_observed",
2175
                                "network_server_to_client_bytes_observed",
2176
                                "oracle_passed",
2177
                                "oracle_head_matches",
2178
                                "oracle_manifest_matches",
2179
                                "oracle_receipt_contract_matches",
2180
                                "oracle_history_matches",
2181
                                "oracle_worktree_bytes_and_modes_match",
2182
                                "oracle_clean_status",
2183
                                "oracle_local_artifact_names_match",
2184
                                "oracle_stat_cache_contract_matches",
2185
                                "stat_cache_metadata_valid_before_status",
2186
                                "stat_cache_rows_before_status",
2187
                                "oracle_clone_residue_absent",
2188
                                "oracle_network_work_matches_baseline",
2189
                                "serve_binary_sha256",
2190
                                "serve_source_head",
2191
                                "fixture_id",
2192
                                "os_cache_state",
2193
                                "materialization_timing_controls_met",
2194
                            )
2195
                        }
2196
                    )
2197
                    total.update(
2198
                        {
2199
                            "clone_elapsed_ms": row.get("elapsed_ms"),
2200
                            "first_status_elapsed_ms": status_row.get("elapsed_ms"),
2201
                            "first_noop_commit_elapsed_ms": commit_row.get("elapsed_ms"),
2202
                        }
2203
                    )
2204
                    rows.extend([row, status_row, commit_row, total])
2205
                    shutil.rmtree(destination, ignore_errors=True)
2206
    finally:
2207
        if proxy is not None:
2208
            proxy.stop()
2209
        if server is not None:
2210
            stop_owned_process(server)
2211
        shutil.rmtree(fixture_root, ignore_errors=True)
2212
2213
    if failure is not None:
2214
        rows.append(
2215
            harness_step_row(
2216
                setup_subject,
2217
                scenario,
2218
                "scenario.fixture_failure",
2219
                0,
2220
                fixture_meta,
2221
                0.0,
2222
                returncode=1,
2223
            )
2224
        )
2225
        rows[-1]["stderr"] = failure
2226
    return rows
2227
2228
2229
def parse_shallow_refresh_fixtures(text: str) -> list[tuple[int, int]]:
2230
    """Parse ``COMMITS:SEED[,COMMITS:SEED...]``; the first is primary, the rest held out."""
2231
    specs: list[tuple[int, int]] = []
2232
    for item in text.split(","):
2233
        item = item.strip()
2234
        if not item:
2235
            continue
2236
        commits, sep, seed = item.partition(":")
2237
        if not sep or not commits.isdigit() or not seed.isdigit() or int(commits) < 1:
2238
            raise ValueError(f"invalid fixture spec {item!r}; expected COMMITS:SEED")
2239
        specs.append((int(commits), int(seed)))
2240
    if not specs:
2241
        raise ValueError("at least one COMMITS:SEED fixture is required")
2242
    return specs
2243
2244
2245
def _summed_or_none(rows: list[dict[str, Any]], key: str) -> int | None:
2246
    values = [row.get(key) for row in rows]
2247
    if any(value is None for value in values):
2248
        return None
2249
    return sum(int(value) for value in values)
2250
2251
2252
def run_shallow_refresh_campaign(
2253
    subjects: list[Subject],
2254
    server_bin: Path | None,
2255
    server_source_head: str | None,
2256
    fixture_specs: list[tuple[int, int]],
2257
    run_root: Path,
2258
    runs: int,
2259
    metadata: dict[str, Any],
2260
    admitted: int,
2261
    randomize_order: bool,
2262
    random_seed: int,
2263
    injected_request_delay_ms: float,
2264
) -> list[dict[str, Any]]:
2265
    """Fresh shallow worker -> named task branch at main -> first parent refresh.
2266
2267
    One deterministic merge-history fixture per spec is imported into an owned
2268
    loopback ``oak serve`` (fixed ``--oak-serve-bin``) and a bare Git repo served
2269
    by ``GitHTTPServer``, both behind request-framing proxies, during untimed
2270
    setup. Each subject then runs its cells in (optionally shuffled) order.
2271
    """
2272
    scenario = SHALLOW_REFRESH_SCENARIO
2273
    rows: list[dict[str, Any]] = []
2274
    if not subjects:
2275
        return rows
2276
2277
    def skip_all(reason: str) -> list[dict[str, Any]]:
2278
        return [
2279
            unmeasured_step_row(subject, scenario, "scenario.skip", 0, metadata, reason)
2280
            for subject in subjects
2281
        ]
2282
2283
    if (
2284
        server_bin is None
2285
        or not server_bin.is_absolute()
2286
        or not server_bin.is_file()
2287
        or not os.access(server_bin, os.X_OK)
2288
    ):
2289
        return skip_all("--oak-serve-bin must name an absolute executable used only as the fixed Serve fixture")
2290
    git_bin = shutil.which("git")
2291
    if git_bin is None:
2292
        return skip_all("git is required to create the deterministic merge-history fixture")
2293
    server_bin = server_bin.resolve()
2294
    serve_meta = {
2295
        "benchmark_track": "loopback-shallow-refresh",
2296
        "remote_transport": "loopback_http",
2297
        "remote_server": "127.0.0.1",
2298
        "serve_binary": str(server_bin),
2299
        "serve_binary_sha256": binary_sha256(server_bin),
2300
        "serve_version": subject_version(Subject("serve_fixture", "oak", "Serve fixture", server_bin)),
2301
        "serve_source_head": server_source_head,
2302
        "injected_request_delay_ms": injected_request_delay_ms,
2303
        "injected_request_delay_kind": "fixed_per_request_sleep_in_proxy_not_netem",
2304
        "hosted_equivalent": False,
2305
        "hosted_equivalence_reason": (
2306
            "loopback oak serve has no bounded integrity preflight (clone needs --allow-legacy-scope) "
2307
            "and is not the hosted server; see serve_depth_behavior for whether --shallow was honored"
2308
        ),
2309
        "os_cache_state": "warm_or_unknown",
2310
        "repository_cache_state": "fresh_destination_per_trial",
2311
    }
2312
    for commits, seed in fixture_specs:
2313
        rows.extend(
2314
            _run_shallow_refresh_fixture(
2315
                subjects, server_bin, git_bin, commits, seed, run_root, runs,
2316
                {**metadata, **serve_meta}, admitted, randomize_order, random_seed,
2317
                injected_request_delay_ms,
2318
            )
2319
        )
2320
    return rows
2321
2322
2323
def _run_shallow_refresh_fixture(
2324
    subjects: list[Subject],
2325
    server_bin: Path,
2326
    git_bin: str,
2327
    commits: int,
2328
    seed: int,
2329
    run_root: Path,
2330
    runs: int,
2331
    metadata: dict[str, Any],
2332
    admitted: int,
2333
    randomize_order: bool,
2334
    random_seed: int,
2335
    injected_request_delay_ms: float,
2336
) -> list[dict[str, Any]]:
2337
    scenario = SHALLOW_REFRESH_SCENARIO
2338
    fixture_root = (run_root / f"shallow-refresh-c{commits}-s{seed}").resolve()
2339
    served_git = fixture_root / "git-served"
2340
    for path in (fixture_root, served_git):
2341
        path.mkdir(parents=True, exist_ok=True)
2342
    fixture_env = isolated_oak_fixture_env(fixture_root / "config")
2343
    rows: list[dict[str, Any]] = []
2344
    setup_subject = subjects[0]
2345
    fixture_meta: dict[str, Any] = {**metadata, "fixture_commits": commits, "fixture_seed": str(seed)}
2346
    repo_spec = "qa/shallow-refresh"
2347
    task_branch = SHALLOW_REFRESH_TASK_BRANCH
2348
    server: subprocess.Popen[Any] | None = None
2349
    oak_proxy: RequestCountingProxy | None = None
2350
    git_proxy: RequestCountingProxy | None = None
2351
    git_server: GitHTTPServer | None = None
2352
    failure: str | None = None
2353
2354
    def setup_command(operation: str, command: list[str], cwd: Path) -> bool:
2355
        started = time.perf_counter()
2356
        result = run_bounded_harness_command(command, cwd, env=fixture_env)
2357
        rows.append(
2358
            harness_step_row(
2359
                setup_subject, scenario, operation, 0, fixture_meta,
2360
                (time.perf_counter() - started) * 1000,
2361
                returncode=0 if result.returncode == 0 else result.returncode,
2362
                harness_command_count=1,
2363
            )
2364
        )
2365
        if result.returncode != 0:
2366
            rows[-1]["stderr"] = result.stderr[-4000:]
2367
        return result.returncode == 0
2368
2369
    try:
2370
        manifest = merge_history_fixture.generate(
2371
            fixture_root / "src.git", commits, seed, git_bin=git_bin, env=fixture_env
2372
        )
2373
        fixture_meta.update(manifest.as_dict())
2374
        expected_tree = manifest.tree_content_hash
2375
        bare = served_git / "fixture.git"
2376
        if not setup_command(
2377
            "setup.git_bare", [git_bin, "clone", "-q", "--bare", str(fixture_root / "src.git"), str(bare)],
2378
            fixture_root,
2379
        ):
2380
            failure = "setup.git_bare_failed"
2381
        port = reserve_loopback_port()
2382
        direct_remote = f"http://127.0.0.1:{port}"
2383
        if failure is None:
2384
            server = subprocess.Popen(
2385
                [str(server_bin), "serve", "--dir", str(fixture_root / "oak-served"),
2386
                 "--host", "127.0.0.1", "--port", str(port)],
2387
                cwd=fixture_root, env=fixture_env,
2388
                stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
2389
                start_new_session=os.name == "posix",
2390
            )
2391
            if not wait_for_loopback_port(server, port):
2392
                failure = "setup.server_not_ready"
2393
        seed_dir = fixture_root / "oak-seed"
2394
        if failure is None:
2395
            for operation, command, cwd in (
2396
                ("setup.oak_import", [str(server_bin), "clone", str(fixture_root / "src.git"), str(seed_dir)], fixture_root),
2397
                ("setup.oak_push", [str(server_bin), "push", "--repo", repo_spec, "--remote", direct_remote], seed_dir),
2398
            ):
2399
                if not setup_command(operation, command, cwd):
2400
                    failure = f"{operation}_failed"
2401
                    break
2402
        oak_main_head = remote_branch_head(direct_remote, repo_spec, "main") if failure is None else None
2403
        git_main = run_bounded_harness_command([git_bin, "rev-parse", "refs/heads/main"], bare, env=fixture_env)
2404
        git_main_head = git_main.stdout.strip() if git_main.returncode == 0 else None
2405
        import_tree = shallow_refresh.worktree_tree_hash(seed_dir) if failure is None else None
2406
        fixture_meta.update(
2407
            {
2408
                "server_oak_main_head": oak_main_head,
2409
                "server_git_main_head": git_main_head,
2410
                "oracle_import_tree_matches_fixture": import_tree == expected_tree,
2411
            }
2412
        )
2413
        if failure is None and not oak_main_head:
2414
            failure = "setup.oak_server_main_head_unobserved"
2415
        if failure is None and git_main_head != manifest.head:
2416
            failure = "setup.git_server_head_mismatch"
2417
        if failure is None and import_tree != expected_tree:
2418
            failure = "setup.oak_import_tree_mismatch"
2419
        shutil.rmtree(seed_dir, ignore_errors=True)
2420
2421
        if failure is None:
2422
            git_server = GitHTTPServer(served_git, git_bin=git_bin).start()
2423
            oak_proxy = RequestCountingProxy(
2424
                "127.0.0.1", port, path_suffixes=(COMMITS_INFO_SUFFIX,),
2425
                injected_request_delay_ms=injected_request_delay_ms,
2426
            ).start()
2427
            git_proxy = RequestCountingProxy(
2428
                "127.0.0.1", git_server.server_port, path_suffixes=(COMMITS_INFO_SUFFIX,),
2429
                injected_request_delay_ms=injected_request_delay_ms,
2430
            ).start()
2431
            oak_remote = f"http://127.0.0.1:{oak_proxy.address[1]}"
2432
            git_url = f"http://127.0.0.1:{git_proxy.address[1]}/fixture.git"
2433
            order_rng = random.Random(f"{random_seed}:{commits}:{seed}")
2434
            for run_index in range(runs):
2435
                run_subjects = list(subjects)
2436
                if randomize_order:
2437
                    order_rng.shuffle(run_subjects)
2438
                trial_meta = {**fixture_meta, "run_subject_order": [s.name for s in run_subjects]}
2439
                for subject in run_subjects:
2440
                    print(
2441
                        f"[run] {scenario} {manifest.fixture_id} run={run_index} subject={subject.name}",
2442
                        flush=True,
2443
                    )
2444
                    tag = f"{subject.name}-c{commits}-s{seed}-r{run_index}"
2445
                    subject_env = isolated_oak_fixture_env(run_root / f"sr-config-{tag}")
2446
                    if subject.kind == "oak":
2447
                        rows.extend(
2448
                            _shallow_refresh_oak_trial(
2449
                                subject, oak_proxy, oak_remote, repo_spec, oak_main_head,
2450
                                expected_tree, run_root / f"sr-oak-{tag}", run_index, trial_meta,
2451
                                admitted, subject_env,
2452
                            )
2453
                        )
2454
                    elif subject.kind == "git":
2455
                        rows.extend(
2456
                            _shallow_refresh_git_trials(
2457
                                subject, git_proxy, git_url, git_main_head, expected_tree,
2458
                                run_root, tag, run_index, trial_meta, admitted, subject_env,
2459
                            )
2460
                        )
2461
                    else:
2462
                        rows.append(
2463
                            unmeasured_step_row(
2464
                                subject, scenario, "scenario.skip", run_index, trial_meta,
2465
                                f"subject kind {subject.kind!r} has no shallow-refresh cell",
2466
                            )
2467
                        )
2468
    except (OSError, RuntimeError, ValueError) as error:
2469
        failure = failure or f"setup_exception:{type(error).__name__}:{error}"
2470
    finally:
2471
        for proxy in (oak_proxy, git_proxy):
2472
            if proxy is not None:
2473
                proxy.stop()
2474
        if git_server is not None:
2475
            git_server.stop()
2476
        if server is not None:
2477
            stop_owned_process(server)
2478
        shutil.rmtree(fixture_root, ignore_errors=True)
2479
2480
    if failure is not None:
2481
        rows.append(
2482
            harness_step_row(setup_subject, scenario, "scenario.fixture_failure", 0, fixture_meta, 0.0, returncode=1)
2483
        )
2484
        rows[-1]["stderr"] = failure
2485
    return rows
2486
2487
2488
def _shallow_refresh_step(
2489
    subject: Subject,
2490
    proxy: RequestCountingProxy,
2491
    operation: str,
2492
    command: list[str],
2493
    cwd: Path,
2494
    run_index: int,
2495
    meta: dict[str, Any],
2496
    admitted: int,
2497
    env: dict[str, str],
2498
) -> dict[str, Any]:
2499
    proxy.reset_counts()
2500
    row = step_row(
2501
        subject, SHALLOW_REFRESH_SCENARIO, operation, command, cwd, run_index, meta, admitted,
2502
        env=env, timeout_seconds=SHALLOW_REFRESH_STEP_TIMEOUT_S,
2503
    )
2504
    row.update(
2505
        shallow_refresh.request_metric_fields(proxy.request_stats(), proxy.snapshot(), COMMITS_INFO_SUFFIX)
2506
    )
2507
    return row
2508
2509
2510
def _shallow_refresh_total(
2511
    steps: list[dict[str, Any]],
2512
    subject: Subject,
2513
    run_index: int,
2514
    meta: dict[str, Any],
2515
    cell: str,
2516
    oracle: dict[str, Any],
2517
    extra: dict[str, Any],
2518
) -> dict[str, Any]:
2519
    total = phase_total_row(steps, subject, SHALLOW_REFRESH_SCENARIO, run_index, meta, f"{cell}.total")
2520
    total.update(oracle)
2521
    total.update(extra)
2522
    for key in (
2523
        "http_request_count",
2524
        "http_commits_info_request_count",
2525
        "network_wire_bytes_observed",
2526
        "network_connections_observed",
2527
    ):
2528
        total[key] = _summed_or_none(steps, key)
2529
    total["step_elapsed_ms"] = {row["operation"]: row.get("elapsed_ms") for row in steps}
2530
    if not oracle.get("oracle_passed"):
2531
        total["returncode"] = 1
2532
        total["stderr"] = total.get("stderr") or f"{cell}_final_state_oracle_failed"
2533
    for row in steps:
2534
        row["oracle_passed"] = oracle.get("oracle_passed")
2535
    return total
2536
2537
2538
def _shallow_refresh_oak_trial(
2539
    subject: Subject,
2540
    proxy: RequestCountingProxy,
2541
    remote: str,
2542
    repo_spec: str,
2543
    expected_head: str | None,
2544
    expected_tree: str,
2545
    worker: Path,
2546
    run_index: int,
2547
    meta: dict[str, Any],
2548
    admitted: int,
2549
    env: dict[str, str],
2550
) -> list[dict[str, Any]]:
2551
    vcs = str(subject.bin)
2552
    cell_meta = {
2553
        **meta,
2554
        "cell": "shallow_clone",
2555
        "starting_state": "no_local_repository_fresh_shallow_clone",
2556
    }
2557
    steps: list[dict[str, Any]] = []
2558
    extra: dict[str, Any] = {}
2559
    try:
2560
        acquire = _shallow_refresh_step(
2561
            subject, proxy, "shallow.acquire",
2562
            [vcs, "clone", "--shallow", "--allow-legacy-scope", "--remote", remote, repo_spec, str(worker)],
2563
            worker.parent, run_index, cell_meta, admitted, env,
2564
        )
2565
        steps.append(acquire)
2566
        after_clone = shallow_refresh.oak_disk_state(worker, SHALLOW_REFRESH_TASK_BRANCH)
2567
        depth_behavior = shallow_refresh.serve_depth_behavior(after_clone["commit_rows"])
2568
        extra.update(
2569
            {
2570
                "oak_commit_rows_after_clone": after_clone["commit_rows"],
2571
                "serve_depth_behavior": depth_behavior,
2572
            }
2573
        )
2574
        if row_returncode(acquire) == 0:
2575
            for operation, command in (
2576
                ("shallow.branch_create", [vcs, "switch", "-c", SHALLOW_REFRESH_TASK_BRANCH]),
2577
                ("shallow.parent_refresh", [vcs, "fetch"]),
2578
            ):
2579
                row = _shallow_refresh_step(
2580
                    subject, proxy, operation, command, worker, run_index, cell_meta, admitted, env
2581
                )
2582
                steps.append(row)
2583
                if row_returncode(row) != 0:
2584
                    break
2585
        disk = shallow_refresh.oak_disk_state(worker, SHALLOW_REFRESH_TASK_BRANCH)
2586
        oracle = shallow_refresh.evaluate_oracle(
2587
            expected_branch=SHALLOW_REFRESH_TASK_BRANCH,
2588
            expected_head=expected_head,
2589
            expected_tree_hash=expected_tree,
2590
            observed_branch=disk["current_branch"],
2591
            observed_branch_head=disk["branch_head"],
2592
            observed_head=disk["head"],
2593
            worktree_tree_hash=shallow_refresh.worktree_tree_hash(worker),
2594
        )
2595
        extra.update(
2596
            {
2597
                "oracle_source": "oak_db_query_only_plus_server_branch_api_plus_fixture_model",
2598
                "oak_commit_rows_after_refresh": disk["commit_rows"],
2599
                "oak_ancestry_missing_edges_after_refresh": disk["ancestry_missing_edges"],
2600
                "oak_disk_state_error": disk["error"],
2601
            }
2602
        )
2603
        for row in steps:
2604
            row["serve_depth_behavior"] = depth_behavior
2605
        total = _shallow_refresh_total(steps, subject, run_index, cell_meta, "shallow", oracle, extra)
2606
        rows = [*steps, total]
2607
        if oracle["oracle_passed"]:
2608
            # Separate measurement: a second branch creation in the now-refreshed
2609
            # worker. Isolates local switch cost from the first-refresh network work.
2610
            repeat = _shallow_refresh_step(
2611
                subject, proxy, "shallow.branch_create_repeat_local",
2612
                [vcs, "switch", "-c", f"{SHALLOW_REFRESH_TASK_BRANCH}-repeat"],
2613
                worker, run_index,
2614
                {**cell_meta, "starting_state": "refreshed_worker_after_first_parent_refresh"},
2615
                admitted, env,
2616
            )
2617
            repeat_disk = shallow_refresh.oak_disk_state(worker, f"{SHALLOW_REFRESH_TASK_BRANCH}-repeat")
2618
            repeat_ok = bool(
2619
                row_returncode(repeat) == 0
2620
                and repeat_disk["current_branch"] == f"{SHALLOW_REFRESH_TASK_BRANCH}-repeat"
2621
                and repeat_disk["branch_head"] == expected_head
2622
            )
2623
            repeat.update({"serve_depth_behavior": depth_behavior, "oracle_passed": repeat_ok})
2624
            if not repeat_ok:
2625
                repeat["returncode"] = 1
2626
                repeat["stderr"] = repeat.get("stderr") or "branch_create_repeat_oracle_failed"
2627
            rows.append(repeat)
2628
        return rows
2629
    finally:
2630
        shutil.rmtree(worker, ignore_errors=True)
2631
2632
2633
def _shallow_refresh_git_trials(
2634
    subject: Subject,
2635
    proxy: RequestCountingProxy,
2636
    url: str,
2637
    expected_head: str | None,
2638
    expected_tree: str,
2639
    run_root: Path,
2640
    tag: str,
2641
    run_index: int,
2642
    meta: dict[str, Any],
2643
    admitted: int,
2644
    env: dict[str, str],
2645
) -> list[dict[str, Any]]:
2646
    git = str(subject.bin)
2647
    rows: list[dict[str, Any]] = []
2648
2649
    def finish(cell: str, cell_meta: dict[str, Any], steps: list[dict[str, Any]], worktree: Path) -> None:
2650
        disk = shallow_refresh.git_disk_state(worktree, git, env)
2651
        oracle = shallow_refresh.evaluate_oracle(
2652
            expected_branch=SHALLOW_REFRESH_TASK_BRANCH,
2653
            expected_head=expected_head,
2654
            expected_tree_hash=expected_tree,
2655
            observed_branch=disk["current_branch"],
2656
            observed_branch_head=disk["head"] if disk["current_branch"] == SHALLOW_REFRESH_TASK_BRANCH else None,
2657
            observed_head=disk["head"],
2658
            worktree_tree_hash=shallow_refresh.worktree_tree_hash(worktree),
2659
        )
2660
        extra = {
2661
            "oracle_source": "git_rev_parse_plus_server_bare_repo_plus_fixture_model",
2662
            "git_commit_rows_after_refresh": disk["commit_rows"],
2663
            "git_is_shallow_after_refresh": disk["shallow"],
2664
            "serve_depth_behavior": None,
2665
        }
2666
        rows.extend([*steps, _shallow_refresh_total(steps, subject, run_index, cell_meta, cell, oracle, extra)])
2667
2668
    shallow = run_root / f"sr-git-shallow-{tag}"
2669
    shallow_meta = {**meta, "cell": "shallow_clone", "starting_state": "no_local_repository_fresh_depth1_clone"}
2670
    try:
2671
        steps = [
2672
            _shallow_refresh_step(
2673
                subject, proxy, "shallow.acquire", [git, "clone", "-q", "--depth", "1", url, str(shallow)],
2674
                run_root, run_index, shallow_meta, admitted, env,
2675
            )
2676
        ]
2677
        if row_returncode(steps[0]) == 0:
2678
            for operation, command in (
2679
                ("shallow.branch_create", [git, "switch", "-q", "-c", SHALLOW_REFRESH_TASK_BRANCH]),
2680
                ("shallow.parent_refresh", [git, "fetch", "-q"]),
2681
            ):
2682
                steps.append(
2683
                    _shallow_refresh_step(subject, proxy, operation, command, shallow, run_index, shallow_meta, admitted, env)
2684
                )
2685
                if row_returncode(steps[-1]) != 0:
2686
                    break
2687
        finish("shallow", shallow_meta, steps, shallow)
2688
    finally:
2689
        shutil.rmtree(shallow, ignore_errors=True)
2690
2691
    full = run_root / f"sr-git-full-{tag}"
2692
    worktree = run_root / f"sr-git-worktree-{tag}"
2693
    warm_meta = {
2694
        **meta,
2695
        "cell": "warm_full_clone_worktree",
2696
        "starting_state": "warm_full_clone_created_untimed_not_equivalent_to_fresh_shallow_worker",
2697
    }
2698
    try:
2699
        setup_started = time.perf_counter()
2700
        clone = run_bounded_harness_command([git, "clone", "-q", url, str(full)], run_root, env=env)
2701
        setup_row = harness_step_row(
2702
            subject, SHALLOW_REFRESH_SCENARIO, "warm_worktree.setup_full_clone", run_index, warm_meta,
2703
            (time.perf_counter() - setup_started) * 1000,
2704
            returncode=0 if clone.returncode == 0 else clone.returncode, harness_command_count=1,
2705
        )
2706
        rows.append(setup_row)
2707
        steps = []
2708
        if clone.returncode == 0:
2709
            for operation, command, cwd in (
2710
                ("warm_worktree.branch_create",
2711
                 [git, "worktree", "add", "-q", "-b", SHALLOW_REFRESH_TASK_BRANCH, str(worktree), "origin/main"], full),
2712
                ("warm_worktree.parent_refresh", [git, "fetch", "-q"], worktree),
2713
            ):
2714
                steps.append(
2715
                    _shallow_refresh_step(subject, proxy, operation, command, cwd, run_index, warm_meta, admitted, env)
2716
                )
2717
                if row_returncode(steps[-1]) != 0:
2718
                    break
2719
            finish("warm_worktree", warm_meta, steps, worktree)
2720
    finally:
2721
        shutil.rmtree(worktree, ignore_errors=True)
2722
        shutil.rmtree(full, ignore_errors=True)
2723
    return rows
2724
2725
2726
def run_git_loop(
2727
    subject: Subject,
2728
    remote: oakbench_remotes.RemoteResolution,
2729
    run_root: Path,
2730
    run_index: int,
2731
    metadata: dict[str, Any],
2732
    admitted: int,
2733
) -> list[dict[str, Any]]:
2734
    scenario = "task_loop"
2735
    meta = {
2736
        **metadata,
2737
        **remote.row_fields(),
2738
        "benchmark_track": (
2739
            "core-equivalent"
2740
            if remote.transport == oakbench_remotes.TRANSPORT_LOCAL_FILE
2741
            else "agent-default"
2742
        ),
2743
    }
2744
    vcs = str(subject.bin)
2745
    rows: list[dict[str, Any]] = []
2746
2747
    seed_error = ensure_git_remote_seeded(subject, remote, run_root)
2748
    if seed_error:
2749
        return [skip_row(subject, scenario, run_index, meta, seed_error)]
2750
2751
    # Untimed: the base clone is the long-lived checkout worktrees hang off;
2752
    # an agent fleet pays it once, not per task.
2753
    base = run_root / f"tl-git-base-r{run_index}"
2754
    clone = run_untimed([vcs, "clone", "-q", str(remote.repo), str(base)], run_root)
2755
    if clone.returncode != 0:
2756
        return [skip_row(subject, scenario, run_index, meta, f"base clone failed: {clone.stderr.strip()[:200]}")]
2757
2758
    branches: dict[str, str] = {}
2759
    payloads: dict[str, str] = {}
2760
    for phase in PHASES:
2761
        workspace = run_root / f"tl-{subject.name}-r{run_index}-{phase}"
2762
        branch = oakbench_remotes.disposable_branch(
2763
            str(metadata["bench_id"]), f"taskloop-{phase}", subject.name, run_index
2764
        )
2765
        branches[phase] = branch
2766
        payload = f"payload {phase} run={run_index} bench={metadata['bench_id']}"
2767
        payloads[phase] = payload
2768
        acquire = step_row(
2769
            subject, scenario, f"{phase}.acquire",
2770
            [vcs, "worktree", "add", str(workspace), "-b", branch], base, run_index, meta, admitted,
2771
        )
2772
        rows.append(acquire)
2773
        if int(acquire["returncode"]) != 0:
2774
            return rows
2775
        rows.append(edit_step(subject, scenario, f"{phase}.edit", workspace, payload, run_index, meta, admitted))
2776
        # Agent-default snapshot: git pays stage+commit as two calls.
2777
        rows.append(step_row(subject, scenario, f"{phase}.snapshot.add", [vcs, "add", "."], workspace, run_index, meta, admitted))
2778
        rows.append(
2779
            step_row(
2780
                subject, scenario, f"{phase}.snapshot.commit",
2781
                [vcs, "commit", "-q", "-m", f"bench task loop {metadata['bench_id']} r{run_index} {phase}"],
2782
                workspace, run_index, meta, admitted,
2783
            )
2784
        )
2785
        rows.append(
2786
            step_row(
2787
                subject, scenario, f"{phase}.publish",
2788
                [vcs, "push", "-q", "-u", "origin", branch], workspace, run_index, meta, admitted,
2789
            )
2790
        )
2791
        # No task.describe: git's description is the commit message already
2792
        # paid in {phase}.snapshot.commit (asymmetry documented in the lane doc).
2793
        rows.append(
2794
            step_row(
2795
                subject, scenario, f"{phase}.end",
2796
                [vcs, "worktree", "remove", str(workspace)], base, run_index, meta, admitted,
2797
            )
2798
        )
2799
2800
    # Untimed oracle: payloads visible on the remote branches.
2801
    oracle_ok: bool | None = True
2802
    for phase in PHASES:
2803
        fetch = run_untimed([vcs, "fetch", "-q", "origin", branches[phase]], base)
2804
        show = run_untimed([vcs, "show", f"FETCH_HEAD:task-notes.md"], base)
2805
        if fetch.returncode != 0 or payloads[phase] not in show.stdout:
2806
            oracle_ok = False
2807
    rows.append(
2808
        {
2809
            **phase_total_row(rows, subject, scenario, run_index, meta, "task_loop.total"),
2810
            "oracle_payload_on_remote_branch": oracle_ok,
2811
            "workspace_branches": branches,
2812
            "measurement_source": "fetch_plus_show_grep",
2813
        }
2814
    )
2815
    return rows
2816
2817
2818
def loop_summary_fields(rows: list[dict[str, Any]]) -> dict[str, Any]:
2819
    """followup-vs-task warm deltas computed from this run's step rows."""
2820
    def op_ms(operation: str) -> float | None:
2821
        for row in rows:
2822
            if row.get("operation") == operation:
2823
                return float(row.get("elapsed_ms", 0.0))
2824
        return None
2825
2826
    def phase_ms(prefix: str) -> float:
2827
        return sum(
2828
            float(row.get("elapsed_ms", 0.0))
2829
            for row in rows
2830
            if str(row.get("operation", "")).startswith(prefix + ".")
2831
        )
2832
2833
    task_acquire = op_ms("task.acquire")
2834
    followup_acquire = op_ms("followup.acquire")
2835
    fields: dict[str, Any] = {
2836
        "task_phase_ms": round(phase_ms("task"), 3),
2837
        "followup_phase_ms": round(phase_ms("followup"), 3),
2838
    }
2839
    if task_acquire is not None and followup_acquire is not None:
2840
        fields["followup_warm_delta_ms"] = round(followup_acquire - task_acquire, 3)
2841
        fields["acquire_ms_task"] = round(task_acquire, 3)
2842
        fields["acquire_ms_followup"] = round(followup_acquire, 3)
2843
    else:
2844
        fields["followup_warm_delta_ms"] = None
2845
    return fields
2846
2847
2848
def summary_text(rows: list[dict[str, Any]]) -> str:
2849
    totals = [row for row in rows if row.get("operation") == "task_loop.total"]
2850
    lines = [
2851
        "# Task Loop Summary",
2852
        "",
2853
        "Whole-task cost of the signature motion: acquire workspace -> edit -> snapshot ->",
2854
        "publish -> describe -> end, then a follow-up task in a fresh workspace.",
2855
        "Command sequences are NOT step-equivalent across subjects",
2856
        "(oak pays desc; git's description is its commit message). Latency across unlike",
2857
        "transports is never comparable; scripted call equivalents and estimated token proxies are.",
2858
        "",
2859
        "| Subject | Run | Transport | Wall ms | Scripted call equivalents | Cost-wt tokens (env) | Warm acquire delta ms | Oracle |",
2860
        "| --- | ---: | --- | ---: | ---: | ---: | ---: | --- |",
2861
    ]
2862
    for row in totals:
2863
        oracle = row.get("oracle_payload_on_remote_branch")
2864
        lines.append(
2865
            "| `{}` | {} | {} | {} | {} | {} | {} | {} |".format(
2866
                row["subject"],
2867
                row["run"],
2868
                row.get("remote_transport", "unmeasured"),
2869
                fmt_ms(row.get("elapsed_ms")),
2870
                row.get("tool_call_count", "unmeasured"),
2871
                row.get("estimated_cost_weighted_tokens_with_envelope", "unmeasured"),
2872
                row.get("followup_warm_delta_ms", "unmeasured"),
2873
                "pass" if oracle else ("FAIL" if oracle is False else "unmeasured"),
2874
            )
2875
        )
2876
    skips = [
2877
        row for row in rows
2878
        if row.get("skipped") and row.get("scenario") == TASK_LOOP_SCENARIO
2879
    ]
2880
    for row in skips:
2881
        lines.append(f"| `{row['subject']}` | {row['run']} | skipped | | | | | {row['skip_reason'][:70]} |")
2882
2883
    agent_totals = [
2884
        row
2885
        for row in rows
2886
        if row.get("operation") in ("agent_task.total", "agent_task_oak_local.total")
2887
    ]
2888
    if agent_totals:
2889
        lines.extend(
2890
            [
2891
                "",
2892
                "## Cold clone and warm isolated task",
2893
                "",
2894
                "Local-file Git rows compare a fresh full clone with a worktree sharing",
2895
                "the first clone's object store. Oak local rows use two independent fresh",
2896
                "clones against an owned loopback server; they do not claim a shared cache.",
2897
                "OS cache and network bytes remain unknown. Hosted CI/review are skips.",
2898
                "",
2899
                "| Subject | Run | Wall ms | Scripted call equivalents | Output bytes | Coverage | Oracle |",
2900
                "| --- | ---: | ---: | ---: | ---: | --- | --- |",
2901
            ]
2902
        )
2903
        for row in agent_totals:
2904
            lines.append(
2905
                "| `{}` | {} | {} | {} | {} | {} | {} |".format(
2906
                    row["subject"],
2907
                    row["run"],
2908
                    fmt_ms(row.get("elapsed_ms")),
2909
                    row.get("tool_call_count", "unmeasured"),
2910
                    row.get("raw_output_bytes", "unmeasured"),
2911
                    "complete" if row.get("coverage_complete") else "partial",
2912
                    "pass" if row.get("oracle_payload_on_remote_branch") else "FAIL",
2913
                )
2914
            )
2915
2916
    materialization_totals = [
2917
        row
2918
        for row in rows
2919
        if row.get("operation") == "legacy_selected_clone_materialization.total"
2920
    ]
2921
    if materialization_totals:
2922
        lines.extend(
2923
            [
2924
                "",
2925
                "## Legacy selected-branch materialization",
2926
                "",
2927
                "One fixed loopback Serve and one immutable fixture are shared across subjects.",
2928
                "Fresh destinations do not imply a cold OS page cache. Timing claims require",
2929
                "separate interleaved A/A noise and A/B campaigns.",
2930
                "",
2931
                "| Subject | Run | Clone ms | First status ms | First no-op commit ms | Passes | Harness controls met | Oracle |",
2932
                "| --- | ---: | ---: | ---: | ---: | ---: | --- | --- |",
2933
            ]
2934
        )
2935
        for row in materialization_totals:
2936
            lines.append(
2937
                "| `{}` | {} | {} | {} | {} | {} | {} | {} |".format(
2938
                    row["subject"],
2939
                    row["run"],
2940
                    fmt_ms(row.get("clone_elapsed_ms")),
2941
                    fmt_ms(row.get("first_status_elapsed_ms")),
2942
                    fmt_ms(row.get("first_noop_commit_elapsed_ms")),
2943
                    row.get("materialization_passes_observed", "unmeasured"),
2944
                    "yes" if row.get("materialization_timing_controls_met") else "no",
2945
                    "pass" if row.get("oracle_passed") else "FAIL",
2946
                )
2947
            )
2948
2949
    interrupted_totals = [
2950
        row for row in rows if row.get("operation") == "interrupted_publication.total"
2951
    ]
2952
    if interrupted_totals:
2953
        lines.extend(
2954
            [
2955
                "",
2956
                "## Interrupted publication",
2957
                "",
2958
                "A loopback fixture validates the request framing and drops the response.",
2959
                "Fixture delivery knowledge is reported separately from the CLI's own outcome",
2960
                "claim. Request receipt is not durability proof; the harness never retries.",
2961
                "",
2962
                "| Subject | Run | Wall ms | Scripted call equivalents | Output bytes | CLI outcome | Fixture delivery | POSTs | Request bytes | Head/work preserved |",
2963
                "| --- | ---: | ---: | ---: | ---: | --- | --- | ---: | ---: | --- |",
2964
            ]
2965
        )
2966
        for row in interrupted_totals:
2967
            lines.append(
2968
                "| `{}` | {} | {} | {} | {} | `{}` | `{}` | {} | {} | {}/{} |".format(
2969
                    row["subject"],
2970
                    row["run"],
2971
                    fmt_ms(row.get("elapsed_ms")),
2972
                    row.get("tool_call_count", "unmeasured"),
2973
                    row.get("raw_output_bytes", "unmeasured"),
2974
                    row.get("publication_outcome", "unmeasured"),
2975
                    row.get("fixture_delivery_state", "unmeasured"),
2976
                    row.get("mutation_requests_observed", "unmeasured"),
2977
                    row.get("network_request_bytes_observed", "unmeasured"),
2978
                    "yes" if row.get("local_head_preserved") else "no",
2979
                    "yes" if row.get("local_work_preserved") else "no",
2980
                )
2981
            )
2982
    return "\n".join(lines) + "\n"
2983
2984
2985
def fmt_ms(value: Any) -> str:
2986
    try:
2987
        return f"{float(value):.0f}"
2988
    except (TypeError, ValueError):
2989
        return "unmeasured"
2990
2991
2992
def main() -> int:
2993
    args = parse_args()
2994
    scenarios = [item.strip() for item in args.scenarios.split(",") if item.strip()]
2995
    known_scenarios = {
2996
        TASK_LOOP_SCENARIO,
2997
        AGENT_TASK_SCENARIO,
2998
        AGENT_TASK_OAK_LOCAL_SCENARIO,
2999
        LEGACY_SELECTED_MATERIALIZATION_SCENARIO,
3000
        SHALLOW_REFRESH_SCENARIO,
3001
        INTERRUPTED_PUBLICATION_SCENARIO,
3002
    }
3003
    unknown_scenarios = sorted(set(scenarios) - known_scenarios)
3004
    if unknown_scenarios:
3005
        raise SystemExit(
3006
            "Unknown scenarios: " + ", ".join(unknown_scenarios)
3007
            + "; valid: " + ", ".join(sorted(known_scenarios))
3008
        )
3009
    subjects = load_subjects(args)
3010
    timestamp = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
3011
    args.results.mkdir(parents=True, exist_ok=True)
3012
    args.workdir.mkdir(parents=True, exist_ok=True)
3013
    run_root = args.workdir / "runs" / timestamp
3014
    run_root.mkdir(parents=True, exist_ok=True)
3015
3016
    metadata = {
3017
        "bench_id": timestamp,
3018
        "profile": "task-loop",
3019
        "benchmark_track": "agent-default",
3020
        "timestamp_utc": timestamp,
3021
        "host": platform.node(),
3022
        "platform": platform.platform(),
3023
        "machine": platform.machine(),
3024
        "python": platform.python_version(),
3025
        "env_isolation_version": ENV_ISOLATION_VERSION,
3026
        "subject_versions": subject_versions(subjects),
3027
        "subject_details": subject_details(subjects),
3028
        "source": source_metadata(args.oak_repo),
3029
        "git_remote_mode": args.git_remote,
3030
        "trial_order_randomized": args.randomize_subject_order,
3031
        "trial_order_seed": args.random_seed if args.randomize_subject_order else None,
3032
    }
3033
3034
    rows: list[dict[str, Any]] = []
3035
    with measurement_lock("task_loop") as lock_info:
3036
        metadata["measurement_lock_wait_ms"] = lock_info.wait_ms
3037
        metadata["measurement_lock"] = "held" if lock_info.enabled else "disabled"
3038
        order_rng = random.Random(args.random_seed)
3039
        if LEGACY_SELECTED_MATERIALIZATION_SCENARIO in scenarios:
3040
            rows.extend(
3041
                run_legacy_selected_materialization_campaign(
3042
                    subjects,
3043
                    args.oak_serve_bin,
3044
                    args.oak_serve_source,
3045
                    args.oak_serve_source_head,
3046
                    args.materialization_baseline_subject,
3047
                    run_root,
3048
                    args.runs,
3049
                    metadata,
3050
                    args.admitted_output_chars,
3051
                    args.randomize_subject_order,
3052
                    args.random_seed,
3053
                    args.materialization_timing_controls_met,
3054
                )
3055
            )
3056
        if SHALLOW_REFRESH_SCENARIO in scenarios:
3057
            rows.extend(
3058
                run_shallow_refresh_campaign(
3059
                    subjects,
3060
                    args.oak_serve_bin,
3061
                    args.oak_serve_source_head,
3062
                    parse_shallow_refresh_fixtures(args.shallow_refresh_fixtures),
3063
                    run_root,
3064
                    args.runs,
3065
                    metadata,
3066
                    args.admitted_output_chars,
3067
                    args.randomize_subject_order,
3068
                    args.random_seed,
3069
                    args.injected_request_delay_ms,
3070
                )
3071
            )
3072
        campaign_scenarios = {LEGACY_SELECTED_MATERIALIZATION_SCENARIO, SHALLOW_REFRESH_SCENARIO}
3073
        ordinary_scenarios = [scenario for scenario in scenarios if scenario not in campaign_scenarios]
3074
        for run_index in range(args.runs):
3075
            run_subjects = list(subjects)
3076
            if args.randomize_subject_order:
3077
                order_rng.shuffle(run_subjects)
3078
            run_metadata = {
3079
                **metadata,
3080
                "run_subject_order": [subject.name for subject in run_subjects],
3081
            }
3082
            for subject in run_subjects:
3083
                for scenario in ordinary_scenarios:
3084
                    print(f"[run] {scenario} run={run_index} subject={subject.name}", flush=True)
3085
                    if scenario == INTERRUPTED_PUBLICATION_SCENARIO:
3086
                        rows.extend(
3087
                            run_interrupted_publication(
3088
                                subject, run_root, run_index, run_metadata, args.admitted_output_chars
3089
                            )
3090
                        )
3091
                        continue
3092
                    if scenario == AGENT_TASK_SCENARIO:
3093
                        if subject.kind != "git":
3094
                            rows.append(
3095
                                unmeasured_step_row(
3096
                                    subject,
3097
                                    scenario,
3098
                                    "agent_task.skipped",
3099
                                    run_index,
3100
                                    run_metadata,
3101
                                    "Oak end-to-end publication and exact CI require an explicitly configured disposable staging lane; production mutation is forbidden",
3102
                                )
3103
                            )
3104
                            continue
3105
                        remote_create_start = time.perf_counter()
3106
                        remote = oakbench_remotes.make_git_bare_remote(
3107
                            subject.bin, run_root, f"agent-task-remote-r{run_index}.git"
3108
                        )
3109
                        remote_create_ms = (time.perf_counter() - remote_create_start) * 1000
3110
                        rows.extend(
3111
                            run_git_agent_task_baseline(
3112
                                subject,
3113
                                remote,
3114
                                run_root,
3115
                                run_index,
3116
                                run_metadata,
3117
                                args.admitted_output_chars,
3118
                                remote_create_ms,
3119
                            )
3120
                        )
3121
                        continue
3122
                    if scenario == AGENT_TASK_OAK_LOCAL_SCENARIO:
3123
                        if subject.kind != "oak":
3124
                            rows.append(
3125
                                unmeasured_step_row(
3126
                                    subject,
3127
                                    scenario,
3128
                                    "agent_task_oak_local.skipped",
3129
                                    run_index,
3130
                                    run_metadata,
3131
                                    "owned local oak serve scenario applies only to Oak subjects",
3132
                                )
3133
                            )
3134
                            continue
3135
                        rows.extend(
3136
                            run_oak_local_agent_task_baseline(
3137
                                subject,
3138
                                run_root,
3139
                                run_index,
3140
                                run_metadata,
3141
                                args.admitted_output_chars,
3142
                            )
3143
                        )
3144
                        continue
3145
                    if subject.kind == "git":
3146
                        if args.git_remote == "github":
3147
                            remote = oakbench_remotes.resolve_git_github_remote()
3148
                        else:
3149
                            remote = oakbench_remotes.make_git_bare_remote(
3150
                                subject.bin, run_root, f"task-loop-remote-r{run_index}.git"
3151
                            )
3152
                        if not remote.resolved:
3153
                            rows.append(skip_row(subject, TASK_LOOP_SCENARIO, run_index, run_metadata, remote.skip_reason or ""))
3154
                            continue
3155
                        subject_rows = run_git_loop(subject, remote, run_root, run_index, run_metadata, args.admitted_output_chars)
3156
                    else:
3157
                        remote = oakbench_remotes.resolve_oak_remote("sync")
3158
                        if not remote.resolved:
3159
                            rows.append(skip_row(subject, TASK_LOOP_SCENARIO, run_index, run_metadata, remote.skip_reason or ""))
3160
                            continue
3161
                        subject_rows = run_oak_loop(subject, remote, run_root, run_index, run_metadata, args.admitted_output_chars)
3162
                    total = next((row for row in subject_rows if row.get("operation") == "task_loop.total"), None)
3163
                    if total is not None:
3164
                        total.update(loop_summary_fields(subject_rows))
3165
                    rows.extend(subject_rows)
3166
3167
    store = ResultsStore(args.results, lane="task-loop")
3168
    raw_path, summary_path = store.write(timestamp, rows, summary_text(rows))
3169
    if not args.keep_workdirs:
3170
        shutil.rmtree(run_root, ignore_errors=True)
3171
    print(f"[result] {raw_path}")
3172
    print(f"[summary] {summary_path}")
3173
    return 1 if has_lane_failures(rows) else 0
3174
3175
3176
if __name__ == "__main__":
3177
    raise SystemExit(main())