fix(forecast): reclassify conflict/ucdp_zone count specs to judged (#5136)#5138
Conversation
…5136) Conflict/ucdp_zone hard-count specs resolve against CONFLICT_COUNT_SOURCE_FEED (conflict:acled-resolution:v1), which only populates with ACLED credentials we don't have -> EXISTS=0 in prod -> every conflict count spec is unresolvable (sits pending/VOID forever). Route these families to judged resolution (live via #5087) instead of emitting a dead hard spec. - deriveHardMetrics: conflict/ucdp_zone return null when the count feed is unavailable -> buildHardSpec falls back to buildJudgedSpec (LLM judge over the news archive). Gated by CONFLICT_COUNT_FEED_AVAILABLE (default false); flip to re-enable hard-count once a populated event-count feed exists. - buildQuestion: family-aware, escalation-framed question for conflict forecasts (resolves more reliably against the archive than the generic phrasing). - Threaded an optional `conflictCountFeedAvailable` override through buildResolutionSpec/attachResolutionSpec so the preserved #5010 horizon-commensurable threshold logic stays under test (the tests that lock it force the hard path); production emits judged by default. - GDELT article-volume is NOT a substitute: its scale is article count, not event count, so the thresholds would mis-resolve. Tests: conflict now judged with an escalation question; scoped-regression that unrest still emits hard; existing conflict-hard tests force the feed-available path (preserves #5010 coverage). 405 forecast tests green. Related: #5136, #5097, #5087 (judged resolver), #5091 (KPI reframe follow-up) Claude-Session: https://claude.ai/code/session_01StNurp4TGC3JLHbTtKJhbp
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Greptile SummaryThis PR reclassifies
Confidence Score: 4/5Safe to merge — the change converts an always-unresolvable path into a working judged path; no data is lost, and the hard-count path is fully preserved behind the flag. The option-threading, feed-guard fallback, and test coverage are all well-executed. The one forward-looking concern is that the conflict question template hardcodes escalation framing, which will misfire if a de-escalation detector is ever added to the conflict domain — but all current emitters are escalation-shaped so there is no present defect. The buildQuestion branch in scripts/_forecast-resolution.mjs (line 539) will need revisiting before any de-escalation or ceasefire-themed conflict detector is added. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[buildResolutionSpec] --> B{state_derived?}
B -- yes --> J[buildJudgedSpec]
B -- no --> C{prediction_market signal?}
C -- yes --> H[buildHardSpec / prediction_market]
C -- no --> D{JUDGED_DOMAINS?}
D -- yes --> J
D -- no --> E[resolveHardFamily]
E -- null --> J
E -- family --> F[buildHardSpec]
F --> G[deriveHardMetrics]
G -- conflict or ucdp_zone --> K{conflictCountFeedAvailable?}
K -- false default --> L[return null]
L --> J
K -- true override --> M[compute threshold]
M --> N[hard spec]
G -- other family --> N
J --> O[buildQuestion]
O -- domain == conflict --> P[escalation-framed question]
O -- other domain --> Q[generic resolve-YES question]
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
A[buildResolutionSpec] --> B{state_derived?}
B -- yes --> J[buildJudgedSpec]
B -- no --> C{prediction_market signal?}
C -- yes --> H[buildHardSpec / prediction_market]
C -- no --> D{JUDGED_DOMAINS?}
D -- yes --> J
D -- no --> E[resolveHardFamily]
E -- null --> J
E -- family --> F[buildHardSpec]
F --> G[deriveHardMetrics]
G -- conflict or ucdp_zone --> K{conflictCountFeedAvailable?}
K -- false default --> L[return null]
L --> J
K -- true override --> M[compute threshold]
M --> N[hard spec]
G -- other family --> N
J --> O[buildQuestion]
O -- domain == conflict --> P[escalation-framed question]
O -- other domain --> Q[generic resolve-YES question]
Reviews (1): Last reviewed commit: "fix(forecast): reclassify conflict/ucdp_..." | Re-trigger Greptile |
#5091) #5138 added a pending-ledger migration for conflict, but the unrest reclassification (this PR) only changed new-forecast emission — existing pending unrest count rows would stay stuck as unresolvable hard specs. Generalize the migration to cover both families. - migratePendingConflictCountEntryToJudged -> table-driven migratePendingCountEntryToJudged over UNAVAILABLE_COUNT_FEED_MIGRATIONS (conflict + unrest), each with its own flag gate + judged-question builder. - unrest question mirrors the generator-side political/instability phrasing. - Tests: new unrest-migration test; the two existing conflict+unrest ledger tests now assert unrest rows migrate to judged (they encoded the old unrest-resolves premise). 150 forecast tests green. Related: #5091, #5138 Claude-Session: https://claude.ai/code/session_01StNurp4TGC3JLHbTtKJhbp
* fix(forecast): reclassify unrest count specs to judged (#5091) The twin of #5136/#5138: unrest hard-count specs resolve against UNREST_COUNT_SOURCE_FEED (unrest:events-resolution:v1), which is EMPTY in prod — seed-unrest-events only writes it from an ACLED resolution fetch, and we have no ACLED credentials. So unrest counts were unresolvable, sitting pending/VOID forever, while inflating the nominal hard-ratio. - deriveHardMetrics: the `unrest` family returns null when the count feed is unavailable -> buildJudgedSpec (LLM judge, #5087). Gated by the new UNREST_COUNT_FEED_AVAILABLE flag (default false) + an `unrestCountFeedAvailable` override; the horizon-scaled threshold logic is preserved for re-enable. - buildQuestion: family-aware, instability-framed question for the political (unrest) domain, mirroring the conflict question. - Not GDELT-for-count: article volume != event count, so the thresholds would mis-resolve (same reason it was rejected for conflict). Tests: unrest now judged by default with an instability question; the scoped regression now uses cyber (a populated feed) as the still-hard exemplar; the unrest feed-mapping + R4 fixtures force the feed-available path. 404 forecast tests green. Related: #5091 (KPI reframe + empty-feed audit), #5136/#5138 (conflict template), #5087 (judged resolver), #4930 Claude-Session: https://claude.ai/code/session_01StNurp4TGC3JLHbTtKJhbp * test(forecast): address Greptile — assert horizon in unrest question + cover tally-null guard under override (#5142) - unrest judged test now asserts the horizon (7d) appears in the question, matching the conflict test's rigor (Greptile P2). - add a test for the unrest tally-null path under unrestCountFeedAvailable:true (a non-numeric count signal still falls back to judged) — the path the flag early-return shadowed by default (Greptile P2). Claude-Session: https://claude.ai/code/session_01StNurp4TGC3JLHbTtKJhbp * fix(forecast): migrate existing pending unrest count entries to judged (#5091) #5138 added a pending-ledger migration for conflict, but the unrest reclassification (this PR) only changed new-forecast emission — existing pending unrest count rows would stay stuck as unresolvable hard specs. Generalize the migration to cover both families. - migratePendingConflictCountEntryToJudged -> table-driven migratePendingCountEntryToJudged over UNAVAILABLE_COUNT_FEED_MIGRATIONS (conflict + unrest), each with its own flag gate + judged-question builder. - unrest question mirrors the generator-side political/instability phrasing. - Tests: new unrest-migration test; the two existing conflict+unrest ledger tests now assert unrest rows migrate to judged (they encoded the old unrest-resolves premise). 150 forecast tests green. Related: #5091, #5138 Claude-Session: https://claude.ai/code/session_01StNurp4TGC3JLHbTtKJhbp * test(forecast): lock unrest threshold scaling
Summary
Implements the #5136 recommendation: reclassify conflict / ucdp_zone count specs to
judged, since their hard-count feed (conflict:acled-resolution:v1) is empty without ACLED credentials — every conflict count spec was otherwise unresolvable (pending/VOID forever). Judged resolution is live (#5087).Why judged (not GDELT-count or UCDP)
CONFLICT_ESCALATION_RATIO(e.g. "≥66 events in-horizon"). GDELT article volume is a different scale (one event → dozens of articles), socount(articles) >= 66would false-YES constantly.count_source_lags_deadline(why Fix forecast hard resolution coverage #5072 moved off it).What changed (
scripts/_forecast-resolution.mjs)deriveHardMetrics: conflict/ucdp_zonereturn nullwhen the count feed is unavailable →buildHardSpecfalls back tobuildJudgedSpec. Gated byCONFLICT_COUNT_FEED_AVAILABLE(defaultfalse); the horizon-commensurable threshold logic (fix(forecast): resolution-spec emission amendment — horizon-commensurable count + resolvable supply/gps windows (Bet-1 follow-up, unblocks #5007) #5010) is preserved, not deleted — flip the flag to re-enable once a populated event-count feed exists.buildQuestion: family-aware, escalation-framed question for conflict forecasts.conflictCountFeedAvailableoverride throughbuildResolutionSpec/attachResolutionSpecs(a real config with a production default, and the seam that lets the preserved-hard-logic tests force the hard path — same pattern as_gdelt-fetch.mjs's seams).Tests
judgedwith an escalation/region-anchored question; scoped-regression that unrest still emits hard (change is conflict/ucdp_zone-only).{ conflictCountFeedAvailable: true }— so they keep validating the preserved logic.forecast-resolution58,forecast-history,forecast-detectors,forecast-resolution-eval,forecast-resolutions-seeder,forecast-scorecard, +.mtsscorecard/get-forecasts).Follow-ups (not in this PR)
pendingJudgeon the scorecard-watch and bumpFORECAST_RESOLUTION_JUDGE_MAX_PER_RUNif the backlog grows.get-forecastsshows conflict specs asjudged; scorecard shows them movingpending-judge → resolved.Related: #5136, #5097, #5087, #5091.
https://claude.ai/code/session_01StNurp4TGC3JLHbTtKJhbp