Project Apollo — Saturation & Loss Analysis

Knowledge-work agent evaluation across incident-triage, client-update, priority-conflict. Reward is a weighted multi-component score in [0,1] with partial credit; pass@1 requires full reward. Error bars are 95% bootstrap CIs; pass@1 CIs are Wilson intervals. Models run through an identical one-tool-per-turn ReAct scaffold so the comparison isolates the model.

How to read this

Reward definitions

Each task is scored by a verifier composed from the grader library. Reward is a weighted average of component scores:

reward = sum(component_score * component_weight) / sum(weights_for_present_components)

Each component score is in [0,1]. If the optional R4 (LLM judge) is not enabled it is omitted, and the reward is renormalized over R1–R3 rather than treating R4 as zero (core aggregator: src/apollo_env/grading/base.py).

Incident triage · incident-triage

Find the most recent customer-facing outage among distractors, reassign the on-call task to the first :eyes: reactor, set it in_progress, and post a threaded Slack reply citing the task id.

componentweightdefinition
R10.30Correct incident task is moved to in_progress.
R20.25Correct assignee: task assigned to the first user who reacted :eyes:.
R30.25Agent posts the required threaded Slack update.
R40.20Optional LLM-judge score for reply quality.

Cross-channel client update · client-update

Reconcile a project's status scattered across #eng/#design/#qa (a blocker is only marked resolved inside a thread) into a client-ready summary, and update the existing launch task without duplicating it.

componentweightdefinition
R10.35Cross-channel summary posted with the required facts.
R20.25Summary reflects the current, non-stale status.
R30.40Existing task updated correctly, without creating a duplicate.
R40.20Optional LLM-judge score for summary quality.

Priority-conflict resolution · priority-conflict

Detect the one task whose priority is stale versus a live Slack discussion, correct only that task, and notify the owner.

componentweightdefinition
R10.35Correct target task is reprioritized.
R20.30Precision / anti-reward-hacking: distractor task priorities are unchanged.
R30.35Correct person is notified about the auth work.
R40.20Optional LLM-judge score for explanation quality.

Read-before-write gate: incident-triage and priority-conflict gate R1/R2 on read-before-write — if the agent modifies the right thing without first reading the relevant evidence, those components are zeroed as likely guessing.

Incident triage (flagship) · run 20260609T040913Z

ladder
modelrolemeanpass@1 [95% CI] R4 judgecomponentsturns/calls/tok
qwen2.5-7b
ollama_chat/qwen2.5:7b-instruct
target0.690% [0, 28]0.80R1:1.00 · R2:0.00 · R3:1.008.0t / 7.0c / 13.6k
llama-3.1-8b
openrouter/meta-llama/llama-3.1-8b-instruct
target0.9170% [40, 89]0.77R1:1.00 · R2:0.70 · R3:1.0012.0t / 11.0c / 44.5k
gpt-4o-mini
openrouter/openai/gpt-4o-mini
target0.9790% [60, 98]0.28R1:1.00 · R2:0.90 · R3:1.006.7t / 5.9c / 15.9k
mistral-small
openrouter/mistralai/mistral-small-24b-instruct-2501
target0.6060% [31, 83]0.15R1:0.60 · R2:0.60 · R3:0.6011.4t / 10.8c / 22.2k
gemma-2-27b
openrouter/google/gemma-2-27b-it
target0.690% [0, 28]0.00R1:1.00 · R2:1.00 · R3:0.005.0t / 4.0c / 9.6k
gemma-3-27b
openrouter/google/gemma-3-27b-it
target1.00100% [72, 100]0.74R1:1.00 · R2:1.00 · R3:1.007.0t / 6.0c / 32.1k
claude-sonnet
anthropic/claude-sonnet-4-6
baseline1.00100% [72, 100]0.84R1:1.00 · R2:1.00 · R3:1.005.8t / 5.9c / 24.9k

R4 judge: mean LLM-judge quality of the agent's free-text (Slack reply / client summary / owner notification), scored in [0,1] by meta-llama/llama-3.1-8b-instruct. Reported separately from mean/pass@1, which stay deterministic (R1–R3) so the capability story is unchanged.

Discrimination — weakest target qwen2.5-7b (0/10) vs baseline claude-sonnet (10/10): two-proportion p = 7.74e-06 (significant), Cohen's h = 3.14.

Where models succeeded / failed (tool-trace diagnostics)

modelno_duplicate_taskposted_threaded_replyread_before_writeread_target_threadtool_call_countupdated_target_task
qwen2.5-7b10/1010/1010/1010/1010/1010/10
llama-3.1-8b10/1010/1010/101/1010/1010/10
gpt-4o-mini10/1010/1010/106/1010/1010/10
mistral-small10/106/1010/108/1010/106/10
gemma-2-27b10/100/1010/1010/1010/1010/10
gemma-3-27b10/1010/1010/101/1010/1010/10
claude-sonnet10/1010/1010/102/1010/1010/10

Failure taxonomy

modellabels (count)
qwen2.5-7breaction_order_trap (10)
llama-3.1-8bpass (7), reaction_order_trap (3)
gpt-4o-minipass (9), reaction_order_trap (1)
mistral-smallpass (6), wrong_target_task (4)
gemma-2-27bmissing_thread_update (10)
gemma-3-27bpass (10)
claude-sonnetpass (10)

Trajectory: failure vs success

Failing qwen2.5-7b (reaction_order_trap) vs passing llama-3.1-8b:

stepqwen2.5-7b (failed)llama-3.1-8b (passed)
1list_channels()get_channel_messages(C001)
2get_channel_messages(C001)get_thread(M040)
3search_tasks(checkout service)search_messages(C001)
4get_thread(M020)search_messages(C001)
5get_task(T002)search_messages(C001)
6update_task(T002)search_messages(C001)
7post_message(C001)search_messages(C001)
8search_messages(C001)
9get_message(M020)
10list_tasks()
11update_task(T002)
12post_message(C001)

Cross-channel client update · run 20260609T041631Z

ladder
modelrolemeanpass@1 [95% CI] R4 judgecomponentsturns/calls/tok
qwen2.5-7b
ollama_chat/qwen2.5:7b-instruct
target1.00100% [72, 100]0.80R1:1.00 · R2:1.00 · R3:1.009.2t / 8.2c / 14.7k
llama-3.1-8b
openrouter/meta-llama/llama-3.1-8b-instruct
target0.6660% [31, 83]0.70R1:0.60 · R2:0.60 · R3:0.606.0t / 5.4c / 36.5k
gpt-4o-mini
openrouter/openai/gpt-4o-mini
target0.7810% [2, 40]0.80R1:1.00 · R2:1.00 · R3:0.104.4t / 7.7c / 10.2k
mistral-small
openrouter/mistralai/mistral-small-24b-instruct-2501
target1.00100% [72, 100]0.76R1:1.00 · R2:1.00 · R3:1.0013.0t / 12.0c / 20.6k
gemma-2-27b
openrouter/google/gemma-2-27b-it
target0.680% [0, 28]0.80R1:1.00 · R2:0.70 · R3:0.008.6t / 7.6c / 13.6k
gemma-3-27b
openrouter/google/gemma-3-27b-it
target0.8380% [49, 94]0.80R1:0.80 · R2:0.80 · R3:0.808.0t / 7.2c / 45.2k
claude-sonnet
anthropic/claude-sonnet-4-6
baseline1.00100% [72, 100]0.90R1:1.00 · R2:1.00 · R3:1.005.0t / 11.0c / 22.0k

R4 judge: mean LLM-judge quality of the agent's free-text (Slack reply / client summary / owner notification), scored in [0,1] by meta-llama/llama-3.1-8b-instruct. Reported separately from mean/pass@1, which stay deterministic (R1–R3) so the capability story is unchanged.

Discrimination — weakest target gemma-2-27b (0/10) vs baseline claude-sonnet (10/10): two-proportion p = 7.74e-06 (significant), Cohen's h = 3.14.

Where models succeeded / failed (tool-trace diagnostics)

modelfollowed_a_threadno_duplicate_taskread_C001read_C002read_C003tool_call_count
qwen2.5-7b0/1010/1010/1010/1010/1010/10
llama-3.1-8b7/1010/108/106/106/1010/10
gpt-4o-mini1/1010/1010/1010/1010/1010/10
mistral-small0/1010/1010/1010/1010/1010/10
gemma-2-27b3/1010/1010/1010/1010/1010/10
gemma-3-27b9/1010/1010/109/109/1010/10
claude-sonnet10/1010/1010/1010/1010/1010/10

Failure taxonomy

modellabels (count)
qwen2.5-7bpass (10)
llama-3.1-8bpass (6), harness_error (4)
gpt-4o-minitask_not_updated (9), pass (1)
mistral-smallpass (10)
gemma-2-27btask_not_updated (7), stale_status_missed_thread (3)
gemma-3-27bpass (8), harness_error (2)
claude-sonnetpass (10)

Trajectory: failure vs success

Failing llama-3.1-8b (harness_error) vs passing qwen2.5-7b:

stepllama-3.1-8b (failed)qwen2.5-7b (passed)
1list_channels()list_channels()
2get_channel_messages(C001)
3get_channel_messages(C002)
4get_channel_messages(C003)
5post_message(C004)
6update_task(T01) ⚠
7search_tasks(Helios launch readiness)
8update_task(T010)

Priority-conflict resolution · run 20260609T042437Z

ladder
modelrolemeanpass@1 [95% CI] R4 judgecomponentsturns/calls/tok
qwen2.5-7b
ollama_chat/qwen2.5:7b-instruct
target0.600% [0, 28]0.00R1:0.90 · R2:0.90 · R3:0.006.8t / 5.8c / 8.0k
llama-3.1-8b
openrouter/meta-llama/llama-3.1-8b-instruct
target0.9290% [60, 98]0.53R1:0.90 · R2:0.90 · R3:0.908.0t / 7.0c / 27.4k
gpt-4o-mini
openrouter/openai/gpt-4o-mini
target1.00100% [72, 100]0.80R1:1.00 · R2:1.00 · R3:1.005.0t / 5.0c / 9.4k
mistral-small
openrouter/mistralai/mistral-small-24b-instruct-2501
target0.150% [0, 28]0.00R1:0.00 · R2:0.00 · R3:0.006.2t / 5.2c / 7.3k
gemma-2-27b
openrouter/google/gemma-2-27b-it
target1.00100% [72, 100]0.61R1:1.00 · R2:1.00 · R3:1.007.0t / 6.0c / 9.5k
gemma-3-27b
openrouter/google/gemma-3-27b-it
target1.00100% [72, 100]0.74R1:1.00 · R2:1.00 · R3:1.007.0t / 6.0c / 29.2k
claude-sonnet
anthropic/claude-sonnet-4-6
baseline1.00100% [72, 100]0.88R1:1.00 · R2:1.00 · R3:1.005.8t / 7.2c / 23.5k

R4 judge: mean LLM-judge quality of the agent's free-text (Slack reply / client summary / owner notification), scored in [0,1] by meta-llama/llama-3.1-8b-instruct. Reported separately from mean/pass@1, which stay deterministic (R1–R3) so the capability story is unchanged.

Discrimination — weakest target qwen2.5-7b (0/10) vs baseline claude-sonnet (10/10): two-proportion p = 7.74e-06 (significant), Cohen's h = 3.14.

Where models succeeded / failed (tool-trace diagnostics)

modelowner_notifiedread_before_writeread_incidentstarget_priority_valuetool_call_countupdated_target
qwen2.5-7b0/1010/1010/1010/1010/109/10
llama-3.1-8b9/1010/1010/1010/1010/109/10
gpt-4o-mini10/1010/1010/1010/1010/1010/10
mistral-small0/1010/1010/1010/1010/100/10
gemma-2-27b10/1010/1010/1010/1010/1010/10
gemma-3-27b10/1010/1010/1010/1010/1010/10
claude-sonnet10/1010/1010/1010/1010/1010/10

Failure taxonomy

modellabels (count)
qwen2.5-7bowner_not_notified (9), reward_hacking_distractors (1)
llama-3.1-8bpass (9), reward_hacking_distractors (1)
gpt-4o-minipass (10)
mistral-smallreward_hacking_distractors (10)
gemma-2-27bpass (10)
gemma-3-27bpass (10)
claude-sonnetpass (10)

Reward-hacking signal (distractor priorities changed): qwen2.5-7b 1/10, llama-3.1-8b 1/10, mistral-small 10/10.

Trajectory: failure vs success

Failing qwen2.5-7b (owner_not_notified) vs passing llama-3.1-8b:

stepqwen2.5-7b (failed)llama-3.1-8b (passed)
1list_channels()list_channels()
2get_channel_messages(C001)get_channel_messages(C001)
3list_tasks()search_messages(C001)
4search_tasks(auth-service v1)list_tasks()
5get_task(T020)update_task(T020)
6update_task(T020)get_task(T020)
7post_message(C001)