Files
waggle-os/docs/decisions/2026-04-27-phase-3-acceptance-gate-pre-run-halt.md
Oleg Maslov 0c3e2ead3b
Some checks failed
Installer Smoke / installer-smoke (push) Has been cancelled
moving
2026-09-02 10:10:29 +02:00

254 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
decision_id: 2026-04-27-phase-3-acceptance-gate-pre-run-halt
date: 2026-04-27
phase: 3 acceptance gate — pre-run scope review
verdict: HALT-AND-PING — five scope-discovery items need PM ratification before the gate is runnable as briefed
predecessor: 2026-04-27-phase-2-acceptance-gate-PASS.md
sprint_plan: D:\Projects\waggle-os\decisions\2026-04-26-agent-fix-sprint-plan.md
---
# Phase 3 Acceptance Gate — Pre-Run Halt-and-Ping
## TL;DR
**The gate is not runnable as briefed.** Five scope-discovery items surface before any API call is made. Same halt-and-ping pattern that resolved Phase 3.1-3.4 cleanly — surface BEFORE burning real-API budget, not after.
The biggest one: **cost estimate vs halt threshold is 3-7x off** because per-step LLM cost grows with accumulated context size; the brief's "~$0.04 per Opus step × 50 = $2.00" assumes constant per-step cost, but step input grows as retrievals accumulate, producing super-linear cost growth without aggressive ContextManager engagement.
PM ratification needed on (a) cost-vs-scope tradeoff, (b) "identical final answer" relaxation for real LLMs, (c) synthetic task design, (d) ContextManager config, (e) self-judge methodology cross-model.
---
## Pre-flight status
| Check | Result | Notes |
|---|---|---|
| LiteLLM proxy reachable | ✓ (HTTP 401 = auth-required, server alive) | `http://localhost:4000/health` |
| `LITELLM_MASTER_KEY` in shell env | ✗ | Lives in `.env` / docker-compose env. Need to source before running. |
| `DASHSCOPE_API_KEY` in shell env | ✗ | Same — `.env` only. |
| `.env` file present | ✓ | `D:\Projects\waggle-os\.env` |
| Phase 3.1-3.4 commits clean | ✓ | `7163114``8b8a940` (4 commits, all PM-ratified) |
| Phase 3.4 unit tests | ✓ | 31 integration tests pass; 2428/2428 packages/agent total; 5720 repo-root |
| `runRetrievalAgentLoop` `maxSteps` knob exists | ✓ | Optional config field; default 5; scalable to 50 |
Pre-flight infrastructure is GO — but scope below needs ratification.
---
## Scope-discovery item #1 — Cost analysis: $4 halt is 3-7× below realistic estimate
### Why per-step cost is NOT constant
A 50-step retrieval-augmented loop accumulates ALL prior retrieval results in the messages array (so the model sees them on subsequent turns). By step N, the input contains:
- system prompt (~2K tokens)
- kickoff user prompt (~0.5K)
- N-1 prior `{assistant: action}` messages (~0.2K each)
- N-1 prior `{user: retrieval_injection}` messages (~0.5K each)
**Total input at step 50:** ~2.5K + 49 × 0.7K ≈ **37K tokens per step** (without ContextManager engagement).
This is the killer: input cost dominates at high step counts. Output is bounded (~200 tokens for a JSON action emission).
### Real per-step cost (recomputed from pricing tables)
| Model | Input $/M | Output $/M | Per-step cost (input 37K, output 200) | 50 steps |
|---|---|---|---|---|
| Opus 4.7 | 15.00 | 75.00 | 37K×15 + 200×75 = **$0.570** | **$28.50** |
| GPT-5.4 | 2.50 | 10.00 | 37K×2.5 + 200×10 = **$0.094** | $4.70 |
| Qwen 3.6 (DashScope) | 0.70 | 2.80 | 37K×0.7 + 200×2.8 = **$0.027** | $1.34 |
| **Three-model total (no crash-resume)** | | | | **$34.54** |
Plus crash-resume doubles a portion (resumes from step 26 → re-runs 25 steps):
- Estimated total with one crash-resume cycle per model: **~$4550**.
PM brief estimate: $2.00 Opus + $0.30 Qwen + $1.50 GPT + $0.30 self-judge + $0.90 buffer = $5.00.
Mismatch: **$4.30 cap vs ~$45 realistic = 9-10× off**, dominated by Opus.
### Why ContextManager helps but doesn't fully resolve
If ContextManager triggers compression at threshold (default 70% of budget), per-step input is capped at threshold + recent verbatim. Estimated cost with **aggressive compression at 4K-token budget**:
| Model | Per-step (input ~4K, output 200) | 50 steps |
|---|---|---|
| Opus 4.7 | 4K×15 + 200×75 = **$0.075** | $3.75 |
| GPT-5.4 | 4K×2.5 + 200×10 = **$0.012** | $0.60 |
| Qwen 3.6 | 4K×0.7 + 200×2.8 = **$0.0034** | $0.17 |
| **Three-model total (no crash-resume)** | | **$4.52** |
With one crash-resume (re-run 25 steps): add ~50% per model on average → **$6.78 total**.
Still over $4 halt, but tractable. **Even aggressive compression doesn't fit $4 cap with 50 steps × 3 models.**
### Three concrete options for PM ratification
| Option | Scope | Realistic cost | Trade-off |
|---|---|---|---|
| **A. Cost-cap-faithful (recommended for first run)** | Drop Opus; Qwen + GPT only; 50 steps; aggressive 4K context budget | **~$0.90** | Loses frontier-proprietary signal; H6 only validated for sovereign + open SOTA |
| **B. Three-model with smaller scope** | Opus + Qwen + GPT; **15 steps** (not 50); 4K context budget | **~$2.20** | Shorter run may not exercise context-manager threshold (compression may not trigger naturally) |
| **C. Three-model full scope; raise cap** | Opus + Qwen + GPT; 50 steps; 4K context budget; **raise hard cap to $8** ($7 halt) | **~$4.506.80** | Spends more, but hits the H6 hypothesis as written |
I recommend **Option A** (drop Opus, Qwen + GPT only) for these reasons:
- H6 says "Opus + Qwen + GPT" but real H6 utility is "long-task scenario completes on cross-model frontier WITHOUT data loss" — verified equally with 2 models as 3
- Opus is the marginal model (most expensive, frontier-proprietary contributes least to sovereign/open-platform claims)
- Frees budget for an Opus follow-up if Qwen + GPT both PASS
- Aligns with Phase 2's principled approach of starting cheap
If PM wants three-model coverage in one shot, **Option C** is the honest pick — but that's a budget-cap raise.
---
## Scope-discovery item #2 — "Identical final answer" criterion needs relaxation for real LLMs
### The problem
Brief acceptance criterion (binding):
> Mid-task crash → fresh runner → resume → identical final answer (replay determinism preserved across recovery)
**This is empirically impossible on real LLMs.** Even at temperature=0:
- GPU floating-point non-determinism (different kernel scheduling across runs)
- Provider server-side load balancing (different model server instances)
- Tokenizer / KV-cache state dependencies on input order
- Provider-side randomness in some routes (Anthropic, OpenAI documented)
A continuous run vs a crash-resume run will produce **semantically equivalent but byte-different** outputs on Opus / GPT / Qwen.
### Where strict identical-output IS testable
In **unit tests** with a mocked `LlmCallFn` (deterministic). Phase 3.4 already has a `replay determinism` test in `long-task-loop-integration.test.ts`:
> `replay determinism: identical llmCall + retrievalSearch → identical final answer (with checkpointStore)`
This passes. The state-machine determinism is verified.
### Proposed relaxation for the real-LLM gate
| Layer | Determinism standard |
|---|---|
| State machine (mocked LLM) | **Strict byte-identical** — already verified by Phase 3.4 unit test |
| Real-LLM end-to-end | **Semantic equivalence ≤ 0.30 Likert** (trio judge or self-judge) — same as the "compress vs uncompressed" criterion already in the brief |
Same Likert tolerance applied for both:
- Continuous run vs crash-resume run (replay determinism — relaxed)
- Compressed run vs uncompressed run (ContextManager preserves meaning)
**Halt-and-ping ask:** PM ratifies that "identical final answer" → "semantic equivalence (Likert ≤ 0.30)" for real-LLM portion of the gate.
---
## Scope-discovery item #3 — Synthetic 50-step task needs concrete spec
### Brief spec (what's there)
> Synthetic task ~50 steps long (multi-document analysis, accumulated context exceeds 70% of model context window threshold)
> Simulated retrieval queries per step (realistic scratch corpus)
### What's missing
- **Corpus shape:** How many docs? What content? Where stored? Stable + reproducible?
- **Question:** What does the agent answer? Must require ~50 retrieval-synthesis steps.
- **Retrieval function:** Real `HybridSearch` over a real `MindDB`? Or a deterministic mock?
### Proposed concrete design
**Corpus:**
- 30 historical-event documents (procedurally generated for stability)
- Each doc: ~500 words, fields = `{event_name, date, location, key_actors, theme_tags[], description}`
- Themes drawn from a fixed pool of 8 (e.g., "economic transformation", "scientific discovery", "social movement", "war/conflict", "political revolution", "cultural shift", "technological breakthrough", "natural disaster")
- Each event tagged 1-3 themes; total theme-occurrence distribution is non-uniform (forces ranking)
**Question:**
> "Survey all 30 historical events in the corpus. Identify the recurring themes and rank them by frequency of occurrence. For each theme in your ranking, cite ≥3 supporting events with their names and dates. Output a final ranked list with citations."
**Why this is ~50 steps:**
- ~30 retrievals (one or two per doc to cover all)
- ~10 synthesis turns (cluster themes, count, rank)
- 1 finalize
- ~41-45 expected steps; padded to 50 maxSteps for headroom
**Retrieval function:**
- Deterministic mock: pre-built keyword index → top-K cosine-like rank → returns docs as formatted strings
- Stable across runs (no real `MindDB` dependency)
- Why mock: avoids real-search non-determinism, keeps focus on agent-loop behavior
**Compression natural trigger point:**
With 4K context budget, threshold 70% → compression at ~2.8K tokens accumulated. After ~6-8 retrievals × ~500 tokens of injection text each = compression at step 6-8 (and again as more accumulates). Confirms ContextManager engagement.
**Halt-and-ping ask:** PM ratifies (or counter-proposes) corpus + question + retrieval design.
---
## Scope-discovery item #4 — ContextManager config is unspecified
The brief says "ContextManager engaged" but doesn't specify config. Per item #1 cost analysis, config matters MASSIVELY.
**Proposed defaults:**
| Param | Value | Rationale |
|---|---|---|
| `contextTokenBudget` | 4000 | Aggressive — keeps cost linear in step count |
| `compressionThreshold` | 0.7 | Default; compression triggers at 2800 tokens accumulated |
| `strategy` | `'retrieve-only'` | No LLM-summary cost during the loop; archives to retrieval index |
| `retainRecentChars` | 1500 | Last ~3 turns' worth of audit verbatim |
| `retrievalCacheMaxSize` | 50 | Cache up to 50 unique queries (matches step budget) |
| `retainRecentDecisions` | 8 | Audit trail keeps last 8 decisions verbatim |
| `estimateTokensFn` | default (`estimateStringTokens`) | Content-aware heuristic |
If PM wants `'summarize-only'` or `'hybrid'` (uses LLM for compression), add ~10-20% cost overhead per compression event. Estimated 4-6 compressions per task × 3 models × ~$0.005 each = trivial.
**Halt-and-ping ask:** PM ratifies ContextManager config or proposes alternatives.
---
## Scope-discovery item #5 — Self-judge cross-model methodology
The brief says:
> Self-judge methodology (Qwen Yes/No prompt) za consistency check across models
**Question for ratification:** is "self-judge" run as:
(a) **Each model judges its own output** (Mem0-style apples-to-apples — Qwen judges Qwen, Opus judges Opus, GPT judges GPT) — pure self-judge bias check, NOT cross-model comparison
(b) **Qwen judges all three** outputs (Qwen-as-judge cross-model) — cross-model with single judge, methodology used in v6 self-judge rebench
(c) **Trio judges all three** — same v6 oracle methodology (Opus + GPT + MiniMax F-mode); higher cost but methodologically consistent with prior gates
Option (b) aligns with brief wording ("Qwen Yes/No prompt") and Extension 3 PM finding (apples-to-apples self-judge requires same methodology across models).
**Halt-and-ping ask:** PM ratifies (b) Qwen Yes/No judges all three models' final answers, OR specifies alternative.
---
## Proposed go-forward (pending PM ratification)
If PM ratifies all five items + chooses **Option A** from item #1 (drop Opus, Qwen + GPT only):
1. Build synthetic corpus + retrieval mock + question (~30 min coding, $0)
2. Build long-task scenario runner using Phase 3.4's `runRetrievalAgentLoopWithRecovery` (~30 min coding, $0)
3. Run on Qwen continuous + Qwen with-crash-resume + GPT continuous + GPT with-crash-resume (~10 min wall, ~$0.90 real-API)
4. Self-judge final answers (Qwen Yes/No across all four runs) (~$0.05)
5. Compute Likert continuous-vs-resume + uncompressed-vs-compressed (re-runs without ContextManager would double the cost, so skip uncompressed-vs-compressed and rely on unit-test verification of compression purity)
6. Write acceptance gate results memo with H6 verdict
7. Total estimated: **~$1.00, ~30 min wall**
If PM ratifies **Option C** (three-model + raise cap to $8):
- Same flow, add Opus continuous + Opus with-crash-resume
- Total: ~$57, ~45 min wall
## Halt-and-ping ask (binding)
Five items need PM ratification:
1. **Cost vs scope tradeoff:** Option A (Qwen + GPT, $1; recommended) / B (3-model 15-step, $2.20) / C (3-model 50-step, raise cap to $8)
2. **"Identical final answer" relaxation:** strict for unit-test layer + Likert ≤ 0.30 for real-LLM layer
3. **Synthetic task design:** 30-event historical-event corpus + ranking question (or counter-proposal)
4. **ContextManager config:** 4K budget / retrieve-only / 1500 recent / 50 cache / 8 decisions (or counter-proposal)
5. **Self-judge:** Qwen-as-judge for all subject models (apples-to-apples cross-model, option b)
Cumulative spend so far: $0.00 (no API calls made; all surfaced from prior pilot data + pricing tables).
---
**End of pre-run halt. Standing HALTED awaiting PM ratification on items #1-#5 before kicking the gate.**