Skip to content

feat(pygal): implement radar-basic#9744

Merged
MarkusNeusinger merged 6 commits into
mainfrom
implementation/radar-basic/pygal
Jul 24, 2026
Merged

feat(pygal): implement radar-basic#9744
MarkusNeusinger merged 6 commits into
mainfrom
implementation/radar-basic/pygal

Conversation

@github-actions

Copy link
Copy Markdown
Contributor

Implementation: radar-basic - python/pygal

Implements the python/pygal version of radar-basic.

File: plots/radar-basic/implementations/python/pygal.py

Parent Issue: #744


🤖 impl-generate workflow

github-actions Bot added 2 commits July 24, 2026 16:08
Regen from quality 90. Addressed:
- canvas drifted to 3600x3600 (historical); reset to canonical 2400x2400 square
- title missing required {language} token; now "radar-basic · python · pygal · anyplot.ai"
- font sizes were at old style-guide minimums; raised to current native-pixel canonical targets (title 66, label 56, major_label 44, legend 44, value 36)
- DE-01/DE-02/DE-03: switched to a humanist sans font stack, added a "hero" series (highest overall average) with bolder stroke/larger dots for visual hierarchy and storytelling, tuned margins/spacing/opacity for more deliberate refinement
- legend switched to a single column to avoid text truncation at the larger legend font size
- version bump in docstring (pygal 3.1.0 -> 3.1.3, Python 3.13.13 -> 3.13.12)

Kept: three archetypal employee profiles, filled polygons with transparency, Okabe-Ito-adjacent Imprint palette in canonical order, gridlines with y_labels_major_every, interactive HTML export.
@claude

claude Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

AI Review - Attempt 1/3

Image Description

Light render (plot-light.png): Warm off-white background (#FAF8F1-consistent). Title "radar-basic · python · pygal · anyplot.ai" sits at top in dark ink, clearly legible. Six axis category labels (Communication, Creativity, Leadership, Problem Solving, Teamwork, Technical Skills) sit at the outer edge of each spoke, dark and readable with balanced sizing. Radial scale labels (0/20/40/60/80/100) run down the top spoke in a smaller, muted dark tone. Three overlapping filled polygons (~22% opacity) with solid outlines and dot markers represent three employees: Employee A (green #009E73, rendered with a bolder stroke and larger dots as the visual "hero" — highest average score), Employee B (lavender #C475FD), and Employee C (blue #4467A3). Legend sits below the chart in a single column, fully readable. All text is readable against the light background — no dark-on-light or overlap issues.

Dark render (plot-dark.png): Warm near-black background (#1A1A17-consistent). Same title, axis labels, and radial scale labels as the light render, flipped to light/off-white ink — all clearly legible against the dark surface, with no "dark-on-dark" failures anywhere (title, category labels, tick labels, and legend text are all light-colored). Data colors are identical to the light render — green #009E73 (hero), lavender #C475FD, blue #4467A3 — confirming only chrome flipped between themes, not data colors. All text is readable against the dark background.

Both renders pass the theme-readability check with no dark-on-dark or light-on-light failures, and no edge clipping was observed in either render.

Score: 87/100

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

Visual Quality (28/30)

  • VQ-01: Text Legibility (7/8) - All font sizes explicitly set via Style, readable in both themes, well-proportioned title
  • VQ-02: No Overlap (6/6) - No collisions between text or data in either render
  • VQ-03: Element Visibility (5/6) - Dots/lines clearly visible and distinguishable; could be marginally larger given only 18 total data points
  • VQ-04: Color Accessibility (2/2) - CVD-safe Imprint palette, alpha fill keeps overlapping polygons distinguishable
  • VQ-05: Layout & Canvas (4/4) - Chart fills a large, balanced portion of the 2400x2400 canvas, legend close below, nothing cut off
  • VQ-06: Axis Labels & Title (2/2) - Descriptive competency-axis labels, 0-100 radial scale gives unit context
  • VQ-07: Palette Compliance (2/2) - Canonical Imprint order, first series #009E73, identical data colors across themes, theme-correct chrome

Design Excellence (12/20)

  • DE-01: Aesthetic Sophistication (4/8) - Correctly applies mandated style-guide tokens plus a hero-emphasis touch, but still reads close to a well-configured default overall
  • DE-02: Visual Refinement (4/6) - Subtle dashed gridlines, alpha-blended fills, generous whitespace and legend spacing
  • DE-03: Data Storytelling (4/6) - Hero-series emphasis and deliberately differentiated employee profiles create visual hierarchy, though the emphasis contrast is somewhat subtle

Spec Compliance (15/15)

  • SC-01: Plot Type (5/5) - Correct radar/spider chart via pygal.Radar
  • SC-02: Required Features (4/4) - Filled polygons w/ transparency, regular gridlines, outer-edge axis labels, distinct colors + legend, closed polygons
  • SC-03: Data Mapping (3/3) - Categories correctly mapped to axes, all 6 axes show all 3 series
  • SC-04: Title & Legend (3/3) - Title matches mandated format exactly; legend labels match data

Data Quality (14/15)

  • DQ-01: Feature Coverage (5/6) - Meaningfully different profiles per employee across axes; ranges could be slightly wider for maximal contrast
  • DQ-02: Realistic Context (5/5) - Neutral, real-world employee performance review scenario, matching a spec-suggested application
  • DQ-03: Appropriate Scale (4/4) - Values (68-95) realistic and sensible for a 0-100 competency scale

Code Quality (10/10)

  • CQ-01: KISS Structure (3/3) - Imports → data → style → plot → save, no functions/classes
  • CQ-02: Reproducibility (2/2) - Fully deterministic hardcoded data
  • CQ-03: Clean Imports (2/2) - Only necessary imports; importlib/os/sys workaround needed to avoid self-shadowing the pygal package
  • CQ-04: Code Elegance (2/2) - Clean, appropriately complex, no fake functionality
  • CQ-05: Output & API (1/1) - Saves plot-{THEME}.png/.html via current render_to_png/render() API

Library Mastery (8/10)

  • LM-01: Idiomatic Usage (5/5) - Idiomatic pygal.Radar + Style usage: x_labels, add(), legend_at_bottom, dots_size, stroke_style
  • LM-02: Distinctive Features (3/5) - Uses several pygal-specific config options but no single standout feature unique to pygal (e.g. print_values unused)

Score Caps Applied

  • None

Strengths

  • Correct Imprint categorical palette in canonical order — first series is #009E73, second #C475FD, third #4467A3, identical across both renders
  • Theme-adaptive chrome is fully correct in both renders: warm off-white background in light, warm near-black in dark, all text readable with no dark-on-dark or light-on-light failures
  • Clean, deterministic KISS code structure — hardcoded literal data, no functions/classes, importlib workaround to avoid shadowing the installed pygal package by the file's own name
  • Realistic, neutral employee-competency-review scenario with genuinely differentiated profiles per axis (technical expert vs. team leader vs. creative visionary)
  • Thoughtful "hero series" visual hierarchy — the highest-average performer gets a bolder stroke and larger dots, giving the chart a clear focal point
  • Title format matches the mandated pattern exactly, and legend labels match the data series
  • No edge clipping — title, axis labels, and legend fully visible with generous margins, good canvas utilization

Weaknesses

  • Design Excellence is still close to the mandated style-guide baseline (theme tokens + Imprint palette) — beyond the hero-series emphasis there's little additional aesthetic polish that would separate it from a well-configured default
  • The hero-series emphasis (stroke width 5.5 vs 3.5, dots_size 9 vs 6) is a nice storytelling touch but fairly subtle at a glance — consider sharpening the contrast further (e.g. slightly higher stroke_opacity gap or dimming the non-hero series a touch more) so the focal point reads immediately
  • dots_size (6-9) and stroke width (3.5-5.5) are on the modest side for only 6 categories / 18 total data points — sparse radar data like this can support slightly larger markers per the data-density heuristic without crowding
  • pygal exposes distinctive features not used here (e.g. print_values=True for on-chart value labels near dots) that could push Library Mastery beyond generic idiomatic usage

Issues Found

  1. DE-01 LOW: Design still reads close to a well-configured default beyond the hero-emphasis touch
    • Fix: Add another layer of intentional design — e.g. dim the non-hero series' opacity slightly further, or refine the radial grid ring styling — to push past "well-configured default"
  2. LM-02 LOW: No single standout pygal-distinctive feature used
    • Fix: Consider print_values=True (or similar) to surface data values directly on the chart, a feature that leans into pygal's specific API surface

AI Feedback for Next Attempt

Solid, spec-compliant implementation with correct palette/theme handling and no defects — the main gap is Design Excellence. Sharpen the hero-series visual contrast (bigger gap in stroke width/dot size or opacity between the hero and the other two series) so the focal point reads at a glance, and consider one more pygal-distinctive touch (e.g. print_values) to lift Library Mastery. Everything else (spec compliance, data quality, code quality) is already strong — keep it.

Verdict: REJECTED

@github-actions github-actions Bot added quality:87 Quality score 87/100 ai-rejected Quality not OK, triggers update labels Jul 24, 2026
@MarkusNeusinger MarkusNeusinger added the ai-attempt-1 First repair attempt label Jul 24, 2026
@github-actions github-actions Bot removed the ai-rejected Quality not OK, triggers update label Jul 24, 2026
Attempt 1/4 - fixes based on AI review
@github-actions

Copy link
Copy Markdown
Contributor Author

🔧 Repair Attempt 1/4

Applied fixes based on AI review feedback.

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


🤖 impl-repair

@claude

claude Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

AI Review - Attempt 2/3

Image Description

Light render (plot-light.png): Warm off-white background (#FAF8F1). Title "radar-basic · python · pygal · anyplot.ai" in dark ink at top, clearly legible. Six category axis labels at the outer edge of each spoke, plus radial scale labels (0/20/40/60/80/100) down the top spoke, all readable dark text. Gridlines now use a two-tier treatment — faint dotted minor rings (20, 60) and firmer dashed major rings (40, 80, 100). Legend below the chart, single column, three entries, fully visible with no clipping. Three overlapping ~22%-opacity filled polygons: Employee A (hero, brand green #009E73) has a solid 7.5px stroke and 11px dots; Employee B (#C475FD) and Employee C (#4467A3) both use a dashed 3px stroke and 5px dots, receding behind the hero. All text is readable against the light background — PASS.

Dark render (plot-dark.png): Warm near-black background (#1A1A17). Same title, axis labels, radial scale, and two-tier gridline treatment as the light render, flipped to light/off-white ink — no dark-on-dark failures anywhere. Data colors and hero/non-hero stroke-and-dot styling are pixel-identical to the light render, confirming only chrome flipped. All text is readable against the dark background — PASS.

Both renders confirmed by direct pixel-level crop inspection (title/tick area, gridline rings, hero-vs-non-hero line styling, legend region).

Score: 93/100

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

Visual Quality (29/30)

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

Design Excellence (16/20)

  • DE-01: Aesthetic Sophistication (6/8) - Two-tier dotted/dashed gridlines + hero-vs-dashed non-hero line styling are genuine custom design beyond the style-guide baseline
  • DE-02: Visual Refinement (5/6) - Subtler gridline rhythm, alpha-blended fills, generous whitespace
  • DE-03: Data Storytelling (5/6) - Hero contrast now reads immediately (solid thick line + big dots vs. dashed thin lines + small dots)

Spec Compliance (15/15)

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

Data Quality (14/15)

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

Code Quality (10/10)

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

Library Mastery (9/10)

  • LM-01: Idiomatic Usage (5/5)
  • LM-02: Distinctive Features (4/5) - Creative stroke_style/guide dasharray usage; still no standout feature like print_values

Score Caps Applied

  • None

Strengths

  • Two-tier radial gridline treatment (dotted minor rings, dashed major rings) is a genuine custom design detail beyond the mandated baseline
  • Hero-vs-supporting series contrast now reads immediately: solid 7.5px/11px-dot hero vs. dashed 3px/5px-dot non-hero series, verified pixel-level in both renders
  • Correct Imprint canonical palette, identical data colors across themes, fully theme-adaptive chrome with no dark-on-dark/light-on-light failures
  • Canvas exactly 2400x2400, no edge clipping of title/labels/legend
  • Clean deterministic KISS code with the importlib self-shadow guard
  • Realistic, neutral employee-competency scenario with genuinely differentiated per-axis profiles
  • Title and legend match mandated format and data exactly

Weaknesses

  • LM-02: no use of pygal's more distinctive display features (e.g. print_values=True) — dash-pattern differentiation is nice but overall usage stays fairly conventional
  • Non-hero dots (size 5) are a touch small in absolute terms on the 2400x2400 canvas — legible but could go up 1-2px without losing the hero/non-hero gap
  • Value ranges (68-95) stay fairly compressed across all three employees — a slightly wider spread on 1-2 axes would sharpen the comparison further

Issues Found

None blocking — all previous-attempt weaknesses (flat gridline treatment, subtle hero contrast) were addressed.

AI Feedback for Next Attempt

This attempt resolved the prior review's main gaps. If iterating further: consider print_values=True or a custom value_formatter for an LM-02 boost, nudge non-hero dots up slightly, and widen the value spread on 1-2 axes for sharper contrast.

Verdict: APPROVED

@github-actions github-actions Bot added quality:93 Quality score 93/100 ai-approved Quality OK, ready for merge and removed quality:87 Quality score 87/100 labels Jul 24, 2026
@MarkusNeusinger
MarkusNeusinger merged commit d285fb1 into main Jul 24, 2026
@MarkusNeusinger
MarkusNeusinger deleted the implementation/radar-basic/pygal branch July 24, 2026 20:31
MarkusNeusinger added a commit that referenced this pull request Jul 24, 2026
…illed (#9772)

## What happened

On **2026-07-24, ~16:05–16:26 UTC** GitHub's API browned out — HTTP 502
on the
workflow-dispatch endpoint, HTTP 504 on GraphQL, HTTP 429 on codeload.
Four `radar-basic`
PRs fell out of the pipeline and stayed out, because the calls that hand
work to the next
workflow had no retry:

| PR | Library | State it was left in | Call that died |
|---|---|---|---|
| #9742 | seaborn | `ai-approved` q90 | `impl-merge` → `gh pr view` →
**504** |
| #9744 | pygal | `ai-rejected` q87 | repair dispatch → **504**;
`ai-attempt-1` also lost |
| #9749 | makie | `ai-rejected` q88 | repair dispatch → **502** |
| #9751 | highcharts | `ai-rejected` q88 | repair dispatch → **502** |

`impl-review`'s verdict step is the pipeline's **only hand-off point** —
every downstream
workflow starts from a call made there. So an unretried blip doesn't
merely fail a step; it
leaves a PR carrying a verdict label with nothing listening.

All four have since been recovered manually and merged; this PR stops it
recurring.

## Changes

**`impl-review.yml` — the verdict step**
- every API call retries 3× with linear backoff (same shape as the
existing `Extract PR info`
  retry, added 2026-05-06 for this same class of failure)
- the repair is **dispatched before** the attempt label is added, and
the label can no longer
gate it. Losing the label costs one over-strict review; losing the
dispatch costs the whole PR
- if the dispatch still exhausts its retries, the step drops
`ai-rejected` on the way out:
`ai-rejected` + `ai-attempt-N` matches **no** watchdog case (Case 2
excludes any verdict
label, Case 4 excludes PRs that already have an attempt label), while
the label-less state is
exactly Case 2 — which re-dispatches the repair at the attempt number
the remaining label
  still encodes
- the label read tracks success explicitly instead of inferring it from
empty output, and a
successful read with no verdict label now fails loudly instead of ending
the step green

**Two bugs found while verifying the above**
- **`ai-attempt-4` never existed as a label.** The attempt label encodes
the cascading
threshold (90 → 80 → 70 → 60 → 50), but the repo only has
`ai-attempt-1..3`, so on the 4th
repair the add failed every time and `|| true` swallowed it — #7268 and
#7039 both reached
"Repair Attempt 4/4" carrying only `ai-attempt-1..3`. Every 4th review
therefore re-applied
the ≥ 90 attempt-0 bar, and the attempts-exhausted branch (close PR,
remove stale
implementation) could never be reached. Labels are now created on
demand.
*This also means my first draft of this PR was a hard blocker:* with the
label add fatal, the
  4th repair would never have been dispatched at all.
- **That newly-reachable branch rendered a literal `$SCORE/100`** —
quoted heredoc. Now
expands, with the markdown backticks escaped so they can't become
command substitution.

**`impl-merge.yml`**
- the 5× merge retry re-reads PR state first. A merge that succeeded
server-side but lost its
HTTP response used to fail four more times with "already merged" and
exit 1 — and because
every post-merge step is gated on `should_run == 'true'` (implicit
`success()`), that skipped
GCS promotion, the `impl:{lib}:done` label, closing the issue and the
Postgres sync. A merged
PR with none of its bookkeeping: exactly the silent partial completion
CLAUDE.md warns about

**`auto-update-pr-branches.yml` — Dependabot PRs could never merge while
the pipeline ran**
- the workflow called `update-branch` on Dependabot PRs. That push is
authored by
`github-actions[bot]`, so GitHub gates the resulting runs behind manual
approval
(`action_required`) — and because `main` advances every few minutes
during impl merges, each
branch was re-updated long before anyone could approve. **PR #9674
accumulated 174 runs in
22 h: 162 `action_required` against only 4 green** (the original
`dependabot[bot]` push)
- Dependabot branches are now skipped. They merge fine while behind —
the `main` ruleset is
**not** strict (`strict_required_status_checks_policy: false`); the
file's header claimed the
  opposite and is corrected
- `/dependabot`'s playbook told operators to run that same harmful
`update-branch` by hand; it
  now says the opposite and documents the `@dependabot recreate` remedy

## Verification

`.github/workflows/` has no verification loop (CLAUDE.md), so this was
reviewed by a 4-lens
adversarial pass with an independent refutation round (regression /
shell-under-`bash -e` / retry idempotency / the Dependabot
premise), and every claim was re-checked against the live repo before
acting. That pass is what
caught the `ai-attempt-4` blocker and the false Dependabot-rebase
premise in my first draft.

- YAML parses; `bash -n` clean on the extracted steps
- `gh_retry` semantics exercised under `bash -e`: succeeds first try /
recovers after 2
  failures / multi-arg passthrough / hard failure aborts the step
- the corrected control flow tested for all three cases — normal,
**attempt 4 with a
permanently failing label add (dispatch must still fire)**, and dispatch
exhaustion
  (`ai-rejected` dropped, exit 1)
- confirmed the CI runner's `gh` adds labels fine; only the
locally-installed gh 2.45.0 hits
the `projectCards` GraphQL deprecation, so hard-failing on `--add-label`
is safe in CI
- ruleset strictness, the `ai-attempt-*` label set, and the poisoned
Dependabot head commits
  were all read from the live repo, not assumed

## Follow-up (not in this PR)

- #9674, #9648 and #8820 already have a `github-actions[bot]` merge
commit as their head. Once
this merges their head stops moving, so approving each gated run
**once** is enough — verified
that the required contexts do report as SUCCESS on a bot-authored head
after approval (#9674,
commit `59545076`). Doing it before this merges would just get re-gated
by the next push to
`main`. (`@dependabot recreate` is an equivalent alternative; it also
restores a clean
  `dependabot[bot]` head but re-resolves the versions.)
- Whether `auto-update-pr-branches.yml` should exist at all now that the
ruleset is non-strict
is worth deciding separately — it affects every auto-merge PR, so it
isn't folded in here.

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

---------

Co-authored-by: Claude Opus 5 (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:93 Quality score 93/100

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant