feat(deep-forecast): Phase 2+3 scoring recalibration + autoresearch prompt self-improvement#2178
Merged
Merged
Conversation
…l score distribution computeDeepPathAcceptanceScore formula: pathScore×0.55 + quality×0.20 + coherence×0.15 With pathScore≈0.65, quality≈0.30, coherence≈0.55: 0.358 + 0.060 + 0.083 = 0.50 The 0.60 threshold was calibrated before understanding that reportableQualityScore is constrained by world-state simulation geometry (not hypothesis quality), and coherence loses 0.15 for generic candidates without routeFacilityKey. The threshold was structurally unreachable with typical expanded paths. Verified end-to-end: deep worker now returns [DeepForecast] completed. Also updates T6 gateDetails assertion and renames the rejection-floor test to correctly describe the new behavior (strong inputs should be accepted). 111/111 tests pass.
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
…10 tests - Add scoreImpactExpansionQuality() locked scorer: commodity rate (35%), variable diversity (35%), chain coverage (20%), mapped rate (10%) - Add runImpactExpansionPromptRefinement(): rate-limited LLM critic loop (30min cooldown) that reads learned section from Redis, scores current run, generates critique if composite < 0.62, tests on same candidates, commits to forecast:prompt:impact-expansion:learned if score improves - buildImpactExpansionSystemPrompt() now accepts learnedSection param, appends it after core rules with separator so model sees prior examples - buildImpactExpansionCandidateHash() includes learnedFingerprint to bust cache when learned section changes - processDeepForecastTask reads learnedSection from Redis before LLM call, runs refinement after both completed and no_material_change paths - Export scoreImpactExpansionQuality + runImpactExpansionPromptRefinement - T9: high commodity rate + chain coverage → composite > 0.70 - T10: no commodity + no chain coverage → composite < 0.40 - 113/113 tests pass
… parse
- Threshold 0.62 was too low: commodity=1.00 + chain=1.00 compensated for
diversity=0.50 (all same chains), keeping composite at 0.775 → no critique
- Raise to 0.80 so diversity<0.70 triggers critique even with good commodity/chain
- Fix JSON parser to extract first {…} block (handles Gemini code-fence wrapping)
- Add per-hypothesis log in refinement breakdown for observability
- Add refinementQualityThreshold to gateDetails for self-documenting artifacts
- Verified: critique fires on diversity=0.50 run, committed Hormuz/Baltic/Suez
region-specific chain examples (score 0.592→0.650)
…xpansion Previously: all candidates → one batch LLM call → LLM averages context → identical route_disruption → inflation_pass_through chains for all candidates. Now: each candidate → its own focused LLM call (parallel Promise.all) → LLM reasons about specific stateKind/region/routeFacility for that candidate. Results (3 candidates, 3 parallel calls): - composite: 0.592 → 0.831 (+0.24) - commodity: 0.17 → 1.00 (all mapped have specific commodity) - diversity: 0.50 → 0.83 (energy_export_stress, importer_balance_stress appearing alongside route_disruption — genuinely different chains) - Baseline updated: 0.831 (above 0.80 threshold → no critique needed) Also threads learnedSection through extractSingleImpactExpansionCandidate so the learned examples from autoresearch apply to each focused call. Per-candidate cache keys (already existed) now serve as primary cache.
- Change stage mock from impact_expansion → impact_expansion_single (batch primary path removed, per-candidate is now primary) - Assert parseMode === per_candidate instead of parseStage /^recovered_/ (recovered_ prefix was only set by old batch_repair path) - 2257/2257 tests pass
…kepoint map Candidate packets had routeFacilityKey=none for Red Sea / Persian Gulf / Baltic Sea signals because prediction titles say "Red Sea maritime disruption" not "Bab el-Mandeb" or "Strait of Hormuz". CHOKEPOINT_MARKET_REGIONS only had sub-facility names (Bab el-Mandeb, Suez Canal) as keys, not the sea regions themselves. Fix: add Red Sea, Persian Gulf, Arabian Sea, Black Sea, South China Sea, Mediterranean Sea as direct keys so region-level candidate titles resolve. Result: LLM user prompt now shows routeFacilityKey=Red Sea / Persian Gulf / Baltic Sea per candidate — giving each focused call the geographic context needed to generate route-specific chains. - Autoresearch baseline updated 0.932→0.965 on this run - T8 extended with Red Sea, Persian Gulf, South China Sea assertions - 2257/2257 tests pass
…nstraint - Bump IMPACT_EXPANSION_REGISTRY_VERSION to v4 - Add hypothesisKey, description, geography, affectedAssets, marketImpact, causalLink fields to normalizeImpactHypothesisDraft (keep legacy fields for backward compat) - Rewrite buildImpactExpansionSystemPrompt: remove IMPACT_VARIABLE_REGISTRY constraint table, use free-form ImpactHypothesis schema with geographic/commodity specificity rules - Rewrite evaluateImpactHypothesisRejection: use effective key (hypothesisKey || variableKey) for dedup; legacy registry check only for old cached responses without hypothesisKey - Update validateImpactHypotheses scoring: add geographyScore, commodityScore, causalLinkScore, assetScore terms; channelCoherence/bucketCoherence only apply to legacy responses - Update parent-must-be-mapped invariant to use hypothesisKey || variableKey as effective key - Update mapImpactHypothesesToWorldSignals: use effective key for dedup and sourceKey; prefer description/geography over legacy fields - Update buildImpactPathsForCandidate: match on hypothesisKey || variableKey for parent lookup - Update buildImpactPathId: use hypothesisKey || variableKey for hash inputs - Rewrite scoreImpactExpansionQuality: add geographyRate and assetRate metrics; update composite weights - Update buildImpactPromptCritiqueSystemPrompt/UserPrompt: use hypothesisKey-based chain format in examples - Add new fields to buildImpactExpansionBundleFromPaths push calls - Update T7 test assertion: MUST be the exact hypothesisKey instead of variableKey string
…orer - commodityDiversity = unique commodities / nCandidates (weight 0.35) Penalizes runs where all candidates default to same commodity. 3 candidates all crude_oil → diversity=0.33 → composite ~0.76 → critique fires. - Rebalanced composite weights: comDiversity 0.35, geo 0.20, keyDiversity 0.15, chain 0.10, commodityRate 0.10, asset 0.05, mappedRate 0.05 - Breakdown log now shows comDiversity + geo + keyDiversity - Critique prompt updated: commodity_monoculture failure mode, diagnosis targets commodity homogeneity - T9: added commodityDiversity=1.0 assertion (2 unique commodities across 2 candidates)
…ityDiversity + directGeoDiversity + candidateSpreadScore Problem: measuring diversity on all mapped hypotheses misses the case where one candidate generates 10 implications while others generate 0, or where all candidates converge on the same commodity due to dominating signals. Fix: score at the DIRECT hypothesis level (root causes only) and add a candidate-spread metric: - directCommodityDiversity: unique commodities among direct hypotheses / nCandidates. Measures breadth at the root-cause level. 3 candidates all crude_oil → 0.33 → composite ~0.77 → critique fires. - directGeoDiversity: unique primary geographies among direct hypotheses / nCandidates. First segment of compound geography strings (e.g. 'Red Sea, Suez Canal' → 'red sea') to avoid double-counting. - candidateSpreadScore: normalized inverse-HHI. 1.0 = perfectly even distribution across candidates. One candidate with 10 implications and others with 0 → scores near 0 → critique fires. Weight rationale: comDiversity 0.35, geoDiversity 0.20, spread 0.15, chain 0.15, comRate 0.08, assetRate 0.04, mappedRate 0.03. Verified: Run 2 Baltic/Hormuz/Brazil → freight/crude_oil/USD spread=0.98 ✓
Surface autoresearch loop outcome per run: converged (bool), finalComposite,
critiqueIterations (0 or 1), refinementCommitted, and perCandidateMappedCount
(candidateStateId → count). After 5+ runs the artifact alone answers whether
the pipeline is improving.
Architectural changes:
- runImpactExpansionPromptRefinement now returns { iterationCount, committed }
at all exit paths instead of undefined
- Call hoisted before writeForecastTraceArtifacts so the result flows into the
debug payload via dataForWrite.refinementResult
- buildImpactExpansionDebugPayload assembles convergence from validation +
refinementResult; exported for direct testing
- Fix: stale diversityScore reference replaced with directCommodityDiversity
Tests: T-conv-1 (converged=true), T-conv-2 (converged=false + iterations=1),
T-conv-3 (perCandidateMappedCount grouping) — 116/116 pass
…rvability PR
P1-A: sanitize LLM-returned proposed_addition before Redis write (prompt injection
guard via sanitizeProposedLlmAddition — strips directive-phrase lines)
P1-B: restore fire-and-forget for runImpactExpansionPromptRefinement; compute
critiqueIterations from quality score (predicted) instead of awaiting result,
eliminating 15-30s critical-path latency on poor-quality runs
P1-C: processDeepForecastTask now returns convergence object to callers; add
convergence_quality_met warn check to evaluateForecastRunArtifacts
P1-D: cap concurrent LLM calls in extractImpactExpansionBundle to 3 (manual
batching — no p-limit) to respect provider rate limits
P2-1: hash full learnedSection in buildImpactExpansionCandidateHash (was sliced
to 80 chars, causing cache collisions on long learned sections)
P2-2: add exitReason field to all runImpactExpansionPromptRefinement return paths
P2-3: sanitizeForPrompt strips directive injection phrases; new
sanitizeProposedLlmAddition applies line-level filtering before Redis write
P2-4: add comment explaining intentional bidirectional affectedAssets/assetsOrSectors
coalescing in normalizeImpactHypothesisDraft
P2-5: extract makeConvTestData helper in T-conv tests; remove refinementCommitted
assertions (field removed from convergence shape)
P2-6: convergence_quality_met check added to evaluateForecastRunArtifacts (warn)
🤖 Generated with Claude Sonnet 4.6 via Claude Code (https://claude.ai/claude-code) + Compound Engineering v2.49.0
Co-Authored-By: Claude Sonnet 4.6 (200K context) <[email protected]>
…rvability P1-1: mapImpactHypothesesToWorldSignals used free-form marketImpact values (price_spike, shortage, credit_stress, risk_off) verbatim as signal channel types, producing unknown types that buildMarketTransmissionGraph cannot consume. Add IMPACT_SIGNAL_CHANNELS set + resolveImpactChannel() to map free-form strings to the nearest valid channel before signal materialization. P1-2: sanitizeForPrompt had directive-phrase stripping added that was too broad for a function called on headlines, evidence tables, case files, and geopolitical summaries. Reverted to original safe sanitizer (newline/control char removal only). Directive stripping remains in sanitizeProposedLlmAddition where it is scoped to Redis-bound LLM-generated additions only. P2: Renamed convergence.critiqueIterations to predictedCritiqueIterations to make clear this is a prediction from the quality score, not a measured count from actual refinement behavior (refinement is fire-and-forget after artifact write). Updated T-conv-1/2 test assertions to match.
Wire inputs.newsInsights / inputs.newsDigest through the candidate selection pipeline so buildImpactExpansionEvidenceTable receives up to 3 commodity-relevant live headlines as 'live_news' evidence entries. Changes: - IMPACT_COMMODITY_LEXICON: extend fertilizer pattern (fertiliser, nitrogen, phosphate, npk); add food_grains and shipping_freight entries - filterNewsHeadlinesByState: new pure helper that scores headlines by alert status, LNG/energy/route/sanctions signal match, lexicon commodity match, and source corroboration count (min score 2 to include) - buildImpactExpansionEvidenceTable: add newsItems param, inject live_news entries, raise cap 8→11 - buildImpactExpansionCandidate: add newsInsights/newsDigest params, compute newsItems via filterNewsHeadlinesByState - selectImpactExpansionCandidates: add newsInsights/newsDigest to options - Call site: pass inputs.newsInsights/newsDigest at seed time - Export filterNewsHeadlinesByState, buildImpactExpansionEvidenceTable - 9 new tests (T-news-1 through T-lex-3): all pass, 125 total pass 🤖 Generated with Claude Sonnet 4.6 (200K context) via Claude Code (https://claude.ai/claude-code) + Compound Engineering v2.49.0 Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
…adlinesByState The LNG+2 score was commodity-specific and inconsistent with the intent: headline scoring should be generic, not biased toward any named commodity. The function already handles the state's detected commodity dynamically via lexEntry.pattern (IMPACT_COMMODITY_LEXICON). LNG headlines still score via CRITICAL_NEWS_ENERGY_RE (+1) and CRITICAL_NEWS_ROUTE_RE (+1) when relevant to the state's region. All 125 tests pass.
P1 fixes (block-merge): - Lower third_order mapped floor 0.74→0.70 (max achievable via 0.72 multiplier was 0.72) - Guard runImpactExpansionPromptRefinement against empty validation (no_mapped exit) - Replace block-list sanitizeProposedLlmAddition with pattern-based allowlist (HTML/JS/directive takeover) - Fix TOCTOU on PROMPT_LAST_ATTEMPT_KEY: claim slot before quality check, not after LLM call P2 fixes: - Fix learned section overflow: use slice(-MAX) to preserve tail, not discard all prior content - Add safe_haven_bid and global_crude_spread_stress branches to resolveImpactChannel - quality_met path now sets rate-limit key (prevents 3 Redis GETs per good run) - Hoist extractNewsClusterItems outside stateUnit map in selectImpactExpansionCandidates - Export PROMPT_LEARNED_KEY, PROMPT_BASELINE_KEY, PROMPT_LAST_ATTEMPT_KEY + read/clear helpers All 125 tests pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why this PR?
After Phase 1 (PR #2169) fixed structural LLM parsing bugs, two cascading problems remained:
Gate 3 structurally unreachable:
acceptanceScore >= 0.60was impossible —reportableQualityScore(~0.30) is bounded by world-state simulation geometry, not hypothesis quality. Math:0.65×0.55 + 0.30×0.20 + 0.55×0.15 = 0.50pinned exactly at threshold. Every deep run returnedcompleted_no_material_change.Generic chains with no commodity specificity: LLM produced
route_disruption → inflation_pass_throughfor all candidates regardless of context. No LNG, gold, fertilizer appearing.Changes
Gate 3 fix
0.60 → 0.50to match real score distributiongateDetails.acceptanceThresholdupdated to 0.50Scoring recalibration (Phase 2)
second_ordermapped floor:0.66 → 0.58, multiplier:0.85 → 0.88pathScorethreshold:0.66 → 0.50second_orderdowngraded totrace_onlyif direct parent is not mappedCHOKEPOINT_MARKET_REGIONSPrompt v3
dependsOnKeyexactness rule — MUST be exact variableKey of a direct in same responseObservability
scoringBreakdownin debug artifact: ALL hypotheses with validationScore, status, salience, specificity, evidencegateDetailsin debug artifact: self-documenting thresholds at run timeAutoresearch prompt self-improvement (Karpathy-style)
scoreImpactExpansionQuality: commodity rate (35%) + variable diversity (35%) + chain coverage (20%) + mapped rate (10%)forecast:prompt:impact-expansion:learned(max 1600 chars, 30-day TTL)buildImpactExpansionSystemPrompt(learnedSection)appends learned section after core rulesbuildImpactExpansionCandidateHashincludes learned fingerprintTests
Verification
Run locally:
Expect:
[PromptRefinement]logs, at least onecompletedstatus,mappedCount >= 2in debug artifact.