@descryy/mcp 0.9.1 → 0.11.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.
- package/dist/browser/auth.d.ts +76 -0
- package/dist/browser/auth.d.ts.map +1 -0
- package/dist/browser/auth.js +223 -0
- package/dist/browser/auth.js.map +1 -0
- package/dist/browser/capability-probe.d.ts +109 -0
- package/dist/browser/capability-probe.d.ts.map +1 -0
- package/dist/browser/capability-probe.js +201 -0
- package/dist/browser/capability-probe.js.map +1 -0
- package/dist/browser/driver.d.ts +342 -4
- package/dist/browser/driver.d.ts.map +1 -1
- package/dist/browser/driver.js +79 -1
- package/dist/browser/driver.js.map +1 -1
- package/dist/browser/evidence.d.ts +3 -0
- package/dist/browser/evidence.d.ts.map +1 -1
- package/dist/browser/evidence.js +6 -1
- package/dist/browser/evidence.js.map +1 -1
- package/dist/browser/fake-driver.d.ts +65 -8
- package/dist/browser/fake-driver.d.ts.map +1 -1
- package/dist/browser/fake-driver.js +235 -8
- package/dist/browser/fake-driver.js.map +1 -1
- package/dist/browser/fault-attribution.d.ts +178 -0
- package/dist/browser/fault-attribution.d.ts.map +1 -0
- package/dist/browser/fault-attribution.js +266 -0
- package/dist/browser/fault-attribution.js.map +1 -0
- package/dist/browser/identity-graph-write.d.ts +55 -0
- package/dist/browser/identity-graph-write.d.ts.map +1 -0
- package/dist/browser/identity-graph-write.js +45 -0
- package/dist/browser/identity-graph-write.js.map +1 -0
- package/dist/browser/page-probe.d.ts +81 -0
- package/dist/browser/page-probe.d.ts.map +1 -0
- package/dist/browser/page-probe.js +202 -0
- package/dist/browser/page-probe.js.map +1 -0
- package/dist/browser/playwright-driver.d.ts +79 -0
- package/dist/browser/playwright-driver.d.ts.map +1 -1
- package/dist/browser/playwright-driver.js +1138 -180
- package/dist/browser/playwright-driver.js.map +1 -1
- package/dist/browser/reach-recording-session.d.ts +44 -0
- package/dist/browser/reach-recording-session.d.ts.map +1 -0
- package/dist/browser/reach-recording-session.js +143 -0
- package/dist/browser/reach-recording-session.js.map +1 -0
- package/dist/browser/reachability.d.ts +168 -0
- package/dist/browser/reachability.d.ts.map +1 -0
- package/dist/browser/reachability.js +294 -0
- package/dist/browser/reachability.js.map +1 -0
- package/dist/browser/registry.d.ts +24 -0
- package/dist/browser/registry.d.ts.map +1 -1
- package/dist/browser/registry.js +21 -2
- package/dist/browser/registry.js.map +1 -1
- package/dist/browser/scenario-provenance.d.ts +1 -1
- package/dist/browser/scenario-provenance.d.ts.map +1 -1
- package/dist/browser/scenario-provenance.js +15 -1
- package/dist/browser/scenario-provenance.js.map +1 -1
- package/dist/browser/scenario-runner.d.ts +10 -2
- package/dist/browser/scenario-runner.d.ts.map +1 -1
- package/dist/browser/scenario-runner.js +77 -9
- package/dist/browser/scenario-runner.js.map +1 -1
- package/dist/browser/tool-support.d.ts +5 -1
- package/dist/browser/tool-support.d.ts.map +1 -1
- package/dist/browser/tool-support.js +80 -4
- package/dist/browser/tool-support.js.map +1 -1
- package/dist/browser/wait-target.d.ts +37 -0
- package/dist/browser/wait-target.d.ts.map +1 -0
- package/dist/browser/wait-target.js +54 -0
- package/dist/browser/wait-target.js.map +1 -0
- package/dist/heap.d.ts +61 -0
- package/dist/heap.d.ts.map +1 -0
- package/dist/heap.js +82 -0
- package/dist/heap.js.map +1 -0
- package/dist/heartbeat.d.ts +46 -0
- package/dist/heartbeat.d.ts.map +1 -0
- package/dist/heartbeat.js +66 -0
- package/dist/heartbeat.js.map +1 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/planner/predict-then-propose.d.ts +101 -0
- package/dist/planner/predict-then-propose.d.ts.map +1 -0
- package/dist/planner/predict-then-propose.js +88 -0
- package/dist/planner/predict-then-propose.js.map +1 -0
- package/dist/planner/propose-journey.d.ts +60 -0
- package/dist/planner/propose-journey.d.ts.map +1 -0
- package/dist/planner/propose-journey.js +96 -0
- package/dist/planner/propose-journey.js.map +1 -0
- package/dist/registry.d.ts +10 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/registry.js +5 -1
- package/dist/registry.js.map +1 -1
- package/dist/render.d.ts +4 -4
- package/dist/render.js +3 -3
- package/dist/scenarios/browser-run-plan-projection.d.ts +143 -0
- package/dist/scenarios/browser-run-plan-projection.d.ts.map +1 -0
- package/dist/scenarios/browser-run-plan-projection.js +228 -0
- package/dist/scenarios/browser-run-plan-projection.js.map +1 -0
- package/dist/scenarios/credential-ref.d.ts +102 -0
- package/dist/scenarios/credential-ref.d.ts.map +1 -0
- package/dist/scenarios/credential-ref.js +148 -0
- package/dist/scenarios/credential-ref.js.map +1 -0
- package/dist/scenarios/index.d.ts +4 -1
- package/dist/scenarios/index.d.ts.map +1 -1
- package/dist/scenarios/index.js +4 -1
- package/dist/scenarios/index.js.map +1 -1
- package/dist/scenarios/parse.d.ts.map +1 -1
- package/dist/scenarios/parse.js +42 -9
- package/dist/scenarios/parse.js.map +1 -1
- package/dist/scenarios/scenario.d.ts +39 -5
- package/dist/scenarios/scenario.d.ts.map +1 -1
- package/dist/scenarios/scenario.js +15 -2
- package/dist/scenarios/scenario.js.map +1 -1
- package/dist/scenarios/secret-ref.d.ts +86 -0
- package/dist/scenarios/secret-ref.d.ts.map +1 -0
- package/dist/scenarios/secret-ref.js +125 -0
- package/dist/scenarios/secret-ref.js.map +1 -0
- package/dist/server.d.ts +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +8 -4
- package/dist/server.js.map +1 -1
- package/dist/session.d.ts +19 -0
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +36 -0
- package/dist/session.js.map +1 -1
- package/dist/tools/analyze-workspace.d.ts +52 -0
- package/dist/tools/analyze-workspace.d.ts.map +1 -0
- package/dist/tools/analyze-workspace.js +207 -0
- package/dist/tools/analyze-workspace.js.map +1 -0
- package/dist/tools/analyze.d.ts +50 -4
- package/dist/tools/analyze.d.ts.map +1 -1
- package/dist/tools/analyze.js +225 -5
- package/dist/tools/analyze.js.map +1 -1
- package/dist/tools/browser-close-session.d.ts +21 -1
- package/dist/tools/browser-close-session.d.ts.map +1 -1
- package/dist/tools/browser-close-session.js +71 -7
- package/dist/tools/browser-close-session.js.map +1 -1
- package/dist/tools/browser-navigate.d.ts +1 -1
- package/dist/tools/browser-navigate.d.ts.map +1 -1
- package/dist/tools/browser-navigate.js +5 -3
- package/dist/tools/browser-navigate.js.map +1 -1
- package/dist/tools/browser-run-scenario.d.ts.map +1 -1
- package/dist/tools/browser-run-scenario.js +6 -1
- package/dist/tools/browser-run-scenario.js.map +1 -1
- package/dist/tools/browser-save-scenario.d.ts.map +1 -1
- package/dist/tools/browser-save-scenario.js +84 -4
- package/dist/tools/browser-save-scenario.js.map +1 -1
- package/dist/tools/browser-select.d.ts +20 -0
- package/dist/tools/browser-select.d.ts.map +1 -0
- package/dist/tools/browser-select.js +120 -0
- package/dist/tools/browser-select.js.map +1 -0
- package/dist/tools/browser-snapshot.d.ts +17 -1
- package/dist/tools/browser-snapshot.d.ts.map +1 -1
- package/dist/tools/browser-snapshot.js +18 -5
- package/dist/tools/browser-snapshot.js.map +1 -1
- package/dist/tools/browser-start-session.d.ts.map +1 -1
- package/dist/tools/browser-start-session.js +161 -9
- package/dist/tools/browser-start-session.js.map +1 -1
- package/dist/tools/browser-submit.d.ts +18 -0
- package/dist/tools/browser-submit.d.ts.map +1 -0
- package/dist/tools/browser-submit.js +106 -0
- package/dist/tools/browser-submit.js.map +1 -0
- package/dist/tools/browser-wait-for.d.ts +41 -0
- package/dist/tools/browser-wait-for.d.ts.map +1 -0
- package/dist/tools/browser-wait-for.js +148 -0
- package/dist/tools/browser-wait-for.js.map +1 -0
- package/dist/tools/chain.d.ts +41 -0
- package/dist/tools/chain.d.ts.map +1 -0
- package/dist/tools/chain.js +105 -0
- package/dist/tools/chain.js.map +1 -0
- package/dist/tools/draw-conclusion.d.ts +44 -0
- package/dist/tools/draw-conclusion.d.ts.map +1 -0
- package/dist/tools/draw-conclusion.js +250 -0
- package/dist/tools/draw-conclusion.js.map +1 -0
- package/dist/tools/index.d.ts +7 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +16 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/kit.d.ts +17 -0
- package/dist/tools/kit.d.ts.map +1 -1
- package/dist/tools/kit.js.map +1 -1
- package/dist/tools/observe-runtime.d.ts +17 -0
- package/dist/tools/observe-runtime.d.ts.map +1 -1
- package/dist/tools/observe-runtime.js +174 -10
- package/dist/tools/observe-runtime.js.map +1 -1
- package/dist/tools/predict-reach.d.ts +57 -0
- package/dist/tools/predict-reach.d.ts.map +1 -0
- package/dist/tools/predict-reach.js +159 -0
- package/dist/tools/predict-reach.js.map +1 -0
- package/dist/tools/propagation.d.ts +8 -3
- package/dist/tools/propagation.d.ts.map +1 -1
- package/dist/tools/propagation.js +16 -9
- package/dist/tools/propagation.js.map +1 -1
- package/dist/tools/propose-journey.d.ts +16 -0
- package/dist/tools/propose-journey.d.ts.map +1 -0
- package/dist/tools/propose-journey.js +121 -0
- package/dist/tools/propose-journey.js.map +1 -0
- package/dist/tools/runtime-journey-drive.d.ts +102 -0
- package/dist/tools/runtime-journey-drive.d.ts.map +1 -0
- package/dist/tools/runtime-journey-drive.js +247 -0
- package/dist/tools/runtime-journey-drive.js.map +1 -0
- package/dist/tools/scope.d.ts +3 -0
- package/dist/tools/scope.d.ts.map +1 -1
- package/dist/tools/scope.js +33 -6
- package/dist/tools/scope.js.map +1 -1
- package/dist/workspace-index.d.ts +164 -0
- package/dist/workspace-index.d.ts.map +1 -0
- package/dist/workspace-index.js +381 -0
- package/dist/workspace-index.js.map +1 -0
- package/package.json +32 -31
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `analyze_workspace` — Gap 1 of
|
|
3
|
+
* `documents/plans/site-claims-closure-production-plan.md`: the cross-repo
|
|
4
|
+
* counterpart of `analyze`. Resolves the repository list from
|
|
5
|
+
* `session.config.workspaceRepos` (written by `link_workspace`, never
|
|
6
|
+
* inferred — same rule `link_workspace`'s own header states), migrates any
|
|
7
|
+
* existing per-repository graph aside per `migrateToWorkspaceGraph`
|
|
8
|
+
* (`@descryy/core`), and calls `indexWorkspace` to read every repository into
|
|
9
|
+
* one shared store.
|
|
10
|
+
*
|
|
11
|
+
* Deliberately a **new** tool rather than a mode on `analyze` — `analyze`'s
|
|
12
|
+
* contract is "read this one repository" and every one of its callers reads
|
|
13
|
+
* its reply as describing `session.repoPath` alone; overloading it would
|
|
14
|
+
* change that contract for all of them. `analyze_workspace` instead composes
|
|
15
|
+
* `analyze`'s own building blocks (via `indexWorkspace`) without touching
|
|
16
|
+
* `analyze.ts` at all.
|
|
17
|
+
*
|
|
18
|
+
* The shared graph's location is a disclosed design choice, not an inferred
|
|
19
|
+
* one — see `workspaceGraphPath`'s own doc in `@descryy/core` for why it
|
|
20
|
+
* lives under the *invoking* repository's `.descry/` directory rather than
|
|
21
|
+
* some central, app-wide location this package (unlike `descry-desktop`) has
|
|
22
|
+
* no concept of.
|
|
23
|
+
*/
|
|
24
|
+
import { join, resolve } from "node:path";
|
|
25
|
+
import { isGitRepository, migrateToWorkspaceGraph, workspaceGraphPath, } from "@descryy/core";
|
|
26
|
+
import { indexWorkspace } from "../workspace-index.js";
|
|
27
|
+
import { loadConfig } from "../session.js";
|
|
28
|
+
import { answer, optionalBoolean, optionalInteger } from "./kit.js";
|
|
29
|
+
const SCHEMA = {
|
|
30
|
+
type: "object",
|
|
31
|
+
properties: {
|
|
32
|
+
includeHistory: {
|
|
33
|
+
type: "boolean",
|
|
34
|
+
description: "Read each repository's git history for co-change coupling. Default true.",
|
|
35
|
+
},
|
|
36
|
+
maxCommits: {
|
|
37
|
+
type: "integer",
|
|
38
|
+
minimum: 1,
|
|
39
|
+
description: "Cap on commits read from each repository's history. Default 2000.",
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
additionalProperties: false,
|
|
43
|
+
};
|
|
44
|
+
/** Where `repository`'s own pre-workspace graph lives today, per its own config — `null` when that cannot be determined (config unreadable), which `migrateToWorkspaceGraph` treats as nothing to move for it. */
|
|
45
|
+
async function ownGraphPath(repository) {
|
|
46
|
+
try {
|
|
47
|
+
const config = await loadConfig(repository.path);
|
|
48
|
+
const graphPath = config.graph.startsWith("/") || /^[a-zA-Z]:[\\/]/.test(config.graph)
|
|
49
|
+
? config.graph
|
|
50
|
+
: join(repository.path, config.graph);
|
|
51
|
+
return { repo: repository.repo, graphPath };
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function run(args, ctx) {
|
|
58
|
+
const { session } = ctx;
|
|
59
|
+
const includeHistory = optionalBoolean(args, "includeHistory") ?? true;
|
|
60
|
+
const maxCommits = optionalInteger(args, "maxCommits", 1);
|
|
61
|
+
const workspace = session.config.workspace;
|
|
62
|
+
if (workspace === undefined) {
|
|
63
|
+
return answer({
|
|
64
|
+
headline: "No workspace is configured for this repository.",
|
|
65
|
+
state: "refused",
|
|
66
|
+
nameLevel: true,
|
|
67
|
+
resolutionFloor: 0,
|
|
68
|
+
commitSha: null,
|
|
69
|
+
graphBuiltAt: null,
|
|
70
|
+
irSchemaVersion: session.provider().baseStamp().irSchemaVersion,
|
|
71
|
+
commitSpread: session.provider().baseStamp().commitSpread,
|
|
72
|
+
repoCount: session.provider().baseStamp().repoCount,
|
|
73
|
+
notes: [
|
|
74
|
+
'Call "link_workspace" first, with the repositories the user has explicitly confirmed ' +
|
|
75
|
+
"belong together, then run analyze_workspace again.",
|
|
76
|
+
],
|
|
77
|
+
data: {
|
|
78
|
+
workspace: "",
|
|
79
|
+
graphPath: "",
|
|
80
|
+
repos: [],
|
|
81
|
+
stored: { nodes: 0, edges: 0, unresolvedRefs: 0 },
|
|
82
|
+
merge: { mergedNodes: 0, mergedEdges: 0, collisions: 0, droppedEdges: 0, rejections: 0 },
|
|
83
|
+
migration: { migrated: false, movedDatabases: [] },
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
const declaredRepos = session.config.workspaceRepos ?? [];
|
|
88
|
+
if (declaredRepos.length < 2) {
|
|
89
|
+
return answer({
|
|
90
|
+
headline: `Workspace "${workspace}" has fewer than two declared repositories — nothing to join.`,
|
|
91
|
+
state: "refused",
|
|
92
|
+
nameLevel: true,
|
|
93
|
+
resolutionFloor: 0,
|
|
94
|
+
commitSha: null,
|
|
95
|
+
graphBuiltAt: null,
|
|
96
|
+
irSchemaVersion: session.provider().baseStamp().irSchemaVersion,
|
|
97
|
+
commitSpread: session.provider().baseStamp().commitSpread,
|
|
98
|
+
repoCount: session.provider().baseStamp().repoCount,
|
|
99
|
+
notes: [
|
|
100
|
+
`session.config.workspaceRepos has ${declaredRepos.length} entr${declaredRepos.length === 1 ? "y" : "ies"}. ` +
|
|
101
|
+
'Re-run "link_workspace" naming every repository that belongs to this workspace, including ' +
|
|
102
|
+
"this one, then run analyze_workspace again.",
|
|
103
|
+
],
|
|
104
|
+
data: {
|
|
105
|
+
workspace,
|
|
106
|
+
graphPath: "",
|
|
107
|
+
repos: [],
|
|
108
|
+
stored: { nodes: 0, edges: 0, unresolvedRefs: 0 },
|
|
109
|
+
merge: { mergedNodes: 0, mergedEdges: 0, collisions: 0, droppedEdges: 0, rejections: 0 },
|
|
110
|
+
migration: { migrated: false, movedDatabases: [] },
|
|
111
|
+
},
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
// The shared graph lives under the *invoking* repository's own `.descry/`
|
|
115
|
+
// directory — a disclosed design choice, not an inferred one; see this
|
|
116
|
+
// module's own header and `workspaceGraphPath`'s doc in `@descryy/core`.
|
|
117
|
+
const rootDir = join(session.repoPath, ".descry");
|
|
118
|
+
const graphDbPath = workspaceGraphPath(rootDir, workspace);
|
|
119
|
+
const notes = [];
|
|
120
|
+
const locations = (await Promise.all(declaredRepos.map(ownGraphPath))).filter((location) => location !== null && location.graphPath !== graphDbPath);
|
|
121
|
+
const migration = migrateToWorkspaceGraph({ rootDir, workspaceId: workspace, repositories: locations });
|
|
122
|
+
if (migration.disclosure !== null)
|
|
123
|
+
notes.push(migration.disclosure);
|
|
124
|
+
const missingRepos = [];
|
|
125
|
+
const repoInputs = [];
|
|
126
|
+
for (const declared of declaredRepos) {
|
|
127
|
+
const absolute = resolve(declared.path);
|
|
128
|
+
if (!(await isGitRepository(absolute).catch(() => false))) {
|
|
129
|
+
// `indexWorkspace` itself would also refuse this repository and say so
|
|
130
|
+
// in its own per-repo notes — checked here too only so a repository
|
|
131
|
+
// that cannot even be reached at all is named before the run starts,
|
|
132
|
+
// not discovered only in a per-repo note buried in the reply.
|
|
133
|
+
missingRepos.push(declared.path);
|
|
134
|
+
}
|
|
135
|
+
repoInputs.push({ repoPath: absolute, repo: declared.repo });
|
|
136
|
+
}
|
|
137
|
+
if (missingRepos.length > 0) {
|
|
138
|
+
notes.push(`${missingRepos.length} declared repositor${missingRepos.length === 1 ? "y is" : "ies are"} not a ` +
|
|
139
|
+
`git repository at the declared path (${missingRepos.join(", ")}) — each will be reported as not ` +
|
|
140
|
+
"indexed rather than skipped silently.");
|
|
141
|
+
}
|
|
142
|
+
const result = await indexWorkspace({
|
|
143
|
+
workspaceId: workspace,
|
|
144
|
+
repos: repoInputs,
|
|
145
|
+
graphDbPath,
|
|
146
|
+
includeHistory,
|
|
147
|
+
...(maxCommits === undefined ? {} : { maxCommits }),
|
|
148
|
+
onProgress: (progress) => ctx.progress(`${progress.repo}: ${progress.phase}`, progress.completed, progress.total),
|
|
149
|
+
signal: ctx.signal,
|
|
150
|
+
});
|
|
151
|
+
for (const note of result.notes)
|
|
152
|
+
notes.push(note);
|
|
153
|
+
for (const repo of result.repos)
|
|
154
|
+
for (const note of repo.notes)
|
|
155
|
+
notes.push(`${repo.repo}: ${note}`);
|
|
156
|
+
const readRepos = result.repos.filter((r) => r.commitSha !== null).length;
|
|
157
|
+
const headline = readRepos === 0
|
|
158
|
+
? `Nothing was read for workspace "${workspace}" — none of its ${result.repos.length} declared ` +
|
|
159
|
+
"repositories could be indexed."
|
|
160
|
+
: `Analysed workspace "${workspace}": ${readRepos} of ${result.repos.length} repositories indexed ` +
|
|
161
|
+
`into one shared graph, which now holds ${result.stored.nodes} nodes, ${result.stored.edges} edges.`;
|
|
162
|
+
return answer({
|
|
163
|
+
headline,
|
|
164
|
+
state: readRepos === 0 ? "refused" : "ok",
|
|
165
|
+
nameLevel: true,
|
|
166
|
+
resolutionFloor: result.stamp.resolutionFloor,
|
|
167
|
+
// No single commit describes a workspace spanning several repositories —
|
|
168
|
+
// each repository's own commit is in `data.repos[].commitSha` instead.
|
|
169
|
+
commitSha: null,
|
|
170
|
+
graphBuiltAt: Date.now(),
|
|
171
|
+
irSchemaVersion: result.stamp.irSchemaVersion,
|
|
172
|
+
commitSpread: result.stamp.commitSpread,
|
|
173
|
+
repoCount: result.stamp.repoCount,
|
|
174
|
+
notes,
|
|
175
|
+
data: {
|
|
176
|
+
workspace,
|
|
177
|
+
graphPath: graphDbPath,
|
|
178
|
+
repos: result.repos,
|
|
179
|
+
stored: {
|
|
180
|
+
nodes: result.stored.nodes,
|
|
181
|
+
edges: result.stored.edges,
|
|
182
|
+
unresolvedRefs: result.stored.unresolvedRefs,
|
|
183
|
+
},
|
|
184
|
+
merge: result.merge,
|
|
185
|
+
migration: {
|
|
186
|
+
migrated: migration.migrated,
|
|
187
|
+
movedDatabases: migration.movedDatabases.map((m) => ({ repo: m.repo, from: m.from, to: m.to })),
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
export const analyzeWorkspaceTool = {
|
|
193
|
+
name: "analyze_workspace",
|
|
194
|
+
class: "stateful-read",
|
|
195
|
+
tier: "evidence",
|
|
196
|
+
version: "1.0.0",
|
|
197
|
+
title: "Analyse every repository in a workspace into one shared code graph",
|
|
198
|
+
description: 'Read every repository declared by "link_workspace" with its own configured adapters, and ' +
|
|
199
|
+
"store the merged result as one shared code graph — so a frontend's USES_API and a backend's " +
|
|
200
|
+
"SERVES_API can join on the same API_ENDPOINT id (DEC-054). Requires a workspace to already be " +
|
|
201
|
+
'linked; call "link_workspace" first if it is not. Moves any existing per-repository graph ' +
|
|
202
|
+
"aside (never deletes it) the first time a repository joins a shared graph, and re-indexes it " +
|
|
203
|
+
"into the shared store in the same call.",
|
|
204
|
+
inputSchema: SCHEMA,
|
|
205
|
+
run,
|
|
206
|
+
};
|
|
207
|
+
//# sourceMappingURL=analyze-workspace.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"analyze-workspace.js","sourceRoot":"","sources":["../../src/tools/analyze-workspace.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/analyze.d.ts
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
* reached (caps reliability, rule 3), id collisions (DEC-024), evicted files, and the
|
|
5
5
|
* cross-repo join count. No config → `repo` defaults to dir name, hashed into every id (DEC-004). */
|
|
6
6
|
import type { IRBatch, UnresolvedRef } from "@descryy/ir";
|
|
7
|
+
import { getCachedFiles } from "@descryy/core";
|
|
7
8
|
import type { BuildResult, JoinMetric, StoreCounts } from "@descryy/core";
|
|
8
9
|
import { type ToolDefinition } from "./kit.ts";
|
|
9
|
-
import { type LoadedSource } from "../registry.ts";
|
|
10
|
+
import { type DetectionEvidence, type LoadedSource } from "../registry.ts";
|
|
10
11
|
export { configDriftNote } from "../session.ts";
|
|
11
12
|
/** What one source read, and how well. `reachedResolution` is what rule 3 caps everything
|
|
12
13
|
* above it by — a run degraded to R0 looks identical to a healthy one until this is read. */
|
|
@@ -101,9 +102,48 @@ export interface AnalyzeData {
|
|
|
101
102
|
readonly evictedFiles: number;
|
|
102
103
|
};
|
|
103
104
|
}
|
|
104
|
-
/**
|
|
105
|
-
*
|
|
106
|
-
*
|
|
105
|
+
/**
|
|
106
|
+
* P6-B1 — the run stopped at the heap fence, before the phase that would have
|
|
107
|
+
* aborted the process.
|
|
108
|
+
*
|
|
109
|
+
* A distinct shape rather than a zeroed `AnalyzeData`, for the same reason
|
|
110
|
+
* `scope` has `ScopeNotSeeded`: a halted run has no source table, no merge
|
|
111
|
+
* counts and no stored totals, and reporting zeros for them would say the
|
|
112
|
+
* repository is empty rather than that Descry stopped looking. Narrow on
|
|
113
|
+
* `"halted" in data`.
|
|
114
|
+
*/
|
|
115
|
+
export interface AnalyzeHalted {
|
|
116
|
+
readonly halted: "heap";
|
|
117
|
+
/** The phase that was refused — what would have run next, not what failed. */
|
|
118
|
+
readonly phase: string;
|
|
119
|
+
readonly heapUsedMib: number;
|
|
120
|
+
readonly heapLimitMib: number;
|
|
121
|
+
/** Sources that had already emitted when the fence tripped. Read, then discarded unwritten. */
|
|
122
|
+
readonly sourcesRead: readonly SourceRun[];
|
|
123
|
+
}
|
|
124
|
+
export type AnalyzeResult = AnalyzeData | AnalyzeHalted;
|
|
125
|
+
/** Gap 6 step 3: attempts whole-producer cache reuse for one `LanguageAdapter`
|
|
126
|
+
* source, so a repeat `analyze` run can skip `emit()` (and the re-parse inside
|
|
127
|
+
* it) entirely when nothing this producer read has changed. Every failure path
|
|
128
|
+
* here returns `{ reused: false }` rather than throwing — declining to reuse is
|
|
129
|
+
* always safe, and the caller's normal `emit()` path is the fallback. See
|
|
130
|
+
* `planIncrementalReuse`'s doc (`@descryy/core`) for why this is whole-producer,
|
|
131
|
+
* never per-file. */
|
|
132
|
+
export declare function attemptIncrementalReuse(params: {
|
|
133
|
+
readonly driver: Parameters<typeof getCachedFiles>[0];
|
|
134
|
+
readonly producedBy: string;
|
|
135
|
+
readonly repo: string;
|
|
136
|
+
readonly repoPath: string;
|
|
137
|
+
readonly repoFileListing: readonly string[] | null;
|
|
138
|
+
readonly commitSha: string;
|
|
139
|
+
readonly workspace: string | undefined;
|
|
140
|
+
}): Promise<{
|
|
141
|
+
readonly reused: true;
|
|
142
|
+
readonly batch: IRBatch;
|
|
143
|
+
} | {
|
|
144
|
+
readonly reused: false;
|
|
145
|
+
readonly reason: string;
|
|
146
|
+
}>;
|
|
107
147
|
export declare function refusalSummaryField(unresolved: readonly UnresolvedRef[]): {
|
|
108
148
|
readonly count: number;
|
|
109
149
|
readonly exemplar: string;
|
|
@@ -113,5 +153,11 @@ export declare function refusalSummaryField(unresolved: readonly UnresolvedRef[]
|
|
|
113
153
|
* every join metric's `suppliedByTwoOrMoreRepos: 0` looks like `NO_CROSS_REPO_JOIN`'s
|
|
114
154
|
* failure case; this states outright it's just scope, not a failed join. */
|
|
115
155
|
export declare function crossRepoJoinNote(joins: readonly JoinMetric[]): string | undefined;
|
|
156
|
+
/** Turns one adapter's `DetectionResult.evidence` into the same kind of disclosed note the
|
|
157
|
+
* `skippedFiles` rollup below builds — grouped by `reason` where an entry is a
|
|
158
|
+
* `StructuredSkipEntry`, left under an "other" bucket, verbatim, where it is still the
|
|
159
|
+
* legacy plain-string shape (`isStructuredSkipEntry`, `@descryy/ir`). `null` on an empty
|
|
160
|
+
* list — nothing to disclose, not a note saying so. */
|
|
161
|
+
export declare function summariseDetectionEvidence(evidence: DetectionEvidence): string | null;
|
|
116
162
|
export declare const analyzeTool: ToolDefinition;
|
|
117
163
|
//# sourceMappingURL=analyze.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/tools/analyze.ts"],"names":[],"mappings":"AAAA;;;;sGAIsG;
|
|
1
|
+
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../../src/tools/analyze.ts"],"names":[],"mappings":"AAAA;;;;sGAIsG;AAMtG,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE1D,OAAO,EAUL,cAAc,EASf,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,WAAW,EAAmB,UAAU,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE3F,OAAO,EAA6D,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAI1G,OAAO,EAA8B,KAAK,iBAAiB,EAAE,KAAK,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAIvG,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAEhD;8FAC8F;AAC9F,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB;;;;;;kEAM8D;IAC9D,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B;0DACsD;IACtD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,0EAA0E;IAC1E,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAED;;sHAEsH;AACtH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,OAAO,GAAG;IAClD,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;IAC/C,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,YAAY,CAAC,CAAC;CAC5C,CAaA;AAED;;yFAEyF;AACzF;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAahE;AAED,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,OAAO,EACd,KAAK,EAAE,MAAM,GAAG,QAAQ,GACvB,MAAM,CASR;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,gFAAgF;IAChF,QAAQ,CAAC,OAAO,EAAE,SAAS,SAAS,EAAE,CAAC;IACvC,4EAA4E;IAC5E,QAAQ,CAAC,aAAa,EAAE,SAAS;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACxF,uEAAuE;IACvE,QAAQ,CAAC,cAAc,EAAE,SAAS;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACzF,8CAA8C;IAC9C,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE;QACd,8FAA8F;QAC9F,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;QAC7B,oGAAoG;QACpG,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAC5B,4EAA4E;QAC5E,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;QAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;KAC7B,CAAC;IACF,kGAAkG;IAClG,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAC,gBAAgB,CAAC,CAAC;IACvD,QAAQ,CAAC,OAAO,EAAE;QAChB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;QAC/B,sFAAsF;QACtF,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;KAC/B,CAAC;IACF,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,EAAE;QACf,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;QAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;KAC/B,CAAC;CACH;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,+FAA+F;IAC/F,QAAQ,CAAC,WAAW,EAAE,SAAS,SAAS,EAAE,CAAC;CAC5C;AAED,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,aAAa,CAAC;AA2CxD;;;;;;sBAMsB;AACtB,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,QAAQ,CAAC,MAAM,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;IACtD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,eAAe,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IACnD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAC;CACxC,GAAG,OAAO,CAAC;IAAE,QAAQ,CAAC,MAAM,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,QAAQ,CAAC,MAAM,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAyCpH;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,SAAS,aAAa,EAAE,GACnC;IAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CAI5F;AAED;;6EAE6E;AAC7E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,MAAM,GAAG,SAAS,CAUlF;AAED;;;;wDAIwD;AACxD,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,CAoBrF;AAwmBD,eAAO,MAAM,WAAW,EAAE,cAazB,CAAC"}
|
package/dist/tools/analyze.js
CHANGED
|
@@ -3,8 +3,14 @@
|
|
|
3
3
|
* didn't claim the repo (§20.2 — not-analysable ≠ clean), each source's resolution
|
|
4
4
|
* reached (caps reliability, rule 3), id collisions (DEC-024), evicted files, and the
|
|
5
5
|
* cross-repo join count. No config → `repo` defaults to dir name, hashed into every id (DEC-004). */
|
|
6
|
-
import {
|
|
6
|
+
import { createHash } from "node:crypto";
|
|
7
|
+
import { readFile } from "node:fs/promises";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { isStructuredSkipEntry } from "@descryy/ir";
|
|
10
|
+
import { applyConfirmedFacts, buildGraph, CONFIRMED_INCIDENT_SOURCE_ID, CONFIRMED_INCIDENT_SOURCE_VERSION, counts, createConfirmedIncidentSource, createGitSource, evictStaleConfirmedIncidentNodes, FILELESS_CACHE_KEY, getCachedFiles, listWorkingTreeFiles, mintEdgesFromConfirmedFacts, MINT_PRODUCED_BY, partitionBatchByFile, persistGraph, planIncrementalReuse, replaceCachedFiles, summariseRefusals, } from "@descryy/core";
|
|
7
11
|
import { answer, optionalBoolean, ToolInputError } from "./kit.js";
|
|
12
|
+
import { heapGuard, readHeap } from "../heap.js";
|
|
13
|
+
import { withHeartbeat } from "../heartbeat.js";
|
|
8
14
|
import { detectSources, loadSources } from "../registry.js";
|
|
9
15
|
// Re-exported for `analyze.test.ts`, which covers this pure helper in isolation.
|
|
10
16
|
// The check itself now runs centrally in `server.ts` (13.3.16 / N2 steps 2-3) —
|
|
@@ -90,6 +96,63 @@ const SCHEMA = {
|
|
|
90
96
|
/** §5.4's `refusals` field for this run only, not the graph's whole ledger
|
|
91
97
|
* (`refusal_fetch`'s job) — a fresh population per call, honestly scoped to what
|
|
92
98
|
* this run found. `undefined` on zero rows so `refusals` stays `null`, not an empty summary. */
|
|
99
|
+
async function hashFileContent(absolutePath) {
|
|
100
|
+
try {
|
|
101
|
+
return createHash("sha256").update(await readFile(absolutePath)).digest("hex");
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// Deleted, unreadable, or otherwise gone — left absent so the caller's
|
|
105
|
+
// hash lookup mismatches the cached value and correctly reports "changed".
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/** Gap 6 step 3: attempts whole-producer cache reuse for one `LanguageAdapter`
|
|
110
|
+
* source, so a repeat `analyze` run can skip `emit()` (and the re-parse inside
|
|
111
|
+
* it) entirely when nothing this producer read has changed. Every failure path
|
|
112
|
+
* here returns `{ reused: false }` rather than throwing — declining to reuse is
|
|
113
|
+
* always safe, and the caller's normal `emit()` path is the fallback. See
|
|
114
|
+
* `planIncrementalReuse`'s doc (`@descryy/core`) for why this is whole-producer,
|
|
115
|
+
* never per-file. */
|
|
116
|
+
export async function attemptIncrementalReuse(params) {
|
|
117
|
+
const cache = getCachedFiles(params.driver, params.producedBy, params.repo);
|
|
118
|
+
const cachedPaths = [...cache.files.keys()].filter((path) => path !== FILELESS_CACHE_KEY);
|
|
119
|
+
const cachedHashes = new Map(cachedPaths.map((path) => [path, cache.files.get(path).contentHash]));
|
|
120
|
+
const currentHashes = new Map();
|
|
121
|
+
for (const path of cachedPaths) {
|
|
122
|
+
const hash = await hashFileContent(join(params.repoPath, path));
|
|
123
|
+
if (hash !== null)
|
|
124
|
+
currentHashes.set(path, hash);
|
|
125
|
+
}
|
|
126
|
+
const decision = planIncrementalReuse({
|
|
127
|
+
cachedPaths,
|
|
128
|
+
repoPaths: params.repoFileListing,
|
|
129
|
+
currentHashes,
|
|
130
|
+
cachedHashes,
|
|
131
|
+
});
|
|
132
|
+
if (!decision.canReuse)
|
|
133
|
+
return { reused: false, reason: decision.reason };
|
|
134
|
+
const nodes = [];
|
|
135
|
+
const edges = [];
|
|
136
|
+
const unresolved = [];
|
|
137
|
+
for (const entry of cache.files.values()) {
|
|
138
|
+
nodes.push(...entry.nodes);
|
|
139
|
+
edges.push(...entry.edges);
|
|
140
|
+
unresolved.push(...entry.unresolved);
|
|
141
|
+
}
|
|
142
|
+
const batch = {
|
|
143
|
+
repo: params.repo,
|
|
144
|
+
...(params.workspace === undefined ? {} : { workspace: params.workspace }),
|
|
145
|
+
commitSha: params.commitSha,
|
|
146
|
+
producedBy: params.producedBy,
|
|
147
|
+
sourceFiles: cachedPaths,
|
|
148
|
+
skippedFiles: cache.skippedFiles,
|
|
149
|
+
reachedResolution: (cache.reachedResolution ?? 0),
|
|
150
|
+
nodes,
|
|
151
|
+
edges,
|
|
152
|
+
unresolved,
|
|
153
|
+
};
|
|
154
|
+
return { reused: true, batch };
|
|
155
|
+
}
|
|
93
156
|
export function refusalSummaryField(unresolved) {
|
|
94
157
|
const summary = summariseRefusals(unresolved);
|
|
95
158
|
if (summary.count === 0 || summary.exemplar === null || summary.handle === null)
|
|
@@ -109,6 +172,30 @@ export function crossRepoJoinNote(joins) {
|
|
|
109
172
|
"take for them; this is not the NO_CROSS_REPO_JOIN warning, which is for two or more " +
|
|
110
173
|
"repositories that failed to join.");
|
|
111
174
|
}
|
|
175
|
+
/** Turns one adapter's `DetectionResult.evidence` into the same kind of disclosed note the
|
|
176
|
+
* `skippedFiles` rollup below builds — grouped by `reason` where an entry is a
|
|
177
|
+
* `StructuredSkipEntry`, left under an "other" bucket, verbatim, where it is still the
|
|
178
|
+
* legacy plain-string shape (`isStructuredSkipEntry`, `@descryy/ir`). `null` on an empty
|
|
179
|
+
* list — nothing to disclose, not a note saying so. */
|
|
180
|
+
export function summariseDetectionEvidence(evidence) {
|
|
181
|
+
if (evidence.entries.length === 0)
|
|
182
|
+
return null;
|
|
183
|
+
const byReason = new Map();
|
|
184
|
+
const legacy = [];
|
|
185
|
+
for (const entry of evidence.entries) {
|
|
186
|
+
if (isStructuredSkipEntry(entry)) {
|
|
187
|
+
byReason.set(entry.reason, (byReason.get(entry.reason) ?? 0) + 1);
|
|
188
|
+
}
|
|
189
|
+
else {
|
|
190
|
+
legacy.push(entry);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const parts = [...byReason.entries()].map(([reason, count]) => `${count} ${reason}`);
|
|
194
|
+
if (legacy.length > 0)
|
|
195
|
+
parts.push(`${legacy.length} other (${legacy.join("; ")})`);
|
|
196
|
+
return (`${evidence.source} reported ${evidence.entries.length} detection note(s) for this ` +
|
|
197
|
+
`repository (${parts.join(", ")}).`);
|
|
198
|
+
}
|
|
112
199
|
async function run(args, ctx) {
|
|
113
200
|
const { session } = ctx;
|
|
114
201
|
const includeHistory = args["includeHistory"] !== false;
|
|
@@ -169,11 +256,16 @@ async function run(args, ctx) {
|
|
|
169
256
|
"specifiers. This engine does not know the name of any adapter: the IR boundary is a " +
|
|
170
257
|
"dependency direction, so the list has to come from configuration.");
|
|
171
258
|
}
|
|
172
|
-
const { applicable, skipped } = await detectSources(registry.loaded, root);
|
|
259
|
+
const { applicable, skipped, evidence } = await detectSources(registry.loaded, root);
|
|
173
260
|
for (const entry of skipped) {
|
|
174
261
|
notes.push(`${entry.source.source.id} did not run: it ${entry.reason}. Any code only that adapter ` +
|
|
175
262
|
"could read is not analysable.");
|
|
176
263
|
}
|
|
264
|
+
for (const item of evidence) {
|
|
265
|
+
const note = summariseDetectionEvidence(item);
|
|
266
|
+
if (note !== null)
|
|
267
|
+
notes.push(note);
|
|
268
|
+
}
|
|
177
269
|
// --- emit ----------------------------------------------------------------
|
|
178
270
|
const context = {
|
|
179
271
|
root,
|
|
@@ -188,14 +280,111 @@ async function run(args, ctx) {
|
|
|
188
280
|
const codeResolutions = [];
|
|
189
281
|
const total = applicable.length + (includeHistory ? 1 : 0);
|
|
190
282
|
let done = 0;
|
|
283
|
+
const driver = session.store().driver;
|
|
284
|
+
// Computed once for the whole run, not per source: every `LanguageAdapter`
|
|
285
|
+
// source's Gap 6 reuse check needs the same "what does the working tree have
|
|
286
|
+
// right now" answer, and it is meaningless to ask git for it twice. `null`
|
|
287
|
+
// when it cannot be determined at all (no git, or git unavailable) — every
|
|
288
|
+
// incremental check below then conservatively declines to reuse anything.
|
|
289
|
+
const repoFileListing = await listWorkingTreeFiles(session.repoPath).catch(() => null);
|
|
290
|
+
/**
|
|
291
|
+
* P6-B1. The fence, at the two points where this tool's memory actually
|
|
292
|
+
* peaks: before an adapter is asked to read the repository (its batch is
|
|
293
|
+
* retained for the whole run), and before the merge, which holds every
|
|
294
|
+
* batch and the built graph live at once. A V8 heap abort cannot be caught
|
|
295
|
+
* — this is the only place the failure can be turned into a reply.
|
|
296
|
+
*
|
|
297
|
+
* `sourcesRead` reports what had already emitted, because those batches are
|
|
298
|
+
* discarded unwritten: without it the reply would be silent about work that
|
|
299
|
+
* did happen and produced nothing.
|
|
300
|
+
*/
|
|
301
|
+
const halted = (phase) => {
|
|
302
|
+
const reading = readHeap();
|
|
303
|
+
const note = heapGuard(reading, phase);
|
|
304
|
+
if (note === null)
|
|
305
|
+
return null;
|
|
306
|
+
const base = session.provider().baseStamp();
|
|
307
|
+
return answer({
|
|
308
|
+
headline: `Analysis of ${session.config.repo} stopped at the heap ceiling before "${phase}". ` +
|
|
309
|
+
"Nothing was written to the graph.",
|
|
310
|
+
state: "failed",
|
|
311
|
+
nameLevel: true,
|
|
312
|
+
resolutionFloor: 0,
|
|
313
|
+
commitSha: root.commitSha,
|
|
314
|
+
graphBuiltAt: base.graphBuiltAt,
|
|
315
|
+
irSchemaVersion: base.irSchemaVersion,
|
|
316
|
+
commitSpread: base.commitSpread,
|
|
317
|
+
repoCount: base.repoCount,
|
|
318
|
+
notes: [...notes, note],
|
|
319
|
+
data: {
|
|
320
|
+
halted: "heap",
|
|
321
|
+
phase,
|
|
322
|
+
heapUsedMib: Math.round(reading.usedBytes / (1024 * 1024)),
|
|
323
|
+
heapLimitMib: Math.round(reading.limitBytes / (1024 * 1024)),
|
|
324
|
+
sourcesRead: perSource,
|
|
325
|
+
},
|
|
326
|
+
});
|
|
327
|
+
};
|
|
191
328
|
for (const entry of applicable) {
|
|
329
|
+
const fence = halted(`Reading ${entry.source.id}`);
|
|
330
|
+
if (fence !== null)
|
|
331
|
+
return fence;
|
|
192
332
|
ctx.progress(`Reading ${entry.source.id}`, done, total);
|
|
193
333
|
const started = Date.now();
|
|
334
|
+
// Gap 6: only `LanguageAdapter` sources pay the re-parse cost this exists
|
|
335
|
+
// to avoid — the git-history/confirmed-incidents/mint sources below are
|
|
336
|
+
// already cheap and deterministic and are out of scope for this cache.
|
|
337
|
+
if (entry.adapter !== null) {
|
|
338
|
+
const expectedProducedBy = `${entry.source.id}@${entry.source.version}`;
|
|
339
|
+
const reuse = await attemptIncrementalReuse({
|
|
340
|
+
driver,
|
|
341
|
+
producedBy: expectedProducedBy,
|
|
342
|
+
repo: session.config.repo,
|
|
343
|
+
repoPath: session.repoPath,
|
|
344
|
+
repoFileListing,
|
|
345
|
+
commitSha: root.commitSha,
|
|
346
|
+
workspace: session.config.workspace,
|
|
347
|
+
});
|
|
348
|
+
if (reuse.reused) {
|
|
349
|
+
const batch = reuse.batch;
|
|
350
|
+
batches.push(batch);
|
|
351
|
+
perSource.push({
|
|
352
|
+
source: batch.producedBy,
|
|
353
|
+
installedVersion: entry.packageVersion,
|
|
354
|
+
files: batch.sourceFiles.length,
|
|
355
|
+
nodes: batch.nodes.length,
|
|
356
|
+
edges: batch.edges.length,
|
|
357
|
+
unresolved: batch.unresolved.length,
|
|
358
|
+
skippedFiles: batch.skippedFiles.length,
|
|
359
|
+
reachedResolution: batch.reachedResolution,
|
|
360
|
+
ms: 0,
|
|
361
|
+
});
|
|
362
|
+
const ceiling = entry.adapter.capabilities().maxResolution;
|
|
363
|
+
codeResolutions.push(batch.reachedResolution);
|
|
364
|
+
notes.push(`${entry.source.id}: reused ${String(batch.sourceFiles.length)} cached file(s) from the ` +
|
|
365
|
+
"previous run, unchanged since — not re-parsed this run (Gap 6 incremental indexing). " +
|
|
366
|
+
"Cross-file effects (a change in one file altering another file's resolved edges) are " +
|
|
367
|
+
"not re-checked by this cache; see documents/plans/site-claims-closure-production-plan.md Gap 6.");
|
|
368
|
+
if (batch.reachedResolution < ceiling) {
|
|
369
|
+
notes.push(`${entry.source.id} reached R${String(batch.reachedResolution)} where it is capable of ` +
|
|
370
|
+
`R${String(ceiling)} (cached from its last real run). Findings resting on its edges are ` +
|
|
371
|
+
"capped accordingly, and shape-level checks (contract matching, field propagation) need R3.");
|
|
372
|
+
}
|
|
373
|
+
done += 1;
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
194
377
|
// Adapter's own work and Descry's handling of its output are separate phases with
|
|
195
378
|
// separate blame — folding them into one `try` let a `TypeError` here be blamed on the adapter.
|
|
196
379
|
let batch;
|
|
197
380
|
try {
|
|
198
|
-
|
|
381
|
+
// P6-B2. An adapter reading a large repository can hold this await for
|
|
382
|
+
// minutes, and until now that was the whole gap between two progress
|
|
383
|
+
// frames — long enough for the client's timeout to fire on a run that
|
|
384
|
+
// was working. The beat keeps the request visibly alive; it does not
|
|
385
|
+
// claim to know how far through the adapter is, because this side
|
|
386
|
+
// cannot see that and will not invent it.
|
|
387
|
+
batch = await withHeartbeat(`Reading ${entry.source.id}`, (message) => ctx.progress(message, done, total), () => entry.source.emit(context));
|
|
199
388
|
}
|
|
200
389
|
catch (error) {
|
|
201
390
|
notes.push(sourceFailureNote(entry.source.id, error, "emit"));
|
|
@@ -217,6 +406,29 @@ async function run(args, ctx) {
|
|
|
217
406
|
reachedResolution: batch.reachedResolution,
|
|
218
407
|
ms: Date.now() - started,
|
|
219
408
|
});
|
|
409
|
+
// Gap 6 step 3: this was a real parse (reuse would have `continue`d above),
|
|
410
|
+
// so refresh the per-file cache from its output — the only place the
|
|
411
|
+
// cache is ever written. A producer whose declared `id@version` drifted
|
|
412
|
+
// from its self-declared `producedBy` (see `sourceProvenanceNote`) simply
|
|
413
|
+
// caches under the real `producedBy`; the next run's reuse check looks
|
|
414
|
+
// for `${id}@${version}` and will not find a mismatched entry, which is
|
|
415
|
+
// the safe direction for this to fail (always falls back to full reparse).
|
|
416
|
+
try {
|
|
417
|
+
const partitioned = partitionBatchByFile(batch);
|
|
418
|
+
const cacheEntries = [];
|
|
419
|
+
for (const [filePath, contribution] of partitioned) {
|
|
420
|
+
const contentHash = filePath === FILELESS_CACHE_KEY
|
|
421
|
+
? "fileless"
|
|
422
|
+
: (await hashFileContent(join(session.repoPath, filePath))) ?? "unreadable";
|
|
423
|
+
cacheEntries.push({ filePath, contentHash, ...contribution });
|
|
424
|
+
}
|
|
425
|
+
replaceCachedFiles(driver, batch.producedBy, session.config.repo, cacheEntries, batch.reachedResolution, skippedFiles, Date.now());
|
|
426
|
+
}
|
|
427
|
+
catch {
|
|
428
|
+
// Caching is an optimisation, never a correctness requirement — a
|
|
429
|
+
// failure here must not fail the run or lose the batch already pushed
|
|
430
|
+
// above. Worst case: the next run finds no cache and reparses fully.
|
|
431
|
+
}
|
|
220
432
|
// Declared ceiling vs what this run reached — a gap is honest degradation (rule 7):
|
|
221
433
|
// the adapter can do better but something in this repo stopped it.
|
|
222
434
|
const ceiling = entry.adapter?.capabilities().maxResolution;
|
|
@@ -252,11 +464,14 @@ async function run(args, ctx) {
|
|
|
252
464
|
ctx.progress("Reading git history", done, total);
|
|
253
465
|
const historyStarted = Date.now();
|
|
254
466
|
try {
|
|
255
|
-
|
|
467
|
+
// P6-B2, same reason as the adapter read above: `maxCommits` defaults to
|
|
468
|
+
// 2000, and walking that much history on a large repository is its own
|
|
469
|
+
// multi-minute silence.
|
|
470
|
+
const history = await withHeartbeat("Reading git history", (message) => ctx.progress(message, done, total), () => createGitSource({
|
|
256
471
|
repo: root.repo,
|
|
257
472
|
cwd: session.repoPath,
|
|
258
473
|
...(typeof maxCommits === "number" ? { maxCommits } : {}),
|
|
259
|
-
}).emit(context);
|
|
474
|
+
}).emit(context));
|
|
260
475
|
batches.push(history);
|
|
261
476
|
perSource.push({
|
|
262
477
|
source: history.producedBy,
|
|
@@ -310,6 +525,11 @@ async function run(args, ctx) {
|
|
|
310
525
|
"an edge resting on one is capped at reliability class B (DEC-223).");
|
|
311
526
|
}
|
|
312
527
|
// --- build and store -----------------------------------------------------
|
|
528
|
+
// The real peak: every batch is live here and `buildGraph` allocates the
|
|
529
|
+
// merged graph beside them. This is the checkpoint the field report needed.
|
|
530
|
+
const mergeFence = halted("Merging into the graph");
|
|
531
|
+
if (mergeFence !== null)
|
|
532
|
+
return mergeFence;
|
|
313
533
|
ctx.progress("Merging into the graph", done, total);
|
|
314
534
|
const built = buildGraph(batches, {
|
|
315
535
|
nodeExists: (id) => session.provider().node(id) !== undefined,
|