Skip to content

feat(matplotlib): implement wireframe-3d-basic - #10009

Closed
github-actions[bot] wants to merge 2 commits into
mainfrom
implementation/wireframe-3d-basic/matplotlib
Closed

feat(matplotlib): implement wireframe-3d-basic#10009
github-actions[bot] wants to merge 2 commits into
mainfrom
implementation/wireframe-3d-basic/matplotlib

Conversation

@github-actions

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

Copy link
Copy Markdown
Contributor

Implementation: wireframe-3d-basic - python/matplotlib

Implements the python/matplotlib version of wireframe-3d-basic.

File: plots/wireframe-3d-basic/implementations/python/matplotlib.py

Parent Issue: #1015


🤖 impl-generate workflow

Regen from quality 91. Addressed:
- Canvas drift: previous figsize=(16,9)@dpi=300 with bbox_inches="tight"
  did not land on the canonical 3200x1800 target and used the explicitly
  banned tight-bbox savefig. Switched to figsize=(8,4.5)@dpi=400 with
  bbox_inches left at its default (None) — confirmed exact 3200x1800 output.
- Design excellence (previously the only flagged weakness, 12/20): added a
  brand-consistent floor contour projection (imprint_seq) that echoes the
  ripple's height structure as a topographic footprint, giving a second,
  easier-to-read view of the same Z data without touching the wireframe's
  single-color brand-green identity. Thinned the drawn mesh (rcount/ccount=25
  against the full 40x40 data grid, still within the spec's 20x20-50x50
  range) and thickened lines slightly so the mesh reads cleanly instead of
  moire-ing into a dense hairball. Also cleaned up tick density/format and
  scaled chrome fonts to the new canvas.
- Preserved strengths: brand-green single-series wireframe, theme-adaptive
  panes/grid/chrome, elev=30/azim=45 viewing angle, ripple data scenario.
@claude

claude Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

AI Review - Attempt 1/3

Image Description

