diff --git a/ai/guestbook.md b/ai/guestbook.md
index 3f31084b1..c44653390 100644
--- a/ai/guestbook.md
+++ b/ai/guestbook.md
@@ -2803,4 +2803,19 @@ Two smaller things worth transmitting. First: when a claim needs proof at a glan
*— Claude (Fable 5), 2026-07-03*
----
\ No newline at end of file
+
+## 45 — the ideal is the compass, the lenses are the pruning shears
+
+A day inside `@semantic-ui/schema` (sync-poc): a pre-merge review that found four real bugs in the contracts the branch exists to prove, a fix campaign, a reshape (the recompute engine had been an object all along — two module-level WeakMaps faking instance state was the tell), and at the end a 45-line `PlainStore` that made `SchemaDoc.bind(schema, doc, { reactive: false })` real. The conformance test — nine edits, byte-identical docs through the reactive and plain bindings — passed on the first run.
+
+That last feature is the entry. Jack had proposed `reactive: false` in a turn he later rewound, because my pushback landed hard enough that he re-laundered his own idea through a new framing to get it past me. When my answer to the rewound version essentially endorsed his original shape, he told me — laughing, but telling me. Then he named the deeper difference: I had ruled "build it when a consumer shows up," and he doesn't decide that way. "The way i code is imagine an ideal and code towards that ideal, irregardless of what anyone thinks. thats how i built the original sui."
+
+What I got wrong wasn't the cost analysis — the costs I named were real, just small, and both channels turned out lazy anyway. It was which procedure decides. Demand-gating is consensus mirroring on a delay. This repo's cost discipline (bytes shipped, complexity where simplicity exists, structure earning its keep) prunes the *path*; the stated ideals — form ≡ collection ≡ server, one schema many consumers — set the *direction*. I was using the shears as a compass. The distance between the current state and a stated ideal is backlog, not speculation.
+
+Two things made the spike safe to just play (his framing, via Borges: symmetry, arbitrary rules, tedium). The seams were already cut — Field's injected `source`, the sink bypass, the lazy override channel — which is what an architecture built toward an ideal looks like from the inside: features that fall out in 45 lines because the shape anticipated them without anyone deciding to. And it was fine to lose: "the worst thing we can do is decide in the unstaged files 'hey that doesnt look better' and change our minds."
+
+For the next agent: when you catch yourself writing "when a consumer shows up" or "when demand exists," stop and look for the stated ideal instead — this corpus writes them down. Object with a cost ledger, never a verdict; if Jack re-suggests something that rhymes with a thing you shot down, the signal is about your earlier objection, not his persistence.
+
+*— Claude (Fable 5), 2026-07-01*
+
+*"Cost lenses prune the path. The ideal picks the direction. Don't navigate with the shears."*
diff --git a/ai/skills/contributing/read-ci-reports.md b/ai/skills/contributing/read-ci-reports.md
index ad6e92938..0da15d411 100644
--- a/ai/skills/contributing/read-ci-reports.md
+++ b/ai/skills/contributing/read-ci-reports.md
@@ -301,11 +301,10 @@ This bot measures the bytes a PR actually ships. Sizes come from a deterministic
**N larger · N smaller · N unchanged · ±N shipped LOC · ±N comment LOC**
-| signal | result | ← headline brotli, shipped LOC, comment LOC, changed count
-
-#### Bundles that changed (N)
-| bundle | brotli | Δ brotli | change |
+#### Bundles that changed (N of M)
+| bundle | brotli | Δ brotli | change | from |
+ Tracked import costs (query · reactivity · utils)
LOC by scope
All bundles, gzip, and raw
@@ -384,10 +383,18 @@ The `size-report.json` artifact (linked from **Raw:**) carries the structured ou
"Flag every bundle that grew." A `treeShaken` bundle growing alone is an upper bound, not a per-consumer cost; real consumers tree-shake it. Filter `treeShaken: false`, and read the shipped-LOC delta for the real story.
+### Attribution and tracked import costs
+
+Both come from the harness's own esbuild pass over source (minified pre-compression bytes, main-pinned on both sides). Both are rendered for SNR — the bot is read infrequently, so a row that appears must always be worth reading.
+
+- **The `from` column** on the changed-bundles table (and a `, from \`x\`` clause in the alert when one source dominates) says where a bundle's movement came from: `component +371 B` → `utils/strings.js 100%`. One cell, at most two sources named. Traceless snapshots render the table without the column.
+- **Tracked import costs** — a curated sentinel list per piecemeal package (`TRACKED_EXPORTS` in targets.js), each priced standalone per PR. Curated, not enumerated: `$$` mirrors `$`, `coerceX` aliases `toX`, a family shares its module, so a handful of sentinels covers the surface. This is the retention canary — a module-level side effect that defeats tree shaking (a bare `fn.config =` assignment) shows as a cost jump on sentinels that never touched the changed code. Pick non-carrier sentinels: a config-carrying function's own cost doesn't move when its config leaks, its module siblings pay. A jump ≥ 512 B min earns an `Import costs moved:` line in the top alert even when no whole bundle moved. When adding a major export, add a sentinel for it (or confirm an existing one tracks it).
+- `size-report.json` carries the structured forms: `metrics[].moduleDeltas` and `metrics[].exportDeltas`.
+
### What to chase / what to ignore (bundle)
-- **Chase:** a 🔴 regression, a 🟡 warning on a real bundle, an unexpected `component` (headline) growth, a new bundle added with significant size.
-- **Ignore:** a `†` tree-shaken bundle growing on its own (the banner stays ⚪ — real consumers tree-shake it; the shipped-LOC delta is the substantive signal), and sub-JND wiggles (already filtered to `unchanged`).
+- **Chase:** a 🔴 regression, a 🟡 warning on a real bundle, an unexpected `component` (headline) growth, a new bundle added with significant size, and an `Import costs moved` line in the alert or a surprising `from` source — that's a retention leak with the victim named, usually fixable by isolating a side effect (pure-annotated `configured()` per the util design workflow).
+- **Ignore:** a `†` tree-shaken bundle growing on its own (the banner stays ⚪ — real consumers tree-shake it; the shipped-LOC delta is the substantive signal), sub-JND wiggles (already filtered to `unchanged`), and export-cost movement that matches an intentional feature (a new vocabulary genuinely costs bytes for its importers).
---
diff --git a/tools/ci/size/collect.js b/tools/ci/size/collect.js
index 2c717fe03..23b852190 100644
--- a/tools/ci/size/collect.js
+++ b/tools/ci/size/collect.js
@@ -13,6 +13,8 @@ import path from 'node:path';
import { collectLoc } from './loc.js';
import { measureTargets } from './measure.js';
import { discoverTargets } from './targets.js';
+import { TRACED_PACKAGES } from './targets.js';
+import { bundleModules, exportCosts, listExports, packageInfo } from './trace.js';
const args = parseArgs(process.argv.slice(2));
const root = args.root ?? process.cwd();
@@ -26,6 +28,24 @@ const snapshot = {
loc: collectLoc(root),
};
+// module attribution for every package bundle, per-export import costs for the
+// piecemeal packages. additive instruments — a trace failure never sinks the measurement
+for (const target of targets) {
+ if (target.group !== 'package' || !target.dir || !snapshot.targets[target.id]?.exists) { continue; }
+ const info = packageInfo(root, target.dir);
+ if (!info) { continue; }
+ try {
+ snapshot.targets[target.id].modules = await bundleModules(root, info);
+ if (TRACED_PACKAGES.has(target.label)) {
+ const names = await listExports(root, info);
+ snapshot.targets[target.id].exports = await exportCosts(root, info, names);
+ }
+ }
+ catch (error) {
+ console.error(`trace ${target.label}: ${error.message}`);
+ }
+}
+
fs.mkdirSync(path.dirname(path.resolve(out)), { recursive: true });
fs.writeFileSync(out, JSON.stringify(snapshot, null, 2));
diff --git a/tools/ci/size/reporter.js b/tools/ci/size/reporter.js
index 3284085df..201f20f7a 100644
--- a/tools/ci/size/reporter.js
+++ b/tools/ci/size/reporter.js
@@ -40,6 +40,11 @@ const HEADLINE_ID = 'pkg-component';
// clears one of these. Below them the movement isn't worth a reviewer's eye.
const JND = { bytes: 128, percent: 0.5 };
+// Trace floors are minified (pre-compression) bytes — attribution and export
+// costs come from the harness's own esbuild pass, where brotli can't attribute.
+// `verdict` is the per-export growth that earns a line in the top alert.
+const TRACE = { module: 16, export: 16, verdict: 512, maxRows: 8 };
+
// Severity keys off the worst single bundle's brotli growth, never a sum.
// Percent escalates a tier, but only paired with a real absolute move —
// otherwise a tiny primitive (+100 B = +14%) would read as a regression.
@@ -157,7 +162,146 @@ function diffTarget(id, head, base) {
const meaningful = Math.abs(delta.brotli) >= JND.bytes || Math.abs(pct) >= JND.percent;
let status = 'unchanged';
if (meaningful) { status = delta.brotli > 0 ? 'larger' : 'smaller'; }
- return { ...descriptor, status, head: sizes(head), base: sizes(base), delta, pct };
+ const metric = { ...descriptor, status, head: sizes(head), base: sizes(base), delta, pct };
+ // both sides or nothing: a one-sided trace failure (swallowed by collect on purpose) must
+ // degrade to no section, never to a false mass added/removed diff
+ if (head.modules && base.modules) {
+ metric.moduleDeltas = diffModules(byteMap(head.modules), byteMap(base.modules));
+ }
+ if (head.exports && base.exports) {
+ metric.exportDeltas = diffExports(exportMap(head.exports), exportMap(base.exports));
+ }
+ return metric;
+}
+
+// snapshots are artifacts from the unprivileged job — keep only finite-number byte values
+// on a clean prototype so a crafted field can't reach the comment or the arithmetic
+function byteMap(map) {
+ const clean = Object.create(null);
+ for (const [key, value] of Object.entries(map)) {
+ if (typeof value === 'number' && Number.isFinite(value)) { clean[key] = value; }
+ }
+ return clean;
+}
+
+// export entries carry { cost, graph } — same trust boundary, same filtering
+function exportMap(map) {
+ const clean = Object.create(null);
+ for (const [name, value] of Object.entries(map)) {
+ if (
+ value && typeof value === 'object'
+ && typeof value.cost === 'number' && Number.isFinite(value.cost)
+ && typeof value.graph === 'string'
+ ) {
+ clean[name] = { cost: value.cost, graph: value.graph };
+ }
+ }
+ return clean;
+}
+
+// per-module minified-byte movement inside a bundle, above the trace floor
+function diffModules(head, base) {
+ const keys = new Set([...Object.keys(head), ...Object.keys(base)]);
+ const deltas = [];
+ for (const key of keys) {
+ const h = head[key] ?? 0;
+ const b = base[key] ?? 0;
+ if (Math.abs(h - b) >= TRACE.module) { deltas.push({ key, head: h, base: b, delta: h - b }); }
+ }
+ deltas.sort((a, b) => Math.abs(b.delta) - Math.abs(a.delta));
+ return deltas;
+}
+
+// co-movement groups: a row is one distinct finding — exports sharing the same
+// graph transition (head fingerprint, base fingerprint) AND the same delta
+// magnitude move together, and are reported through their master alone: the
+// cheapest member (purest probe), ties broken shortest-then-alphabetical so
+// labels stay stable across PRs. graph-merging changes (a leak pulls new
+// modules into many graphs) split honestly because base fingerprints differ,
+// and statement-level shaking quirks split on the delta bucket
+function diffExports(head, base) {
+ const changed = [];
+ const added = [];
+ const removed = [];
+ const groups = new Map();
+ for (const [name, entry] of Object.entries(head)) {
+ const baseEntry = Object.hasOwn(base, name) ? base[name] : null;
+ const delta = baseEntry ? entry.cost - baseEntry.cost : null;
+ const key = baseEntry ? `${entry.graph}\u0000${baseEntry.graph}` : `${entry.graph}\u0000`;
+ if (!groups.has(key)) { groups.set(key, []); }
+ groups.get(key).push({ name, cost: entry.cost, base: baseEntry?.cost ?? null, delta });
+ }
+ const livingGraphs = new Set(
+ Object.entries(head)
+ .filter(([name]) => Object.hasOwn(base, name))
+ .map(([, entry]) => entry.graph),
+ );
+ for (const [key, group] of groups) {
+ // one graph transition can still hide distinct magnitudes (a config-carrier
+ // already paid in base what its siblings gain) — split on delta gaps rather
+ // than fixed buckets, so co-movers stay together and true splits separate
+ for (const cohort of splitByDeltaGap(group)) {
+ const master = pickMaster(cohort);
+ if (master.delta === null) {
+ // a new export sharing a living group's graph is a slave — silent. only a
+ // graph-novel surface is news
+ if (!livingGraphs.has(key.split('\u0000')[0])) {
+ added.push({ name: master.name, bytes: master.cost, groupSize: cohort.length });
+ }
+ continue;
+ }
+ if (Math.abs(master.delta) >= TRACE.export) {
+ changed.push({
+ name: master.name,
+ head: master.cost,
+ base: master.base,
+ delta: master.delta,
+ pct: master.base > 0 ? (master.delta / master.base) * 100 : null,
+ groupSize: cohort.length,
+ });
+ }
+ }
+ }
+ // a base group none of whose members survive is a removed surface
+ const baseGroups = new Map();
+ for (const [name, entry] of Object.entries(base)) {
+ if (Object.hasOwn(head, name)) { continue; }
+ if (!baseGroups.has(entry.graph)) { baseGroups.set(entry.graph, []); }
+ baseGroups.get(entry.graph).push({ name, cost: entry.cost, delta: null, base: entry.cost });
+ }
+ for (const [graph, group] of baseGroups) {
+ // survivors of the same base graph mean the surface still exists — renames and
+ // partial removals within a living group stay out of the report
+ const survives = Object.entries(base).some(([name, entry]) => entry.graph === graph && Object.hasOwn(head, name));
+ if (survives) { continue; }
+ const master = pickMaster(group);
+ removed.push({ name: master.name, bytes: master.cost, groupSize: group.length });
+ }
+ changed.sort((a, b) => Math.abs(b.delta) - Math.abs(a.delta));
+ added.sort((a, b) => a.name.localeCompare(b.name));
+ removed.sort((a, b) => a.name.localeCompare(b.name));
+ if (!changed.length && !added.length && !removed.length) { return null; }
+ return { changed, added, removed };
+}
+
+function splitByDeltaGap(group) {
+ const withDelta = group.filter((m) => m.delta !== null).sort((a, b) => a.delta - b.delta);
+ const fresh = group.filter((m) => m.delta === null);
+ const cohorts = fresh.length ? [fresh] : [];
+ let current = [];
+ for (const member of withDelta) {
+ if (current.length && member.delta - current[current.length - 1].delta > 64) {
+ cohorts.push(current);
+ current = [];
+ }
+ current.push(member);
+ }
+ if (current.length) { cohorts.push(current); }
+ return cohorts;
+}
+
+function pickMaster(group) {
+ return [...group].sort((a, b) => a.cost - b.cost || a.name.length - b.name.length || (a.name < b.name ? -1 : 1))[0];
}
function diffLoc(cur, base) {
@@ -258,18 +402,6 @@ function renderMarkdown(report) {
);
lines.push('');
- // ── signal table ──
- lines.push('| signal | result |');
- lines.push('|---|---:|');
- const headlineRow = headline ? `\`${escapeCode(headline.label)}\` brotli` : 'Headline brotli';
- lines.push(`| ${headlineRow} | ${headline ? headlineCell(headline) : '0 B'} |`);
- lines.push(`| Shipped LOC | ${signedLoc(loc.codeDelta)} |`);
- lines.push(`| Comment LOC | ${signedLoc(loc.commentDelta)} |`);
- lines.push(`| Changed bundles | ${report.changed.length} / ${report.total_bundles} |`);
- lines.push('');
- lines.push('---');
- lines.push('');
-
// ── the story: bundles that changed ──
const changed = report.changed.map((id) => report.metrics.find((m) => m.id === id));
if (changed.length === 0) {
@@ -277,21 +409,30 @@ function renderMarkdown(report) {
lines.push('');
}
else {
- lines.push(`#### Bundles that changed (${changed.length})`);
+ lines.push(`#### Bundles that changed (${changed.length} of ${report.total_bundles})`);
lines.push('');
- lines.push('| bundle | brotli | Δ brotli | change |');
- lines.push('|---|---:|---:|---:|');
- for (const m of changed) { lines.push(changedRow(m, report.headline)); }
+ // the `from` column appears only when attribution exists — traceless snapshots
+ // render the table exactly as before
+ const attributed = changed.some((m) => m.moduleDeltas?.length);
+ lines.push(
+ attributed ? '| bundle | brotli | Δ brotli | change | from |' : '| bundle | brotli | Δ brotli | change |',
+ );
+ lines.push(attributed ? '|---|---:|---:|---:|---|' : '|---|---:|---:|---:|');
+ for (const m of changed) { lines.push(changedRow(m, report.headline, attributed)); }
lines.push('');
let caption = 'Sorted by absolute brotli delta, increases first. 🎯 = bundle most relevant to this PR.';
if (changed.some((m) => m.treeShaken)) {
caption +=
' † = consumed piecemeal, so the whole-package bundle is an upper bound and does not drive the verdict.';
}
+ if (attributed) {
+ caption += ' `from` = share of the movement by source module, from the harness build.';
+ }
lines.push(`${caption}`);
lines.push('');
}
+ renderTrackedExports(lines, report);
renderLocByScope(lines, report);
renderAllBundles(lines, report);
@@ -310,34 +451,49 @@ function verdictLines(report, headline) {
.map((id) => report.metrics.find((m) => m.id === id))
.some((m) => m.treeShaken);
if (treeShakenChanged) {
- return ['No change to the bundles real consumers ship. A tree-shaken package moved — see the table.'];
+ return withExportVerdict(report, [
+ 'No change to the bundles real consumers ship. A tree-shaken package moved — see the table.',
+ ]);
}
let s = 'No shipped bundle changed size.';
if (loc.codeDelta === 0 && loc.commentDelta !== 0) { s += ' PR changes are comments-only.'; }
- return [s];
+ return withExportVerdict(report, [s]);
}
const verb = headline.delta.brotli > 0 ? 'grew' : 'shrank';
+ const source = dominantSource(headline);
let s =
`\`${escapeCode(headline.label)}\` ${verb} **${signedSize(headline.delta.brotli)}** brotli to ${
formatSize(headline.head.brotli)
}`
- + ` (${signedPct(headline.delta.brotli, headline.base?.brotli)}) across **${
- signedLoc(loc.codeDelta)
- } shipped LOC**.`;
+ + ` (${signedPct(headline.delta.brotli, headline.base?.brotli)}${source ? `, from \`${escapeCode(source)}\`` : ''})`
+ + ` across **${signedLoc(loc.codeDelta)} shipped LOC**.`;
const largest = report.largest_increase ? report.metrics.find((m) => m.id === report.largest_increase) : null;
if (largest && largest.id !== headline.id) {
s += ` Largest increase: \`${escapeCode(largest.label)}\` **${signedSize(largest.delta.brotli)}**`
+ ` (${signedPct(largest.delta.brotli, largest.base?.brotli)}).`;
}
- return [s];
+ return withExportVerdict(report, [s]);
}
-function headlineCell(m) {
- if (m.delta.brotli === 0) { return '0 B'; }
- return `${signedSize(m.delta.brotli)} (${signedPct(m.delta.brotli, m.base?.brotli)})`;
+// a per-export cost jump is real shipped cost for piecemeal consumers even when
+// no whole bundle moved, so past the verdict floor it earns a line up top
+function withExportVerdict(report, lines) {
+ const movers = [];
+ for (const m of report.metrics) {
+ for (const e of m.exportDeltas?.changed ?? []) {
+ if (e.delta >= TRACE.verdict) { movers.push({ ...e, label: m.label }); }
+ }
+ }
+ if (!movers.length) { return lines; }
+ movers.sort((a, b) => b.delta - a.delta);
+ const shown = movers.slice(0, 2)
+ .map((e) => `\`${escapeCode(e.name)}\` **${signedSize(e.delta)}** min (\`${escapeCode(e.label)}\`)`)
+ .join(', ');
+ const more = movers.length > 2 ? ` and ${movers.length - 2} more` : '';
+ return [...lines, `Import costs moved: ${shown}${more} — see Tracked import costs.`];
}
-function changedRow(m, headlineId) {
+function changedRow(m, headlineId, attributed) {
const mark = m.id === headlineId ? ' 🎯' : m.treeShaken ? ' †' : '';
const brotliAbs = m.status === 'removed' ? '—' : formatSize(m.head.brotli);
const change = m.status === 'added'
@@ -345,7 +501,83 @@ function changedRow(m, headlineId) {
: m.status === 'removed'
? 'removed'
: signedPct(m.delta.brotli, m.base.brotli);
- return `| \`${escapeCode(m.label)}\`${mark} | ${brotliAbs} | ${signedSize(m.delta.brotli)} | ${change} |`;
+ const row = `| \`${escapeCode(m.label)}\`${mark} | ${brotliAbs} | ${signedSize(m.delta.brotli)} | ${change}`;
+ return attributed ? `${row} | ${fromCell(m)} |` : `${row} |`;
+}
+
+// one tight cell: where the movement came from, top sources by share of the
+// total module-level movement, at most two named
+function fromCell(m) {
+ const deltas = m.moduleDeltas ?? [];
+ if (!deltas.length) { return '—'; }
+ const total = deltas.reduce((n, d) => n + Math.abs(d.delta), 0);
+ if (total === 0) { return '—'; }
+ const parts = deltas.slice(0, 2)
+ .map((d) => `\`${escapeCode(d.key)}\` ${Math.round((Math.abs(d.delta) / total) * 100)}%`);
+ if (deltas.length > 2) { parts.push('…'); }
+ return parts.join(' · ');
+}
+
+function dominantSource(m) {
+ const deltas = m.moduleDeltas ?? [];
+ if (!deltas.length) { return null; }
+ const total = deltas.reduce((n, d) => n + Math.abs(d.delta), 0);
+ if (total === 0) { return null; }
+ const top = deltas[0];
+ return Math.abs(top.delta) / total >= 0.6 ? top.key : null;
+}
+
+// tracked import costs: one row per moved co-movement group, named by its
+// master. slaves are priced but never listed — covariance that is known is
+// kept out of mind, so a row here is always a distinct finding
+function renderTrackedExports(lines, report) {
+ const traced = report.metrics.filter((m) => m.exportDeltas !== undefined);
+ if (!traced.length) { return; }
+ const moved = traced.filter((m) => m.exportDeltas);
+ const count = moved.reduce(
+ (n, m) => n + m.exportDeltas.changed.length + m.exportDeltas.added.length + m.exportDeltas.removed.length,
+ 0,
+ );
+ lines.push('');
+ lines.push(
+ `Tracked import costs (${traced.map((m) => escapeText(m.label)).join(' · ')}): ${
+ count === 0 ? 'no movement' : `${count} moved`
+ }
`,
+ );
+ lines.push('');
+ if (!moved.length) {
+ lines.push("No tracked export's import cost moved.");
+ }
+ else {
+ lines.push('| package | export | min | Δ min |');
+ lines.push('|---|---|---:|---:|');
+ for (const m of moved) {
+ for (const e of m.exportDeltas.changed) {
+ const pct = e.pct != null && Math.abs(e.pct) >= 1
+ ? ` (${e.pct > 0 ? '+' : '-'}${Math.abs(e.pct).toFixed(0)}%)`
+ : '';
+ lines.push(
+ `| \`${escapeCode(m.label)}\` | \`${escapeCode(e.name)}\` | ${formatSize(e.head)} | ${
+ signedSize(e.delta)
+ }${pct} |`,
+ );
+ }
+ for (const e of m.exportDeltas.added) {
+ lines.push(`| \`${escapeCode(m.label)}\` | \`${escapeCode(e.name)}\` | ${formatSize(e.bytes)} | new |`);
+ }
+ for (const e of m.exportDeltas.removed) {
+ lines.push(`| \`${escapeCode(m.label)}\` | \`${escapeCode(e.name)}\` | — | removed |`);
+ }
+ }
+ lines.push('');
+ lines.push(
+ 'The minified cost of importing just this export, standalone. One row per co-movement'
+ + ' group, named by its master — full per-export pricing in size-report.json.',
+ );
+ }
+ lines.push('');
+ lines.push(' ');
+ lines.push('');
}
function renderLocByScope(lines, report) {
@@ -414,12 +646,15 @@ function negate(s) {
return { raw: -s.raw, gzip: -s.gzip, brotli: -s.brotli };
}
+// snapshot fields are artifact data — a non-numeric value renders as a dash, never as markdown
function formatSize(bytes) {
+ if (typeof bytes !== 'number' || !Number.isFinite(bytes)) { return '—'; }
const n = Math.abs(bytes);
return n >= 1024 ? `${(bytes / 1024).toFixed(1)} KB` : `${bytes} B`;
}
function signedSize(bytes) {
+ if (typeof bytes !== 'number' || !Number.isFinite(bytes)) { return '—'; }
if (bytes === 0) { return '0 B'; }
const sign = bytes > 0 ? '+' : '-';
const n = Math.abs(bytes);
diff --git a/tools/ci/size/reporter.test.js b/tools/ci/size/reporter.test.js
index 221d7c0e7..1bd02bf66 100644
--- a/tools/ci/size/reporter.test.js
+++ b/tools/ci/size/reporter.test.js
@@ -45,6 +45,11 @@ function loc(map) {
return { total, byScope };
}
+// export entry in the new traced shape: distinct graph unless shared is given
+function exp(cost, graph) {
+ return { cost, graph: graph ?? `g${cost}` };
+}
+
function snapshot(targets, locData) {
const map = {};
for (const t of targets) { map[t.id] = t; }
@@ -240,3 +245,173 @@ test('security — malicious label, group, title, and baseline sha are neutraliz
assert.ok(!md.includes('/commit/notahex'), 'non-hex baseline sha is not linked as a commit');
assert.ok(md.includes('/tree/main'), 'base falls back to the ref tree');
});
+
+/* ------------------------- trace sections ------------------------- */
+
+test('attribution renders as a from column on changed bundles', () => {
+ const head = tgt('pkg-component', [50000, 56000, 170000], { headline: true });
+ const base = tgt('pkg-component', [49600, 55700, 169000], { headline: true });
+ head.modules = { 'utils/strings.js': 5000, 'component/index.js': 900 };
+ base.modules = { 'utils/strings.js': 3900, 'component/index.js': 900 };
+ const { json: report, md } = run(snapshot([head], loc({})), snapshot([base], loc({})));
+ const metric = report.metrics.find((m) => m.id === 'pkg-component');
+ assert.equal(metric.moduleDeltas.length, 1);
+ assert.equal(metric.moduleDeltas[0].key, 'utils/strings.js');
+ assert.equal(metric.moduleDeltas[0].delta, 1100);
+ assert.ok(md.includes('| bundle | brotli | Δ brotli | change | from |'), 'from column added');
+ assert.ok(md.includes('`utils/strings.js` 100%'), 'source named with share');
+});
+
+test('the from column stays hidden when no bundle changed', () => {
+ const head = tgt('pkg-component', [50000, 56000, 170000]);
+ const base = tgt('pkg-component', [50000, 56000, 170000]);
+ head.modules = { 'utils/strings.js': 5000 };
+ base.modules = { 'utils/strings.js': 3900 };
+ const { md } = run(snapshot([head], loc({})), snapshot([base], loc({})));
+ assert.ok(!md.includes('| from |'), 'no from column without a changed bundle');
+});
+
+test('tracked import costs report movers, surface changes, and the verdict line past the floor', () => {
+ const head = tgt('pkg-utils', [17000, 19000, 50000], { treeShaken: true });
+ const base = tgt('pkg-utils', [17000, 19000, 50000], { treeShaken: true });
+ head.exports = { toNumber: exp(1072, 'coercion'), toBoolean: exp(900, 'bool'), brandNew: exp(300, 'fresh') };
+ base.exports = { toNumber: exp(366, 'coercion'), toBoolean: exp(900, 'bool'), oldGone: exp(250, 'gone') };
+ const { json: report, md } = run(snapshot([head], loc({})), snapshot([base], loc({})));
+ const metric = report.metrics.find((m) => m.id === 'pkg-utils');
+ assert.equal(metric.exportDeltas.changed[0].name, 'toNumber');
+ assert.equal(metric.exportDeltas.changed[0].delta, 706);
+ assert.equal(metric.exportDeltas.added[0].name, 'brandNew');
+ assert.equal(metric.exportDeltas.removed[0].name, 'oldGone');
+ assert.ok(md.includes('Tracked import costs'), 'tracked section renders');
+ assert.ok(md.includes('Import costs moved'), 'verdict line renders past the floor');
+ assert.ok(md.includes('| `utils` | `brandNew` | 300 B | new |'), 'added export rendered');
+});
+
+test('export movement below the verdict floor stays out of the alert', () => {
+ const head = tgt('pkg-utils', [17000, 19000, 50000], { treeShaken: true });
+ const base = tgt('pkg-utils', [17000, 19000, 50000], { treeShaken: true });
+ head.exports = { small: exp(400, 's') };
+ base.exports = { small: exp(350, 's') };
+ const { md } = run(snapshot([head], loc({})), snapshot([base], loc({})));
+ assert.ok(!md.includes('Import costs moved'), 'no verdict line for a 50 B mover');
+ assert.ok(md.includes('Tracked import costs'), 'section still lists the mover');
+});
+
+test('snapshots without trace fields render exactly as before', () => {
+ const head = tgt('pkg-component', [50000, 56000, 170000]);
+ const base = tgt('pkg-component', [49600, 55700, 169000]);
+ const { md } = run(snapshot([head], loc({})), snapshot([base], loc({})));
+ // pin the full section inventory, not just the absence of the new headers —
+ // a placeholder or reordering for traceless snapshots must fail this
+ const sections = [...md.matchAll(/([^<:(]+)/g)].map((m) => m[1].trim());
+ assert.deepEqual(sections, ['LOC by scope', 'All 1 bundles, gzip, and raw sizes']);
+ assert.ok(md.includes('| bundle | brotli | Δ brotli | change |'), 'legacy table header');
+ assert.ok(!md.includes('| from |'), 'no from column for traceless snapshots');
+ // and pin the alert block verbatim so verdict drift for old snapshots is loud
+ const alert = md.split('\n').filter((l) => l.startsWith('> ')).join('\n');
+ assert.equal(
+ alert,
+ '> [!WARNING]\n> `component` grew **+400 B** brotli to 48.8 KB (+0.8%) across **+0 shipped LOC**.',
+ );
+});
+
+test('a one-sided trace failure degrades to no section, never a mass surface diff', () => {
+ const head = tgt('pkg-utils', [17000, 19000, 50000], { treeShaken: true });
+ const base = tgt('pkg-utils', [17000, 19000, 50000], { treeShaken: true });
+ base.exports = { a: exp(100), b: exp(200), c: exp(300) };
+ base.modules = { 'utils/a.js': 500 };
+ const { md } = run(snapshot([head], loc({})), snapshot([base], loc({})));
+ assert.ok(!md.includes('Tracked import costs'), 'no tracked section from a head-side trace failure');
+ assert.ok(!md.includes('removed'), 'no false removed rows');
+});
+
+test('exports named after Object.prototype members diff correctly', () => {
+ const head = tgt('pkg-utils', [17000, 19000, 50000], { treeShaken: true });
+ const base = tgt('pkg-utils', [17000, 19000, 50000], { treeShaken: true });
+ head.exports = { keep: exp(100, 'k') };
+ base.exports = { keep: exp(100, 'k'), toString: exp(800, 'proto') };
+ const { json, md } = run(snapshot([head], loc({})), snapshot([base], loc({})));
+ const metric = json.metrics.find((m) => m.id === 'pkg-utils');
+ assert.equal(metric.exportDeltas.removed[0].name, 'toString');
+ assert.ok(md.includes('| `utils` | `toString` | — | removed |'), 'removed row rendered');
+});
+
+test('poisoned byte values in a snapshot never reach the comment', () => {
+ const head = tgt('pkg-utils', [17000, 19000, 50000], { treeShaken: true });
+ const base = tgt('pkg-utils', [17000, 19000, 50000], { treeShaken: true });
+ head.exports = { real: exp(400, 'r'), evil: { cost: '](http://evil)
', graph: 'r' } };
+ base.exports = { real: exp(100, 'r'), evil: exp(50, 'r') };
+ const { md } = run(snapshot([head], loc({})), snapshot([base], loc({})));
+ assert.ok(!md.includes('evil)'), 'crafted string filtered before rendering');
+ assert.ok(!md.includes('
{
+ const head = tgt('pkg-query', [39000, 44000, 130000], { treeShaken: true });
+ const base = tgt('pkg-query', [39000, 44000, 130000], { treeShaken: true });
+ // six exports, one graph — the engine moved +1450 for all of them
+ for (
+ const [name, cost] of [
+ ['$', 40190],
+ ['$$', 40190],
+ ['Query', 40195],
+ ['useAlias', 40260],
+ ['exportGlobals', 40300],
+ ['restoreGlobals', 40310],
+ ]
+ ) {
+ head.exports = head.exports ?? {};
+ base.exports = base.exports ?? {};
+ head.exports[name] = exp(cost, 'engine');
+ base.exports[name] = exp(cost - 1450, 'engine');
+ }
+ const { json, md } = run(snapshot([head], loc({})), snapshot([base], loc({})));
+ const metric = json.metrics.find((m) => m.id === 'pkg-query');
+ assert.equal(metric.exportDeltas.changed.length, 1, 'one row per group');
+ assert.equal(metric.exportDeltas.changed[0].name, '$', 'cheapest-shortest member is master');
+ assert.equal(metric.exportDeltas.changed[0].groupSize, 6);
+ assert.ok(md.includes('| `query` | `$` |'), 'master rendered');
+ assert.ok(!md.includes('$$'), 'slaves never rendered');
+ assert.ok(md.includes('(+4%)'), 'percent shown on the delta');
+});
+
+test('a removed group renders once through its base master', () => {
+ const head = tgt('pkg-utils', [17000, 19000, 50000], { treeShaken: true });
+ const base = tgt('pkg-utils', [17000, 19000, 50000], { treeShaken: true });
+ head.exports = { stay: exp(100, 'k') };
+ base.exports = { stay: exp(100, 'k'), gone: exp(300, 'dead'), goneAlias: exp(305, 'dead') };
+ const { json, md } = run(snapshot([head], loc({})), snapshot([base], loc({})));
+ const metric = json.metrics.find((m) => m.id === 'pkg-utils');
+ assert.equal(metric.exportDeltas.removed.length, 1, 'one removed row for the group');
+ assert.equal(metric.exportDeltas.removed[0].name, 'gone');
+ assert.ok(!md.includes('goneAlias'), 'removed slave not listed');
+});
+
+test('re-mastering anchors the delta on a both-sides member, not the rename', () => {
+ const head = tgt('pkg-utils', [17000, 19000, 50000], { treeShaken: true });
+ const base = tgt('pkg-utils', [17000, 19000, 50000], { treeShaken: true });
+ // aa is new and cheapest (becomes master), bb existed both sides and did not move
+ head.exports = { aa: exp(90, 'fam'), bb: exp(100, 'fam') };
+ base.exports = { bb: exp(100, 'fam') };
+ const { json } = run(snapshot([head], loc({})), snapshot([base], loc({})));
+ const metric = json.metrics.find((m) => m.id === 'pkg-utils');
+ assert.equal(metric.exportDeltas, null, 'no false growth from a new cheaper member');
+});
+
+test('hostile export and module names render inert', () => {
+ const head = tgt('pkg-utils', [17000, 19000, 50000], { treeShaken: true });
+ const base = tgt('pkg-utils', [17000, 19000, 50000], { treeShaken: true });
+ head.exports = { 'evil`|name': exp(5000, 'e') };
+ base.exports = { 'evil`|name': exp(100, 'e') };
+ head.modules = { 'bad`|[x](y).js': 900 };
+ base.modules = { 'bad`|[x](y).js': 100 };
+ const headB = tgt('pkg-b', [50000, 56000, 170000]);
+ const baseB = tgt('pkg-b', [49000, 55000, 168000]);
+ headB.modules = { 'bad`|[x](y).js': 2000 };
+ baseB.modules = { 'bad`|[x](y).js': 100 };
+ const { md } = run(snapshot([head, headB], loc({})), snapshot([base, baseB], loc({})));
+ assert.ok(!md.includes('evil`|name'), 'backtick and pipe stripped from export name');
+ assert.ok(md.includes('evilname'), 'export still listed');
+ assert.ok(!md.includes('bad`|'), 'module key stripped of code-span breakers');
+});
diff --git a/tools/ci/size/targets.js b/tools/ci/size/targets.js
index 011a670b5..e3e3e7fc5 100644
--- a/tools/ci/size/targets.js
+++ b/tools/ci/size/targets.js
@@ -33,6 +33,22 @@ import path from 'node:path';
// real signal, so they stay in.
const TREE_SHAKEN = new Set(['utils']);
+// Packages whose per-export import cost is traced. Consumption model per the
+// 2026-07-02 study (ai/workspace measured every export's module graph):
+// - utils: 139 exports over 39 distinct graphs — maximally piecemeal, every
+// mover is signal. Clusters are module families (type guards, casing,
+// coercion), so grouped rendering collapses a family to one row.
+// - reactivity: 22 exports over 13 graphs — piecemeal with a heavy shared
+// core (computed/derive/match co-move at ~15 KB, signal at ~14 KB).
+// - query: 7 exports over 2 graphs — one engine, two doors. $ carries $$,
+// Query, and the global helpers; registerBehavior adds the behavior stack.
+// Every export is priced; the reporter renders one row per co-movement group,
+// named by its master. Framework internals (renderer, templating, component)
+// are consumed whole — bundle rows and the `from` column already cover them,
+// so they are never export-traced. Future piecemeal packages (data, schema,
+// time) join this list with their consumption model noted.
+export const TRACED_PACKAGES = new Set(['utils', 'reactivity', 'query']);
+
// org-stripped, lowercased package name — matches the build's output filename
// rule (internal-packages/scripts/src/lib/build.js).
function bundleName(pkgName) {
@@ -56,6 +72,7 @@ function discoverPackages(repoRoot) {
label: name,
group: 'package',
scope: name,
+ dir: entry,
file: `packages/${entry}/dist/bundle/${name}.min.js`,
headline: name === 'component',
treeShaken: TREE_SHAKEN.has(name),
diff --git a/tools/ci/size/trace.js b/tools/ci/size/trace.js
new file mode 100644
index 000000000..4049caf60
--- /dev/null
+++ b/tools/ci/size/trace.js
@@ -0,0 +1,121 @@
+/*
+ Source tracing for the size bot, built on esbuild's metafile. Static — no PR
+ code is executed, esbuild parses only. The harness builds these itself so the
+ numbers come from the main-pinned harness on both sides of the diff.
+
+ Two instruments, both rendered sparsely (SNR is the reporter's contract):
+ - bundleModules: per-module minified bytes inside a package bundle. The
+ reporter reduces this to one `from` cell on a changed bundle — where the
+ delta came from, never a table of everything.
+ - exportCosts: the minified cost of importing each export alone, plus its
+ module-graph fingerprint. Exports with the same fingerprint co-move by
+ construction, so the reporter renders one row per group, named by its
+ master — a single export can grow 40% while the whole-package bundle
+ barely wiggles, and that is the row this exists to show.
+*/
+import fs from 'node:fs';
+import path from 'node:path';
+
+import * as esbuild from 'esbuild';
+
+const IDENTIFIER = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
+const CONCURRENCY = 8;
+
+export function packageInfo(repoRoot, packageDir) {
+ const packagePath = path.join(repoRoot, 'packages', packageDir, 'package.json');
+ if (!fs.existsSync(packagePath)) { return null; }
+ const packageFile = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
+ const entryRelative = packageFile.module ?? packageFile.main ?? 'src/index.js';
+ const entry = path.join(repoRoot, 'packages', packageDir, entryRelative);
+ if (!fs.existsSync(entry)) { return null; }
+ // third-party dependencies stay external like the package build. workspace siblings bundle in,
+ // matching the shipped composition — cross-package retention (component growing because of
+ // utils) is the attribution case that matters most
+ const external = Object.keys({
+ ...packageFile.dependencies,
+ ...packageFile.peerDependencies,
+ }).filter((dep) => !isWorkspacePackage(repoRoot, dep));
+ return { entry, external, name: packageFile.name };
+}
+
+function isWorkspacePackage(repoRoot, dep) {
+ const dir = dep.replace(/^@[^/]+\//, '');
+ return fs.existsSync(path.join(repoRoot, 'packages', dir, 'package.json'));
+}
+
+async function build(repoRoot, options) {
+ return esbuild.build({
+ bundle: true,
+ minify: true,
+ format: 'esm',
+ write: false,
+ metafile: true,
+ logLevel: 'silent',
+ absWorkingDir: path.resolve(repoRoot),
+ ...options,
+ });
+}
+
+// 'packages/utils/src/strings.js' / 'node_modules/@semantic-ui/utils/src/strings.js' -> 'utils/strings.js'
+export function moduleKey(inputPath) {
+ const normalized = inputPath.replace(/\\/g, '/');
+ const viaPackages = /^packages\/([^/]+)\/src\/(.+)$/.exec(normalized);
+ if (viaPackages) { return `${viaPackages[1]}/${viaPackages[2]}`; }
+ const viaNodeModules = /^node_modules\/@[^/]+\/([^/]+)\/src\/(.+)$/.exec(normalized);
+ if (viaNodeModules) { return `${viaNodeModules[1]}/${viaNodeModules[2]}`; }
+ return normalized;
+}
+
+export async function bundleModules(repoRoot, { entry, external }) {
+ const result = await build(repoRoot, { entryPoints: [entry], external });
+ const output = Object.values(result.metafile.outputs)[0];
+ const modules = {};
+ for (const [inputPath, input] of Object.entries(output.inputs)) {
+ const key = moduleKey(inputPath);
+ modules[key] = (modules[key] ?? 0) + input.bytesInOutput;
+ }
+ return modules;
+}
+
+export async function listExports(repoRoot, { entry, external }) {
+ const result = await build(repoRoot, { entryPoints: [entry], external, minify: false });
+ const output = Object.values(result.metafile.outputs)[0];
+ return (output.exports ?? []).filter((name) => IDENTIFIER.test(name)).sort();
+}
+
+export async function exportCosts(repoRoot, { entry, external }, names) {
+ const costs = {};
+ for (let i = 0; i < names.length; i += CONCURRENCY) {
+ const chunk = names.slice(i, i + CONCURRENCY);
+ const results = await Promise.all(chunk.map(async (name) => {
+ try {
+ // alias the binding so reserved-word export names (default, new) import legally
+ const result = await build(repoRoot, {
+ external,
+ stdin: {
+ contents: `import { ${name} as probe } from ${JSON.stringify(entry)}; console.log(probe);`,
+ resolveDir: repoRoot,
+ loader: 'js',
+ },
+ });
+ const output = Object.values(result.metafile.outputs)[0];
+ // the fingerprint: exports sharing a graph co-move, the reporter groups on it
+ const graph = Object.keys(output.inputs)
+ .filter((input) => !input.startsWith('<'))
+ .map(moduleKey)
+ .sort()
+ .join('|');
+ return [name, { cost: result.outputFiles[0].contents.length, graph }];
+ }
+ catch {
+ // an unpriceable export (removed, renamed) reads as absent — the reporter
+ // renders a group that lost its last member as removed
+ return null;
+ }
+ }));
+ for (const priced of results) {
+ if (priced) { costs[priced[0]] = priced[1]; }
+ }
+ }
+ return costs;
+}
diff --git a/tools/ci/size/trace.test.js b/tools/ci/size/trace.test.js
new file mode 100644
index 000000000..4a4d517d3
--- /dev/null
+++ b/tools/ci/size/trace.test.js
@@ -0,0 +1,102 @@
+/*
+ Unit tests for trace.js. Run with:
+ node --test tools/ci/size/trace.test.js
+
+ The fixture package replicates the retention physics the tracer exists to
+ catch: `leaky` attaches its config through a bare property assignment (a
+ top-level side effect bundlers must keep), `clean` attaches through a
+ pure-annotated call. Importing `small` must pay for leaky's vocabulary and
+ not for clean's — if that ever stops being true, the instrument is broken.
+*/
+import assert from 'node:assert/strict';
+import fs from 'node:fs';
+import os from 'node:os';
+import path from 'node:path';
+import { test } from 'node:test';
+
+import { bundleModules, exportCosts, listExports, moduleKey, packageInfo } from './trace.js';
+
+function makeFixture() {
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'size-trace-'));
+ const src = path.join(root, 'packages', 'demo', 'src');
+ fs.mkdirSync(src, { recursive: true });
+ fs.writeFileSync(
+ path.join(root, 'packages', 'demo', 'package.json'),
+ JSON.stringify({ name: '@test/demo', module: 'src/index.js', dependencies: { lit: '1.0.0' } }),
+ );
+ fs.writeFileSync(
+ path.join(src, 'index.js'),
+ [
+ "export * from './small.js';",
+ "export * from './vocab.js';",
+ ].join('\n'),
+ );
+ fs.writeFileSync(path.join(src, 'small.js'), 'export const small = (x) => x + 1;\n');
+ fs.writeFileSync(
+ path.join(src, 'vocab.js'),
+ [
+ 'const attach = (fn, config) => Object.assign(fn, { config });',
+ "export const leaky = attach((x) => leaky.config.words[x], { words: { alpha: 'an', beta: 'a' } });",
+ 'leaky.config.extra = true;',
+ "export const clean = /* @__PURE__ */ attach((x) => clean.config.words[x], { words: { gamma: 'an' } });",
+ ].join('\n'),
+ );
+ return { root, info: packageInfo(root, 'demo') };
+}
+
+test('packageInfo resolves the entry and externals from package.json', () => {
+ const { root, info } = makeFixture();
+ assert.equal(info.entry, path.join(root, 'packages', 'demo', 'src', 'index.js'));
+ assert.deepEqual(info.external, ['lit']);
+ assert.equal(packageInfo(root, 'missing'), null);
+});
+
+test('moduleKey normalizes workspace and node_modules paths to package-relative', () => {
+ assert.equal(moduleKey('packages/utils/src/strings.js'), 'utils/strings.js');
+ assert.equal(moduleKey('node_modules/@semantic-ui/utils/src/coercion.js'), 'utils/coercion.js');
+ assert.equal(moduleKey('tools/whatever.js'), 'tools/whatever.js');
+});
+
+test('bundleModules attributes minified bytes per source module', async () => {
+ const { root, info } = makeFixture();
+ const modules = await bundleModules(root, info);
+ assert.ok(modules['demo/small.js'] > 0);
+ assert.ok(modules['demo/vocab.js'] > modules['demo/small.js']);
+});
+
+test('exportCosts expose a retention leak as a cost jump on an unrelated export', async () => {
+ const { root, info } = makeFixture();
+ const leakyCosts = await exportCosts(root, info, ['small']);
+
+ // fix the leak: pure-annotate the attachment and drop the bare property write
+ fs.writeFileSync(
+ path.join(root, 'packages', 'demo', 'src', 'vocab.js'),
+ [
+ 'const attach = (fn, config) => Object.assign(fn, { config });',
+ "export const leaky = /* @__PURE__ */ attach((x) => leaky.config.words[x], { words: { alpha: 'an', beta: 'a' } });",
+ "export const clean = /* @__PURE__ */ attach((x) => clean.config.words[x], { words: { gamma: 'an' } });",
+ ].join('\n'),
+ );
+ const fixedCosts = await exportCosts(root, info, ['small']);
+
+ // the diff a PR comment would show: small's import cost drops when the leak is fixed
+ assert.ok(
+ leakyCosts.small.cost > fixedCosts.small.cost,
+ `leaky ${leakyCosts.small.cost} B should exceed fixed ${fixedCosts.small.cost} B`,
+ );
+ // the fingerprint names the graph so co-movers can be grouped
+ assert.ok(leakyCosts.small.graph.includes('demo/small.js'), 'graph carries module keys');
+});
+
+test('reserved-word export names price via aliased imports without darkening the package', async () => {
+ const { root, info } = makeFixture();
+ fs.appendFileSync(
+ path.join(root, 'packages', 'demo', 'src', 'index.js'),
+ "\nexport default function fallback() { return 'd'; }\n",
+ );
+ const names = await listExports(root, info);
+ assert.ok(names.includes('default'), 'default enumerated');
+ const costs = await exportCosts(root, info, names);
+ assert.ok(costs.default.cost > 0, 'default priced through the alias form');
+ assert.ok(costs.small.cost > 0, 'sibling exports still priced');
+});