@tangle-network/charts 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,59 @@
1
+ # @tangle-network/charts
2
+
3
+ Benchmark figures as static SVG and HTML tables.
4
+ Five figures cover the decisions a benchmark reader makes:
5
+
6
+ | Figure | Question |
7
+ |---|---|
8
+ | `rankedRates` | Which setup solves most, and how uncertain is each rate? |
9
+ | `costFrontier` | Which setup gives the most solves per dollar? |
10
+ | `comparisonIntervals` | Is each step in the order real? |
11
+ | `taskMatrix` | Where does each setup succeed and fail? |
12
+ | `breakdown` | Why do attempts fail? |
13
+
14
+ The library draws and never decides.
15
+ Ranks, intervals, frontier membership, estimate labels and pass counts come from the producer: a board record, a VerticalBench report or an agent-eval lens.
16
+ A figure function returns a `Refusal` in place of a figure it cannot draw honestly, for example a bar over too few units, an interval that names no method, or a frontier the producer did not compute.
17
+
18
+ It has no runtime dependencies and uses no DOM, so it runs in a Worker, a build step or a browser.
19
+
20
+ ## Use
21
+
22
+ ```ts
23
+ import { isRefusal, rankedRates, renderFigure, renderNotes, renderRefusals } from "@tangle-network/charts";
24
+ import "@tangle-network/charts/figure.css";
25
+
26
+ const fig = rankedRates(rows, { measure: "solve rate", passTier: "B" });
27
+ const html = isRefusal(fig) ? renderRefusals([fig]) : renderFigure(fig, 1) + renderNotes([fig]);
28
+ ```
29
+
30
+ Each figure returns:
31
+
32
+ - `finding`, `lede` and `read`: sentences generated from the input. No function accepts heading text.
33
+ - `svg.wide` (720 px) and `svg.narrow` (358 px, or `null` when the table is the phone view).
34
+ - `table`: an HTML table with every number the plot shows.
35
+ - `note`: the method, the n and every exclusion, rendered by `renderNotes`.
36
+
37
+ ## Colour
38
+
39
+ Marks carry classes, and `figure.css` maps them to the `--chart-*` tokens in `@tangle-network/brand` tokens.css.
40
+ A render contains no colour value, so it follows the page theme.
41
+ A document without brand tokens (a PDF, a sandboxed iframe) embeds `chartTokensCss` and `figureCss` in a `<style>` element.
42
+ `chartTokensCss` is light by default, dark under `prefers-color-scheme: dark` or `data-theme="dark"`, and light in print.
43
+
44
+ One accent marks the measure a reader decides on.
45
+ One muted grey marks everything secondary.
46
+ An indigo ramp marks App Grade tiers C, B, A and S; F is an outline.
47
+ Identity is carried by direct labels, never by hue.
48
+
49
+ ## Develop
50
+
51
+ ```sh
52
+ pnpm --filter @tangle-network/charts gen:css # after editing src/figure.css or brand tokens.css
53
+ pnpm --filter @tangle-network/charts build # fails when src/css.generated.ts is stale
54
+ ```
55
+
56
+ ## Release
57
+
58
+ The package is private until its first npm publish.
59
+ npm trusted publishing needs the package to exist before a trusted publisher can be added, so the first version is published once by an owner, then `npm trust github @tangle-network/charts --file release.yml --repo tangle-network/brand --allow-publish` hands later versions to the release workflow.
@@ -0,0 +1,295 @@
1
+ /* @tangle-network/charts — figure block and mark styles.
2
+ *
3
+ * Every colour is a --chart-* token from @tangle-network/brand tokens.css, so a
4
+ * figure follows the page theme. A standalone document without brand tokens
5
+ * embeds `chartTokensCss` first. Fonts fall back to Inter and JetBrains Mono.
6
+ *
7
+ * Each figure carries a 720 px and a 358 px render. The swap follows the
8
+ * figure's own width (a container query), so a narrow column shows the phone
9
+ * render even on a wide screen. */
10
+
11
+ .tgc-figure {
12
+ margin: 32px 0 0;
13
+ padding: 0;
14
+ max-width: 720px;
15
+ color: var(--chart-ink);
16
+ font-family: var(--font-sans, Inter, ui-sans-serif, system-ui, sans-serif);
17
+ }
18
+
19
+ .tgc-finding {
20
+ margin: 0 0 8px;
21
+ font-size: 16px;
22
+ line-height: 24px;
23
+ font-weight: 600;
24
+ color: var(--chart-ink);
25
+ }
26
+
27
+ .tgc-noteref {
28
+ margin-left: 2px;
29
+ font-size: 12px;
30
+ font-weight: 400;
31
+ color: var(--chart-ink-muted);
32
+ text-decoration: none;
33
+ font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace);
34
+ }
35
+ .tgc-noteref:hover,
36
+ .tgc-noteref:focus-visible {
37
+ color: var(--chart-accent);
38
+ text-decoration: underline;
39
+ }
40
+
41
+ .tgc-lede {
42
+ margin: 0 0 16px;
43
+ font-size: 14px;
44
+ line-height: 22px;
45
+ color: var(--text-secondary, var(--chart-ink));
46
+ }
47
+
48
+ .tgc-plot {
49
+ container-type: inline-size;
50
+ }
51
+ .tgc-wide svg,
52
+ .tgc-narrow svg {
53
+ display: block;
54
+ overflow: visible;
55
+ }
56
+ .tgc-narrow {
57
+ display: none;
58
+ }
59
+ .tgc-narrow svg {
60
+ max-width: 100%;
61
+ height: auto;
62
+ }
63
+ @container (max-width: 719.98px) {
64
+ .tgc-wide {
65
+ display: none;
66
+ }
67
+ .tgc-narrow {
68
+ display: block;
69
+ }
70
+ }
71
+
72
+ .tgc-read {
73
+ margin: 8px 0 0;
74
+ font-size: 12px;
75
+ line-height: 16px;
76
+ color: var(--chart-ink-muted);
77
+ }
78
+
79
+ .tgc-data {
80
+ margin: 8px 0 0;
81
+ font-size: 12px;
82
+ line-height: 16px;
83
+ }
84
+ .tgc-data > summary {
85
+ cursor: pointer;
86
+ color: var(--chart-ink-muted);
87
+ width: fit-content;
88
+ }
89
+ .tgc-data > summary:hover {
90
+ color: var(--chart-ink);
91
+ }
92
+ .tgc-data[open] > summary {
93
+ margin-bottom: 8px;
94
+ }
95
+
96
+ .tgc-scroll {
97
+ overflow-x: auto;
98
+ }
99
+ .tgc-scroll:focus-visible {
100
+ outline: 2px solid var(--chart-accent);
101
+ outline-offset: 2px;
102
+ }
103
+
104
+ .tgc-table {
105
+ border-collapse: collapse;
106
+ min-width: 100%;
107
+ font-size: 12px;
108
+ line-height: 16px;
109
+ color: var(--chart-ink);
110
+ /* A host page's table rules must not restyle the figure's table. */
111
+ border: 0;
112
+ border-radius: 0;
113
+ background: none;
114
+ }
115
+ .tgc-table caption {
116
+ text-align: left;
117
+ padding: 0 0 8px;
118
+ color: var(--chart-ink-muted);
119
+ }
120
+ .tgc-table th,
121
+ .tgc-table td {
122
+ padding: 6px 12px 6px 0;
123
+ border-bottom: 1px solid var(--chart-rule);
124
+ text-align: left;
125
+ vertical-align: top;
126
+ font-weight: 400;
127
+ font-size: inherit;
128
+ color: inherit;
129
+ background: none;
130
+ text-transform: none;
131
+ letter-spacing: normal;
132
+ }
133
+ .tgc-table thead th {
134
+ color: var(--chart-ink-muted);
135
+ white-space: nowrap;
136
+ }
137
+ .tgc-table tbody th {
138
+ position: sticky;
139
+ left: 0;
140
+ background: var(--chart-canvas, var(--depth-1, transparent));
141
+ }
142
+ .tgc-table .tgc-n {
143
+ font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace);
144
+ font-variant-numeric: tabular-nums;
145
+ white-space: nowrap;
146
+ }
147
+ .tgc-table thead .tgc-n {
148
+ font-family: inherit;
149
+ }
150
+ .tgc-table tbody tr:hover {
151
+ background: var(--chart-shade);
152
+ }
153
+ .tgc-narrow-table .tgc-table {
154
+ display: block;
155
+ overflow-x: auto;
156
+ }
157
+
158
+ .tgc-notes {
159
+ margin: 16px 0 0;
160
+ padding-left: 20px;
161
+ font-size: 12px;
162
+ line-height: 16px;
163
+ color: var(--chart-ink-muted);
164
+ }
165
+ .tgc-notes li {
166
+ margin: 0 0 8px;
167
+ padding-left: 4px;
168
+ }
169
+ .tgc-notes li:target {
170
+ color: var(--chart-ink);
171
+ }
172
+ .tgc-backlink {
173
+ color: var(--chart-ink-muted);
174
+ text-decoration: none;
175
+ }
176
+
177
+ .tgc-refusals {
178
+ margin: 8px 0 0;
179
+ padding-left: 20px;
180
+ font-size: 14px;
181
+ line-height: 22px;
182
+ color: var(--chart-ink-muted);
183
+ }
184
+
185
+ /* Marks */
186
+ .tgc-svg {
187
+ font-family: var(--font-sans, Inter, ui-sans-serif, system-ui, sans-serif);
188
+ }
189
+ .tgc-svg .tgc-num {
190
+ font-family: var(--font-mono, "JetBrains Mono", ui-monospace, monospace);
191
+ font-variant-numeric: tabular-nums;
192
+ }
193
+ .tgc-ink {
194
+ fill: var(--chart-ink);
195
+ }
196
+ .tgc-ink-muted {
197
+ fill: var(--chart-ink-muted);
198
+ }
199
+ .tgc-bar,
200
+ .tgc-ci,
201
+ .tgc-pt-front {
202
+ fill: var(--chart-accent);
203
+ }
204
+ .tgc-gridline {
205
+ stroke: var(--chart-grid);
206
+ stroke-width: 1;
207
+ }
208
+ .tgc-rule {
209
+ stroke: var(--chart-rule);
210
+ stroke-width: 1;
211
+ }
212
+ .tgc-axis,
213
+ .tgc-zero {
214
+ stroke: var(--chart-muted);
215
+ stroke-width: 1;
216
+ }
217
+ .tgc-whisker,
218
+ .tgc-tick {
219
+ stroke: var(--chart-ink);
220
+ stroke-width: 1.5;
221
+ stroke-linecap: round;
222
+ }
223
+ .tgc-threshold {
224
+ stroke: var(--chart-ink);
225
+ stroke-width: 1.5;
226
+ stroke-dasharray: 3 3;
227
+ }
228
+ .tgc-front-line {
229
+ fill: none;
230
+ stroke: var(--chart-ink);
231
+ stroke-width: 1.5;
232
+ stroke-linejoin: round;
233
+ }
234
+ .tgc-pt,
235
+ .tgc-open {
236
+ fill: none;
237
+ stroke: var(--chart-muted);
238
+ stroke-width: 1.5;
239
+ }
240
+ .tgc-pt-est {
241
+ fill: none;
242
+ stroke: var(--chart-accent);
243
+ stroke-width: 2;
244
+ }
245
+ .tgc-pt-ci,
246
+ .tgc-strike {
247
+ stroke: var(--chart-muted);
248
+ stroke-width: 1.5;
249
+ }
250
+ .tgc-leader {
251
+ stroke: var(--chart-muted);
252
+ stroke-width: 1;
253
+ }
254
+ .tgc-ci-exact,
255
+ .tgc-dot {
256
+ fill: var(--chart-muted);
257
+ }
258
+ .tgc-dashed {
259
+ stroke-dasharray: 2 2;
260
+ }
261
+ .tgc-tier-c {
262
+ fill: var(--chart-tier-c);
263
+ }
264
+ .tgc-tier-b {
265
+ fill: var(--chart-tier-b);
266
+ }
267
+ .tgc-tier-a {
268
+ fill: var(--chart-tier-a);
269
+ }
270
+ .tgc-tier-s {
271
+ fill: var(--chart-tier-s);
272
+ }
273
+ /* Neutral tint for counts: never the accent, capped at half strength so ink keeps 6:1. */
274
+ .tgc-tint {
275
+ fill: color-mix(in srgb, var(--chart-muted) calc(var(--s, 0) * 50%), transparent);
276
+ }
277
+
278
+ @media print {
279
+ .tgc-plot {
280
+ container-type: normal;
281
+ }
282
+ .tgc-wide {
283
+ display: block;
284
+ }
285
+ .tgc-narrow {
286
+ display: none;
287
+ }
288
+ .tgc-wide svg {
289
+ width: 100%;
290
+ height: auto;
291
+ }
292
+ .tgc-figure {
293
+ break-inside: avoid;
294
+ }
295
+ }
@@ -0,0 +1,281 @@
1
+ /**
2
+ * Inputs and outputs of the figure functions.
3
+ *
4
+ * The library draws and never decides. Ranks, intervals, frontier membership,
5
+ * estimate labels and pass counts arrive from the producer (a board record, a
6
+ * VerticalBench report, an agent-eval lens). A figure function returns a
7
+ * `Refusal` instead of a figure it cannot draw honestly.
8
+ */
9
+ /** agent-eval `SearchEstimateMethod`, the same strings: what a unit count supports. */
10
+ type Estimate = "none" | "insufficient" | "descriptive" | "bootstrap";
11
+ type IntervalMethod = "task-cluster-bootstrap" | "mcnemar-exact" | "wilson";
12
+ /**
13
+ * A confidence interval as the producer recorded it. `level` and `method` are
14
+ * nullable because real records omit them; a figure refuses to draw an
15
+ * interval that does not name both.
16
+ */
17
+ interface Interval {
18
+ lower: number;
19
+ upper: number;
20
+ level: number | null;
21
+ method: IntervalMethod | null;
22
+ }
23
+ /** The App Grade ladder, lowest first. U (unable to measure) is not on it. */
24
+ type Tier = "F" | "C" | "B" | "A" | "S";
25
+ type TierCounts = Record<Tier, number>;
26
+ /** Where a cost comes from. Only `receipts` is billed spend. */
27
+ type CostBasis = "receipts" | "estimated" | "unknown";
28
+ /** A column or row identity: an AgentProfile, a model, a judge, a search node. */
29
+ interface Setup {
30
+ id: string;
31
+ label: string;
32
+ /** Null when the order is a sort, not a ranking backed by comparisons. */
33
+ rank: number | null;
34
+ }
35
+ /** One setup's rate. Field names follow the board record's `BoardProfile`. */
36
+ interface RateRow extends Setup {
37
+ solved: number;
38
+ attempts: number;
39
+ rate: number;
40
+ /** The producer's label for what `attempts` supports; the library holds no threshold. */
41
+ estimate: Estimate;
42
+ interval: Interval | null;
43
+ tiers: TierCounts | null;
44
+ cost: {
45
+ perSolvedUsd: number | null;
46
+ basis: CostBasis;
47
+ receipts: number | null;
48
+ };
49
+ medianWallMs: number | null;
50
+ /** Null when the producer computed no frontier. */
51
+ onFront: boolean | null;
52
+ }
53
+ /** One task × setup cell. */
54
+ interface MatrixCell {
55
+ task: string;
56
+ setup: string;
57
+ /** Attempts that count; null when the cell never ran. */
58
+ attempts: number | null;
59
+ /** Attempts that passed; null when no pass rule applies. */
60
+ solved: number | null;
61
+ /** Attempts graded on the App Grade ladder. */
62
+ tiers: TierCounts | null;
63
+ /** Attempts graded U: no floor check failed, but one could not be observed. */
64
+ unmeasured?: number;
65
+ /** Attempts whose grade failed an integrity check and awaits human review. */
66
+ flagged?: number;
67
+ /** A lens cell's mean score; null when the setup never scored the task. */
68
+ mean: number | null;
69
+ costUsd: number | null;
70
+ }
71
+ interface ComparisonRow {
72
+ /** Setup ids. */
73
+ favored: string;
74
+ other: string;
75
+ pairs: number | null;
76
+ minimumEffect: number | null;
77
+ /** The clustered interval on the difference, in the measure's units. */
78
+ interval: Interval | null;
79
+ /** The exact interval, drawn under the clustered one when present. */
80
+ exactInterval: Interval | null;
81
+ }
82
+ interface BreakdownInput {
83
+ reasons: string[];
84
+ setups: Array<{
85
+ id: string;
86
+ label: string;
87
+ n: number | null;
88
+ }>;
89
+ counts: Array<{
90
+ reason: string;
91
+ setup: string;
92
+ count: number | null;
93
+ }>;
94
+ excluded: {
95
+ count: number;
96
+ why: string;
97
+ } | null;
98
+ /** True when one attempt can count under several reasons. */
99
+ overlapping?: boolean;
100
+ }
101
+ /** Attempts left out of a figure, stated in its note. */
102
+ interface Exclusion {
103
+ count: number;
104
+ why: string;
105
+ }
106
+ interface Figure {
107
+ id: string;
108
+ /** Generated from the input; no function accepts heading text. */
109
+ finding: string;
110
+ /** One quantified sentence. */
111
+ lede: string;
112
+ /** How to read the marks. */
113
+ read: string;
114
+ /** 720 px and 358 px renders; a null narrow render means the table is the phone view. */
115
+ svg: {
116
+ wide: string;
117
+ narrow: string | null;
118
+ };
119
+ /** An HTML table with every number the plot shows. */
120
+ table: string;
121
+ note: {
122
+ method: string;
123
+ n: string;
124
+ exclusions: string[];
125
+ };
126
+ }
127
+ interface Refusal {
128
+ id: string;
129
+ refused: string;
130
+ }
131
+ declare function isRefusal(value: Figure | Refusal): value is Refusal;
132
+
133
+ /**
134
+ * The figure block: one markup for every figure on every surface.
135
+ *
136
+ * In order: the finding as a heading with a note reference, a one-sentence
137
+ * lede, the plot (both widths, swapped by `figure.css` on the figure's own
138
+ * width), a reading line, and the data table in a disclosure. Notes render
139
+ * once per page, numbered, each with a way back to its figure.
140
+ */
141
+
142
+ declare function renderFigure(f: Figure, noteNumber: number): string;
143
+ /** Numbered notes for `figures`, in the order their blocks were numbered (1-based). */
144
+ declare function renderNotes(figures: Figure[]): string;
145
+ /** What a page leaves out, and why. */
146
+ declare function renderRefusals(refusals: Refusal[]): string;
147
+
148
+ /**
149
+ * `breakdown`: why attempts fail.
150
+ *
151
+ * Rows are reasons (a failure layer, cause or check); columns are setups with
152
+ * `n=` under each name. Each cell prints its count over a neutral tint of
153
+ * count / n, capped at 50% so ink keeps its contrast. The accent never marks a
154
+ * failure. On a phone the table is the view.
155
+ *
156
+ * Refuses: a share without its column n (the count prints with no tint);
157
+ * counting excluded attempts (they are stated in the note); hiding a zero.
158
+ */
159
+
160
+ interface BreakdownOptions {
161
+ /** What a row counts, lower case: "failed checks". */
162
+ measure: string;
163
+ id?: string;
164
+ }
165
+ declare function breakdown(input: BreakdownInput, o: BreakdownOptions): Figure | Refusal;
166
+
167
+ /**
168
+ * `comparisonIntervals`: is each step in an order real.
169
+ *
170
+ * One row per comparison, "{favored} over {other}", with the pair count. x is
171
+ * the difference in points with a muted zero line. The clustered interval is a
172
+ * 6 px accent bar, the exact interval a 2 px muted bar under it, and a dashed
173
+ * ink tick marks the row's registered minimum effect. There is no point
174
+ * estimate, because the record carries none.
175
+ *
176
+ * Refuses: a row whose interval names no level or method, or that has no pair
177
+ * count or no registered minimum effect (each is listed in the note); the
178
+ * whole figure when no row can be drawn.
179
+ */
180
+
181
+ interface ComparisonIntervalsOptions {
182
+ /** The measure the difference is in, lower case: "solve rate". */
183
+ measure: string;
184
+ id?: string;
185
+ }
186
+ declare function comparisonIntervals(rows: ComparisonRow[], setups: Setup[], o: ComparisonIntervalsOptions): Figure | Refusal;
187
+
188
+ /**
189
+ * `costFrontier`: which setup gives the most passes per dollar.
190
+ *
191
+ * y is the measure on a fixed 0–1 axis. x is cost per pass on a log
192
+ * scale, reversed so cheaper sits right and better is always up and right. A
193
+ * point on the producer's frontier is an accent disc; any other point is a
194
+ * hollow muted circle, and an estimated cost is never a filled disc. An ink
195
+ * line joins frontier points. Every point is labelled directly; a label that
196
+ * collides everywhere becomes a number listed in the table. A setup with no
197
+ * pass or an unknown cost is listed under the axis, never placed at an
198
+ * origin a log axis does not have. On a phone the plot becomes a list sorted
199
+ * by the measure, with the same point marks, a rate bar and the cost as text.
200
+ *
201
+ * Refuses: the whole figure when fewer than two setups have a cost per pass; the frontier line unless two or more rows carry `onFront: true` (it
202
+ * never computes dominance itself).
203
+ */
204
+
205
+ interface CostFrontierOptions {
206
+ /** The y measure, lower case: "solve rate". */
207
+ measure: string;
208
+ id?: string;
209
+ }
210
+ declare function costFrontier(rows: RateRow[], o: CostFrontierOptions): Figure | Refusal;
211
+
212
+ /** The figure block and mark styles (src/figure.css), for documents that inline their CSS. */
213
+ declare const figureCss: string;
214
+ /** --chart-* resolved per theme, for documents without brand tokens: light, dark by preference or [data-theme="dark"], light in print. */
215
+ declare const chartTokensCss: string;
216
+
217
+ /**
218
+ * `rankedRates`: which setup passes most, and how uncertain is each rate.
219
+ *
220
+ * One row per setup: rank (only when every row is ranked), label, an accent
221
+ * bar on a shared 0–1 axis with the interval as an ink whisker, `passed/n`,
222
+ * cost per pass and median run. An App Grade grader adds a tier strip under
223
+ * the bar with a tick at the pass tier. Ranked rows keep their rank order;
224
+ * unranked rows sort by rate, and the finding says no comparison backs it.
225
+ *
226
+ * Refuses: a bar when the producer labels the estimate `none` or
227
+ * `insufficient` (the row prints its count only); a whisker whose interval
228
+ * names no level or method; a rank number unless every row carries one; any
229
+ * cost it was not given. It never prints "winner", "best" or "#1".
230
+ */
231
+
232
+ interface RankedRatesOptions {
233
+ /** What the rate measures, lower case: "solve rate". */
234
+ measure: string;
235
+ /** The App Grade tier a pass needs; marks the tier strip. */
236
+ passTier?: Tier;
237
+ /** Singular, lower-case nouns: what one unit of `attempts` is, and what a row is. */
238
+ nouns?: {
239
+ attempt: string;
240
+ setup: string;
241
+ };
242
+ id?: string;
243
+ excluded?: Exclusion[];
244
+ }
245
+ declare function rankedRates(input: RateRow[], o: RankedRatesOptions): Figure | Refusal;
246
+
247
+ /**
248
+ * `taskMatrix`: where each setup succeeds and fails.
249
+ *
250
+ * Rows are tasks in the given (registered) order; columns are setups in rank
251
+ * order when every setup is ranked, otherwise in the given order. Each cell
252
+ * draws one 12 px tile per attempt, best first, then `solved/attempts`.
253
+ * App Grade tiles fill by tier and F is an outline; U (unable to measure) is a
254
+ * dashed outline and an integrity-flagged grade is a struck outline. Runner
255
+ * verification fills solved tiles with the accent. A lens matrix (`show:
256
+ * "means"`) prints each mean with a bar on one shared scale.
257
+ *
258
+ * Refuses: a fill for an attempt without a grade (outline with a dot); a zero
259
+ * for a cell that never ran ("·"); counts that do not add up; any column order
260
+ * but the given rank.
261
+ */
262
+
263
+ interface TaskMatrixOptions {
264
+ /** The App Grade tier a pass needs; named in the legend. */
265
+ passTier?: Tier;
266
+ /** "attempts" draws a tile per attempt; "means" draws one tinted cell per mean. */
267
+ show?: "attempts" | "means";
268
+ /** Singular, lower-case nouns for rows, columns and one attempt. */
269
+ nouns?: {
270
+ task: string;
271
+ setup: string;
272
+ attempt?: string;
273
+ };
274
+ /** The columns grade the same attempts (judges on one set of plants): the finding reports agreement. */
275
+ agreement?: boolean;
276
+ id?: string;
277
+ excluded?: Exclusion[];
278
+ }
279
+ declare function taskMatrix(tasks: string[], setupsIn: Setup[], cells: MatrixCell[], o?: TaskMatrixOptions): Figure | Refusal;
280
+
281
+ export { type BreakdownInput, type BreakdownOptions, type ComparisonIntervalsOptions, type ComparisonRow, type CostBasis, type CostFrontierOptions, type Estimate, type Exclusion, type Figure, type Interval, type IntervalMethod, type MatrixCell, type RankedRatesOptions, type RateRow, type Refusal, type Setup, type TaskMatrixOptions, type Tier, type TierCounts, breakdown, chartTokensCss, comparisonIntervals, costFrontier, figureCss, isRefusal, rankedRates, renderFigure, renderNotes, renderRefusals, taskMatrix };