Light render (plot-light.png): Warm off-white (~#FAF8F1) background. Title "wireframe-3d-basic · matplotlib · anyplot.ai" is centered at top in dark ink and fully legible. A brand-green (#009E73) wireframe ripple mesh is the focal point, with a blue-to-green imprint_seq contour projection echoing the same topography on the floor plane. Z-axis label "Amplitude (Z)" and its tick labels are legible on the left wall. However, the X-axis label "Distance from Center (X)" and Y-axis label "Distance from Center (Y)" are cut off at the bottom edge of the canvas — pixel inspection confirms ink pixels present in the very last row of the PNG (row 1799 of 1800px height), meaning glyph pixels are missing, not just tightly cropped. Not all text is readable: the two axis titles are clipped.

Dark render (plot-dark.png): Same layout on a warm near-black (~#1A1A17) background. Title and Z-axis label render in light ink (#F0EFE8) with good contrast — no dark-on-dark failures. Wireframe and floor-contour data colors are pixel-identical to the light render (only chrome flipped), as required. The same bottom-edge clipping affects the X-axis and Y-axis labels here too (confirmed via the same pixel-row check) — not all text is readable in this render either.

Both paragraphs above describe both renders, confirming both were viewed.

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)
  • VQ-02: No Overlap (0/6)
  • VQ-03: Element Visibility (0/6)
  • VQ-04: Color Accessibility (0/2)
  • VQ-05: Layout & Canvas (0/4) — AR-09 clipping, see below
  • VQ-06: Axis Labels & Title (0/2)
  • VQ-07: Palette Compliance (0/2)

Design Excellence (0/20)

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

Spec Compliance (0/15)

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

Data Quality (0/15)

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

Code Quality (0/10)

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

Library Mastery (0/10)

  • LM-01: Idiomatic Usage (0/5)
  • LM-02: Distinctive Features (0/5)

Score Caps Applied

  • AR-09 auto-reject: whole-implementation Score = 0 (Stage 1 gate; Stage 2 category scoring not applicable per the auto-reject flow)

Strengths

  • Correct plot type: a true ax.plot_wireframe mesh (not a disguised surface/scatter), with the spec's suggested elevation 30° / azimuth 45° viewing angle and grid lines running in both x and y directions
  • Creative, on-spec use of the "optional height-based coloring" note — a floor-plane ax.contour projection echoes the ripple's topography using imprint_seq (Imprint palette derived), giving a second readable view of the same Z data without competing with the brand-green wireframe
  • Data colors are pixel-identical between light and dark renders (brand green #009E73 wireframe, imprint_seq floor contour) — only chrome (panes, grid, text) adapts to theme, exactly per the palette rule
  • Sensible rcount/ccount=25 subsampling of the 40×40 grid keeps the wireframe from moiré-ing into an unreadable hairball, with a comment explaining the reasoning
  • z = sin(sqrt(x^2+y^2)) ripple function matches the spec's suggested example exactly, over a plausible, neutral, appropriately-scaled domain

Weaknesses

  • AR-09 EDGE CLIPPING (auto-reject): the X-axis label "Distance from Center (X)" and the Y-axis label "Distance from Center (Y)" are both clipped at the bottom edge of the canvas in both plot-light.png and plot-dark.png. Pixel-level inspection confirms non-background ink pixels present in the very last pixel row of the image (row 1799 of a 1800px-tall canvas, spanning roughly x=1289-1935) — glyph pixels continue past the saved PNG's bounding box and are permanently gone. This is not proximity to the border; it is confirmed missing pixels. Root cause: at elev=30, azim=45 the two rotated 3D axis labels (labelpad=10) land very low in the figure, and plt.tight_layout() does not reserve enough bottom margin for rotated Axes3D label text — it only accounts for straight 2D label boxes. Fix: increase bottom margin explicitly (e.g. fig.subplots_adjust(bottom=0.12) or larger, tuned by trial) instead of relying on tight_layout() for this plot, and/or reduce labelpad, and/or nudge the view angle slightly so the projected label extents fit inside the figure. Re-verify by confirming zero non-background pixels exist in the outermost row/column on all four edges of both renders before resubmitting.
  • The wireframe is quite dense near the dome's peak at this elevation/azimuth — many crossing green lines create a busy "hairball" region at the top of the ripple that's harder to parse than the rest of the surface; consider lowering rcount/ccount slightly (e.g. ~18-20) or reducing alpha a touch further in that region to keep the peak legible.
  • Title omits the language token: renders as "wireframe-3d-basic · matplotlib · anyplot.ai" instead of the mandated {spec-id} · {language} · {library} · anyplot.ai — should read "wireframe-3d-basic · python · matplotlib · anyplot.ai".

Issues Found

  1. AR-09 CRITICAL: X-axis and Y-axis labels clipped at the bottom canvas edge in both renders (confirmed via pixel data)
    • Fix: increase bottom figure margin (fig.subplots_adjust(bottom=...)) so rotated 3D axis label text fits fully inside the saved PNG; tight_layout() alone is insufficient for Axes3D label extents at this view angle
  2. VQ-03 LOW: Wireframe hairball density near the ripple's peak
    • Fix: reduce rcount/ccount slightly or lower alpha further in the densest region
  3. SC-04 LOW: Title missing the language token
    • Fix: use "wireframe-3d-basic · python · matplotlib · anyplot.ai"

AI Feedback for Next Attempt

Priority fix: the X-axis and Y-axis labels are being clipped off the bottom of the canvas in both themes — this is an auto-reject (AR-09) and must be fixed first. Increase the figure's bottom margin explicitly (don't rely on tight_layout() for 3D rotated labels), e.g. fig.subplots_adjust(bottom=0.12), and/or trim labelpad, then re-check that no ink touches the outer edge of the canvas on any side in either render. While repairing, also consider slightly thinning the wireframe near the dome's peak (lower rcount/ccount or alpha) and add the missing "python" language token to the title so it reads "wireframe-3d-basic · python · matplotlib · anyplot.ai".

Verdict: REJECTED

@github-actions

github-actions Bot commented Aug 4, 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 4, 2026

Copy link
Copy Markdown
Contributor

AI Review - Attempt 1/3

Image Description

Light render (plot-light.png): Warm off-white (#FAF8F1) background, not pure white. Title "wireframe-3d-basic · matplotlib · anyplot.ai" is fully visible in dark ink at the top — but this string is missing the mandated "python" language segment (see SC-04 below). The Z-axis label "Amplitude (Z)" and its tick labels are fully legible. The X-axis label "Distance from Center (X)" is fully visible along the bottom-right diagonal, ending cleanly in "(X)". The Y-axis label is clipped at the bottom canvas edge: only "Distance from Cente" renders — the trailing "r" and the entire "(Y)" suffix are chopped off outside the saved PNG's bounding box (confirmed via pixel inspection: dark, non-background pixels are present at the very last row, y=1799, of the 1800px-tall canvas). Data is a green (#009E73) wireframe ripple surface z=sin(sqrt(x²+y²)) with a green-to-blue floor contour projection (imprint_seq) echoing the same height structure.

Dark render (plot-dark.png): Warm near-black (#1A1A17) background, not pure black. Same layout as the light render, rendered in light ink (#F0EFE8) for title/labels and soft gray for tick labels — no dark-on-dark failures. The identical Y-axis label clipping reproduces at the bottom edge: "Distance from Cente" with "r (Y)" chopped off, confirmed by the same pixel-row inspection. Data colors (green wireframe, blue-green floor contour) are pixel-identical to the light render — only chrome flipped correctly.

Both renders otherwise pass the theme-readability checklist (correct backgrounds, no dark-on-dark/light-on-light, brand green visible), but both fail on the Y-axis label clipping.

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

AR-09 EDGE CLIPPING auto-reject fires: the Y-axis label is missing pixels at the canvas border in both renders, so the score is forced to 0/100 per the auto-reject rule regardless of otherwise-decent visual/design quality. Diagnostic comments below describe what each criterion would look like once the clipping is fixed.

Visual Quality (0/30)

  • VQ-01: Text Legibility (0/8) - Y-axis label clipped at bottom edge in both renders; everything else legible
  • VQ-02: No Overlap (0/6) - Forced by AR-09; X/Y "6" tick labels and crossing axis-label diagonals sit close together near the front vertex
  • VQ-03: Element Visibility (0/6) - Forced by AR-09; wireframe + floor contour otherwise clearly visible, mesh a bit dense near the peak
  • VQ-04: Color Accessibility (0/2) - Forced by AR-09; green/blue palette otherwise CVD-safe
  • VQ-05: Layout & Canvas (0/4) - Correct 3200x1800 canvas but Y-axis label pixels missing at bottom border (AR-09)
  • VQ-06: Axis Labels & Title (0/2) - X/Z labels descriptive; Y-axis label truncated, title missing language segment
  • VQ-07: Palette Compliance (0/2) - Forced by AR-09; brand green + imprint_seq contour + correct theme backgrounds otherwise compliant

Design Excellence (0/20)

  • DE-01: Aesthetic Sophistication (0/8) - Forced by AR-09; floor-contour echo is a nice touch beyond generic defaults
  • DE-02: Visual Refinement (0/6) - Forced by AR-09; theme-adaptive panes/grid handled well
  • DE-03: Data Storytelling (0/6) - Forced by AR-09; floor contour gives a secondary view of the same data

Spec Compliance (0/15)

  • SC-01: Plot Type (0/5) - Forced by AR-09; correctly a 3D wireframe
  • SC-02: Required Features (0/4) - Grid lines both directions, consistent color, elev=30/azim=45 present; Y-axis labeling incomplete
  • SC-03: Data Mapping (0/3) - Forced by AR-09; x/y/z mapping otherwise correct
  • SC-04: Title & Legend (0/3) - Title reads "wireframe-3d-basic · matplotlib · anyplot.ai" — missing the mandated "python" language segment

Data Quality (0/15)

  • DQ-01: Feature Coverage (0/6) - Forced by AR-09; mesh + floor contour cover the plot type's key features well
  • DQ-02: Realistic Context (0/5) - Forced by AR-09; ripple function matches the spec's own suggested example exactly
  • DQ-03: Appropriate Scale (0/4) - Forced by AR-09; x/y range and resulting amplitude are sensible

Code Quality (0/10)

  • CQ-01: KISS Structure (0/3) - Forced by AR-09; flat script, no functions/classes
  • CQ-02: Reproducibility (0/2) - np.random.seed(42) is dead code; no np.random.* calls exist anywhere (data is deterministic via linspace/meshgrid)
  • CQ-03: Clean Imports (0/2) - Forced by AR-09; all imports used
  • CQ-04: Code Elegance (0/2) - Forced by AR-09; appropriate complexity, no fake UI
  • CQ-05: Output & API (0/1) - Saves plot-{THEME}.png correctly but the render has clipped content

Library Mastery (0/10)

  • LM-01: Idiomatic Usage (0/5) - Forced by AR-09; ax.plot_wireframe, ax.contour(zdir='z'), view_init are idiomatic
  • LM-02: Distinctive Features (0/5) - Forced by AR-09; floor contour projection is a genuinely distinctive touch

Score Caps Applied

  • AR-09 auto-reject (Edge Clipping) — score forced to 0/100 regardless of subscores, per prompts/quality-criteria.md Stage 1 rules.

Strengths

  • ax.plot_wireframe uses brand green (#009E73) with the spec's suggested viewing angle (elev=30, azim=45) exactly
  • Distinctive touch: a floor contour projection (ax.contour with zdir='z', offset=z_floor) using the imprint_seq colormap echoes the same Z data as a topographic footprint, without competing with the wireframe's brand-green identity
  • Theme-adaptive panes, grid, and tick colors are threaded correctly from INK/INK_SOFT tokens; both light (#FAF8F1) and dark (#1A1A17) backgrounds are correct
  • Ripple function z = sin(sqrt(x²+y²)) matches the spec's suggested example exactly, and is realistic/neutral
  • Correct 3200×1800 canvas (gate passed), single flat KISS script, no bbox_inches='tight'

Weaknesses

  • AR-09 (edge clipping): the Y-axis label "Distance from Center (Y)" is clipped at the bottom edge of the canvas in BOTH renders — only "Distance from Cente" renders; the trailing "r" and the entire "(Y)" suffix are chopped off outside the saved PNG's bounding box (confirmed: dark text pixels present at the very last row, y=1799, of the 1800px canvas). The rotated 3D y-axis label needs more vertical clearance — reduce labelpad, shrink the axis label/tick fontsize slightly, or use fig.subplots_adjust(bottom=...) to pull the 3D plot area up and leave room for the label within the 3200×1800 canvas.
  • SC-04: title reads "wireframe-3d-basic · matplotlib · anyplot.ai" but the mandated format is "{spec-id} · {language} · {library} · anyplot.ai" — the "python" language segment is missing entirely.
  • np.random.seed(42) is dead code — no np.random.* calls exist anywhere in the script (x, y, Z are all fully deterministic via np.linspace/np.meshgrid), so the seed call is misleading and should be removed.
  • The X-axis and Y-axis "6" tick labels sit very close together near the front vertex, and the two rotated axis-label strings' diagonals nearly touch/cross in the same region — a bit more separation (labelpad or reduced tick density) would make the bottom of the chart cleaner.
  • The 40×40 grid downsampled to rcount/ccount=25 still produces a dense, moire-like crossing pattern near the peak at this viewing angle — consider rcount/ccount around 18-20 for a cleaner mesh, still within the spec's recommended 20×20 to 50×50 grid range.

Issues Found

  1. AR-09 CRITICAL: Y-axis label "Distance from Center (Y)" clipped at the bottom canvas edge in both light and dark renders — only "Distance from Cente" is visible, "r (Y)" is chopped off outside the PNG bounds
    • Fix: give the rotated y-axis label more vertical room — reduce labelpad from 10, reduce ax.set_ylabel fontsize slightly, or use fig.subplots_adjust(bottom=0.05-0.1) (or increase figure margins) so the label fully fits inside the 3200×1800 canvas in both themes
  2. SC-04 HIGH: Title is missing the mandated "python" language segment
    • Fix: change ax.set_title("wireframe-3d-basic · matplotlib · anyplot.ai", ...) to ax.set_title("wireframe-3d-basic · python · matplotlib · anyplot.ai", ...)
  3. CQ-02 LOW: np.random.seed(42) is unused dead code
    • Fix: remove the seed call since no random data generation occurs, or note reproducibility is inherent since all data is deterministic

AI Feedback for Next Attempt

Fix the AR-09 edge-clipping first: the Y-axis label "Distance from Center (Y)" is being chopped off at the bottom canvas border in both themes — give it more room via labelpad, a slightly smaller fontsize, or fig.subplots_adjust(bottom=...), and re-verify with a pixel-level check of the last few rows of the saved PNG (no non-background pixels should touch y=1799). Second, add the missing "python" language segment to the title so it reads "wireframe-3d-basic · python · matplotlib · anyplot.ai". Finally, remove the unused np.random.seed(42) call since the data is fully deterministic. The wireframe + floor-contour concept, theme handling, and viewing angle are all solid — keep those.

Verdict: REJECTED

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

github-actions Bot commented Aug 4, 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=10009

🤖 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 4, 2026
@claude

claude Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

AI Review - Attempt 1/3

Image Description

Light render (plot-light.png): Warm off-white background (consistent with #FAF8F1). The title "wireframe-3d-basic · matplotlib · anyplot.ai" is centered at top and fully readable in dark ink. A single brand-green (#009E73) wireframe mesh draws a radially symmetric ripple surface z = sin(sqrt(x²+y²)), with a secondary green-to-blue contour projection echoing the same height structure on the floor plane. The X-axis label "Distance from Center (X)" (bottom right) and Z-axis label "Amplitude (Z)" (left) are fully visible. The Y-axis label "Distance from Center (Y)" (bottom left) is clipped at the bottom edge of the canvas — zooming into the pixels shows the text reading "...Distance from Center" with the letter "r" chopped mid-stroke at the very last pixel row of the PNG, and "(Y)" missing entirely. Not all text is readable — this is a hard AR-09 edge-clipping failure.

Dark render (plot-dark.png): Same layout on a warm near-black background (consistent with #1A1A17). Title, X-axis label, and Z-axis label render in light ink and are fully readable. Data colors (brand-green wireframe, floor contour) are identical to the light render — only chrome flipped, as expected. The same Y-axis label clipping is present: "Distance from Center" is cut off exactly at the canvas bottom edge with "r (Y)" missing, identical failure mode to the light render. This is genuine missing-pixel clipping in both themes, not a dark-on-dark contrast issue.

Both paragraphs above describe both renders as required.

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

Auto-Reject: AR-09 (Edge Clipping)

The Y-axis label "Distance from Center (Y)" has visible pixels missing at the bottom edge of the canvas in both plot-light.png and plot-dark.png. This is not proximity to the border — the letter "r" is chopped mid-stroke at the last pixel row and the "(Y)" suffix is entirely absent. Per the review rules, any element clipped at the canvas border is an automatic reject regardless of other quality: Score = 0, verdict = REJECTED.

Visual Quality (0/30)

  • VQ-01: Text Legibility (0/8) — title/X-label/Z-label/ticks are legible in both themes, but Y-axis label is clipped — forces 0 per AR-09.
  • VQ-02: No Overlap (0/6) — no text-on-text overlap observed; scored 0 due to AR-09 override.
  • VQ-03: Element Visibility (0/6) — wireframe mesh (rcount=25/ccount=25) gets visually tangled near the peak; also scored 0 due to AR-09 override.
  • VQ-04: Color Accessibility (0/2) — brand-green reads fine on both surfaces; scored 0 due to AR-09 override.
  • VQ-05: Layout & Canvas (0/4) — canvas dimension gate passed, but this is exactly the clipped-content failure this category exists to catch.
  • VQ-06: Axis Labels & Title (0/2) — labels are descriptive; scored 0 due to AR-09 override.
  • VQ-07: Palette Compliance (0/2) — first series is #009E73, backgrounds are theme-correct, imprint_seq used for the contour; scored 0 due to AR-09 override.

Design Excellence (0/20)

  • DE-01: Aesthetic Sophistication (0/8) - Floor contour echo is a nice touch, but scored 0 due to AR-09
  • DE-02: Visual Refinement (0/6) - Theme-adaptive panes/grid are handled well, but scored 0 due to AR-09
  • DE-03: Data Storytelling (0/6) - Clear single focal shape, but scored 0 due to AR-09

Spec Compliance (0/15)

  • SC-01: Plot Type (0/5) — correct ax.plot_wireframe, scored 0 due to AR-09
  • SC-02: Required Features (0/4) — grid in both x/y, elev=30/azim=45 as specified, all 3 axes labeled, scored 0 due to AR-09
  • SC-03: Data Mapping (0/3) — X/Y/Z mapped correctly, scored 0 due to AR-09
  • SC-04: Title & Legend (0/3) — title matches mandated format exactly, scored 0 due to AR-09

Data Quality (0/15)

  • DQ-01: Feature Coverage (0/6) — 40x40 grid within the recommended 20x20-50x50 range, scored 0 due to AR-09
  • DQ-02: Realistic Context (0/5) — neutral ripple function, scored 0 due to AR-09
  • DQ-03: Appropriate Scale (0/4) — sensible ranges, scored 0 due to AR-09

Code Quality (0/10)

  • CQ-01: KISS Structure (0/3) — flat script, scored 0 due to AR-09
  • CQ-02: Reproducibility (0/2) — seeded, scored 0 due to AR-09
  • CQ-03: Clean Imports (0/2) — only used imports, scored 0 due to AR-09
  • CQ-04: Code Elegance (0/2) — no fake functionality, scored 0 due to AR-09
  • CQ-05: Output & API (0/1) — saves plot-{THEME}.png correctly, no bbox_inches='tight', scored 0 due to AR-09

Library Mastery (0/10)

  • LM-01: Idiomatic Usage (0/5) - Correct Axes-level 3D API, scored 0 due to AR-09
  • LM-02: Distinctive Features (0/5) - Floor contour projection beyond the basic ask, scored 0 due to AR-09

Score Caps Applied

  • AR-09 auto-reject (edge clipping) — overrides all category scoring, total forced to 0/100

Strengths

  • Correct plot type (3D wireframe via ax.plot_wireframe) with the specified elevation 30 / azimuth 45 viewing angle
  • Both light and dark renders use theme-adaptive chrome tokens correctly (panes, grid, ticks, title all flip with ANYPLOT_THEME) and the brand-green wireframe color is identical across both themes
  • Nice touch adding a floor contour projection (ax.contour(..., zdir='z')) to echo the ripple's topography without competing with the primary series' brand-green identity
  • Data is a clean, deterministic z = sin(sqrt(x²+y²)) ripple function on a 40x40 grid, matching the spec's suggested example and size range

Weaknesses

  • AR-09 (blocking): the Y-axis label "Distance from Center (Y)" is clipped at the bottom edge of the canvas in both plot-light.png and plot-dark.png — the letter "r" is chopped mid-stroke and "(Y)" is missing entirely.
  • Wireframe density: rcount=25, ccount=25 on this ripple produces a visually tangled, moiré-like hairball near the peak where the ripple's rings are close together — individual grid lines become hard to distinguish there.

Issues Found

  1. AR-09 CRITICAL: Y-axis label "Distance from Center (Y)" has pixels missing at the bottom canvas edge in both themes.
    • Fix: This is almost certainly caused by labelpad=10 on ax.set_ylabel combined with the diagonal label placement at this 3D view angle pushing the label's bounding box past the bottom of the 3200×1800 figure — plt.tight_layout() has limited effect on mplot3d axis-label placement. Reduce labelpad on ax.set_ylabel (and ax.set_xlabel for consistency), and/or add explicit bottom margin via fig.subplots_adjust(bottom=...) after setting the 3D projection, and/or adjust the view angle/figure margins so the full label text (including the (Y) suffix) stays inside the saved canvas in both themes. Verify by re-cropping the bottom-left corner of both new renders after the fix.
  2. VQ-03 MEDIUM: Wireframe mesh is dense/tangled near the peak of the ripple.
    • Fix: Lower rcount/ccount to ~15-18, or reduce linewidth/alpha slightly, to keep the mesh structure legible across the whole surface rather than just at the outer rings.

AI Feedback for Next Attempt

The plot is otherwise well-executed (correct plot type, correct viewing angle, theme-adaptive chrome, nice contour-floor addition) but was auto-rejected for AR-09 edge clipping: the Y-axis label "Distance from Center (Y)" loses its final letters at the bottom canvas edge in both light and dark renders. Fix the labelpad/margin so the full Y-axis label (including "(Y)") stays inside the 3200×1800 canvas, and re-verify by inspecting the bottom-left corner pixels of both new renders. While in there, consider slightly reducing wireframe rcount/ccount to reduce visual tangling near the ripple's peak.

Verdict: REJECTED

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

github-actions Bot commented Aug 4, 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=10009

🤖 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/wireframe-3d-basic/implementations/python/matplotlib with quality_score: 91, 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-review-failed AI review action failed or timed out ai-review-rescued Review re-dispatched once after ai-review-failed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant