Skip to content

feat(forecast): simulation confidence sub-bar in ForecastPanel#2526

Merged
koala73 merged 1 commit intomainfrom
feat/forecast-sim-confidence-bar
Mar 29, 2026
Merged

feat(forecast): simulation confidence sub-bar in ForecastPanel#2526
koala73 merged 1 commit intomainfrom
feat/forecast-sim-confidence-bar

Conversation

@koala73
Copy link
Copy Markdown
Owner

@koala73 koala73 commented Mar 29, 2026

Summary

  • Adds three new Forecast proto fields: simulation_adjustment, sim_path_confidence, demoted_by_simulation (fields 20–22)
  • Implements Option D visual design in ForecastPanel.ts: a thin 2px colored underbar below each forecast title that encodes simulation evidence without adding columns or visible text at rest
  • Passes through sim fields in buildPublishedForecastPayload (zero overhead — all fields default to 0/false when absent)

Visual design

The sub-bar is deliberately quiet:

  • At rest: 2px colored bar, opacity 0.45 — barely noticeable; just a hint of signal
  • On hover: bar goes to 0.9 opacity + a plain-language text label appears below it
  • Bar width encodes sim-path confidence for positive adjustments (min 20%), 100% for structural negative adjustments
  • Color encodes direction: green (AI-backed, ≥0.70 conf), amber (backed, <0.70), orange (contested), red (demoted)
  • Demoted rows dim to opacity 0.5 in addition to the red bar

Hover labels use plain language — no "sim" jargon:

  • AI signal · +8%
  • AI signal (moderate) · +12%
  • AI caution · −12%
  • AI flag: dropped · −15%

Data plumbing

The rendering is fully implemented and typecheck-clean. The chip will not show in production until the ExpandedPath → Forecast plumbing lands. That follow-up requires:

  1. applyPostSimulationRescore to write sim decorations back to the published Redis forecast key (via stateUnit.forecastIdscandidateStateId mapping)
  2. The sim fields (simulationAdjustment, simPathConfidence, demotedBySimulation) to be populated on pred objects before buildPublishedForecastPayload runs

This PR adds the proto schema and UI code as the foundation. The plumbing is the next step.

Testing

  • node --test tests/forecast-trace-export.test.mjs → 272/272 pass (no changes to test suite needed)
  • npm run typecheck → clean
  • Pre-push hook: typecheck, typecheck:api, test:data, boundaries, proto regeneration check — all pass

Post-Deploy Monitoring & Validation

  • No operational impact at deploy time: new proto fields default to 0/false; no existing forecast data is modified; the sub-bar renders nothing until plumbing lands
  • No additional operational monitoring required: additive optional fields with zero-value defaults

Add three fields to Forecast proto (simulation_adjustment,
sim_path_confidence, demoted_by_simulation) and implement a
thin colored underbar below each forecast title that encodes
simulation evidence without adding columns or text clutter.

Visual design (Option D):
- 2px colored bar, width = sim path confidence for positive adj,
  100% for negative/demoted (structural, not confidence-dependent)
- Green ≥0.70 conf, amber <0.70, orange negative, red demoted
- Opacity 0.45 at rest; 0.9 + text label on hover
- Plain language hover labels: "AI signal · +8%", "AI caution · −12%",
  "AI flag: dropped · −15%" — no "sim" jargon visible to users
- Demoted rows dim to opacity 0.5

Passes through simulation fields in buildPublishedForecastPayload.
No chip renders until the ExpandedPath → Forecast plumbing lands
(follow-up PR); all rendering code is ready and typecheck clean.

🤖 Generated with Claude Sonnet 4.6 via Claude Code + Compound Engineering v2.49.0

Co-Authored-By: Claude Sonnet 4.6 (200K context) <[email protected]>
@mintlify
Copy link
Copy Markdown

mintlify bot commented Mar 29, 2026

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
WorldMonitor 🟢 Ready View Preview Mar 29, 2026, 6:53 PM

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 29, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
worldmonitor Ignored Ignored Mar 29, 2026 6:52pm

Request Review

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Mar 29, 2026

Greptile Summary

This PR adds three simulation-scoring proto fields (simulation_adjustment, sim_path_confidence, demoted_by_simulation) to the Forecast message and wires them end-to-end: proto → generated stubs → seed pass-through → ForecastPanel UI. The centrepiece is a deliberately unobtrusive 2px coloured underbar ("Option D") rendered below each forecast title that encodes simulation evidence via colour (green/amber/orange/red) and width (confidence-proportional for positive signals, 100% for structural negatives), with a plain-language text label that only appears on hover. The feature is intentionally dormant in production until a follow-up PR writes sim decorations back to the published Redis forecast key.

Key changes:

  • proto/: fields 20-22 added to Forecast; numbering is clean (field 19 was the pre-existing feed_summary)
  • src/generated/: both client and server stubs regenerated correctly as required (non-optional) fields
  • scripts/seed-forecasts.mjs: zero-cost pass-through with safe 0/false defaults
  • src/components/ForecastPanel.ts: new renderSimBar() helper + CSS; fc-prob-label promoted from <span> to <div> to contain the block-level bar

Issues found:

  • simPathConfidence ?? 1.0 fallback should be ?? 0 to match proto's "0 = not set" sentinel; using 1.0 would misrepresent missing data as high confidence for any Forecast constructed outside the proto pipeline
  • opacity: 0.5 on fc-prob-row for demoted rows stacks multiplicatively with the sim-bar's CSS opacity, capping hover opacity at 0.45 instead of 0.9 and dimming the "AI flag: dropped" label the user most needs to read
  • Toggling fc-sim-label from display: none to display: block on hover will cause a row-height reflow in the align-items: center grid, producing visible layout jitter

Confidence Score: 5/5

Safe to merge — the sub-bar is a no-op until the plumbing PR lands, and all three findings are P2 visual/style improvements

All remaining findings are P2: the fallback default requires undefined (not zero) to misfire, the opacity stacking affects only the visual polish of demoted rows (which won't appear in production yet), and the layout-shift is a UX improvement suggestion. No P0 or P1 issues found.

src/components/ForecastPanel.ts — three style/visual issues in renderSimBar and CSS rules worth fixing before the plumbing PR activates the feature

Important Files Changed

Filename Overview
src/components/ForecastPanel.ts Core UI change: adds renderSimBar() and integrates sim sub-bar into renderProbRow(); three style issues noted (fallback default, opacity stacking, layout shift)
proto/worldmonitor/forecast/v1/forecast.proto Adds fields 20-22 (simulation_adjustment, sim_path_confidence, demoted_by_simulation) to Forecast message; field numbering is correct (field 19 is the pre-existing feed_summary)
scripts/seed-forecasts.mjs Adds zero-safe pass-through of sim fields in buildPublishedForecastPayload; defaults to 0/false when absent, no behavioral change until plumbing lands
src/generated/client/worldmonitor/forecast/v1/service_client.ts Proto-regenerated client stub adds three required (non-optional) fields to the Forecast interface; consistent with proto definition
src/generated/server/worldmonitor/forecast/v1/service_server.ts Proto-regenerated server stub adds same three required fields to Forecast interface; mirrors client change
docs/api/ForecastService.openapi.yaml Adds simulationAdjustment, simPathConfidence, demotedBySimulation fields to the Forecast schema in the human-readable YAML spec
docs/api/ForecastService.openapi.json Minified JSON OpenAPI spec regenerated to include the same three new Forecast fields; consistent with YAML counterpart

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[proto Forecast message\nfields 20-22 added] --> B[buf generate]
    B --> C[service_client.ts\nForecast interface]
    B --> D[service_server.ts\nForecast interface]
    C --> E[ForecastPanel.ts\nrenderProbRow]
    D --> F[seed-forecasts.mjs\nbuildPublishedForecastPayload]
    F --> G[(Redis published\nforecast key)]
    G -->|plumbing PR future| H[applyPostSimulationRescore\nwrites sim decorations]
    H --> G
    G --> I[GetForecasts API]
    I --> E
    E --> J{simulationAdjustment != 0?}
    J -- Yes --> K[renderSimBar]
    K --> L{demotedBySimulation?}
    L -- Yes --> M[Red bar 100% AI flag: dropped]
    L -- No --> N{adj > 0?}
    N -- Yes conf>=0.70 --> O[Green bar conf*100% width]
    N -- Yes conf<0.70 --> P[Amber bar conf*100% width]
    N -- No --> Q[Orange bar 100% AI caution]
    J -- No --> R[No bar rendered]
Loading

Reviews (1): Last reviewed commit: "feat(forecast): simulation confidence su..." | Re-trigger Greptile

const adj = f.simulationAdjustment ?? 0;
if (adj === 0) return '';

const conf = f.simPathConfidence ?? 1.0;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 simPathConfidence fallback conflicts with proto "0 = not set" semantics

The proto comment explicitly documents 0 = not set for sim_path_confidence. Using ?? 1.0 as the fallback means that if simPathConfidence arrives as undefined at runtime (e.g. a Forecast object constructed without this field, or data in-flight before the plumbing PR lands), the bar will render as a full-width green "AI signal" — the exact opposite of "not set."

The correct fallback is 0, which matches the proto sentinel and falls through to the amber/moderate branch with minimum bar width (20%):

Suggested change
const conf = f.simPathConfidence ?? 1.0;
const conf = f.simPathConfidence ?? 0;

With proto3 wire encoding this ?? 0 is dead code for well-formed responses, but it makes the JS fallback safe for any Forecast object that omits the field.

<div class="fc-prob-row">
<span class="fc-prob-label"
style="border-left:2px solid ${catColor}47;padding-left:6px">
<div class="fc-prob-row"${demoted ? ' style="opacity:0.5"' : ''}>
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Demoted-row opacity: 0.5 stacks with fc-sim-bar opacity, defeating hover intent

CSS opacity is applied multiplicatively through the DOM tree. Setting opacity: 0.5 on fc-prob-row (this line) means the fc-sim-bar inside it can only reach 0.5 × 0.9 = 0.45 effective opacity on hover — not the 0.9 the CSS rule targets. More critically, the .fc-sim-label text that is supposed to become fully legible on hover also inherits the 0.5 dimming, keeping it at half readability.

For demoted forecasts this is most important — the "AI flag: dropped" label is the signal users most need to read.

One approach is to apply the dimming to the title text only (rather than the whole row), leaving the sim bar and label at full stacking opacity:

Suggested change
<div class="fc-prob-row"${demoted ? ' style="opacity:0.5"' : ''}>
<div class="fc-prob-row">
<div class="fc-prob-label"
style="border-left:2px solid ${catColor}47;padding-left:6px${demoted ? ';opacity:0.5' : ''}">

Alternatively, move the dimming to the individual text/bar cells so the sim-bar-wrap can opt out.

Comment on lines +210 to +211
.fc-sim-label { font-size: 9px; display: none; margin-top: 2px; line-height: 1.2; }
.fc-prob-item:hover .fc-sim-label { display: block; }
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Hover label reveal causes row height jump

.fc-prob-row is a CSS Grid with align-items: center. When .fc-sim-label switches from display: none to display: block on hover, the first grid column (.fc-prob-label) grows taller, re-centering all other columns (probability bar, trend, domain tag) to the new row height. This produces a visible layout shift and can make adjacent cells feel jittery, particularly noticeable when quickly scanning forecasts.

Consider visibility: hidden + height: 1.2em instead of toggling display, so the row height is always reserved:

Suggested change
.fc-sim-label { font-size: 9px; display: none; margin-top: 2px; line-height: 1.2; }
.fc-prob-item:hover .fc-sim-label { display: block; }
.fc-sim-label { font-size: 9px; visibility: hidden; height: 1.2em; margin-top: 2px; line-height: 1.2; }
.fc-prob-item:hover .fc-sim-label { visibility: visible; }

This pre-allocates the space even when the label is hidden and avoids the reflow.

@koala73 koala73 merged commit dca83bb into main Mar 29, 2026
9 checks passed
@koala73 koala73 deleted the feat/forecast-sim-confidence-bar branch March 29, 2026 19:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant