From 0327aaf6b4670c9282ad43bbdb8535c209a83965 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Jul 2026 23:29:17 +0000 Subject: [PATCH 1/5] feat(plotly): implement slope-basic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regen from quality 88. Addressed: - Change request: swapped domain from tech-product sales (matched altair) to manufacturing sector output ($M, 2023 vs 2024) to break cross-library convergence - Step 0 canvas contract: previous write_image used width=1600/height=900/ scale=3 (4800x2700, off-target); corrected to width=800/height=450/ scale=4 (3200x1800) and rescaled all font/margin/line/marker values to match the smaller layout base - Label crowding (VQ-02/DE-02): added a two-pass declutter algorithm that pushes overlapping annotation labels apart in y while keeping a thin leader line back to the true data point, so decluttering never disconnects a label from its entity - No size/weight variation on biggest movers (DE-03): line width and marker size now scale with each entity's magnitude of % change - Top/right spines (DE-02): explicit mirror=False on both axes - Fixed missing {language} token in the mandated title format ("slope-basic · plotly · anyplot.ai" -> ". . . · python · plotly · anyplot.ai") Preserved: semantic green/red direction coding, inline bilateral labels, hover tooltips, HTML export, KISS structure. --- .../implementations/python/plotly.py | 177 ++++++++++++------ 1 file changed, 115 insertions(+), 62 deletions(-) diff --git a/plots/slope-basic/implementations/python/plotly.py b/plots/slope-basic/implementations/python/plotly.py index 706b62e74e..b20f171e78 100644 --- a/plots/slope-basic/implementations/python/plotly.py +++ b/plots/slope-basic/implementations/python/plotly.py @@ -1,7 +1,7 @@ -""" anyplot.ai +"""anyplot.ai slope-basic: Basic Slope Chart (Slopegraph) Library: plotly 6.7.0 | Python 3.13.13 -Quality: 88/100 | Updated: 2026-04-30 +Quality: pending | Updated: 2026-07-25 """ import os @@ -12,115 +12,168 @@ # Theme tokens THEME = os.getenv("ANYPLOT_THEME", "light") PAGE_BG = "#FAF8F1" if THEME == "light" else "#1A1A17" -ELEVATED_BG = "#FFFDF6" if THEME == "light" else "#242420" INK = "#1A1A17" if THEME == "light" else "#F0EFE8" INK_SOFT = "#4A4A44" if THEME == "light" else "#B8B7B0" -INK_MUTED = "#6B6A63" if THEME == "light" else "#A8A79F" GRID = "rgba(26,26,23,0.10)" if THEME == "light" else "rgba(240,239,232,0.10)" -# Okabe-Ito: increase = brand green, decrease = vermillion, flat = adaptive neutral +# Imprint palette: brand green = growth, matte red = decline (finance/industrial +# gain-loss semantic exception — see default-style-guide.md "Semantic exception") COLOR_UP = "#009E73" -COLOR_DOWN = "#AE3030" # imprint red — decrease -COLOR_FLAT = INK_MUTED - -# Data - Product sales Q1 vs Q4 comparison (10 products showing various patterns) -products = [ - "Laptop Pro", - "Wireless Earbuds", - "Smart Watch", - "Tablet Ultra", - "Gaming Mouse", - "Mechanical Keyboard", - "Webcam HD", - "USB Hub", - "Portable SSD", - "Monitor Stand", +COLOR_DOWN = "#AE3030" + +# Data - US manufacturing sector output ($M), 2023 vs 2024 +sectors = [ + "Aerospace", + "Electronics", + "Pharmaceuticals", + "Machinery", + "Food Processing", + "Plastics", + "Chemicals", + "Metals", + "Automotive", + "Textiles", ] -sales_q1 = [245, 180, 120, 195, 85, 110, 45, 30, 75, 55] -sales_q4 = [310, 220, 195, 160, 145, 130, 95, 85, 70, 40] +output_2023 = [145, 260, 190, 130, 275, 80, 205, 155, 320, 95] +output_2024 = [210, 310, 225, 175, 290, 100, 195, 140, 285, 70] + +colors = [COLOR_UP if end > start else COLOR_DOWN for start, end in zip(output_2023, output_2024, strict=True)] + +# Emphasize the biggest movers with bolder lines/markers (data-driven, not decorative) +pct_changes = [abs(end - start) / start for start, end in zip(output_2023, output_2024, strict=True)] +lo, hi = min(pct_changes), max(pct_changes) +spread = hi - lo +emphasis = [(c - lo) / spread if spread else 0.5 for c in pct_changes] +line_widths = [2.0 + e * 2.0 for e in emphasis] +marker_sizes = [9 + e * 6 for e in emphasis] + + +def declutter(values, min_gap): + """Push overlapping label positions apart while keeping the stack centered.""" + order = sorted(range(len(values)), key=lambda i: values[i]) + stacked = [values[i] for i in order] + for i in range(1, len(stacked)): + if stacked[i] - stacked[i - 1] < min_gap: + stacked[i] = stacked[i - 1] + min_gap + for i in range(len(stacked) - 2, -1, -1): + if stacked[i + 1] - stacked[i] < min_gap: + stacked[i] = stacked[i + 1] - min_gap + adjusted = [0.0] * len(values) + for position, i in enumerate(order): + adjusted[i] = stacked[position] + return adjusted -colors = [] -for q1, q4 in zip(sales_q1, sales_q4, strict=True): - if q4 > q1: - colors.append(COLOR_UP) - elif q4 < q1: - colors.append(COLOR_DOWN) - else: - colors.append(COLOR_FLAT) + +all_values = output_2023 + output_2024 +y_min, y_max = min(all_values), max(all_values) +y_pad = (y_max - y_min) * 0.18 +y_axis_range = [y_min - y_pad, y_max + y_pad] +min_label_gap = (y_axis_range[1] - y_axis_range[0]) * 0.052 + +left_label_y = declutter(output_2023, min_label_gap) +right_label_y = declutter(output_2024, min_label_gap) # Plot fig = go.Figure() -for i, product in enumerate(products): +for i, sector in enumerate(sectors): fig.add_trace( go.Scatter( x=[0, 1], - y=[sales_q1[i], sales_q4[i]], + y=[output_2023[i], output_2024[i]], mode="lines+markers", - line={"color": colors[i], "width": 3}, - marker={"size": 14, "color": colors[i]}, - name=product, + line={"color": colors[i], "width": line_widths[i]}, + marker={"size": marker_sizes[i], "color": colors[i]}, + name=sector, showlegend=False, - hovertemplate=f"{product}
Q1: ${sales_q1[i]}K
Q4: ${sales_q4[i]}K", + hovertemplate=(f"{sector}
2023: ${output_2023[i]}M
2024: ${output_2024[i]}M"), ) ) -# Labels at Q1 (left side) -for i, product in enumerate(products): +# Labels at 2023 (left side) — leader line points from the (possibly nudged) +# label position back to the true data value, so decluttering never disconnects +# a label from the entity it describes. +for i, sector in enumerate(sectors): fig.add_annotation( - x=-0.05, - y=sales_q1[i], - text=f"{product}: ${sales_q1[i]}K", - showarrow=False, + x=0, + y=output_2023[i], + ax=-0.12, + ay=left_label_y[i], + axref="x", + ayref="y", + xref="x", + yref="y", + text=f"{sector}: ${output_2023[i]}M", + showarrow=True, + arrowhead=0, + arrowwidth=1, + arrowcolor=colors[i], xanchor="right", - font={"size": 16, "color": colors[i]}, + yanchor="middle", + align="right", + font={"size": 11, "color": colors[i]}, ) -# Labels at Q4 (right side) -for i, product in enumerate(products): +# Labels at 2024 (right side) +for i, sector in enumerate(sectors): fig.add_annotation( - x=1.05, - y=sales_q4[i], - text=f"${sales_q4[i]}K: {product}", - showarrow=False, + x=1, + y=output_2024[i], + ax=1.12, + ay=right_label_y[i], + axref="x", + ayref="y", + xref="x", + yref="y", + text=f"${output_2024[i]}M: {sector}", + showarrow=True, + arrowhead=0, + arrowwidth=1, + arrowcolor=colors[i], xanchor="left", - font={"size": 16, "color": colors[i]}, + yanchor="middle", + align="left", + font={"size": 11, "color": colors[i]}, ) # Style +title_text = "Manufacturing Output by Sector · slope-basic · python · plotly · anyplot.ai" +title_fontsize = round(14 * min(1.0, 67 / len(title_text))) + fig.update_layout( + autosize=False, + width=800, + height=450, paper_bgcolor=PAGE_BG, plot_bgcolor=PAGE_BG, font={"color": INK}, - title={ - "text": "Product Sales Q1 vs Q4 · slope-basic · plotly · anyplot.ai", - "font": {"size": 28, "color": INK}, - "x": 0.5, - "xanchor": "center", - }, + title={"text": title_text, "font": {"size": title_fontsize, "color": INK}, "x": 0.5, "xanchor": "center"}, xaxis={ "tickmode": "array", "tickvals": [0, 1], - "ticktext": ["Q1 2024", "Q4 2024"], - "tickfont": {"size": 22, "color": INK_SOFT}, + "ticktext": ["2023", "2024"], + "tickfont": {"size": 13, "color": INK_SOFT}, "range": [-0.5, 1.5], "showgrid": False, "zeroline": False, "linecolor": INK_SOFT, + "mirror": False, }, yaxis={ - "title": {"text": "Sales ($K)", "font": {"size": 22, "color": INK}}, - "tickfont": {"size": 18, "color": INK_SOFT}, + "title": {"text": "Output ($M)", "font": {"size": 13, "color": INK}}, + "tickfont": {"size": 11, "color": INK_SOFT}, + "range": y_axis_range, "showgrid": True, "gridwidth": 1, "gridcolor": GRID, "zeroline": False, "linecolor": INK_SOFT, + "mirror": False, }, - margin={"l": 220, "r": 220, "t": 80, "b": 60}, + margin={"l": 140, "r": 140, "t": 45, "b": 35}, ) # Save -fig.write_image(f"plot-{THEME}.png", width=1600, height=900, scale=3) +fig.write_image(f"plot-{THEME}.png", width=800, height=450, scale=4) fig.write_html(f"plot-{THEME}.html", include_plotlyjs="cdn") From 46b7a27744013b7a3ac41124aed98596b16c9023 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Jul 2026 23:29:25 +0000 Subject: [PATCH 2/5] chore(plotly): add metadata for slope-basic --- plots/slope-basic/metadata/python/plotly.yaml | 244 +----------------- 1 file changed, 10 insertions(+), 234 deletions(-) diff --git a/plots/slope-basic/metadata/python/plotly.yaml b/plots/slope-basic/metadata/python/plotly.yaml index c462247808..063f53434c 100644 --- a/plots/slope-basic/metadata/python/plotly.yaml +++ b/plots/slope-basic/metadata/python/plotly.yaml @@ -1,245 +1,21 @@ +# Per-library metadata for plotly implementation of slope-basic +# Auto-generated by impl-generate.yml + library: plotly language: python specification_id: slope-basic created: '2025-12-23T20:45:26Z' -updated: '2026-04-30T17:01:48Z' +updated: '2026-07-25T23:29:25Z' generated_by: claude-sonnet -workflow_run: 25177301713 +workflow_run: 30179281969 issue: 981 -python_version: 3.13.13 -library_version: 6.7.0 +language_version: 3.13.14 +library_version: 6.9.0 preview_url_light: https://storage.googleapis.com/anyplot-images/plots/slope-basic/python/plotly/plot-light.png preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/slope-basic/python/plotly/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/slope-basic/python/plotly/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/slope-basic/python/plotly/plot-dark.html -quality_score: 88 +quality_score: null review: - strengths: - - Semantic color encoding (green=increase, orange=decrease) creates immediate directional - communication - - All theme tokens correctly applied — both light and dark renders pass legibility - checks - - 'Perfect spec compliance: bilateral labels, direction color coding, correct time-point - labels' - - Realistic neutral business data demonstrating rank inversions and varied magnitudes - - Clean plotly idioms with proper HTML+PNG output and working interactive hover - tooltips - weaknesses: - - Label crowding in lower y-range ($30-$110K band) where 7 products compete for - vertical annotation space - - Top and right axis spines still visible — removing them would add visual refinement - - No size or weight variation on biggest movers (Laptop Pro, Tablet Ultra) to amplify - storytelling emphasis - image_description: |- - Light render (plot-light.png): - Background: Warm off-white (#FAF8F1) — correct, not pure white - Chrome: Title "Product Sales Q1 vs Q4 · slope-basic · plotly · anyplot.ai" in dark ink, clearly readable. Y-axis label "Sales ($K)" readable. X-tick labels "Q1 2024"/"Q4 2024" in INK_SOFT, readable. Annotations in matching line colors (green/orange), readable. - Data: 10 product lines — most in #009E73 (green, increasing), 3 in #D55E00 (orange, decreasing). Lines at width=3 with size-14 markers. Crossing lines visible (Tablet Ultra falls while Smart Watch rises). - Legibility verdict: PASS — all text readable; minor label crowding in lower value band ($30-$110K) but no unreadable text. - - Dark render (plot-dark.png): - Background: Warm near-black (#1A1A17) — correct, not pure black - Chrome: Title in #F0EFE8 (light), clearly readable. Axis labels and ticks in #B8B7B0, readable. Annotations in matching line colors (same green/orange as light render), readable against dark background. - Data: Colors identical to light render — green #009E73 and orange #D55E00 unchanged. Only background, text, and grid chrome flipped. No dark-on-dark failures detected. - Legibility verdict: PASS — all text readable in dark theme; brand green #009E73 highly visible on near-black surface. - criteria_checklist: - visual_quality: - score: 28 - max: 30 - items: - - id: VQ-01 - name: Text Legibility - score: 8 - max: 8 - passed: true - comment: 'All font sizes explicitly set: title 28px, x-ticks 22px, y-title - 22px, y-ticks 18px, annotations 16px' - - id: VQ-02 - name: No Overlap - score: 4 - max: 6 - passed: true - comment: Moderate label crowding in lower y-band ($30-$110K) with 7 products; - readable but not perfectly spaced - - id: VQ-03 - name: Element Visibility - score: 6 - max: 6 - passed: true - comment: Lines at width=3, markers at size=14, well-adapted for canvas size - - id: VQ-04 - name: Color Accessibility - score: 2 - max: 2 - passed: true - comment: Okabe-Ito positions 1 and 2 are CVD-safe with strong luminance contrast - - id: VQ-05 - name: Layout & Canvas - score: 4 - max: 4 - passed: true - comment: Generous bilateral margins (220px) appropriate for slope chart annotations; - balanced layout - - id: VQ-06 - name: Axis Labels & Title - score: 2 - max: 2 - passed: true - comment: Y-axis Sales ($K) with units; X-axis time-point tick labels Q1 2024/Q4 - 2024 - - id: VQ-07 - name: Palette Compliance - score: 2 - max: 2 - passed: true - comment: 'Background #FAF8F1/#1A1A17 confirmed; first series #009E73, second - #D55E00; chrome fully theme-adaptive' - design_excellence: - score: 13 - max: 20 - items: - - id: DE-01 - name: Aesthetic Sophistication - score: 5 - max: 8 - passed: true - comment: Semantic color encoding and inline labels above defaults; not yet - at strong-design level - - id: DE-02 - name: Visual Refinement - score: 4 - max: 6 - passed: true - comment: X-axis grid hidden, y-axis grid uses rgba token; top/right spines - still visible - - id: DE-03 - name: Data Storytelling - score: 4 - max: 6 - passed: true - comment: Semantic colors communicate direction immediately; crossing lines - tell rank-change story; no additional size variation for biggest movers - spec_compliance: - score: 15 - max: 15 - items: - - id: SC-01 - name: Plot Type - score: 5 - max: 5 - passed: true - comment: Correct slope chart/slopegraph with bilateral axes and connecting - lines - - id: SC-02 - name: Required Features - score: 4 - max: 4 - passed: true - comment: Labels at both endpoints, direction color coding, time-point axis - labels, 10 entities in 5-15 range - - id: SC-03 - name: Data Mapping - score: 3 - max: 3 - passed: true - comment: X=time points, Y=sales values, all 10 entities visible - - id: SC-04 - name: Title & Legend - score: 3 - max: 3 - passed: true - comment: 'Title format correct: slope-basic · plotly · anyplot.ai; inline - labels replace legend appropriately' - data_quality: - score: 15 - max: 15 - items: - - id: DQ-01 - name: Feature Coverage - score: 6 - max: 6 - passed: true - comment: Shows increases, decreases, rank inversions, steep vs gradual changes - — all slope chart features - - id: DQ-02 - name: Realistic Context - score: 5 - max: 5 - passed: true - comment: Tech product quarterly sales comparison — neutral, recognizable business - scenario - - id: DQ-03 - name: Appropriate Scale - score: 4 - max: 4 - passed: true - comment: Sales values $30K-$310K plausible for product category quarterly - sales; realistic change percentages - code_quality: - score: 10 - max: 10 - items: - - id: CQ-01 - name: KISS Structure - score: 3 - max: 3 - passed: true - comment: 'Flat: imports → tokens → data → colors → plot → annotations → layout - → save' - - id: CQ-02 - name: Reproducibility - score: 2 - max: 2 - passed: true - comment: All data hardcoded and deterministic; no random seed needed - - id: CQ-03 - name: Clean Imports - score: 2 - max: 2 - passed: true - comment: Only os and plotly.graph_objects; both used - - id: CQ-04 - name: Code Elegance - score: 2 - max: 2 - passed: true - comment: Clean iteration patterns for colors, traces, and annotations; no - over-engineering - - id: CQ-05 - name: Output & API - score: 1 - max: 1 - passed: true - comment: Saves plot-{THEME}.png (4800x2700 via scale=3) and plot-{THEME}.html; - correct for plotly - library_mastery: - score: 7 - max: 10 - items: - - id: LM-01 - name: Idiomatic Usage - score: 4 - max: 5 - passed: true - comment: Proper go.Figure/add_trace pattern with hovertemplate and update_layout; - good idiomatic plotly - - id: LM-02 - name: Distinctive Features - score: 3 - max: 5 - passed: true - comment: Interactive hover with hovertemplate and HTML export are genuinely - plotly-distinctive capabilities - verdict: APPROVED -impl_tags: - dependencies: [] - techniques: - - annotations - - hover-tooltips - - html-export - patterns: - - data-generation - - iteration-over-groups - dataprep: [] - styling: - - grid-styling + strengths: [] + weaknesses: [] From d8ea52a5235b8c3480726855c82a1c67a56c2fc3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Jul 2026 23:38:11 +0000 Subject: [PATCH 3/5] chore(plotly): update quality score 85 and review feedback for slope-basic --- .../implementations/python/plotly.py | 6 +- plots/slope-basic/metadata/python/plotly.yaml | 256 +++++++++++++++++- 2 files changed, 252 insertions(+), 10 deletions(-) diff --git a/plots/slope-basic/implementations/python/plotly.py b/plots/slope-basic/implementations/python/plotly.py index b20f171e78..17b9e2ef77 100644 --- a/plots/slope-basic/implementations/python/plotly.py +++ b/plots/slope-basic/implementations/python/plotly.py @@ -1,7 +1,7 @@ -"""anyplot.ai +""" anyplot.ai slope-basic: Basic Slope Chart (Slopegraph) -Library: plotly 6.7.0 | Python 3.13.13 -Quality: pending | Updated: 2026-07-25 +Library: plotly 6.9.0 | Python 3.13.14 +Quality: 85/100 | Updated: 2026-07-25 """ import os diff --git a/plots/slope-basic/metadata/python/plotly.yaml b/plots/slope-basic/metadata/python/plotly.yaml index 063f53434c..26383c487a 100644 --- a/plots/slope-basic/metadata/python/plotly.yaml +++ b/plots/slope-basic/metadata/python/plotly.yaml @@ -1,11 +1,8 @@ -# Per-library metadata for plotly implementation of slope-basic -# Auto-generated by impl-generate.yml - library: plotly language: python specification_id: slope-basic created: '2025-12-23T20:45:26Z' -updated: '2026-07-25T23:29:25Z' +updated: '2026-07-25T23:38:11Z' generated_by: claude-sonnet workflow_run: 30179281969 issue: 981 @@ -15,7 +12,252 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/slope-bas preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/slope-basic/python/plotly/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/slope-basic/python/plotly/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/slope-basic/python/plotly/plot-dark.html -quality_score: null +quality_score: 85 review: - strengths: [] - weaknesses: [] + strengths: + - Direct at-a-glance value labels at both endpoints for every entity, connected + to their true data point via a thin leader line, decluttered vertically so labels + never overlap each other + - 'Data-driven visual emphasis: line width and marker size scale with each sector''s + percentage change magnitude, drawing the eye to the biggest movers (Automotive''s + decline, Machinery''s rise) without decorative chartjunk' + - 'Correct semantic color mapping (brand green #009E73 for gains, matte-red Imprint + anchor #AE3030 for losses) matching the style guide''s finance gain/loss exception, + with a code comment documenting the rationale' + - Theme-adaptive chrome (background, ink, grid) correctly threaded through both + renders; palette data colors identical across light and dark + - Rich hovertemplate for the interactive HTML export exposing exact sector/year + values, a genuine plotly-specific interactivity win + weaknesses: + - 'Label/axis-spine overlap: the two longest left-side entity labels, ''Food Processing: + $275M'' and ''Pharmaceuticals: $190M'', are visually cut through by the y-axis + spine line in BOTH light and dark renders. Pixel check on plot-light.png: leftmost + text pixel of ''Food Processing'' starts at x=464 while the y-axis spine sits + at x=556-559 (3200px-wide render) - the fixed `ax=-0.12` annotation anchor doesn''t + scale with label string length, so the two longest labels overflow past the xaxis + range''s left bound (-0.5) and the spine draws directly over the letters. Fix: + scale the `ax` offset (or the xaxis range''s left bound / left margin) by the + longest label''s character count so no label text ever crosses the spine.' + - 'CQ-01 KISS structure: a custom `declutter()` helper function is defined mid-script + for anti-overlap label placement; it is short and purposeful, but the code-quality + rubric expects a flat imports -> data -> plot -> save script with no functions/classes.' + - No explicit key/caption stating that green = increase and red = decrease; the + mapping is inferable from slope direction and the numeric labels at both ends, + but an explicit note would remove any ambiguity for a first-time viewer. + image_description: |- + Light render (plot-light.png): + Background: Warm off-white, consistent with #FAF8F1 - not pure white. + Chrome: Title "Manufacturing Output by Sector · slope-basic · python · plotly · anyplot.ai" is dark ink, centered, ~70% of plot width, fully readable. Y-axis title "Output ($M)" rotated, dark ink, readable. X-axis ticks "2023"/"2024" in soft ink, readable. Y-axis numeric ticks (50-350) readable. Ten entity labels (e.g. "Automotive: $320M", "$310M: Electronics") flank the plot on left (2023 values) and right (2024 values), colored to match their line (green = increase, matte red = decrease). + Data: Ten slope lines connecting 2023 -> 2024 output values per sector. First-glance colors are Imprint brand green (#009E73, 6 increasing sectors) and Imprint matte red (#AE3030, 4 declining sectors) - correct semantic gain/loss mapping. Line width and marker size scale with % change magnitude (e.g. Machinery's steep rise and Automotive's steep drop get visibly thicker lines/larger markers than smaller moves like Chemicals). + Legibility verdict: FAIL (partial) - all text is high-contrast and readable in isolation, but the y-axis spine line visibly cuts through the "Food Processing: $275M" and "Pharmaceuticals: $190M" labels (confirmed via pixel crop: label text starts at x=464, spine sits at x=556-559 on the 3200px-wide render), because those two labels are longer than the fixed annotation anchor accounts for. + + Dark render (plot-dark.png): + Background: Warm near-black, consistent with #1A1A17 - not pure black. + Chrome: Same title, now in light ink (#F0EFE8), fully readable against the dark surface. Axis ticks and "Output ($M)" label rendered in light ink tones, readable. Gridlines subtle and visible without competing with data. No dark-on-dark failures observed elsewhere. + Data: Identical brand green (#009E73) and matte red (#AE3030) data colors to the light render, confirming only chrome flips between themes - data colors unchanged as required. + Legibility verdict: FAIL (partial) - same spine-through-label defect reproduces identically in the dark render ("Food Processing" and "Pharmaceuticals" labels cut by the axis spine at the same pixel coordinates); every other text element is clearly readable in light-on-dark contrast, with no dark-on-dark failures. + criteria_checklist: + visual_quality: + score: 24 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 6 + max: 8 + passed: true + comment: All font sizes explicitly set (title scales with title length via + formula, ticks/labels sized); readable in both themes, but the spine-overlap + defect on two labels keeps this below perfect. + - id: VQ-02 + name: No Overlap + score: 3 + max: 6 + passed: false + comment: y-axis spine visibly cuts through 'Food Processing' and 'Pharmaceuticals' + labels in both renders (confirmed via pixel inspection); the other 8 of + 10 labels are clean. + - id: VQ-03 + name: Element Visibility + score: 6 + max: 6 + passed: true + comment: Markers/lines well-sized for 10 sparse entities, with density-aware + emphasis scaling by % change magnitude. + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Green/red direction coding is redundantly backed by slope direction + and explicit numeric labels at both ends, so hue is not the sole signal. + - id: VQ-05 + name: Layout & Canvas + score: 3 + max: 4 + passed: true + comment: Canvas is exactly 3200x1800 (gate passed); balanced margins for direct + labeling either side; minor deduction for the label/spine miscalculation. + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: Y-axis 'Output ($M)' has units; X-axis ticks show the two time points + ('2023'/'2024') as required by spec. + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: Imprint brand green (#009E73) and matte red (#AE3030) correctly used + for the finance gain/loss semantic exception; backgrounds and chrome theme-correct + in both renders. + design_excellence: + score: 15 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 6 + max: 8 + passed: true + comment: Thoughtful semantic color, direct labeling instead of a legend, and + magnitude-driven emphasis are clearly above a configured default. + - id: DE-02 + name: Visual Refinement + score: 4 + max: 6 + passed: true + comment: Subtle single-axis grid, L-shaped frame (no top/right spine), generous + margins; the spine-overlap defect costs points on attention to detail. + - id: DE-03 + name: Data Storytelling + score: 5 + max: 6 + passed: true + comment: Direct labeling plus width/size emphasis on the biggest movers gives + the viewer an immediate read on which sectors changed most and in which + direction. + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Correct slopegraph connecting two vertical time-point axes. + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: Endpoint labels, direction color coding, time-point axis labels, + and entity count (10, within the 5-15 range) all present. + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: X = time point (2023/2024), Y = output value; all ten entities visible. + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Title matches '{Descriptive Title} · slope-basic · python · plotly + · anyplot.ai' format exactly; no traditional legend needed since labels + are direct. + data_quality: + score: 15 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 6 + max: 6 + passed: true + comment: Mix of 6 increasing and 4 declining sectors, with varying magnitudes + of change. + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: 'Neutral, plausible business scenario: US manufacturing sector output + 2023 vs 2024.' + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: Output values ($70M-$320M) and year-over-year deltas are plausible + for the domain. + code_quality: + score: 9 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 2 + max: 3 + passed: true + comment: One small `declutter()` helper function breaks the strict flat imports->data->plot->save + structure, though it is short and single-purpose. + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: All data is hardcoded/deterministic; no randomness. + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: Only `os` and `plotly.graph_objects` imported, both used. + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Clean and purposeful; no fake UI or over-engineering. + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Saves plot-{theme}.png and plot-{theme}.html via current write_image/write_html + API with width/height/scale=4 per the canvas rule. + library_mastery: + score: 7 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 4 + max: 5 + passed: true + comment: Solid graph_objects usage with per-trace styling and layout config. + - id: LM-02 + name: Distinctive Features + score: 3 + max: 5 + passed: true + comment: Uses annotation-based leader lines (axref/ayref-anchored) for decluttered + direct labeling and a rich per-sector hovertemplate for the interactive + HTML export - both genuinely plotly-specific. + verdict: REJECTED +impl_tags: + dependencies: [] + techniques: + - annotations + - manual-ticks + patterns: + - iteration-over-groups + dataprep: + - normalization + styling: + - grid-styling From bbbf6a08bce244898cc396a7be9f2d031f4ba3f4 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Jul 2026 23:49:08 +0000 Subject: [PATCH 4/5] fix(plotly): address review feedback for slope-basic Attempt 1/4 - fixes based on AI review --- .../implementations/python/plotly.py | 91 +++++++++++++------ 1 file changed, 61 insertions(+), 30 deletions(-) diff --git a/plots/slope-basic/implementations/python/plotly.py b/plots/slope-basic/implementations/python/plotly.py index 17b9e2ef77..94aeed00d7 100644 --- a/plots/slope-basic/implementations/python/plotly.py +++ b/plots/slope-basic/implementations/python/plotly.py @@ -1,4 +1,4 @@ -""" anyplot.ai +"""anyplot.ai slope-basic: Basic Slope Chart (Slopegraph) Library: plotly 6.9.0 | Python 3.13.14 Quality: 85/100 | Updated: 2026-07-25 @@ -49,30 +49,45 @@ marker_sizes = [9 + e * 6 for e in emphasis] -def declutter(values, min_gap): - """Push overlapping label positions apart while keeping the stack centered.""" +all_values = output_2023 + output_2024 +y_min, y_max = min(all_values), max(all_values) +y_pad = (y_max - y_min) * 0.18 +y_axis_range = [y_min - y_pad, y_max + y_pad] +min_label_gap = (y_axis_range[1] - y_axis_range[0]) * 0.052 + +# Anti-overlap label placement: push stacked label positions apart while +# keeping the stack centered on the original values (left/right run the same +# pass independently, since the two label columns never interact). +left_label_y = [] +for values in (output_2023, output_2024): order = sorted(range(len(values)), key=lambda i: values[i]) stacked = [values[i] for i in order] for i in range(1, len(stacked)): - if stacked[i] - stacked[i - 1] < min_gap: - stacked[i] = stacked[i - 1] + min_gap + if stacked[i] - stacked[i - 1] < min_label_gap: + stacked[i] = stacked[i - 1] + min_label_gap for i in range(len(stacked) - 2, -1, -1): - if stacked[i + 1] - stacked[i] < min_gap: - stacked[i] = stacked[i + 1] - min_gap + if stacked[i + 1] - stacked[i] < min_label_gap: + stacked[i] = stacked[i + 1] - min_label_gap adjusted = [0.0] * len(values) for position, i in enumerate(order): adjusted[i] = stacked[position] - return adjusted - - -all_values = output_2023 + output_2024 -y_min, y_max = min(all_values), max(all_values) -y_pad = (y_max - y_min) * 0.18 -y_axis_range = [y_min - y_pad, y_max + y_pad] -min_label_gap = (y_axis_range[1] - y_axis_range[0]) * 0.052 - -left_label_y = declutter(output_2023, min_label_gap) -right_label_y = declutter(output_2024, min_label_gap) + left_label_y.append(adjusted) +left_label_y, right_label_y = left_label_y + +# Label columns: leader-line anchors sit exactly at the xaxis range bounds (the +# same x position as the axis spine), so label text - regardless of how long a +# sector name is - always renders into the margin whitespace and never crosses +# back over the spine into the plot area. Margins are sized from the longest +# label string in each column so no text clips the canvas edge either. +x_axis_range = [-0.5, 1.5] +left_label_text = [f"{sector}: ${value}M" for sector, value in zip(sectors, output_2023, strict=True)] +right_label_text = [f"${value}M: {sector}" for sector, value in zip(sectors, output_2024, strict=True)] +label_fontsize = 11 +char_width = 0.62 * label_fontsize +left_margin = round(24 + char_width * max(len(t) for t in left_label_text)) +right_margin = round(24 + char_width * max(len(t) for t in right_label_text)) +left_anchor_x = x_axis_range[0] - 0.02 +right_anchor_x = x_axis_range[1] + 0.02 # Plot fig = go.Figure() @@ -94,17 +109,17 @@ def declutter(values, min_gap): # Labels at 2023 (left side) — leader line points from the (possibly nudged) # label position back to the true data value, so decluttering never disconnects # a label from the entity it describes. -for i, sector in enumerate(sectors): +for i in range(len(sectors)): fig.add_annotation( x=0, y=output_2023[i], - ax=-0.12, + ax=left_anchor_x, ay=left_label_y[i], axref="x", ayref="y", xref="x", yref="y", - text=f"{sector}: ${output_2023[i]}M", + text=left_label_text[i], showarrow=True, arrowhead=0, arrowwidth=1, @@ -112,21 +127,21 @@ def declutter(values, min_gap): xanchor="right", yanchor="middle", align="right", - font={"size": 11, "color": colors[i]}, + font={"size": label_fontsize, "color": colors[i]}, ) # Labels at 2024 (right side) -for i, sector in enumerate(sectors): +for i in range(len(sectors)): fig.add_annotation( x=1, y=output_2024[i], - ax=1.12, + ax=right_anchor_x, ay=right_label_y[i], axref="x", ayref="y", xref="x", yref="y", - text=f"${output_2024[i]}M: {sector}", + text=right_label_text[i], showarrow=True, arrowhead=0, arrowwidth=1, @@ -134,7 +149,7 @@ def declutter(values, min_gap): xanchor="left", yanchor="middle", align="left", - font={"size": 11, "color": colors[i]}, + font={"size": label_fontsize, "color": colors[i]}, ) # Style @@ -154,15 +169,17 @@ def declutter(values, min_gap): "tickvals": [0, 1], "ticktext": ["2023", "2024"], "tickfont": {"size": 13, "color": INK_SOFT}, - "range": [-0.5, 1.5], + "range": x_axis_range, "showgrid": False, "zeroline": False, "linecolor": INK_SOFT, "mirror": False, }, yaxis={ - "title": {"text": "Output ($M)", "font": {"size": 13, "color": INK}}, - "tickfont": {"size": 11, "color": INK_SOFT}, + # Numeric ticks are hidden: every entity is already directly labeled with + # its exact value at both endpoints, and the tick-number column would sit + # in the same margin band as those labels and collide with them. + "showticklabels": False, "range": y_axis_range, "showgrid": True, "gridwidth": 1, @@ -171,7 +188,21 @@ def declutter(values, min_gap): "linecolor": INK_SOFT, "mirror": False, }, - margin={"l": 140, "r": 140, "t": 45, "b": 35}, + margin={"l": left_margin, "r": right_margin, "t": 45, "b": 35}, +) + +# Units label placed above the plot instead of a rotated axis title, so it +# never competes with the wide left-side entity label column for margin space. +fig.add_annotation( + x=0, + y=1, + xref="paper", + yref="paper", + xanchor="left", + yanchor="bottom", + text="Output ($M)", + showarrow=False, + font={"size": 13, "color": INK}, ) # Save From 58635b84f9b14d3f38e251e99a9be18e54076db0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 25 Jul 2026 23:55:55 +0000 Subject: [PATCH 5/5] chore(plotly): update quality score 91 and review feedback for slope-basic --- .../implementations/python/plotly.py | 4 +- plots/slope-basic/metadata/python/plotly.yaml | 180 +++++++++--------- 2 files changed, 87 insertions(+), 97 deletions(-) diff --git a/plots/slope-basic/implementations/python/plotly.py b/plots/slope-basic/implementations/python/plotly.py index 94aeed00d7..a38f2448a9 100644 --- a/plots/slope-basic/implementations/python/plotly.py +++ b/plots/slope-basic/implementations/python/plotly.py @@ -1,7 +1,7 @@ -"""anyplot.ai +""" anyplot.ai slope-basic: Basic Slope Chart (Slopegraph) Library: plotly 6.9.0 | Python 3.13.14 -Quality: 85/100 | Updated: 2026-07-25 +Quality: 91/100 | Updated: 2026-07-25 """ import os diff --git a/plots/slope-basic/metadata/python/plotly.yaml b/plots/slope-basic/metadata/python/plotly.yaml index 26383c487a..94b3c1ddfc 100644 --- a/plots/slope-basic/metadata/python/plotly.yaml +++ b/plots/slope-basic/metadata/python/plotly.yaml @@ -2,7 +2,7 @@ library: plotly language: python specification_id: slope-basic created: '2025-12-23T20:45:26Z' -updated: '2026-07-25T23:38:11Z' +updated: '2026-07-25T23:55:55Z' generated_by: claude-sonnet workflow_run: 30179281969 issue: 981 @@ -12,109 +12,107 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/slope-bas preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/slope-basic/python/plotly/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/slope-basic/python/plotly/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/slope-basic/python/plotly/plot-dark.html -quality_score: 85 +quality_score: 91 review: strengths: - - Direct at-a-glance value labels at both endpoints for every entity, connected - to their true data point via a thin leader line, decluttered vertically so labels - never overlap each other + - 'Fixed both blockers from the attempt-1 review: the left-side leader labels ("Food + Processing", "Pharmaceuticals") no longer collide with the y-axis spine in either + theme (verified via pixel-level crop), and the anti-overlap declutter logic is + now fully inlined in the main script body with no helper function, satisfying + strict KISS single-script-flow.' + - 'Direct bilateral value labels ("Entity: $XM" / "$XM: Entity") at both endpoints + for all 10 sectors, each connected to its true data point via a thin leader line, + decluttered vertically so labels never overlap while never disconnecting from + their entity.' - 'Data-driven visual emphasis: line width and marker size scale with each sector''s - percentage change magnitude, drawing the eye to the biggest movers (Automotive''s - decline, Machinery''s rise) without decorative chartjunk' - - 'Correct semantic color mapping (brand green #009E73 for gains, matte-red Imprint - anchor #AE3030 for losses) matching the style guide''s finance gain/loss exception, - with a code comment documenting the rationale' + percentage-change magnitude, drawing the eye to the biggest movers (Automotive''s + decline, Machinery''s rise) without decorative chartjunk.' + - 'Correct semantic finance color mapping (brand green #009E73 for gains, Imprint + matte-red anchor #AE3030 for losses), identical across both themes as required.' - Theme-adaptive chrome (background, ink, grid) correctly threaded through both - renders; palette data colors identical across light and dark - - Rich hovertemplate for the interactive HTML export exposing exact sector/year - values, a genuine plotly-specific interactivity win + renders; rich hovertemplate exposes exact sector/year values in the interactive + HTML export. weaknesses: - - 'Label/axis-spine overlap: the two longest left-side entity labels, ''Food Processing: - $275M'' and ''Pharmaceuticals: $190M'', are visually cut through by the y-axis - spine line in BOTH light and dark renders. Pixel check on plot-light.png: leftmost - text pixel of ''Food Processing'' starts at x=464 while the y-axis spine sits - at x=556-559 (3200px-wide render) - the fixed `ax=-0.12` annotation anchor doesn''t - scale with label string length, so the two longest labels overflow past the xaxis - range''s left bound (-0.5) and the spine draws directly over the letters. Fix: - scale the `ax` offset (or the xaxis range''s left bound / left margin) by the - longest label''s character count so no label text ever crosses the spine.' - - 'CQ-01 KISS structure: a custom `declutter()` helper function is defined mid-script - for anti-overlap label placement; it is short and purposeful, but the code-quality - rubric expects a flat imports -> data -> plot -> save script with no functions/classes.' - - No explicit key/caption stating that green = increase and red = decrease; the - mapping is inferable from slope direction and the numeric labels at both ends, - but an explicit note would remove any ambiguity for a first-time viewer. + - Matte-red (#AE3030) label text has a low luminance-contrast ratio (~2.7:1) against + the dark-theme background (#1A1A17) — still legible thanks to hue separation and + generous font size, but below the general 4.5:1 text-contrast guideline. A thin + ink-colored micro-stroke on the red text/leader-lines in dark theme (per the style + guide's 'Optional outline pattern'), applied to text only while keeping the canonical + hex on the connecting line/marker, would tighten this. + - The 'Output ($M)' units annotation sits immediately beneath the title with very + little vertical gap, reading almost like an unlabeled second title line rather + than a clearly separated axis-unit caption. A bit more top margin or a lighter/smaller + treatment would clarify the hierarchy. + - No explicit caption states 'green = increase, red = decrease' — inferable from + slope direction and the endpoint values, but an explicit note would remove any + residual ambiguity for a first-time viewer (minor, optional). image_description: |- Light render (plot-light.png): - Background: Warm off-white, consistent with #FAF8F1 - not pure white. - Chrome: Title "Manufacturing Output by Sector · slope-basic · python · plotly · anyplot.ai" is dark ink, centered, ~70% of plot width, fully readable. Y-axis title "Output ($M)" rotated, dark ink, readable. X-axis ticks "2023"/"2024" in soft ink, readable. Y-axis numeric ticks (50-350) readable. Ten entity labels (e.g. "Automotive: $320M", "$310M: Electronics") flank the plot on left (2023 values) and right (2024 values), colored to match their line (green = increase, matte red = decrease). - Data: Ten slope lines connecting 2023 -> 2024 output values per sector. First-glance colors are Imprint brand green (#009E73, 6 increasing sectors) and Imprint matte red (#AE3030, 4 declining sectors) - correct semantic gain/loss mapping. Line width and marker size scale with % change magnitude (e.g. Machinery's steep rise and Automotive's steep drop get visibly thicker lines/larger markers than smaller moves like Chemicals). - Legibility verdict: FAIL (partial) - all text is high-contrast and readable in isolation, but the y-axis spine line visibly cuts through the "Food Processing: $275M" and "Pharmaceuticals: $190M" labels (confirmed via pixel crop: label text starts at x=464, spine sits at x=556-559 on the 3200px-wide render), because those two labels are longer than the fixed annotation anchor accounts for. + Background: warm off-white, consistent with #FAF8F1 — not pure white. + Chrome: dark-ink title "Manufacturing Output by Sector · slope-basic · python · plotly · anyplot.ai" centered at top, ~50% of plot width, fully readable. "Output ($M)" units annotation sits directly below the title (soft-ink but slightly under-spaced from the title line). "2023"/"2024" x-axis tick labels in soft ink, clearly readable. Y-axis numeric ticks intentionally hidden since every entity is directly labeled with its exact value. + Data: Ten slope lines connect 2023 -> 2024 values for manufacturing sectors, colored Imprint brand green (#009E73, 6 increasing sectors) or Imprint matte red (#AE3030, 4 declining sectors) — correct gain/loss semantic mapping. Line width and marker size scale with each sector's percentage-change magnitude, emphasizing the biggest movers (Automotive's decline, Machinery's rise). Bilateral value+entity labels ("Entity: $XM" left, "$XM: Entity" right) flank the chart, each linked to its true data point via a thin leader line; a prior spine-through-label collision (on "Food Processing" and "Pharmaceuticals") is fixed — verified via pixel-level crop that no label text touches the y-axis spine. + Legibility verdict: PASS — all text is clearly readable against the light background; no light-on-light issues. Dark render (plot-dark.png): - Background: Warm near-black, consistent with #1A1A17 - not pure black. - Chrome: Same title, now in light ink (#F0EFE8), fully readable against the dark surface. Axis ticks and "Output ($M)" label rendered in light ink tones, readable. Gridlines subtle and visible without competing with data. No dark-on-dark failures observed elsewhere. - Data: Identical brand green (#009E73) and matte red (#AE3030) data colors to the light render, confirming only chrome flips between themes - data colors unchanged as required. - Legibility verdict: FAIL (partial) - same spine-through-label defect reproduces identically in the dark render ("Food Processing" and "Pharmaceuticals" labels cut by the axis spine at the same pixel coordinates); every other text element is clearly readable in light-on-dark contrast, with no dark-on-dark failures. + Background: warm near-black, consistent with #1A1A17 — not pure black. + Chrome: title and "Output ($M)" annotation now render in light ink (#F0EFE8-equivalent), x-axis tick labels in soft light tone — both fully readable against the dark surface, no dark-on-dark failures on the primary chrome. + Data: identical Imprint colors to the light render (#009E73 / #AE3030), confirming only chrome flips between themes as required. Same fixed leader-line/label layout, no spine collisions reproduced. + Legibility verdict: PASS with a minor caveat — the matte-red (#AE3030) label text carries a low luminance-contrast ratio (~2.7:1 by WCAG relative-luminance calculation) against the near-black background. It remains legible (verified via direct crop inspection: text is clearly distinguishable, not "invisible" or truly dark-on-dark) but is noticeably softer/dimmer than the green counterpart. Flagged as a weakness for a future micro-polish, not a legibility failure. criteria_checklist: visual_quality: - score: 24 + score: 28 max: 30 items: - id: VQ-01 name: Text Legibility - score: 6 + score: 7 max: 8 passed: true - comment: All font sizes explicitly set (title scales with title length via - formula, ticks/labels sized); readable in both themes, but the spine-overlap - defect on two labels keeps this below perfect. + comment: All text readable in both themes; matte-red dark-theme labels (~2.7:1 + contrast) are legible but softer than the green counterpart - id: VQ-02 name: No Overlap - score: 3 + score: 6 max: 6 - passed: false - comment: y-axis spine visibly cuts through 'Food Processing' and 'Pharmaceuticals' - labels in both renders (confirmed via pixel inspection); the other 8 of - 10 labels are clean. + passed: true + comment: Prior spine-through-label collision fixed and verified via pixel + crop; no overlaps anywhere - id: VQ-03 name: Element Visibility score: 6 max: 6 passed: true - comment: Markers/lines well-sized for 10 sparse entities, with density-aware - emphasis scaling by % change magnitude. + comment: Sparse data (10 entities) given prominent markers/lines, sized by + % change magnitude - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Green/red direction coding is redundantly backed by slope direction - and explicit numeric labels at both ends, so hue is not the sole signal. + comment: CVD-safe Imprint hues; correct finance semantic exception (green=gain, + red=loss) - id: VQ-05 name: Layout & Canvas score: 3 max: 4 passed: true - comment: Canvas is exactly 3200x1800 (gate passed); balanced margins for direct - labeling either side; minor deduction for the label/spine miscalculation. + comment: Canvas gate passed (3200x1800); title/units-annotation vertical spacing + is a bit tight - id: VQ-06 name: Axis Labels & Title score: 2 max: 2 passed: true - comment: Y-axis 'Output ($M)' has units; X-axis ticks show the two time points - ('2023'/'2024') as required by spec. + comment: Units label 'Output ($M)' present; x-axis time points labeled - id: VQ-07 name: Palette Compliance score: 2 max: 2 passed: true - comment: Imprint brand green (#009E73) and matte red (#AE3030) correctly used - for the finance gain/loss semantic exception; backgrounds and chrome theme-correct - in both renders. + comment: Brand green + matte-red anchor, identical across themes; backgrounds + theme-correct design_excellence: - score: 15 + score: 16 max: 20 items: - id: DE-01 @@ -122,23 +120,20 @@ review: score: 6 max: 8 passed: true - comment: Thoughtful semantic color, direct labeling instead of a legend, and - magnitude-driven emphasis are clearly above a configured default. + comment: Thoughtful semantic color, direct labeling, magnitude-driven emphasis - id: DE-02 name: Visual Refinement - score: 4 + score: 5 max: 6 passed: true - comment: Subtle single-axis grid, L-shaped frame (no top/right spine), generous - margins; the spine-overlap defect costs points on attention to detail. + comment: Spine-overlap defect resolved; subtle grid, L-shaped frame; minor + title/subtitle crowding remains - id: DE-03 name: Data Storytelling score: 5 max: 6 passed: true - comment: Direct labeling plus width/size emphasis on the biggest movers gives - the viewer an immediate read on which sectors changed most and in which - direction. + comment: Emphasis on biggest movers gives an immediate read on direction/magnitude spec_compliance: score: 15 max: 15 @@ -148,28 +143,27 @@ review: score: 5 max: 5 passed: true - comment: Correct slopegraph connecting two vertical time-point axes. + comment: Correct slopegraph - id: SC-02 name: Required Features score: 4 max: 4 passed: true - comment: Endpoint labels, direction color coding, time-point axis labels, - and entity count (10, within the 5-15 range) all present. + comment: Bilateral labels, direction color-coding, labeled time-point axes + all present - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: X = time point (2023/2024), Y = output value; all ten entities visible. + comment: x=time point, y=value, correct - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Title matches '{Descriptive Title} · slope-basic · python · plotly - · anyplot.ai' format exactly; no traditional legend needed since labels - are direct. + comment: Title matches mandated format; direct labels substitute for a 10-entry + legend appropriately data_quality: score: 15 max: 15 @@ -179,58 +173,54 @@ review: score: 6 max: 6 passed: true - comment: Mix of 6 increasing and 4 declining sectors, with varying magnitudes - of change. + comment: 10 sectors (within 5-15 spec range), two time points, direction coding - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: 'Neutral, plausible business scenario: US manufacturing sector output - 2023 vs 2024.' + comment: Plausible, neutral US manufacturing output data - id: DQ-03 name: Appropriate Scale score: 4 max: 4 passed: true - comment: Output values ($70M-$320M) and year-over-year deltas are plausible - for the domain. + comment: Sensible $M values (70-320) for manufacturing sector output code_quality: - score: 9 + score: 10 max: 10 items: - id: CQ-01 name: KISS Structure - score: 2 + score: 3 max: 3 passed: true - comment: One small `declutter()` helper function breaks the strict flat imports->data->plot->save - structure, though it is short and single-purpose. + comment: Helper function removed; anti-overlap logic now fully inlined in + a flat script - id: CQ-02 name: Reproducibility score: 2 max: 2 passed: true - comment: All data is hardcoded/deterministic; no randomness. + comment: Fully deterministic, hardcoded data - id: CQ-03 name: Clean Imports score: 2 max: 2 passed: true - comment: Only `os` and `plotly.graph_objects` imported, both used. + comment: Only os + plotly.graph_objects - id: CQ-04 name: Code Elegance score: 2 max: 2 passed: true - comment: Clean and purposeful; no fake UI or over-engineering. + comment: No fake UI, appropriate complexity for the declutter algorithm - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Saves plot-{theme}.png and plot-{theme}.html via current write_image/write_html - API with width/height/scale=4 per the canvas rule. + comment: write_image + write_html to plot-{THEME}.png/.html library_mastery: score: 7 max: 10 @@ -240,24 +230,24 @@ review: score: 4 max: 5 passed: true - comment: Solid graph_objects usage with per-trace styling and layout config. + comment: Correct low-level go.Figure/go.Scatter/add_annotation usage, appropriate + for this custom layout - id: LM-02 name: Distinctive Features score: 3 max: 5 passed: true - comment: Uses annotation-based leader lines (axref/ayref-anchored) for decluttered - direct labeling and a rich per-sector hovertemplate for the interactive - HTML export - both genuinely plotly-specific. - verdict: REJECTED + comment: Annotation-anchored leader lines + rich hovertemplate for HTML interactivity + verdict: APPROVED impl_tags: dependencies: [] techniques: - annotations - manual-ticks + - hover-tooltips + - html-export patterns: - iteration-over-groups dataprep: - normalization - styling: - - grid-styling + styling: []