|
| 1 | +# Dogfood: goose-gemini vs codex on hardening /atlas |
| 2 | + |
| 3 | +**Goal:** loop until a cheap goose+Gemini-2.5-Flash worker reaches results |
| 4 | +comparable to codex on real /atlas hardening tasks — decomposing into smaller |
| 5 | +subtasks as needed to compensate for the cheaper model. |
| 6 | + |
| 7 | +**Method (A/B, unfakable):** for each real task I (the orchestrator) write a |
| 8 | +pytest acceptance gate the worker may NOT edit. `.atlas-dogfood/bench/run_trial.sh` |
| 9 | +runs a worker (codex | gemini) autonomously, runs the external gate (pytest + |
| 10 | +ruff), records verdict/wall/diff, then resets the source file to baseline so the |
| 11 | +next worker gets a clean tree. Worker self-report is never trusted — the gate is |
| 12 | +truth. Codex (`codex exec`, gpt via OPENAI_API_KEY) is the bar; gemini is goose |
| 13 | +(`GOOSE_PROVIDER=google GOOSE_MODEL=gemini-2.5-flash`). |
| 14 | + |
| 15 | +## Results |
| 16 | + |
| 17 | +| Iter | Task | Granularity | codex | gemini | Note | |
| 18 | +|------|------|-------------|-------|--------|------| |
| 19 | +| 1 | T2 `_parse_version` fixed 3-tuple (real cmp bug) | coarse (1 instr) | PASS 358s | PASS 38s | gemini matches codex; ~9x faster, far cheaper. Small task → no discrimination. | |
| 20 | +| 2 | T3 google/gemini credential-gating in `_provider_usable` + `validate_setup` wiring | coarse, **cross-file (2 files)** | PASS 362s | PASS 41s | gemini matches codex on a cross-file task with **no decomposition**; tighter diff (4 vs 6 ins). ~9x faster, far cheaper. | |
| 21 | + |
| 22 | +## Emerging insight (after 2 iterations) |
| 23 | +The discriminator is NOT task size or cross-file-ness — goose-gemini-2.5-flash matches |
| 24 | +codex on both, at ~1/9th the wall time and far lower cost, when the task is |
| 25 | +**precisely specified** (exact contract in the instruction). The decomposition |
| 26 | +hypothesis most likely bites on **under-specified / autonomous** tasks (e.g. "these |
| 27 | +5 tests fail, find and fix the root cause") where the model must infer the spec |
| 28 | +itself. Reframe for the orchestrator: the lever for cheap models is **specification |
| 29 | +precision** — atomizing a task into small, precisely-specified subtasks IS the |
| 30 | +decomposition that lets a cheap model match a strong one. Iter 3 will test an |
| 31 | +UNDER-specified task to find gemini's failure point, then show decomposition |
| 32 | +(adding spec/structure) closing the gap. |
| 33 | + |
| 34 | +## Iteration 3 — the decomposition test (under-specified vs precise) ★ |
| 35 | +Real task: a chunk of the suite was **non-hermetic** — `resolve_provider` reads real |
| 36 | +API keys (os.environ + bound `discover_key`), so results flipped on the shell: |
| 37 | +native_backend needed a key present (5 fail without); test_cli/test_engine_preflight |
| 38 | +"no-key" tests needed keys absent (fail with one). NO env was fully green. |
| 39 | + |
| 40 | +Two-phase experiment (operator planned the fix in a SEALED file first): |
| 41 | +- **Under-specified** ("5 tests fail, find root cause & fix"): goose-gemini ran with |
| 42 | + ambient keys → failures were masked → it made **ZERO changes**, got confused, and |
| 43 | + asked for clarification. It did NOT find the root cause autonomously. **gemini < codex/operator.** |
| 44 | +- **Decomposed/precise** (operator handed gemini the exact recipe: patch |
| 45 | + `backend.resolve_provider` in native tests; clear key env in no-key tests): gemini |
| 46 | + produced a diff **matching the operator's sealed plan exactly** — test-side only, no |
| 47 | + production change, no weakened assertions. Verified 36/36 green **with AND without** |
| 48 | + ambient keys. Committed (fbac5f1). **gemini == operator/codex.** |
| 49 | + |
| 50 | +## VERDICT |
| 51 | +goose-gemini-2.5-flash is **comparable to codex** on /atlas hardening — and ~9x |
| 52 | +faster / far cheaper — **whenever the task is precisely specified**, including |
| 53 | +cross-file changes. It falls short only on **autonomous diagnosis of under-specified** |
| 54 | +problems. The user's hypothesis is confirmed and operationalized: **decomposition = |
| 55 | +specification precision is the lever** that lets the cheap model match the strong one. |
| 56 | +Orchestrator implication: spend the strong/expensive model on *decomposition & |
| 57 | +diagnosis*; fan the *precisely-specified subtasks* out to cheap goose-gemini workers. |
| 58 | + |
| 59 | +Real hardening landed this run: google provider (f994018) + hermetic test suite (fbac5f1). |
| 60 | + |
| 61 | +## Iteration 4 — full model spectrum: DECOMPOSITION vs EXECUTION ★★ |
| 62 | +Question: how far are cheap/local models from codex at BOTH (a) decomposing a PRD into |
| 63 | +tasks and (b) actually completing the generated tasks? Harnesses: |
| 64 | +`.atlas-dogfood/bench/decomp_bench.py` (decomposition, scored by the engine's own |
| 65 | +`run_validate_tasks`) and goose+ollama on the T2 coding gate (execution). |
| 66 | + |
| 67 | +### (a) Decomposition — PRD → tasks (one-shot JSON, gate = run_validate_tasks) |
| 68 | +| Model | Gate | Note | |
| 69 | +|---|---|---| |
| 70 | +| openai gpt-4.1-mini (strong API) | PASS | clean 5 tasks | |
| 71 | +| **gemini-2.5-flash** | **PASS** | **matches the strong API** | |
| 72 | +| qwen3 ~8B (local) | FAIL | no JSON extracted — reasoning-model `<think>` wrapper defeats _extract_json | |
| 73 | +| llama3.2:3B (local) | FAIL | 14 problems: status "in progress" (not "in-progress"), missing fields | |
| 74 | +| qwen2.5:1.5B (local) | FAIL | only 3 problems — produced 6 tasks, 3 lacked a 2nd subtask | |
| 75 | +Local failures are FIXABLE format/discipline issues, not reasoning failures. The gap |
| 76 | +to codex on decomposition is SHORT. |
| 77 | + |
| 78 | +### (b) Execution — complete the T2 coding task (agentic, gate = pytest+ruff) |
| 79 | +| Worker | Gate | Note | |
| 80 | +|---|---|---| |
| 81 | +| codex | PASS | (iter 1) | |
| 82 | +| gemini-2.5-flash via goose | PASS | (iter 1) == codex, ~9x faster | |
| 83 | +| llama3.2:3B via goose | FAIL | 4s, emitted a malformed tool call (`edit` missing `path`), then rambled; ZERO edits | |
| 84 | +| qwen3 ~8B via goose | FAIL | 219s wandering files, confused, ZERO edits | |
| 85 | +Local ≤8B models CANNOT drive an agentic tool-using harness. The gap to codex on |
| 86 | +execution is LONG — a model-capability wall (tool-calling), not a prompt issue. |
| 87 | + |
| 88 | +## What prd-taskmaster should ADJUST (to bring goose + any/local model in line) |
| 89 | +DECOMPOSITION (cheap wins, likely gets local models passing the gate): |
| 90 | +1. Validation-aware REPAIR loop: when run_validate_tasks fails, feed the specific |
| 91 | + `problems` back for ONE repair pass. Today generate_json only retries on JSON-parse |
| 92 | + failure, not on validation problems — so weak models never get to self-correct |
| 93 | + "missing 2nd subtask" / "invalid status". |
| 94 | +2. Reasoning-model JSON extraction: strip `<think>...</think>` (and similar) before |
| 95 | + _extract_json. qwen3/deepseek-r1 failed ONLY on this. |
| 96 | +3. Status/field normalization: map "in progress"->"in-progress", coerce obvious |
| 97 | + variants before validating (kills avoidable llama failures). |
| 98 | +4. Few-shot: embed one COMPLETE valid example task in the prompt for weak models. |
| 99 | + |
| 100 | +EXECUTION (the hard wall — needs an architectural path, not prompt tweaks): |
| 101 | +5. A constrained STRUCTURED-EDIT execution path for models that can't drive freeform |
| 102 | + tool-calling: the model returns a structured patch/full-file JSON; the HARNESS |
| 103 | + applies it + runs the gate. This is the original "raw API + harness applies" |
| 104 | + design — it lets cheap/local models contribute to execution without agentic tool-use. |
| 105 | +6. Capability-tiered routing: goose agentic path only for models proven to drive tools |
| 106 | + (gemini+); structured-edit path for weak/local; codex/claude for the hardest tasks. |
| 107 | + The orchestrator decomposes with a strong model, then routes each subtask to the |
| 108 | + cheapest worker whose capability clears the bar. |
| 109 | + |
| 110 | +### Bottom line for the user's question |
| 111 | +- Decomposition: gemini is ALREADY at codex level; local models are CLOSE and reachable |
| 112 | + with adjustments #1-#4. |
| 113 | +- Execution: gemini is at codex level; local models are FAR — only adjustments #5-#6 |
| 114 | + (a non-agentic structured-edit path) can bring them in, and even then only for simple |
| 115 | + tasks. The strong model stays essential for decomposition/diagnosis and hard execution. |
| 116 | + |
| 117 | +## Engine debt found (not yet fixed) |
| 118 | +5 tests in `tests/core/test_native_backend.py` (parse_prd / expand / rate) fail on |
| 119 | +`main`: they mock the OLD seam `llm_client.discover_key`, but backend.py now routes |
| 120 | +via `resolve_provider("main")` (backend.py:344) which returns `kind="plan"` in a |
| 121 | +keyless env → handoff path → `ok:False`. Stale tests from the resolver refactor. |
| 122 | +Fix = update those tests to mock `resolve_provider` (or add a discover_key API |
| 123 | +fallback in backend). Deferred: fixing requires editing tests, which conflicts with |
| 124 | +the worker-gate methodology; will handle as a direct maintenance task. |
| 125 | + |
| 126 | +### Iteration 1 detail (T2) |
| 127 | +Real bug: variable-length version tuples mis-compare ((1,2) < (1,2,0) is True → |
| 128 | +"1.2" wrongly older than "1.2.0"). Gate: `tests/core/test_parse_version_tuple.py` |
| 129 | +(11 cases incl. the equality regression). Both workers normalized to a 3-tuple |
| 130 | +and passed the external gate; both diffs clean; source reset verified; gate test |
| 131 | +untampered. **Verdict: comparable on small, well-specified tasks — gemini wins on |
| 132 | +cost/latency.** |
| 133 | + |
| 134 | +## Next |
| 135 | +T2 did not discriminate (too small). Escalate to a harder, multi-branch real task |
| 136 | +— hero candidate: **credential-aware `validate_setup`** (audit P0-2, the v5.2.0 |
| 137 | +relaunch blocker: the readiness gate green-lights a keyless paid config). Expect |
| 138 | +codex to one-shot it and gemini-coarse to struggle → then demonstrate the |
| 139 | +decomposition hypothesis (split into smaller subtasks until gemini matches codex). |
| 140 | + |
| 141 | +## Iteration 5 — quantifying the decomposition gap-closing ★★★ |
| 142 | +Enhanced decomp_bench.py to measure raw → +normalize → +repair (max_tokens 8192). |
| 143 | +- **Variance is the headline for raw local runs:** qwen2.5:1.5b went 3-problems → 0-tasks |
| 144 | + across runs; qwen3 went clean-JSON → no-json. Small local models are UNRELIABLE |
| 145 | + run-to-run, and a validation-aware repair pass did NOT rescue them (llama3.2:3b could |
| 146 | + not self-correct even when handed the exact problems). Normalization/repair help only |
| 147 | + capable models that slip; they do not lift sub-floor (<=3B) models. |
| 148 | +- **DECISIVE FIX for local reasoning models — disable thinking:** qwen3:8b with `/no_think` |
| 149 | + passed the gate **3/3, clean (5 tasks, 2 subtasks each)**. Thinking-mode was the entire |
| 150 | + source of its strict-JSON instability. With it off, a LOCAL 8B model == codex/gemini on |
| 151 | + decomposition. |
| 152 | + |
| 153 | +### QUANTIFIED answer — "how far to bring local in line with codex" |
| 154 | +DECOMPOSITION: hosted-cheap gemini-flash already == codex (distance 0); local 8B reasoning |
| 155 | +(qwen3) + thinking-disabled == codex, 3/3 (distance ≈ ONE config flag); local <=3B not |
| 156 | +reliable (distance = a bigger model). EXECUTION: unchanged — local can't drive the agentic |
| 157 | +harness; needs the structured-edit path (distance = an architectural change, not a model swap). |
| 158 | + |
| 159 | +### Refined prd-taskmaster adjustments (priority order) |
| 160 | +1. ★ Reasoning-model handling: detect reasoning models and DISABLE thinking for |
| 161 | + structured-gen (ollama think:false / `/no_think`; strip `<think>` before JSON extract). |
| 162 | + Decisive: qwen3 0%→100% reliable on decomposition. |
| 163 | +2. Raise structured-gen max_tokens (4096→8192+) to avoid truncating multi-task JSON. |
| 164 | +3. Validation-aware repair loop (feed run_validate_tasks problems back, 1 pass) — helps |
| 165 | + capable models on hard PRDs; will NOT rescue sub-floor models. |
| 166 | +4. Status/priority normalization before validation — kills avoidable format fails. |
| 167 | +EXECUTION: structured-edit path + capability-tiered routing (from iter4) remain the levers. |
| 168 | + |
| 169 | +## Iteration 6 — structured-edit EXECUTION POC (does adjustment #5 work?) ★★★ |
| 170 | +POC: model never touches the FS; it returns JSON {find, replace}; a deterministic |
| 171 | +harness applies it (str.replace) + runs gate_t2.sh. (.atlas-dogfood/bench/structured_edit_poc.py) |
| 172 | +| Model | Result | Meaning | |
| 173 | +|---|---|---| |
| 174 | +| gemini-2.5-flash (control) | edit applied, near-pass | viable; failed only an over-specified edge ("1.two.3"→(0,0,0) vs its (1,0,3) per-part leniency) | |
| 175 | +| qwen3:8b (/no_think) | applicable edit, wrong fix | **path lowers the bar: 0 edits in agentic mode → an APPLICABLE edit here** | |
| 176 | +| llama3.2:3b | NO-EDIT | below protocol floor — can't emit valid {find,replace} JSON | |
| 177 | +| qwen2.5:1.5b | NO-EDIT | below protocol floor | |
| 178 | + |
| 179 | +CONCLUSION: the structured-edit path REMOVES the tool-calling wall (an 8B local model |
| 180 | +that made zero edits via agentic goose can now produce an applicable edit), but it does |
| 181 | +NOT remove the CAPABILITY requirement — the edit must still be correct, and that still |
| 182 | +tracks model strength. Tiny (<=3B) models can't even follow the JSON edit protocol. |
| 183 | + |
| 184 | +## ===== FINAL SYNTHESIS: how far to bring goose+local in line with codex ===== |
| 185 | +DECOMPOSITION (PRD→tasks): |
| 186 | +- gemini-flash (hosted cheap): == codex. distance 0. |
| 187 | +- local 8B reasoning + thinking-disabled: == codex (3/3). distance ≈ one config flag. |
| 188 | +- local <=3B: unreliable; below floor. distance = a bigger model. |
| 189 | +EXECUTION (complete tasks): |
| 190 | +- gemini via goose: == codex (~9x cheaper/faster). |
| 191 | +- local via AGENTIC goose: fails entirely (tool-calling wall). |
| 192 | +- local via STRUCTURED-EDIT: 8B can participate (applicable edits) but not yet reliably |
| 193 | + correct; <=3B can't follow the protocol. distance = architectural path (built here) + |
| 194 | + a stronger local model for correctness. |
| 195 | +NET: a cheap HOSTED model (gemini-flash) already matches codex for BOTH decomposition and |
| 196 | +execution at a fraction of the cost — that is the immediate win. LOCAL models reach codex |
| 197 | +on decomposition (8B + no-think) but remain short on execution correctness; the |
| 198 | +structured-edit path is the right architecture to let them contribute as they improve. |
| 199 | +Highest-value prd-taskmaster changes: (1) reasoning-model thinking-disable for structured |
| 200 | +gen, (2) max_tokens bump, (3) validation-aware repair loop, (4) a structured-edit execution |
| 201 | +backend + capability-tiered routing (strong model decomposes/diagnoses; cheapest capable |
| 202 | +worker executes each precisely-specified subtask, gated externally). |
0 commit comments