Current section
Files
Jump to
Current section
Files
lib/mix/tasks/pixir.bench.subagents.ex
defmodule Mix.Tasks.Pixir.Bench.Subagents do
@shortdoc "Run a verifiable no-network Subagents benchmark"
@moduledoc """
Runs a deterministic Subagents benchmark and writes evidence artifacts.
This is the executable adapter for `docs/benchmarks/subagents.md`'s Pixir-native
stress layer. It intentionally avoids real provider calls by injecting fake
providers through Pixir's existing test seam.
Usage:
mix pixir.bench.subagents
mix pixir.bench.subagents --n 1,5,10 --repetitions 3
mix pixir.bench.subagents --dry-run --json
mix pixir.bench.subagents --output .pixir/benchmarks/subagents/custom-run
Artifacts:
runs.jsonl
summary.json
report.md
"""
use Mix.Task
alias Pixir.{Auth, Log, Provider, SessionSupervisor, Subagents}
@schema_version 1
@default_n [1, 5, 10, 25, 50]
@default_repetitions 1
@scenarios [
"pixir_spawn_wait_n",
"pixir_close_mid_fanout",
"pixir_replay_summary",
"codex_visible_fanout_probe"
]
@switches [
output: :string,
n: :string,
repetitions: :integer,
dry_run: :boolean,
json: :boolean,
help: :boolean
]
@aliases [o: :output, r: :repetitions, h: :help]
@impl Mix.Task
def run(args) do
{opts, _argv, invalid} = OptionParser.parse(args, switches: @switches, aliases: @aliases)
json? = Keyword.get(opts, :json, false)
if Keyword.get(opts, :help, false) do
print_help(json?)
exit(:normal)
end
if invalid != [] do
fail!(:invalid_options, "Invalid command-line options.", %{invalid: invalid}, json?)
end
run_id = timestamp()
output_dir =
Keyword.get(opts, :output, Path.join([".pixir", "benchmarks", "subagents", run_id]))
ns =
case parse_n(Keyword.get(opts, :n)) do
{:ok, values} ->
values
{:error, details} ->
fail!(:invalid_n_values, "--n must contain positive integers.", details, json?)
end
repetitions = Keyword.get(opts, :repetitions, @default_repetitions)
if repetitions < 1 do
fail!(
:invalid_repetitions,
"--repetitions must be a positive integer.",
%{repetitions: repetitions},
json?
)
end
if Keyword.get(opts, :dry_run, false) do
print_dry_run(output_dir, ns, repetitions, json?)
exit(:normal)
end
Mix.Task.run("app.start")
File.mkdir_p!(output_dir)
File.mkdir_p!(Path.join(output_dir, "workspaces"))
records =
Enum.flat_map(ns, fn n ->
for repetition <- 1..repetitions do
run_spawn_wait(output_dir, run_id, n, repetition)
end
end) ++
[
run_close_mid_fanout(output_dir, run_id),
run_replay_summary(output_dir, run_id),
codex_observability_record(run_id)
]
summary = summarize(records, run_id, output_dir, ns)
draft_report = render_report(summary, records)
validation = validate_benchmark(records, summary, draft_report, ns)
completion_audit = completion_audit(records, summary, validation)
summary =
summary
|> Map.put("schema_validation", validation)
|> Map.put("completion_audit", completion_audit)
report = render_report(summary, records)
validation = validate_benchmark(records, summary, report, ns)
completion_audit = completion_audit(records, summary, validation)
summary =
summary
|> Map.put("schema_validation", validation)
|> Map.put("completion_audit", completion_audit)
report = render_report(summary, records)
write_jsonl!(Path.join(output_dir, "runs.jsonl"), records)
File.write!(Path.join(output_dir, "summary.json"), Jason.encode!(summary, pretty: true))
File.write!(Path.join(output_dir, "report.md"), report)
File.write!(
Path.join(output_dir, "completion_audit.json"),
Jason.encode!(completion_audit, pretty: true)
)
ok? = summary["status"] == "passed" and completion_audit["status"] == "completion_ready"
result = %{
"ok" => ok?,
"mode" => "run",
"output_dir" => Path.expand(output_dir),
"report" => Path.expand(Path.join(output_dir, "report.md")),
"completion_audit" => completion_audit,
"completion_audit_path" => Path.expand(Path.join(output_dir, "completion_audit.json")),
"summary" => summary
}
if json? do
IO.puts(Jason.encode!(result, pretty: true))
else
Mix.shell().info("""
Subagents benchmark finished.
output: #{output_dir}
report: #{Path.join(output_dir, "report.md")}
status: #{summary["status"]}
""")
end
if not ok?, do: exit({:shutdown, 1})
end
defmodule WritingProvider do
def stream(%{history: history}, opts) do
on_delta = Keyword.get(opts, :on_delta, fn _ -> :ok end)
users = Enum.filter(history, &(&1.type == :user_message))
results = Enum.filter(history, &(&1.type == :tool_result))
prompt = users |> List.last() |> then(&((&1 && &1.data["text"]) || ""))
if length(results) < length(users) do
{:ok,
%{
text: "",
reasoning: "",
reasoning_items: [],
function_calls: [
%{
call_id: "bench_write_#{length(users)}",
name: "write",
args: %{"path" => "result.txt", "content" => prompt}
}
],
finish_reason: :tool_calls
}}
else
on_delta.({:text_delta, "done"})
{:ok,
%{
text: "completed #{prompt}",
reasoning: "",
reasoning_items: [],
function_calls: [],
finish_reason: :stop
}}
end
end
end
defmodule BlockingProvider do
def stream(_request, _opts) do
Process.sleep(10_000)
{:ok,
%{
text: "late",
reasoning: "",
reasoning_items: [],
function_calls: [],
finish_reason: :stop
}}
end
end
defmodule NoOAuth do
def refresh_skew_ms, do: 60_000
end
defp run_spawn_wait(output_dir, run_id, n, repetition) do
scenario = "pixir_spawn_wait_n"
workspace = workspace_dir(output_dir, "#{scenario}_#{n}_r#{repetition}")
File.mkdir_p!(workspace)
File.write!(Path.join(workspace, "source.txt"), "parent source")
started_at = DateTime.utc_now()
started_native = System.monotonic_time(:millisecond)
{:ok, sid, _pid} = SessionSupervisor.start_session(workspace: workspace, role: :build)
spawn_started = System.monotonic_time(:millisecond)
agents =
for i <- 1..n do
{:ok, agent} =
Subagents.spawn_agent(
sid,
%{
"task" => "bench-task-#{i}",
"agent" => "worker",
"max_threads" => min(max(n, 1), 16),
"timeout_ms" => 10_000
},
workspace: workspace,
provider: WritingProvider,
permission_mode: :auto
)
agent
end
first_child_ms =
if agents == [], do: nil, else: System.monotonic_time(:millisecond) - spawn_started
all_spawned_ms = System.monotonic_time(:millisecond) - spawn_started
{:ok, completed} = Subagents.wait(sid, Enum.map(agents, & &1["id"]), 30_000)
total_ms = System.monotonic_time(:millisecond) - started_native
{:ok, history} = Log.fold(sid, workspace: workspace)
{:ok, listed} = Subagents.list(sid)
missing_child_outputs =
Enum.reject(completed, fn agent ->
Path.join(agent["workspace"], "result.txt")
|> File.read()
|> case do
{:ok, "bench-task-" <> _} -> true
_ -> false
end
end)
parent_write_present = File.exists?(Path.join(workspace, "result.txt"))
reconstructed = Subagents.reconstruct(history)
events_by_type = count_by(history, &Atom.to_string(&1.type))
subagent_events_by_name = count_by_subagent_event(history)
statuses = count_by(completed, & &1["status"])
active_after_wait = Enum.count(listed, &(not Subagents.terminal?(&1["status"])))
ok =
length(completed) == n and statuses["completed"] == n and missing_child_outputs == [] and
not parent_write_present and map_size(reconstructed) == n and active_after_wait == 0
%{
"run_id" => run_id,
"schema_version" => @schema_version,
"scenario" => scenario,
"provider_path" => "pixir-native",
"network" => false,
"status" => if(ok, do: "passed", else: "failed"),
"started_at" => DateTime.to_iso8601(started_at),
"n" => n,
"repetition" => repetition,
"workspace" => Path.expand(workspace),
"parent_session_id" => sid,
"child_ids" => Enum.map(completed, & &1["id"]),
"child_session_ids" => Enum.map(completed, & &1["child_session_id"]),
"metrics" => %{
"prompt_to_first_child_event_ms" => first_child_ms,
"prompt_to_all_spawned_ms" => all_spawned_ms,
"wait_all_completed_ms" => total_ms - all_spawned_ms,
"total_turn_ms" => total_ms,
"spawned_count" => length(agents),
"completed_count" => statuses["completed"] || 0,
"failed_count" => statuses["failed"] || 0,
"timed_out_count" => statuses["timed_out"] || 0,
"cancelled_count" => statuses["cancelled"] || 0,
"active_after_wait_count" => active_after_wait,
"parent_log_events" => length(history),
"child_log_count" => count_child_logs(completed),
"parent_log_bytes" => log_bytes(workspace, sid),
"child_log_bytes_total" => child_log_bytes(completed)
},
"evidence" => %{
"events_by_type" => events_by_type,
"subagent_events_by_name" => subagent_events_by_name,
"reconstructed_count" => map_size(reconstructed),
"missing_child_output_ids" => Enum.map(missing_child_outputs, & &1["id"]),
"parent_write_present" => parent_write_present,
"terminal_subagent_events" =>
Enum.count(history, &(&1.type == :subagent_event and &1.data["event"] == "finished"))
}
}
end
defp run_close_mid_fanout(output_dir, run_id) do
scenario = "pixir_close_mid_fanout"
workspace = workspace_dir(output_dir, scenario)
File.mkdir_p!(workspace)
File.write!(Path.join(workspace, "source.txt"), "parent source")
started_at = DateTime.utc_now()
started_native = System.monotonic_time(:millisecond)
{:ok, sid, _pid} = SessionSupervisor.start_session(workspace: workspace, role: :build)
agents =
for i <- 1..5 do
{:ok, agent} =
Subagents.spawn_agent(
sid,
%{
"task" => "blocking-#{i}",
"agent" => "worker",
"max_threads" => 5,
"timeout_ms" => 30_000
},
workspace: workspace,
provider: BlockingProvider,
permission_mode: :auto
)
agent
end
Process.sleep(100)
closed =
for agent <- agents do
{:ok, closed} = Subagents.close(sid, agent["id"])
closed
end
total_ms = System.monotonic_time(:millisecond) - started_native
{:ok, history} = Log.fold(sid, workspace: workspace)
closed_events =
Enum.count(history, &(&1.type == :subagent_event and &1.data["event"] == "closed"))
ok =
length(closed) == 5 and Enum.all?(closed, &(&1["status"] == "closed")) and
closed_events == 5
%{
"run_id" => run_id,
"schema_version" => @schema_version,
"scenario" => scenario,
"provider_path" => "pixir-native",
"network" => false,
"status" => if(ok, do: "passed", else: "failed"),
"started_at" => DateTime.to_iso8601(started_at),
"n" => 5,
"workspace" => Path.expand(workspace),
"parent_session_id" => sid,
"child_ids" => Enum.map(closed, & &1["id"]),
"child_session_ids" => Enum.map(closed, & &1["child_session_id"]),
"metrics" => %{
"total_turn_ms" => total_ms,
"spawned_count" => length(agents),
"closed_count" => Enum.count(closed, &(&1["status"] == "closed")),
"parent_log_events" => length(history),
"child_log_count" => count_child_logs(closed)
},
"evidence" => %{
"events_by_type" => count_by(history, &Atom.to_string(&1.type)),
"subagent_events_by_name" => count_by_subagent_event(history),
"closed_events" => closed_events
}
}
end
defp run_replay_summary(output_dir, run_id) do
scenario = "pixir_replay_summary"
workspace = workspace_dir(output_dir, scenario)
File.mkdir_p!(workspace)
File.write!(Path.join(workspace, "source.txt"), "parent source")
started_at = DateTime.utc_now()
{:ok, sid, _pid} = SessionSupervisor.start_session(workspace: workspace, role: :build)
agents =
for i <- 1..2 do
{:ok, agent} =
Subagents.spawn_agent(
sid,
%{
"task" => "replay-task-#{i}",
"agent" => "explorer",
"max_threads" => 2,
"timeout_ms" => 10_000
},
workspace: workspace,
provider: WritingProvider,
permission_mode: :auto
)
agent
end
{:ok, completed} = Subagents.wait(sid, Enum.map(agents, & &1["id"]), 30_000)
{:ok, history} = Log.fold(sid, workspace: workspace)
replay_text = capture_replay_text(history)
ok =
Enum.all?(completed, &(&1["status"] == "completed")) and
String.contains?(replay_text, "Subagent") and
String.contains?(replay_text, "completed") and
not String.contains?(replay_text, "bench_write_")
%{
"run_id" => run_id,
"schema_version" => @schema_version,
"scenario" => scenario,
"provider_path" => "pixir-native",
"network" => false,
"status" => if(ok, do: "passed", else: "failed"),
"started_at" => DateTime.to_iso8601(started_at),
"n" => 2,
"workspace" => Path.expand(workspace),
"parent_session_id" => sid,
"child_ids" => Enum.map(completed, & &1["id"]),
"child_session_ids" => Enum.map(completed, & &1["child_session_id"]),
"metrics" => %{
"parent_log_events" => length(history),
"child_log_count" => count_child_logs(completed),
"replay_text_bytes" => byte_size(replay_text)
},
"evidence" => %{
"replay_contains_subagent" => String.contains?(replay_text, "Subagent"),
"replay_contains_completed" => String.contains?(replay_text, "completed"),
"replay_drops_raw_tool_call_ids" => not String.contains?(replay_text, "bench_write_"),
"replay_excerpt" => String.slice(replay_text, 0, 1_000)
}
}
end
defp capture_replay_text(history) do
name = :"subagents_bench_auth_#{System.unique_integer([:positive])}"
path = Path.join(System.tmp_dir!(), "pixir-subagents-bench-auth-#{name}.json")
try do
{:ok, _pid} =
Auth.start_link(name: name, store_path: path, env_api_key: "sk-bench", oauth: NoOAuth)
{:ok, _} =
Provider.stream(%{history: history},
auth: name,
transport: capture_transport(self())
)
receive do
{:provider_body, body} ->
body["input"]
|> Enum.flat_map(&Map.get(&1, "content", []))
|> Enum.map_join("\n", &Map.get(&1, "text", ""))
after
1_000 -> ""
end
after
File.rm_rf!(path)
end
end
defp capture_transport(test_pid) do
fn request, acc, feed ->
send(test_pid, {:provider_body, Jason.decode!(request.body)})
acc = feed.({:status, 200}, acc)
{:ok, feed.({:data, "data: {\"type\":\"response.completed\"}\n\n"}, acc)}
end
end
defp codex_observability_record(run_id) do
%{
"run_id" => run_id,
"schema_version" => @schema_version,
"scenario" => "codex_visible_fanout_probe",
"provider_path" => "codex-through-t3",
"network" => false,
"status" => "not_observed",
"started_at" => DateTime.utc_now() |> DateTime.to_iso8601(),
"n" => nil,
"metrics" => %{},
"evidence" => %{
"reason" =>
"This Pixir-native benchmark adapter does not drive T3 Code's Codex provider. Codex visibility is measured by the separate local T3 harness at scripts/codex-subagents-observability-probe.ts."
}
}
end
defp summarize(records, run_id, output_dir, required_scales) do
failed = Enum.filter(records, &(&1["status"] == "failed"))
%{
"schema_version" => @schema_version,
"run_id" => run_id,
"status" => if(failed == [], do: "passed", else: "failed"),
"output_dir" => Path.expand(output_dir),
"generated_at" => DateTime.utc_now() |> DateTime.to_iso8601(),
"records_count" => length(records),
"failed_count" => length(failed),
"not_observed_count" => Enum.count(records, &(&1["status"] == "not_observed")),
"scales" =>
records
|> Enum.filter(&(&1["scenario"] == "pixir_spawn_wait_n"))
|> Enum.map(& &1["n"])
|> Enum.uniq()
|> Enum.sort(),
"requirements" => %{
"pixir_required_scales_present" =>
Enum.all?(required_scales, fn n ->
Enum.any?(records, &(&1["scenario"] == "pixir_spawn_wait_n" and &1["n"] == n))
end),
"no_failed_records" => failed == [],
"close_mid_fanout_checked" =>
Enum.any?(records, &(&1["scenario"] == "pixir_close_mid_fanout")),
"replay_summary_checked" =>
Enum.any?(records, &(&1["scenario"] == "pixir_replay_summary")),
"codex_comparability_noted" =>
Enum.any?(records, &(&1["scenario"] == "codex_visible_fanout_probe"))
}
}
end
defp validate_benchmark(records, summary, report, required_scales) do
record_issues = validate_records(records)
summary_issues = validate_summary(summary, records, required_scales)
report_issues = validate_report(report, summary, records)
%{
"schema_version" => @schema_version,
"status" =>
if(record_issues == [] and summary_issues == [] and report_issues == [],
do: "passed",
else: "failed"
),
"record_count" => length(records),
"record_issues" => record_issues,
"summary_issues" => summary_issues,
"report_issues" => report_issues
}
end
defp validate_records(records) do
records
|> Enum.with_index()
|> Enum.flat_map(fn {record, index} ->
base_record_issues(record, index) ++ scenario_record_issues(record, index)
end)
end
defp base_record_issues(record, index) do
[]
|> require_equal(
["records", index, "schema_version"],
record["schema_version"],
@schema_version
)
|> require_string(["records", index, "run_id"], record["run_id"])
|> require_in(["records", index, "scenario"], record["scenario"], @scenarios)
|> require_string(["records", index, "provider_path"], record["provider_path"])
|> require_in(["records", index, "status"], record["status"], [
"passed",
"failed",
"not_observed"
])
|> require_string(["records", index, "started_at"], record["started_at"])
|> require_equal(["records", index, "network"], record["network"], false)
|> require_map(["records", index, "metrics"], record["metrics"])
|> require_map(["records", index, "evidence"], record["evidence"])
end
defp scenario_record_issues(%{"scenario" => "pixir_spawn_wait_n"} = record, index) do
n = record["n"]
metrics = record["metrics"] || %{}
evidence = record["evidence"] || %{}
[]
|> require_positive_integer(["records", index, "n"], n)
|> require_positive_integer(["records", index, "repetition"], record["repetition"])
|> require_list_length(["records", index, "child_ids"], record["child_ids"], n)
|> require_list_length(
["records", index, "child_session_ids"],
record["child_session_ids"],
n
)
|> require_equal(["records", index, "metrics", "spawned_count"], metrics["spawned_count"], n)
|> require_equal(
["records", index, "metrics", "completed_count"],
metrics["completed_count"],
n
)
|> require_equal(
["records", index, "metrics", "active_after_wait_count"],
metrics["active_after_wait_count"],
0
)
|> require_equal(
["records", index, "metrics", "child_log_count"],
metrics["child_log_count"],
n
)
|> require_equal(
["records", index, "evidence", "reconstructed_count"],
evidence["reconstructed_count"],
n
)
|> require_equal(
["records", index, "evidence", "missing_child_output_ids"],
evidence["missing_child_output_ids"],
[]
)
|> require_equal(
["records", index, "evidence", "parent_write_present"],
evidence["parent_write_present"],
false
)
end
defp scenario_record_issues(%{"scenario" => "pixir_close_mid_fanout"} = record, index) do
metrics = record["metrics"] || %{}
evidence = record["evidence"] || %{}
[]
|> require_equal(["records", index, "n"], record["n"], 5)
|> require_list_length(["records", index, "child_ids"], record["child_ids"], 5)
|> require_equal(["records", index, "metrics", "closed_count"], metrics["closed_count"], 5)
|> require_equal(
["records", index, "evidence", "closed_events"],
evidence["closed_events"],
5
)
end
defp scenario_record_issues(%{"scenario" => "pixir_replay_summary"} = record, index) do
evidence = record["evidence"] || %{}
[]
|> require_equal(["records", index, "n"], record["n"], 2)
|> require_list_length(["records", index, "child_ids"], record["child_ids"], 2)
|> require_equal(
["records", index, "evidence", "replay_contains_subagent"],
evidence["replay_contains_subagent"],
true
)
|> require_equal(
["records", index, "evidence", "replay_contains_completed"],
evidence["replay_contains_completed"],
true
)
|> require_equal(
["records", index, "evidence", "replay_drops_raw_tool_call_ids"],
evidence["replay_drops_raw_tool_call_ids"],
true
)
end
defp scenario_record_issues(%{"scenario" => "codex_visible_fanout_probe"} = record, index) do
evidence = record["evidence"] || %{}
[]
|> require_equal(["records", index, "status"], record["status"], "not_observed")
|> require_string(["records", index, "evidence", "reason"], evidence["reason"])
end
defp scenario_record_issues(record, index) do
[
issue(
["records", index, "scenario"],
"unknown_scenario",
"Unknown scenario.",
record["scenario"]
)
]
end
defp validate_summary(summary, records, required_scales) do
actual_scales =
records
|> Enum.filter(&(&1["scenario"] == "pixir_spawn_wait_n"))
|> Enum.map(& &1["n"])
|> Enum.uniq()
|> Enum.sort()
failed_count = Enum.count(records, &(&1["status"] == "failed"))
requirements = summary["requirements"] || %{}
[]
|> require_equal(["summary", "schema_version"], summary["schema_version"], @schema_version)
|> require_string(["summary", "run_id"], summary["run_id"])
|> require_in(["summary", "status"], summary["status"], ["passed", "failed"])
|> require_string(["summary", "output_dir"], summary["output_dir"])
|> require_equal(["summary", "records_count"], summary["records_count"], length(records))
|> require_equal(["summary", "failed_count"], summary["failed_count"], failed_count)
|> require_equal(["summary", "scales"], summary["scales"], actual_scales)
|> require_map(["summary", "requirements"], requirements)
|> require_requirement_true(requirements, "pixir_required_scales_present")
|> require_requirement_true(requirements, "no_failed_records")
|> require_requirement_true(requirements, "close_mid_fanout_checked")
|> require_requirement_true(requirements, "replay_summary_checked")
|> require_requirement_true(requirements, "codex_comparability_noted")
|> require_equal(
["summary", "required_scales_observed"],
Enum.all?(required_scales, &(&1 in actual_scales)),
true
)
end
defp validate_report(report, summary, _records) do
[
{"run_id", summary["run_id"]},
{"status", "Status: **#{summary["status"]}**"},
{"raw_records", "Raw records: `runs.jsonl`"},
{"summary", "Summary: `summary.json`"},
{"completion_audit", "## Completion Audit"},
{"schema_validation", "## Schema Validation"}
]
|> Enum.flat_map(fn {field, needle} ->
if is_binary(needle) and String.contains?(report || "", needle) do
[]
else
[
issue(
["report", field],
"missing_report_evidence",
"Report did not include required evidence.",
needle
)
]
end
end)
end
defp completion_audit(records, summary, validation) do
requirements = [
audit_requirement(
"deterministic_no_network",
Enum.all?(records, &(&1["network"] == false)),
"Every benchmark record declares network=false."
),
audit_requirement(
"records_schema_validated",
validation["record_issues"] == [],
"runs.jsonl records validate against deterministic benchmark schema."
),
audit_requirement(
"summary_schema_validated",
validation["summary_issues"] == [],
"summary.json validates against deterministic benchmark schema."
),
audit_requirement(
"report_reconciled",
validation["report_issues"] == [],
"report.md includes run id, status, raw records, summary, schema validation, and completion audit."
),
audit_requirement(
"all_requirements_true",
summary["requirements"] |> Map.values() |> Enum.all?(&(&1 == true)),
"All scenario requirements are true."
),
audit_requirement(
"no_orphaned_active_children",
no_orphaned_active_children?(records),
"Spawn/wait records have zero active children after wait and close records terminally close children."
),
audit_requirement(
"codex_non_comparability_recorded",
Enum.any?(records, &(&1["scenario"] == "codex_visible_fanout_probe")),
"The deterministic Pixir-native adapter records that Codex/T3 comparability is out of scope."
)
]
%{
"schema_version" => @schema_version,
"status" =>
if(Enum.all?(requirements, &(&1["status"] == "proved")),
do: "completion_ready",
else: "incomplete"
),
"proof_states" => [
"intent_declared",
"dry_run_passed",
"benchmark_records_produced",
"records_validated",
"schema_validated",
"report_reconciled",
"completion_ready"
],
"requirements" => requirements
}
end
defp audit_requirement(name, true, evidence) do
%{"requirement" => name, "status" => "proved", "evidence" => evidence}
end
defp audit_requirement(name, false, evidence) do
%{"requirement" => name, "status" => "missing", "evidence" => evidence}
end
defp no_orphaned_active_children?(records) do
Enum.all?(records, fn
%{"scenario" => "pixir_spawn_wait_n", "metrics" => metrics} ->
metrics["active_after_wait_count"] == 0
%{"scenario" => "pixir_close_mid_fanout", "metrics" => metrics} ->
metrics["closed_count"] == metrics["spawned_count"]
_record ->
true
end)
end
defp issue(path, kind, message, value) do
%{
"path" => Enum.map(path, &to_string/1),
"kind" => kind,
"message" => message,
"value" => value
}
end
defp require_string(issues, _path, value) when is_binary(value) and value != "", do: issues
defp require_string(issues, path, value),
do: [issue(path, "required_string", "Expected a non-empty string.", value) | issues]
defp require_map(issues, _path, value) when is_map(value), do: issues
defp require_map(issues, path, value),
do: [issue(path, "required_map", "Expected a map.", value) | issues]
defp require_in(issues, path, value, allowed) do
if value in allowed do
issues
else
[issue(path, "unexpected_value", "Value was not in the allowed set.", value) | issues]
end
end
defp require_equal(issues, path, value, expected) do
if value == expected do
issues
else
[
issue(path, "unexpected_value", "Value did not match the expected value.", %{
"actual" => value,
"expected" => expected
})
| issues
]
end
end
defp require_positive_integer(issues, _path, value) when is_integer(value) and value >= 1,
do: issues
defp require_positive_integer(issues, path, value),
do: [issue(path, "positive_integer_required", "Expected a positive integer.", value) | issues]
defp require_list_length(issues, _path, value, expected_length)
when is_list(value) and length(value) == expected_length,
do: issues
defp require_list_length(issues, path, value, expected_length) do
[
issue(path, "unexpected_list_length", "List length did not match expected length.", %{
"actual" => if(is_list(value), do: length(value), else: nil),
"expected" => expected_length
})
| issues
]
end
defp require_requirement_true(issues, requirements, key) do
require_equal(issues, ["summary", "requirements", key], Map.get(requirements, key), true)
end
defp render_report(summary, records) do
scale_rows =
records
|> Enum.filter(&(&1["scenario"] == "pixir_spawn_wait_n"))
|> Enum.sort_by(&{&1["n"], &1["repetition"]})
|> Enum.map_join("\n", fn record ->
m = record["metrics"]
"| #{record["n"]} | #{record["repetition"]} | #{record["status"]} | #{m["prompt_to_first_child_event_ms"]} | #{m["prompt_to_all_spawned_ms"]} | #{m["wait_all_completed_ms"]} | #{m["total_turn_ms"]} | #{m["completed_count"]} | #{m["active_after_wait_count"]} |"
end)
codex_note =
records
|> Enum.find(&(&1["scenario"] == "codex_visible_fanout_probe"))
|> get_in(["evidence", "reason"])
validation_section = validation_section(summary["schema_validation"])
audit_section = audit_section(summary["completion_audit"])
"""
# Subagents Benchmark Report
Run id: `#{summary["run_id"]}`
Status: **#{summary["status"]}**
Output directory: `#{summary["output_dir"]}`
## Pixir Spawn/Wait Stress
| N | Rep | Status | First child ms | All spawned ms | Wait completed ms | Total ms | Completed | Active after wait |
|---:|---:|---|---:|---:|---:|---:|---:|---:|
#{scale_rows}
## Resilience Checks
- Close mid-fanout checked: #{summary["requirements"]["close_mid_fanout_checked"]}
- Replay summary checked: #{summary["requirements"]["replay_summary_checked"]}
- Required scales present: #{summary["requirements"]["pixir_required_scales_present"]}
- No failed records: #{summary["requirements"]["no_failed_records"]}
#{validation_section}
#{audit_section}
## Codex Comparability
#{codex_note}
Raw records: `runs.jsonl`
Summary: `summary.json`
"""
end
defp validation_section(nil), do: ""
defp validation_section(validation) do
"""
## Schema Validation
Status: **#{validation["status"]}**
- Record issues: #{length(validation["record_issues"] || [])}
- Summary issues: #{length(validation["summary_issues"] || [])}
- Report issues: #{length(validation["report_issues"] || [])}
"""
end
defp audit_section(nil), do: ""
defp audit_section(audit) do
rows =
audit["requirements"]
|> Enum.map_join("\n", fn requirement ->
"| #{requirement["requirement"]} | #{requirement["status"]} | #{requirement["evidence"]} |"
end)
"""
## Completion Audit
Status: **#{audit["status"]}**
| Requirement | Status | Evidence |
|---|---|---|
#{rows}
"""
end
defp print_help(json?) do
payload = %{
"ok" => true,
"command" => "mix pixir.bench.subagents",
"description" => "Run deterministic no-network Pixir Subagents benchmarks.",
"options" => [
"--n 1,5,10,25,50",
"--repetitions 3",
"--dry-run",
"--json",
"--output PATH"
],
"proof_states" => [
"intent_declared",
"dry_run_passed",
"benchmark_records_produced",
"records_validated",
"schema_validated",
"report_reconciled",
"completion_ready"
]
}
if json? do
IO.puts(Jason.encode!(payload, pretty: true))
else
Mix.shell().info("""
Run deterministic no-network Pixir Subagents benchmarks.
Usage:
mix pixir.bench.subagents [options]
Common:
mix pixir.bench.subagents --dry-run
mix pixir.bench.subagents --n 1,5,10,25,50 --repetitions 3
Agent-facing:
--json emit machine-readable dry-run/result/error JSON
--dry-run print planned artifacts and scenarios without writing files
""")
end
end
defp print_dry_run(output_dir, ns, repetitions, json?) do
scenarios =
Enum.flat_map(ns, fn n ->
for repetition <- 1..repetitions do
%{
"scenario" => "pixir_spawn_wait_n",
"n" => n,
"repetition" => repetition,
"network" => false
}
end
end) ++
[
%{"scenario" => "close_mid_fanout", "network" => false},
%{"scenario" => "replay_summary", "network" => false},
%{"scenario" => "codex_observability_record", "network" => false}
]
payload = %{
"ok" => true,
"mode" => "dry_run",
"would_write" => [
Path.join(output_dir, "runs.jsonl"),
Path.join(output_dir, "summary.json"),
Path.join(output_dir, "report.md"),
Path.join(output_dir, "completion_audit.json"),
Path.join(output_dir, "workspaces")
],
"scenarios" => scenarios,
"estimated_real_network_runs" => 0,
"requires" => ["Pixir application can start", "fake provider test seam"]
}
if json? do
IO.puts(Jason.encode!(payload, pretty: true))
else
Mix.shell().info("Dry run. Would write: #{output_dir}")
Mix.shell().info("Scenarios: #{Jason.encode!(scenarios)}")
end
end
defp fail!(kind, message, details, json?) do
payload = %{
"ok" => false,
"error" => %{
"kind" => Atom.to_string(kind),
"message" => message,
"details" => details,
"root_agent_hint" =>
"Run with --help or --json --help to inspect the supported adapter contract."
}
}
if json? do
IO.puts(Jason.encode!(payload, pretty: true))
else
Mix.shell().error("#{message} #{inspect(details)}")
end
exit({:shutdown, 1})
end
defp parse_n(nil), do: {:ok, @default_n}
defp parse_n(raw) do
values = raw |> String.split(",", trim: true) |> Enum.map(&String.trim/1)
parsed =
Enum.map(values, fn value ->
case Integer.parse(value) do
{int, ""} when int >= 1 -> {:ok, int}
_ -> {:error, value}
end
end)
case Enum.filter(parsed, &match?({:error, _}, &1)) do
[] -> {:ok, parsed |> Enum.map(fn {:ok, int} -> int end) |> Enum.uniq()}
invalid -> {:error, %{"invalid" => Enum.map(invalid, fn {:error, value} -> value end)}}
end
end
defp write_jsonl!(path, records) do
contents = Enum.map_join(records, "\n", &Jason.encode!/1) <> "\n"
File.write!(path, contents)
end
defp workspace_dir(output_dir, name), do: Path.join([output_dir, "workspaces", name])
defp timestamp do
DateTime.utc_now()
|> DateTime.to_iso8601(:basic)
|> String.replace(~r/[^0-9TZ]/, "")
|> String.replace("Z", "")
end
defp count_by(items, fun) do
Enum.reduce(items, %{}, fn item, acc ->
key = fun.(item)
Map.update(acc, key, 1, &(&1 + 1))
end)
end
defp count_by_subagent_event(history) do
history
|> Enum.filter(&(&1.type == :subagent_event))
|> count_by(& &1.data["event"])
end
defp count_child_logs(agents) do
Enum.count(agents, fn agent ->
agent["child_session_id"] &&
log_path(agent["workspace"], agent["child_session_id"]) |> File.exists?()
end)
end
defp child_log_bytes(agents) do
Enum.reduce(agents, 0, fn agent, acc ->
acc + log_bytes(agent["workspace"], agent["child_session_id"])
end)
end
defp log_bytes(_workspace, nil), do: 0
defp log_bytes(workspace, sid) do
path = log_path(workspace, sid)
case File.stat(path) do
{:ok, stat} -> stat.size
_ -> 0
end
end
defp log_path(workspace, sid) do
Path.join([Pixir.Paths.project_root(workspace), "sessions", "#{sid}.ndjson"])
end
end