Skip to content

feat(muix): implement streamgraph-basic - #10130

Merged
MarkusNeusinger merged 6 commits into
mainfrom
implementation/streamgraph-basic/muix
Aug 5, 2026
Merged

feat(muix): implement streamgraph-basic#10130
MarkusNeusinger merged 6 commits into
mainfrom
implementation/streamgraph-basic/muix

Conversation

@github-actions

@github-actions github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Implementation: streamgraph-basic - javascript/muix

Implements the javascript/muix version of streamgraph-basic.

File: plots/streamgraph-basic/implementations/javascript/muix.tsx

Parent Issue: #856


🤖 impl-generate workflow

@claude

claude Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

AI Review - Attempt 1/3

Image Description

Light render (plot-light.png): A genuine streamgraph on a #FAF8F1 background — measured pixel value (250,248,241), matching spec exactly. Six genres (Pop, Hip-Hop, Electronic, Rock, Jazz, Classical) render as smoothly-curved, wiggle-offset stacked bands from Jan 2024 to Nov 2025, using the canonical Imprint order (green/lavender/blue/ochre/red/cyan). Title "Genre Streaming Hours · streamgraph-basic · javascript · muix · anyplot.ai" sits centered at top in dark ink, fully readable, with generous whitespace above it. A horizontal legend below the title uses matching swatches and dark text. X-axis shows every third month ("Jan 2024", "Apr 2024", …) in dark text under a thin baseline; no y-axis is drawn (intentional for a streamgraph, since the wiggled baseline makes absolute y-values meaningless). All chrome text is dark-on-light and legible except the very first x-axis tick label, "Jan 2024" — its leading "J" is visibly chopped at the left canvas edge (ink pixels sampled at x=0, the true image boundary). Legibility verdict: FAIL for that one label; PASS elsewhere.

Dark render (plot-dark.png): Same composition on a #1A1A17 background — measured pixel value (26,26,23), exact match. Data colors are pixel-identical to the light render (only the chrome flipped): title and legend text are now light/off-white, x-axis tick labels are light-colored and clearly legible against the near-black surface, no dark-on-dark failures anywhere. The same "Jan 2024" leftmost tick label is clipped identically at the left edge (the "J" is missing its leftmost strokes in this render too), confirming the clipping is a layout issue independent of theme. Legibility verdict: FAIL for the leftmost tick label (same edge-clipping issue); PASS elsewhere.

Both renders were opened and visually inspected, including pixel-level zooms of the corners and edges.

Score: 0/100

AR-09 (Edge Clipping) triggered — Auto-Reject. The leftmost x-axis tick label "Jan 2024" has its "J" glyph clipped at the canvas's left edge in both the light and dark renders (verified via pixel sampling: ink present at x=0, the literal image boundary, with no white margin before it — consistent with the glyph originating at a negative x-coordinate and being chopped by the canvas bounds, not merely rendered close to the edge). Per the quality-criteria AR-09 rule, this forces Score = 0 regardless of the categories below.

The category breakdown below is still scored honestly for repair-loop signal, per the "score the rest, don't inflate VQ-05" precedent used for the canvas-dimension gate — it is not the final score.

Category Score Max
Visual Quality 29 30
Design Excellence 14 20
Spec Compliance 14 15
Data Quality 14 15
Code Quality 10 10
Library Mastery 10 10
Total (pre-AR-09) 91 100
Final (AR-09 applied) 0 100

Visual Quality (29/30)

  • VQ-01: Text Legibility (8/8) — Title fontsize computed dynamically (Math.max(15, round(22 * min(1, 67/title.length)))), tick labels and legend explicitly set to 14px. Well-proportioned, no overflow beyond the one edge-clipped label (scored separately under AR-09).
  • VQ-02: No Overlap (6/6) — Legend, title, and sparse (every-3rd-month) tick labels never collide with each other or the data.
  • VQ-03: Element Visibility (6/6) — Six stacked bands are all clearly visible and proportioned to their (varying) magnitudes; 0.92 fill opacity + 1.5px stroke keeps adjacent layers distinct.
  • VQ-04: Color Accessibility (2/2) — Adjacent-layer hues (e.g. cyan/blue, ochre/red) stay distinguishable; thin light strokes between bands aid separation.
  • VQ-05: Layout & Canvas (4/4) — Stream fills ~74% of vertical space, near-full width (24px margins), legend sits directly under the title, balanced whitespace.
  • VQ-06: Axis Labels & Title (1/2) — No explicit x-axis title text ("Month"/"Time") and no y-axis at all (intentional, since the wiggle-offset baseline makes absolute values non-meaningful) — tick date labels are self-descriptive but there's no formal axis label.
  • VQ-07: Palette Compliance (2/2) — First series #009E73, remaining 5 in canonical Imprint order, identical across themes; backgrounds pixel-verified at exactly #FAF8F1 / #1A1A17.

Design Excellence (14/20)

  • DE-01: Aesthetic Sophistication (6/8) - Genuine streamgraph shape (not a default stacked-area look), smooth catmullRom curves, deliberate opacity/stroke tuning — clearly above a configured default, short of full publication polish.
  • DE-02: Visual Refinement (4/6) - Grid off, y-axis hidden, tick marks removed, clean legend row; margins could be a touch more generous.
  • DE-03: Data Storytelling (4/6) - Band-width variation visually communicates genre trends (Hip-Hop/Electronic swelling, Jazz/Classical thinning) without needing annotations — genuine implicit hierarchy, though no explicit callout on the key insight.

Spec Compliance (14/15)

  • SC-01: Plot Type (5/5) - Correct: true streamgraph via stacked area with stackOrder: "insideOut" + stackOffset: "wiggle" (verified: these map directly to d3-shape's stackOrderInsideOut/stackOffsetWiggle, the Byron & Wattenberg streamgraph algorithm — real, working API, not a hallucinated prop).
  • SC-02: Required Features (3/4) - Smooth spline interpolation ✓ (catmullRom), harmonious distinct colors ✓, legend ✓. Spec note "center the baseline symmetrically around the x-axis" describes a "silhouette" offset; this implementation uses "wiggle" (the more common/canonical streamgraph minimization, per MUI X's own source comments — "recommended for streamgraphs"), which does not produce a strictly symmetric baseline. Minor, defensible deviation.
  • SC-03: Data Mapping (3/3) - X = time (24 months), Y = stacked genre values; all data represented.
  • SC-04: Title & Legend (3/3) - Title format correct with descriptive prefix; legend labels match the six genre series exactly.

Data Quality (14/15)

  • DQ-01: Feature Coverage (5/6) - Shows growth (Hip-Hop, Electronic), decline (Rock, Jazz, Classical), and roughly-flat (Pop) trajectories plus periodic wave + noise — good variety, if formulaically generated (sine + linear trend + LCG noise).
  • DQ-02: Realistic Context (5/5) - Music-genre streaming hours over two years — realistic, neutral, comprehensible scenario.
  • DQ-03: Appropriate Scale (4/4) - Base/amplitude/trend values (100–380 base, single-digit monthly trend) are plausible relative magnitudes for streaming-hours-in-thousands.

Code Quality (10/10)

  • CQ-01: KISS Structure (3/3) - Imports → data → chart; only structure is the harness-mandated default-export component plus a tiny LCG helper.
  • CQ-02: Reproducibility (2/2) - Fixed-seed LCG (seed = 42), fully deterministic.
  • CQ-03: Clean Imports (2/2) - Only LineChart, Box, Typography imported, all used.
  • CQ-04: Code Elegance (2/2) - Clean, no fake UI, no over-engineering.
  • CQ-05: Output & API (1/1) - Uses current, real @mui/x-charts v7 API (verified stackOrder/stackOffset/curve: "catmullRom" are valid typed props, not deprecated/hallucinated); harness owns file output.

Library Mastery (10/10)

  • LM-01: Idiomatic Usage (5/5) - High-level <LineChart> with declarative area/stack/axis/legend/sx configuration; no manual SVG/DOM work.
  • LM-02: Distinctive Features (5/5) - Leverages MUI X's built-in d3-shape stacking configuration (stackOrder/stackOffset) directly through props — a sparsely-documented but real and distinctive convenience most other JS chart libraries would require manual stack computation to replicate.

Score Caps Applied

  • AR-09 auto-reject overrides all category scoring — final score forced to 0/100 (see below), independent of the score-cap table.

Strengths

  • Real streamgraph technique: stackOrder: "insideOut" + stackOffset: "wiggle" verified to map directly to d3-shape's Byron & Wattenberg streamgraph algorithm (MUI X's own source JSDoc calls this "recommended for streamgraphs") — not a plain stacked-area fake.
  • Smooth catmullRom curve interpolation delivers the organic, flowing look the spec asks for.
  • Correct Imprint palette in canonical order, identical across light/dark; backgrounds pixel-verified exactly #FAF8F1 / #1A1A17.
  • Realistic, neutral, reproducible synthetic dataset (seeded LCG) with varied genre trajectories (growth/decline/flat).
  • Minimal, clean chrome: no grid, hidden y-axis (correct call for a wiggle-offset stream), sparse tick labels, legend tight under the title.

Weaknesses

  • AR-09 (auto-reject): the leftmost x-axis tick label "Jan 2024" is clipped at the left canvas edge in both renders — its "J" glyph is missing its leftmost strokes (ink pixels sample at x=0, the true canvas boundary, with no preceding margin). Fix by increasing margin.left (currently 24px) to give the first point-scale tick label room to draw fully inside the canvas, e.g. bump to ~48–56px, or shift the scaleType: "point" domain padding so the first tick doesn't sit flush against x=0.
  • stackOffset: "wiggle" does not produce a baseline strictly symmetric around a fixed center the way the spec note literally describes; if a truly symmetric baseline is wanted, switch to stackOffset: "silhouette" — otherwise keep "wiggle" (arguably the more canonical streamgraph algorithm) but this is worth a deliberate choice rather than a default.
  • No explicit x-axis title text and no y-axis at all — defensible for a wiggle-offset streamgraph, but an "x-axis: Month" label would close the VQ-06 gap.

Issues Found

  1. AR-09 CRITICAL: Leftmost x-axis tick label "Jan 2024" has its "J" character clipped at the left canvas edge in both light and dark renders.
    • Fix: Increase margin.left on the <LineChart> (from 24 to ~48–56px) so the first point-scale tick label has room to render fully within the canvas bounds; re-verify with a pixel check at x=0 that no ink touches the boundary.
  2. SC-02 LOW: stackOffset: "wiggle" doesn't literally center the baseline symmetrically as the spec note requests.
    • Fix: Either switch to stackOffset: "silhouette" for a literal symmetric baseline, or keep "wiggle" as the more idiomatic streamgraph choice — a one-line comment justifying the choice would resolve the ambiguity for reviewers.
  3. VQ-06 LOW: No axis title text (only tick values), y-axis fully absent.
    • Fix: Add a subtle x-axis label ("Month") via xAxis[0].label, or leave as-is if the minimalist streamgraph convention is preferred — note this explicitly in a comment if kept.

AI Feedback for Next Attempt

Primary fix: the left canvas edge is clipping the first x-axis tick label ("Jan 2024" → its "J" is chopped). Increase margin.left on the <LineChart> from 24 to roughly 48–56px (or otherwise inset the point-scale domain) so the first tick label draws entirely inside the canvas — verify by sampling pixel column x=0 for ink after the fix. Everything else here is strong: keep the insideOut/wiggle stacking, the catmullRom curve, the Imprint palette usage, and the minimal chrome. Optionally reconsider stackOffset: "silhouette" if a literally symmetric baseline matters more than the "wiggle" minimization, and consider adding a light x-axis title for full VQ-06 credit.

Verdict: REJECTED

@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

🔧 AI Review Produced No Score — Auto-Retrying

The Claude Code Action ran but didn't write quality_score.txt. Auto-retrying review once...


🤖 impl-review

@claude

claude Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

AI Review - Attempt 1/3

Image Description

Light render (plot-light.png): Warm off-white background (#FAF8F1-consistent). Title "Genre Streaming Hours · streamgraph-basic · javascript · muix · anyplot.ai" is centered, dark, and clearly readable. A horizontal legend with 6 swatches (Pop, Hip-Hop, Electronic, Rock, Jazz, Classical) sits below it, all readable. Six smoothly-interpolated wiggle bands stack with a symmetric, organic baseline — a genuine streamgraph, not a plain stacked area. Colors follow the canonical Imprint order (Pop=#009E73 green first, then lavender/blue/ochre/red/cyan). X-axis tick labels (Jan 2024 … Oct 2025) are readable except the very first label, "Jan 2024", whose "J" is clipped at the left canvas edge — confirmed by pixel inspection (non-background pixels touch column 0) and a 6× zoom crop showing the glyph visibly chopped, not merely close to the border. No y-axis is rendered.

Dark render (plot-dark.png): Warm near-black background (#1A1A17-consistent). Same title and legend, now in light ink, correctly theme-adaptive with no dark-on-dark failures. Data colors are pixel-identical to the light render — only chrome flipped, as required. The same "Jan 2024" left-edge clipping reproduces identically here, since the LineChart geometry is theme-independent.

Both renders otherwise pass legibility; only the one clipped label fails.

Score: 0/100

Category Score Max
Visual Quality 17 30
Design Excellence 13 20
Spec Compliance 14 15
Data Quality 15 15
Code Quality 10 10
Library Mastery 9 10
Total (pre-AR-09 override) 78 100
Final Score (AR-09 auto-reject) 0 100

AR-09 (Edge Clipping) fired — per prompts/quality-criteria.md, this is an auto-reject that overrides the 78/100 quality-criteria subtotal above. The subtotal is reported for repair-loop signal only; the actual score is 0.

Visual Quality (17/30)

  • VQ-01: Text Legibility (0/8) — "Jan 2024" tick label clipped/unreadable in both themes
  • VQ-02: No Overlap (6/6)
  • VQ-03: Element Visibility (6/6)
  • VQ-04: Color Accessibility (2/2)
  • VQ-05: Layout & Canvas (0/4) — content cut off at canvas edge
  • VQ-06: Axis Labels & Title (1/2) — no explicit axis title text, no y-axis at all
  • VQ-07: Palette Compliance (2/2)

Design Excellence (13/20)

  • DE-01: Aesthetic Sophistication (5/8) - Smooth catmullRom curves + insideOut/wiggle offset give an organic, above-default look
  • DE-02: Visual Refinement (4/6) - No grid, no y-axis clutter, generous whitespace
  • DE-03: Data Storytelling (4/6) - Band-width variation clearly communicates rising vs. declining genres

Spec Compliance (14/15)

  • SC-01: Plot Type (5/5) — true streamgraph: symmetric wiggle baseline + smooth spline interpolation
  • SC-02: Required Features (4/4) — smooth interpolation, centered baseline, distinct colors, legend all present
  • SC-03: Data Mapping (2/3) — X fully shown; Y has no scale reference at all, only relative band thickness
  • SC-04: Title & Legend (3/3)

Data Quality (15/15)

  • DQ-01: Feature Coverage (6/6)
  • DQ-02: Realistic Context (5/5)
  • DQ-03: Appropriate Scale (4/4)

Code Quality (10/10)

  • CQ-01: KISS Structure (3/3)
  • CQ-02: Reproducibility (2/2)
  • CQ-03: Clean Imports (2/2)
  • CQ-04: Code Elegance (2/2)
  • CQ-05: Output & API (1/1)

Library Mastery (9/10)

  • LM-01: Idiomatic Usage (5/5)
  • LM-02: Distinctive Features (4/5) — stackOrder="insideOut" + stackOffset="wiggle" is a MUI X-specific streamgraph capability

Score Caps Applied

  • AR-09 auto-reject overrides all category scoring — final score forced to 0

Strengths

  • Genuine streamgraph via LineChart's stackOrder="insideOut" + stackOffset="wiggle" + curve="catmullRom" — a distinctive MUI X capability, not a generic stacked-area fake
  • Imprint palette applied in perfect canonical order across all 6 series, identical between light and dark renders
  • Theme chrome (background, title, tick labels, legend) is correctly adaptive in both renders with no dark-on-dark or light-on-light failures
  • Deterministic fixed-seed LCG data generation with plausible, varied genre trajectories
  • Title correctly formatted with descriptive prefix and legend labels matching the data

Weaknesses

  • AR-09 EDGE CLIPPING (blocking): the first x-axis tick label "Jan 2024" is clipped at the left canvas border in BOTH plot-light.png and plot-dark.png — the leftmost stroke/hook of the "J" glyph has missing pixels, not just a tight margin. Root cause is almost certainly margin={{ left: 24, ... }} being too small for the point-scale x-axis, whose first tick is centered flush on the left plot-area boundary. Fix: increase the left margin enough to fit half the first tick label's width (or configure the point scale / tickLabelStyle so edge labels aren't centered past the plot boundary).
  • No axis title text anywhere (only bare date tick values on x; y-axis fully hidden) — acceptable for a streamgraph's wiggle baseline per the spec, but currently there's no scale reference at all for the stacked magnitude, not even a subtle baseline gridline.

Issues Found

  1. AR-09 CRITICAL: "Jan 2024" x-axis tick label has pixels chopped off at the left canvas edge in both light and dark renders
    • Fix: increase margin.left (currently 24 CSS px) so the first/last point-scale tick labels have room to render fully inside the mount, or adjust label anchoring so edge ticks don't center flush against the plot-area boundary.

AI Feedback for Next Attempt

The chart concept, palette, and streamgraph technique are all correct and well-executed — do not change the wiggle/insideOut stacking, curve interpolation, colors, or title. The ONLY required fix is the left-edge clipping of the "Jan 2024" tick label: increase margin.left (try 56–64 instead of 24) or otherwise ensure the first point-scale tick label has enough room to render fully within the canvas in both themes. Re-verify with a zoomed crop of the bottom-left corner after the fix.

Verdict: REJECTED

@github-actions github-actions Bot added the ai-review-failed AI review action failed or timed out label Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

❌ AI Review Failed (auto-retry exhausted)

The AI review action completed but did not produce valid output files. Auto-retry already tried once.

What happened:

  • The Claude Code Action ran
  • No quality_score.txt file was created

Manual rerun:

gh workflow run impl-review.yml -f pr_number=10130

🤖 impl-review

@github-actions github-actions Bot added ai-review-rescued Review re-dispatched once after ai-review-failed and removed ai-review-failed AI review action failed or timed out labels Aug 5, 2026
@claude

claude Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

AI Review - Attempt 1/3

Image Description

Light render (plot-light.png): Warm off-white background (#FAF8F1-consistent). Title "Genre Streaming Hours · streamgraph-basic · javascript · muix · anyplot.ai" is centered at top in dark ink, clearly readable. A horizontal legend below lists six genres (Pop, Hip-Hop, Electronic, Rock, Jazz, Classical) with color swatches and dark, readable text. Six smoothly-curved, wiggle-baseline stream bands (Imprint palette, canonical order) fill nearly the whole canvas. Bottom x-axis shows month/year tick labels every 3 months. All text is readable EXCEPT the leftmost tick label "Jan 2024" — its leading "J" glyph is visibly chopped off at the very left edge of the canvas (confirmed via pixel inspection: non-background ink starts at column x=0, and a 10x zoom crop shows the bottom-left hook of the "J" is missing).

Dark render (plot-dark.png): Warm near-black background (#1A1A17-consistent). Title and legend render in light ink, clearly readable — no dark-on-dark issues. Data colors are identical to the light render (only chrome flipped, as required). The same defect reproduces identically: "Jan 2024" is clipped at the left canvas edge in this render too (verified via the same pixel-level crop technique).

Both paragraphs above describe both renders in full, including the shared edge-clipping defect.

Score: 0/100

AR-09 (Edge Clipping) auto-reject triggered — see "Issues Found" below. Per prompts/quality-criteria.md, this is a Stage-1 gate: Score = 0, Verdict = REJECTED, regardless of the (otherwise solid, ~76/100-equivalent) rubric breakdown below, which is provided purely as repair guidance.

Category Score Max
Visual Quality 17 30
Design Excellence 12 20
Spec Compliance 15 15
Data Quality 14 15
Code Quality 10 10
Library Mastery 8 10
Total (pre-override) 76 100
Total (AR-09 override) 0 100

Visual Quality (17/30)

  • VQ-01: Text Legibility (0/8) — "Jan 2024" tick label clipped/missing pixels in both themes
  • VQ-02: No Overlap (6/6)
  • VQ-03: Element Visibility (6/6)
  • VQ-04: Color Accessibility (2/2)
  • VQ-05: Layout & Canvas (0/4) — content cut off at canvas border
  • VQ-06: Axis Labels & Title (1/2) — no explicit x-axis title, tick labels only
  • VQ-07: Palette Compliance (2/2)

Design Excellence (12/20)

  • DE-01: Aesthetic Sophistication (4/8) - Well-configured, above-default streamgraph, not yet publication-level
  • DE-02: Visual Refinement (4/6) - Grid/y-axis/ticks removed for a clean frame
  • DE-03: Data Storytelling (4/6) - Wiggle shape itself conveys genre rise/decline, no explicit emphasis

Spec Compliance (15/15)

  • SC-01: Plot Type (5/5)
  • SC-02: Required Features (4/4)
  • SC-03: Data Mapping (3/3)
  • SC-04: Title & Legend (3/3)

Data Quality (14/15)

  • DQ-01: Feature Coverage (5/6)
  • DQ-02: Realistic Context (5/5)
  • DQ-03: Appropriate Scale (4/4)

Code Quality (10/10)

  • CQ-01: KISS Structure (3/3)
  • CQ-02: Reproducibility (2/2)
  • CQ-03: Clean Imports (2/2)
  • CQ-04: Code Elegance (2/2)
  • CQ-05: Output & API (1/1)

Library Mastery (8/10)

  • LM-01: Idiomatic Usage (5/5)
  • LM-02: Distinctive Features (3/5) - stackOffset="wiggle" + stackOrder="insideOut" is a nice MUI X-specific stacking feature

Score Caps Applied

  • AR-09 Edge Clipping — Total Score forced to 0/100, Verdict forced to REJECTED, per Stage 1 auto-reject rules in prompts/quality-criteria.md. The category table above reflects the honest per-criterion assessment for repair guidance only.

Strengths

  • Idiomatic MUI X streamgraph recipe: area + stack + stackOrder="insideOut" + stackOffset="wiggle" + curve="catmullRom" produces a correct, smoothly-interpolated, symmetric-baseline streamgraph exactly as the spec requires.
  • Correct Imprint palette: six genres map to canonical positions 1-6 (Pop=#009E73 brand green first), identical across light/dark.
  • Realistic, neutral, well-varied data: six genres with genuinely different trajectories (growth, decline, flat) rather than uniform-looking series.
  • Deterministic in-browser data generation via a fixed-seed LCG.
  • Title and legend correctly formatted and theme-adaptive.

Weaknesses

  • AR-09 (blocking): The leftmost x-axis tick label "Jan 2024" is clipped at the canvas's left border in both renders — the bottom-left hook of the "J" glyph is missing (not just close to the edge). Root cause: margin={{left: 24, right: 24, ...}} is far too tight — MUI X centers point-scale tick labels on their tick position, and the first tick sits at the very edge of the plot area, so roughly half of "Jan 2024" extends past the canvas boundary.
  • No explicit x-axis title (e.g. "Month") — time dimension conveyed only through tick label content. Minor relative to the AR-09 blocker.
  • Margins are very tight overall (24 CSS px left/right), leaving the stream bands themselves flush against the left/right plot edges with little breathing room.

Issues Found

  1. AR-09 CRITICAL (Edge Clipping): "Jan 2024" x-axis tick label is chopped off at the left canvas edge in both plot-light.png and plot-dark.png.
    • Fix: substantially increase margin.left (and margin.right for symmetry / to protect "Oct 2025" from the same risk), e.g. to roughly 70-90 CSS px, so edge tick labels render fully inside the canvas bounds. Alternatively, adjust label anchoring near the first/last tick so it doesn't overhang the plot area.
  2. VQ-06 minor: No explicit x-axis title.
    • Fix: consider adding a light "Month" x-axis label, though this is secondary to the AR-09 fix.

AI Feedback for Next Attempt

Fix the AR-09 blocker first: widen margin.left (and check margin.right) enough that the "Jan 2024" / "Oct 2025" tick labels render fully within the canvas — the first tick's label is currently overhanging the left edge because it's centered on a tick that sits right at the plot boundary. Keep everything else unchanged: the wiggle/insideOut stack configuration, catmullRom curve, Imprint palette assignment, legend, and title are all working well.

Verdict: REJECTED

@github-actions github-actions Bot added the ai-review-failed AI review action failed or timed out label Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor Author

❌ AI Review Failed (auto-retry exhausted)

The AI review action completed but did not produce valid output files. Auto-retry already tried once.

What happened:

  • The Claude Code Action ran
  • No quality_score.txt file was created

Manual rerun:

gh workflow run impl-review.yml -f pr_number=10130

🤖 impl-review

MarkusNeusinger added a commit that referenced this pull request Aug 5, 2026
…review (#10179)

## The bug

`impl-review.yml` used quality score `0` as its sentinel for *"the AI
review produced no output"*. But `0` is also a score the review prompt
**mandates**: the Stage 1 auto-reject gates in
`prompts/workflow-prompts/ai-quality-review.md` require exactly `Score =
0, verdict = REJECTED` for **AR-08** (clipped element) and **AR-09**
(mandatory title not visible).

Prompt and workflow therefore contradicted each other, and the pipeline
was guaranteed to dead-end on exactly the plots it is designed to reject
hardest.

## The deadlock chain

1. Plot renders without a visible title → **AR-09** → reviewer returns
`Score: 0/100`, `Verdict: REJECTED` — **this is correct behaviour**
2. `Extract quality score` normalised it: `::warning::Invalid quality
score '0', defaulting to 0`
3. `Validate review output` fired on `score == '0'` → `::error::AI
Review did not produce valid output files` → `ai-review-failed` → `exit
1`
4. `exit 1` skipped **`Add verdict label and take action`** — which the
file itself documents as *"the pipeline's only hand-off point: every
downstream workflow (merge, repair) starts from a call made right here"*
5. So **`impl-repair` was never dispatched** and the missing title was
never fixed
6. `impl-review-retry.yml` rescued once → the re-review scored `0` again
(deterministically — the plot was unchanged) → `ai-review-failed`
re-applied
7. `ai-review-failed` + `ai-review-rescued` matches **no** watchdog case
(`watchdog-stuck-jobs.yml:116` only emits a `::warning::` and defers to
a human) → **PR stranded permanently**

## Evidence

Six open PRs sit in exactly that state, each with an AR-09 verdict
already in hand:

| PR | Library / spec | Reported score | Verdict | Gate |
|---|---|---|---|---|
| #10152 | seaborn windrose-basic | 0 | REJECTED | AR-09 |
| #10130 | muix streamgraph-basic | 0 | REJECTED | AR-09 |
| #10009 | matplotlib wireframe-3d-basic | 0 | REJECTED | AR-09 |
| #10003 | d3 ternary-basic | 0 | REJECTED | AR-09 |
| #9968 | plotnine treemap-basic | 0 | REJECTED | AR-09 |
| #9776 | matplotlib polar-basic | 0 | REJECTED | AR-09 |

This was never an infrastructure failure. In run
[31035492709](https://github.com/MarkusNeusinger/anyplot/actions/runs/31035492709)
the Claude action reported `"subtype": "success"`, `"is_error": false`,
15 turns, `permission_denials_count: 0` — and posted a complete review
ending in `### Score: 0/100` / `### Verdict: REJECTED`. 94 of the last
100 `impl-review` runs are green; the 6 failures are these gate-tripping
plots.

## The fix

Output presence becomes its own signal, decoupled from the score value:

- `Extract quality score` now emits **`has_output`** alongside `score`.
`0` is accepted as a valid score; only a non-numeric or out-of-range
value marks output as missing.
- The six gates that keyed off `score != '0'` / `score == '0'` now key
off `has_output`.
- A score of `0` therefore flows into the normal `ai-rejected` →
`impl-repair` path (threshold floor is 50, so `0 < 50` → rejected →
repair dispatched), and only genuinely absent output raises
`ai-review-failed`.

**Second, latent bug fixed in the same step:** the comment fallback read
`.comments[-1].body`, but on a retry the workflow's own
*"auto-retrying"* notice is posted **after** the review — so the
fallback searched the notice and found no score. It now selects the last
`claude[bot]` comment.

**Deliberately not changed:** `watchdog-stuck-jobs.yml`. With the root
cause fixed, "review produced no output twice in a row" (PRs
#9953/#9952/#9951, which have no `claude[bot]` comment at all) is a
genuine failure that *should* escalate to a human rather than loop
forever.

## Verification

GitHub Actions changes have no verification loop in this repo, so the
step's shell body was tested directly: a harness extracts the `Extract
quality score` `run:` block **verbatim from the YAML** and exercises it
with a stubbed `gh`.

```
--- the regression that caused the deadlock ---
PASS  file score 0 (AR-09 auto-reject)               score=0    has_output=true
PASS  comment fallback, score 0                      score=0    has_output=true
--- normal operation must be unchanged ---
PASS  file score 87 / 100 / 1 / trailing-newline 73  score=...  has_output=true
PASS  comment fallback, score 87                     score=87   has_output=true
--- genuine 'no output' must still be detected ---
PASS  no file, no review comment                     score=0    has_output=false
PASS  no file, comment without a score line          score=0    has_output=false
PASS  file with non-numeric garbage                  score=0    has_output=false
PASS  file with out-of-range score                   score=0    has_output=false
PASS  empty file                                     score=0    has_output=false

ALL CASES PASS
```

YAML validity re-checked after the edit (`yaml.safe_load`, 20 steps
parsed).

Residual risk: the `if:` expression rewrites and the `REPOSITORY` env
addition are only observable on a real pipeline run. Recovery path for
the six stranded PRs after merge: re-dispatch `impl-review.yml -f
pr_number=<n>`, which will now score them 0, label `ai-rejected`, and
hand them to `impl-repair` to fix the titles.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@MarkusNeusinger MarkusNeusinger removed ai-review-failed AI review action failed or timed out ai-review-rescued Review re-dispatched once after ai-review-failed labels Aug 5, 2026
@claude

claude Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

AI Review - Attempt 1/3

Image Description

Light render (plot-light.png): Warm off-white background (#FAF8F1-matching). Title "Genre Streaming Hours · streamgraph-basic · javascript · muix · anyplot.ai" is centered at top in dark, legible ink, with a horizontal legend below (Pop, Hip-Hop, Electronic, Rock, Jazz, Classical — six color swatches). Six smoothly-curved, stacked bands use the Imprint palette in canonical order (Pop = brand green #009E73 first, then lavender, blue, ochre, matte red, cyan), arranged with insideOut stack order and wiggle offset for an organic river-like silhouette. Y-axis and gridlines are intentionally hidden; only a thin x-axis baseline with month/year ticks remains. All chrome text is readable against the light background EXCEPT one defect: a 6x-zoomed pixel crop of the bottom-left corner shows the first x-axis tick label "Jan 2024" has the leading "J" clipped at the canvas edge — ink pixels are present at column x=0, and the glyph's left hook is visibly missing, not merely close to the border.

Dark render (plot-dark.png): Same layout on the warm near-black (#1A1A17-matching) background, with all title/legend/tick text correctly flipped to light ink — no dark-on-dark failures. Data band colors are pixel-identical to the light render; only chrome flipped, as required. The same left-edge clipping of "Jan 2024" reproduces identically in this render (confirmed via the same zoomed-crop technique) — the left hook of the "J" is missing here too.

Both paragraphs confirm: the implementation is well-executed everywhere except for one hard-rule violation (edge clipping) that reproduces in both themes.

Score: 0/100

Category Score Max
Visual Quality 0 30
Design Excellence 0 20
Spec Compliance 0 15
Data Quality 0 15
Code Quality 0 10
Library Mastery 0 10
Total 0 100

Visual Quality (0/30)

  • VQ-01: Text Legibility (0/8) — AR-09 clipping (see below); all other text legible in both themes
  • VQ-02: No Overlap (0/6) — capped by AR-09; no overlap otherwise
  • VQ-03: Element Visibility (0/6) — capped by AR-09; bands/legend otherwise clear
  • VQ-04: Color Accessibility (0/2) — capped by AR-09; 6 distinct Imprint hues otherwise
  • VQ-05: Layout & Canvas (0/4) — capped by AR-09; the clipped label IS the Layout & Canvas defect
  • VQ-06: Axis Labels & Title (0/2) — capped by AR-09; title format otherwise correct
  • VQ-07: Palette Compliance (0/2) — capped by AR-09; canonical Imprint order otherwise correct

Design Excellence (0/20)

  • DE-01: Aesthetic Sophistication (0/8) - Capped by AR-09; otherwise a considered streamgraph (hidden y-axis, insideOut ordering, decluttered ticks)
  • DE-02: Visual Refinement (0/6) - Capped by AR-09; grid removed, generous whitespace otherwise
  • DE-03: Data Storytelling (0/6) - Capped by AR-09; insideOut ordering otherwise gives a legible focal hierarchy

Spec Compliance (0/15)

  • SC-01: Plot Type (0/5) — Capped by AR-09; correct streamgraph technique otherwise (area+stack+wiggle+insideOut+catmullRom)
  • SC-02: Required Features (0/4) — Capped by AR-09; smooth curves, near-centered baseline, legend, distinct colors otherwise present
  • SC-03: Data Mapping (0/3) — Capped by AR-09; time/value mapping otherwise correct
  • SC-04: Title & Legend (0/3) — Capped by AR-09; title format and legend labels otherwise correct

Data Quality (0/15)

  • DQ-01: Feature Coverage (0/6) — Capped by AR-09; 6 genres × 24 months, within spec range, with distinct trajectories otherwise
  • DQ-02: Realistic Context (0/5) — Capped by AR-09; plausible, neutral music-streaming context otherwise
  • DQ-03: Appropriate Scale (0/4) — Capped by AR-09; sensible value ranges otherwise

Code Quality (0/10)

  • CQ-01: KISS Structure (0/3) — Capped by AR-09; flat script otherwise
  • CQ-02: Reproducibility (0/2) — Capped by AR-09; fixed-seed LCG otherwise
  • CQ-03: Clean Imports (0/2) — Capped by AR-09; only used imports otherwise
  • CQ-04: Code Elegance (0/2) — Capped by AR-09; no fake UI otherwise
  • CQ-05: Output & API (0/1) — Capped by AR-09; correct default-export/harness contract otherwise

Library Mastery (0/10)

  • LM-01: Idiomatic Usage (0/5) - Capped by AR-09; correct area/stack/stackOrder/stackOffset/curve combination is idiomatic MUI X streamgraph usage otherwise
  • LM-02: Distinctive Features (0/5) - Capped by AR-09; stackOrder="insideOut" + stackOffset="wiggle" is a lesser-used, distinctive MUI X LineChart capability otherwise

Score Caps Applied

  • AR-09 auto-reject applied — total score forced to 0/100. Edge clipping of the first x-axis tick label overrides all other category scoring.

Strengths

  • Correct streamgraph technique: area + stack with stackOrder="insideOut" and stackOffset="wiggle" is the textbook Byron/Wattenberg streamgraph algorithm, paired with catmullRom curve interpolation for smooth flowing bands.
  • Imprint categorical palette applied in exact canonical order (t.palette[0..5]): brand green first (Pop), then lavender, blue, ochre, matte red, cyan — identical hues in light and dark renders, only chrome flips.
  • Deliberate, theme-appropriate chrome: y-axis and gridlines hidden (appropriate for a streamgraph where shape/proportion is the message), thin single x-axis baseline, decluttered tick labels via tickLabelInterval to avoid crowding.
  • Title follows the mandated format and both renders keep title/legend text fully legible against their respective backgrounds.

Weaknesses

  • AR-09 (auto-reject): the first x-axis tick label "Jan 2024" is clipped at the left canvas edge in BOTH plot-light.png and plot-dark.png — pixel inspection confirms ink-colored pixels at column x=0, and a 6x zoom crop shows the left hook/curve of the "J" glyph is missing, not just close to the border. Root cause: xAxis uses scaleType="point" with margin={{left: 24, ...}}, so the first point sits at x=24 in the plot's local coordinate space and MUI X centers the point-scale tick label under that position — a label this wide ("Jan 2024") needs far more than 24px of left margin to avoid its centered text starting before x=0. Fix: increase margin.left substantially (e.g. to ~60-80px) so the first tick's label box fits within the canvas, or suppress/adjust the very first shown label so its centered text doesn't overflow the left edge.
  • Minor, not a required fix: the spec describes a "baseline centered symmetrically around the x-axis" — stackOffset="wiggle" is the correct and expected choice, but note it does not produce a perfectly mirror-symmetric envelope like stackOffset="silhouette" would; both are valid readings of "streamgraph", no deduction taken.

Issues Found

  1. AR-09 CRITICAL: X-axis tick label "Jan 2024" clipped at the left canvas edge in both light and dark renders — left hook of the "J" glyph is missing.
    • Fix: Increase margin.left (currently 24px) to give the first point-scale tick's centered label enough room, e.g. margin={{ left: 64, right: 24, top: 44, bottom: 56 }}, or otherwise ensure the leftmost tick label's bounding box stays within the canvas.

AI Feedback for Next Attempt

The implementation is otherwise strong (correct streamgraph algorithm, canonical palette, clean theme-adaptive chrome) — the sole blocker is that the first x-axis tick label "Jan 2024" is clipped at the left edge of the canvas in both themes because the point-scale's first tick sits only 24px from the left margin while its centered label is far wider than that. Increase margin.left (try ~64-80px) so the label's full bounding box fits inside the canvas, then re-verify no clipping remains at the left edge in both renders.

Verdict: REJECTED

@github-actions github-actions Bot added quality:0 Quality score 0/100 ai-rejected Quality not OK, triggers update quality-poor Score < 50, needs fundamental fixes labels Aug 5, 2026
@github-actions github-actions Bot added ai-attempt-1 First repair attempt and removed ai-rejected Quality not OK, triggers update labels Aug 5, 2026
Attempt 1/4 - fixes based on AI review
@github-actions

github-actions Bot commented Aug 5, 2026

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

claude Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

AI Review - Attempt 2/3

Image Description

Light render (plot-light.png): Warm off-white (#FAF8F1) background with a centered dark-ink title ("Genre Streaming Hours · streamgraph-basic · javascript · muix · anyplot.ai") and a horizontal legend of 6 swatches (Pop, Hip-Hop, Electronic, Rock, Jazz, Classical) directly below it. The chart itself is a six-band streamgraph with a wiggle-centered baseline and smooth catmullRom curves spanning Jan 2024–Oct 2025 (x-axis tick labels every 3rd month, no y-axis at all). Band colors read top-to-bottom as lavender (Hip-Hop), green (Pop), red (Jazz), ochre (Rock), cyan (Classical), blue (Electronic), each separated by a thin same-hue stroke. All text (title, legend, x tick labels) is clearly legible dark-on-cream; no light-on-light issues.

Dark render (plot-dark.png): Near-black (#1A1A17) background with the same layout — title and legend text rendered in light ink, x-axis tick labels in soft light gray, both fully readable against the dark surface with no dark-on-dark failures. The six band colors (green, lavender, blue, ochre, red, cyan) are pixel-identical to the light render — only the chrome (background, title/label ink, axis line) flips, exactly as required. Brand green (Pop, #009E73) is clearly visible on both surfaces.

Both renders are theme-correct and fully legible.

Score: 88/100

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

Visual Quality (28/30)

  • VQ-01: Text Legibility (7/8) - Title/legend/ticks all readable in both themes at explicit sizes; minor: axis tick label size (14px) is on the small side and not verified to hold up at 400px thumbnail scale.
  • VQ-02: No Overlap (6/6) - No collisions between title, legend, bands, or tick labels.
  • VQ-03: Element Visibility (6/6) - 24 monthly points per band render as smooth, clearly visible flowing shapes; no overplotting.
  • VQ-04: Color Accessibility (2/2) - Imprint palette is CVD-safe; category identity carried by both color and position/legend, not color alone.
  • VQ-05: Layout & Canvas (4/4) - Clean margins, nothing clipped, title width (~70%) matches the expected range for the mandated long title.
  • VQ-06: Axis Labels & Title (1/2) - Title is descriptive, but the y-axis (and its unit — "thousands of hours") is fully suppressed with no numeric reference anywhere on the chart, and the x-axis has no explicit axis title (only bare month/year tick labels).
  • VQ-07: Palette Compliance (2/2) - First series (Pop) is #009E73; all 6 categories follow canonical Imprint order (green, lavender, blue, ochre, red, cyan); backgrounds are correct #FAF8F1/#1A1A17; data colors identical across themes.

Design Excellence (12/20)

  • DE-01: Aesthetic Sophistication (5/8) - Harmonious palette, smooth curves, and thin same-hue stroke separators between bands give a polished look, but styling otherwise stays close to the library defaults.
  • DE-02: Visual Refinement (4/6) - Y-axis, grid, and ticks are deliberately suppressed for a clean minimalist look consistent with the streamgraph genre; generous whitespace.
  • DE-03: Data Storytelling (3/6) - The flowing shapes convey relative genre trends (e.g., Electronic growing at the bottom, Hip-Hop share narrowing at the top) but there's no explicit emphasis, annotation, or callout guiding the viewer to the key trend.

Spec Compliance (14/15)

  • SC-01: Plot Type (5/5) - Correct streamgraph via stackOffset: "wiggle" + stackOrder: "insideOut", the standard MUI X streamgraph recipe.
  • SC-02: Required Features (4/4) - Smooth spline curves (catmullRom), wiggle-centered baseline, distinct harmonious colors, legend present.
  • SC-03: Data Mapping (2/3) - X (time) and stacked category values are correctly mapped, but the y-axis is entirely hidden (leftAxis={null}) so the chart shows no numeric scale reference at all for the mapped values.
  • SC-04: Title & Legend (3/3) - Title matches the required {Descriptive Title} · {spec-id} · {language} · {library} · anyplot.ai format; legend labels match the 6 genres.

Data Quality (15/15)

  • DQ-01: Feature Coverage (6/6) - 24 monthly points, 6 categories — within spec's 10-100 points / 3-8 categories range; all streamgraph-defining aspects present.
  • DQ-02: Realistic Context (5/5) - Plausible, neutral monthly listening-hours-by-genre trends (Electronic/Hip-Hop growing, Rock/Jazz/Classical declining, Pop flat).
  • DQ-03: Appropriate Scale (4/4) - Values (hundreds of thousands range) are sensible for streaming-hours data.

Code Quality (10/10)

  • CQ-01: KISS Structure (3/3) - No unnecessary functions/classes; flat, readable data + component.
  • CQ-02: Reproducibility (2/2) - Deterministic fixed-seed LCG (seed = 42).
  • CQ-03: Clean Imports (2/2) - Only LineChart, Box, Typography imported, all used.
  • CQ-04: Code Elegance (2/2) - Appropriate complexity, no fake UI/interactivity.
  • CQ-05: Output & API (1/1) - Correct harness contract (default-exported component, skipAnimation, sized to ANYPLOT_SIZE), current MUI X stacking API.

Library Mastery (9/10)

  • LM-01: Idiomatic Usage (5/5) - stackOffset: "wiggle" + stackOrder: "insideOut" + curve: "catmullRom" is exactly MUI X's documented streamgraph pattern.
  • LM-02: Distinctive Features (4/5) - Custom tickLabelInterval function, disableTicks, custom legend positioning via slotProps, and per-series fillOpacity/strokeWidth overrides show real command of the API surface.

Score Caps Applied

  • None

Strengths

  • Correct, idiomatic MUI X streamgraph implementation (wiggle offset + insideOut order + catmullRom curve) matching the library's documented recipe.
  • Both renders are fully theme-correct: identical data colors, correctly flipped chrome, no dark-on-dark or light-on-light legibility failures.
  • Canonical Imprint palette order across all 6 categories with brand green correctly first.
  • Deterministic, realistic monthly genre-listening data well within the spec's size/category ranges.

Weaknesses

  • Y-axis is fully hidden with no numeric scale or unit reference anywhere in the chart — the title mentions "Streaming Hours" but the actual magnitude (the code comment says "in thousands") is never surfaced to the viewer. Consider adding a subtle value reference (e.g. a minimal right-side axis, or a unit note near the title) so the chart isn't purely qualitative.
  • No explicit x-axis title (only bare month/year tick labels) — a short axis label would remove ambiguity for viewers unfamiliar with the convention.
  • Design Excellence stays close to library defaults beyond the palette/curve choices — no annotation or emphasis calls out the clearest trend (e.g. Electronic's steady growth) to strengthen the data storytelling.

Issues Found

  1. VQ-06 / SC-03 MEDIUM: Y-axis and its unit are completely absent, leaving the chart without any quantitative reference.
    • Fix: Add a minimal value reference (subtle right-axis with a few ticks, or a small unit annotation) without disrupting the wiggle baseline's organic look.
  2. DE-03 LOW: No explicit visual emphasis or annotation highlighting the key trend.
    • Fix: Consider a subtle annotation or label pointing to the most notable shift (e.g. Electronic's growth) to sharpen the storytelling.

AI Feedback for Next Attempt

Implementation is solid and idiomatic. To push further: surface some numeric value reference (even minimal) since the y-axis is currently fully suppressed, add a short x-axis title, and consider a subtle annotation calling out the clearest genre trend to strengthen data storytelling.

Verdict: APPROVED

@github-actions github-actions Bot added quality:88 Quality score: 88/100 and removed quality:0 Quality score 0/100 labels Aug 5, 2026
@github-actions github-actions Bot added the ai-approved Quality OK, ready for merge label Aug 5, 2026
@MarkusNeusinger
MarkusNeusinger merged commit 9c12df2 into main Aug 5, 2026
3 checks passed
@MarkusNeusinger
MarkusNeusinger deleted the implementation/streamgraph-basic/muix branch August 5, 2026 21:46
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:88 Quality score: 88/100 quality-poor Score < 50, needs fundamental fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant