@descryy/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 (118) hide show
  1. package/LICENSE +21 -0
  2. package/dist/action-handshake.d.ts +86 -0
  3. package/dist/action-handshake.d.ts.map +1 -0
  4. package/dist/action-handshake.js +144 -0
  5. package/dist/action-handshake.js.map +1 -0
  6. package/dist/bin/descry-mcp.d.ts +18 -0
  7. package/dist/bin/descry-mcp.d.ts.map +1 -0
  8. package/dist/bin/descry-mcp.js +54 -0
  9. package/dist/bin/descry-mcp.js.map +1 -0
  10. package/dist/index.d.ts +32 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +15 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/language-guess.d.ts +17 -0
  15. package/dist/language-guess.d.ts.map +1 -0
  16. package/dist/language-guess.js +43 -0
  17. package/dist/language-guess.js.map +1 -0
  18. package/dist/protocol.d.ts +91 -0
  19. package/dist/protocol.d.ts.map +1 -0
  20. package/dist/protocol.js +127 -0
  21. package/dist/protocol.js.map +1 -0
  22. package/dist/registry.d.ts +72 -0
  23. package/dist/registry.d.ts.map +1 -0
  24. package/dist/registry.js +136 -0
  25. package/dist/registry.js.map +1 -0
  26. package/dist/render.d.ts +275 -0
  27. package/dist/render.d.ts.map +1 -0
  28. package/dist/render.js +333 -0
  29. package/dist/render.js.map +1 -0
  30. package/dist/server.d.ts +42 -0
  31. package/dist/server.d.ts.map +1 -0
  32. package/dist/server.js +263 -0
  33. package/dist/server.js.map +1 -0
  34. package/dist/session.d.ts +164 -0
  35. package/dist/session.d.ts.map +1 -0
  36. package/dist/session.js +302 -0
  37. package/dist/session.js.map +1 -0
  38. package/dist/tools/analyze.d.ts +124 -0
  39. package/dist/tools/analyze.d.ts.map +1 -0
  40. package/dist/tools/analyze.js +411 -0
  41. package/dist/tools/analyze.js.map +1 -0
  42. package/dist/tools/contracts.d.ts +105 -0
  43. package/dist/tools/contracts.d.ts.map +1 -0
  44. package/dist/tools/contracts.js +141 -0
  45. package/dist/tools/contracts.js.map +1 -0
  46. package/dist/tools/cross-pr.d.ts +137 -0
  47. package/dist/tools/cross-pr.d.ts.map +1 -0
  48. package/dist/tools/cross-pr.js +216 -0
  49. package/dist/tools/cross-pr.js.map +1 -0
  50. package/dist/tools/git-diff.d.ts +29 -0
  51. package/dist/tools/git-diff.d.ts.map +1 -0
  52. package/dist/tools/git-diff.js +117 -0
  53. package/dist/tools/git-diff.js.map +1 -0
  54. package/dist/tools/git-history.d.ts +27 -0
  55. package/dist/tools/git-history.d.ts.map +1 -0
  56. package/dist/tools/git-history.js +91 -0
  57. package/dist/tools/git-history.js.map +1 -0
  58. package/dist/tools/history.d.ts +63 -0
  59. package/dist/tools/history.d.ts.map +1 -0
  60. package/dist/tools/history.js +114 -0
  61. package/dist/tools/history.js.map +1 -0
  62. package/dist/tools/impact.d.ts +81 -0
  63. package/dist/tools/impact.d.ts.map +1 -0
  64. package/dist/tools/impact.js +229 -0
  65. package/dist/tools/impact.js.map +1 -0
  66. package/dist/tools/index.d.ts +33 -0
  67. package/dist/tools/index.d.ts.map +1 -0
  68. package/dist/tools/index.js +47 -0
  69. package/dist/tools/index.js.map +1 -0
  70. package/dist/tools/kit.d.ts +183 -0
  71. package/dist/tools/kit.d.ts.map +1 -0
  72. package/dist/tools/kit.js +117 -0
  73. package/dist/tools/kit.js.map +1 -0
  74. package/dist/tools/link-workspace.d.ts +62 -0
  75. package/dist/tools/link-workspace.d.ts.map +1 -0
  76. package/dist/tools/link-workspace.js +156 -0
  77. package/dist/tools/link-workspace.js.map +1 -0
  78. package/dist/tools/lookup.d.ts +29 -0
  79. package/dist/tools/lookup.d.ts.map +1 -0
  80. package/dist/tools/lookup.js +73 -0
  81. package/dist/tools/lookup.js.map +1 -0
  82. package/dist/tools/pr-analysis.d.ts +89 -0
  83. package/dist/tools/pr-analysis.d.ts.map +1 -0
  84. package/dist/tools/pr-analysis.js +169 -0
  85. package/dist/tools/pr-analysis.js.map +1 -0
  86. package/dist/tools/propagation.d.ts +74 -0
  87. package/dist/tools/propagation.d.ts.map +1 -0
  88. package/dist/tools/propagation.js +194 -0
  89. package/dist/tools/propagation.js.map +1 -0
  90. package/dist/tools/questions.d.ts +80 -0
  91. package/dist/tools/questions.d.ts.map +1 -0
  92. package/dist/tools/questions.js +213 -0
  93. package/dist/tools/questions.js.map +1 -0
  94. package/dist/tools/refusal-fetch.d.ts +71 -0
  95. package/dist/tools/refusal-fetch.d.ts.map +1 -0
  96. package/dist/tools/refusal-fetch.js +137 -0
  97. package/dist/tools/refusal-fetch.js.map +1 -0
  98. package/dist/tools/scope.d.ts +60 -0
  99. package/dist/tools/scope.d.ts.map +1 -0
  100. package/dist/tools/scope.js +160 -0
  101. package/dist/tools/scope.js.map +1 -0
  102. package/dist/tools/similar-incidents.d.ts +105 -0
  103. package/dist/tools/similar-incidents.d.ts.map +1 -0
  104. package/dist/tools/similar-incidents.js +228 -0
  105. package/dist/tools/similar-incidents.js.map +1 -0
  106. package/dist/tools/validate.d.ts +103 -0
  107. package/dist/tools/validate.d.ts.map +1 -0
  108. package/dist/tools/validate.js +176 -0
  109. package/dist/tools/validate.js.map +1 -0
  110. package/dist/tools/verification-status.d.ts +68 -0
  111. package/dist/tools/verification-status.d.ts.map +1 -0
  112. package/dist/tools/verification-status.js +179 -0
  113. package/dist/tools/verification-status.js.map +1 -0
  114. package/dist/transport.d.ts +58 -0
  115. package/dist/transport.d.ts.map +1 -0
  116. package/dist/transport.js +115 -0
  117. package/dist/transport.js.map +1 -0
  118. package/package.json +33 -0
@@ -0,0 +1,411 @@
1
+ /**
2
+ * `analyze` — read the repository into the graph.
3
+ *
4
+ * The only tool that writes. Everything else queries what this produced, so its
5
+ * disclosures are the ones that matter most: a query cannot report a gap it was
6
+ * never told about, and this is where the gaps become known.
7
+ *
8
+ * ## What is disclosed, and why each one earns its line
9
+ *
10
+ * - **Sources that would not load.** "No problem found in your Python service"
11
+ * and "your Python adapter did not import" are different statements (§20.2).
12
+ * - **Sources that loaded and did not claim the repository.** The same
13
+ * distinction, one level up: an unanalysed language is *not analysable*, not
14
+ * clean.
15
+ * - **The resolution each source reached.** A run that degraded to R0 because a
16
+ * virtualenv was missing produces a graph that looks identical to a healthy
17
+ * one until you read this number. Principle 3 then caps everything above it.
18
+ * - **Identity collisions.** Two things arrived under one id. There is no
19
+ * correct merge, so the builder reports rather than picks (DEC-024).
20
+ * - **Evicted files.** Rows a previous run owned and this one did not re-supply.
21
+ * They are gone from the graph; a query over them will now find nothing, and
22
+ * nothing would otherwise say why.
23
+ * - **The workspace join.** Zero cross-repo joins looks exactly like success.
24
+ *
25
+ * ## The repo identity warning
26
+ *
27
+ * With no `.descry/config.json`, `repo` defaults to the directory name — and
28
+ * every repo-scoped node id is a hash over it. Clone into a differently-named
29
+ * folder and every id changes, which silently destroys incident links and
30
+ * `CHANGES_WITH` weights. That is DEC-004's failure arriving through the one
31
+ * field a default cannot get right, so it is stated on every unconfigured run.
32
+ */
33
+ import { applyConfirmedFacts, buildGraph, counts, createGitSource, mintEdgesFromConfirmedFacts, MINT_PRODUCED_BY, persistGraph, summariseRefusals, } from "@descryy/core";
34
+ import { answer, ToolInputError } from "./kit.js";
35
+ import { detectSources, loadSources } from "../registry.js";
36
+ const SCHEMA = {
37
+ type: "object",
38
+ properties: {
39
+ includeHistory: {
40
+ type: "boolean",
41
+ description: "Read git history for co-change coupling (CHANGES_WITH edges). Default true. " +
42
+ "Costs a few seconds on a large repository.",
43
+ },
44
+ maxCommits: {
45
+ type: "integer",
46
+ minimum: 1,
47
+ description: "Cap on commits read from history. Default 2000.",
48
+ },
49
+ },
50
+ additionalProperties: false,
51
+ };
52
+ /**
53
+ * §5.4's `refusals` field, for this run only — never the graph's whole
54
+ * stored ledger (`refusal_fetch`'s job). `built.unresolved` is what the
55
+ * sources this call just read could not resolve, so it is a fresh
56
+ * population every run rather than accumulated history, and honestly
57
+ * scoped to "what this call found" the way the field's own contract asks.
58
+ *
59
+ * `undefined` on zero rows — the envelope's `refusals` stays `null` rather
60
+ * than carrying a summary with no exemplar or handle to show (matching
61
+ * `summariseRefusals`'s own `count === 0` shape one level up).
62
+ */
63
+ export function refusalSummaryField(unresolved) {
64
+ const summary = summariseRefusals(unresolved);
65
+ if (summary.count === 0 || summary.exemplar === null || summary.handle === null)
66
+ return undefined;
67
+ return { count: summary.count, exemplar: summary.exemplar, handle: summary.handle };
68
+ }
69
+ async function run(args, ctx) {
70
+ const { session } = ctx;
71
+ const includeHistory = args["includeHistory"] !== false;
72
+ const maxCommits = args["maxCommits"];
73
+ if (maxCommits !== undefined && (typeof maxCommits !== "number" || !Number.isInteger(maxCommits) || maxCommits < 1)) {
74
+ throw new ToolInputError('"maxCommits" must be a positive integer');
75
+ }
76
+ const root = await session.root();
77
+ const notes = [];
78
+ // MK-4: analyze is about to rebuild the graph from source regardless, so a
79
+ // stale IR schema version is repaired here rather than left to refuse —
80
+ // see Session.repairStaleGraph's own doc for why only this tool does this.
81
+ const { rebuilt } = session.repairStaleGraph();
82
+ if (rebuilt) {
83
+ notes.push("The stored graph was written under an older IR schema version. Its derived tables were " +
84
+ "dropped and are rebuilt by this run; earned data (R4 observations, edge corrections, " +
85
+ "incident links) was not affected.");
86
+ }
87
+ if (session.config.configPath === null) {
88
+ notes.push(`No ${".descry/config.json"} was found, so the repository identity defaults to the ` +
89
+ `directory name ("${session.config.repo}"). Every repo-scoped node id is a hash over that ` +
90
+ "string: cloning into a differently-named folder produces a different graph, and incident " +
91
+ "links and co-change weights do not survive the change. Set \"repo\" explicitly.");
92
+ }
93
+ if (session.config.workspace === undefined) {
94
+ notes.push("No workspace is configured, so this repository is its own contract boundary. Endpoints, " +
95
+ "tables and incidents will not join with any other repository's (DEC-054). If there is a " +
96
+ 'sibling repository this one should join — a frontend to this backend, for example — ask ' +
97
+ 'whether they should share a workspace, and call "link_workspace" once confirmed. Never ' +
98
+ "call it on a guess: a directory sitting next to another is a suggestion, not evidence.");
99
+ }
100
+ // --- sources -------------------------------------------------------------
101
+ const registry = await loadSources(session.config.sources);
102
+ for (const failure of registry.failed) {
103
+ notes.push(`The source "${failure.spec.module}" could not be loaded (${failure.reason}). Anything it ` +
104
+ "would have read is not analysable — not clean.");
105
+ }
106
+ if (session.config.sources.length === 0) {
107
+ notes.push(
108
+ // No example package name here, deliberately, and the boundary lint
109
+ // enforces it. Naming one would make this engine know about a specific
110
+ // language — the exact inversion `registry.ts` exists to prevent — and the
111
+ // list of installed adapters is not something this process can see.
112
+ "No language adapters are configured, so nothing was read from source. Add a \"sources\" " +
113
+ `array to ${".descry/config.json"} listing the adapter packages to load, as module ` +
114
+ "specifiers. This engine does not know the name of any adapter: the IR boundary is a " +
115
+ "dependency direction, so the list has to come from configuration.");
116
+ }
117
+ const { applicable, skipped } = await detectSources(registry.loaded, root);
118
+ for (const entry of skipped) {
119
+ notes.push(`${entry.source.source.id} did not run: it ${entry.reason}. Any code only that adapter ` +
120
+ "could read is not analysable.");
121
+ }
122
+ // --- emit ----------------------------------------------------------------
123
+ const context = {
124
+ root,
125
+ prepareTimeoutMs: 300_000,
126
+ signal: ctx.signal,
127
+ };
128
+ const batches = [];
129
+ const perSource = [];
130
+ /**
131
+ * Resolution levels reached by sources that read **source code**.
132
+ *
133
+ * The floor is drawn from these alone, and the git-history reader is why. It
134
+ * reaches R0 and always will: R0 is what reading a commit log *is*, not a
135
+ * degradation of it. Folding it into a single graph-wide minimum pinned every
136
+ * analyse reply at R0 and therefore at reliability class C — which then
137
+ * described a set of directly counted rows as "a behavioural prediction".
138
+ *
139
+ * DEC-058 already settled the general form of this: an edge carries the
140
+ * resolution of *its own evidence*, not of the run, so a graph has no single
141
+ * floor and each answer computes its own. This is that rule applied one level
142
+ * up. Every source's level is still reported individually in `sources`, and a
143
+ * source that fell short of its own declared ceiling is disclosed separately.
144
+ */
145
+ const codeResolutions = [];
146
+ const total = applicable.length + (includeHistory ? 1 : 0);
147
+ let done = 0;
148
+ for (const entry of applicable) {
149
+ ctx.progress(`Reading ${entry.source.id}`, done, total);
150
+ const started = Date.now();
151
+ try {
152
+ const batch = await entry.source.emit(context);
153
+ batches.push(batch);
154
+ perSource.push({
155
+ source: batch.producedBy,
156
+ files: batch.sourceFiles.length,
157
+ nodes: batch.nodes.length,
158
+ edges: batch.edges.length,
159
+ unresolved: batch.unresolved.length,
160
+ skippedFiles: batch.skippedFiles.length,
161
+ reachedResolution: batch.reachedResolution,
162
+ ms: Date.now() - started,
163
+ });
164
+ // The declared ceiling versus what this run actually reached. A gap is the
165
+ // honest-degradation case: the adapter can do better and something in this
166
+ // repository stopped it.
167
+ const ceiling = entry.adapter?.capabilities().maxResolution;
168
+ if (ceiling !== undefined)
169
+ codeResolutions.push(batch.reachedResolution);
170
+ if (ceiling !== undefined && batch.reachedResolution < ceiling) {
171
+ notes.push(`${entry.source.id} reached R${batch.reachedResolution} where it is capable of ` +
172
+ `R${ceiling}. Findings resting on its edges are capped accordingly, and shape-level ` +
173
+ "checks (contract matching, field propagation) need R3.");
174
+ }
175
+ if (batch.skippedFiles.length > 0) {
176
+ const byReason = new Map();
177
+ for (const skipped of batch.skippedFiles) {
178
+ byReason.set(skipped.reason, (byReason.get(skipped.reason) ?? 0) + 1);
179
+ }
180
+ const summary = [...byReason.entries()].map(([reason, count]) => `${count} ${reason}`).join(", ");
181
+ notes.push(`${entry.source.id} could not analyse ${batch.skippedFiles.length} file(s) it attempted ` +
182
+ `(${summary}). Not analysable — absent from the graph, not confirmed clean.`);
183
+ }
184
+ }
185
+ catch (error) {
186
+ notes.push(`${entry.source.id} failed while reading this repository (${error.message}). ` +
187
+ "Everything it covers is not analysable.");
188
+ }
189
+ finally {
190
+ // `dispose` is a `LanguageAdapter` obligation, not an `IRSource` one — a
191
+ // language server is a long-lived child process and leaking one costs
192
+ // hundreds of megabytes per repo. Sources with no processes to release
193
+ // (the git reader) have no method and need none.
194
+ await entry.adapter?.dispose();
195
+ done += 1;
196
+ }
197
+ }
198
+ if (includeHistory) {
199
+ ctx.progress("Reading git history", done, total);
200
+ const historyStarted = Date.now();
201
+ try {
202
+ const history = await createGitSource({
203
+ repo: root.repo,
204
+ cwd: session.repoPath,
205
+ ...(typeof maxCommits === "number" ? { maxCommits } : {}),
206
+ }).emit(context);
207
+ batches.push(history);
208
+ perSource.push({
209
+ source: history.producedBy,
210
+ files: history.sourceFiles.length,
211
+ nodes: history.nodes.length,
212
+ edges: history.edges.length,
213
+ unresolved: history.unresolved.length,
214
+ skippedFiles: history.skippedFiles.length,
215
+ reachedResolution: history.reachedResolution,
216
+ ms: Date.now() - historyStarted,
217
+ });
218
+ }
219
+ catch (error) {
220
+ notes.push(`Git history was not read (${error.message}), so co-change coupling is absent ` +
221
+ "and blast radii will not include historically coupled files.");
222
+ }
223
+ done += 1;
224
+ }
225
+ else {
226
+ notes.push("Git history was skipped by request, so no CHANGES_WITH edges were produced and the blast " +
227
+ "radius will miss files that historically change together.");
228
+ }
229
+ // --- build and store -----------------------------------------------------
230
+ ctx.progress("Merging into the graph", done, total);
231
+ const built = buildGraph(batches, {
232
+ nodeExists: (id) => session.provider().node(id) !== undefined,
233
+ });
234
+ const persisted = persistGraph(session.store().driver, batches, built);
235
+ for (const collision of built.collisions) {
236
+ notes.push(`Two different things claimed node id ${collision.nodeId}: a ${collision.kept.type} from ` +
237
+ `${collision.kept.producedBy} and a ${collision.discarded.type} from ` +
238
+ `${collision.discarded.producedBy}. They are not the same symbol and there is no correct ` +
239
+ "merge, so the second was discarded (DEC-024).");
240
+ }
241
+ if (built.droppedEdges > 0) {
242
+ notes.push(`${built.droppedEdges} edge(s) were dropped because an endpoint did not resolve to a real ` +
243
+ "node. They are never stubbed into existence — a name-matched guess belongs in the " +
244
+ "unresolved-reference ledger, not in the graph.");
245
+ }
246
+ if (built.rejections.length > 0) {
247
+ notes.push(`${built.rejections.length} item(s) were turned away at the IR boundary as malformed. They ` +
248
+ "are not in the graph and nothing above will see them.");
249
+ }
250
+ if (persisted.evictedFiles.length > 0) {
251
+ notes.push(`${persisted.evictedFiles.length} file(s) covered by a previous run were not re-supplied by ` +
252
+ "this one; their rows have been removed. A query about them will now find nothing because " +
253
+ "nothing is stored, not because there is nothing there.");
254
+ }
255
+ for (const warning of built.warnings)
256
+ notes.push(warning.detail);
257
+ if (built.unresolved.length > 0) {
258
+ notes.push(`${built.unresolved.length} reference(s) could not be resolved to a target and are recorded ` +
259
+ "in the ledger rather than guessed at. They are the measured recall gap, not an error.");
260
+ }
261
+ // --- mint edges from confirmed facts --------------------------------------
262
+ //
263
+ // DEC-223's answer half, minting side (DEC-138) — sequenced after the base
264
+ // graph above is already persisted, because a confirmed answer joins
265
+ // against a real, stored `API_ENDPOINT` row: `EndpointLookup.node(id)` is
266
+ // backed by the SQL reader, not by `built`'s in-memory result, so the
267
+ // endpoint has to already exist as a row for the lookup to find it.
268
+ ctx.progress("Minting edges from confirmed facts", done, total);
269
+ const provider = session.provider();
270
+ const { refs: ledgerRefs } = provider.unresolvedRefs();
271
+ const { answered } = applyConfirmedFacts(ledgerRefs, session.config.confirmedFacts, session.config.repo);
272
+ const scope = {
273
+ repo: session.config.repo,
274
+ ...(session.config.workspace === undefined ? {} : { workspace: session.config.workspace }),
275
+ };
276
+ const mintResult = mintEdgesFromConfirmedFacts(answered, provider, scope, MINT_PRODUCED_BY, root.commitSha);
277
+ // The invalidation key (DEC-015: "producedBy + sourceFiles"). Scoped to
278
+ // every file carrying an *askable* ("value-unknown") row this run, not
279
+ // only the files a fact happened to answer. That distinction is what makes
280
+ // a re-run after a changed or removed answer safe: the run id is derived
281
+ // from (producedBy, repo, commitSha, sourceFiles), so a re-run at the same
282
+ // commit with the same askable domain lands on the *same* run id, and the
283
+ // writer's own self-clean pass (`writer.ts` rule 3) retires whatever this
284
+ // run no longer supplies — the edge from a superseded answer does not
285
+ // survive next to a new one. Scoping to the *answered* subset instead
286
+ // would let a file drop out of `sourceFiles` the moment its one answer was
287
+ // removed, and a stale edge for it would then never be found as a
288
+ // prior-run to retire at all.
289
+ const mintFiles = [
290
+ ...new Set(ledgerRefs.flatMap((ref) => ref.attrs?.["refusalClass"] === "value-unknown" && typeof ref.file === "string" ? [ref.file] : [])),
291
+ ].sort();
292
+ const mintBatch = {
293
+ repo: session.config.repo,
294
+ ...(session.config.workspace === undefined ? {} : { workspace: session.config.workspace }),
295
+ commitSha: root.commitSha,
296
+ producedBy: MINT_PRODUCED_BY,
297
+ sourceFiles: mintFiles,
298
+ skippedFiles: [],
299
+ reachedResolution: 2,
300
+ nodes: [],
301
+ edges: mintResult.minted.map((m) => m.edge),
302
+ unresolved: [],
303
+ };
304
+ const mintBuilt = buildGraph([mintBatch], {
305
+ nodeExists: (id) => provider.node(id) !== undefined,
306
+ });
307
+ const mintPersisted = persistGraph(session.store().driver, [mintBatch], mintBuilt);
308
+ const stored = counts(session.store().driver);
309
+ if (mintResult.minted.length > 0) {
310
+ notes.push(`${mintResult.minted.length} human-confirmed answer(s) minted a USES_API edge this run. Every ` +
311
+ "one is capped at reliability class B regardless of the resolution it would otherwise earn " +
312
+ "(DEC-223) — a confirmed value is testimony, not a structural read.");
313
+ const stale = mintResult.minted.filter((m) => m.edge.attrs?.["staleFact"] === true).length;
314
+ if (stale > 0) {
315
+ notes.push(`${stale} of those edge(s) rest on an answer not affirmatively current for this commit — either ` +
316
+ "given against a different commit, or given before this system recorded which commit an answer " +
317
+ "was confirmed against. Not re-verified against live code (there is no live re-verification " +
318
+ "mechanism yet); disclosed on the edge's attrs.confirmedFacts[].staleness so a consumer can see " +
319
+ "it rather than treat an old answer as unconditionally current.");
320
+ }
321
+ }
322
+ if (mintResult.orphaned.length > 0) {
323
+ notes.push(`${mintResult.orphaned.length} confirmed answer(s) named a path/method that matches no known ` +
324
+ "endpoint, so no edge was minted for them — never guessed into existence: " +
325
+ mintResult.orphaned.map((o) => `${o.method} ${o.path}`).join(", ") +
326
+ ".");
327
+ }
328
+ if (mintResult.unjoinable.length > 0) {
329
+ notes.push(`${mintResult.unjoinable.length} confirmed answer(s) apply to a row with no resolved ` +
330
+ "path/method to join against at all, so no edge was minted for those either.");
331
+ }
332
+ if (mintPersisted.evictedFiles.length > 0) {
333
+ notes.push(`${mintPersisted.evictedFiles.length} file(s) that previously grounded a minted edge were not ` +
334
+ "in this run's askable set (their confirmed answer was removed, or the row it answered " +
335
+ "changed) — the edges they previously grounded have been retired, not left to coexist with " +
336
+ "whatever this run minted instead.");
337
+ }
338
+ // No code-reading source ran, so there is no level to report and nothing to
339
+ // rest a claim on. R0 rather than a cheerful default.
340
+ const floor = (codeResolutions.length === 0
341
+ ? 0
342
+ : Math.min(...codeResolutions));
343
+ if (codeResolutions.length > 1 && new Set(codeResolutions).size > 1) {
344
+ notes.push(`Sources reached different resolution levels (R${Math.min(...codeResolutions)}-` +
345
+ `R${Math.max(...codeResolutions)}). The floor quoted here is the lowest of them, but an ` +
346
+ "individual answer computes its own from the edges it actually traversed, so a query " +
347
+ "confined to well-resolved code is not capped by the weakest source in the graph.");
348
+ }
349
+ const headline = batches.length === 0
350
+ ? `Nothing was read from ${session.config.repo}.`
351
+ : `Analysed ${session.config.repo} at ${root.commitSha.slice(0, 12)}: ` +
352
+ `${stored.nodes} nodes, ${stored.edges} edges, ${stored.unresolvedRefs} disclosed gaps ` +
353
+ `across ${batches.length} source(s).`;
354
+ const refusalSummary = refusalSummaryField(built.unresolved);
355
+ return answer({
356
+ headline,
357
+ state: batches.length === 0 ? "refused" : "ok",
358
+ nameLevel: true,
359
+ resolutionFloor: floor,
360
+ commitSha: root.commitSha,
361
+ graphBuiltAt: Date.now(),
362
+ irSchemaVersion: session.provider().baseStamp().irSchemaVersion,
363
+ notes,
364
+ ...(refusalSummary === undefined ? {} : { refusalSummary }),
365
+ data: {
366
+ repo: session.config.repo,
367
+ workspace: session.config.workspace ?? null,
368
+ commitSha: root.commitSha,
369
+ graphPath: session.graphPath,
370
+ sources: perSource,
371
+ sourcesFailed: registry.failed.map((f) => ({ module: f.spec.module, reason: f.reason })),
372
+ sourcesSkipped: skipped.map((s) => ({ source: s.source.source.id, reason: s.reason })),
373
+ stored,
374
+ merge: {
375
+ mergedNodes: built.mergedNodes,
376
+ mergedEdges: built.mergedEdges,
377
+ collisions: built.collisions.length,
378
+ droppedEdges: built.droppedEdges,
379
+ rejections: built.rejections.length,
380
+ },
381
+ joins: built.joins,
382
+ written: {
383
+ nodes: persisted.nodesWritten,
384
+ edges: persisted.edgesWritten,
385
+ unresolved: persisted.unresolvedWritten,
386
+ orphanedEdges: persisted.orphanedEdges,
387
+ evictedFiles: persisted.evictedFiles.length,
388
+ },
389
+ minted: {
390
+ edges: mintResult.minted.length,
391
+ orphaned: mintResult.orphaned.length,
392
+ unjoinable: mintResult.unjoinable.length,
393
+ evictedFiles: mintPersisted.evictedFiles.length,
394
+ },
395
+ },
396
+ });
397
+ }
398
+ export const analyzeTool = {
399
+ name: "analyze",
400
+ class: "stateful-read",
401
+ tier: "evidence",
402
+ version: "1.0.0",
403
+ title: "Analyse a repository into the code graph",
404
+ description: "Read the repository with every configured adapter and store the result as a code graph. " +
405
+ "Run this before any other Descry tool, and again after the working tree changes. Reports " +
406
+ "what it read, what it could not read, and the resolution level each adapter reached — the " +
407
+ "last one caps how strongly anything built on this graph may be stated.",
408
+ inputSchema: SCHEMA,
409
+ run,
410
+ };
411
+ //# sourceMappingURL=analyze.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyze.js","sourceRoot":"","sources":["../../src/tools/analyze.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * `contracts` — "does the caller agree with the thing it is calling?"
3
+ *
4
+ * §13, in two halves that fail independently.
5
+ *
6
+ * **The path half runs today.** A call that reaches no route, a route nothing
7
+ * calls. Name-level facts, so R2 evidence supports them at class A.
8
+ *
9
+ * **The shape half needs both producers to attach something they do not attach
10
+ * yet**, and it names exactly what — see `engine.ts`. It reports
11
+ * `refused` with those names rather than an empty mismatch list, because
12
+ * "no contract mismatches found" is a false statement when nothing was compared.
13
+ *
14
+ * ## Why the tool is `ok` when only the path half ran
15
+ *
16
+ * The path half produced a real answer over real evidence. Marking the whole
17
+ * call `refused` because a second check did not run would hide a genuine
18
+ * finding behind a caveat about a different question. The shape half's status is
19
+ * carried in `data.shapes.ran` and in the blockers, where a reader can act on it.
20
+ *
21
+ * The one case that *is* `refused` outright: no endpoints in the graph.
22
+ * Then neither half looked at anything.
23
+ */
24
+ import type { ContractCheck, ContractFindingKind, EndpointKey, NearMissKind, ShapeDifference, ShapeDirection } from "@descryy/core";
25
+ import type { ResolutionLevel } from "@descryy/ir";
26
+ import { type BriefNode, type ToolDefinition } from "./kit.ts";
27
+ /** A call that reaches no route, or a route nothing calls. */
28
+ export interface ContractPathFinding {
29
+ readonly kind: ContractFindingKind;
30
+ readonly method: string;
31
+ /** The normalised path template, not the raw literal. */
32
+ readonly path: string;
33
+ readonly detail: string;
34
+ /** Lowest resolution among the edges this finding rests on. Caps its class. */
35
+ readonly resolution: ResolutionLevel;
36
+ /**
37
+ * Absent when nothing on the other side resembles this endpoint.
38
+ *
39
+ * Its absence is what separates a typo from a call into a service outside
40
+ * this graph — the second is disclosed as unanalysed, never reported broken.
41
+ */
42
+ readonly nearMiss?: {
43
+ readonly kind: NearMissKind;
44
+ readonly against: EndpointKey;
45
+ };
46
+ /** Capped at five. Populated for a call that reaches no route. */
47
+ readonly callers: readonly BriefNode[];
48
+ /** Capped at five. Populated for a route nothing calls. */
49
+ readonly routes: readonly BriefNode[];
50
+ }
51
+ /** A field one side reads that the other does not send. Requires R3. */
52
+ export interface ContractShapeMismatchReport {
53
+ readonly endpoint: string;
54
+ readonly direction: ShapeDirection;
55
+ /** The side a missing field breaks. */
56
+ readonly reader: BriefNode;
57
+ /** The side that declares the shape. */
58
+ readonly writer: BriefNode;
59
+ readonly differences: readonly ShapeDifference[];
60
+ readonly resolution: ResolutionLevel;
61
+ }
62
+ /**
63
+ * The shape half's own status, carried in the payload rather than in the
64
+ * envelope.
65
+ *
66
+ * The call is `ok` when only the path half ran, because the path half produced
67
+ * a real answer over real evidence; marking the whole reply degraded because a
68
+ * second check did not run would hide a genuine finding behind a caveat about
69
+ * a different question. So `ran` is where a reader looks, and `blockers` names
70
+ * exactly what has to be attached before it can be true.
71
+ */
72
+ export interface ContractShapeStatus {
73
+ readonly ran: boolean;
74
+ readonly comparable: number;
75
+ /** Endpoints with a shape on the consumer side only. */
76
+ readonly consumerOnly: number;
77
+ /** Endpoints with a shape on the provider side only. */
78
+ readonly providerOnly: number;
79
+ /** Routes naming a DTO with no request/response role, so direction is unknown. */
80
+ readonly unroledDtos: number;
81
+ /** Never empty when `ran` is false. */
82
+ readonly blockers: readonly string[];
83
+ }
84
+ /**
85
+ * One shape, always.
86
+ *
87
+ * The counts in `checked` describe the **whole graph** even when `paths` was
88
+ * given: a denominator that shrank with the filter would make a narrow query
89
+ * look like a healthy repository. The two lists describe the filter.
90
+ */
91
+ export interface ContractsData {
92
+ readonly findings: readonly ContractPathFinding[];
93
+ readonly shapeMismatches: readonly ContractShapeMismatchReport[];
94
+ readonly checked: ContractCheck["paths"]["checked"];
95
+ readonly shapes: ContractShapeStatus;
96
+ /**
97
+ * Unmatched callers with nothing on the provider side resembling them.
98
+ *
99
+ * Not findings. This is the size of the region the analysis could not speak
100
+ * about at all.
101
+ */
102
+ readonly unmatchedWithoutNearMiss: number;
103
+ }
104
+ export declare const contractsTool: ToolDefinition;
105
+ //# sourceMappingURL=contracts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contracts.d.ts","sourceRoot":"","sources":["../../src/tools/contracts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAGH,OAAO,KAAK,EACV,aAAa,EACb,mBAAmB,EACnB,WAAW,EACX,YAAY,EACZ,eAAe,EACf,cAAc,EACf,MAAM,eAAe,CAAC;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,OAAO,EAIL,KAAK,SAAS,EAEd,KAAK,cAAc,EACpB,MAAM,UAAU,CAAC;AAGlB,8DAA8D;AAC9D,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,mBAAmB,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,yDAAyD;IACzD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,+EAA+E;IAC/E,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE;QAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,WAAW,CAAA;KAAE,CAAC;IACnF,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC,2DAA2D;IAC3D,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;CACvC;AAED,wEAAwE;AACxE,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC;IACnC,uCAAuC;IACvC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,wCAAwC;IACxC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,WAAW,EAAE,SAAS,eAAe,EAAE,CAAC;IACjD,QAAQ,CAAC,UAAU,EAAE,eAAe,CAAC;CACtC;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,wDAAwD;IACxD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,wDAAwD;IACxD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,kFAAkF;IAClF,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,uCAAuC;IACvC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED;;;;;;GAMG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,mBAAmB,EAAE,CAAC;IAClD,QAAQ,CAAC,eAAe,EAAE,SAAS,2BAA2B,EAAE,CAAC;IACjE,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,CAAC;IACpD,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC;;;;;OAKG;IACH,QAAQ,CAAC,wBAAwB,EAAE,MAAM,CAAC;CAC3C;AA0HD,eAAO,MAAM,aAAa,EAAE,cAe3B,CAAC"}