fix(insights): pass the record's module map to every correlation scan

The Coach correlations tool, the per-metric card and the period
narrative scanned every channel, so a switched-off module still fed
their statistics and the text a model was grounded on. The matrix's
modules option is now required, each caller resolves the record's map,
and the Coach skips the lab pass with labs off. A registry guard holds
every caller to resolving the map itself.

The mask's doc comment now states what it does to the false discovery
correction: fewer channels is a smaller family, so q-values of the
remaining pairs move and a borderline pair can cross the threshold.
This commit is contained in:
Marc-André Bombeck committed 2026-09-24 10:17:14 +02:00
1 parent ab0d1b4b71
commit 5bd19573ea
11 files changed
+238 -7

No files matched your search

@@ -300,6 +300,25 @@ describe("the replaced maps and guards stay gone", () => {
expect(hits).toEqual([]);
});
it("every discovery-matrix caller passes the record's module map", () => {
// `modules` is a required option, so omitting it fails to compile; this
// catches the other way to get it wrong, a hand-built map, by requiring
// each calling file to resolve the record's own. A file that forwards a
// map it received from its caller would need an entry here.
const callers = sources.filter(
(s) =>
/\bassembleDiscoveryMatrix\s*\(/.test(s.code) &&
s.rel !== "lib/insights/discovery-matrix.ts",
);
expect(callers.length).toBeGreaterThanOrEqual(4);
for (const caller of callers) {
expect(caller.code, caller.rel).toMatch(/\bresolveModuleMap\s*\(/);
expect(caller.code, caller.rel).toMatch(
/assembleDiscoveryMatrix\s*\([\s\S]*?\bmodules\b/,
);
}
});
it("the dashboard maps are views, built from the surface map and nowhere else", () => {
const widgetModules = sources.find(
(s) => s.rel === "lib/dashboard/widget-modules.ts",