@descryy/mcp 0.6.0 → 0.7.1

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 (264) hide show
  1. package/dist/action-handshake.d.ts +8 -75
  2. package/dist/action-handshake.d.ts.map +1 -1
  3. package/dist/action-handshake.js +9 -79
  4. package/dist/action-handshake.js.map +1 -1
  5. package/dist/bin/descry-mcp.d.ts +4 -15
  6. package/dist/bin/descry-mcp.d.ts.map +1 -1
  7. package/dist/bin/descry-mcp.js +12 -42
  8. package/dist/bin/descry-mcp.js.map +1 -1
  9. package/dist/browser/driver.d.ts +121 -224
  10. package/dist/browser/driver.d.ts.map +1 -1
  11. package/dist/browser/driver.js +9 -55
  12. package/dist/browser/driver.js.map +1 -1
  13. package/dist/browser/evidence.d.ts +10 -43
  14. package/dist/browser/evidence.d.ts.map +1 -1
  15. package/dist/browser/evidence.js +20 -54
  16. package/dist/browser/evidence.js.map +1 -1
  17. package/dist/browser/fake-driver.d.ts +12 -19
  18. package/dist/browser/fake-driver.d.ts.map +1 -1
  19. package/dist/browser/fake-driver.js +31 -26
  20. package/dist/browser/fake-driver.js.map +1 -1
  21. package/dist/browser/graph-write.d.ts +4 -45
  22. package/dist/browser/graph-write.d.ts.map +1 -1
  23. package/dist/browser/graph-write.js +8 -53
  24. package/dist/browser/graph-write.js.map +1 -1
  25. package/dist/browser/playwright-driver.d.ts +7 -91
  26. package/dist/browser/playwright-driver.d.ts.map +1 -1
  27. package/dist/browser/playwright-driver.js +59 -197
  28. package/dist/browser/playwright-driver.js.map +1 -1
  29. package/dist/browser/provider.d.ts +5 -34
  30. package/dist/browser/provider.d.ts.map +1 -1
  31. package/dist/browser/provider.js +4 -24
  32. package/dist/browser/provider.js.map +1 -1
  33. package/dist/browser/registry.d.ts +26 -106
  34. package/dist/browser/registry.d.ts.map +1 -1
  35. package/dist/browser/registry.js +20 -77
  36. package/dist/browser/registry.js.map +1 -1
  37. package/dist/browser/scenario-provenance.d.ts +23 -0
  38. package/dist/browser/scenario-provenance.d.ts.map +1 -0
  39. package/dist/browser/scenario-provenance.js +59 -0
  40. package/dist/browser/scenario-provenance.js.map +1 -0
  41. package/dist/browser/scenario-resolve.d.ts +10 -44
  42. package/dist/browser/scenario-resolve.d.ts.map +1 -1
  43. package/dist/browser/scenario-resolve.js +10 -41
  44. package/dist/browser/scenario-resolve.js.map +1 -1
  45. package/dist/browser/scenario-runner.d.ts +12 -70
  46. package/dist/browser/scenario-runner.d.ts.map +1 -1
  47. package/dist/browser/scenario-runner.js +30 -90
  48. package/dist/browser/scenario-runner.js.map +1 -1
  49. package/dist/browser/stack-parser.d.ts +4 -28
  50. package/dist/browser/stack-parser.d.ts.map +1 -1
  51. package/dist/browser/stack-parser.js +12 -39
  52. package/dist/browser/stack-parser.js.map +1 -1
  53. package/dist/browser/tool-support.d.ts +20 -67
  54. package/dist/browser/tool-support.d.ts.map +1 -1
  55. package/dist/browser/tool-support.js +21 -65
  56. package/dist/browser/tool-support.js.map +1 -1
  57. package/dist/browser/url-scheme.d.ts +14 -0
  58. package/dist/browser/url-scheme.d.ts.map +1 -0
  59. package/dist/browser/url-scheme.js +38 -0
  60. package/dist/browser/url-scheme.js.map +1 -0
  61. package/dist/cancellation.d.ts +11 -46
  62. package/dist/cancellation.d.ts.map +1 -1
  63. package/dist/cancellation.js +11 -46
  64. package/dist/cancellation.js.map +1 -1
  65. package/dist/capped.d.ts +9 -33
  66. package/dist/capped.d.ts.map +1 -1
  67. package/dist/capped.js +7 -27
  68. package/dist/capped.js.map +1 -1
  69. package/dist/disclosure-ledger.d.ts +6 -30
  70. package/dist/disclosure-ledger.d.ts.map +1 -1
  71. package/dist/disclosure-ledger.js +4 -26
  72. package/dist/disclosure-ledger.js.map +1 -1
  73. package/dist/index.d.ts +10 -26
  74. package/dist/index.d.ts.map +1 -1
  75. package/dist/index.js +7 -17
  76. package/dist/index.js.map +1 -1
  77. package/dist/module-trust.d.ts +24 -0
  78. package/dist/module-trust.d.ts.map +1 -0
  79. package/dist/module-trust.js +60 -0
  80. package/dist/module-trust.js.map +1 -0
  81. package/dist/path-confinement.d.ts +31 -0
  82. package/dist/path-confinement.d.ts.map +1 -0
  83. package/dist/path-confinement.js +44 -0
  84. package/dist/path-confinement.js.map +1 -0
  85. package/dist/protocol.d.ts +10 -53
  86. package/dist/protocol.d.ts.map +1 -1
  87. package/dist/protocol.js +14 -60
  88. package/dist/protocol.js.map +1 -1
  89. package/dist/registry.d.ts +37 -58
  90. package/dist/registry.d.ts.map +1 -1
  91. package/dist/registry.js +88 -96
  92. package/dist/registry.js.map +1 -1
  93. package/dist/render.d.ts +89 -13
  94. package/dist/render.d.ts.map +1 -1
  95. package/dist/render.js +111 -16
  96. package/dist/render.js.map +1 -1
  97. package/dist/runtime-registry.d.ts +7 -66
  98. package/dist/runtime-registry.d.ts.map +1 -1
  99. package/dist/runtime-registry.js +39 -114
  100. package/dist/runtime-registry.js.map +1 -1
  101. package/dist/scenarios/index.d.ts +1 -1
  102. package/dist/scenarios/index.d.ts.map +1 -1
  103. package/dist/scenarios/index.js +1 -1
  104. package/dist/scenarios/index.js.map +1 -1
  105. package/dist/scenarios/parse.d.ts +4 -18
  106. package/dist/scenarios/parse.d.ts.map +1 -1
  107. package/dist/scenarios/parse.js +14 -34
  108. package/dist/scenarios/parse.js.map +1 -1
  109. package/dist/scenarios/scenario.d.ts +18 -74
  110. package/dist/scenarios/scenario.d.ts.map +1 -1
  111. package/dist/scenarios/scenario.js +7 -34
  112. package/dist/scenarios/scenario.js.map +1 -1
  113. package/dist/scenarios/storage.d.ts +11 -41
  114. package/dist/scenarios/storage.d.ts.map +1 -1
  115. package/dist/scenarios/storage.js +57 -47
  116. package/dist/scenarios/storage.js.map +1 -1
  117. package/dist/server.d.ts.map +1 -1
  118. package/dist/server.js +69 -19
  119. package/dist/server.js.map +1 -1
  120. package/dist/session.d.ts +90 -259
  121. package/dist/session.d.ts.map +1 -1
  122. package/dist/session.js +136 -250
  123. package/dist/session.js.map +1 -1
  124. package/dist/tools/analyze.d.ts +32 -118
  125. package/dist/tools/analyze.d.ts.map +1 -1
  126. package/dist/tools/analyze.js +65 -186
  127. package/dist/tools/analyze.js.map +1 -1
  128. package/dist/tools/browser-click.d.ts +3 -21
  129. package/dist/tools/browser-click.d.ts.map +1 -1
  130. package/dist/tools/browser-click.js +10 -31
  131. package/dist/tools/browser-click.js.map +1 -1
  132. package/dist/tools/browser-close-session.d.ts +4 -13
  133. package/dist/tools/browser-close-session.d.ts.map +1 -1
  134. package/dist/tools/browser-close-session.js +4 -13
  135. package/dist/tools/browser-close-session.js.map +1 -1
  136. package/dist/tools/browser-fill.d.ts +5 -36
  137. package/dist/tools/browser-fill.d.ts.map +1 -1
  138. package/dist/tools/browser-fill.js +9 -44
  139. package/dist/tools/browser-fill.js.map +1 -1
  140. package/dist/tools/browser-navigate.d.ts +6 -27
  141. package/dist/tools/browser-navigate.d.ts.map +1 -1
  142. package/dist/tools/browser-navigate.js +6 -23
  143. package/dist/tools/browser-navigate.js.map +1 -1
  144. package/dist/tools/browser-run-scenario.d.ts +5 -31
  145. package/dist/tools/browser-run-scenario.d.ts.map +1 -1
  146. package/dist/tools/browser-run-scenario.js +10 -46
  147. package/dist/tools/browser-run-scenario.js.map +1 -1
  148. package/dist/tools/browser-save-scenario.d.ts +3 -28
  149. package/dist/tools/browser-save-scenario.d.ts.map +1 -1
  150. package/dist/tools/browser-save-scenario.js +47 -84
  151. package/dist/tools/browser-save-scenario.js.map +1 -1
  152. package/dist/tools/browser-snapshot.d.ts +6 -39
  153. package/dist/tools/browser-snapshot.d.ts.map +1 -1
  154. package/dist/tools/browser-snapshot.js +4 -31
  155. package/dist/tools/browser-snapshot.js.map +1 -1
  156. package/dist/tools/browser-start-session.d.ts +4 -23
  157. package/dist/tools/browser-start-session.d.ts.map +1 -1
  158. package/dist/tools/browser-start-session.js +145 -56
  159. package/dist/tools/browser-start-session.js.map +1 -1
  160. package/dist/tools/browser-type.d.ts +5 -35
  161. package/dist/tools/browser-type.d.ts.map +1 -1
  162. package/dist/tools/browser-type.js +10 -45
  163. package/dist/tools/browser-type.js.map +1 -1
  164. package/dist/tools/contracts.d.ts +9 -86
  165. package/dist/tools/contracts.d.ts.map +1 -1
  166. package/dist/tools/contracts.js +24 -80
  167. package/dist/tools/contracts.js.map +1 -1
  168. package/dist/tools/cross-pr.d.ts +42 -30
  169. package/dist/tools/cross-pr.d.ts.map +1 -1
  170. package/dist/tools/cross-pr.js +81 -62
  171. package/dist/tools/cross-pr.js.map +1 -1
  172. package/dist/tools/git-diff.d.ts +18 -2
  173. package/dist/tools/git-diff.d.ts.map +1 -1
  174. package/dist/tools/git-diff.js +118 -26
  175. package/dist/tools/git-diff.js.map +1 -1
  176. package/dist/tools/git-history.d.ts +5 -16
  177. package/dist/tools/git-history.d.ts.map +1 -1
  178. package/dist/tools/git-history.js +3 -10
  179. package/dist/tools/git-history.js.map +1 -1
  180. package/dist/tools/history.d.ts +4 -33
  181. package/dist/tools/history.d.ts.map +1 -1
  182. package/dist/tools/history.js +6 -31
  183. package/dist/tools/history.js.map +1 -1
  184. package/dist/tools/impact.d.ts +7 -51
  185. package/dist/tools/impact.d.ts.map +1 -1
  186. package/dist/tools/impact.js +15 -67
  187. package/dist/tools/impact.js.map +1 -1
  188. package/dist/tools/index.d.ts +3 -8
  189. package/dist/tools/index.d.ts.map +1 -1
  190. package/dist/tools/index.js +3 -8
  191. package/dist/tools/index.js.map +1 -1
  192. package/dist/tools/kit.d.ts +66 -113
  193. package/dist/tools/kit.d.ts.map +1 -1
  194. package/dist/tools/kit.js +60 -28
  195. package/dist/tools/kit.js.map +1 -1
  196. package/dist/tools/link-workspace.d.ts +7 -45
  197. package/dist/tools/link-workspace.d.ts.map +1 -1
  198. package/dist/tools/link-workspace.js +10 -48
  199. package/dist/tools/link-workspace.js.map +1 -1
  200. package/dist/tools/lookup.d.ts +4 -14
  201. package/dist/tools/lookup.d.ts.map +1 -1
  202. package/dist/tools/lookup.js +4 -14
  203. package/dist/tools/lookup.js.map +1 -1
  204. package/dist/tools/mark-incident.d.ts +7 -54
  205. package/dist/tools/mark-incident.d.ts.map +1 -1
  206. package/dist/tools/mark-incident.js +15 -68
  207. package/dist/tools/mark-incident.js.map +1 -1
  208. package/dist/tools/observe-runtime.d.ts +27 -210
  209. package/dist/tools/observe-runtime.d.ts.map +1 -1
  210. package/dist/tools/observe-runtime.js +273 -434
  211. package/dist/tools/observe-runtime.js.map +1 -1
  212. package/dist/tools/observe-tests.d.ts +9 -100
  213. package/dist/tools/observe-tests.d.ts.map +1 -1
  214. package/dist/tools/observe-tests.js +74 -152
  215. package/dist/tools/observe-tests.js.map +1 -1
  216. package/dist/tools/pr-analysis.d.ts +18 -2
  217. package/dist/tools/pr-analysis.d.ts.map +1 -1
  218. package/dist/tools/pr-analysis.js +73 -23
  219. package/dist/tools/pr-analysis.js.map +1 -1
  220. package/dist/tools/pre-push.d.ts +74 -16
  221. package/dist/tools/pre-push.d.ts.map +1 -1
  222. package/dist/tools/pre-push.js +152 -51
  223. package/dist/tools/pre-push.js.map +1 -1
  224. package/dist/tools/propagation.d.ts +11 -53
  225. package/dist/tools/propagation.d.ts.map +1 -1
  226. package/dist/tools/propagation.js +17 -58
  227. package/dist/tools/propagation.js.map +1 -1
  228. package/dist/tools/questions.d.ts +13 -63
  229. package/dist/tools/questions.d.ts.map +1 -1
  230. package/dist/tools/questions.js +33 -105
  231. package/dist/tools/questions.js.map +1 -1
  232. package/dist/tools/refusal-fetch.d.ts +4 -40
  233. package/dist/tools/refusal-fetch.d.ts.map +1 -1
  234. package/dist/tools/refusal-fetch.js +4 -40
  235. package/dist/tools/refusal-fetch.js.map +1 -1
  236. package/dist/tools/runtime-incident.d.ts +4 -63
  237. package/dist/tools/runtime-incident.d.ts.map +1 -1
  238. package/dist/tools/runtime-incident.js +10 -87
  239. package/dist/tools/runtime-incident.js.map +1 -1
  240. package/dist/tools/scope.d.ts +7 -25
  241. package/dist/tools/scope.d.ts.map +1 -1
  242. package/dist/tools/scope.js +10 -29
  243. package/dist/tools/scope.js.map +1 -1
  244. package/dist/tools/similar-incidents.d.ts +11 -86
  245. package/dist/tools/similar-incidents.d.ts.map +1 -1
  246. package/dist/tools/similar-incidents.js +7 -71
  247. package/dist/tools/similar-incidents.js.map +1 -1
  248. package/dist/tools/validate.d.ts +29 -70
  249. package/dist/tools/validate.d.ts.map +1 -1
  250. package/dist/tools/validate.js +141 -85
  251. package/dist/tools/validate.js.map +1 -1
  252. package/dist/tools/verification-status.d.ts +9 -64
  253. package/dist/tools/verification-status.d.ts.map +1 -1
  254. package/dist/tools/verification-status.js +9 -62
  255. package/dist/tools/verification-status.js.map +1 -1
  256. package/dist/tools/verify-claim.d.ts +5 -52
  257. package/dist/tools/verify-claim.d.ts.map +1 -1
  258. package/dist/tools/verify-claim.js +6 -56
  259. package/dist/tools/verify-claim.js.map +1 -1
  260. package/dist/transport.d.ts +15 -52
  261. package/dist/transport.d.ts.map +1 -1
  262. package/dist/transport.js +16 -60
  263. package/dist/transport.js.map +1 -1
  264. package/package.json +40 -22
@@ -1,48 +1,15 @@
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 — and, on the ordinary
24
- * single-repository run every shipped call actually makes, is the *only* value possible, which is
25
- * narrated too (`crossRepoJoinNote`) rather than left for a reader to infer from `repos.length`.
26
- *
27
- * ## The repo identity warning
28
- *
29
- * With no `.descry/config.json`, `repo` defaults to the directory name — and
30
- * every repo-scoped node id is a hash over it. Clone into a differently-named
31
- * folder and every id changes, which silently destroys incident links and
32
- * `CHANGES_WITH` weights. That is DEC-004's failure arriving through the one
33
- * field a default cannot get right, so it is stated on every unconfigured run.
34
- */
1
+ /** `analyze` — the only tool that writes to the graph. Disclosures matter most: a
2
+ * query can't report a gap it wasn't told about. Discloses: sources that failed or
3
+ * didn't claim the repo (§20.2 — not-analysable ≠ clean), each source's resolution
4
+ * reached (caps reliability, rule 3), id collisions (DEC-024), evicted files, and the
5
+ * cross-repo join count. No config → `repo` defaults to dir name, hashed into every id (DEC-004). */
35
6
  import type { IRBatch, UnresolvedRef } from "@descryy/ir";
36
7
  import type { BuildResult, JoinMetric, StoreCounts } from "@descryy/core";
37
8
  import { type ToolDefinition } from "./kit.ts";
38
- import { type DescryConfig } from "../session.ts";
39
- /**
40
- * What one source read, and how well.
41
- *
42
- * `reachedResolution` is the number principle 3 caps everything above it by. A
43
- * run that degraded to R0 because a dependency was missing produces a graph
44
- * that looks identical to a healthy one until this field is read.
45
- */
9
+ import { type LoadedSource } from "../registry.ts";
10
+ export { configDriftNote } from "../session.ts";
11
+ /** What one source read, and how well. `reachedResolution` is what rule 3 caps everything
12
+ * above it by — a run degraded to R0 looks identical to a healthy one until this is read. */
46
13
  export interface SourceRun {
47
14
  readonly source: string;
48
15
  readonly files: number;
@@ -50,55 +17,34 @@ export interface SourceRun {
50
17
  readonly edges: number;
51
18
  /** Refusal-ledger rows: gaps this source disclosed rather than guessed at. */
52
19
  readonly unresolved: number;
53
- /**
54
- * Files this source attempted but could not parse — absent from the graph
55
- * entirely, not confirmed clean. Never counted in `files`; see
56
- * `IRBatch.skippedFiles`.
57
- */
20
+ /** Files attempted but not parsed — absent from the graph, not confirmed clean. Never
21
+ * counted in `files`; see `IRBatch.skippedFiles`. */
58
22
  readonly skippedFiles: number;
59
23
  readonly reachedResolution: number;
60
24
  /** Wall-clock time this source took, including the git-history reader. */
61
25
  readonly ms: number;
62
26
  }
63
- /**
64
- * The two batch fields a producer is allowed not to have heard of.
65
- *
66
- * `normaliseBatch` — every batch's gatekeeper on the way to the store — treats
67
- * `skippedFiles` and `unresolved` as absent-means-none, and says why in its own
68
- * comment: absent is what a producer not yet updated to report them sends. That
69
- * is a deliberate compatibility promise to adapters compiled against an older
70
- * `@descryy/ir`, and adapters are versioned and released on their own cadence
71
- * precisely so that can happen.
72
- *
73
- * This function is that promise, applied one layer earlier. `analyze` used to
74
- * read `.length` off both directly, which meant an adapter the Normaliser would
75
- * have happily accepted crashed the run before it ever got there — reported as
76
- * the *adapter* failing, taking a whole language into "not analysable". A real
77
- * shipped adapter did exactly that while returning over a thousand perfectly
78
- * good nodes.
79
- *
80
- * Absent and malformed stay different, which is the line the Normaliser draws
81
- * too: a missing field is an older producer, a field holding a string is a
82
- * broken one, and defaulting the second away would hide a real defect.
83
- */
27
+ /** `skippedFiles`/`unresolved`: absent means an adapter compiled against an older
28
+ * `@descryy/ir`, not malformed — a real adapter once crashed here on `.length` of
29
+ * undefined despite returning 1000+ good nodes. Absent ⇒ old producer; wrong type ⇒ broken, never defaulted away. */
84
30
  export declare function optionalBatchLists(batch: IRBatch): {
85
31
  readonly skippedFiles: IRBatch["skippedFiles"];
86
32
  readonly unresolved: IRBatch["unresolved"];
87
33
  };
34
+ /** Distinguishes "the adapter failed" from "Descry's own code broke handling its
35
+ * output" — before this, a `TypeError` thrown in this file was reported as the
36
+ * adapter failing, sending users to debug working code instead of reporting our bug. */
88
37
  /**
89
- * What went wrong, and — the part that was missing — *whose* fault it was.
90
- *
91
- * The original note said "<source> failed while reading this repository" for
92
- * anything thrown anywhere in the loop, including inside Descry's own code
93
- * after the adapter had returned successfully. That is how a `TypeError` thrown
94
- * in this very file came to be reported as an adapter failing: a developer
95
- * reading it would go and debug an adapter that worked.
96
- *
97
- * The distinction is not cosmetic. "That adapter could not read this" tells a
98
- * user to look at their own toolchain; "Descry broke" tells them to report a
99
- * bug. Sending the second as the first spends the user's time on the wrong
100
- * thing and hides the defect from us.
38
+ * F2 (UAT phase 4 F3 / Part 13 §13.3.2): which build actually ran. A bare
39
+ * specifier can resolve to more than one installed copy with the same
40
+ * name — CJS resolution walks upward through `node_modules`, so a stray
41
+ * install above the repository silently wins over the intended one, and
42
+ * `producedBy` alone cannot tell them apart (it carries the module's own
43
+ * self-declared version, which can drift from what actually shipped).
44
+ * This is the disclosure that makes "why are my results different from
45
+ * yours" answerable instead of a debugging session.
101
46
  */
47
+ export declare function sourceProvenanceNote(entry: LoadedSource): string;
102
48
  export declare function sourceFailureNote(sourceId: string, error: unknown, phase: "emit" | "engine"): string;
103
49
  export interface AnalyzeData {
104
50
  readonly repo: string;
@@ -147,49 +93,17 @@ export interface AnalyzeData {
147
93
  readonly evictedFiles: number;
148
94
  };
149
95
  }
150
- /**
151
- * §5.4's `refusals` field, for this run only — never the graph's whole
152
- * stored ledger (`refusal_fetch`'s job). `built.unresolved` is what the
153
- * sources this call just read could not resolve, so it is a fresh
154
- * population every run rather than accumulated history, and honestly
155
- * scoped to "what this call found" the way the field's own contract asks.
156
- *
157
- * `undefined` on zero rows — the envelope's `refusals` stays `null` rather
158
- * than carrying a summary with no exemplar or handle to show (matching
159
- * `summariseRefusals`'s own `count === 0` shape one level up).
160
- */
96
+ /** §5.4's `refusals` field for this run only, not the graph's whole ledger
97
+ * (`refusal_fetch`'s job) — a fresh population per call, honestly scoped to what
98
+ * this run found. `undefined` on zero rows so `refusals` stays `null`, not an empty summary. */
161
99
  export declare function refusalSummaryField(unresolved: readonly UnresolvedRef[]): {
162
100
  readonly count: number;
163
101
  readonly exemplar: string;
164
102
  readonly handle: string;
165
103
  } | undefined;
166
- /**
167
- * `Session.config` is read once at process startup and held for the process's
168
- * whole life (session.ts's own header). A write tool — `link_workspace`, or a
169
- * hand edit — changes the file on disk without this process ever knowing, and
170
- * without this check the notes below would tell the agent to add a "sources"
171
- * array that the file has actually had since the write. Comparing the two
172
- * turns a misleading "still not configured" into an accurate "restart to pick
173
- * this up".
174
- */
175
- export declare function configDriftNote(onDisk: DescryConfig | null, loaded: DescryConfig): string | undefined;
176
- /**
177
- * The single-repo half of the joins disclosure — UAT round 3, second-look
178
- * item's other half.
179
- *
180
- * `build.ts`'s own `NO_CROSS_REPO_JOIN` warning (folded into `notes` just
181
- * above this function's call site) already narrates the alarming case: two
182
- * or more repositories that emitted a workspace-scoped type and none of it
183
- * joined. What it does not narrate is the ordinary case — every `analyze`
184
- * call in the shipped product passes exactly one repository's batches, so
185
- * every entry in `built.crossRepoJoins` has `crossRepoJoinPossible: false`
186
- * and `suppliedByTwoOrMoreRepos: 0` by construction, not by failure. Left
187
- * un-narrated, `{ total: 48, suppliedByTwoOrMoreRepos: 0 }` sitting beside a
188
- * disclosure about 55 unrelated within-repo edges having just succeeded
189
- * reads as a failure of the very thing that disclosure said worked. This
190
- * states outright that one repository was in scope, so a reader is not left
191
- * to infer it from `repos.length` themselves.
192
- */
104
+ /** Single-repo half of the joins disclosure. In the ordinary case — one repo in scope —
105
+ * every join metric's `suppliedByTwoOrMoreRepos: 0` looks like `NO_CROSS_REPO_JOIN`'s
106
+ * failure case; this states outright it's just scope, not a failed join. */
193
107
  export declare function crossRepoJoinNote(joins: readonly JoinMetric[]): string | undefined;
194
108
  export declare const analyzeTool: ToolDefinition;
195
109
  //# sourceMappingURL=analyze.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/tools/analyze.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/tools/analyze.ts"],"names":[],"mappings":"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"}
@@ -1,63 +1,18 @@
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 — and, on the ordinary
24
- * single-repository run every shipped call actually makes, is the *only* value possible, which is
25
- * narrated too (`crossRepoJoinNote`) rather than left for a reader to infer from `repos.length`.
26
- *
27
- * ## The repo identity warning
28
- *
29
- * With no `.descry/config.json`, `repo` defaults to the directory name — and
30
- * every repo-scoped node id is a hash over it. Clone into a differently-named
31
- * folder and every id changes, which silently destroys incident links and
32
- * `CHANGES_WITH` weights. That is DEC-004's failure arriving through the one
33
- * field a default cannot get right, so it is stated on every unconfigured run.
34
- */
35
- import { existsSync } from "node:fs";
1
+ /** `analyze` — the only tool that writes to the graph. Disclosures matter most: a
2
+ * query can't report a gap it wasn't told about. Discloses: sources that failed or
3
+ * didn't claim the repo (§20.2 — not-analysable ≠ clean), each source's resolution
4
+ * reached (caps reliability, rule 3), id collisions (DEC-024), evicted files, and the
5
+ * cross-repo join count. No config → `repo` defaults to dir name, hashed into every id (DEC-004). */
36
6
  import { applyConfirmedFacts, buildGraph, counts, createConfirmedIncidentSource, createGitSource, mintEdgesFromConfirmedFacts, MINT_PRODUCED_BY, persistGraph, summariseRefusals, } from "@descryy/core";
37
7
  import { answer, ToolInputError } from "./kit.js";
38
8
  import { detectSources, loadSources } from "../registry.js";
39
- import { loadConfig } from "../session.js";
40
- /**
41
- * The two batch fields a producer is allowed not to have heard of.
42
- *
43
- * `normaliseBatch` — every batch's gatekeeper on the way to the store — treats
44
- * `skippedFiles` and `unresolved` as absent-means-none, and says why in its own
45
- * comment: absent is what a producer not yet updated to report them sends. That
46
- * is a deliberate compatibility promise to adapters compiled against an older
47
- * `@descryy/ir`, and adapters are versioned and released on their own cadence
48
- * precisely so that can happen.
49
- *
50
- * This function is that promise, applied one layer earlier. `analyze` used to
51
- * read `.length` off both directly, which meant an adapter the Normaliser would
52
- * have happily accepted crashed the run before it ever got there — reported as
53
- * the *adapter* failing, taking a whole language into "not analysable". A real
54
- * shipped adapter did exactly that while returning over a thousand perfectly
55
- * good nodes.
56
- *
57
- * Absent and malformed stay different, which is the line the Normaliser draws
58
- * too: a missing field is an older producer, a field holding a string is a
59
- * broken one, and defaulting the second away would hide a real defect.
60
- */
9
+ // Re-exported for `analyze.test.ts`, which covers this pure helper in isolation.
10
+ // The check itself now runs centrally in `server.ts` (13.3.16 / N2 steps 2-3) —
11
+ // every reply discloses a stale config, not only this tool's.
12
+ export { configDriftNote } from "../session.js";
13
+ /** `skippedFiles`/`unresolved`: absent means an adapter compiled against an older
14
+ * `@descryy/ir`, not malformed — a real adapter once crashed here on `.length` of
15
+ * undefined despite returning 1000+ good nodes. Absent ⇒ old producer; wrong type ⇒ broken, never defaulted away. */
61
16
  export function optionalBatchLists(batch) {
62
17
  const read = (value, field) => {
63
18
  if (value === undefined || value === null)
@@ -72,20 +27,26 @@ export function optionalBatchLists(batch) {
72
27
  unresolved: read(batch.unresolved, "unresolved"),
73
28
  };
74
29
  }
30
+ /** Distinguishes "the adapter failed" from "Descry's own code broke handling its
31
+ * output" — before this, a `TypeError` thrown in this file was reported as the
32
+ * adapter failing, sending users to debug working code instead of reporting our bug. */
75
33
  /**
76
- * What went wrong, and — the part that was missing — *whose* fault it was.
77
- *
78
- * The original note said "<source> failed while reading this repository" for
79
- * anything thrown anywhere in the loop, including inside Descry's own code
80
- * after the adapter had returned successfully. That is how a `TypeError` thrown
81
- * in this very file came to be reported as an adapter failing: a developer
82
- * reading it would go and debug an adapter that worked.
83
- *
84
- * The distinction is not cosmetic. "That adapter could not read this" tells a
85
- * user to look at their own toolchain; "Descry broke" tells them to report a
86
- * bug. Sending the second as the first spends the user's time on the wrong
87
- * thing and hides the defect from us.
34
+ * F2 (UAT phase 4 F3 / Part 13 §13.3.2): which build actually ran. A bare
35
+ * specifier can resolve to more than one installed copy with the same
36
+ * name — CJS resolution walks upward through `node_modules`, so a stray
37
+ * install above the repository silently wins over the intended one, and
38
+ * `producedBy` alone cannot tell them apart (it carries the module's own
39
+ * self-declared version, which can drift from what actually shipped).
40
+ * This is the disclosure that makes "why are my results different from
41
+ * yours" answerable instead of a debugging session.
88
42
  */
43
+ export function sourceProvenanceNote(entry) {
44
+ const version = entry.packageVersion ?? "unknown version";
45
+ const location = entry.resolvedPath !== null
46
+ ? `resolved from ${entry.resolvedPath} (${entry.via === "repo" ? "the scanned repository" : "this server's own module closure"})`
47
+ : "resolved via this server's own module closure (the exact path could not be determined)";
48
+ return `${entry.source.id} — ${entry.spec.module}@${version}, ${location}.`;
49
+ }
89
50
  export function sourceFailureNote(sourceId, error, phase) {
90
51
  const message = error instanceof Error ? error.message : String(error);
91
52
  return phase === "emit"
@@ -112,63 +73,18 @@ const SCHEMA = {
112
73
  },
113
74
  additionalProperties: false,
114
75
  };
115
- /**
116
- * §5.4's `refusals` field, for this run only — never the graph's whole
117
- * stored ledger (`refusal_fetch`'s job). `built.unresolved` is what the
118
- * sources this call just read could not resolve, so it is a fresh
119
- * population every run rather than accumulated history, and honestly
120
- * scoped to "what this call found" the way the field's own contract asks.
121
- *
122
- * `undefined` on zero rows — the envelope's `refusals` stays `null` rather
123
- * than carrying a summary with no exemplar or handle to show (matching
124
- * `summariseRefusals`'s own `count === 0` shape one level up).
125
- */
76
+ /** §5.4's `refusals` field for this run only, not the graph's whole ledger
77
+ * (`refusal_fetch`'s job) — a fresh population per call, honestly scoped to what
78
+ * this run found. `undefined` on zero rows so `refusals` stays `null`, not an empty summary. */
126
79
  export function refusalSummaryField(unresolved) {
127
80
  const summary = summariseRefusals(unresolved);
128
81
  if (summary.count === 0 || summary.exemplar === null || summary.handle === null)
129
82
  return undefined;
130
83
  return { count: summary.count, exemplar: summary.exemplar, handle: summary.handle };
131
84
  }
132
- /**
133
- * `Session.config` is read once at process startup and held for the process's
134
- * whole life (session.ts's own header). A write tool — `link_workspace`, or a
135
- * hand edit — changes the file on disk without this process ever knowing, and
136
- * without this check the notes below would tell the agent to add a "sources"
137
- * array that the file has actually had since the write. Comparing the two
138
- * turns a misleading "still not configured" into an accurate "restart to pick
139
- * this up".
140
- */
141
- export function configDriftNote(onDisk, loaded) {
142
- if (onDisk === null)
143
- return undefined;
144
- const changed = onDisk.configPath !== loaded.configPath ||
145
- onDisk.workspace !== loaded.workspace ||
146
- onDisk.sources.length !== loaded.sources.length;
147
- if (!changed)
148
- return undefined;
149
- return ("The on-disk .descry/config.json no longer matches what this server loaded at startup. " +
150
- "Configuration is read once per process, not on every call (session.ts) — this run is still " +
151
- "using the configuration this server started with, not the current file. Restart the MCP " +
152
- "server for on-disk changes to take effect; the notes below describe the stale, running " +
153
- "configuration, not what is actually on disk now.");
154
- }
155
- /**
156
- * The single-repo half of the joins disclosure — UAT round 3, second-look
157
- * item's other half.
158
- *
159
- * `build.ts`'s own `NO_CROSS_REPO_JOIN` warning (folded into `notes` just
160
- * above this function's call site) already narrates the alarming case: two
161
- * or more repositories that emitted a workspace-scoped type and none of it
162
- * joined. What it does not narrate is the ordinary case — every `analyze`
163
- * call in the shipped product passes exactly one repository's batches, so
164
- * every entry in `built.crossRepoJoins` has `crossRepoJoinPossible: false`
165
- * and `suppliedByTwoOrMoreRepos: 0` by construction, not by failure. Left
166
- * un-narrated, `{ total: 48, suppliedByTwoOrMoreRepos: 0 }` sitting beside a
167
- * disclosure about 55 unrelated within-repo edges having just succeeded
168
- * reads as a failure of the very thing that disclosure said worked. This
169
- * states outright that one repository was in scope, so a reader is not left
170
- * to infer it from `repos.length` themselves.
171
- */
85
+ /** Single-repo half of the joins disclosure. In the ordinary case — one repo in scope —
86
+ * every join metric's `suppliedByTwoOrMoreRepos: 0` looks like `NO_CROSS_REPO_JOIN`'s
87
+ * failure case; this states outright it's just scope, not a failed join. */
172
88
  export function crossRepoJoinNote(joins) {
173
89
  const singleRepo = joins.filter((j) => !j.crossRepoJoinPossible);
174
90
  if (singleRepo.length === 0)
@@ -188,21 +104,16 @@ async function run(args, ctx) {
188
104
  }
189
105
  const root = await session.root();
190
106
  const notes = [];
191
- // MK-4: analyze is about to rebuild the graph from source regardless, so a
192
- // stale IR schema version is repaired here rather than left to refuse —
193
- // see Session.repairStaleGraph's own doc for why only this tool does this.
107
+ // MK-4: analyze rebuilds from source regardless, so a stale IR schema is repaired
108
+ // here rather than refused — see `Session.repairStaleGraph` for why only this tool does this.
194
109
  const { rebuilt } = session.repairStaleGraph();
195
110
  if (rebuilt) {
196
111
  notes.push("The stored graph was written under an older IR schema version. Its derived tables were " +
197
112
  "dropped and are rebuilt by this run; earned data (R4 observations, edge corrections, " +
198
113
  "incident links) was not affected.");
199
114
  }
200
- if (existsSync(session.repoPath)) {
201
- const onDisk = await loadConfig(session.repoPath).catch(() => null);
202
- const drift = configDriftNote(onDisk, session.config);
203
- if (drift !== undefined)
204
- notes.push(drift);
205
- }
115
+ // Config drift is now checked centrally in server.ts for every tool (13.3.16 /
116
+ // N2 steps 2-3), not just this one — see `checkConfigDrift` in session.ts.
206
117
  if (session.config.configPath === null) {
207
118
  notes.push(`No ${".descry/config.json"} was found, so the repository identity defaults to the ` +
208
119
  `directory name ("${session.config.repo}"). Every repo-scoped node id is a hash over that ` +
@@ -222,12 +133,13 @@ async function run(args, ctx) {
222
133
  notes.push(`The source "${failure.spec.module}" could not be loaded (${failure.reason}). Anything it ` +
223
134
  "would have read is not analysable — not clean.");
224
135
  }
136
+ for (const entry of registry.loaded) {
137
+ notes.push(sourceProvenanceNote(entry));
138
+ }
225
139
  if (session.config.sources.length === 0) {
226
140
  notes.push(
227
- // No example package name here, deliberately, and the boundary lint
228
- // enforces it. Naming one would make this engine know about a specific
229
- // language — the exact inversion `registry.ts` exists to prevent — and the
230
- // list of installed adapters is not something this process can see.
141
+ // No example package name here — naming one would leak a specific language into
142
+ // this engine, the inversion `registry.ts` exists to prevent (rule 1: IR boundary).
231
143
  "No language adapters are configured, so nothing was read from source. Add a \"sources\" " +
232
144
  `array to ${".descry/config.json"} listing the adapter packages to load, as module ` +
233
145
  "specifiers. This engine does not know the name of any adapter: the IR boundary is a " +
@@ -246,30 +158,17 @@ async function run(args, ctx) {
246
158
  };
247
159
  const batches = [];
248
160
  const perSource = [];
249
- /**
250
- * Resolution levels reached by sources that read **source code**.
251
- *
252
- * The floor is drawn from these alone, and the git-history reader is why. It
253
- * reaches R0 and always will: R0 is what reading a commit log *is*, not a
254
- * degradation of it. Folding it into a single graph-wide minimum pinned every
255
- * analyse reply at R0 and therefore at reliability class C — which then
256
- * described a set of directly counted rows as "a behavioural prediction".
257
- *
258
- * DEC-058 already settled the general form of this: an edge carries the
259
- * resolution of *its own evidence*, not of the run, so a graph has no single
260
- * floor and each answer computes its own. This is that rule applied one level
261
- * up. Every source's level is still reported individually in `sources`, and a
262
- * source that fell short of its own declared ceiling is disclosed separately.
263
- */
161
+ /** Resolution floor comes from code-reading sources only — git history is always R0
162
+ * by nature, not degradation; folding it in would cap every reply at class C. DEC-058:
163
+ * an edge carries its own evidence's resolution, not the run's; each source still reported individually. */
264
164
  const codeResolutions = [];
265
165
  const total = applicable.length + (includeHistory ? 1 : 0);
266
166
  let done = 0;
267
167
  for (const entry of applicable) {
268
168
  ctx.progress(`Reading ${entry.source.id}`, done, total);
269
169
  const started = Date.now();
270
- // The adapter's own work and Descry's handling of its result are separate
271
- // phases with separate blame. Folding them into one `try` is what let a
272
- // TypeError in the lines below be reported as the adapter failing.
170
+ // Adapter's own work and Descry's handling of its output are separate phases with
171
+ // separate blame — folding them into one `try` let a `TypeError` here be blamed on the adapter.
273
172
  let batch;
274
173
  try {
275
174
  batch = await entry.source.emit(context);
@@ -293,9 +192,8 @@ async function run(args, ctx) {
293
192
  reachedResolution: batch.reachedResolution,
294
193
  ms: Date.now() - started,
295
194
  });
296
- // The declared ceiling versus what this run actually reached. A gap is the
297
- // honest-degradation case: the adapter can do better and something in this
298
- // repository stopped it.
195
+ // Declared ceiling vs what this run reached — a gap is honest degradation (rule 7):
196
+ // the adapter can do better but something in this repo stopped it.
299
197
  const ceiling = entry.adapter?.capabilities().maxResolution;
300
198
  if (ceiling !== undefined)
301
199
  codeResolutions.push(batch.reachedResolution);
@@ -315,15 +213,12 @@ async function run(args, ctx) {
315
213
  }
316
214
  }
317
215
  catch (error) {
318
- // Reached only when Descry's own handling threw. The adapter already
319
- // returned, so blaming it here would send the user to debug working code.
216
+ // Reached only when Descry's own handling threw — the adapter already returned successfully.
320
217
  notes.push(sourceFailureNote(entry.source.id, error, "engine"));
321
218
  }
322
219
  finally {
323
- // `dispose` is a `LanguageAdapter` obligation, not an `IRSource` one — a
324
- // language server is a long-lived child process and leaking one costs
325
- // hundreds of megabytes per repo. Sources with no processes to release
326
- // (the git reader) have no method and need none.
220
+ // `dispose` is a `LanguageAdapter` obligation, not `IRSource`'s — a leaked language
221
+ // server costs hundreds of MB per repo. Sources with none to release (git reader) need no method.
327
222
  await entry.adapter?.dispose();
328
223
  done += 1;
329
224
  }
@@ -359,13 +254,9 @@ async function run(args, ctx) {
359
254
  notes.push("Git history was skipped by request, so no CHANGES_WITH edges were produced and the blast " +
360
255
  "radius will miss files that historically change together.");
361
256
  }
362
- // Developer-confirmed incidents, re-projected on every analyze.
363
- //
364
- // The record lives in `.descry/config.json` because it is testimony that
365
- // exists nowhere else (DEC-223's storage decision); the graph holds a
366
- // projection. Re-emitting it here is what makes "a rebuild never loses it"
367
- // true rather than merely intended — a store is derived data and every
368
- // derived table is dropped on a schema bump.
257
+ // Developer-confirmed incidents, re-projected every analyze run. The record lives in
258
+ // `.descry/config.json` (testimony, DEC-223) — the graph only holds a derived projection,
259
+ // dropped on every schema bump, so re-emitting here is what keeps a rebuild from losing it.
369
260
  const confirmedIncidents = session.config.confirmedIncidents ?? [];
370
261
  if (confirmedIncidents.length > 0) {
371
262
  const projected = await createConfirmedIncidentSource({
@@ -424,11 +315,9 @@ async function run(args, ctx) {
424
315
  }
425
316
  // --- mint edges from confirmed facts --------------------------------------
426
317
  //
427
- // DEC-223's answer half, minting side (DEC-138) — sequenced after the base
428
- // graph above is already persisted, because a confirmed answer joins
429
- // against a real, stored `API_ENDPOINT` row: `EndpointLookup.node(id)` is
430
- // backed by the SQL reader, not by `built`'s in-memory result, so the
431
- // endpoint has to already exist as a row for the lookup to find it.
318
+ // DEC-223/DEC-138 minting, sequenced after the graph is persisted: a confirmed answer
319
+ // joins against a stored `API_ENDPOINT` row via the SQL-backed `EndpointLookup`, not
320
+ // `built`'s in-memory result, so the row must already exist for the lookup to find it.
432
321
  ctx.progress("Minting edges from confirmed facts", done, total);
433
322
  const provider = session.provider();
434
323
  const { refs: ledgerRefs } = provider.unresolvedRefs();
@@ -438,18 +327,9 @@ async function run(args, ctx) {
438
327
  ...(session.config.workspace === undefined ? {} : { workspace: session.config.workspace }),
439
328
  };
440
329
  const mintResult = mintEdgesFromConfirmedFacts(answered, provider, scope, MINT_PRODUCED_BY, root.commitSha);
441
- // The invalidation key (DEC-015: "producedBy + sourceFiles"). Scoped to
442
- // every file carrying an *askable* ("value-unknown") row this run, not
443
- // only the files a fact happened to answer. That distinction is what makes
444
- // a re-run after a changed or removed answer safe: the run id is derived
445
- // from (producedBy, repo, commitSha, sourceFiles), so a re-run at the same
446
- // commit with the same askable domain lands on the *same* run id, and the
447
- // writer's own self-clean pass (`writer.ts` rule 3) retires whatever this
448
- // run no longer supplies — the edge from a superseded answer does not
449
- // survive next to a new one. Scoping to the *answered* subset instead
450
- // would let a file drop out of `sourceFiles` the moment its one answer was
451
- // removed, and a stale edge for it would then never be found as a
452
- // prior-run to retire at all.
330
+ // Invalidation key (DEC-015: producedBy+sourceFiles), scoped to every *askable* file this
331
+ // run, not just answered ones — else a file dropping its one answer would vanish from
332
+ // sourceFiles and its stale edge could never be found by the writer's self-clean pass to retire.
453
333
  const mintFiles = [
454
334
  ...new Set(ledgerRefs.flatMap((ref) => ref.attrs?.["refusalClass"] === "value-unknown" && typeof ref.file === "string" ? [ref.file] : [])),
455
335
  ].sort();
@@ -499,8 +379,7 @@ async function run(args, ctx) {
499
379
  "changed) — the edges they previously grounded have been retired, not left to coexist with " +
500
380
  "whatever this run minted instead.");
501
381
  }
502
- // No code-reading source ran, so there is no level to report and nothing to
503
- // rest a claim on. R0 rather than a cheerful default.
382
+ // No code-reading source ran — R0, not a cheerful default.
504
383
  const floor = (codeResolutions.length === 0
505
384
  ? 0
506
385
  : Math.min(...codeResolutions));