diff --git a/docs/JOURNAL.md b/docs/JOURNAL.md index 3f0ebc9..129fc3c 100644 --- a/docs/JOURNAL.md +++ b/docs/JOURNAL.md @@ -19,6 +19,32 @@ or files, and the "so-what" for future readers. --- +## 2026-07-18 — Corpus reduce truncated on the breadth brief ("hit its output limit") + +**Tags:** bug, design + +After the corpus-v3 breadth nudge (list EVERY holder, enumerate ALL cruxes), +the reduce for a large case (~125 members) started failing with "The +synthesis hit its output limit before finishing" — `runCorpusReducePass` +seeing `stop_reason: 'max_tokens'` against `MAX_REDUCE_OUTPUT_TOKENS = +16384`. Two compounding causes: (1) the breadth brief legitimately runs +18-25k output tokens now (every position's holders as full 64-hex hashes, +all cruxes, load-bearing, the proposal queue); (2) the reduce omits the +`thinking` param, so on **Sonnet 5** adaptive thinking is ON by default and +its thinking tokens share the same `max_tokens` budget — Sonnet had *less* +room for the brief than the earlier (pre-nudge) Opus run that fit in 16384. + +Fix (surgical, no prompt change — preserves the graded breadth): +`MAX_REDUCE_OUTPUT_TOKENS` 16384 → 32768 (well under every current model's +128k output ceiling), and `CORPUS_REDUCE_TIMEOUT_MS` 300000 → 420000 so the +now-larger brief can finish on the slower Opus tier (~55 tok/s × ~22k tokens +≈ 400s) instead of trading a token-cap failure for an abort. The reduce is +the one long single fetch in the corpus flow; its pending sendResponse keeps +the MV3 SW alive. Deliberately NOT a prompt/tool/digest change → no +`MAP_PROMPT_VERSION`/`CORPUS_PROMPT_VERSION` bump → the ~100 cached map +extracts and brief-staleness keys are untouched. See +`src/shared/corpus-prompts.js`, `src/shared/llm-client.js`. + ## 2026-07-18 — A background re-render KILLED the running corpus analysis (token-scoped render guard) **Tags:** bug, pattern diff --git a/src/shared/corpus-prompts.js b/src/shared/corpus-prompts.js index 8b7ecb9..7da1fcd 100644 --- a/src/shared/corpus-prompts.js +++ b/src/shared/corpus-prompts.js @@ -52,7 +52,16 @@ export const HYPOTHESIS_EDGE_TOOL_NAME = 'propose_hypothesis_edges'; export const MAX_MEMBER_INPUT_CHARS = 60000; export const MAX_CLAIMS_DIGEST_CHARS = 4000; export const MAX_MAP_OUTPUT_TOKENS = 8192; -export const MAX_REDUCE_OUTPUT_TOKENS = 16384; +// The reduce emits the WHOLE-corpus brief: every position's holders, ALL +// cruxes, the load-bearing claims, and the proposal queue (the corpus-v3 +// breadth nudge above). For a large case (~125 members) that legitimately +// runs 18-25k output tokens, and on Sonnet 5 — where `thinking` is omitted +// so adaptive thinking is ON by default — those thinking tokens share this +// same budget. 16384 truncated it ("hit its output limit before finishing"); +// 32768 fits the breadth brief with headroom and stays far under every +// current model's 128k output ceiling. NOT a prompt/tool/digest change, so +// no version bump — the map cache and brief staleness are unaffected. +export const MAX_REDUCE_OUTPUT_TOKENS = 32768; export const MAX_HYPOTHESIS_EDGE_OUTPUT_TOKENS = 8192; // ------------------------------------------------------------------ diff --git a/src/shared/llm-client.js b/src/shared/llm-client.js index 0a36f83..bc18615 100644 --- a/src/shared/llm-client.js +++ b/src/shared/llm-client.js @@ -539,7 +539,13 @@ export async function runAuditModulePass(req = {}) { // ------------------------------------------------------------------ const CORPUS_MAP_TIMEOUT_MS = 120000; -const CORPUS_REDUCE_TIMEOUT_MS = 300000; +// The reduce is the ONE long single fetch in the corpus flow (the map phase +// is many short calls). With MAX_REDUCE_OUTPUT_TOKENS raised to 32768, a +// full breadth brief can generate ~20-25k tokens, which on the slower +// (Opus-tier) models runs past the old 300s cap — so give it 7 min rather +// than trade a token-cap failure for an abort. The pending sendResponse +// keeps the MV3 service worker alive for the duration. +const CORPUS_REDUCE_TIMEOUT_MS = 420000; /** Gating snapshot for the portal's "Analyze corpus" control. */ export async function getCorpusConfig() {