@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,130 @@
1
+ import { AGE_WORDS, ageOf, arrivalLine, bandTitle, carriedLine, mixedAges, } from '@variance-authority/report';
2
+ /**
3
+ * `variance_findings` — the one answer here that reads no comparison.
4
+ *
5
+ * Alone in a module because nothing else in this package groups by anything but
6
+ * a subject. This tool inverts that twice — by rule, then by the place the rule
7
+ * fired — and both inversions exist for the same reason the docket does: the
8
+ * unit a reader acts on is an edit, not an occurrence.
9
+ */
10
+ /**
11
+ * Defects in the render itself, which no comparison could have reported.
12
+ *
13
+ * A separate tool rather than part of `variance_describe` because it answers a
14
+ * different question. `describe` answers "what did this change do"; this answers
15
+ * "what is wrong with this component regardless of whether anyone touched it" —
16
+ * and the second is the one a comparison structurally cannot reach, since a
17
+ * control that never had an accessible name compares equal to itself forever.
18
+ *
19
+ * Grouped by rule rather than by subject. One missing `alt` in twelve stories is
20
+ * one edit to one component, and listing it twelve times under twelve subject
21
+ * headings is the same fatigue the docket exists to prevent.
22
+ *
23
+ * ## Two things an agent cannot act on this list without
24
+ *
25
+ * **What kind of defect it is.** Most of these rules are accessibility and the
26
+ * description used to say all of them were, which is wrong about the two that
27
+ * are not — a string the locale pass never translated is a content defect, and
28
+ * a box past its container's edge is a layout one. The band is the same word the
29
+ * project blocks on, so the heading a reader sees and the policy that stops
30
+ * their merge are the same word.
31
+ *
32
+ * **Whether it is theirs.** A defect the baseline carried too is somebody else's
33
+ * afternoon, and an agent told twelve defects with no dating will either fix
34
+ * twelve or fix none. The date is only ever printed from a record; nothing here
35
+ * infers it from an absence — and the rules holding a defect this change brought
36
+ * are listed before the rules holding only inherited ones, so an agent that reads
37
+ * the first group and stops has read the part it can act on.
38
+ */
39
+ export const findings = {
40
+ name: 'variance_findings',
41
+ description: 'Defects found in the renders themselves, with no baseline involved: mostly accessibility ' +
42
+ '— a control with no accessible name, a skipped heading level, a broken label ' +
43
+ 'association — and some content and layout. Grouped by rule, each naming the band, the ' +
44
+ 'component, the file, and whether the baseline already carried it. These are invisible to ' +
45
+ 'any comparison, so they do not affect the verdict.',
46
+ inputSchema: {
47
+ type: 'object',
48
+ properties: {
49
+ rule: { type: 'string', description: 'Optional. Only findings of this rule.' },
50
+ },
51
+ additionalProperties: false,
52
+ },
53
+ run(report, input) {
54
+ const wanted = typeof input?.['rule'] === 'string' ? input['rule'] : undefined;
55
+ const all = report.observations.flatMap((observation) => (observation.findings ?? [])
56
+ .filter((finding) => wanted === undefined || finding.rule === wanted)
57
+ .map((finding) => ({ subject: observation.subject, finding })));
58
+ if (all.length === 0) {
59
+ if (wanted !== undefined)
60
+ return `No findings for rule ${wanted}.`;
61
+ const inspected = report.observations.filter((o) => o.findings !== undefined).length;
62
+ // Two different sentences, because they are two different claims. A report
63
+ // whose writer never inspected anything has not found the renders clean;
64
+ // it has not looked at them, and an agent told otherwise acts on it.
65
+ return inspected === 0
66
+ ? 'Nothing in this report was inspected — no observation carries a findings list, ' +
67
+ 'so this is not evidence that the renders were clean.'
68
+ : `No findings across ${inspected} inspected subject(s).`;
69
+ }
70
+ const byRule = new Map();
71
+ for (const entry of all) {
72
+ const bucket = byRule.get(entry.finding.rule) ?? [];
73
+ bucket.push(entry);
74
+ byRule.set(entry.finding.rule, bucket);
75
+ }
76
+ // Printed per place only where the lines around it disagree. On a report
77
+ // written before the marks were kept, `not dated` on every line is a column
78
+ // of one repeated word, and the lead sentence has already said it.
79
+ const dated = mixedAges(all.map((entry) => entry.finding));
80
+ // Rules that hold something this change brought, first. The band order is the
81
+ // report's own and it is a claim about severity; this is a claim about whose
82
+ // afternoon it is, and it outranks severity for the length of one review.
83
+ const ranked = [...byRule].sort((left, right) => Number(right[1].some(({ finding }) => ageOf(finding) === 'new')) -
84
+ Number(left[1].some(({ finding }) => ageOf(finding) === 'new')));
85
+ const groups = ranked.map(([rule, entries]) => {
86
+ // Same finding, same component, several subjects: one edit, so one line
87
+ // with the subjects counted rather than one line per place it shows up.
88
+ // The age joins the key for the same reason it is drawn on the row: one
89
+ // rule firing on an element the baseline had and on one it did not is two
90
+ // findings, and folding them files the new one under the old one's date.
91
+ const places = new Map();
92
+ for (const { subject, finding } of entries) {
93
+ const age = ageOf(finding);
94
+ const key = `${finding.component ?? finding.path}\u0000${finding.what}\u0000${age}`;
95
+ const place = places.get(key) ?? {
96
+ what: finding.what,
97
+ age,
98
+ ...(finding.file !== undefined ? { file: finding.file } : {}),
99
+ subjects: [],
100
+ };
101
+ place.subjects.push(subject);
102
+ places.set(key, place);
103
+ }
104
+ const band = entries[0]?.finding.band;
105
+ return [
106
+ `${band === undefined ? '' : `${bandTitle(band)} · `}${rule} — ` +
107
+ `${entries.length} occurrence(s)`,
108
+ ...[...places.values()].map((place) => {
109
+ const reach = place.subjects.length === 1
110
+ ? place.subjects[0]
111
+ : `${place.subjects.length} subjects: ${place.subjects.slice(0, 3).join(', ')}` +
112
+ (place.subjects.length > 3 ? ', …' : '');
113
+ return (` ${place.what}${dated ? ` — ${AGE_WORDS[place.age]}` : ''}` +
114
+ `\n ${place.file ?? '(no source index)'} — in ${reach}`);
115
+ }),
116
+ ].join('\n');
117
+ });
118
+ const subjects = new Set(all.map((entry) => entry.subject)).size;
119
+ const found = all.map((entry) => entry.finding);
120
+ const lead = [
121
+ arrivalLine(found),
122
+ carriedLine(found),
123
+ `${all.length} occurrence(s) across ${subjects} subject(s), read without a baseline`,
124
+ ]
125
+ .filter((clause) => clause !== undefined)
126
+ .join(' · ');
127
+ return [lead, ...groups].join('\n\n');
128
+ },
129
+ };
130
+ //# sourceMappingURL=findings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"findings.js","sourceRoot":"","sources":["../../src/tools/findings.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AGE_WORDS,\n ageOf,\n arrivalLine,\n bandTitle,\n carriedLine,\n mixedAges,\n} from '@variance-authority/report';\nimport type { Age } from '@variance-authority/report';\nimport type { Tool } from './tool.js';\n\n/**\n * `variance_findings` — the one answer here that reads no comparison.\n *\n * Alone in a module because nothing else in this package groups by anything but\n * a subject. This tool inverts that twice — by rule, then by the place the rule\n * fired — and both inversions exist for the same reason the docket does: the\n * unit a reader acts on is an edit, not an occurrence.\n */\n\n/**\n * Defects in the render itself, which no comparison could have reported.\n *\n * A separate tool rather than part of `variance_describe` because it answers a\n * different question. `describe` answers \"what did this change do\"; this answers\n * \"what is wrong with this component regardless of whether anyone touched it\" —\n * and the second is the one a comparison structurally cannot reach, since a\n * control that never had an accessible name compares equal to itself forever.\n *\n * Grouped by rule rather than by subject. One missing `alt` in twelve stories is\n * one edit to one component, and listing it twelve times under twelve subject\n * headings is the same fatigue the docket exists to prevent.\n *\n * ## Two things an agent cannot act on this list without\n *\n * **What kind of defect it is.** Most of these rules are accessibility and the\n * description used to say all of them were, which is wrong about the two that\n * are not — a string the locale pass never translated is a content defect, and\n * a box past its container's edge is a layout one. The band is the same word the\n * project blocks on, so the heading a reader sees and the policy that stops\n * their merge are the same word.\n *\n * **Whether it is theirs.** A defect the baseline carried too is somebody else's\n * afternoon, and an agent told twelve defects with no dating will either fix\n * twelve or fix none. The date is only ever printed from a record; nothing here\n * infers it from an absence — and the rules holding a defect this change brought\n * are listed before the rules holding only inherited ones, so an agent that reads\n * the first group and stops has read the part it can act on.\n */\nexport const findings: Tool = {\n name: 'variance_findings',\n description:\n 'Defects found in the renders themselves, with no baseline involved: mostly accessibility ' +\n '— a control with no accessible name, a skipped heading level, a broken label ' +\n 'association — and some content and layout. Grouped by rule, each naming the band, the ' +\n 'component, the file, and whether the baseline already carried it. These are invisible to ' +\n 'any comparison, so they do not affect the verdict.',\n inputSchema: {\n type: 'object',\n properties: {\n rule: { type: 'string', description: 'Optional. Only findings of this rule.' },\n },\n additionalProperties: false,\n },\n\n run(report, input) {\n const wanted = typeof input?.['rule'] === 'string' ? input['rule'] : undefined;\n\n const all = report.observations.flatMap((observation) =>\n (observation.findings ?? [])\n .filter((finding) => wanted === undefined || finding.rule === wanted)\n .map((finding) => ({ subject: observation.subject, finding })),\n );\n\n if (all.length === 0) {\n if (wanted !== undefined) return `No findings for rule ${wanted}.`;\n\n const inspected = report.observations.filter((o) => o.findings !== undefined).length;\n\n // Two different sentences, because they are two different claims. A report\n // whose writer never inspected anything has not found the renders clean;\n // it has not looked at them, and an agent told otherwise acts on it.\n return inspected === 0\n ? 'Nothing in this report was inspected — no observation carries a findings list, ' +\n 'so this is not evidence that the renders were clean.'\n : `No findings across ${inspected} inspected subject(s).`;\n }\n\n const byRule = new Map<string, typeof all>();\n for (const entry of all) {\n const bucket = byRule.get(entry.finding.rule) ?? [];\n bucket.push(entry);\n byRule.set(entry.finding.rule, bucket);\n }\n\n // Printed per place only where the lines around it disagree. On a report\n // written before the marks were kept, `not dated` on every line is a column\n // of one repeated word, and the lead sentence has already said it.\n const dated = mixedAges(all.map((entry) => entry.finding));\n\n // Rules that hold something this change brought, first. The band order is the\n // report's own and it is a claim about severity; this is a claim about whose\n // afternoon it is, and it outranks severity for the length of one review.\n const ranked = [...byRule].sort(\n (left, right) =>\n Number(right[1].some(({ finding }) => ageOf(finding) === 'new')) -\n Number(left[1].some(({ finding }) => ageOf(finding) === 'new')),\n );\n\n const groups = ranked.map(([rule, entries]) => {\n // Same finding, same component, several subjects: one edit, so one line\n // with the subjects counted rather than one line per place it shows up.\n // The age joins the key for the same reason it is drawn on the row: one\n // rule firing on an element the baseline had and on one it did not is two\n // findings, and folding them files the new one under the old one's date.\n const places = new Map<string, { what: string; age: Age; file?: string; subjects: string[] }>();\n for (const { subject, finding } of entries) {\n const age = ageOf(finding);\n const key = `${finding.component ?? finding.path}\\u0000${finding.what}\\u0000${age}`;\n const place = places.get(key) ?? {\n what: finding.what,\n age,\n ...(finding.file !== undefined ? { file: finding.file } : {}),\n subjects: [],\n };\n place.subjects.push(subject);\n places.set(key, place);\n }\n\n const band = entries[0]?.finding.band;\n\n return [\n `${band === undefined ? '' : `${bandTitle(band)} · `}${rule} — ` +\n `${entries.length} occurrence(s)`,\n ...[...places.values()].map((place) => {\n const reach =\n place.subjects.length === 1\n ? place.subjects[0]\n : `${place.subjects.length} subjects: ${place.subjects.slice(0, 3).join(', ')}` +\n (place.subjects.length > 3 ? ', …' : '');\n\n return (\n ` ${place.what}${dated ? ` — ${AGE_WORDS[place.age]}` : ''}` +\n `\\n ${place.file ?? '(no source index)'} — in ${reach}`\n );\n }),\n ].join('\\n');\n });\n\n const subjects = new Set(all.map((entry) => entry.subject)).size;\n const found = all.map((entry) => entry.finding);\n const lead = [\n arrivalLine(found),\n carriedLine(found),\n `${all.length} occurrence(s) across ${subjects} subject(s), read without a baseline`,\n ]\n .filter((clause): clause is string => clause !== undefined)\n .join(' · ');\n\n return [lead, ...groups].join('\\n\\n');\n },\n};\n"]}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * The narrowing coordinate, as the one line a summary header gives it.
3
+ *
4
+ * Beside [`summary.ts`](./summary.ts) rather than inside it because it answers a
5
+ * different question from every other line in that header. The rest of the
6
+ * header reports what the run found; this reports what the run did not do and
7
+ * could have, which is a fact about the operator's options and not about the
8
+ * suite.
9
+ */
10
+ import type { RunReport } from '@variance-authority/report';
11
+ /**
12
+ * What this run narrowed by, in the header rather than in a section.
13
+ *
14
+ * Narrowing is an option and stays one — nothing here proposes that a run should
15
+ * have skipped anything. What it refuses is the state where an agent works
16
+ * against this suite for weeks without ever learning that an index is on disk,
17
+ * that the index knows the commit it stands at, and that the distance from there
18
+ * is a number. An option nobody is told about is an option nobody has.
19
+ *
20
+ * So the coordinate is printed and the command is spelled out, and both are
21
+ * omitted the moment there is nothing to offer: no index, or an index the tree
22
+ * has not moved from, and the line would be an invitation to spend a call
23
+ * learning that zero files changed.
24
+ */
25
+ export declare function narrowing(report: RunReport): readonly string[];
26
+ //# sourceMappingURL=narrowing.d.ts.map
@@ -0,0 +1,38 @@
1
+ /**
2
+ * The narrowing coordinate, as the one line a summary header gives it.
3
+ *
4
+ * Beside [`summary.ts`](./summary.ts) rather than inside it because it answers a
5
+ * different question from every other line in that header. The rest of the
6
+ * header reports what the run found; this reports what the run did not do and
7
+ * could have, which is a fact about the operator's options and not about the
8
+ * suite.
9
+ */
10
+ /**
11
+ * What this run narrowed by, in the header rather than in a section.
12
+ *
13
+ * Narrowing is an option and stays one — nothing here proposes that a run should
14
+ * have skipped anything. What it refuses is the state where an agent works
15
+ * against this suite for weeks without ever learning that an index is on disk,
16
+ * that the index knows the commit it stands at, and that the distance from there
17
+ * is a number. An option nobody is told about is an option nobody has.
18
+ *
19
+ * So the coordinate is printed and the command is spelled out, and both are
20
+ * omitted the moment there is nothing to offer: no index, or an index the tree
21
+ * has not moved from, and the line would be an invitation to spend a call
22
+ * learning that zero files changed.
23
+ */
24
+ export function narrowing(report) {
25
+ const state = report.narrowing;
26
+ if (state === undefined)
27
+ return [];
28
+ if (state.since !== undefined)
29
+ return [`narrowed from ${state.since}`];
30
+ if (state.index === undefined || state.index.changed === 0)
31
+ return [];
32
+ return [
33
+ `observed everything — the execution index stands at ${state.index.commit}, ` +
34
+ `${state.index.changed} file(s) differ from it; ` +
35
+ `\`variance run --since ${state.index.commit}\` observes only what those reach`,
36
+ ];
37
+ }
38
+ //# sourceMappingURL=narrowing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"narrowing.js","sourceRoot":"","sources":["../../src/tools/narrowing.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,SAAS,CAAC,MAAiB;IACzC,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC;IAC/B,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,CAAC;IACnC,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;QAAE,OAAO,CAAC,iBAAiB,KAAK,CAAC,KAAK,EAAE,CAAC,CAAC;IACvE,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,KAAK,CAAC,OAAO,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACtE,OAAO;QACL,uDAAuD,KAAK,CAAC,KAAK,CAAC,MAAM,IAAI;YAC3E,GAAG,KAAK,CAAC,KAAK,CAAC,OAAO,2BAA2B;YACjD,0BAA0B,KAAK,CAAC,KAAK,CAAC,MAAM,mCAAmC;KAClF,CAAC;AACJ,CAAC","sourcesContent":["/**\n * The narrowing coordinate, as the one line a summary header gives it.\n *\n * Beside [`summary.ts`](./summary.ts) rather than inside it because it answers a\n * different question from every other line in that header. The rest of the\n * header reports what the run found; this reports what the run did not do and\n * could have, which is a fact about the operator's options and not about the\n * suite.\n */\n\nimport type { RunReport } from '@variance-authority/report';\n\n/**\n * What this run narrowed by, in the header rather than in a section.\n *\n * Narrowing is an option and stays one — nothing here proposes that a run should\n * have skipped anything. What it refuses is the state where an agent works\n * against this suite for weeks without ever learning that an index is on disk,\n * that the index knows the commit it stands at, and that the distance from there\n * is a number. An option nobody is told about is an option nobody has.\n *\n * So the coordinate is printed and the command is spelled out, and both are\n * omitted the moment there is nothing to offer: no index, or an index the tree\n * has not moved from, and the line would be an invitation to spend a call\n * learning that zero files changed.\n */\nexport function narrowing(report: RunReport): readonly string[] {\n const state = report.narrowing;\n if (state === undefined) return [];\n if (state.since !== undefined) return [`narrowed from ${state.since}`];\n if (state.index === undefined || state.index.changed === 0) return [];\n return [\n `observed everything — the execution index stands at ${state.index.commit}, ` +\n `${state.index.changed} file(s) differ from it; ` +\n `\\`variance run --since ${state.index.commit}\\` observes only what those reach`,\n ];\n}\n"]}
@@ -0,0 +1,7 @@
1
+ import type { ObservabilitySubject } from '../observability-subject.js';
2
+ import { type Tool } from './tool.js';
3
+ /** Which independent instruments this MCP connection can actually answer from. */
4
+ export declare const observability: Tool<ObservabilitySubject>;
5
+ /** Join a test's addressed DOM owners to its runtime execution without calling either silence safe. */
6
+ export declare const testingSurface: Tool<ObservabilitySubject>;
7
+ //# sourceMappingURL=observability.d.ts.map
@@ -0,0 +1,199 @@
1
+ import { locateEyesTest, phasedAttention, targetsOf } from './attention.js';
2
+ import { NO_ARGS, stringArg } from './tool.js';
3
+ /** Which independent instruments this MCP connection can actually answer from. */
4
+ export const observability = {
5
+ name: 'variance_observability',
6
+ description: 'Inventory every observability domain supplied to this MCP connection, distinguishing unavailable evidence from a completed empty measurement.',
7
+ inputSchema: NO_ARGS,
8
+ run(subject) {
9
+ const presentation = subject.report?.observations.filter((observation) => observation.signals?.presentation !== undefined).length;
10
+ return [
11
+ 'Observability available to this MCP connection:',
12
+ line('visual/report', subject.report, subject.report === undefined
13
+ ? undefined
14
+ : `${subject.report.observations.length} observation(s)`),
15
+ line('durable presentation signals', presentation === undefined ? undefined : true, presentation === undefined ? undefined : `${presentation} durable signal(s)`),
16
+ line('presentation readings', subject.presentations, subject.presentations === undefined
17
+ ? undefined
18
+ : `${subject.presentations.length} full graph(s)`),
19
+ line('runtime journey', subject.execution, subject.execution === undefined
20
+ ? undefined
21
+ : `${subject.execution.tests.length} named test(s), ${subject.execution.modules.length} source module(s)`),
22
+ line('live journey/events', subject.vantage, subject.vantage === undefined ? undefined : `${subject.vantage.tests.length} reported test(s)`),
23
+ line('Eyes attention', subject.eyes, subject.eyes === undefined ? undefined : `${subject.eyes.tests.length} test journal(s)`),
24
+ line('scenario AAA', subject.scenarios, subject.scenarios === undefined ? undefined : `${subject.scenarios.length} retained execution(s)`),
25
+ '',
26
+ 'Unavailable means no producer supplied that domain; it is not an empty measurement.',
27
+ ].join('\n');
28
+ },
29
+ };
30
+ /** Join a test's addressed DOM owners to its runtime execution without calling either silence safe. */
31
+ export const testingSurface = {
32
+ name: 'variance_testing_surface',
33
+ description: 'Map one test’s Eyes-addressed components and source locations by AAA phase, then contrast them with its Sense runtime journey to identify replay candidates—not safe mocks.',
34
+ inputSchema: {
35
+ type: 'object',
36
+ properties: {
37
+ test: { type: 'string', description: 'An Eyes test id, exact title, or unambiguous title part.' },
38
+ },
39
+ required: ['test'],
40
+ additionalProperties: false,
41
+ },
42
+ run(subject, input) {
43
+ if (subject.eyes === undefined) {
44
+ return 'Eyes attention is unavailable to this MCP connection; no testing surface can be attributed.';
45
+ }
46
+ const asked = stringArg(input, 'test');
47
+ const found = locateEyesTest(subject.eyes, asked);
48
+ if (typeof found === 'string')
49
+ return found;
50
+ return surface(found, subject.execution);
51
+ },
52
+ };
53
+ function line(name, evidence, detail) {
54
+ return evidence === undefined ? ` unavailable — ${name}` : ` available — ${name}: ${detail}`;
55
+ }
56
+ function surface(test, execution) {
57
+ const phases = new Map();
58
+ const updates = new Map();
59
+ let unattributed = 0;
60
+ let targets = 0;
61
+ for (const { phase, entry } of phasedAttention(test.attention)) {
62
+ if (entry.kind === 'react-commit') {
63
+ const bucket = updates.get(phase) ?? { commits: 0, unavailable: 0, updaters: [] };
64
+ bucket.commits += 1;
65
+ if (entry.commit.updaters === undefined)
66
+ bucket.unavailable += 1;
67
+ else
68
+ bucket.updaters.push(...entry.commit.updaters);
69
+ updates.set(phase, bucket);
70
+ }
71
+ for (const target of targetsOf(entry)) {
72
+ targets += 1;
73
+ const bucket = phases.get(phase) ?? { owners: new Set(), files: new Set(), paths: [] };
74
+ phases.set(phase, bucket);
75
+ if (target.provenance.status === 'no-fiber') {
76
+ unattributed += 1;
77
+ continue;
78
+ }
79
+ addTarget(bucket, target);
80
+ }
81
+ }
82
+ const addressedFiles = new Set([...phases.values()].flatMap((phase) => [...phase.files]));
83
+ const executionLines = executionSurface(execution, test, addressedFiles);
84
+ return [
85
+ `${test.title} — ${test.file ?? 'file not supplied'} [${test.id}]`,
86
+ test.complete ? 'Eyes journal: complete.' : `Eyes journal: partial — ${test.because}`,
87
+ `${targets} target snapshot(s); ${unattributed} had no live React Fiber.`,
88
+ '',
89
+ ...phaseLines(phases),
90
+ '',
91
+ ...updateLines(updates, phases),
92
+ '',
93
+ ...executionLines,
94
+ '',
95
+ 'Reduction rule: an executed file with no addressed target attribution is a replay candidate only. ' +
96
+ 'Neither Eyes nor Sense establishes that it is safe to mock.',
97
+ ].join('\n');
98
+ }
99
+ function addTarget(bucket, target) {
100
+ if (target.provenance.status !== 'resolved')
101
+ return;
102
+ const provenance = target.provenance.provenance;
103
+ bucket.paths.push(provenance.owners);
104
+ for (const owner of provenance.owners)
105
+ bucket.owners.add(owner.name);
106
+ if (provenance.createdBy !== undefined)
107
+ bucket.owners.add(provenance.createdBy);
108
+ if (provenance.source !== undefined)
109
+ bucket.files.add(provenance.source.file);
110
+ }
111
+ function phaseLines(phases) {
112
+ if (phases.size === 0)
113
+ return ['Addressed surface: measured empty.'];
114
+ const order = ['unphased', 'arrange', 'act', 'assert'];
115
+ return order.flatMap((phase) => {
116
+ const found = phases.get(phase);
117
+ if (found === undefined)
118
+ return [];
119
+ return [
120
+ `${phase}:`,
121
+ ` components: ${values(found.owners)}`,
122
+ ` source: ${values(found.files)}`,
123
+ ];
124
+ });
125
+ }
126
+ function updateLines(updates, addressed) {
127
+ if (updates.size === 0) {
128
+ return ['React update initiators: unavailable; no commit evidence was recorded.'];
129
+ }
130
+ const lines = ['React update initiators:'];
131
+ const order = ['unphased', 'arrange', 'act', 'assert'];
132
+ for (const phase of order) {
133
+ const found = updates.get(phase);
134
+ if (found === undefined)
135
+ continue;
136
+ const paths = addressed.get(phase)?.paths ?? [];
137
+ const connected = found.updaters.filter((updater) => paths.some((path) => pathsOverlap(updater, path)));
138
+ const elsewhere = found.updaters.filter((updater) => !connected.includes(updater));
139
+ lines.push(` ${phase}: ${found.commits} commit(s)`);
140
+ if (found.unavailable > 0) {
141
+ lines.push(` unavailable in ${found.unavailable} commit(s)`);
142
+ }
143
+ if (found.updaters.length === 0 && found.unavailable < found.commits) {
144
+ lines.push(' measured empty');
145
+ }
146
+ if (connected.length > 0) {
147
+ lines.push(` inside addressed component paths: ${updaterNames(connected)}`);
148
+ }
149
+ if (elsewhere.length > 0) {
150
+ lines.push(` outside addressed component paths: ${updaterNames(elsewhere)}`);
151
+ }
152
+ }
153
+ return lines;
154
+ }
155
+ function pathsOverlap(updater, target) {
156
+ const updatePath = updater.path;
157
+ const length = Math.min(updatePath.length, target.length);
158
+ for (let offset = 1; offset <= length; offset += 1) {
159
+ const left = updatePath[updatePath.length - offset];
160
+ const right = target[target.length - offset];
161
+ if (left.name !== right.name || left.propsDigest !== right.propsDigest)
162
+ return false;
163
+ }
164
+ return length > 0;
165
+ }
166
+ function updaterNames(updaters) {
167
+ return updaters.map((updater) => updater.path.map((frame) => frame.name).join(' ← ')).join('; ');
168
+ }
169
+ function executionSurface(index, test, addressed) {
170
+ if (index === undefined) {
171
+ return ['Runtime journey: unavailable; no executed-versus-addressed comparison was made.'];
172
+ }
173
+ const testIndex = index.tests.findIndex((candidate) => candidate.id === test.id);
174
+ if (testIndex < 0) {
175
+ return [
176
+ `Runtime journey: supplied, but it contains no test with exact id ${test.id}.`,
177
+ 'No title or file join was guessed.',
178
+ ];
179
+ }
180
+ const files = index.modules.flatMap((module) => {
181
+ const distances = module.blocks.flatMap((block) => block.crossings.filter((crossing) => crossing.test === testIndex).map((crossing) => crossing.distance));
182
+ return distances.length === 0 ? [] : [{ file: module.file, distance: Math.min(...distances) }];
183
+ }).sort((left, right) => left.distance - right.distance || compare(left.file, right.file));
184
+ const candidates = files.filter(({ file }) => !addressed.has(file));
185
+ return [
186
+ 'Runtime phase attribution: unavailable; ExecutionIndex retains test crossings, not AAA intervals.',
187
+ `Runtime journey: ${files.length} source file(s) entered by exact test id.`,
188
+ ...(files.length === 0 ? [' measured empty'] : files.map(({ file, distance }) => ` depth ${distance} — ${file}`)),
189
+ `Executed with no addressed target attributed to the same file: ${candidates.length}.`,
190
+ ...candidates.map(({ file, distance }) => ` replay candidate at depth ${distance} — ${file}`),
191
+ ];
192
+ }
193
+ function values(found) {
194
+ return found.size === 0 ? 'none attributed by Eyes' : [...found].sort(compare).join(', ');
195
+ }
196
+ function compare(left, right) {
197
+ return left < right ? -1 : left > right ? 1 : 0;
198
+ }
199
+ //# sourceMappingURL=observability.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"observability.js","sourceRoot":"","sources":["../../src/tools/observability.ts"],"names":[],"mappings":"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","sourcesContent":["import type { EyesPhase, EyesTestAttention, TargetSnapshot } from '@variance-authority/eyes';\nimport type { ExecutionIndex } from '@variance-authority/sense/test-selection';\nimport type { ObservabilitySubject } from '../observability-subject.js';\nimport { locateEyesTest, phasedAttention, targetsOf } from './attention.js';\nimport { NO_ARGS, stringArg, type Tool } from './tool.js';\n\ntype Phase = EyesPhase | 'unphased';\ntype OwnerPath = Extract<TargetSnapshot['provenance'], { status: 'resolved' }>['provenance']['owners'];\ntype CommitAttention = Extract<EyesTestAttention['attention'][number], { kind: 'react-commit' }>;\ntype Updater = NonNullable<CommitAttention['commit']['updaters']>[number];\n\ninterface AddressedPhase {\n readonly owners: Set<string>;\n readonly files: Set<string>;\n readonly paths: OwnerPath[];\n}\n\ninterface UpdatedPhase {\n commits: number;\n unavailable: number;\n readonly updaters: Updater[];\n}\n\n/** Which independent instruments this MCP connection can actually answer from. */\nexport const observability: Tool<ObservabilitySubject> = {\n name: 'variance_observability',\n description:\n 'Inventory every observability domain supplied to this MCP connection, distinguishing unavailable evidence from a completed empty measurement.',\n inputSchema: NO_ARGS,\n run(subject) {\n const presentation = subject.report?.observations.filter(\n (observation) => observation.signals?.presentation !== undefined,\n ).length;\n return [\n 'Observability available to this MCP connection:',\n line('visual/report', subject.report, subject.report === undefined\n ? undefined\n : `${subject.report.observations.length} observation(s)`),\n line('durable presentation signals', presentation === undefined ? undefined : true,\n presentation === undefined ? undefined : `${presentation} durable signal(s)`),\n line('presentation readings', subject.presentations,\n subject.presentations === undefined\n ? undefined\n : `${subject.presentations.length} full graph(s)`),\n line('runtime journey', subject.execution,\n subject.execution === undefined\n ? undefined\n : `${subject.execution.tests.length} named test(s), ${subject.execution.modules.length} source module(s)`),\n line('live journey/events', subject.vantage,\n subject.vantage === undefined ? undefined : `${subject.vantage.tests.length} reported test(s)`),\n line('Eyes attention', subject.eyes,\n subject.eyes === undefined ? undefined : `${subject.eyes.tests.length} test journal(s)`),\n line('scenario AAA', subject.scenarios,\n subject.scenarios === undefined ? undefined : `${subject.scenarios.length} retained execution(s)`),\n '',\n 'Unavailable means no producer supplied that domain; it is not an empty measurement.',\n ].join('\\n');\n },\n};\n\n/** Join a test's addressed DOM owners to its runtime execution without calling either silence safe. */\nexport const testingSurface: Tool<ObservabilitySubject> = {\n name: 'variance_testing_surface',\n description:\n 'Map one test’s Eyes-addressed components and source locations by AAA phase, then contrast them with its Sense runtime journey to identify replay candidates—not safe mocks.',\n inputSchema: {\n type: 'object',\n properties: {\n test: { type: 'string', description: 'An Eyes test id, exact title, or unambiguous title part.' },\n },\n required: ['test'],\n additionalProperties: false,\n },\n run(subject, input) {\n if (subject.eyes === undefined) {\n return 'Eyes attention is unavailable to this MCP connection; no testing surface can be attributed.';\n }\n const asked = stringArg(input, 'test');\n const found = locateEyesTest(subject.eyes, asked);\n if (typeof found === 'string') return found;\n return surface(found, subject.execution);\n },\n};\n\nfunction line(name: string, evidence: unknown, detail: string | undefined): string {\n return evidence === undefined ? ` unavailable — ${name}` : ` available — ${name}: ${detail}`;\n}\n\nfunction surface(test: EyesTestAttention, execution: ExecutionIndex | undefined): string {\n const phases = new Map<Phase, AddressedPhase>();\n const updates = new Map<Phase, UpdatedPhase>();\n let unattributed = 0;\n let targets = 0;\n for (const { phase, entry } of phasedAttention(test.attention)) {\n if (entry.kind === 'react-commit') {\n const bucket = updates.get(phase) ?? { commits: 0, unavailable: 0, updaters: [] };\n bucket.commits += 1;\n if (entry.commit.updaters === undefined) bucket.unavailable += 1;\n else bucket.updaters.push(...entry.commit.updaters);\n updates.set(phase, bucket);\n }\n for (const target of targetsOf(entry)) {\n targets += 1;\n const bucket = phases.get(phase) ?? { owners: new Set(), files: new Set(), paths: [] };\n phases.set(phase, bucket);\n if (target.provenance.status === 'no-fiber') {\n unattributed += 1;\n continue;\n }\n addTarget(bucket, target);\n }\n }\n\n const addressedFiles = new Set([...phases.values()].flatMap((phase) => [...phase.files]));\n const executionLines = executionSurface(execution, test, addressedFiles);\n return [\n `${test.title} — ${test.file ?? 'file not supplied'} [${test.id}]`,\n test.complete ? 'Eyes journal: complete.' : `Eyes journal: partial — ${test.because}`,\n `${targets} target snapshot(s); ${unattributed} had no live React Fiber.`,\n '',\n ...phaseLines(phases),\n '',\n ...updateLines(updates, phases),\n '',\n ...executionLines,\n '',\n 'Reduction rule: an executed file with no addressed target attribution is a replay candidate only. ' +\n 'Neither Eyes nor Sense establishes that it is safe to mock.',\n ].join('\\n');\n}\n\nfunction addTarget(\n bucket: AddressedPhase,\n target: TargetSnapshot,\n): void {\n if (target.provenance.status !== 'resolved') return;\n const provenance = target.provenance.provenance;\n bucket.paths.push(provenance.owners);\n for (const owner of provenance.owners) bucket.owners.add(owner.name);\n if (provenance.createdBy !== undefined) bucket.owners.add(provenance.createdBy);\n if (provenance.source !== undefined) bucket.files.add(provenance.source.file);\n}\n\nfunction phaseLines(\n phases: ReadonlyMap<Phase, AddressedPhase>,\n): string[] {\n if (phases.size === 0) return ['Addressed surface: measured empty.'];\n const order = ['unphased', 'arrange', 'act', 'assert'] as const;\n return order.flatMap((phase) => {\n const found = phases.get(phase);\n if (found === undefined) return [];\n return [\n `${phase}:`,\n ` components: ${values(found.owners)}`,\n ` source: ${values(found.files)}`,\n ];\n });\n}\n\nfunction updateLines(\n updates: ReadonlyMap<Phase, UpdatedPhase>,\n addressed: ReadonlyMap<Phase, AddressedPhase>,\n): string[] {\n if (updates.size === 0) {\n return ['React update initiators: unavailable; no commit evidence was recorded.'];\n }\n const lines = ['React update initiators:'];\n const order = ['unphased', 'arrange', 'act', 'assert'] as const;\n for (const phase of order) {\n const found = updates.get(phase);\n if (found === undefined) continue;\n const paths = addressed.get(phase)?.paths ?? [];\n const connected = found.updaters.filter((updater) =>\n paths.some((path) => pathsOverlap(updater, path)));\n const elsewhere = found.updaters.filter((updater) => !connected.includes(updater));\n lines.push(` ${phase}: ${found.commits} commit(s)`);\n if (found.unavailable > 0) {\n lines.push(` unavailable in ${found.unavailable} commit(s)`);\n }\n if (found.updaters.length === 0 && found.unavailable < found.commits) {\n lines.push(' measured empty');\n }\n if (connected.length > 0) {\n lines.push(` inside addressed component paths: ${updaterNames(connected)}`);\n }\n if (elsewhere.length > 0) {\n lines.push(` outside addressed component paths: ${updaterNames(elsewhere)}`);\n }\n }\n return lines;\n}\n\nfunction pathsOverlap(updater: Updater, target: OwnerPath): boolean {\n const updatePath = updater.path;\n const length = Math.min(updatePath.length, target.length);\n for (let offset = 1; offset <= length; offset += 1) {\n const left = updatePath[updatePath.length - offset]!;\n const right = target[target.length - offset]!;\n if (left.name !== right.name || left.propsDigest !== right.propsDigest) return false;\n }\n return length > 0;\n}\n\nfunction updaterNames(updaters: readonly Updater[]): string {\n return updaters.map((updater) =>\n updater.path.map((frame) => frame.name).join(' ← ')).join('; ');\n}\n\nfunction executionSurface(\n index: ExecutionIndex | undefined,\n test: EyesTestAttention,\n addressed: ReadonlySet<string>,\n): string[] {\n if (index === undefined) {\n return ['Runtime journey: unavailable; no executed-versus-addressed comparison was made.'];\n }\n const testIndex = index.tests.findIndex((candidate) => candidate.id === test.id);\n if (testIndex < 0) {\n return [\n `Runtime journey: supplied, but it contains no test with exact id ${test.id}.`,\n 'No title or file join was guessed.',\n ];\n }\n const files = index.modules.flatMap((module) => {\n const distances = module.blocks.flatMap((block) =>\n block.crossings.filter((crossing) => crossing.test === testIndex).map((crossing) => crossing.distance));\n return distances.length === 0 ? [] : [{ file: module.file, distance: Math.min(...distances) }];\n }).sort((left, right) => left.distance - right.distance || compare(left.file, right.file));\n const candidates = files.filter(({ file }) => !addressed.has(file));\n return [\n 'Runtime phase attribution: unavailable; ExecutionIndex retains test crossings, not AAA intervals.',\n `Runtime journey: ${files.length} source file(s) entered by exact test id.`,\n ...(files.length === 0 ? [' measured empty'] : files.map(({ file, distance }) => ` depth ${distance} — ${file}`)),\n `Executed with no addressed target attributed to the same file: ${candidates.length}.`,\n ...candidates.map(({ file, distance }) => ` replay candidate at depth ${distance} — ${file}`),\n ];\n}\n\nfunction values(found: ReadonlySet<string>): string {\n return found.size === 0 ? 'none attributed by Eyes' : [...found].sort(compare).join(', ');\n}\n\nfunction compare(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0;\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import type { PresentationReport } from '@variance-authority/presentation';
2
+ import { type Tool } from './tool.js';
3
+ export type PresentationEvidence = readonly PresentationReport[];
4
+ /** Full, process-owned presentation graphs supplied explicitly to the connection. */
5
+ export declare const presentations: Tool<PresentationEvidence>;
6
+ //# sourceMappingURL=presentations.d.ts.map
@@ -0,0 +1,74 @@
1
+ import { stringArg } from './tool.js';
2
+ /** Full, process-owned presentation graphs supplied explicitly to the connection. */
3
+ export const presentations = {
4
+ name: 'variance_presentations',
5
+ description: 'List supplied presentation readings, or describe one graph, its telemetry, semantic evidence, measured structures, and findings without reacquiring the page.',
6
+ inputSchema: {
7
+ type: 'object',
8
+ properties: {
9
+ subject: { type: 'string', description: 'A subject id, exact title, or unambiguous title part.' },
10
+ },
11
+ additionalProperties: false,
12
+ },
13
+ run(reports, input) {
14
+ if (input['subject'] === undefined)
15
+ return listing(reports);
16
+ const found = locate(reports, stringArg(input, 'subject'));
17
+ if (typeof found === 'string')
18
+ return found;
19
+ return describe(found);
20
+ },
21
+ };
22
+ function listing(reports) {
23
+ if (reports.length === 0)
24
+ return 'Presentation measured a supplied collection containing no readings.';
25
+ return [
26
+ `${reports.length} presentation reading(s):`,
27
+ ...reports.map((report) => ` ${report.subject.title ?? report.subject.id} [${report.subject.id}] — ` +
28
+ `${report.graph.nodes.length} node(s), ${report.graph.relations.length} relation(s)`),
29
+ ].join('\n');
30
+ }
31
+ function locate(reports, asked) {
32
+ const byId = reports.find((report) => report.subject.id === asked);
33
+ if (byId !== undefined)
34
+ return byId;
35
+ const exact = reports.filter((report) => report.subject.title === asked);
36
+ if (exact.length === 1)
37
+ return exact[0];
38
+ const lowered = asked.toLowerCase();
39
+ const partial = reports.filter((report) => report.subject.title?.toLowerCase().includes(lowered) === true);
40
+ if (partial.length === 1)
41
+ return partial[0];
42
+ if (partial.length === 0)
43
+ return `No presentation subject matches ${asked}.`;
44
+ return `${partial.length} presentation subjects match ${asked}; ask by id:\n${partial.map((report) => ` ${report.subject.title ?? report.subject.id} [${report.subject.id}]`).join('\n')}`;
45
+ }
46
+ function describe(report) {
47
+ const content = report.telemetry.content;
48
+ const findings = report.findings;
49
+ return [
50
+ `${report.subject.title ?? report.subject.id} [${report.subject.id}]`,
51
+ `Presentation ${report.digest}; content ${report.contentDigest}.`,
52
+ `Graph: ${report.graph.nodes.length} node(s), ${report.graph.relations.length} relation(s).`,
53
+ `Content: ${content.elements} element(s), ${content.characters} character(s), ` +
54
+ `${content.controls} control(s), ${content.repeatedObjects} repeated object(s).`,
55
+ report.semantic.browserAccessibility === undefined
56
+ ? 'Browser accessibility: unavailable.'
57
+ : `Browser accessibility: ${report.semantic.browserAccessibility.roots.length} root(s).`,
58
+ measured('spacing clusters', report.spacing),
59
+ measured('alignment axes', report.axes),
60
+ measured('baseline clusters', report.baselines),
61
+ measured('prominence clusters', report.prominence),
62
+ measured('surface groups', report.surfaces),
63
+ measured('repeated patterns', report.patterns),
64
+ findings === undefined
65
+ ? 'Findings: unavailable because this reading has no layout evidence.'
66
+ : findings.length === 0
67
+ ? 'Findings: measured empty.'
68
+ : `Findings: ${findings.length}.\n${findings.map((finding) => ` ${finding.rule} — owner ${finding.owner} — ${finding.nodes.join(', ')}`).join('\n')}`,
69
+ ].join('\n');
70
+ }
71
+ function measured(name, values) {
72
+ return values === undefined ? `${name}: unavailable.` : `${name}: measured ${values.length}.`;
73
+ }
74
+ //# sourceMappingURL=presentations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presentations.js","sourceRoot":"","sources":["../../src/tools/presentations.ts"],"names":[],"mappings":"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","sourcesContent":["import type { PresentationReport } from '@variance-authority/presentation';\nimport { stringArg, type Tool } from './tool.js';\n\nexport type PresentationEvidence = readonly PresentationReport[];\n\n/** Full, process-owned presentation graphs supplied explicitly to the connection. */\nexport const presentations: Tool<PresentationEvidence> = {\n name: 'variance_presentations',\n description:\n 'List supplied presentation readings, or describe one graph, its telemetry, semantic evidence, measured structures, and findings without reacquiring the page.',\n inputSchema: {\n type: 'object',\n properties: {\n subject: { type: 'string', description: 'A subject id, exact title, or unambiguous title part.' },\n },\n additionalProperties: false,\n },\n run(reports, input) {\n if (input['subject'] === undefined) return listing(reports);\n const found = locate(reports, stringArg(input, 'subject'));\n if (typeof found === 'string') return found;\n return describe(found);\n },\n};\n\nfunction listing(reports: PresentationEvidence): string {\n if (reports.length === 0) return 'Presentation measured a supplied collection containing no readings.';\n return [\n `${reports.length} presentation reading(s):`,\n ...reports.map((report) =>\n ` ${report.subject.title ?? report.subject.id} [${report.subject.id}] — ` +\n `${report.graph.nodes.length} node(s), ${report.graph.relations.length} relation(s)`),\n ].join('\\n');\n}\n\nfunction locate(reports: PresentationEvidence, asked: string): PresentationReport | string {\n const byId = reports.find((report) => report.subject.id === asked);\n if (byId !== undefined) return byId;\n const exact = reports.filter((report) => report.subject.title === asked);\n if (exact.length === 1) return exact[0]!;\n const lowered = asked.toLowerCase();\n const partial = reports.filter((report) =>\n report.subject.title?.toLowerCase().includes(lowered) === true);\n if (partial.length === 1) return partial[0]!;\n if (partial.length === 0) return `No presentation subject matches ${asked}.`;\n return `${partial.length} presentation subjects match ${asked}; ask by id:\\n${partial.map((report) =>\n ` ${report.subject.title ?? report.subject.id} [${report.subject.id}]`).join('\\n')}`;\n}\n\nfunction describe(report: PresentationReport): string {\n const content = report.telemetry.content;\n const findings = report.findings;\n return [\n `${report.subject.title ?? report.subject.id} [${report.subject.id}]`,\n `Presentation ${report.digest}; content ${report.contentDigest}.`,\n `Graph: ${report.graph.nodes.length} node(s), ${report.graph.relations.length} relation(s).`,\n `Content: ${content.elements} element(s), ${content.characters} character(s), ` +\n `${content.controls} control(s), ${content.repeatedObjects} repeated object(s).`,\n report.semantic.browserAccessibility === undefined\n ? 'Browser accessibility: unavailable.'\n : `Browser accessibility: ${report.semantic.browserAccessibility.roots.length} root(s).`,\n measured('spacing clusters', report.spacing),\n measured('alignment axes', report.axes),\n measured('baseline clusters', report.baselines),\n measured('prominence clusters', report.prominence),\n measured('surface groups', report.surfaces),\n measured('repeated patterns', report.patterns),\n findings === undefined\n ? 'Findings: unavailable because this reading has no layout evidence.'\n : findings.length === 0\n ? 'Findings: measured empty.'\n : `Findings: ${findings.length}.\\n${findings.map((finding) =>\n ` ${finding.rule} — owner ${finding.owner} — ${finding.nodes.join(', ')}`).join('\\n')}`,\n ].join('\\n');\n}\n\nfunction measured(name: string, values: readonly unknown[] | undefined): string {\n return values === undefined ? `${name}: unavailable.` : `${name}: measured ${values.length}.`;\n}\n"]}
@@ -0,0 +1,12 @@
1
+ import type { VantageState } from '@variance-authority/vantage';
2
+ import type { Tool } from './tool.js';
3
+ /**
4
+ * What the suite is doing, right now.
5
+ *
6
+ * The listing an agent reads before it asks about one test. Ordered as the run
7
+ * opened them, because the useful reading of a suite in flight is chronological
8
+ * — the test that is hanging is the last one with an arrow next to it — and
9
+ * newest-first would put the answer at the top and the story in reverse.
10
+ */
11
+ export declare const runSignals: Tool<VantageState>;
12
+ //# sourceMappingURL=run-signals.d.ts.map