From df49240101fcf6ecde6cc70e8a31ecf4b5abbd91 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 24 Oct 2026 11:57:04 +0000 Subject: [PATCH 1/5] feat(muix): implement probability-weibull Adopted from 5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69 (5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69), written by gemini-4-argon outside the pipeline. --- .../implementations/javascript/muix.tsx | 530 ++++++++---------- 1 file changed, 243 insertions(+), 287 deletions(-) diff --git a/plots/probability-weibull/implementations/javascript/muix.tsx b/plots/probability-weibull/implementations/javascript/muix.tsx index 6791f5d93af..a6b9ebb98a2 100644 --- a/plots/probability-weibull/implementations/javascript/muix.tsx +++ b/plots/probability-weibull/implementations/javascript/muix.tsx @@ -2,93 +2,91 @@ // probability-weibull: Weibull Probability Plot for Reliability Analysis // Library: muix 7.29.1 | JavaScript 22.23.2 // Quality: 92/100 | Created: 2026-08-24 -import { ChartContainer } from "@mui/x-charts/ChartContainer"; -import { ScatterPlot } from "@mui/x-charts/ScatterChart"; + +import { ChartsContainer } from "@mui/x-charts/ChartsContainer"; import { LinePlot } from "@mui/x-charts/LineChart"; import { ChartsXAxis } from "@mui/x-charts/ChartsXAxis"; import { ChartsYAxis } from "@mui/x-charts/ChartsYAxis"; import { ChartsGrid } from "@mui/x-charts/ChartsGrid"; import { ChartsReferenceLine } from "@mui/x-charts/ChartsReferenceLine"; -import { useDrawingArea, useYScale } from "@mui/x-charts/hooks"; +import { useDrawingArea, useXScale, useYScale } from "@mui/x-charts/hooks"; const t = window.ANYPLOT_TOKENS; -const THEME = window.ANYPLOT_THEME === "dark" ? "dark" : "light"; -// ANYPLOT_TOKENS has no "muted" anchor — derive it from default-style-guide.md -// "Theme-adaptive Chrome" (tertiary text token). -const INK_MUTED = THEME === "dark" ? "#A8A79F" : "#6B6A63"; -// --- Data: fixed-seed LCG (the browser has no seeded RNG) ------------------- -function makeLcg(seed: number) { - let state = seed >>> 0; - return () => { - state = (state * 1664525 + 1013904223) >>> 0; - return state / 4294967296; - }; -} -const rand = makeLcg(42); +// Constants +const TITLE = "probability-weibull · javascript · muix · anyplot.ai"; +const CHAR_LIFE_PCT = 63.2; +const PROB_TICKS = [0.1, 0.5, 1, 2, 5, 10, 20, 30, 50, 63.2, 80, 90, 95, 99, 99.9]; +const DEFAULT_MIN_PROB_PCT = 1; +const DEFAULT_MAX_PROB_PCT = 99; +const Y_PAD_SHARE = 0.08; +const X_MIN_FACTOR = 0.72; +const X_MAX_FACTOR = 1.38; +const MARKER_RADIUS = 7; +const HEADER_HEIGHT = 76; +const PROB_AXIS_LABEL = "Cumulative Failure Probability (%)"; +const FAILURE_LABEL = "Failure"; +const CENSORED_LABEL = "Suspended (censored)"; + +// Columns: set these to your data's column names +const TIME = "Cycles to Failure (kcycles)"; // role: x +const IS_CENSORED = "Right Censored"; // role: group -// Turbine blade fatigue-life test: cycles to failure, in thousands (kcycles). +// Data: replace this block with your own records const BETA_TRUE = 2.4; const ETA_TRUE = 420; -const N = 24; -const CENSORED_EVERY = 6; // every 6th-ranked blade pulled for inspection, unfailed +const SAMPLE_SIZE = 24; +const CENSORED_EVERY = 6; -const sortedTimes = Array.from({ length: N }, () => { - const u = rand(); - return ( - Math.round(ETA_TRUE * Math.pow(-Math.log(1 - u), 1 / BETA_TRUE) * 10) / 10 - ); -}).sort((a, b) => a - b); +const rawSamples: number[] = []; +let lcgState = 42 >>> 0; +for (let i = 0; i < SAMPLE_SIZE; i += 1) { + lcgState = (Math.imul(lcgState, 1664525) + 1013904223) >>> 0; + const u = lcgState / 4294967296; + const sampleTime = Math.round(ETA_TRUE * Math.pow(-Math.log(1 - u), 1 / BETA_TRUE) * 10) / 10; + rawSamples.push(sampleTime); +} +rawSamples.sort((a, b) => a - b); -// Never censor the very first (smallest) time — a suspension needs at least -// one preceding failure to inherit a plotting position from. -const observations = sortedTimes.map((time, idx) => ({ - time, - isCensored: idx > 0 && (idx + 1) % CENSORED_EVERY === 0, +const data: Array> = rawSamples.map((timeVal, idx) => ({ + [TIME]: timeVal, + [IS_CENSORED]: idx > 0 && (idx + 1) % CENSORED_EVERY === 0, })); -// Median-rank plotting positions via Johnson's rank-adjustment method: each -// failure's adjusted rank absorbs the "credit" left behind by suspensions -// that preceded it, then Benard's approximation turns rank into probability. +// Plot: reads only data and the constants above +const sortedObs = [...data].sort((a, b) => Number(a[TIME]) - Number(b[TIME])); +const totalN = sortedObs.length; let adjustedRank = 0; let lastProbability = 0; -const n = observations.length; -const points = observations.map((obs, idx) => { - const reverseRank = n - idx; - if (!obs.isCensored) { - adjustedRank += (n + 1 - adjustedRank) / (1 + reverseRank); - lastProbability = (adjustedRank - 0.3) / (n + 0.4); +const plottedPoints = sortedObs.map((row, idx) => { + const time = Number(row[TIME]); + const isCensored = Boolean(row[IS_CENSORED]); + const reverseRank = totalN - idx; + if (!isCensored) { + adjustedRank += (totalN + 1 - adjustedRank) / (1 + reverseRank); + lastProbability = (adjustedRank - 0.3) / (totalN + 0.4); } - return { ...obs, probability: lastProbability }; + const weibullYVal = Math.log(-Math.log(1 - lastProbability)); + return { + id: `obs-${idx}`, + time, + isCensored, + probability: lastProbability, + y: weibullYVal, + }; }); -// y = ln(-ln(1-F)) linearizes the Weibull CDF so Weibull data plots straight. -const weibullY = (p: number) => Math.log(-Math.log(1 - p)); - -const failurePoints = points.filter((p) => !p.isCensored); -const censoredPoints = points.filter((p) => p.isCensored); +const failurePoints = plottedPoints.filter((p) => !p.isCensored); +const censoredPoints = plottedPoints.filter((p) => p.isCensored); -const failureData = failurePoints.map((p, i) => ({ - x: p.time, - y: weibullY(p.probability), - id: `failure-${i}`, -})); -const censoredData = censoredPoints.map((p, i) => ({ - x: p.time, - y: weibullY(p.probability), - id: `censored-${i}`, -})); - -// Least-squares fit of ln(t) vs y over failures only: y = beta*ln(t) + b, so -// slope = shape parameter (beta) and eta = exp(-intercept / beta). const xs = failurePoints.map((p) => Math.log(p.time)); -const ys = failureData.map((d) => d.y); -const nF = xs.length; -const meanX = xs.reduce((a, b) => a + b, 0) / nF; -const meanY = ys.reduce((a, b) => a + b, 0) / nF; +const ys = failurePoints.map((p) => p.y); +const nFailures = xs.length; +const meanX = xs.reduce((a, b) => a + b, 0) / nFailures; +const meanY = ys.reduce((a, b) => a + b, 0) / nFailures; let sxy = 0; let sxx = 0; -for (let i = 0; i < nF; i += 1) { +for (let i = 0; i < nFailures; i += 1) { sxy += (xs[i] - meanX) * (ys[i] - meanY); sxx += (xs[i] - meanX) ** 2; } @@ -96,142 +94,78 @@ const beta = sxy / sxx; const intercept = meanY - beta * meanX; const eta = Math.exp(-intercept / beta); -const X_MIN = 80; -const X_MAX = 1000; -const fitLineX = [X_MIN, X_MAX]; -const fitLineY = fitLineX.map((x) => beta * Math.log(x) + intercept); - -const yAt632 = weibullY(0.632); +const minObsTime = Math.min(...plottedPoints.map((p) => p.time)); +const maxObsTime = Math.max(...plottedPoints.map((p) => p.time)); +const xMin = minObsTime * X_MIN_FACTOR; +const xMax = maxObsTime * X_MAX_FACTOR; +const yAtCharLife = Math.log(-Math.log(1 - CHAR_LIFE_PCT / 100)); -// Probability-paper y-axis: fixed percentage gridlines, deliberately NOT -// evenly spaced in y — that nonlinearity is the whole point of the transform. -const PROB_TICKS = [1, 2, 5, 10, 20, 30, 50, 63.2, 80, 90, 95, 99]; -const yTickValues = PROB_TICKS.map((p) => weibullY(p / 100)); -const yTickLabels = new Map( - PROB_TICKS.map((p, i) => [yTickValues[i], `${p}%`]), +const minPlotY = Math.min(...plottedPoints.map((p) => p.y)); +const maxPlotY = Math.max(...plottedPoints.map((p) => p.y)); +const plotYSpan = maxPlotY - minPlotY; +const yMin = Math.min( + minPlotY - plotYSpan * Y_PAD_SHARE, + Math.log(-Math.log(1 - DEFAULT_MIN_PROB_PCT / 100)), +); +const yMax = Math.max( + maxPlotY + plotYSpan * Y_PAD_SHARE, + Math.log(-Math.log(1 - DEFAULT_MAX_PROB_PCT / 100)), ); -const Y_MIN = weibullY(0.01); -const Y_MAX = weibullY(0.99); - -// Custom scatter marker: filled circle for observed failures, hollow ring for -// right-censored (suspended) units — same hue, status told by fill alone. -function StatusMarker({ series, xScale, yScale, color, markerSize }: any) { - const isCensored = series.id === "censored"; - return ( - - {series.data.map((point: any) => ( - - ))} - - ); -} -const TITLE = "probability-weibull · javascript · muix · anyplot.ai"; -const TITLE_FONT_SIZE = - TITLE.length > 67 ? Math.max(15, Math.round((22 * 67) / TITLE.length)) : 22; +const activeProbTicks = PROB_TICKS.filter((p) => { + const wy = Math.log(-Math.log(1 - p / 100)); + return wy >= yMin && wy <= yMax; +}); +const yTickValues = activeProbTicks.map((p) => Math.log(-Math.log(1 - p / 100))); +const yTickLabels = new Map(activeProbTicks.map((p, i) => [yTickValues[i], `${p}%`])); -// Chart margins — the y-axis label below is a hand-placed , not the -// built-in `label` prop, because ChartsYAxis offsets it by a fixed -// `tickFontSize + tickSize + 10` regardless of the actual (custom-formatted) -// tick label width, which collided with "63.2%"-style ticks. -// `top` is tall enough for the hand-rolled 3-row legend below the title. -const MARGIN = { top: 132, right: 64, bottom: 84, left: 140 }; +const fitXStart = Math.max(xMin, Math.exp((yMin - intercept) / beta)); +const fitXEnd = Math.min(xMax, Math.exp((yMax - intercept) / beta)); +const fitLineX = [fitXStart, fitXEnd]; +const fitLineY = fitLineX.map((x) => beta * Math.log(x) + intercept); -// Hand-rolled legend: the built-in ChartsLegend only draws solid-color square -// swatches, which can't reflect the filled-vs-hollow marker encoding used on -// the chart. Swatches here mirror the on-chart marks exactly (dash for the -// fit line, filled circle for failures, hollow ring for suspensions). -const LEGEND_FONT_SIZE = 13; -const LEGEND_ROW_HEIGHT = 22; -const LEGEND_TOP = 60; +const timeUnitMatch = /\(([^)]+)\)/.exec(TIME); +const timeUnit = timeUnitMatch ? timeUnitMatch[1] : ""; +const unitSuffix = timeUnit ? ` ${timeUnit}` : ""; +const fitLegendText = `Fit: β ≈ ${beta.toFixed(2)}, η ≈ ${Math.round(eta)}${unitSuffix}`; +const charLifeCalloutText = `${CHAR_LIFE_PCT}% · η ≈ ${Math.round(eta)}${unitSuffix}`; -function Legend({ right }: { right: number }) { - const legendItems = [ - { - kind: "dash" as const, - label: `Fit: β≈${beta.toFixed(2)}, η≈${Math.round(eta)} kcycles`, - swatchColor: t.ink, - }, - { kind: "filled" as const, label: "Failure", swatchColor: t.palette[0] }, - { - kind: "hollow" as const, - label: "Suspended (censored)", - swatchColor: t.palette[0], - }, - ]; - const legendWidth = Math.max( - ...legendItems.map( - (item) => 22 + item.label.length * LEGEND_FONT_SIZE * 0.58, - ), - ); - const left = right - legendWidth; - return ( - - {legendItems.map((item, i) => { - const y = LEGEND_TOP + i * LEGEND_ROW_HEIGHT; - return ( - - {item.kind === "dash" && ( - - )} - {item.kind === "filled" && ( - - )} - {item.kind === "hollow" && ( - - )} - - {item.label} - - - ); - })} - - ); -} - -// Small elevated callout for the eta/63.2% crossing — a filled rounded box -// behind the label sharpens the focal point beyond a thin dashed line + text. -function ReferenceCallout({ y, label }: { y: number; label: string }) { +function WeibullOverlay() { const { left, width: drawWidth } = useDrawingArea(); + const xScale = useXScale(); const yScale = useYScale(); - const yPixel = yScale(y); - const paddingX = 8; - const boxWidth = label.length * 6.8 + paddingX * 2; - const boxHeight = 22; - const boxX = left + drawWidth - boxWidth; - const boxY = yPixel - boxHeight - 8; + + const yPixelChar = Number(yScale(yAtCharLife)); + const paddingX = 10; + const boxWidth = charLifeCalloutText.length * 7.6 + paddingX * 2; + const boxHeight = 26; + const boxX = left + drawWidth - boxWidth - 10; + const boxY = yPixelChar - boxHeight - 8; + return ( + {failurePoints.map((pt) => ( + + ))} + {censoredPoints.map((pt) => ( + + ))} - {label} + {charLifeCalloutText} ); } -// --- Chart (default-exported component — the harness mounts it) ----------- export default function Chart() { const { width, height } = window.ANYPLOT_SIZE; - const plotMidY = MARGIN.top + (height - MARGIN.top - MARGIN.bottom) / 2; + const chartHeight = height - HEADER_HEIGHT; return ( - `${v}`, - label: "Cycles to Failure (thousands)", - tickLabelStyle: { fontSize: 14 }, - labelStyle: { fontSize: 16 }, - }, - ]} - yAxis={[ - { - scaleType: "linear", - min: Y_MIN, - max: Y_MAX, - tickInterval: yTickValues, - valueFormatter: (v: number) => yTickLabels.get(v) ?? "", - tickLabelStyle: { fontSize: 14 }, - }, - ]} - skipAnimation +
- - - - - - - - - - {TITLE} - - {TITLE}
+
+
+ + {fitLegendText} +
+
+ + {FAILURE_LABEL} +
+
+ + + {CENSORED_LABEL} + +
+
+ + `${Math.round(v)}`, + label: TIME, + tickLabelStyle: { fontSize: 14, fill: t.inkSoft }, + labelStyle: { fontSize: 15, fill: t.ink }, + }, + ]} + yAxis={[ + { + scaleType: "linear", + min: yMin, + max: yMax, + width: 96, + tickInterval: yTickValues, + valueFormatter: (v: number) => yTickLabels.get(v) ?? "", + label: PROB_AXIS_LABEL, + tickLabelStyle: { fontSize: 14, fill: t.inkSoft }, + labelStyle: { fontSize: 15, fill: t.ink }, + }, + ]} + skipAnimation > - Cumulative Failure Probability -
- + + + + + + + + ); } From 2e1c640b56b0e52dafd9b165cfa4aa555446eead Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 24 Oct 2026 11:57:06 +0000 Subject: [PATCH 2/5] chore(muix): add metadata for probability-weibull --- .../implementations/javascript/muix.tsx | 2 +- .../metadata/javascript/muix.yaml | 270 +----------------- 2 files changed, 13 insertions(+), 259 deletions(-) diff --git a/plots/probability-weibull/implementations/javascript/muix.tsx b/plots/probability-weibull/implementations/javascript/muix.tsx index a6b9ebb98a2..84fc8752d0c 100644 --- a/plots/probability-weibull/implementations/javascript/muix.tsx +++ b/plots/probability-weibull/implementations/javascript/muix.tsx @@ -1,7 +1,7 @@ // anyplot.ai // probability-weibull: Weibull Probability Plot for Reliability Analysis // Library: muix 7.29.1 | JavaScript 22.23.2 -// Quality: 92/100 | Created: 2026-08-24 +// Quality: pending | Created: 2026-08-24 import { ChartsContainer } from "@mui/x-charts/ChartsContainer"; import { LinePlot } from "@mui/x-charts/LineChart"; diff --git a/plots/probability-weibull/metadata/javascript/muix.yaml b/plots/probability-weibull/metadata/javascript/muix.yaml index 453af16cc59..5a5e6f94ee0 100644 --- a/plots/probability-weibull/metadata/javascript/muix.yaml +++ b/plots/probability-weibull/metadata/javascript/muix.yaml @@ -1,268 +1,22 @@ +# Per-library metadata for muix implementation of probability-weibull +# Auto-generated by impl-generate.yml + library: muix language: javascript specification_id: probability-weibull created: '2026-08-24T08:12:54Z' -updated: '2026-08-24T08:29:17Z' -generated_by: claude-sonnet -workflow_run: 32703903746 +updated: '2026-10-24T11:57:06Z' +generated_by: gemini-4-argon +adopted_from: 5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69 (5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69) +workflow_run: 40091871386 issue: 4668 -language_version: 22.23.2 -library_version: 7.29.1 +language_version: 22.23.3 +library_version: 9.16.0 preview_url_light: https://storage.googleapis.com/anyplot-images/plots/probability-weibull/javascript/muix/plot-light.png preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/probability-weibull/javascript/muix/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/probability-weibull/javascript/muix/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/probability-weibull/javascript/muix/plot-dark.html -quality_score: 92 +quality_score: null review: - strengths: - - Correctly implements median-rank plotting positions via Johnson's rank-adjustment - method for censored data (not a naive median-rank shortcut) — a genuinely reliability-engineering-accurate - technique for handling suspensions. - - Custom StatusMarker (filled circle for failures, hollow ring for suspensions) - encodes censoring by shape/fill, not color alone — CVD-safe and matches the hand-rolled - legend swatches exactly. - - Reference-line callout at 63.2% cleanly surfaces the fitted characteristic life - (eta) with a real least-squares fit computed in-code (beta and eta both annotated), - not hard-coded values. - - Correct log x-axis and Weibull-linearized y-axis (ln(-ln(1-F))) with percentage - tick labels — the nonlinear, non-evenly-spaced y-grid is exactly what real Weibull - probability paper looks like. - - 'Data colors (brand green #009E73) are identical across light and dark renders; - only chrome (background, grid, ink, callout box) adapts via ANYPLOT_TOKENS.' - weaknesses: - - Grid lines (both axes) are on the more visible side rather than a subtle backdrop - — could be lightened/thinned slightly for a cleaner probability-paper look without - losing the reference gridlines' function. - - A few adjacent points (~250 kcycles and ~380-400 kcycles clusters) sit close enough - that their 12px markers nearly touch — not a real overlap, but a touch more separation - or slightly smaller markers in dense clusters would improve individual-point readability. - - 'LM-02 headroom: the implementation already goes beyond the composable ChartContainer - API with custom slots and hooks; a further distinctive touch (e.g. a subtle tooltip - customization visible only in the interactive HTML) would round it out, though - this is a nice-to-have, not a defect.' - image_description: |- - Light render (plot-light.png): - Background: Warm off-white, consistent with #FAF8F1 — not pure white. - Chrome: Bold dark title "probability-weibull · javascript · muix · anyplot.ai" centered at top; x-axis labeled "Cycles to Failure (thousands)" (log scale, ticks 100/200/300/500/700/1000); y-axis labeled "Cumulative Failure Probability" (rotated, hand-placed) with percentage ticks from 1% to 99% on the Weibull-linearized scale; three-row hand-rolled legend top-right (dashed fit line, filled "Failure" swatch, hollow "Suspended (censored)" swatch); horizontal dotted reference line at 63.2% with an elevated callout box reading "63.2% · η ≈ 453 kcycles". All chrome text renders in dark ink against the light background. - Data: Brand green (#009E73) filled circles for failures and hollow green rings for censored/suspended units, roughly following a straight dashed fit line across the log-x / linearized-y plot — exactly the expected "straight line on Weibull paper" signature. First/only categorical series correctly uses palette[0]. - Legibility verdict: PASS — title, axis labels, tick labels, legend text, and the reference callout are all 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: Identical layout and text content to the light render, now rendered in light ink (title, axis labels, tick labels, legend text, callout text) against the dark background. Grid lines are dim gray, still visible but not dominant. - Data: Same brand green (#009E73) filled/hollow markers and dashed fit line as the light render — data colors are pixel-identical between themes, confirming only chrome flipped. - Legibility verdict: PASS — no dark-on-dark failures observed; every text element (including the small legend and callout labels) remains legible against the near-black surface. - criteria_checklist: - visual_quality: - score: 28 - max: 30 - items: - - id: VQ-01 - name: Text Legibility - score: 7 - max: 8 - passed: true - comment: All font sizes explicit (title 22px, axis labels 16px, tick labels - 14px, legend 13px); readable in both themes with no dark-on-dark or light-on-light - issues. - - id: VQ-02 - name: No Overlap - score: 6 - max: 6 - passed: true - comment: No text/legend/data collisions; legend and callout box sit clear - of the plotted data and each other. - - id: VQ-03 - name: Element Visibility - score: 5 - max: 6 - passed: true - comment: Markers (12px) are prominent for the 24-point sparse dataset; a couple - of adjacent points in dense clusters (~250, ~380-400 kcycles) sit close - enough to nearly touch. - - id: VQ-04 - name: Color Accessibility - score: 2 - max: 2 - passed: true - comment: Failure vs censored distinguished by fill/shape (filled vs hollow), - not color alone — CVD-safe. - - id: VQ-05 - name: Layout & Canvas - score: 4 - max: 4 - passed: true - comment: Good proportions, title well within bounds (~35% width at default - size), nothing clipped, generous margins. - - id: VQ-06 - name: Axis Labels & Title - score: 2 - max: 2 - passed: true - comment: Descriptive axis labels with units (kcycles, cumulative failure probability - %). - - id: VQ-07 - name: Palette Compliance - score: 2 - max: 2 - passed: true - comment: 'First/only series uses Imprint palette[0] (#009E73); backgrounds - correct #FAF8F1/#1A1A17; both renders theme-correct.' - design_excellence: - score: 15 - max: 20 - items: - - id: DE-01 - name: Aesthetic Sophistication - score: 6 - max: 8 - passed: true - comment: Custom marker slot, hand-rolled legend that mirrors the on-chart - encoding exactly, and an elevated reference callout — deliberate polish - beyond defaults. - - id: DE-02 - name: Visual Refinement - score: 4 - max: 6 - passed: true - comment: Clean chart with generous whitespace; grid is present on both axes - and is slightly more visible than a minimal-chrome ideal. - - id: DE-03 - name: Data Storytelling - score: 5 - max: 6 - passed: true - comment: The 63.2% reference line + callout creates a clear focal point (characteristic - life), and the fitted dashed line visually anchors the whole story. - spec_compliance: - score: 15 - max: 15 - items: - - id: SC-01 - name: Plot Type - score: 5 - max: 5 - passed: true - comment: 'Correct Weibull probability plot: log-x, linearized Weibull y-axis, - fitted straight line.' - - id: SC-02 - name: Required Features - score: 4 - max: 4 - passed: true - comment: Log x-axis, linearized y-axis, annotated beta/eta, median-rank plotting - positions (i-0.3)/(n+0.4) via Johnson's rank-adjustment, censored-vs-failure - marker distinction, 63.2% reference line — all present. - - id: SC-03 - name: Data Mapping - score: 3 - max: 3 - passed: true - comment: x = time-to-failure, y = weibullY(probability); full data range shown - (80-1000 x-axis, 1%-99% y-axis). - - id: SC-04 - name: Title & Legend - score: 3 - max: 3 - passed: true - comment: Title exactly matches 'probability-weibull · javascript · muix · - anyplot.ai'; legend labels (Fit, Failure, Suspended (censored)) match the - data. - data_quality: - score: 15 - max: 15 - items: - - id: DQ-01 - name: Feature Coverage - score: 6 - max: 6 - passed: true - comment: Shows failures, suspensions, fitted line, and the characteristic-life - reference — all plot-type aspects covered. - - id: DQ-02 - name: Realistic Context - score: 5 - max: 5 - passed: true - comment: Turbine blade fatigue-life data with periodic inspection-driven censoring - is plausible and neutral. - - id: DQ-03 - name: Appropriate Scale - score: 4 - max: 4 - passed: true - comment: Kcycles values (roughly 80-1000) are sensible for turbine blade fatigue - testing. - code_quality: - score: 10 - max: 10 - items: - - id: CQ-01 - name: KISS Structure - score: 3 - max: 3 - passed: true - comment: Functions present are React-required (component + small slot helpers) - or clearly justified (LCG, StatusMarker, Legend, ReferenceCallout) — no - gratuitous abstraction. - - id: CQ-02 - name: Reproducibility - score: 2 - max: 2 - passed: true - comment: Fixed-seed LCG (seed=42) makes data fully deterministic. - - id: CQ-03 - name: Clean Imports - score: 2 - max: 2 - passed: true - comment: Every import (ChartContainer, ScatterPlot, LinePlot, axes, grid, - reference line, hooks) is used. - - id: CQ-04 - name: Code Elegance - score: 2 - max: 2 - passed: true - comment: No fake interactivity; comments explain non-obvious design tradeoffs - (custom legend, hand-placed y-label). - - id: CQ-05 - name: Output & API - score: 1 - max: 1 - passed: true - comment: 'Correct harness contract: default-exported component sized to ANYPLOT_SIZE, - skipAnimation set on ChartContainer and LinePlot.' - library_mastery: - score: 9 - max: 10 - items: - - id: LM-01 - name: Idiomatic Usage - score: 5 - max: 5 - passed: true - comment: Uses the composable ChartContainer + individual plot/axis components - pattern (the recommended advanced MUI X API) rather than a monolithic chart - wrapper. - - id: LM-02 - name: Distinctive Features - score: 4 - max: 5 - passed: true - comment: Custom marker via `slots.scatter`, useDrawingArea/useYScale hooks - for the reference callout, ChartsReferenceLine — genuinely distinctive MUI - X usage, though the interactive HTML surface (tooltips) isn't leveraged - further. - verdict: APPROVED -impl_tags: - dependencies: [] - techniques: - - annotations - - custom-legend - - manual-ticks - patterns: - - data-generation - dataprep: - - regression - styling: - - edge-highlighting + strengths: [] + weaknesses: [] From 22124f7c35780a5f574860d360aa030e5900eb34 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 24 Oct 2026 12:00:08 +0000 Subject: [PATCH 3/5] chore(muix): store the review for probability-weibull --- .../implementations/javascript/muix.tsx | 4 +- .../metadata/javascript/muix.yaml | 246 +++++++++++++++++- 2 files changed, 241 insertions(+), 9 deletions(-) diff --git a/plots/probability-weibull/implementations/javascript/muix.tsx b/plots/probability-weibull/implementations/javascript/muix.tsx index 84fc8752d0c..fa3a4c7636f 100644 --- a/plots/probability-weibull/implementations/javascript/muix.tsx +++ b/plots/probability-weibull/implementations/javascript/muix.tsx @@ -1,7 +1,7 @@ // anyplot.ai // probability-weibull: Weibull Probability Plot for Reliability Analysis -// Library: muix 7.29.1 | JavaScript 22.23.2 -// Quality: pending | Created: 2026-08-24 +// Library: muix 9.16.0 | JavaScript 22.23.3 +// Quality: 85/100 | Updated: 2026-10-24 import { ChartsContainer } from "@mui/x-charts/ChartsContainer"; import { LinePlot } from "@mui/x-charts/LineChart"; diff --git a/plots/probability-weibull/metadata/javascript/muix.yaml b/plots/probability-weibull/metadata/javascript/muix.yaml index 5a5e6f94ee0..bc8718370ec 100644 --- a/plots/probability-weibull/metadata/javascript/muix.yaml +++ b/plots/probability-weibull/metadata/javascript/muix.yaml @@ -1,11 +1,8 @@ -# Per-library metadata for muix implementation of probability-weibull -# Auto-generated by impl-generate.yml - library: muix language: javascript specification_id: probability-weibull created: '2026-08-24T08:12:54Z' -updated: '2026-10-24T11:57:06Z' +updated: '2026-10-24T12:00:08Z' generated_by: gemini-4-argon adopted_from: 5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69 (5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69) workflow_run: 40091871386 @@ -16,7 +13,242 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/probabili preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/probability-weibull/javascript/muix/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/probability-weibull/javascript/muix/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/probability-weibull/javascript/muix/plot-dark.html -quality_score: null +quality_score: 85 review: - strengths: [] - weaknesses: [] + strengths: + - 'Correct Weibull probability paper: ChartsContainer x axis uses scaleType "log" + and the y axis is the linearized ln(-ln(1-F)) scale with tickInterval/valueFormatter + mapping back to readable percentage ticks (1%…99%, including 63.2%)' + - Median-rank plotting positions (i-0.3)/(n+0.4) with Johnson adjusted ranks for + the suspensions, a proper treatment of right-censored data rather than simply + dropping it + - Rank-regression fit gives beta ≈ 2.09 and eta ≈ 453 kcycles; the dashed fit line + visibly crosses the 63.2% ChartsReferenceLine at about 450 kcycles, so the annotated + values agree with the line + - Censored observations are hollow Imprint-green rings against filled failures, + and the difference reads clearly in both themes + - Composes the chart from ChartsContainer + LinePlot + ChartsReferenceLine + ChartsGrid + and draws the markers through useXScale/useYScale/useDrawingArea, so marks sit + exactly at their data values + - 'Fully theme-token driven: fit line in ink, reference line in inkSoft, callout + on elevatedBg, so both renders are legible with identical data colours' + - Deterministic LCG-generated turbine-blade fatigue data (24 units, 4 suspensions) + in a realistic kcycle range + weaknesses: + - 'VQ-05 (both): the log x axis uses 50-kcycle linear tick steps, so the labels + bunch together on the right ("750 800 850 900" sit about 10–15 px apart and the + 950/1050 ticks carry no label) while the left third has a single "100" label → + log-friendly ticks (e.g. 100, 150, 200, 300, 500, 700, 1000) with roughly even + spacing. Likely cause: no tickInterval set on the log x axis, so MUI''s default + linear-step ticks are used.' + - 'Suggestion: draw the failures and suspensions as two ScatterPlot series (hollow + via slotProps/sx) so MUI''s native ChartsLegend and tooltips cover them instead + of the hand-built HTML legend.' + - 'Suggestion: anchor the 63.2% callout near the fit/reference-line crossing at + eta instead of the far right edge so the eye connects the label with the crossing.' + image_description: |- + Light render (plot-light.png): + Background: warm off-white (#FAF8F1), not pure white. + Chrome: bold dark title "probability-weibull · javascript · muix · anyplot.ai" top-left; HTML legend top-right (dashed "Fit: β ≈ 2.09, η ≈ 453 kcycles", filled "Failure", hollow "Suspended (censored)"); y-axis "Cumulative Failure Probability (%)" with ticks 1%…99% incl. 63.2%; x-axis "Cycles to Failure (kcycles)" log scale 100–1100 (labels crowd on the right: 750 800 850 900). Subtle grid. All text readable dark-on-light. + Data: 20 filled #009E73 failure markers and 4 hollow green suspension rings around a black dashed fit line spanning the plot; dotted horizontal reference at 63.2% with a boxed callout "63.2% · η ≈ 453 kcycles" at the right; line crosses the reference near 450. + Legibility verdict: PASS + + Dark render (plot-dark.png): + Background: warm near-black (#1A1A17), not pure black. + Chrome: title, legend text, axis labels and tick labels in light ink, clearly readable; grid subtle grey; callout box on elevated dark surface with light text. No dark-on-dark text. + Data: identical #009E73 filled and hollow markers; fit line flips to light ink; reference line light dotted. Data colours identical to the light render. + Legibility verdict: PASS + criteria_checklist: + visual_quality: + score: 29 + max: 30 + items: + - id: VQ-01 + name: Text Legibility + score: 8 + max: 8 + passed: true + comment: Title 22px, ticks 14px, labels 15px; readable in both themes + - id: VQ-02 + name: No Overlap + score: 6 + max: 6 + passed: true + comment: No text collisions; callout clear of markers + - id: VQ-03 + name: Element Visibility + score: 6 + max: 6 + passed: true + comment: 24 sparse points at r=7 clearly visible; legend glyphs match marks + in both themes + - id: VQ-04 + name: Color Accessibility + score: 2 + max: 2 + passed: true + comment: Filled vs hollow encodes censoring, not colour; good contrast + - id: VQ-05 + name: Layout & Canvas + score: 3 + max: 4 + passed: true + comment: x-axis tick labels bunch on the right of the log axis (defect line) + - id: VQ-06 + name: Axis Labels & Title + score: 2 + max: 2 + passed: true + comment: Descriptive labels with units (kcycles, %) + - id: VQ-07 + name: Palette Compliance + score: 2 + max: 2 + passed: true + comment: '#009E73 for data, theme-correct backgrounds, chrome via tokens' + design_excellence: + score: 10 + max: 20 + items: + - id: DE-01 + name: Aesthetic Sophistication + score: 5 + max: 8 + passed: true + comment: 'Custom header band: title left, compact inline legend right, clear + weight hierarchy beyond baseline' + - id: DE-02 + name: Visual Refinement + score: 3 + max: 6 + passed: true + comment: Distinct dash rhythms for fit (12/7) vs reference (4/4), page-colour + marker outlines, rounded elevated callout + - id: DE-03 + name: Data Storytelling + score: 2 + max: 6 + passed: false + comment: Displays what the Notes ask; no further hierarchy chosen + spec_compliance: + score: 15 + max: 15 + items: + - id: SC-01 + name: Plot Type + score: 5 + max: 5 + passed: true + comment: Weibull probability plot with fitted line + - id: SC-02 + name: Required Features + score: 4 + max: 4 + passed: true + comment: Log x, linearized Weibull y, median ranks, beta/eta annotated, hollow + censored markers, 63.2% reference line — all present + - id: SC-03 + name: Data Mapping + score: 3 + max: 3 + passed: true + comment: Marks at their data values via scale hooks + - id: SC-04 + name: Title & Legend + score: 3 + max: 3 + passed: true + comment: Correct title format; legend entries match marks + data_quality: + score: 15 + max: 15 + items: + - id: DQ-01 + name: Feature Coverage + score: 6 + max: 6 + passed: true + comment: Failures, suspensions, fit, char-life crossing all shown + - id: DQ-02 + name: Realistic Context + score: 5 + max: 5 + passed: true + comment: Turbine blade fatigue life in kcycles + - id: DQ-03 + name: Appropriate Scale + score: 4 + max: 4 + passed: true + comment: 'Checked: fit line crosses 63.2% at ~450 kcycles matching η ≈ 453; + β 2.09 plausible for wear-out (true 2.4, n=24)' + code_quality: + score: 10 + max: 10 + items: + - id: CQ-01 + name: KISS Structure + score: 3 + max: 3 + passed: true + comment: Linear module-level flow; one overlay component needed for hooks + - id: CQ-02 + name: Reproducibility + score: 2 + max: 2 + passed: true + comment: Fixed-seed LCG + - id: CQ-03 + name: Clean Imports + score: 2 + max: 2 + passed: true + comment: All imports used + - id: CQ-04 + name: Code Elegance + score: 2 + max: 2 + passed: true + comment: Short written-out regression is allowed per library prompt + - id: CQ-05 + name: Output & API + score: 1 + max: 1 + passed: true + comment: Default export, skipAnimation, v9 ChartsContainer API + library_mastery: + score: 6 + max: 10 + items: + - id: LM-01 + name: Idiomatic Usage + score: 4 + max: 5 + passed: true + comment: Composition API (ChartsContainer, LinePlot, ChartsReferenceLine, + ChartsGrid, axes) with log scaleType and valueFormatter + - id: LM-02 + name: Distinctive Features + score: 2 + max: 5 + passed: false + comment: Custom SVG overlay via useXScale/useYScale/useDrawingArea hooks + verdict: REJECTED + model: claude-opus-5-5 + criteria_version: qc-899b86ffde.aqr-869a70087c.sg-c73b4641dd.lib-f7283eb626 + rendered_at: '2026-10-24T11:57:26Z' +impl_tags: + dependencies: [] + techniques: + - layer-composition + - custom-legend + - annotations + - manual-ticks + - hover-tooltips + - html-export + patterns: + - data-generation + dataprep: + - regression + styling: + - publication-ready From e2a1633443dee887251c38a45f499c24a80c2dd7 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 24 Oct 2026 12:01:52 +0000 Subject: [PATCH 4/5] fix(muix): address review feedback for probability-weibull MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Attempt 1/4 - fixes based on AI review - VQ-05: log x axis now uses log-friendly ticks (1-1.5-2-3-5-7 per decade, derived from the axis range) instead of MUI's default linear steps Declined: - "Suggestion: draw failures and suspensions as two ScatterPlot series" — suggestion, not taken - "Suggestion: anchor the 63.2% callout near the crossing at eta" — suggestion, not taken --- .../implementations/javascript/muix.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/plots/probability-weibull/implementations/javascript/muix.tsx b/plots/probability-weibull/implementations/javascript/muix.tsx index fa3a4c7636f..214b810b7d7 100644 --- a/plots/probability-weibull/implementations/javascript/muix.tsx +++ b/plots/probability-weibull/implementations/javascript/muix.tsx @@ -17,6 +17,7 @@ const t = window.ANYPLOT_TOKENS; const TITLE = "probability-weibull · javascript · muix · anyplot.ai"; const CHAR_LIFE_PCT = 63.2; const PROB_TICKS = [0.1, 0.5, 1, 2, 5, 10, 20, 30, 50, 63.2, 80, 90, 95, 99, 99.9]; +const LOG_TICK_MANTISSAS = [1, 1.5, 2, 3, 5, 7]; const DEFAULT_MIN_PROB_PCT = 1; const DEFAULT_MAX_PROB_PCT = 99; const Y_PAD_SHARE = 0.08; @@ -98,7 +99,14 @@ const minObsTime = Math.min(...plottedPoints.map((p) => p.time)); const maxObsTime = Math.max(...plottedPoints.map((p) => p.time)); const xMin = minObsTime * X_MIN_FACTOR; const xMax = maxObsTime * X_MAX_FACTOR; -const yAtCharLife = Math.log(-Math.log(1 - CHAR_LIFE_PCT / 100)); +const xTickValues: number[] = []; +for (let decade = Math.floor(Math.log10(xMin)); decade <= Math.ceil(Math.log10(xMax)); decade += 1) { + for (const m of LOG_TICK_MANTISSAS) { + const tick = m * 10 ** decade; + if (tick >= xMin && tick <= xMax) xTickValues.push(tick); + } +} +const yAtCharLife =Math.log(-Math.log(1 - CHAR_LIFE_PCT / 100)); const minPlotY = Math.min(...plottedPoints.map((p) => p.y)); const maxPlotY = Math.max(...plottedPoints.map((p) => p.y)); @@ -275,7 +283,8 @@ export default function Chart() { min: xMin, max: xMax, height: 58, - valueFormatter: (v: number) => `${Math.round(v)}`, + tickInterval: xTickValues, + valueFormatter: (v: number) => `${Number(v.toPrecision(3))}`, label: TIME, tickLabelStyle: { fontSize: 14, fill: t.inkSoft }, labelStyle: { fontSize: 15, fill: t.ink }, From 8503f090fa8671a323c600120b237a7b97b26fe6 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 24 Oct 2026 12:05:19 +0000 Subject: [PATCH 5/5] chore(muix): store the review for probability-weibull --- .../implementations/javascript/muix.tsx | 2 +- .../metadata/javascript/muix.yaml | 152 +++++++++--------- 2 files changed, 77 insertions(+), 77 deletions(-) diff --git a/plots/probability-weibull/implementations/javascript/muix.tsx b/plots/probability-weibull/implementations/javascript/muix.tsx index 214b810b7d7..bf171a13157 100644 --- a/plots/probability-weibull/implementations/javascript/muix.tsx +++ b/plots/probability-weibull/implementations/javascript/muix.tsx @@ -1,7 +1,7 @@ // anyplot.ai // probability-weibull: Weibull Probability Plot for Reliability Analysis // Library: muix 9.16.0 | JavaScript 22.23.3 -// Quality: 85/100 | Updated: 2026-10-24 +// Quality: 87/100 | Updated: 2026-10-24 import { ChartsContainer } from "@mui/x-charts/ChartsContainer"; import { LinePlot } from "@mui/x-charts/LineChart"; diff --git a/plots/probability-weibull/metadata/javascript/muix.yaml b/plots/probability-weibull/metadata/javascript/muix.yaml index bc8718370ec..288a20c3d77 100644 --- a/plots/probability-weibull/metadata/javascript/muix.yaml +++ b/plots/probability-weibull/metadata/javascript/muix.yaml @@ -2,7 +2,7 @@ library: muix language: javascript specification_id: probability-weibull created: '2026-08-24T08:12:54Z' -updated: '2026-10-24T12:00:08Z' +updated: '2026-10-24T12:05:19Z' generated_by: gemini-4-argon adopted_from: 5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69 (5b2bcb3e5e4dc5b3685a669c07cf3ab1b9918f69) workflow_run: 40091871386 @@ -13,54 +13,46 @@ preview_url_light: https://storage.googleapis.com/anyplot-images/plots/probabili preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/probability-weibull/javascript/muix/plot-dark.png preview_html_light: https://storage.googleapis.com/anyplot-images/plots/probability-weibull/javascript/muix/plot-light.html preview_html_dark: https://storage.googleapis.com/anyplot-images/plots/probability-weibull/javascript/muix/plot-dark.html -quality_score: 85 +quality_score: 87 review: strengths: - - 'Correct Weibull probability paper: ChartsContainer x axis uses scaleType "log" - and the y axis is the linearized ln(-ln(1-F)) scale with tickInterval/valueFormatter - mapping back to readable percentage ticks (1%…99%, including 63.2%)' - - Median-rank plotting positions (i-0.3)/(n+0.4) with Johnson adjusted ranks for - the suspensions, a proper treatment of right-censored data rather than simply - dropping it - - Rank-regression fit gives beta ≈ 2.09 and eta ≈ 453 kcycles; the dashed fit line - visibly crosses the 63.2% ChartsReferenceLine at about 450 kcycles, so the annotated - values agree with the line - - Censored observations are hollow Imprint-green rings against filled failures, - and the difference reads clearly in both themes - - Composes the chart from ChartsContainer + LinePlot + ChartsReferenceLine + ChartsGrid - and draws the markers through useXScale/useYScale/useDrawingArea, so marks sit - exactly at their data values - - 'Fully theme-token driven: fit line in ink, reference line in inkSoft, callout - on elevatedBg, so both renders are legible with identical data colours' - - Deterministic LCG-generated turbine-blade fatigue data (24 units, 4 suspensions) - in a realistic kcycle range + - 'Correct Weibull probability paper: log-scaled time axis (scaleType ''log'') and + a linearized ln(-ln(1-F)) y axis with reliability-standard probability ticks (1 + %…99 %, plus 63.2 %) placed via tickInterval and a valueFormatter, so the fit + is a straight line.' + - Plotting positions follow the median rank (i-0.3)/(n+0.4) with Johnson's adjusted + rank for suspensions; the first failure sits at 2.87 % as computed, and the fitted + beta/eta (2.09 / 453 kcycles) agree with the line, which crosses the 63.2 % reference + line at about 450 kcycles. + - Censored observations are hollow brand-green rings against filled failures, clearly + distinct in both themes; all data colors are identical between renders and chrome + flips cleanly. + - 'Uses MUI X v9 composition idiomatically: ChartsContainer with a log x axis, LinePlot + for the fit, ChartsReferenceLine for the 63.2 % line, ChartsGrid, and the useDrawingArea/useXScale/useYScale + hooks to draw the point overlay in data coordinates; skipAnimation is set.' + - Compact header row puts the title on the left and the legend (with the fit's beta + and eta in the fit entry) on the right, leaving the full height to the plot. weaknesses: - - 'VQ-05 (both): the log x axis uses 50-kcycle linear tick steps, so the labels - bunch together on the right ("750 800 850 900" sit about 10–15 px apart and the - 950/1050 ticks carry no label) while the left third has a single "100" label → - log-friendly ticks (e.g. 100, 150, 200, 300, 500, 700, 1000) with roughly even - spacing. Likely cause: no tickInterval set on the log x axis, so MUI''s default - linear-step ticks are used.' - - 'Suggestion: draw the failures and suspensions as two ScatterPlot series (hollow - via slotProps/sx) so MUI''s native ChartsLegend and tooltips cover them instead - of the hand-built HTML legend.' - - 'Suggestion: anchor the 63.2% callout near the fit/reference-line crossing at - eta instead of the far right edge so the eye connects the label with the crossing.' + - 'Suggestion: anchor the eta callout at the crossing of the fit line and the 63.2 + % reference line (about 453 kcycles) instead of the right edge, so the characteristic + life is read where it occurs.' + - 'Suggestion: the fit line and the reference line are both dashed; a solid fit + line would separate the model from the reference more clearly.' image_description: |- Light render (plot-light.png): - Background: warm off-white (#FAF8F1), not pure white. - Chrome: bold dark title "probability-weibull · javascript · muix · anyplot.ai" top-left; HTML legend top-right (dashed "Fit: β ≈ 2.09, η ≈ 453 kcycles", filled "Failure", hollow "Suspended (censored)"); y-axis "Cumulative Failure Probability (%)" with ticks 1%…99% incl. 63.2%; x-axis "Cycles to Failure (kcycles)" log scale 100–1100 (labels crowd on the right: 750 800 850 900). Subtle grid. All text readable dark-on-light. - Data: 20 filled #009E73 failure markers and 4 hollow green suspension rings around a black dashed fit line spanning the plot; dotted horizontal reference at 63.2% with a boxed callout "63.2% · η ≈ 453 kcycles" at the right; line crosses the reference near 450. + Background: warm off-white #FAF8F1 across the whole canvas, no white panel. + Chrome: bold dark title "probability-weibull · javascript · muix · anyplot.ai" top-left (about a third of the width); legend top-right ("Fit: β ≈ 2.09, η ≈ 453 kcycles" dashed line, filled green "Failure", hollow green "Suspended (censored)"); y-axis "Cumulative Failure Probability (%)" with ticks 1 %–99 % plus 63.2 %; x-axis "Cycles to Failure (kcycles)" with log ticks 70–1000; subtle grey grid in both directions. All text dark and readable. + Data: 20 filled #009E73 failure dots and 4 hollow green censored rings along a dark dashed fit line from ~1.8 % at 65 kcycles to 99 % at ~950; thin dotted 63.2 % reference line with a boxed callout "63.2% · η ≈ 453 kcycles" at the right; fit crosses the reference line near 450 kcycles. Nothing clipped or overlapping. Legibility verdict: PASS Dark render (plot-dark.png): - Background: warm near-black (#1A1A17), not pure black. - Chrome: title, legend text, axis labels and tick labels in light ink, clearly readable; grid subtle grey; callout box on elevated dark surface with light text. No dark-on-dark text. - Data: identical #009E73 filled and hollow markers; fit line flips to light ink; reference line light dotted. Data colours identical to the light render. + Background: warm near-black #1A1A17. + Chrome: title, legend text, axis titles and tick labels all light ink and readable; grid subtle grey; callout box on elevated dark surface with light text. No dark-on-dark text. + Data: identical #009E73 filled dots and hollow rings at the same positions; fit line flips to light ink as chrome; brand green clearly visible. Legibility verdict: PASS criteria_checklist: visual_quality: - score: 29 + score: 30 max: 30 items: - id: VQ-01 @@ -68,32 +60,33 @@ review: score: 8 max: 8 passed: true - comment: Title 22px, ticks 14px, labels 15px; readable in both themes + comment: Title 22 px, axis titles 15 px, ticks and legend 14 px (CSS), all + readable in both themes - id: VQ-02 name: No Overlap score: 6 max: 6 passed: true - comment: No text collisions; callout clear of markers + comment: No text collisions; callout box clear of data - id: VQ-03 name: Element Visibility score: 6 max: 6 passed: true - comment: 24 sparse points at r=7 clearly visible; legend glyphs match marks - in both themes + comment: 24 sparse points at r=7 with page-colored outline; hollow rings distinct; + legend glyphs match marks - id: VQ-04 name: Color Accessibility score: 2 max: 2 passed: true - comment: Filled vs hollow encodes censoring, not colour; good contrast + comment: Single hue; filled vs hollow carries the censoring distinction, CVD-safe - id: VQ-05 name: Layout & Canvas - score: 3 + score: 4 max: 4 passed: true - comment: x-axis tick labels bunch on the right of the log axis (defect line) + comment: 3200x1800; balanced margins, nothing cut off - id: VQ-06 name: Axis Labels & Title score: 2 @@ -105,7 +98,8 @@ review: score: 2 max: 2 passed: true - comment: '#009E73 for data, theme-correct backgrounds, chrome via tokens' + comment: 'Data in #009E73, fit and reference in ink tokens, correct backgrounds + in both renders' design_excellence: score: 10 max: 20 @@ -114,22 +108,23 @@ review: name: Aesthetic Sophistication score: 5 max: 8 - passed: true - comment: 'Custom header band: title left, compact inline legend right, clear - weight hierarchy beyond baseline' + passed: false + comment: Header row integrating title and a legend that carries the fit parameters; + clear ink-vs-green hierarchy between model and data - id: DE-02 name: Visual Refinement score: 3 max: 6 - passed: true - comment: Distinct dash rhythms for fit (12/7) vs reference (4/4), page-colour - marker outlines, rounded elevated callout + passed: false + comment: Probability ticks chosen at reliability-paper positions with the + 63.2 % tick, log ticks at 1-1.5-2-3-5-7 mantissas, generous plot area - id: DE-03 name: Data Storytelling score: 2 max: 6 passed: false - comment: Displays what the Notes ask; no further hierarchy chosen + comment: Displays the spec's annotations; no focal point chosen beyond the + Notes spec_compliance: score: 15 max: 15 @@ -139,26 +134,27 @@ review: score: 5 max: 5 passed: true - comment: Weibull probability plot with fitted line + comment: Weibull probability plot on probability paper - id: SC-02 name: Required Features score: 4 max: 4 passed: true - comment: Log x, linearized Weibull y, median ranks, beta/eta annotated, hollow - censored markers, 63.2% reference line — all present + comment: Log x, linearized y, median ranks, fit with beta/eta, hollow censored + markers, 63.2 % reference line all present - id: SC-03 name: Data Mapping score: 3 max: 3 passed: true - comment: Marks at their data values via scale hooks + comment: Points at their time and plotting position, no displacement; axes + cover all data - id: SC-04 name: Title & Legend score: 3 max: 3 passed: true - comment: Correct title format; legend entries match marks + comment: Mandated title; legend entries readable and correct data_quality: score: 15 max: 15 @@ -168,20 +164,21 @@ review: score: 6 max: 6 passed: true - comment: Failures, suspensions, fit, char-life crossing all shown + comment: Failures, suspensions mixed in, scatter around the fit at both ends - id: DQ-02 name: Realistic Context score: 5 max: 5 passed: true - comment: Turbine blade fatigue life in kcycles + comment: Fatigue life in kcycles with suspensions, neutral engineering context - id: DQ-03 name: Appropriate Scale score: 4 max: 4 passed: true - comment: 'Checked: fit line crosses 63.2% at ~450 kcycles matching η ≈ 453; - β 2.09 plausible for wear-out (true 2.4, n=24)' + comment: 'Checked: first failure at (1-0.3)/24.4 = 2.87 % matches the render; + beta 2.09 and eta 453 are consistent with the generating Weibull(2.4, 420) + for n=24; fit crosses 63.2 % at about 450' code_quality: score: 10 max: 10 @@ -191,7 +188,8 @@ review: score: 3 max: 3 passed: true - comment: Linear module-level flow; one overlay component needed for hooks + comment: Flat module-level code; the only extra function is the React overlay + component that needs the scale hooks - id: CQ-02 name: Reproducibility score: 2 @@ -209,46 +207,48 @@ review: score: 2 max: 2 passed: true - comment: Short written-out regression is allowed per library prompt + comment: Closed-form least squares and rank computation are short and appropriate; + no library call replaces them - id: CQ-05 name: Output & API score: 1 max: 1 passed: true - comment: Default export, skipAnimation, v9 ChartsContainer API + comment: Default-exported component, v9 API, skipAnimation set library_mastery: - score: 6 + score: 7 max: 10 items: - id: LM-01 name: Idiomatic Usage score: 4 max: 5 - passed: true - comment: Composition API (ChartsContainer, LinePlot, ChartsReferenceLine, - ChartsGrid, axes) with log scaleType and valueFormatter + passed: false + comment: v9 composition (ChartsContainer, LinePlot, ChartsGrid, ChartsXAxis/YAxis) + with log scaleType, tickInterval and valueFormatter for probability-paper + ticks - id: LM-02 name: Distinctive Features - score: 2 + score: 3 max: 5 passed: false - comment: Custom SVG overlay via useXScale/useYScale/useDrawingArea hooks - verdict: REJECTED + comment: useDrawingArea/useXScale/useYScale hooks to draw custom SVG marks + in data coordinates, ChartsReferenceLine for the 63.2 % line + verdict: APPROVED model: claude-opus-5-5 criteria_version: qc-899b86ffde.aqr-869a70087c.sg-c73b4641dd.lib-f7283eb626 - rendered_at: '2026-10-24T11:57:26Z' + rendered_at: '2026-10-24T12:02:14Z' impl_tags: dependencies: [] techniques: - layer-composition - custom-legend - - annotations - manual-ticks - - hover-tooltips + - annotations - html-export patterns: - data-generation dataprep: - regression styling: - - publication-ready + - edge-highlighting