Skip to content

feat(highcharts): implement timeseries-forecast-uncertainty#7400

Merged
MarkusNeusinger merged 5 commits into
mainfrom
implementation/timeseries-forecast-uncertainty/highcharts
May 19, 2026
Merged

feat(highcharts): implement timeseries-forecast-uncertainty#7400
MarkusNeusinger merged 5 commits into
mainfrom
implementation/timeseries-forecast-uncertainty/highcharts

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

Implementation: timeseries-forecast-uncertainty - python/highcharts

Implements the python/highcharts version of timeseries-forecast-uncertainty.

File: plots/timeseries-forecast-uncertainty/implementations/python/highcharts.py

Parent Issue: #3188


🤖 impl-generate workflow

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 19, 2026

AI Review - Attempt 1/3

Image Description

Light render (plot-light.png): The plot renders on a warm off-white #FAF8F1 background. The title appears in the top-left in dark ink — rendered as "timeseries-forecast-uncertainty · python · highcharts · anyplot.ai" with a subtitle "Monthly product demand — 3-year history with 12-month ARIMA forecast" beneath it. The Y-axis label "Product Demand (Units)" is rotated and visible on the left in dark text. The X-axis label "Date" appears at the bottom. Monthly tick labels run from Jan 2022 through Nov 2025 — dense but legible. The historical data (Jan 2022 – Dec 2024) is rendered as a solid green (#009E73) line with circular markers, showing a clear upward trend with seasonal oscillations. At Jan 2025, a vertical orange line marks the forecast start with a "Forecast Start" label at the top. The forecast region shows a dashed orange (#D55E00) line with widening nested confidence bands: a darker blue (#0072B2) for 80% CI and a lighter sky-blue (#56B4E9) for 95% CI. All text is readable against the light background. Legibility verdict: PASS.

Dark render (plot-dark.png): The same chart on a warm near-black #1A1A17 background. Title and axis label text flips to light-colored (#F0EFE8 / #B8B7B0) and is clearly readable against the dark surface. The green historical line (#009E73) and orange forecast dashed line are identical to the light render — data colors preserved correctly. The 80% CI band remains a visible blue against the dark background. The 95% CI band, however, uses a different color in dark mode (#87CEEB instead of the light-mode #56B4E9) — this violates the rule that data colors must be identical across themes. Additionally, the 95% CI band renders as an extremely faint brownish-dark tint at 15% fill opacity on the near-black surface, making it barely distinguishable. No dark-on-dark text failures observed. Legibility verdict: PASS for text; PARTIAL FAIL for 95% CI band visibility and color change.

Score: 81/100

Category Score Max
Visual Quality 24 30
Design Excellence 12 20
Spec Compliance 14 15
Data Quality 15 15
Code Quality 9 10
Library Mastery 7 10
Total 81 100

Visual Quality (24/30)

  • VQ-01: Text Legibility (6/8) — Font sizes explicitly set throughout (28px title, 22px axis labels, 18px tick labels); readable in both themes. Canvas is non-standard 4800×2700 (should be 3200×1800); font scaling is proportionally acceptable but not per spec.
  • VQ-02: No Overlap (5/6) — 48 monthly x-axis tick labels are dense but not overlapping; minor concern about label crowding.
  • VQ-03: Element Visibility (5/6) — Historical and forecast lines are clear; 95% CI band is barely distinguishable in dark mode at 15% opacity with a different color.
  • VQ-04: Color Accessibility (2/2) — Green/orange/blue combination is CVD-safe and high-contrast.
  • VQ-05: Layout & Canvas (3/4) — Good proportions; historical section (~75% width) and forecast section (~25%) are appropriately balanced.
  • VQ-06: Axis Labels & Title (2/2) — "Product Demand (Units)" includes units; "Date" is descriptive.
  • VQ-07: Palette Compliance (1/2) — First series (historical) correctly uses #009E73. Forecast uses Okabe-Ito Add Claude Code GitHub Workflow #2 (#D55E00) ✓. 80% CI uses #0072B2 ✓. Violation: 95% CI color changes between themes (#56B4E9 light → #87CEEB dark); data colors must be identical across themes.

Design Excellence (12/20)

  • DE-01: Aesthetic Sophistication (5/8) — Custom Okabe-Ito palette, full theme adaptation (INK/INK_SOFT/GRID tokens), nested CI bands with appropriate transparency, and visual distinction between solid historical and dashed forecast series. Above library defaults but not publication-level.
  • DE-02: Visual Refinement (3/6) — Subtle 10%-opacity grid lines, generous spacing parameters (spacingTop: 60, spacingBottom: 120), theme-adaptive colors throughout. Some refinement present but overall still follows standard Highcharts layout.
  • DE-03: Data Storytelling (4/6) — Clear visual hierarchy: green historical vs. orange forecast creates immediate distinction. Widening CI bands effectively communicate increasing uncertainty over time. Vertical forecast start line is a strong focal point guiding the viewer.

Spec Compliance (14/15)

  • SC-01: Plot Type (5/5) — Correct: time series with historical + forecast + nested confidence bands.
  • SC-02: Required Features (4/4) — All required: solid historical line, dashed forecast line, vertical forecast-start marker, 80% CI band, 95% CI band, semi-transparent fills, legend enabled.
  • SC-03: Data Mapping (3/3) — Date on X-axis, Product Demand (Units) on Y-axis; all data visible.
  • SC-04: Title & Legend (2/3) — The code title is "timeseries-forecast-uncertainty · highcharts · anyplot.ai"missing the "python" language token required by the {spec-id} · {language} · {library} · anyplot.ai format. The rendered images appear to show the correct format (with "python"), suggesting a code regression. Legend is enabled in code but positioning/visibility in the final PNG is unclear.

Data Quality (15/15)

  • DQ-01: Feature Coverage (6/6) — Shows all aspects: trend + seasonality + noise in historical; continuation with widening CI bands in forecast; clear transition point.
  • DQ-02: Realistic Context (5/5) — Monthly product demand with 3-year history and 12-month ARIMA forecast is a real, neutral, business scenario.
  • DQ-03: Appropriate Scale (4/4) — Values 80–225 units plausible; +50 unit trend over 3 years and seasonal oscillations of ±20 units are realistic; CI widening is proportionally correct.

Code Quality (9/10)

  • CQ-01: KISS Structure (3/3) — Linear Imports → Data → Chart config → Series → Save structure; no functions or classes.
  • CQ-02: Reproducibility (2/2) — np.random.seed(42)
  • CQ-03: Clean Imports (2/2) — All imports are used.
  • CQ-04: Code Elegance (1/2) — The CDN download with fallback retry logic is duplicated verbatim for both highcharts.js and highcharts-more.js; could be a one-liner function, but KISS rule discourages it — acceptable verbosity given the context, but slightly messy.
  • CQ-05: Output & API (1/1) — Saves plot-{THEME}.png and plot-{THEME}.html ✓; current Highcharts 11 API used.

Library Mastery (7/10)

  • LM-01: Idiomatic Usage (4/5) — Correct use of Chart(container="container"), AreaRangeSeries for CI bands (requires highcharts-more), plotLines for the forecast start marker, datetime x-axis type, to_js_literal() for HTML generation. Good idiomatic Highcharts Python usage.
  • LM-02: Distinctive Features (3/5) — AreaRangeSeries from highcharts-more is a Highcharts-specific feature for confidence bands; plotLines with labeled markers is Highcharts-native; dateTimeLabelFormats for x-axis formatting; z_index ordering for overlapping series — several Highcharts-distinctive features are used.

Score Caps Applied

  • None — all major criteria are non-zero; DE-01=5 and DE-02=3, no DE cap triggered.

Strengths

  • First series (historical) correctly uses brand green #009E73 as Okabe-Ito position 1.
  • Full theme adaptation: INK/INK_SOFT/GRID/PAGE_BG tokens wired to all chart elements in both themes.
  • Good data storytelling: widening CI bands visually communicate increasing forecast uncertainty; green vs. orange color contrast clearly separates historical from forecast.
  • AreaRangeSeries from highcharts-more used idiomatically for confidence interval bands.
  • Realistic, neutral product-demand scenario with plausible trend, seasonality, and noise.
  • plotLines with a labeled "Forecast Start" vertical marker is an effective and Highcharts-native technique.

Weaknesses

  • 95% CI color changes between themes (#56B4E9 in light → #87CEEB in dark): data colors must be identical across themes. Fix: use "#56B4E9" unconditionally for the 95% CI series color; only chrome (backgrounds, text, grid) should vary.
  • 95% CI band barely visible in dark mode: fill_opacity=0.15 with a light color on #1A1A17 produces an almost invisible tint. Increase to fill_opacity=0.25 in dark mode, or use a slightly more opaque color.
  • Code title missing "python": chart.options.title["text"] is "timeseries-forecast-uncertainty · highcharts · anyplot.ai" but must be "timeseries-forecast-uncertainty · python · highcharts · anyplot.ai".
  • Non-standard canvas size: 4800×2700 px is not in the allowed formats (3200×1800 landscape or 2400×2400 square). Resize to 3200×1800 and adjust font sizes accordingly (18px title, 14px axis labels, 12px tick labels).
  • Legend positioning unclear: Highcharts places the legend at chart bottom by default; verify it is visible and not cut off in the final PNG by checking spacingBottom is sufficient.

Issues Found

  1. VQ-07 PARTIAL / SC-04 PARTIAL: 95% CI color changes between themes — data series colors must be theme-invariant.
    • Fix: Remove the conditional "#56B4E9" if THEME == "light" else "#87CEEB" and use "#56B4E9" unconditionally.
  2. SC-04 CODE BUG: Title missing "python" language token.
    • Fix: Change to "timeseries-forecast-uncertainty · python · highcharts · anyplot.ai".
  3. VQ-03 DARK: 95% CI band nearly invisible at dark theme.
    • Fix: Increase ci_95_series.fill_opacity to 0.25 (same as 80% CI), or adjust for dark theme.
  4. VQ-01 / CQ-05: Non-standard canvas 4800×2700.
    • Fix: Use 3200×1800 with proportionally scaled font sizes (18px title, 14px labels, 12px ticks).

AI Feedback for Next Attempt

Fix the four issues in priority order: (1) use "#56B4E9" unconditionally for the 95% CI color so both themes are identical; (2) increase 95% CI fill_opacity to 0.25 in dark mode so it's visible; (3) fix the title to include "python": "timeseries-forecast-uncertainty · python · highcharts · anyplot.ai"; (4) resize canvas from 4800×2700 to the standard 3200×1800 and scale font sizes to 18px/14px/12px. Optionally add a Highcharts subtitle for the "Monthly product demand — 3-year history…" context line.

Verdict: REJECTED

@github-actions github-actions Bot added quality:81 Quality score 81/100 ai-rejected Quality not OK, triggers update labels May 19, 2026
@github-actions github-actions Bot added ai-attempt-1 First repair attempt and removed ai-rejected Quality not OK, triggers update labels May 19, 2026
…rtainty

Attempt 1/3 - fixes based on AI review
@github-actions
Copy link
Copy Markdown
Contributor Author

🔧 Repair Attempt 1/4

Applied fixes based on AI review feedback.

Status: Repair completed, re-triggering review...


🤖 impl-repair

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 19, 2026

AI Review - Attempt 2/3

Image Description

Light render (plot-light.png): Warm off-white background (#FAF8F1). Title "timeseries-forecast-uncertainty · python · highcharts · anyplot.ai" in dark ink at top — readable and appropriately sized. Historical solid green line (#009E73) with circular markers spans the left 75% of the chart, showing clear seasonal cycles and an upward trend. A dashed vertical line marks the forecast start with a "Forecast Start" label at the top. In the forecast period, two nested semi-transparent bands appear: a wider light-blue (#56B4E9) outer band (95% CI) and a narrower blue (#0072B2) inner band (80% CI), both widening convincingly over time. The orange dashed forecast line (#D55E00) with diamond markers runs through the center of the bands. Y-axis label "Product Demand (Units)" and X-axis label "Date" in dark ink. Legend present at bottom with four series labels. Subtle horizontal grid lines. All text is clearly readable against the light background.

Dark render (plot-dark.png): Near-black background (#1A1A17). Title renders in light off-white text — fully readable against the dark surface. The green historical line (#009E73) is identical in color to the light render, standing out well against the dark background. The orange dashed forecast (#D55E00) is equally visible. The CI bands render as layered dark-steel blues on the dark background — the outer 95% band is a lighter shade, the inner 80% slightly darker, visually distinct though with less contrast than in the light render. The dashed vertical forecast-start line and its label remain readable in light text. Tick labels and axis labels render in the appropriate soft light tone (#B8B7B0). No dark-on-dark failures detected. Grid lines are very subtle (10% opacity) — appropriate for dark mode. Legend visible at bottom. All text readable against the dark background.

Both paragraphs confirm: data colors are identical between themes; only chrome (background, text, grid) flips. Legibility verdict: PASS for both renders.

Score: 87/100

Category Score Max
Visual Quality 27 30
Design Excellence 13 20
Spec Compliance 15 15
Data Quality 15 15
Code Quality 9 10
Library Mastery 8 10
Total 87 100

Visual Quality (27/30)

  • VQ-01: Text Legibility (7/8) — All font sizes explicitly set (title 18px, axes 14px, ticks/legend 12px); all text readable in both themes. Minor deduction: at thumbnail resolution the legend text at the bottom edge is barely visible — may be at the canvas boundary depending on full-resolution layout.
  • VQ-02: No Overlap (6/6) — No text or data element collisions in either render.
  • VQ-03: Element Visibility (5/6) — Historical and forecast lines are well-sized for ~36/12 data points (lineWidth 4, marker radius 5). CI bands are distinct in light mode; slightly less visually differentiated in dark mode (both render as dark-steel blue layers), but still discernible.
  • VQ-04: Color Accessibility (2/2) — Okabe-Ito palette; line style also differentiates historical (solid) from forecast (dashed), not relying on color alone.
  • VQ-05: Layout & Canvas (3/4) — Chart fills canvas well with generous spacing. Minor deduction: legend at bottom is near the edge; at full canvas size the 100px spacingBottom should be adequate, but this is a borderline concern.
  • VQ-06: Axis Labels & Title (2/2) — Y-axis "Product Demand (Units)" includes units; X-axis "Date" is descriptive; title format correct.
  • VQ-07: Palette Compliance (2/2) — Historical series (primary) uses brand green #009E73; forecast uses #D55E00 (Okabe-Ito position 2); CI bands use blue-family Okabe-Ito colors (#0072B2, #56B4E9). Background #FAF8F1 (light) / #1A1A17 (dark) correct. All chrome tokens (INK, INK_SOFT, GRID) properly theme-adapted.

Design Excellence (13/20)

  • DE-01: Aesthetic Sophistication (5/8) — Above "well-configured library default." The color contrast between the solid green historical line and the widening blue CI bands is visually compelling. Custom spacing, explicit typography hierarchy, and the distinct forecast-start marker show design intent. Not quite FiveThirtyEight-level polish.
  • DE-02: Visual Refinement (4/6) — Subtle grid (10% opacity), clean Highcharts layout without heavy box frames, generous whitespace via custom spacing parameters. Top/right spines not explicitly removed, but Highcharts' default line chart layout is relatively minimal here.
  • DE-03: Data Storytelling (4/6) — The plot narrates a clear progression: historical trend → forecast start marker → widening uncertainty fan. The visual widening of CI bands communicates growing uncertainty over time without needing annotations. Above the default "data displayed but no interpretation" level.

Spec Compliance (15/15)

  • SC-01: Plot Type (5/5) — Correct: LineSeries + AreaRangeSeries combination for time-series forecast with confidence bands.
  • SC-02: Required Features (4/4) — Solid historical line ✓; dashed forecast line ✓; vertical plotLine marking forecast start with label ✓; nested 80% and 95% CI bands ✓; semi-transparent fills (fill_opacity 0.25) ✓; legend enabled ✓.
  • SC-03: Data Mapping (3/3) — X-axis datetime, Y-axis demand. Historical in left portion, forecast bands in right portion only. CI bands correctly cover only the forecast period.
  • SC-04: Title & Legend (3/3) — Title is exactly timeseries-forecast-uncertainty · python · highcharts · anyplot.ai. Legend labels "95% Confidence Interval", "80% Confidence Interval", "Historical (Actual)", "Forecast" match the spec.

Data Quality (15/15)

  • DQ-01: Feature Coverage (6/6) — Shows all aspects: seasonal oscillation in history, upward trend, widening CI bands, clear forecast start, realistic ARIMA-style forecast continuing the seasonal pattern.
  • DQ-02: Realistic Context (5/5) — Monthly product demand over 3 years with 12-month forecast. Real business scenario. Neutral content.
  • DQ-03: Appropriate Scale (4/4) — Demand range ~90–180 units with ±10–54 unit uncertainty bands is physically plausible for monthly product demand. CI widening from ±10 (short-term) to ±54 (long-term) is realistic for forecast uncertainty.

Code Quality (9/10)

  • CQ-01: KISS Structure (2/3) — _download_js helper function breaks the strict "no functions" rule, though it serves a practical purpose (CDN fallback with retry logic).
  • CQ-02: Reproducibility (2/2) — np.random.seed(42) set.
  • CQ-03: Clean Imports (2/2) — All imports used.
  • CQ-04: Code Elegance (2/2) — Clean, no fake interactivity, appropriate Highcharts object-attribute configuration style.
  • CQ-05: Output & API (1/1) — Saves plot-{THEME}.png and plot-{THEME}.html ✓.

Library Mastery (8/10)

  • LM-01: Idiomatic Usage (4/5) — Uses highcharts_core's OO API correctly: AreaRangeSeries, LineSeries, HighchartsOptions. Series-level attribute setting is idiomatic. to_js_literal() for HTML generation is the correct pattern.
  • LM-02: Distinctive Features (4/5) — Uses Highcharts-specific plotLines for the forecast start marker (a genuinely Highcharts-distinctive feature not easily replicated in matplotlib/plotly). AreaRangeSeries for CI bands, shared: True tooltip, and zIndex layer ordering are all Highcharts-native capabilities used well.

Score Caps Applied

  • None applied (DE-01=5 > 2, DE-02=4 > 2; no other triggers).

Strengths

  • plotLines for the forecast start marker is an elegant Highcharts-native solution — the label + dashed style combo is precisely what this feature is designed for.
  • Theme adaptation is thorough: all INK/INK_SOFT/GRID/PAGE_BG tokens are applied to every chrome element, ensuring both renders are correct.
  • Data storytelling is above average — the widening CI band fan visually encodes growing uncertainty without any annotations.
  • 36-point historical + 12-point forecast data is well-proportioned and representative of a real ARIMA/Prophet-style output.
  • CDN fallback with retry logic ensures robust JS download in restricted environments.

Weaknesses

  • _download_js helper function breaks KISS (CQ-01 -1). Inline the retry loop directly.
  • CI bands are slightly less visually distinct in dark mode — both render as layered dark-steel blues. Consider adding a slight opacity difference or border to improve differentiation.
  • Legend is at the bottom edge; increase spacingBottom slightly (e.g., 120–140px) to ensure legend is never clipped at full canvas size.
  • CI band colors use non-sequential Okabe-Ito positions (positions 6 and 3, in that order rather than 3 and 4). For CI bands that visually "belong together," using the two adjacent blue tones (positions 3 and 6) is reasonable, but reordering to positions 3 (#0072B2) and 6 (#56B4E9) with 95% being lighter (#56B4E9, outer band) is already correct — just note the series-add order doesn't match Okabe-Ito positional order.

Issues Found

  1. CQ-01 MINOR: _download_js function breaks flat KISS structure.
    • Fix: Inline the download + retry logic directly before chart creation.
  2. VQ-03 / DE-02 MINOR: CI bands less distinct in dark mode.
    • Fix: Increase fill_opacity slightly for dark theme (e.g., 0.35 dark vs 0.25 light), or add a faint border line to each CI band.

AI Feedback for Next Attempt

Implementation is solid. Primary improvements: (1) inline _download_js to restore KISS structure; (2) slightly increase CI band opacity in dark theme for better visual differentiation; (3) increase spacingBottom to 130px to guarantee legend is never at the canvas edge.

Verdict: APPROVED

@github-actions github-actions Bot added quality:87 Quality score 87/100 ai-approved Quality OK, ready for merge and removed quality:81 Quality score 81/100 labels May 19, 2026
@MarkusNeusinger MarkusNeusinger merged commit 15b4fae into main May 19, 2026
@MarkusNeusinger MarkusNeusinger deleted the implementation/timeseries-forecast-uncertainty/highcharts branch May 19, 2026 13:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-approved Quality OK, ready for merge ai-attempt-1 First repair attempt quality:87 Quality score 87/100

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant