import { Profiler, type ReactNode } from "react";
// Render-budget test infrastructure for the agent GUI refactor
// (docs/architecture/agent-gui-refactor-plan.md, section 5.2 item 3).
//
// Performance work is accepted when a budget test goes green, not when more
// memoization is added. Feature-module slices wrap their components with a
// probe and assert commit counts for typical interactions, for example:
//
// const probe = createRenderBudgetProbe();
// render(probe.wrap("composer", ));
// probe.reset();
// await user.type(input, "hello");
// probe.assertRenderBudget({ composer: 3, transcript: 0 });
//
// This module is test-only infrastructure. It must not be exported from the
// package public entry points.
export interface RenderBudgetProbe {
/** Wrap a component subtree so its React commits are counted under `id`. */
wrap(id: string, node: ReactNode): ReactNode;
/** Commits observed for `id` since creation or the last `reset()`. */
renderCount(id: string): number;
/** Zero all counters, typically right before the measured interaction. */
reset(): void;
/**
* Assert an upper bound of commits per probe id. Ids wrapped but not listed
* are ignored; listing an id that was never wrapped fails the assertion so
* budgets cannot silently rot when components are renamed.
*/
assertRenderBudget(budgetByProbeId: Record): void;
}
export function createRenderBudgetProbe(): RenderBudgetProbe {
const commitCountByProbeId = new Map();
const wrappedProbeIds = new Set();
const handleRender = (id: string) => {
commitCountByProbeId.set(id, (commitCountByProbeId.get(id) ?? 0) + 1);
};
return {
assertRenderBudget(budgetByProbeId) {
const failures: string[] = [];
for (const [id, budget] of Object.entries(budgetByProbeId)) {
if (!wrappedProbeIds.has(id)) {
failures.push(
`probe "${id}" is asserted but was never wrapped; update the budget test to match the component tree`
);
continue;
}
const count = commitCountByProbeId.get(id) ?? 0;
if (count > budget) {
failures.push(
`probe "${id}" rendered ${count} time(s), budget is ${budget}`
);
}
}
if (failures.length > 0) {
throw new Error(
`render budget exceeded:\n${failures.map((line) => `- ${line}`).join("\n")}\n` +
"Fix the subscription granularity at its source instead of raising the budget."
);
}
},
renderCount(id) {
return commitCountByProbeId.get(id) ?? 0;
},
reset() {
commitCountByProbeId.clear();
},
wrap(id, node) {
wrappedProbeIds.add(id);
return (
{node}
);
}
};
}