@variance-authority/mcp 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.
Files changed (92) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/LICENSE +21 -0
  3. package/README.md +407 -0
  4. package/dist/bin.d.ts +3 -0
  5. package/dist/bin.js +32 -0
  6. package/dist/bin.js.map +1 -0
  7. package/dist/index.d.ts +35 -0
  8. package/dist/index.js +31 -0
  9. package/dist/index.js.map +1 -0
  10. package/dist/observability-subject.d.ts +22 -0
  11. package/dist/observability-subject.js +2 -0
  12. package/dist/observability-subject.js.map +1 -0
  13. package/dist/presentation.d.ts +6 -0
  14. package/dist/presentation.js +64 -0
  15. package/dist/presentation.js.map +1 -0
  16. package/dist/protocol.d.ts +82 -0
  17. package/dist/protocol.js +186 -0
  18. package/dist/protocol.js.map +1 -0
  19. package/dist/server.d.ts +71 -0
  20. package/dist/server.js +103 -0
  21. package/dist/server.js.map +1 -0
  22. package/dist/tools/adjudicate.d.ts +25 -0
  23. package/dist/tools/adjudicate.js +122 -0
  24. package/dist/tools/adjudicate.js.map +1 -0
  25. package/dist/tools/attention.d.ts +13 -0
  26. package/dist/tools/attention.js +151 -0
  27. package/dist/tools/attention.js.map +1 -0
  28. package/dist/tools/changelog.d.ts +38 -0
  29. package/dist/tools/changelog.js +201 -0
  30. package/dist/tools/changelog.js.map +1 -0
  31. package/dist/tools/changes.d.ts +31 -0
  32. package/dist/tools/changes.js +120 -0
  33. package/dist/tools/changes.js.map +1 -0
  34. package/dist/tools/composition.d.ts +13 -0
  35. package/dist/tools/composition.js +366 -0
  36. package/dist/tools/composition.js.map +1 -0
  37. package/dist/tools/describe.d.ts +20 -0
  38. package/dist/tools/describe.js +279 -0
  39. package/dist/tools/describe.js.map +1 -0
  40. package/dist/tools/diff.d.ts +20 -0
  41. package/dist/tools/diff.js +85 -0
  42. package/dist/tools/diff.js.map +1 -0
  43. package/dist/tools/explain-verdict.d.ts +25 -0
  44. package/dist/tools/explain-verdict.js +92 -0
  45. package/dist/tools/explain-verdict.js.map +1 -0
  46. package/dist/tools/findings.d.ts +40 -0
  47. package/dist/tools/findings.js +130 -0
  48. package/dist/tools/findings.js.map +1 -0
  49. package/dist/tools/narrowing.d.ts +26 -0
  50. package/dist/tools/narrowing.js +38 -0
  51. package/dist/tools/narrowing.js.map +1 -0
  52. package/dist/tools/observability.d.ts +7 -0
  53. package/dist/tools/observability.js +199 -0
  54. package/dist/tools/observability.js.map +1 -0
  55. package/dist/tools/presentations.d.ts +6 -0
  56. package/dist/tools/presentations.js +74 -0
  57. package/dist/tools/presentations.js.map +1 -0
  58. package/dist/tools/run-signals.d.ts +12 -0
  59. package/dist/tools/run-signals.js +115 -0
  60. package/dist/tools/run-signals.js.map +1 -0
  61. package/dist/tools/scenarios.d.ts +6 -0
  62. package/dist/tools/scenarios.js +62 -0
  63. package/dist/tools/scenarios.js.map +1 -0
  64. package/dist/tools/source-tests.d.ts +5 -0
  65. package/dist/tools/source-tests.js +95 -0
  66. package/dist/tools/source-tests.js.map +1 -0
  67. package/dist/tools/subject.d.ts +45 -0
  68. package/dist/tools/subject.js +57 -0
  69. package/dist/tools/subject.js.map +1 -0
  70. package/dist/tools/summary.d.ts +31 -0
  71. package/dist/tools/summary.js +436 -0
  72. package/dist/tools/summary.js.map +1 -0
  73. package/dist/tools/test-signals.d.ts +16 -0
  74. package/dist/tools/test-signals.js +106 -0
  75. package/dist/tools/test-signals.js.map +1 -0
  76. package/dist/tools/tool.d.ts +64 -0
  77. package/dist/tools/tool.js +9 -0
  78. package/dist/tools/tool.js.map +1 -0
  79. package/dist/tools/trace-component.d.ts +17 -0
  80. package/dist/tools/trace-component.js +50 -0
  81. package/dist/tools/trace-component.js.map +1 -0
  82. package/dist/tools/vantage-lines.d.ts +44 -0
  83. package/dist/tools/vantage-lines.js +66 -0
  84. package/dist/tools/vantage-lines.js.map +1 -0
  85. package/dist/tools/variations.d.ts +27 -0
  86. package/dist/tools/variations.js +125 -0
  87. package/dist/tools/variations.js.map +1 -0
  88. package/dist/tools.d.ts +120 -0
  89. package/dist/tools.js +127 -0
  90. package/dist/tools.js.map +1 -0
  91. package/mark.svg +30 -0
  92. package/package.json +64 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-signals.js","sourceRoot":"","sources":["../../src/tools/test-signals.ts"],"names":[],"mappings":"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","sourcesContent":["import type { VantageState, WatchedTest } from '@variance-authority/vantage';\nimport { stringArg, type Tool } from './tool.js';\nimport { announcement, heading, unattached } from './vantage-lines.js';\n\n/**\n * Everything one test has announced, in order, whatever realm said it.\n *\n * This is the answer a timeout cannot give from outside the worker. A test that\n * is hanging has usually heard *something*, and which realm stopped talking is\n * the whole of the diagnosis: nothing at all means no listener or no `vae` call,\n * and a page that spoke while a service did not means the request never reached\n * the service or never came back.\n *\n * Asked while the test is still running, which is the point. The answer is a\n * snapshot and says so.\n */\nexport const testSignals: Tool<VantageState> = {\n name: 'variance_test_signals',\n description:\n 'Everything one test has announced, in order, with the realm that said each — plus work that started and never ended. Answers while the test is still running.',\n inputSchema: {\n type: 'object',\n properties: {\n test: {\n type: 'string',\n description: 'A test id from variance_run_signals, or part of its title.',\n },\n },\n required: ['test'],\n additionalProperties: false,\n },\n run(state, input) {\n if (state.tests.length === 0) return unattached(state);\n const asked = stringArg(input, 'test');\n const found = locate(state, asked);\n return typeof found === 'string' ? found : described(found);\n },\n};\n\n/** The one test meant, or the sentence explaining why it is not one test. */\nfunction locate(state: VantageState, asked: string): WatchedTest | string {\n const byId = state.tests.find((test) => test.id === asked);\n if (byId !== undefined) return byId;\n\n const byTitle = state.tests.filter((test) => test.title === asked);\n if (byTitle.length === 1 && byTitle[0] !== undefined) return byTitle[0];\n\n const lowered = asked.toLowerCase();\n const partial = state.tests.filter((test) => test.title.toLowerCase().includes(lowered));\n if (partial.length === 1 && partial[0] !== undefined) return partial[0];\n if (partial.length === 0) {\n return [\n `No test here matches ${asked}. ${state.tests.length} test(s) have reported:`,\n ...state.tests.map((test) => ` ${test.title} [${test.id}]`),\n ].join('\\n');\n }\n return [\n `${partial.length} tests match ${asked}; ask for one by id:`,\n ...partial.map((test) => ` ${test.title} [${test.id}]`),\n ].join('\\n');\n}\n\nfunction described(test: WatchedTest): string {\n return [\n `${heading(test)} — ${test.state}`,\n '',\n ...heard(test),\n ...pending(test),\n ...remarks(test),\n ...ended(test),\n ].join('\\n');\n}\n\nfunction heard(test: WatchedTest): string[] {\n if (test.heard.length === 0) {\n return [\n 'Nothing has been announced in this execution, by any realm. Either no ' +\n 'listener is installed for it — the test destructures no `events` ' +\n 'fixture — or the code it drives does not call `vae` yet.',\n ];\n }\n return [\n test.forgotten === 0\n ? 'Heard, in order:'\n : `Heard, in order (the first ${test.forgotten} were dropped to stay bounded):`,\n ...test.heard.map((event) => ` ${announcement(event)}`),\n ];\n}\n\nfunction pending(test: WatchedTest): string[] {\n if (test.pending.length === 0) return [];\n return [\n '',\n 'Started and never ended:',\n ...test.pending.map(\n (event) => ` ${event.realm} ${event.location} / ${event.subject} / ${event.action}`,\n ),\n ];\n}\n\nfunction remarks(test: WatchedTest): string[] {\n if (test.remarks.length === 0) return [];\n return ['', 'The listener also knows:', ...test.remarks.map((line) => ` ${line}`)];\n}\n\nfunction ended(test: WatchedTest): string[] {\n if (test.state === 'running') return ['', 'Still running; this is where it had got to.'];\n if (test.error === undefined) return ['', `Ended: ${test.state}.`];\n return ['', `Ended: ${test.state}.`, ...test.error.split('\\n').map((line) => ` ${line}`)];\n}\n"]}
@@ -0,0 +1,64 @@
1
+ import type { RunReport } from '@variance-authority/report';
2
+ /**
3
+ * The contract every tool implements, and the untyped boundary it sits behind.
4
+ *
5
+ * Its own module because every tool imports it and it imports no tool: an
6
+ * interface that lives in the registry beside the list of tools makes each tool
7
+ * depend on all the others, and nothing about that dependency is visible until
8
+ * something loads in the wrong order.
9
+ *
10
+ * The subject is a type parameter rather than `RunReport`, because a tool is a
11
+ * pure function from *something already read* to text, and nothing in that
12
+ * sentence is about a report. It defaults to `RunReport` so the report tools
13
+ * this package ships say nothing about it.
14
+ *
15
+ * `stringArg` is here for the same reason it exists at all. A tool is called with
16
+ * whatever JSON a model produced, so `input` is `unknown` all the way down, and
17
+ * the first thing any tool does with an argument is refuse it or narrow it. That
18
+ * refusal is part of the contract, not a detail of whichever tool refuses first.
19
+ */
20
+ export interface Tool<Subject = RunReport> {
21
+ readonly name: string;
22
+ readonly description: string;
23
+ readonly inputSchema: Readonly<Record<string, unknown>>;
24
+ run(subject: Subject, input: Readonly<Record<string, unknown>>, invocation?: ToolInvocation<Subject>): string;
25
+ }
26
+ /** State held for exactly one previous MCP tool invocation. */
27
+ export interface ToolInvocation<Subject> {
28
+ readonly previous?: Subject;
29
+ }
30
+ /**
31
+ * A set of tools, and what the server answering with them calls itself.
32
+ *
33
+ * The subject is a parameter because the protocol has no opinion about it. A run
34
+ * report is one subject; a workspace's published API is another, and the framing
35
+ * between a JSON-RPC line and a tool's text is identical for both. What differs
36
+ * is the list and the name, and that is exactly what this holds.
37
+ */
38
+ export interface Served<Subject = RunReport> {
39
+ readonly name: string;
40
+ readonly version: string;
41
+ readonly tools: readonly Tool<Subject>[];
42
+ /**
43
+ * What a client puts in front of the model before it has called anything.
44
+ *
45
+ * A tool list says what each tool answers and cannot say when to reach for
46
+ * one, and there is a class of server where that gap is the whole product: a
47
+ * watcher nobody attached a run to lists tools about a run that does not
48
+ * exist, reads as broken, and is never called again. So this takes the
49
+ * subject — the one thing a set of tools cannot see at handshake time — and
50
+ * anything it needs to say about **setup** goes here rather than into a tool
51
+ * nobody has a reason to call yet.
52
+ *
53
+ * Omitted where a tool list is self-explanatory, which is the ordinary case:
54
+ * a report on disk is already there, and nothing has to be arranged.
55
+ */
56
+ readonly instructions?: (subject: Subject) => string;
57
+ }
58
+ export declare const NO_ARGS: {
59
+ readonly type: 'object';
60
+ readonly properties: {};
61
+ readonly additionalProperties: false;
62
+ };
63
+ export declare function stringArg(input: Readonly<Record<string, unknown>>, name: string): string;
64
+ //# sourceMappingURL=tool.d.ts.map
@@ -0,0 +1,9 @@
1
+ export const NO_ARGS = { type: 'object', properties: {}, additionalProperties: false };
2
+ export function stringArg(input, name) {
3
+ const value = input[name];
4
+ if (typeof value !== 'string' || value === '') {
5
+ throw new Error(`\`${name}\` is required and must be a non-empty string`);
6
+ }
7
+ return value;
8
+ }
9
+ //# sourceMappingURL=tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool.js","sourceRoot":"","sources":["../../src/tools/tool.ts"],"names":[],"mappings":"AAkEA,MAAM,CAAC,MAAM,OAAO,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,EAAE,oBAAoB,EAAE,KAAK,EAAW,CAAC;AAEhG,MAAM,UAAU,SAAS,CAAC,KAAwC,EAAE,IAAY;IAC9E,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;QAC9C,MAAM,IAAI,KAAK,CAAC,KAAK,IAAI,+CAA+C,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC","sourcesContent":["import type { RunReport } from '@variance-authority/report';\n\n/**\n * The contract every tool implements, and the untyped boundary it sits behind.\n *\n * Its own module because every tool imports it and it imports no tool: an\n * interface that lives in the registry beside the list of tools makes each tool\n * depend on all the others, and nothing about that dependency is visible until\n * something loads in the wrong order.\n *\n * The subject is a type parameter rather than `RunReport`, because a tool is a\n * pure function from *something already read* to text, and nothing in that\n * sentence is about a report. It defaults to `RunReport` so the report tools\n * this package ships say nothing about it.\n *\n * `stringArg` is here for the same reason it exists at all. A tool is called with\n * whatever JSON a model produced, so `input` is `unknown` all the way down, and\n * the first thing any tool does with an argument is refuse it or narrow it. That\n * refusal is part of the contract, not a detail of whichever tool refuses first.\n */\n\nexport interface Tool<Subject = RunReport> {\n readonly name: string;\n readonly description: string;\n readonly inputSchema: Readonly<Record<string, unknown>>;\n run(\n subject: Subject,\n input: Readonly<Record<string, unknown>>,\n invocation?: ToolInvocation<Subject>,\n ): string;\n}\n\n/** State held for exactly one previous MCP tool invocation. */\nexport interface ToolInvocation<Subject> {\n readonly previous?: Subject;\n}\n\n/**\n * A set of tools, and what the server answering with them calls itself.\n *\n * The subject is a parameter because the protocol has no opinion about it. A run\n * report is one subject; a workspace's published API is another, and the framing\n * between a JSON-RPC line and a tool's text is identical for both. What differs\n * is the list and the name, and that is exactly what this holds.\n */\nexport interface Served<Subject = RunReport> {\n readonly name: string;\n readonly version: string;\n readonly tools: readonly Tool<Subject>[];\n /**\n * What a client puts in front of the model before it has called anything.\n *\n * A tool list says what each tool answers and cannot say when to reach for\n * one, and there is a class of server where that gap is the whole product: a\n * watcher nobody attached a run to lists tools about a run that does not\n * exist, reads as broken, and is never called again. So this takes the\n * subject — the one thing a set of tools cannot see at handshake time — and\n * anything it needs to say about **setup** goes here rather than into a tool\n * nobody has a reason to call yet.\n *\n * Omitted where a tool list is self-explanatory, which is the ordinary case:\n * a report on disk is already there, and nothing has to be arranged.\n */\n readonly instructions?: (subject: Subject) => string;\n}\n\nexport const NO_ARGS = { type: 'object', properties: {}, additionalProperties: false } as const;\n\nexport function stringArg(input: Readonly<Record<string, unknown>>, name: string): string {\n const value = input[name];\n if (typeof value !== 'string' || value === '') {\n throw new Error(`\\`${name}\\` is required and must be a non-empty string`);\n }\n return value;\n}\n"]}
@@ -0,0 +1,17 @@
1
+ import { type Tool } from './tool.js';
2
+ /**
3
+ * `variance_trace_component` — the only answer here that reads across subjects.
4
+ *
5
+ * Its own module because everything else in this package narrows to one subject
6
+ * and then goes deeper. This goes the other way: it holds a component still and
7
+ * sweeps the run past it, and what it returns is a size rather than a diagnosis.
8
+ */
9
+ /**
10
+ * A component, across the whole run.
11
+ *
12
+ * The question a design-system change actually raises. "Did `Button` change" is
13
+ * answerable from one subject; "what did changing `Button` reach" is not, and it
14
+ * is the one that decides whether a branch is safe.
15
+ */
16
+ export declare const trace: Tool;
17
+ //# sourceMappingURL=trace-component.d.ts.map
@@ -0,0 +1,50 @@
1
+ import { stringArg } from './tool.js';
2
+ /**
3
+ * `variance_trace_component` — the only answer here that reads across subjects.
4
+ *
5
+ * Its own module because everything else in this package narrows to one subject
6
+ * and then goes deeper. This goes the other way: it holds a component still and
7
+ * sweeps the run past it, and what it returns is a size rather than a diagnosis.
8
+ */
9
+ /**
10
+ * A component, across the whole run.
11
+ *
12
+ * The question a design-system change actually raises. "Did `Button` change" is
13
+ * answerable from one subject; "what did changing `Button` reach" is not, and it
14
+ * is the one that decides whether a branch is safe.
15
+ */
16
+ export const trace = {
17
+ name: 'variance_trace_component',
18
+ description: 'Every subject a component appears in across the run, with pixels and whether it was ' +
19
+ 'the cause of the change or was displaced by it. Use to size the blast radius of a ' +
20
+ 'design-system or token edit.',
21
+ inputSchema: {
22
+ type: 'object',
23
+ properties: { component: { type: 'string' } },
24
+ required: ['component'],
25
+ additionalProperties: false,
26
+ },
27
+ run(report, input) {
28
+ const component = stringArg(input, 'component');
29
+ const hits = report.observations.flatMap((observation) => {
30
+ const regions = observation.regions.filter((region) => region.component === component);
31
+ return regions.length === 0 ? [] : [{ observation, regions }];
32
+ });
33
+ if (hits.length === 0)
34
+ return `\`${component}\` does not appear in any region in this run`;
35
+ const causeIn = hits.filter(({ regions }) => regions.some((region) => region.cause));
36
+ const file = hits.flatMap(({ regions }) => regions.map((r) => r.file)).find(Boolean);
37
+ return [
38
+ `\`${component}\` appears in ${hits.length} subject(s); ` +
39
+ `it is the cause in ${causeIn.length} of them` +
40
+ (file !== undefined ? ` — ${file}` : ''),
41
+ '',
42
+ ...hits.map(({ observation, regions }) => {
43
+ const pixels = regions.reduce((sum, region) => sum + region.pixels, 0);
44
+ const role = regions.some((region) => region.cause) ? 'cause' : 'collateral';
45
+ return ` ${observation.subject}: ${pixels}px in ${regions.length} region(s) [${role}]`;
46
+ }),
47
+ ].join('\n');
48
+ },
49
+ };
50
+ //# sourceMappingURL=trace-component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace-component.js","sourceRoot":"","sources":["../../src/tools/trace-component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAa,MAAM,WAAW,CAAC;AAEjD;;;;;;GAMG;AAEH;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,KAAK,GAAS;IACzB,IAAI,EAAE,0BAA0B;IAChC,WAAW,EACT,sFAAsF;QACtF,oFAAoF;QACpF,8BAA8B;IAChC,WAAW,EAAE;QACX,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;QAC7C,QAAQ,EAAE,CAAC,WAAW,CAAC;QACvB,oBAAoB,EAAE,KAAK;KAC5B;IAED,GAAG,CAAC,MAAM,EAAE,KAAK;QACf,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;QAEhD,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,EAAE;YACvD,MAAM,OAAO,GAAG,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC;YACvF,OAAO,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;QAChE,CAAC,CAAC,CAAC;QAEH,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,KAAK,SAAS,8CAA8C,CAAC;QAE3F,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACrF,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAErF,OAAO;YACL,KAAK,SAAS,iBAAiB,IAAI,CAAC,MAAM,eAAe;gBACvD,sBAAsB,OAAO,CAAC,MAAM,UAAU;gBAC9C,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC1C,EAAE;YACF,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,EAAE;gBACvC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CAAC,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;gBACvE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC;gBAC7E,OAAO,KAAK,WAAW,CAAC,OAAO,KAAK,MAAM,SAAS,OAAO,CAAC,MAAM,eAAe,IAAI,GAAG,CAAC;YAC1F,CAAC,CAAC;SACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;CACF,CAAC","sourcesContent":["import { stringArg, type Tool } from './tool.js';\n\n/**\n * `variance_trace_component` — the only answer here that reads across subjects.\n *\n * Its own module because everything else in this package narrows to one subject\n * and then goes deeper. This goes the other way: it holds a component still and\n * sweeps the run past it, and what it returns is a size rather than a diagnosis.\n */\n\n/**\n * A component, across the whole run.\n *\n * The question a design-system change actually raises. \"Did `Button` change\" is\n * answerable from one subject; \"what did changing `Button` reach\" is not, and it\n * is the one that decides whether a branch is safe.\n */\nexport const trace: Tool = {\n name: 'variance_trace_component',\n description:\n 'Every subject a component appears in across the run, with pixels and whether it was ' +\n 'the cause of the change or was displaced by it. Use to size the blast radius of a ' +\n 'design-system or token edit.',\n inputSchema: {\n type: 'object',\n properties: { component: { type: 'string' } },\n required: ['component'],\n additionalProperties: false,\n },\n\n run(report, input) {\n const component = stringArg(input, 'component');\n\n const hits = report.observations.flatMap((observation) => {\n const regions = observation.regions.filter((region) => region.component === component);\n return regions.length === 0 ? [] : [{ observation, regions }];\n });\n\n if (hits.length === 0) return `\\`${component}\\` does not appear in any region in this run`;\n\n const causeIn = hits.filter(({ regions }) => regions.some((region) => region.cause));\n const file = hits.flatMap(({ regions }) => regions.map((r) => r.file)).find(Boolean);\n\n return [\n `\\`${component}\\` appears in ${hits.length} subject(s); ` +\n `it is the cause in ${causeIn.length} of them` +\n (file !== undefined ? ` — ${file}` : ''),\n '',\n ...hits.map(({ observation, regions }) => {\n const pixels = regions.reduce((sum, region) => sum + region.pixels, 0);\n const role = regions.some((region) => region.cause) ? 'cause' : 'collateral';\n return ` ${observation.subject}: ${pixels}px in ${regions.length} region(s) [${role}]`;\n }),\n ].join('\\n');\n },\n};\n"]}
@@ -0,0 +1,44 @@
1
+ /**
2
+ * The sentences both live tools share.
3
+ *
4
+ * Its own module for the reason every extracted module here has: the two tools
5
+ * disagreeing about how an announcement is written would make one of them the
6
+ * odd one out in a transcript an agent reads end to end, and the way to stop
7
+ * that is for there to be one spelling rather than a convention.
8
+ */
9
+ import type { VantageState, WatchedTest } from '@variance-authority/vantage';
10
+ /** One announcement, as a line in a listing. */
11
+ export declare function announcement(event: {
12
+ readonly realm: string;
13
+ readonly location: string;
14
+ readonly subject: string;
15
+ readonly action: string;
16
+ readonly phase: string;
17
+ readonly ordinal: number;
18
+ }): string;
19
+ /** A test in one line, without what it heard. */
20
+ export declare function heading(test: WatchedTest): string;
21
+ /** How much this test has to say, as a phrase. */
22
+ export declare function tally(test: WatchedTest): string;
23
+ /**
24
+ * How a run is pointed at this vantage, in the form it goes in.
25
+ *
26
+ * One spelling, said in two places, because the two places are the two moments
27
+ * a reader can act on it: the handshake, before anything has been started, and
28
+ * the empty answer, once something has been started the wrong way. Saying it
29
+ * differently in each would leave a reader comparing two lines to work out
30
+ * whether they are the same instruction.
31
+ *
32
+ * The address is printed verbatim every time. A reader who has to assemble it
33
+ * from a variable name and a port they were told elsewhere is a reader who
34
+ * starts the suite without it.
35
+ */
36
+ export declare function attaching(state: VantageState): string;
37
+ /**
38
+ * What to do when a vantage has heard nothing.
39
+ *
40
+ * A reader told only "no tests" concludes the run has none, or that the tool is
41
+ * broken, and does not discover that attaching is one environment variable.
42
+ */
43
+ export declare function unattached(state: VantageState): string;
44
+ //# sourceMappingURL=vantage-lines.d.ts.map
@@ -0,0 +1,66 @@
1
+ /**
2
+ * The sentences both live tools share.
3
+ *
4
+ * Its own module for the reason every extracted module here has: the two tools
5
+ * disagreeing about how an announcement is written would make one of them the
6
+ * odd one out in a transcript an agent reads end to end, and the way to stop
7
+ * that is for there to be one spelling rather than a convention.
8
+ */
9
+ /** One announcement, as a line in a listing. */
10
+ export function announcement(event) {
11
+ const phase = event.phase === 'once' ? '' : ` (${event.phase})`;
12
+ return `${String(event.ordinal).padStart(4)} ${event.realm} ${event.location} / ${event.subject} / ${event.action}${phase}`;
13
+ }
14
+ /** A test in one line, without what it heard. */
15
+ export function heading(test) {
16
+ const parts = [test.title, test.file];
17
+ if (test.project !== undefined)
18
+ parts.push(`project ${test.project}`);
19
+ if (test.worker >= 0)
20
+ parts.push(`worker ${test.worker}`);
21
+ return parts.join(' — ');
22
+ }
23
+ /** How much this test has to say, as a phrase. */
24
+ export function tally(test) {
25
+ const heard = test.forgotten === 0
26
+ ? `heard ${test.heard.length}`
27
+ : `heard ${test.heard.length} (+${test.forgotten} forgotten)`;
28
+ return test.pending.length === 0 ? heard : `${heard}, pending ${test.pending.length}`;
29
+ }
30
+ /**
31
+ * How a run is pointed at this vantage, in the form it goes in.
32
+ *
33
+ * One spelling, said in two places, because the two places are the two moments
34
+ * a reader can act on it: the handshake, before anything has been started, and
35
+ * the empty answer, once something has been started the wrong way. Saying it
36
+ * differently in each would leave a reader comparing two lines to work out
37
+ * whether they are the same instruction.
38
+ *
39
+ * The address is printed verbatim every time. A reader who has to assemble it
40
+ * from a variable name and a port they were told elsewhere is a reader who
41
+ * starts the suite without it.
42
+ */
43
+ export function attaching(state) {
44
+ const address = state.address ?? 'http://127.0.0.1:<port>';
45
+ return [
46
+ 'A run reports here when it is started with this in its environment:',
47
+ '',
48
+ ` ${VARIABLE}=${address}`,
49
+ '',
50
+ 'That is the same env block `VARIANCE_AUTHORITY_EVENTS` goes in. The suite ' +
51
+ 'needs `varianceFixtures` from `@variance-authority/playwright-test` and ' +
52
+ 'nothing else — a test that takes no screenshot reports exactly what one ' +
53
+ 'that does reports, and a run started without the variable pays nothing.',
54
+ ].join('\n');
55
+ }
56
+ /**
57
+ * What to do when a vantage has heard nothing.
58
+ *
59
+ * A reader told only "no tests" concludes the run has none, or that the tool is
60
+ * broken, and does not discover that attaching is one environment variable.
61
+ */
62
+ export function unattached(state) {
63
+ return ['Nothing has reported to this vantage yet.', '', attaching(state)].join('\n');
64
+ }
65
+ const VARIABLE = 'VARIANCE_AUTHORITY_VANTAGE';
66
+ //# sourceMappingURL=vantage-lines.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"vantage-lines.js","sourceRoot":"","sources":["../../src/tools/vantage-lines.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,gDAAgD;AAChD,MAAM,UAAU,YAAY,CAAC,KAO5B;IACC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,KAAK,GAAG,CAAC;IAChE,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,QAAQ,MAAM,KAAK,CAAC,OAAO,MAAM,KAAK,CAAC,MAAM,GAAG,KAAK,EAAE,CAAC;AAChI,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,OAAO,CAAC,IAAiB;IACvC,MAAM,KAAK,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IACtC,IAAI,IAAI,CAAC,OAAO,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,WAAW,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;IACtE,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,UAAU,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1D,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AAC3B,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,KAAK,CAAC,IAAiB;IACrC,MAAM,KAAK,GACT,IAAI,CAAC,SAAS,KAAK,CAAC;QAClB,CAAC,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE;QAC9B,CAAC,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,MAAM,MAAM,IAAI,CAAC,SAAS,aAAa,CAAC;IAClE,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,aAAa,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;AACxF,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,SAAS,CAAC,KAAmB;IAC3C,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,yBAAyB,CAAC;IAC3D,OAAO;QACL,qEAAqE;QACrE,EAAE;QACF,KAAK,QAAQ,IAAI,OAAO,EAAE;QAC1B,EAAE;QACF,4EAA4E;YAC1E,0EAA0E;YAC1E,0EAA0E;YAC1E,yEAAyE;KAC5E,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,UAAU,CAAC,KAAmB;IAC5C,OAAO,CAAC,2CAA2C,EAAE,EAAE,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACxF,CAAC;AAED,MAAM,QAAQ,GAAG,4BAA4B,CAAC","sourcesContent":["/**\n * The sentences both live tools share.\n *\n * Its own module for the reason every extracted module here has: the two tools\n * disagreeing about how an announcement is written would make one of them the\n * odd one out in a transcript an agent reads end to end, and the way to stop\n * that is for there to be one spelling rather than a convention.\n */\n\nimport type { VantageState, WatchedTest } from '@variance-authority/vantage';\n\n/** One announcement, as a line in a listing. */\nexport function announcement(event: {\n readonly realm: string;\n readonly location: string;\n readonly subject: string;\n readonly action: string;\n readonly phase: string;\n readonly ordinal: number;\n}): string {\n const phase = event.phase === 'once' ? '' : ` (${event.phase})`;\n return `${String(event.ordinal).padStart(4)} ${event.realm} ${event.location} / ${event.subject} / ${event.action}${phase}`;\n}\n\n/** A test in one line, without what it heard. */\nexport function heading(test: WatchedTest): string {\n const parts = [test.title, test.file];\n if (test.project !== undefined) parts.push(`project ${test.project}`);\n if (test.worker >= 0) parts.push(`worker ${test.worker}`);\n return parts.join(' — ');\n}\n\n/** How much this test has to say, as a phrase. */\nexport function tally(test: WatchedTest): string {\n const heard =\n test.forgotten === 0\n ? `heard ${test.heard.length}`\n : `heard ${test.heard.length} (+${test.forgotten} forgotten)`;\n return test.pending.length === 0 ? heard : `${heard}, pending ${test.pending.length}`;\n}\n\n/**\n * How a run is pointed at this vantage, in the form it goes in.\n *\n * One spelling, said in two places, because the two places are the two moments\n * a reader can act on it: the handshake, before anything has been started, and\n * the empty answer, once something has been started the wrong way. Saying it\n * differently in each would leave a reader comparing two lines to work out\n * whether they are the same instruction.\n *\n * The address is printed verbatim every time. A reader who has to assemble it\n * from a variable name and a port they were told elsewhere is a reader who\n * starts the suite without it.\n */\nexport function attaching(state: VantageState): string {\n const address = state.address ?? 'http://127.0.0.1:<port>';\n return [\n 'A run reports here when it is started with this in its environment:',\n '',\n ` ${VARIABLE}=${address}`,\n '',\n 'That is the same env block `VARIANCE_AUTHORITY_EVENTS` goes in. The suite ' +\n 'needs `varianceFixtures` from `@variance-authority/playwright-test` and ' +\n 'nothing else — a test that takes no screenshot reports exactly what one ' +\n 'that does reports, and a run started without the variable pays nothing.',\n ].join('\\n');\n}\n\n/**\n * What to do when a vantage has heard nothing.\n *\n * A reader told only \"no tests\" concludes the run has none, or that the tool is\n * broken, and does not discover that attaching is one environment variable.\n */\nexport function unattached(state: VantageState): string {\n return ['Nothing has reported to this vantage yet.', '', attaching(state)].join('\\n');\n}\n\nconst VARIABLE = 'VARIANCE_AUTHORITY_VANTAGE';\n"]}
@@ -0,0 +1,27 @@
1
+ import type { Tool } from './tool.js';
2
+ /**
3
+ * `variance_variations` — subjects that are other subjects on purpose.
4
+ *
5
+ * The third axis, and the smallest. `variance_summary` and everything under it
6
+ * answer *this subject against its baseline*: two revisions, one thing.
7
+ * `variance_composition` answers *these subjects against each other*, at one
8
+ * commit, over what they are built from. This answers a question neither can
9
+ * reach, because it is not a question about a diff at all:
10
+ *
11
+ * > What does the flag actually do?
12
+ *
13
+ * A new arm behind a feature flag, a second viewport, the dark scheme, a backend
14
+ * fixture returning the empty state — each arrives as a new subject, and a new
15
+ * subject is `new`: one baseline written, an empty diff, nothing said. Every run
16
+ * afterwards compares it only to itself, so the difference the arm exists *for*
17
+ * is the one difference nothing in the suite has ever measured.
18
+ *
19
+ * A subject that declares a parent gets it measured. What is reported is the
20
+ * difference between the two, in this run, with no verdict attached — a dark
21
+ * story is darker than its light parent and that is not a regression. What it
22
+ * is, is reviewable: the difference carries a digest that holds still while the
23
+ * two subjects move together, so *the flag changed what it does* becomes a thing
24
+ * a person can see rather than infer.
25
+ */
26
+ export declare const variations: Tool;
27
+ //# sourceMappingURL=variations.d.ts.map
@@ -0,0 +1,125 @@
1
+ /**
2
+ * `variance_variations` — subjects that are other subjects on purpose.
3
+ *
4
+ * The third axis, and the smallest. `variance_summary` and everything under it
5
+ * answer *this subject against its baseline*: two revisions, one thing.
6
+ * `variance_composition` answers *these subjects against each other*, at one
7
+ * commit, over what they are built from. This answers a question neither can
8
+ * reach, because it is not a question about a diff at all:
9
+ *
10
+ * > What does the flag actually do?
11
+ *
12
+ * A new arm behind a feature flag, a second viewport, the dark scheme, a backend
13
+ * fixture returning the empty state — each arrives as a new subject, and a new
14
+ * subject is `new`: one baseline written, an empty diff, nothing said. Every run
15
+ * afterwards compares it only to itself, so the difference the arm exists *for*
16
+ * is the one difference nothing in the suite has ever measured.
17
+ *
18
+ * A subject that declares a parent gets it measured. What is reported is the
19
+ * difference between the two, in this run, with no verdict attached — a dark
20
+ * story is darker than its light parent and that is not a regression. What it
21
+ * is, is reviewable: the difference carries a digest that holds still while the
22
+ * two subjects move together, so *the flag changed what it does* becomes a thing
23
+ * a person can see rather than infer.
24
+ */
25
+ export const variations = {
26
+ name: 'variance_variations',
27
+ description: 'Subjects this run measured against another subject rather than against a baseline — a ' +
28
+ 'feature flag’s other arm, a second viewport, a dark scheme, a backend fixture — where the ' +
29
+ 'subject declared which one it is a variation of. Says what the variation actually changes ' +
30
+ '(which bands, which components) and carries a digest of that difference which stays the ' +
31
+ 'same for as long as parent and variation move together. Nothing here is a verdict: a ' +
32
+ 'variation is a difference somebody built. Ask this to find out what a flag or a theme does, ' +
33
+ 'or when reviewing a change to a subject that has arms.',
34
+ inputSchema: {
35
+ type: 'object',
36
+ properties: {
37
+ subject: {
38
+ type: 'string',
39
+ description: 'Optional. One variation’s entry, instead of every variation in the run.',
40
+ },
41
+ },
42
+ additionalProperties: false,
43
+ },
44
+ run(report, input) {
45
+ const declared = report.variations;
46
+ if (declared === undefined || declared.length === 0)
47
+ return ABSENT;
48
+ const wanted = typeof input['subject'] === 'string' ? input['subject'] : undefined;
49
+ if (wanted === undefined)
50
+ return whole(declared);
51
+ const entry = declared.find((record) => record.subject === wanted);
52
+ if (entry === undefined)
53
+ return missing(declared, wanted);
54
+ return render(entry);
55
+ },
56
+ };
57
+ /**
58
+ * The absence, said as an absence.
59
+ *
60
+ * A run with no variations is the normal run, and it is not evidence that the
61
+ * suite has no arms — it is evidence that no subject said which subject it
62
+ * varies. That distinction is the whole finding here, so the answer names the
63
+ * declaration rather than reporting an empty list.
64
+ */
65
+ const ABSENT = 'No subject in this run declared itself a variation of another, so nothing was measured on ' +
66
+ 'this axis. That is not a claim that the suite has no variants: a feature flag’s second arm, ' +
67
+ 'a dark story or a narrow-viewport route are ordinary subjects here, each compared only to its ' +
68
+ 'own baseline, and the difference between an arm and what it varies is measured only where a ' +
69
+ 'subject carries a `variance-parent:<id>` tag naming the subject it varies.';
70
+ /**
71
+ * Everything, with the ones somebody wrote down first.
72
+ *
73
+ * The split is not cosmetic. A tag is a statement about two subjects; a name is
74
+ * a convention being read back, and a suite that names its variants gets many of
75
+ * those at once. Printing them interleaved would make the weakest entries the
76
+ * bulk of the answer and put them alongside the strongest with nothing marking
77
+ * the difference.
78
+ */
79
+ function whole(records) {
80
+ const measured = records.filter((record) => record.digest !== undefined);
81
+ const unmeasured = records.filter((record) => record.digest === undefined);
82
+ const identical = measured.filter((record) => record.identical === true);
83
+ const stated = records.filter((record) => record.how !== 'named');
84
+ const named = records.filter((record) => record.how === 'named');
85
+ return [
86
+ `${records.length} variation(s) — ${measured.length} measured` +
87
+ (identical.length === 0 ? '' : `, ${identical.length} of them identical to their parent`) +
88
+ (unmeasured.length === 0 ? '' : `, ${unmeasured.length} not compared`) +
89
+ `; ${stated.length} declared with a tag, ${named.length} read off the names`,
90
+ ...(stated.length === 0 ? [] : ['', 'declared', '', ...stated.map(render)]),
91
+ ...(named.length === 0
92
+ ? []
93
+ : [
94
+ '',
95
+ 'named — nobody declared these pairs; each subject’s id extends its parent’s id, so ' +
96
+ 'the link is only as good as the naming convention',
97
+ '',
98
+ ...named.map(render),
99
+ ]),
100
+ ].join('\n');
101
+ }
102
+ function render(record) {
103
+ const head = record.parent === undefined
104
+ ? record.subject
105
+ : `${record.subject} ← ${record.parent}${bands(record.bands)}`;
106
+ return [
107
+ head,
108
+ ` ${record.because}`,
109
+ ...(record.components === undefined
110
+ ? []
111
+ : [` components: ${record.components.join(', ')}`]),
112
+ ...(record.unobserved === undefined
113
+ ? []
114
+ : [` unobserved here: ${record.unobserved.join(', ')}`]),
115
+ ].join('\n');
116
+ }
117
+ function missing(declared, wanted) {
118
+ return (`\`${wanted}\` declared no parent in this run, so there is no variation to describe. Every ` +
119
+ `subject that did: ${declared.map((record) => record.subject).join(', ')}`);
120
+ }
121
+ /** Empty means the two are alike everywhere observed, never *no band*. */
122
+ function bands(bands) {
123
+ return bands === undefined || bands.length === 0 ? '' : ` (${bands.join(', ')})`;
124
+ }
125
+ //# sourceMappingURL=variations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"variations.js","sourceRoot":"","sources":["../../src/tools/variations.ts"],"names":[],"mappings":"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","sourcesContent":["import type { VariationRecord } from '@variance-authority/report';\nimport type { Tool } from './tool.js';\n\n/**\n * `variance_variations` — subjects that are other subjects on purpose.\n *\n * The third axis, and the smallest. `variance_summary` and everything under it\n * answer *this subject against its baseline*: two revisions, one thing.\n * `variance_composition` answers *these subjects against each other*, at one\n * commit, over what they are built from. This answers a question neither can\n * reach, because it is not a question about a diff at all:\n *\n * > What does the flag actually do?\n *\n * A new arm behind a feature flag, a second viewport, the dark scheme, a backend\n * fixture returning the empty state — each arrives as a new subject, and a new\n * subject is `new`: one baseline written, an empty diff, nothing said. Every run\n * afterwards compares it only to itself, so the difference the arm exists *for*\n * is the one difference nothing in the suite has ever measured.\n *\n * A subject that declares a parent gets it measured. What is reported is the\n * difference between the two, in this run, with no verdict attached — a dark\n * story is darker than its light parent and that is not a regression. What it\n * is, is reviewable: the difference carries a digest that holds still while the\n * two subjects move together, so *the flag changed what it does* becomes a thing\n * a person can see rather than infer.\n */\n\nexport const variations: Tool = {\n name: 'variance_variations',\n description:\n 'Subjects this run measured against another subject rather than against a baseline — a ' +\n 'feature flag’s other arm, a second viewport, a dark scheme, a backend fixture — where the ' +\n 'subject declared which one it is a variation of. Says what the variation actually changes ' +\n '(which bands, which components) and carries a digest of that difference which stays the ' +\n 'same for as long as parent and variation move together. Nothing here is a verdict: a ' +\n 'variation is a difference somebody built. Ask this to find out what a flag or a theme does, ' +\n 'or when reviewing a change to a subject that has arms.',\n inputSchema: {\n type: 'object',\n properties: {\n subject: {\n type: 'string',\n description: 'Optional. One variation’s entry, instead of every variation in the run.',\n },\n },\n additionalProperties: false,\n },\n\n run(report, input) {\n const declared = report.variations;\n if (declared === undefined || declared.length === 0) return ABSENT;\n\n const wanted = typeof input['subject'] === 'string' ? input['subject'] : undefined;\n if (wanted === undefined) return whole(declared);\n\n const entry = declared.find((record) => record.subject === wanted);\n if (entry === undefined) return missing(declared, wanted);\n return render(entry);\n },\n};\n\n/**\n * The absence, said as an absence.\n *\n * A run with no variations is the normal run, and it is not evidence that the\n * suite has no arms — it is evidence that no subject said which subject it\n * varies. That distinction is the whole finding here, so the answer names the\n * declaration rather than reporting an empty list.\n */\nconst ABSENT =\n 'No subject in this run declared itself a variation of another, so nothing was measured on ' +\n 'this axis. That is not a claim that the suite has no variants: a feature flag’s second arm, ' +\n 'a dark story or a narrow-viewport route are ordinary subjects here, each compared only to its ' +\n 'own baseline, and the difference between an arm and what it varies is measured only where a ' +\n 'subject carries a `variance-parent:<id>` tag naming the subject it varies.';\n\n/**\n * Everything, with the ones somebody wrote down first.\n *\n * The split is not cosmetic. A tag is a statement about two subjects; a name is\n * a convention being read back, and a suite that names its variants gets many of\n * those at once. Printing them interleaved would make the weakest entries the\n * bulk of the answer and put them alongside the strongest with nothing marking\n * the difference.\n */\nfunction whole(records: readonly VariationRecord[]): string {\n const measured = records.filter((record) => record.digest !== undefined);\n const unmeasured = records.filter((record) => record.digest === undefined);\n const identical = measured.filter((record) => record.identical === true);\n const stated = records.filter((record) => record.how !== 'named');\n const named = records.filter((record) => record.how === 'named');\n\n return [\n `${records.length} variation(s) — ${measured.length} measured` +\n (identical.length === 0 ? '' : `, ${identical.length} of them identical to their parent`) +\n (unmeasured.length === 0 ? '' : `, ${unmeasured.length} not compared`) +\n `; ${stated.length} declared with a tag, ${named.length} read off the names`,\n ...(stated.length === 0 ? [] : ['', 'declared', '', ...stated.map(render)]),\n ...(named.length === 0\n ? []\n : [\n '',\n 'named — nobody declared these pairs; each subject’s id extends its parent’s id, so ' +\n 'the link is only as good as the naming convention',\n '',\n ...named.map(render),\n ]),\n ].join('\\n');\n}\n\nfunction render(record: VariationRecord): string {\n const head =\n record.parent === undefined\n ? record.subject\n : `${record.subject} ← ${record.parent}${bands(record.bands)}`;\n\n return [\n head,\n ` ${record.because}`,\n ...(record.components === undefined\n ? []\n : [` components: ${record.components.join(', ')}`]),\n ...(record.unobserved === undefined\n ? []\n : [` unobserved here: ${record.unobserved.join(', ')}`]),\n ].join('\\n');\n}\n\nfunction missing(declared: readonly VariationRecord[], wanted: string): string {\n return (\n `\\`${wanted}\\` declared no parent in this run, so there is no variation to describe. Every ` +\n `subject that did: ${declared.map((record) => record.subject).join(', ')}`\n );\n}\n\n/** Empty means the two are alike everywhere observed, never *no band*. */\nfunction bands(bands: readonly string[] | undefined): string {\n return bands === undefined || bands.length === 0 ? '' : ` (${bands.join(', ')})`;\n}\n"]}
@@ -0,0 +1,120 @@
1
+ import type { EyesArchive } from '@variance-authority/eyes';
2
+ import type { PresentationReport } from '@variance-authority/presentation';
3
+ import type { ExecutionIndex } from '@variance-authority/sense/test-selection';
4
+ import type { VantageState } from '@variance-authority/vantage';
5
+ import type { ObservabilitySubject } from './observability-subject.js';
6
+ import { diffState, type StateDifference } from './tools/diff.js';
7
+ import { type PresentationEvidence } from './tools/presentations.js';
8
+ import { type ScenarioEvidence } from './tools/scenarios.js';
9
+ import { NO_ARGS, stringArg, type Tool } from './tools/tool.js';
10
+ /**
11
+ * The tools, as pure functions over a run report.
12
+ *
13
+ * Separated from the transport deliberately. A tool implementation tangled into
14
+ * a JSON-RPC handler can only be exercised by speaking JSON-RPC at it, and the
15
+ * interesting question — *does this answer help an agent fix the thing?* — then
16
+ * becomes the hardest thing in the package to ask.
17
+ *
18
+ * Every answer is text, and the shape of that text is the product. An agent does
19
+ * not benefit from a JSON blob it has to interpret; it benefits from the same
20
+ * sentence a person would want, with a file path on the end. So these read like
21
+ * the report does: cause first, collateral counted, and a path an editor opens.
22
+ *
23
+ * `variance_adjudicate` is the one tool that takes evidence *in*. It follows
24
+ * `summary` and `changes` because it needs nothing they printed and everything
25
+ * they cannot supply: an agent's own account of what it was doing. The other report tools
26
+ * answer *what changed*; this one answers *what changed against what you claimed*, and
27
+ * its third arm — declared, and did not happen — is the only thing here that can
28
+ * catch an edit which never landed. An agent that has just edited something
29
+ * should call it before `describe`, and an agent reviewing somebody else's run
30
+ * has nothing to declare and should skip it.
31
+ *
32
+ * One tool per module under `./tools/`, and this file is the list. Each answer is
33
+ * a paragraph somebody argued about, and the arguments do not compose — the
34
+ * reason the summary refuses to say "nothing to review" has nothing to do with
35
+ * the reason findings are grouped by rule — so they are read, and edited, one at
36
+ * a time. What stays here is the only thing that is genuinely about the set:
37
+ * the order, which is the order `tools/list` announces them in and therefore the
38
+ * order an agent meets them in. `variance_summary` is first because every report
39
+ * tool takes an argument it printed. `variance_diff` sits beside it as the session
40
+ * question, and `variance_changes` follows because it
41
+ * is the one that decides how many of the rest get called: an agent that walks
42
+ * forty changed subjects one at a time spends forty calls learning what one call
43
+ * says, which is *three things happened and one of them explains thirty-one*.
44
+ *
45
+ * `variance_composition` follows `adjudicate`, and the boundary it sits on is
46
+ * the one worth seeing: the first seven answer about the *suite* and the last four narrow to a
47
+ * subject. It goes after `changes` rather than before because the two reshape
48
+ * the same run along different axes and only one of them is about this run's
49
+ * diff — `changes` says which decisions there are, and this says which component
50
+ * and which caller is behind one, including when the answer is *nothing in this
51
+ * run*.
52
+ *
53
+ * `variance_variations` follows it, on the same axis and one step further out. Both
54
+ * compare this run to itself; `composition` compares subjects that were never
55
+ * meant to differ, and this compares the ones that were. It is the only tool here
56
+ * whose answer is not, in any reading, a finding — which is why it is neither
57
+ * earlier (an agent triaging a visual diff would spend a call learning that a dark story
58
+ * is dark) nor omitted (when a change *is* to a flagged component, what the flag
59
+ * does is the first thing the reviewer does not know).
60
+ *
61
+ * `variance_changelog` closes the suite-level group because it is the only tool
62
+ * here that is not about the run. The other report tools describe what a run observed;
63
+ * this one describes what *accepting* it would write down, and that answer is
64
+ * the last thing an agent needs before it proposes a command. Its position is
65
+ * also a claim about when it stops being useful: after acceptance there is
66
+ * nothing to preview, because the record exists and `git log` has it.
67
+ *
68
+ * It is the second tool whose answer changes with the agent's own input, and
69
+ * unlike `adjudicate` the input is not evidence — it is the selection, the same
70
+ * one `accept` takes. That is why it must not be earlier: an agent that has not
71
+ * yet read `changes` has no shape to ask about, and would be told what `--all`
72
+ * records before knowing whether `--all` is what it wants.
73
+ */
74
+ export type { Served, Tool, ToolInvocation } from './tools/tool.js';
75
+ export type { StateDifference };
76
+ export { NO_ARGS, stringArg };
77
+ export { notObservedSentence } from './tools/subject.js';
78
+ export { diffState };
79
+ /** The source-to-named-test tool set for an MCP server over an execution index. */
80
+ export declare const SOURCE_TEST_TOOLS: readonly [Tool<ExecutionIndex>, Tool<ExecutionIndex>];
81
+ /** Look up one source-test tool without widening it to the visual-report subject. */
82
+ export declare function sourceTestToolByName(name: string): Tool<ExecutionIndex> | undefined;
83
+ /**
84
+ * The tool set for a watcher attached to a suite that is still running.
85
+ *
86
+ * A third subject, and the first one that is not a thing somebody produced. The
87
+ * report tools answer about a run that finished and the source-test tools answer
88
+ * about an index that was written; these answer about a run *in flight*, held in
89
+ * the memory of the process answering, and gone when it exits.
90
+ *
91
+ * The listing comes first for the same reason `variance_summary` does: the other
92
+ * two take an argument it printed. `variance_diff` is last here rather than
93
+ * second, because on a live subject it is not the session question but the
94
+ * *progress* question — what the suite did between two asks — and that is only
95
+ * worth asking once a reader knows what they are watching.
96
+ */
97
+ export declare const VANTAGE_TOOLS: readonly [Tool<VantageState>, Tool<VantageState>, Tool<VantageState>];
98
+ /** Look up one live-run tool without widening it to the visual-report subject. */
99
+ export declare function vantageToolByName(name: string): Tool<VantageState> | undefined;
100
+ export declare const TOOLS: readonly Tool[];
101
+ export declare function toolByName(name: string): Tool | undefined;
102
+ /** Full presentation graphs supplied by the caller, independent of report projections. */
103
+ export declare const PRESENTATION_TOOLS: readonly [Tool<PresentationEvidence>, Tool<PresentationEvidence>];
104
+ export declare function presentationToolByName(name: string): Tool<readonly PresentationReport[]> | undefined;
105
+ /** Test attention captured synchronously while DOM nodes still have attribution. */
106
+ export declare const EYES_TOOLS: readonly [Tool<EyesArchive>, Tool<EyesArchive>];
107
+ export declare function eyesToolByName(name: string): Tool<EyesArchive> | undefined;
108
+ /** Retained scenario executions, including witnessed Arrange state and Act outcomes. */
109
+ export declare const SCENARIO_TOOLS: readonly [Tool<ScenarioEvidence>, Tool<ScenarioEvidence>];
110
+ export declare function scenarioToolByName(name: string): Tool<ScenarioEvidence> | undefined;
111
+ /**
112
+ * Every independently supplied observability domain behind one MCP connection.
113
+ *
114
+ * Native tools are lifted without changing their answers. A missing field is a
115
+ * tool error, not an empty subject, and previous invocation state is projected
116
+ * through the same field before a native diff sees it.
117
+ */
118
+ export declare const OBSERVABILITY_TOOLS: readonly Tool<ObservabilitySubject>[];
119
+ export declare function observabilityToolByName(name: string): Tool<ObservabilitySubject> | undefined;
120
+ //# sourceMappingURL=tools.d.ts.map