@skillcap/gdh 0.25.5 → 0.26.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.
- package/INSTALL-BUNDLE.json +1 -1
- package/RELEASE-SPAN-UPDATE-CONTRACTS.json +121 -0
- package/node_modules/@gdh/adapters/dist/claude-settings-patch.d.ts.map +1 -1
- package/node_modules/@gdh/adapters/dist/claude-settings-patch.js +38 -15
- package/node_modules/@gdh/adapters/dist/claude-settings-patch.js.map +1 -1
- package/node_modules/@gdh/adapters/dist/index.d.ts +12 -0
- package/node_modules/@gdh/adapters/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/adapters/dist/index.js +21 -0
- package/node_modules/@gdh/adapters/dist/index.js.map +1 -1
- package/node_modules/@gdh/adapters/dist/skill-rendering.d.ts +5 -2
- package/node_modules/@gdh/adapters/dist/skill-rendering.d.ts.map +1 -1
- package/node_modules/@gdh/adapters/dist/skill-rendering.js +39 -0
- package/node_modules/@gdh/adapters/dist/skill-rendering.js.map +1 -1
- package/node_modules/@gdh/adapters/dist/templates/authoring-hook.js.tpl +196 -5
- package/node_modules/@gdh/adapters/package.json +8 -8
- package/node_modules/@gdh/authoring/dist/diagnostics-broker-contract.d.ts +1 -0
- package/node_modules/@gdh/authoring/dist/diagnostics-broker-contract.d.ts.map +1 -1
- package/node_modules/@gdh/authoring/dist/diagnostics-broker-contract.js +1 -0
- package/node_modules/@gdh/authoring/dist/diagnostics-broker-contract.js.map +1 -1
- package/node_modules/@gdh/authoring/dist/diagnostics-broker.d.ts +2 -1
- package/node_modules/@gdh/authoring/dist/diagnostics-broker.d.ts.map +1 -1
- package/node_modules/@gdh/authoring/dist/diagnostics-broker.js +90 -11
- package/node_modules/@gdh/authoring/dist/diagnostics-broker.js.map +1 -1
- package/node_modules/@gdh/authoring/dist/index.d.ts +1 -1
- package/node_modules/@gdh/authoring/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/authoring/dist/index.js +1 -1
- package/node_modules/@gdh/authoring/dist/index.js.map +1 -1
- package/node_modules/@gdh/authoring/dist/lsp-warmup.d.ts +30 -0
- package/node_modules/@gdh/authoring/dist/lsp-warmup.d.ts.map +1 -0
- package/node_modules/@gdh/authoring/dist/lsp-warmup.js +213 -0
- package/node_modules/@gdh/authoring/dist/lsp-warmup.js.map +1 -0
- package/node_modules/@gdh/authoring/dist/lsp.d.ts +7 -1
- package/node_modules/@gdh/authoring/dist/lsp.d.ts.map +1 -1
- package/node_modules/@gdh/authoring/dist/lsp.js +223 -121
- package/node_modules/@gdh/authoring/dist/lsp.js.map +1 -1
- package/node_modules/@gdh/authoring/package.json +2 -2
- package/node_modules/@gdh/cli/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/cli/dist/index.js +73 -8
- package/node_modules/@gdh/cli/dist/index.js.map +1 -1
- package/node_modules/@gdh/cli/package.json +10 -10
- package/node_modules/@gdh/core/dist/index.d.ts +177 -5
- package/node_modules/@gdh/core/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/core/dist/index.js +43 -3
- package/node_modules/@gdh/core/dist/index.js.map +1 -1
- package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.d.ts +15 -0
- package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.d.ts.map +1 -1
- package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.js +18 -0
- package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.js.map +1 -1
- package/node_modules/@gdh/core/dist/migrations/managed-target-surface-inventory.d.ts.map +1 -1
- package/node_modules/@gdh/core/dist/migrations/managed-target-surface-inventory.js +2 -0
- package/node_modules/@gdh/core/dist/migrations/managed-target-surface-inventory.js.map +1 -1
- package/node_modules/@gdh/core/package.json +1 -1
- package/node_modules/@gdh/docs/package.json +2 -2
- package/node_modules/@gdh/mcp/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/mcp/dist/index.js +30 -1
- package/node_modules/@gdh/mcp/dist/index.js.map +1 -1
- package/node_modules/@gdh/mcp/package.json +8 -8
- package/node_modules/@gdh/observability/package.json +2 -2
- package/node_modules/@gdh/runtime/package.json +2 -2
- package/node_modules/@gdh/scan/package.json +3 -3
- package/node_modules/@gdh/verify/package.json +7 -7
- package/package.json +11 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lsp.d.ts","sourceRoot":"","sources":["../src/lsp.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EACV,uBAAuB,EAEvB,0BAA0B,EAC1B,wBAAwB,EAExB,kBAAkB,EAKlB,yBAAyB,EACzB,gBAAgB,
|
|
1
|
+
{"version":3,"file":"lsp.d.ts","sourceRoot":"","sources":["../src/lsp.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,7 @@ import fs from "node:fs/promises";
|
|
|
5
5
|
import net from "node:net";
|
|
6
6
|
import path from "node:path";
|
|
7
7
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
8
|
-
import { GDH_MANAGED_LSP_SURFACE_VERSION, resolveConfiguredGodotEditorBin } from "@gdh/core";
|
|
8
|
+
import { GDH_MANAGED_LSP_MIN_GODOT_VERSION, GDH_MANAGED_LSP_SURFACE_VERSION, resolveConfiguredGodotEditorBin, } from "@gdh/core";
|
|
9
9
|
import { connectGodotLspClient, GdhLspClientError, } from "./lsp-client.js";
|
|
10
10
|
import { summarizeBlockingDiagnostics } from "./diagnostics-summary.js";
|
|
11
11
|
import { classifyDiagnosticsFreshness, DIAGNOSTICS_BROKER_DEFAULT_FRESHNESS_STALE_AFTER_MS, resolveDiagnosticsBrokerPaths, } from "./diagnostics-broker-contract.js";
|
|
@@ -122,149 +122,190 @@ export async function getManagedLspStatus(input) {
|
|
|
122
122
|
return unavailable;
|
|
123
123
|
}
|
|
124
124
|
try {
|
|
125
|
-
return await withManagedLspStateLock(worktree, input.owner ?? "gdh lsp status", async (stateLock) =>
|
|
126
|
-
let cleanedStaleInstance = false;
|
|
127
|
-
let cleanupReasons = [];
|
|
125
|
+
return await withManagedLspStateLock(worktree, input.owner ?? "gdh lsp status", async (stateLock) => {
|
|
128
126
|
const projectPath = path.resolve(input.targetPath, input.status.primaryProjectPath ?? ".");
|
|
129
127
|
const expectedIdentity = await resolveCurrentManagedLspIdentity(worktree, projectPath);
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
128
|
+
// Phase 81 / LSP-08 / D-17: refuse-to-launch when the configured
|
|
129
|
+
// Godot version is below the managed-LSP floor. Short-circuits
|
|
130
|
+
// BEFORE lsp-leases.json acquisition (withLease) and BEFORE
|
|
131
|
+
// persisted-instance reads — but executes inside the lsp.lock
|
|
132
|
+
// (stateLock) so identity resolution is serialized with other
|
|
133
|
+
// lifecycle mutations. Misconfigured 4.5.0/4.5.1 setups never
|
|
134
|
+
// acquire a lease or block other waiters.
|
|
135
|
+
if (expectedIdentity.editorVersionReason === "godot_editor_version_unsupported_for_lsp") {
|
|
136
|
+
return createUnavailableStatus({
|
|
137
|
+
targetPath: input.targetPath,
|
|
138
|
+
readiness: input.status.readiness,
|
|
139
|
+
worktree,
|
|
140
|
+
inventoryObservedAt: input.status.inventoryObservedAt,
|
|
141
|
+
projectConfigPresent: input.projectConfig !== null,
|
|
142
|
+
availability: "unavailable",
|
|
143
|
+
reasons: dedupe([
|
|
144
|
+
...stateLock.recoveredReasons,
|
|
145
|
+
"godot_editor_version_unsupported_for_lsp",
|
|
146
|
+
]),
|
|
147
|
+
summary: "The configured Godot editor version does not meet the minimum version required for the managed authoring.lsp. Upgrade to Godot 4.5.2 or later.",
|
|
148
|
+
versionFloorAdvisory: {
|
|
149
|
+
detectedVersion: expectedIdentity.editorVersion,
|
|
150
|
+
minimumVersion: GDH_MANAGED_LSP_MIN_GODOT_VERSION,
|
|
151
|
+
recommendedVersions: ["4.5.2", "4.6.x"],
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
return await withLease(worktree, input.owner ?? "gdh lsp status", async (lease, activeLeaseCount) => {
|
|
156
|
+
let cleanedStaleInstance = false;
|
|
157
|
+
let cleanupReasons = [];
|
|
158
|
+
const existing = await readPersistedInstance(worktree);
|
|
159
|
+
if (existing !== null) {
|
|
160
|
+
const validation = await validatePersistedInstance(existing.instance, expectedIdentity);
|
|
161
|
+
if (validation.reusable) {
|
|
162
|
+
const reusedInstance = {
|
|
163
|
+
...existing.instance,
|
|
164
|
+
lastValidatedAt: new Date().toISOString(),
|
|
165
|
+
trust: validation.trust,
|
|
166
|
+
};
|
|
167
|
+
await persistInstance(worktree, {
|
|
168
|
+
...existing,
|
|
169
|
+
instance: reusedInstance,
|
|
170
|
+
rawPayload: validation.rawPayload ?? existing.rawPayload,
|
|
171
|
+
});
|
|
172
|
+
return createStatusResult({
|
|
173
|
+
targetPath: input.targetPath,
|
|
174
|
+
readiness: input.status.readiness,
|
|
175
|
+
availability: "available",
|
|
176
|
+
status: validation.trust === "trusted" ? "ready" : "degraded",
|
|
177
|
+
validationMode: existing.validationMode,
|
|
178
|
+
summary: validation.trust === "trusted"
|
|
179
|
+
? "Reused the managed authoring.lsp instance for this worktree."
|
|
180
|
+
: "Reused the managed authoring.lsp instance, but trust is degraded and the instance should be treated cautiously.",
|
|
181
|
+
reasons: dedupe([
|
|
182
|
+
...stateLock.recoveredReasons,
|
|
183
|
+
...(validation.trust === "trusted" ? [] : ["lsp_instance_degraded"]),
|
|
184
|
+
]),
|
|
185
|
+
worktree,
|
|
186
|
+
lease,
|
|
187
|
+
activeLeaseCount,
|
|
188
|
+
reusedInstance: true,
|
|
189
|
+
cleanedStaleInstance,
|
|
190
|
+
instance: reusedInstance,
|
|
191
|
+
inventoryObservedAt: input.status.inventoryObservedAt,
|
|
192
|
+
projectConfigPresent: input.projectConfig !== null,
|
|
193
|
+
rawPayload: validation.rawPayload ?? existing.rawPayload,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
cleanedStaleInstance = true;
|
|
197
|
+
cleanupReasons = validation.reasons;
|
|
198
|
+
await cleanupPersistedInstance(worktree, existing.instance, validation.reason, {
|
|
199
|
+
expectedIdentity,
|
|
143
200
|
});
|
|
144
|
-
|
|
201
|
+
// Phase 81 / LSP-04 / D-11 / Pitfall 7: when the LSP instance
|
|
202
|
+
// identity has changed, the broker snapshot referenced the
|
|
203
|
+
// now-defunct instance. Prune so the next read returns
|
|
204
|
+
// broker_metadata_missing instead of lsp_instance_identity_mismatch.
|
|
205
|
+
{
|
|
206
|
+
const { pruneAuthoringDiagnostics } = await import("./diagnostics-broker.js");
|
|
207
|
+
await pruneAuthoringDiagnostics({
|
|
208
|
+
targetPath: input.targetPath,
|
|
209
|
+
status: input.status,
|
|
210
|
+
projectConfig: input.projectConfig,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (input.launchPolicy === "reuse_existing") {
|
|
215
|
+
return createUnavailableStatus({
|
|
145
216
|
targetPath: input.targetPath,
|
|
146
217
|
readiness: input.status.readiness,
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
? "Reused the managed authoring.lsp instance for this worktree."
|
|
152
|
-
: "Reused the managed authoring.lsp instance, but trust is degraded and the instance should be treated cautiously.",
|
|
218
|
+
worktree,
|
|
219
|
+
inventoryObservedAt: input.status.inventoryObservedAt,
|
|
220
|
+
projectConfigPresent: input.projectConfig !== null,
|
|
221
|
+
availability: lspCapability?.availability ?? "available",
|
|
153
222
|
reasons: dedupe([
|
|
154
223
|
...stateLock.recoveredReasons,
|
|
155
|
-
...(
|
|
224
|
+
...(cleanedStaleInstance ? ["stale_lsp_instance_cleaned"] : []),
|
|
225
|
+
...cleanupReasons,
|
|
226
|
+
"lsp_instance_not_running",
|
|
227
|
+
"post_edit_fast_path_did_not_launch_lsp",
|
|
156
228
|
]),
|
|
157
|
-
|
|
229
|
+
summary: "No reusable managed authoring.lsp instance is currently running; post-edit fast check did not launch Godot.",
|
|
158
230
|
lease,
|
|
159
231
|
activeLeaseCount,
|
|
160
|
-
reusedInstance: true,
|
|
161
232
|
cleanedStaleInstance,
|
|
162
|
-
|
|
233
|
+
rawPayload: {
|
|
234
|
+
launchPolicy: "reuse_existing",
|
|
235
|
+
projectPath,
|
|
236
|
+
cleanedStaleInstance,
|
|
237
|
+
cleanupReasons,
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
const launchResult = await launchManagedInstance(worktree, projectPath);
|
|
242
|
+
if ("unavailable" in launchResult) {
|
|
243
|
+
return createUnavailableStatus({
|
|
244
|
+
targetPath: input.targetPath,
|
|
245
|
+
readiness: input.status.readiness,
|
|
246
|
+
worktree,
|
|
163
247
|
inventoryObservedAt: input.status.inventoryObservedAt,
|
|
164
248
|
projectConfigPresent: input.projectConfig !== null,
|
|
165
|
-
|
|
249
|
+
availability: "unavailable",
|
|
250
|
+
reasons: [
|
|
251
|
+
...stateLock.recoveredReasons,
|
|
252
|
+
...(cleanedStaleInstance ? ["stale_lsp_instance_cleaned"] : []),
|
|
253
|
+
...cleanupReasons,
|
|
254
|
+
...launchResult.unavailable.reasons,
|
|
255
|
+
],
|
|
256
|
+
summary: launchResult.unavailable.summary,
|
|
257
|
+
lease,
|
|
258
|
+
activeLeaseCount,
|
|
259
|
+
cleanedStaleInstance,
|
|
260
|
+
rawPayload: launchResult.unavailable.rawPayload,
|
|
166
261
|
});
|
|
167
262
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
263
|
+
const launchedInstance = {
|
|
264
|
+
instanceId: launchResult.instanceId,
|
|
265
|
+
launcher: launchResult.launcher,
|
|
266
|
+
port: launchResult.port,
|
|
267
|
+
pid: launchResult.pid,
|
|
268
|
+
projectPath,
|
|
269
|
+
command: launchResult.command,
|
|
270
|
+
trust: launchResult.trust,
|
|
271
|
+
launchedAt: new Date().toISOString(),
|
|
272
|
+
lastValidatedAt: new Date().toISOString(),
|
|
273
|
+
identity: launchResult.identity,
|
|
274
|
+
};
|
|
275
|
+
await persistInstance(worktree, {
|
|
276
|
+
schemaVersion: LSP_INSTANCE_SCHEMA_VERSION,
|
|
277
|
+
instance: launchedInstance,
|
|
278
|
+
validationMode: launchResult.validationMode,
|
|
279
|
+
diagnostics: launchResult.diagnostics,
|
|
280
|
+
rawPayload: launchResult.rawPayload,
|
|
172
281
|
});
|
|
173
|
-
|
|
174
|
-
if (input.launchPolicy === "reuse_existing") {
|
|
175
|
-
return createUnavailableStatus({
|
|
282
|
+
return createStatusResult({
|
|
176
283
|
targetPath: input.targetPath,
|
|
177
284
|
readiness: input.status.readiness,
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
285
|
+
availability: "available",
|
|
286
|
+
status: launchResult.trust === "trusted" ? "ready" : "degraded",
|
|
287
|
+
validationMode: launchResult.validationMode,
|
|
288
|
+
summary: launchResult.trust === "trusted"
|
|
289
|
+
? "Started a managed authoring.lsp instance for this worktree."
|
|
290
|
+
: "Started a managed authoring.lsp instance, but trust is degraded and validation should be treated cautiously.",
|
|
182
291
|
reasons: dedupe([
|
|
183
292
|
...stateLock.recoveredReasons,
|
|
184
293
|
...(cleanedStaleInstance ? ["stale_lsp_instance_cleaned"] : []),
|
|
185
294
|
...cleanupReasons,
|
|
186
|
-
"
|
|
187
|
-
"post_edit_fast_path_did_not_launch_lsp",
|
|
295
|
+
...(launchResult.trust === "trusted" ? [] : ["lsp_instance_degraded"]),
|
|
188
296
|
]),
|
|
189
|
-
|
|
297
|
+
worktree,
|
|
190
298
|
lease,
|
|
191
299
|
activeLeaseCount,
|
|
300
|
+
reusedInstance: false,
|
|
192
301
|
cleanedStaleInstance,
|
|
193
|
-
|
|
194
|
-
launchPolicy: "reuse_existing",
|
|
195
|
-
projectPath,
|
|
196
|
-
cleanedStaleInstance,
|
|
197
|
-
cleanupReasons,
|
|
198
|
-
},
|
|
199
|
-
});
|
|
200
|
-
}
|
|
201
|
-
const launchResult = await launchManagedInstance(worktree, projectPath);
|
|
202
|
-
if ("unavailable" in launchResult) {
|
|
203
|
-
return createUnavailableStatus({
|
|
204
|
-
targetPath: input.targetPath,
|
|
205
|
-
readiness: input.status.readiness,
|
|
206
|
-
worktree,
|
|
302
|
+
instance: launchedInstance,
|
|
207
303
|
inventoryObservedAt: input.status.inventoryObservedAt,
|
|
208
304
|
projectConfigPresent: input.projectConfig !== null,
|
|
209
|
-
|
|
210
|
-
reasons: [
|
|
211
|
-
...stateLock.recoveredReasons,
|
|
212
|
-
...(cleanedStaleInstance ? ["stale_lsp_instance_cleaned"] : []),
|
|
213
|
-
...cleanupReasons,
|
|
214
|
-
...launchResult.unavailable.reasons,
|
|
215
|
-
],
|
|
216
|
-
summary: launchResult.unavailable.summary,
|
|
217
|
-
lease,
|
|
218
|
-
activeLeaseCount,
|
|
219
|
-
cleanedStaleInstance,
|
|
220
|
-
rawPayload: launchResult.unavailable.rawPayload,
|
|
305
|
+
rawPayload: launchResult.rawPayload,
|
|
221
306
|
});
|
|
222
|
-
}
|
|
223
|
-
const launchedInstance = {
|
|
224
|
-
instanceId: launchResult.instanceId,
|
|
225
|
-
launcher: launchResult.launcher,
|
|
226
|
-
port: launchResult.port,
|
|
227
|
-
pid: launchResult.pid,
|
|
228
|
-
projectPath,
|
|
229
|
-
command: launchResult.command,
|
|
230
|
-
trust: launchResult.trust,
|
|
231
|
-
launchedAt: new Date().toISOString(),
|
|
232
|
-
lastValidatedAt: new Date().toISOString(),
|
|
233
|
-
identity: launchResult.identity,
|
|
234
|
-
};
|
|
235
|
-
await persistInstance(worktree, {
|
|
236
|
-
schemaVersion: LSP_INSTANCE_SCHEMA_VERSION,
|
|
237
|
-
instance: launchedInstance,
|
|
238
|
-
validationMode: launchResult.validationMode,
|
|
239
|
-
diagnostics: launchResult.diagnostics,
|
|
240
|
-
rawPayload: launchResult.rawPayload,
|
|
241
307
|
});
|
|
242
|
-
|
|
243
|
-
targetPath: input.targetPath,
|
|
244
|
-
readiness: input.status.readiness,
|
|
245
|
-
availability: "available",
|
|
246
|
-
status: launchResult.trust === "trusted" ? "ready" : "degraded",
|
|
247
|
-
validationMode: launchResult.validationMode,
|
|
248
|
-
summary: launchResult.trust === "trusted"
|
|
249
|
-
? "Started a managed authoring.lsp instance for this worktree."
|
|
250
|
-
: "Started a managed authoring.lsp instance, but trust is degraded and validation should be treated cautiously.",
|
|
251
|
-
reasons: dedupe([
|
|
252
|
-
...stateLock.recoveredReasons,
|
|
253
|
-
...(cleanedStaleInstance ? ["stale_lsp_instance_cleaned"] : []),
|
|
254
|
-
...cleanupReasons,
|
|
255
|
-
...(launchResult.trust === "trusted" ? [] : ["lsp_instance_degraded"]),
|
|
256
|
-
]),
|
|
257
|
-
worktree,
|
|
258
|
-
lease,
|
|
259
|
-
activeLeaseCount,
|
|
260
|
-
reusedInstance: false,
|
|
261
|
-
cleanedStaleInstance,
|
|
262
|
-
instance: launchedInstance,
|
|
263
|
-
inventoryObservedAt: input.status.inventoryObservedAt,
|
|
264
|
-
projectConfigPresent: input.projectConfig !== null,
|
|
265
|
-
rawPayload: launchResult.rawPayload,
|
|
266
|
-
});
|
|
267
|
-
}));
|
|
308
|
+
});
|
|
268
309
|
}
|
|
269
310
|
catch (error) {
|
|
270
311
|
if (error instanceof GdhManagedLspStateLockError) {
|
|
@@ -282,6 +323,36 @@ export async function getManagedLspStatus(input) {
|
|
|
282
323
|
throw error;
|
|
283
324
|
}
|
|
284
325
|
}
|
|
326
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
327
|
+
// Phase 82 / LSP-02 / LSP-07 — managed-LSP warmup surface.
|
|
328
|
+
//
|
|
329
|
+
// `warmupManagedLsp` is the in-process entry point that the post-edit hook
|
|
330
|
+
// detach-spawns (via `gdh lsp warmup`) and the MCP `authoring.warmup` tool
|
|
331
|
+
// invokes. It is idempotent: concurrent callers fan in via a single-attempt
|
|
332
|
+
// `<targetPath>/.gdh-state/lsp.lock` acquire so the underlying Godot launch
|
|
333
|
+
// happens at most once.
|
|
334
|
+
//
|
|
335
|
+
// The implementation lives in `./lsp-warmup.js`. That sibling module imports
|
|
336
|
+
// `getManagedLspStatus` from `./lsp.js`, which is the documented vitest
|
|
337
|
+
// workaround for the "vi.mock cannot intercept same-module internal calls"
|
|
338
|
+
// pitfall (https://vitest.dev/guide/mocking/modules — Internal Method Calls).
|
|
339
|
+
//
|
|
340
|
+
// IMPORTANT: this is a thin wrapper that DEFERS to the impl via dynamic
|
|
341
|
+
// `await import("./lsp-warmup.js")`. The deferral guarantees `lsp-warmup.ts`
|
|
342
|
+
// is NOT a static dependency of `lsp.ts`. Were it static (a re-export), it
|
|
343
|
+
// would be evaluated as part of `./lsp.js`'s module graph, with its
|
|
344
|
+
// `import { getManagedLspStatus } from "./lsp.js"` resolving against the
|
|
345
|
+
// partially-evaluated original module record — so the unit-test mock
|
|
346
|
+
// (`vi.mock("./lsp.js", ...)`) would never reach the warmup impl. By
|
|
347
|
+
// keeping it deferred, the test's `await import("./lsp-warmup.js")` is
|
|
348
|
+
// the FIRST point at which lsp-warmup.ts loads, and at that time the mock
|
|
349
|
+
// is already registered, so the `./lsp.js` import resolves to the mocked
|
|
350
|
+
// module. See `./lsp-warmup.ts` and `./lsp-warmup.test.ts` for context.
|
|
351
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
352
|
+
export async function warmupManagedLsp(input) {
|
|
353
|
+
const { warmupManagedLsp: impl } = await import("./lsp-warmup.js");
|
|
354
|
+
return impl(input);
|
|
355
|
+
}
|
|
285
356
|
export async function checkManagedLsp(input) {
|
|
286
357
|
const worktree = resolveWorktreeIdentity(input.targetPath, input.status, input.projectConfig);
|
|
287
358
|
return await withManagedLspLifecycleLock(worktree, "check", "gdh lsp check", async (stateLock) => {
|
|
@@ -404,6 +475,19 @@ export async function stopManagedLsp(input) {
|
|
|
404
475
|
killProcess: true,
|
|
405
476
|
expectedIdentity,
|
|
406
477
|
});
|
|
478
|
+
// Phase 81 / LSP-04 / D-11: prune the broker snapshot inside the lifecycle
|
|
479
|
+
// lock so concurrent readers cannot observe a stale snapshot pointing at
|
|
480
|
+
// the just-stopped LSP instance. The .primed marker is intentionally
|
|
481
|
+
// preserved (D-10) — broker absence remains "broker_metadata_missing"
|
|
482
|
+
// until the next refresh, distinct from "broker_not_yet_primed".
|
|
483
|
+
{
|
|
484
|
+
const { pruneAuthoringDiagnostics } = await import("./diagnostics-broker.js");
|
|
485
|
+
await pruneAuthoringDiagnostics({
|
|
486
|
+
targetPath: input.targetPath,
|
|
487
|
+
status: input.status,
|
|
488
|
+
projectConfig: input.projectConfig,
|
|
489
|
+
});
|
|
490
|
+
}
|
|
407
491
|
return createLifecycleCommandResult({
|
|
408
492
|
targetPath: input.targetPath,
|
|
409
493
|
command: "stop",
|
|
@@ -1152,13 +1236,20 @@ function resolveWorktreeIdentity(targetPath, status, projectConfig) {
|
|
|
1152
1236
|
async function resolveCurrentManagedLspIdentity(worktree, projectPath) {
|
|
1153
1237
|
const testMode = process.env["GDH_TEST_LSP_MODE"];
|
|
1154
1238
|
if (testMode) {
|
|
1239
|
+
const syntheticVersion = normalizeTestMode(testMode);
|
|
1240
|
+
// Phase 81 / LSP-08 / D-15: honor the version floor for synthetic test versions
|
|
1241
|
+
// so unit tests can exercise the floor-blocked path via GDH_TEST_LSP_MODE.
|
|
1242
|
+
const syntheticFloorViolation = typeof syntheticVersion === "string" &&
|
|
1243
|
+
(syntheticVersion.startsWith("4.5.0") || syntheticVersion.startsWith("4.5.1"));
|
|
1155
1244
|
return await createManagedLspInstanceIdentity({
|
|
1156
1245
|
worktree,
|
|
1157
1246
|
projectPath,
|
|
1158
1247
|
launcher: "test_fake",
|
|
1159
|
-
editorBinPath: `test_fake:${
|
|
1160
|
-
editorVersion:
|
|
1161
|
-
editorVersionReason:
|
|
1248
|
+
editorBinPath: `test_fake:${syntheticVersion}`,
|
|
1249
|
+
editorVersion: syntheticVersion,
|
|
1250
|
+
editorVersionReason: syntheticFloorViolation
|
|
1251
|
+
? "godot_editor_version_unsupported_for_lsp"
|
|
1252
|
+
: null,
|
|
1162
1253
|
});
|
|
1163
1254
|
}
|
|
1164
1255
|
const godotBin = await resolveConfiguredGodotEditorBin({
|
|
@@ -1166,15 +1257,25 @@ async function resolveCurrentManagedLspIdentity(worktree, projectPath) {
|
|
|
1166
1257
|
environment: process.env,
|
|
1167
1258
|
});
|
|
1168
1259
|
const version = godotBin === null ? null : await probeGodotEditorVersion(godotBin);
|
|
1260
|
+
const versionStr = version?.version ?? null;
|
|
1261
|
+
// Phase 81 / LSP-08 / D-15 / Pitfall 4: Godot reports versions like
|
|
1262
|
+
// "4.5.0.stable" or "4.5.1.stable.official"; use a startsWith prefix match
|
|
1263
|
+
// on the 3-part version prefix to handle every suffix form. No semver
|
|
1264
|
+
// dependency is added — the floor check is intentionally narrow.
|
|
1265
|
+
const isFloorViolation = versionStr !== null &&
|
|
1266
|
+
(versionStr.startsWith("4.5.0") || versionStr.startsWith("4.5.1"));
|
|
1267
|
+
const editorVersionReason = godotBin === null
|
|
1268
|
+
? "godot_editor_not_configured"
|
|
1269
|
+
: isFloorViolation
|
|
1270
|
+
? "godot_editor_version_unsupported_for_lsp"
|
|
1271
|
+
: (version?.reason ?? null);
|
|
1169
1272
|
return await createManagedLspInstanceIdentity({
|
|
1170
1273
|
worktree,
|
|
1171
1274
|
projectPath,
|
|
1172
1275
|
launcher: "godot_editor",
|
|
1173
1276
|
editorBinPath: godotBin,
|
|
1174
|
-
editorVersion:
|
|
1175
|
-
editorVersionReason
|
|
1176
|
-
? "godot_editor_not_configured"
|
|
1177
|
-
: (version?.reason ?? "godot_editor_version_unavailable"),
|
|
1277
|
+
editorVersion: versionStr,
|
|
1278
|
+
editorVersionReason,
|
|
1178
1279
|
});
|
|
1179
1280
|
}
|
|
1180
1281
|
async function createManagedLspInstanceIdentity(input) {
|
|
@@ -2026,6 +2127,7 @@ function createUnavailableStatus(input) {
|
|
|
2026
2127
|
stateRootPath: input.worktree.stateRootPath,
|
|
2027
2128
|
rawPayload: input.rawPayload ?? null,
|
|
2028
2129
|
},
|
|
2130
|
+
versionFloorAdvisory: input.versionFloorAdvisory ?? null,
|
|
2029
2131
|
};
|
|
2030
2132
|
}
|
|
2031
2133
|
function isPidAlive(pid) {
|