Skip to content

feat(altair): implement area-mountain-panorama#5370

Merged
github-actions[bot] merged 6 commits into
mainfrom
implementation/area-mountain-panorama/altair
Apr 25, 2026
Merged

feat(altair): implement area-mountain-panorama#5370
github-actions[bot] merged 6 commits into
mainfrom
implementation/area-mountain-panorama/altair

Conversation

@github-actions
Copy link
Copy Markdown
Contributor

Implementation: area-mountain-panorama - python/altair

Implements the python/altair version of area-mountain-panorama.

File: plots/area-mountain-panorama/implementations/python/altair.py

Parent Issue: #5365


🤖 impl-generate workflow

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 25, 2026

AI Review - Attempt 1/3

Image Description

Light render (plot-light.png): Warm off-white #FAF8F1 background. A filled area chart showing a mountain panorama silhouette of the Valais Alps (16 labeled 4000-m summits across a 180° horizontal sweep). The filled area is solid brand green (#009E73), with a thin green stroke tracing the ridgeline. Sixteen peaks are annotated with thin, subtle leader lines in INK_SOFT color from each summit point up to staggered two-row labels — peak name in bold and elevation in meters below. Y-axis labeled "Elevation (m)" with ticks at 3000/3500/4000/4500/5000; X-axis is hidden. Title reads "Wallis Panorama · area-mountain-panorama · altair · anyplot.ai" with a subtitle line in INK_SOFT. All title, axis label, and tick text is dark and clearly readable against the light background. Peak annotation labels (15px/13px base) are slightly smaller than the tick-label recommendation but are legible at final resolution. Legibility verdict: PASS.

Dark render (plot-dark.png): Near-black #1A1A17 background. The mountain silhouette retains the identical brand green (#009E73) fill and ridgeline — data colors are unchanged between themes. Leader lines, peak-name labels, and elevation labels all render in light theme-adaptive tokens (INK / INK_SOFT), yielding white-on-dark text throughout. Title and axis label are light-colored and clearly readable. No dark-on-dark failures detected — all annotation text, axis tick labels, Y-axis title, and chart title are visibly light-colored against the near-black background. Background is warm near-black (not pure #000000). Legibility verdict: PASS.

Both renders show the same 16-peak staggered-label panorama. Only chrome (background, text, leader-line color) flips between themes; the green silhouette remains identical.

Score: 82/100

Category Score Max
Visual Quality 25 30
Design Excellence 13 20
Spec Compliance 14 15
Data Quality 14 15
Code Quality 10 10
Library Mastery 6 10
Total 82 100

Visual Quality (25/30)

  • VQ-01: Text Legibility (6/8) — Title (28px), axis labels (22px), ticks (18px) all explicitly set. Peak-name annotations at 15px and elevation sub-labels at 13px are below the 18px tick-label recommendation; readable but not optimal.
  • VQ-02: No Overlap (5/6) — Staggered two-row labeling prevents most collisions. A few closely spaced peaks (e.g., Breithorn/Pollux/Castor cluster) push labels close but remain distinct.
  • VQ-03: Element Visibility (5/6) — The filled green silhouette is clearly visible and well-sized. Leader lines (1px, 55% opacity) are intentionally subtle; they read fine at full resolution.
  • VQ-04: Color Accessibility (2/2) — Single series; no CVD conflict. Good contrast between green fill and both light/dark backgrounds.
  • VQ-05: Layout & Canvas (3/4) — Canvas is 4800×2700px. The panorama fills the lower ~60% well. The annotation zone above 5000m creates meaningful whitespace for labels, but the Y-scale extends to 6300m (well above the highest label at ~6050m), leaving a small amount of unused headroom.
  • VQ-06: Axis Labels & Title (2/2) — Y-axis: "Elevation (m)" with units. X-axis hidden per spec guidance. Title correctly formatted.
  • VQ-07: Palette Compliance (2/2) — Primary series uses #009E73 (Okabe-Ito position 1). Background #FAF8F1/#1A1A17 correct in both renders. All chrome tokens theme-adaptive.

Design Excellence (13/20)

  • DE-01: Aesthetic Sophistication (5/8) — Clean, professional panoramic layout with hidden X-axis, staggered labels, and subtle leader lines. Good execution of minimalist data viz. However, the spec calls for a "photo-like silhouette, evening/dusk feel" with a dark solid fill — using the brand green produces a clean data chart rather than an evocative mountain silhouette infographic. The intended alpine-photography mood is not reached.
  • DE-02: Visual Refinement (4/6) — Grid disabled (gridOpacity=0), no view stroke, L-axis only. Clean and well-refined for a data viz approach. Missing the dusk/twilight aesthetic polish the spec envisions.
  • DE-03: Data Storytelling (4/6) — The panoramic format with named peaks tells the Wallis Alps story clearly. Matterhorn label is slightly elevated above others (label_y +100) to create a focal point. Visual hierarchy is present through staggering. The "Matterhorn as dominant focal summit" effect is mild — Monte Rosa (4634m) naturally reads as the tallest peak in the silhouette.

Spec Compliance (14/15)

  • SC-01: Plot Type (5/5) — Correct: filled area chart with ridgeline silhouette and annotated peaks.
  • SC-02: Required Features (3/4) — Two-line peak labels (name + elevation), leader lines, staggered label positions, wide panoramic aspect, Y-axis in meters with sensible lower bound — all present. The spec specifies "dark solid color (photo-like silhouette)" for the fill; the implementation uses #009E73 (bright brand green) instead of a dark color.
  • SC-03: Data Mapping (3/3) — Angle_deg on X, elevation_m on Y, all 16 summits labeled at correct positions.
  • SC-04: Title & Legend (3/3) — Title: "Wallis Panorama · area-mountain-panorama · altair · anyplot.ai". No legend needed (single series).

Data Quality (14/15)

  • DQ-01: Feature Coverage (5/6) — 16 real Valais 4000-m peaks, naturalistic minor ridge texture between summits via random sub-peak gaussians, staggered annotation zones.
  • DQ-02: Realistic Context (5/5) — Real-world Wallis/Valais, Switzerland panorama with accurate peak names and elevations.
  • DQ-03: Appropriate Scale (4/4) — Elevations 4027–4634m, Y-axis starting at 2900m, 180° angular sweep — all realistic.

Code Quality (10/10)

  • CQ-01: KISS Structure (3/3) — Linear Imports → Data → Plot → Save. No functions or classes.
  • CQ-02: Reproducibility (2/2) — np.random.seed(42).
  • CQ-03: Clean Imports (2/2) — Only altair, numpy, pandas, os, sys (all used). importlib pattern unusual but functional.
  • CQ-04: Code Elegance (2/2) — Clean Altair layer composition; gaussian peak generation is elegant.
  • CQ-05: Output & API (1/1) — Saves plot-{THEME}.png and plot-{THEME}.html. No bare plot.png.

Library Mastery (6/10)

  • LM-01: Idiomatic Usage (4/5) — Good use of Altair layer composition (+), mark_area / mark_rule / mark_text, alt.Title with subtitle/anchor/color, configure_* methods. alt.Axis(values=[...]) for explicit tick positions.
  • LM-02: Distinctive Features (2/5) — Layer composition via + is distinctly Altair. However, the HTML file is exported with no tooltips, selections, or hover interactivity — Altair's primary differentiator over static libraries. Adding tooltip encodings for peak data would be the natural Altair touch.

Score Caps Applied

  • None applied.

Strengths

  • Accurate, real-world Wallis Alps data with 16 correctly positioned and elevated peaks
  • Clean panoramic layout with hidden X-axis and staggered two-row peak labels
  • Excellent theme adaptation — both renders pass legibility checks with no dark-on-dark failures
  • Smooth gaussian ridge generation creates a naturalistic silhouette
  • Perfect code quality: seed, clean imports, KISS structure, correct output files

Weaknesses

  • Peak annotation labels are 15px/13px — below the 18px tick-label floor; raise to 18px (name) and 15px (elevation) for full legibility score
  • Fill color (#009E73 bright green) deviates from the spec's "dark solid color, photo-like silhouette, evening/dusk feel" — consider a dark slate/charcoal fill with a contrasting ridge edge or gradient that evokes the alpine dusk mood
  • No tooltips in the exported HTML — Altair's interactive capability is unused (peak name + elevation on hover would be natural and library-distinctive)
  • The Matterhorn's focal-point treatment (label_y +100) is too subtle — the Matterhorn is not the tallest peak and doesn't visually dominate; more explicit emphasis (larger font, color accent on the label) would strengthen storytelling

Issues Found

  1. VQ-01 LOW: Peak name labels at fontSize=15, elevation at fontSize=13 are below the 18px threshold
    • Fix: Raise name labels to fontSize=18, elevation labels to fontSize=15
  2. DE-01 / SC-02: Spec specifies "dark solid color" silhouette for evening/dusk photographic feel; bright #009E73 fill reads as a data chart rather than a panoramic silhouette
    • Fix: Use a dark fill (e.g., deep slate #2C3E50 or near-black) with the brand green as a ridge-edge stroke or peak highlight; OR maintain green fill but add dusk-gradient sky layer above the ridgeline to evoke the photographic mood
  3. LM-02 LOW: No interactivity in the exported HTML
    • Fix: Add tooltip=[alt.Tooltip('name:N', title='Peak'), alt.Tooltip('elevation_m:Q', title='Elevation (m)')] to the peak layers so the HTML version shows summit info on hover

AI Feedback for Next Attempt

Raise peak annotation font sizes to 18px (name) and 15px (elevation) for full VQ-01. Address the silhouette color: the spec wants "dark solid color, photo-like, evening/dusk feel" — either use a dark fill color or add a subtle sky-gradient layer above the ridgeline to achieve the panoramic mood. Add tooltip encodings to the peak marks so the HTML output leverages Altair's interactivity. Give the Matterhorn a more prominent label treatment (larger fontSize or a distinct color accent) to satisfy the "visually prominent focal point" requirement.

Verdict: REJECTED

@github-actions github-actions Bot added quality:82 Quality score 82/100 ai-rejected Quality not OK, triggers update labels Apr 25, 2026
@github-actions github-actions Bot added ai-attempt-1 First repair attempt and removed ai-rejected Quality not OK, triggers update labels Apr 25, 2026
@github-actions
Copy link
Copy Markdown
Contributor Author

🔧 Repair Attempt 1/3

Applied fixes based on AI review feedback.

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


🤖 impl-repair

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented Apr 25, 2026

AI Review - Attempt 2/3

Image Description

Light render (plot-light.png): The plot renders on a warm off-white #FAF8F1 background. A beautiful dusk gradient sky spans the upper portion — warm peach/orange at the ridgeline, transitioning through twilight rose/mauve toward a deep violet-blue at the zenith. The mountain silhouette is filled solid in brand green #009E73, creating a striking contrast against the gradient sky. All 16 Valais 4000-m summits are annotated with dark #1A1A17 text: peak names in bold (18–26px) with elevation sub-labels in #4A4A44 (15px) at staggered heights to prevent overlap. Matterhorn occupies the central position with its label lifted to the highest tier (6000 m on the y-scale) at 26px — clearly the focal summit. Thin leader lines connect each summit to its label. The left y-axis shows elevation in meters from ~3000 to 5000 with gridOpacity=0.0 (no grid lines). The title reads "Wallis Panorama · area-mountain-panorama · altair · anyplot.ai" with subtitle "Sixteen 4000-m summits along a 180° horizontal sweep, Valais Alps". All text is clearly readable against the warm light background. Legibility verdict: PASS.

Dark render (plot-dark.png): The same plot renders on a near-black #1A1A17 background. The sky gradient flips to deep space colors — very dark navy/midnight at the zenith through deep indigo to a dark maroon/rust at the ridgeline, creating a night-sky alpine feel. The mountain silhouette remains in the same brand green #009E73 (data color unchanged, identical to light render). Peak names and elevation labels switch to light-colored ink (#F0EFE8 and #B8B7B0), appearing as white/light-gray text on the dark sky — all readable. The Matterhorn focal label remains prominent in white. Leader lines use the dark-theme INK_SOFT (#B8B7B0 at 55% opacity), visible as light-gray lines against the dark sky. Y-axis tick labels and title render in light ink tones, all readable against the dark background. No "dark-on-dark" failures observed — all text colors properly flip via the theme tokens. Legibility verdict: PASS.

Both paragraphs are required. A review that only describes one render is invalid.

Score: 93/100

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

Visual Quality (29/30)

  • VQ-01: Text Legibility (7/8) — Title 28px, axis labels 22px, tick labels 18px all explicitly set; peak name labels 18–26px. Elevation sub-labels at 15px are just below the 16px legend floor. Both themes fully readable.
  • VQ-02: No Overlap (6/6) — Alternating LABEL_HIGH/LABEL_LOW stagger works cleanly; all 16 labels readable with adequate spacing.
  • VQ-03: Element Visibility (6/6) — Green silhouette fills canvas prominently; leader lines and labels all clearly visible in both themes.
  • VQ-04: Color Accessibility (2/2) — Brand green on gradient sky provides adequate contrast; CVD-safe single-color silhouette.
  • VQ-05: Layout & Canvas (4/4) — Wide landscape format well-suited to panoramic subject; plot fills the canvas appropriately.
  • VQ-06: Axis Labels & Title (2/2) — Y-axis labeled "Elevation (m)" with units; x-axis intentionally hidden per spec.
  • VQ-07: Palette Compliance (2/2) — Single series correctly uses #009E73; light background #FAF8F1, dark #1A1A17; all theme chrome properly adaptive.

Design Excellence (15/20)

  • DE-01: Aesthetic Sophistication (6/8) — Strong design well above defaults: custom dusk gradient sky, focal summit hierarchy with size contrast, intentional panoramic composition. Not quite publication-ready (gradient could be more refined, sky/silhouette junction slightly abrupt) but clearly thoughtful.
  • DE-02: Visual Refinement (5/6) — No grid (appropriate for panorama), no view stroke, generous whitespace, thin elegant leader lines. Clean and polished.
  • DE-03: Data Storytelling (4/6) — Matterhorn visually elevated as the focal summit via larger label and higher position; staggered annotations guide the viewer across the panorama. Good visual hierarchy.

Spec Compliance (15/15)

  • SC-01: Plot Type (5/5) — Correct filled-area panorama chart; silhouette skyline exactly as specified.
  • SC-02: Required Features (4/4) — Sky gradient present; all peaks annotated with leader lines; labels staggered; wide landscape format; Matterhorn as visual focal point. Silhouette uses brand green (palette rule overrides spec's "dark color" guidance).
  • SC-03: Data Mapping (3/3) — angle_deg on X, elevation_m on Y; all 16 Valais summits plotted.
  • SC-04: Title & Legend (3/3) — Title format "Wallis Panorama · area-mountain-panorama · altair · anyplot.ai" correct; no legend needed (single series).

Data Quality (15/15)

  • DQ-01: Feature Coverage (6/6) — Demonstrates all panorama chart aspects: filled silhouette, sky gradient, 16 labeled peaks, staggered annotations, focal summit emphasis.
  • DQ-02: Realistic Context (5/5) — Real Valais Alps summits with correct elevations; canonical Zermatt panorama scenario matching the spec's example data. Neutral geographic context.
  • DQ-03: Appropriate Scale (4/4) — Elevations 4027–4634 m are accurate for Swiss 4000-m peaks; y-axis lower bound at 2900 m is realistic for the valley/ridge floor.

Code Quality (10/10)

  • CQ-01: KISS Structure (3/3) — Flat linear structure: theme tokens → data → chart layers → save. No functions or classes.
  • CQ-02: Reproducibility (2/2) — np.random.seed(42) set for ridge generation.
  • CQ-03: Clean Imports (2/2) — Only altair, numpy, pandas used (loaded via importlib for path isolation); os and sys used for path fix.
  • CQ-04: Code Elegance (2/2) — Altair layer composition via + is clean and idiomatic; gradient sky via rect mark with color dict is appropriate.
  • CQ-05: Output & API (1/1) — Saves plot-{THEME}.png (scale_factor=3.0) and plot-{THEME}.html; no bare plot.png.

Library Mastery (9/10)

  • LM-01: Idiomatic Usage (5/5) — Expertly uses Altair's layer composition, mark_area/mark_rect/mark_rule/mark_text marks, encode() shorthand, configure_* global styling.
  • LM-02: Distinctive Features (4/5) — Altair gradient fill via the color dict with gradient: "linear" and stops is a distinctively Altair feature not trivially replicable in matplotlib. Layer composition with shared scales is also Altair-specific.

Score Caps Applied

  • None — all criteria above caps.

Strengths

  • Custom dusk sky gradient (theme-adaptive to both light/dark) transforms the chart into a striking landscape infographic
  • Proper Altair layer composition: sky rect + silhouette area + leaders + labels in clean declarative layers
  • Focal summit hierarchy: Matterhorn at 26px bold vs 18px for others, elevated label position — composition has a clear center of gravity
  • Complete theme adaptation with all chrome tokens (INK, INK_SOFT, PAGE_BG) correctly applied
  • Real Valais Alps data with authentic elevations for all 16 peaks

Weaknesses

  • Elevation sub-labels at 15px are marginally below the 16px minimum; raising to 16px would bring them to spec
  • DE-01 could reach 7-8 with a slightly more polished sky-to-silhouette boundary or a subtle alpenglow rim highlight at the ridgeline

Issues Found

  1. VQ-01 MINOR: Elevation sub-labels (elev_labels, matterhorn_elev) set to 15px and 18px respectively — the non-Matterhorn ones at 15px are 1px below the 16px legend floor.
    • Fix: Raise fontSize=15fontSize=16 in elev_labels mark_text call

AI Feedback for Next Attempt

Implementation is excellent. Only minor fix needed: raise the elevation sub-label font size from 15px to 16px to meet the minimum tick/legend label size floor. The dusk gradient, layer composition, and focal summit hierarchy are all strong — keep them.

Verdict: APPROVED

@github-actions github-actions Bot added quality:93 Quality score 93/100 ai-approved Quality OK, ready for merge labels Apr 25, 2026
@github-actions github-actions Bot merged commit 722e4ac into main Apr 25, 2026
@github-actions github-actions Bot deleted the implementation/area-mountain-panorama/altair branch April 25, 2026 21:52
MarkusNeusinger added a commit that referenced this pull request Apr 25, 2026
…ures (#5410)

## Summary

The implementation pipeline was leaving PRs and issues stuck after a
single Claude Code Action hiccup. Three fixes restore self-healing
behavior:

- **`impl-generate.yml`**: cap raised from **2 → 3** generation
attempts, aligning with the existing `impl:{lib}:failed` label
description (*"max retries exhausted (3 attempts)"*) and the repair
phase's 3-attempt budget. Failure comments now read `Attempt N/3`.
- **`impl-repair.yml`**: previously had no failure handler — when the
Claude Code Action itself crashed, the workflow ended with `ai-rejected`
already removed and re-review never fired, leaving the PR silently
stuck. Added a `Handle repair failure` step that restores `ai-rejected`
and auto-retries the same attempt **once** via a marker comment, then
falls back to manual.
- **`impl-review.yml`**: both failure paths (Claude crash → `Handle
review failure`, and score=0 from missing `quality_score.txt` →
`Validate review output`) immediately surfaced `ai-review-failed`,
requiring manual rerun. Both now auto-retry **once** via
`repository_dispatch` with a shared marker comment before giving up.

The `>=50% after 3 attempts` merge logic in `impl-review.yml` was
already correct and is unchanged — these fixes only ensure PRs reach
that gate instead of stalling earlier.

## Concrete trigger (not added to the PR but motivated it)

Issue #5365 (`area-mountain-panorama`) had **4/9 libraries hard-failed**
without ever creating a PR (transient Claude crashes during generate,
capped at 2 attempts), **1 PR stuck** with `ai-review-failed` (plotnine
#5372), and **1 PR stuck** mid-repair (altair #5370 — repair workflow
itself crashed on attempt 1). Manual recovery was triggered earlier in
the conversation.

## Test plan

- [ ] Trigger a generate that fails twice (e.g., simulate or wait for
transient flake) — should auto-retry to attempt 3 instead of stopping at
2
- [ ] Trigger a repair where Claude Code Action crashes — should restore
`ai-rejected` and auto-retry the same attempt once via marker comment
- [ ] Trigger a review where Claude crashes — should auto-retry via
`repository_dispatch` once before adding `ai-review-failed`
- [ ] Trigger a review where Claude runs but writes no
`quality_score.txt` — same auto-retry behavior
- [ ] Verify markers prevent infinite retry loops (each marker only
allows one auto-retry)

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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:82 Quality score 82/100 quality:93 Quality score 93/100

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants