@mrclrchtr/supi-code-intelligence 5.0.0 → 6.0.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/README.md +4 -2
- package/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-core/package.json +2 -2
- package/node_modules/@mrclrchtr/supi-core/src/api.ts +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +0 -3
- package/node_modules/@mrclrchtr/supi-core/src/debug.ts +9 -0
- package/node_modules/@mrclrchtr/supi-core/src/index.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/README.md +36 -3
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/api.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +0 -3
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug.ts +9 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +12 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +67 -28
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-collection.ts +63 -6
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +46 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +1 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-publication.ts +214 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +278 -79
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +29 -8
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +101 -19
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +19 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +49 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +73 -15
- package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/config/config.ts +5 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/config/server-config.ts +3 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-extends.ts +68 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-path.ts +8 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +176 -24
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +4 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sentinels.ts +46 -10
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +4 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +65 -17
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +143 -21
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +43 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +31 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +2 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/api.ts +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +0 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug.ts +9 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm +0 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm.map +4 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm +0 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm.map +4 -4
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -3
- package/package.json +5 -5
- package/src/analysis/health/diagnostics.ts +58 -126
- package/src/analysis/health/file-scope.ts +184 -0
- package/src/analysis/health/recovery.ts +8 -1
- package/src/analysis/readiness.ts +4 -0
- package/src/analysis/search/ast-scan.ts +4 -1
- package/src/analysis/search/pattern-analysis.ts +10 -3
- package/src/analysis/search/pattern.ts +6 -1
- package/src/config.ts +35 -1
- package/src/extension.ts +57 -14
- package/src/overview/overview-data.ts +9 -2
- package/src/overview/overview.ts +49 -30
- package/src/overview/types.ts +2 -0
- package/src/session/find-types.ts +2 -2
- package/src/session/find-workflow.ts +3 -2
- package/src/session/health-refresh.ts +76 -49
- package/src/session/health-types.ts +7 -1
- package/src/session/health-workflow.ts +35 -18
- package/src/session/input/workflows.ts +2 -2
- package/src/session/inspect-workflow.ts +2 -1
- package/src/session/orientation/collect.ts +2 -2
- package/src/session/orientation-types.ts +2 -0
- package/src/session/orientation-workflow.ts +8 -2
- package/src/session/refactor-workflow.ts +2 -1
- package/src/session/session.ts +25 -1
- package/src/substrate/lsp/maintenance.ts +105 -23
- package/src/substrate/lsp/recovery.ts +11 -5
- package/src/tool/code_find/execute.ts +23 -0
- package/src/tool/code_find/guidance.ts +8 -0
- package/src/tool/{find → code_find}/render.ts +1 -1
- package/src/tool/{result/find.ts → code_find/result.ts} +36 -11
- package/src/tool/code_find/spec.ts +37 -0
- package/src/tool/code_graph/execute.ts +24 -0
- package/src/tool/code_graph/guidance.ts +6 -0
- package/src/tool/{graph → code_graph}/markdown.ts +1 -1
- package/src/tool/{result/graph.ts → code_graph/result.ts} +69 -4
- package/src/tool/code_graph/spec.ts +39 -0
- package/src/tool/{health → code_health}/execute.ts +2 -18
- package/src/tool/code_health/guidance.ts +8 -0
- package/src/tool/{health → code_health}/markdown.ts +68 -18
- package/src/tool/{result/health.ts → code_health/result.ts} +29 -4
- package/src/tool/code_health/spec.ts +41 -0
- package/src/tool/code_inspect/execute.ts +20 -0
- package/src/tool/code_inspect/guidance.ts +6 -0
- package/src/tool/{inspect → code_inspect}/markdown.ts +1 -1
- package/src/tool/{result/inspect.ts → code_inspect/result.ts} +32 -3
- package/src/tool/code_inspect/spec.ts +25 -0
- package/src/tool/code_orientation/execute.ts +25 -0
- package/src/tool/code_orientation/guidance.ts +9 -0
- package/src/tool/{orientation → code_orientation}/markdown.ts +1 -1
- package/src/tool/{result/orientation.ts → code_orientation/result.ts} +67 -3
- package/src/tool/code_orientation/spec.ts +25 -0
- package/src/tool/code_refactor_apply/execute.ts +17 -0
- package/src/tool/code_refactor_apply/guidance.ts +5 -0
- package/src/tool/{refactor-apply/execute.ts → code_refactor_apply/result.ts} +5 -11
- package/src/tool/code_refactor_apply/spec.ts +26 -0
- package/src/tool/code_refactor_plan/execute.ts +27 -0
- package/src/tool/code_refactor_plan/guidance.ts +8 -0
- package/src/tool/{refactor-plan/execute.ts → code_refactor_plan/result.ts} +9 -22
- package/src/tool/code_refactor_plan/spec.ts +25 -0
- package/src/tool/code_resolve/execute.ts +19 -0
- package/src/tool/code_resolve/guidance.ts +8 -0
- package/src/tool/{resolve → code_resolve}/markdown.ts +1 -1
- package/src/tool/{result/resolve.ts → code_resolve/result.ts} +37 -24
- package/src/tool/code_resolve/spec.ts +25 -0
- package/src/tool/guidance.ts +71 -52
- package/src/tool/infra/truncate.ts +40 -0
- package/src/tool/{refactor-plan/markdown.ts → refactor-markdown.ts} +4 -7
- package/src/tool/register.ts +9 -97
- package/src/tool/result/refactor.ts +1 -4
- package/src/tool/schemas.ts +26 -157
- package/src/tool/specs.ts +40 -126
- package/src/tool/find/execute.ts +0 -43
- package/src/tool/graph/execute.ts +0 -82
- package/src/tool/inspect/execute.ts +0 -40
- package/src/tool/orientation/execute.ts +0 -80
- package/src/tool/resolve/execute.ts +0 -53
- /package/src/tool/{find → code_find}/ast-kinds.ts +0 -0
- /package/src/tool/{find → code_find}/markdown.ts +0 -0
- /package/src/tool/{find → code_find}/modes.ts +0 -0
- /package/src/tool/{find → code_find}/tui.ts +0 -0
- /package/src/tool/{graph → code_graph}/tui.ts +0 -0
- /package/src/tool/{health → code_health}/refresh-status.ts +0 -0
- /package/src/tool/{health → code_health}/semantic-state.ts +0 -0
- /package/src/tool/{health → code_health}/tui.ts +0 -0
- /package/src/tool/{inspect → code_inspect}/tui.ts +0 -0
- /package/src/tool/{orientation → code_orientation}/tui.ts +0 -0
- /package/src/tool/{refactor-apply → code_refactor_apply}/tui.ts +0 -0
- /package/src/tool/{refactor-plan → code_refactor_plan}/tui.ts +0 -0
- /package/src/tool/{resolve → code_resolve}/tui.ts +0 -0
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
# @mrclrchtr/supi-code-intelligence — LSP and Tree-sitter AST Code Intelligence for Pi
|
|
8
8
|
|
|
9
|
+
[](https://github.com/mrclrchtr/supi/stargazers) [](https://www.npmjs.com/package/@mrclrchtr/supi-code-intelligence)
|
|
10
|
+
|
|
9
11
|
Gives the [Pi coding agent](https://github.com/earendil-works/pi) direct, model-callable LSP semantic navigation and Tree-sitter AST structural code analysis.
|
|
10
12
|
|
|
11
13
|
## LSP + AST, directly available to the agent
|
|
@@ -30,7 +32,7 @@ After installation, keep asking Pi normal coding questions. The agent can:
|
|
|
30
32
|
- **Refactor safely** — preview language-aware renames and extractions before applying them. Plans are rejected if the files changed in the meantime.
|
|
31
33
|
- **See uncertainty clearly** — results distinguish “nothing found” from incomplete or unavailable analysis and disclose omitted matches.
|
|
32
34
|
|
|
33
|
-
When Pi recognizes a workspace, the agent also receives
|
|
35
|
+
When Pi recognizes a workspace, the agent also receives a compact architecture overview near the start of the session, so it can orient before spending turns opening files. The overview contains manifest facts (module names, one-line descriptions, declared topology, declared entrypoints, and detected languages), is labeled as untrusted repository evidence, and is controlled by the `code-intelligence.overviewEnabled` setting in `/supi-settings`.
|
|
34
36
|
|
|
35
37
|
## Example requests
|
|
36
38
|
|
|
@@ -127,7 +129,7 @@ Open the status view to see detected languages, running or missing servers, and
|
|
|
127
129
|
/supi-ci-status
|
|
128
130
|
```
|
|
129
131
|
|
|
130
|
-
Use `/supi-settings` to disable language servers you do not need
|
|
132
|
+
Use `/supi-settings` to disable language servers you do not need, change the instruction filenames surfaced during directory orientation (defaults `CLAUDE.md` and `AGENTS.md`), or disable the first-turn architecture overview with `overviewEnabled`.
|
|
131
133
|
|
|
132
134
|
[workspace-orientation]: https://raw.githubusercontent.com/mrclrchtr/supi/main/packages/supi-code-intelligence/assets/workspace-orientation.png
|
|
133
135
|
[symbol-inspection]: https://raw.githubusercontent.com/mrclrchtr/supi/main/packages/supi-code-intelligence/assets/symbol-inspection.png
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
# @mrclrchtr/supi-code-runtime
|
|
8
8
|
|
|
9
|
+
[](https://github.com/mrclrchtr/supi/stargazers)
|
|
10
|
+
|
|
9
11
|
Shared workspace context, capability contracts, and canonical types for the SuPi code-understanding stack.
|
|
10
12
|
|
|
11
13
|
This is a **library-only package** — it has no pi extension surface, no user-facing tools, and no UI. It is not installed directly — `supi-lsp`, `supi-tree-sitter`, and `supi-code-intelligence` bundle it and use its shared abstractions to communicate capability availability.
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
# @mrclrchtr/supi-core
|
|
8
8
|
|
|
9
|
+
[](https://github.com/mrclrchtr/supi/stargazers)
|
|
10
|
+
|
|
9
11
|
Shared infrastructure for SuPi extensions.
|
|
10
12
|
|
|
11
13
|
This is a **pure library** — it does not register any pi commands or tools. The `/supi-settings` command is now available through `@mrclrchtr/supi-settings`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.1",
|
|
4
4
|
"description": "Shared settings, configuration, reporting, and session infrastructure",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"./api": "./src/api.ts",
|
|
54
54
|
"./config": "./src/config.ts",
|
|
55
55
|
"./context": "./src/context.ts",
|
|
56
|
-
"./debug": "./src/debug
|
|
56
|
+
"./debug": "./src/debug.ts",
|
|
57
57
|
"./evidence-badge": "./src/evidence-badge.ts",
|
|
58
58
|
"./footer-registry": "./src/footer-registry.ts",
|
|
59
59
|
"./llm": "./src/llm.ts",
|
|
@@ -11,7 +11,7 @@ export * from "./config.ts";
|
|
|
11
11
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
12
12
|
export * from "./context.ts";
|
|
13
13
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
14
|
-
export * from "./debug
|
|
14
|
+
export * from "./debug.ts";
|
|
15
15
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
16
16
|
export * from "./evidence-badge.ts";
|
|
17
17
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
@@ -4,9 +4,6 @@
|
|
|
4
4
|
// supi-debug extension owns policy/configuration and exposes events through a
|
|
5
5
|
// command/tool while this module stays dependency-free for producers.
|
|
6
6
|
|
|
7
|
-
// biome-ignore lint/performance/noReExportAll: preserve the stable debug domain entry point
|
|
8
|
-
export * from "./debug-timing.ts";
|
|
9
|
-
|
|
10
7
|
export type DebugLevel = "debug" | "info" | "warning" | "error";
|
|
11
8
|
export type DebugAgentAccess = "off" | "sanitized" | "raw";
|
|
12
9
|
export interface DebugRegistryConfig {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Debug domain entry for `@mrclrchtr/supi-core/debug`.
|
|
2
|
+
//
|
|
3
|
+
// Kept separate from debug-registry.ts so debug-timing.ts can import the
|
|
4
|
+
// registry without creating an import cycle through the barrel re-export.
|
|
5
|
+
|
|
6
|
+
// biome-ignore lint/performance/noReExportAll: preserve the stable debug domain entry point
|
|
7
|
+
export * from "./debug-registry.ts";
|
|
8
|
+
// biome-ignore lint/performance/noReExportAll: preserve the stable debug domain entry point
|
|
9
|
+
export * from "./debug-timing.ts";
|
|
@@ -11,7 +11,7 @@ export * from "./config.ts";
|
|
|
11
11
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
12
12
|
export * from "./context.ts";
|
|
13
13
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
14
|
-
export * from "./debug
|
|
14
|
+
export * from "./debug.ts";
|
|
15
15
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
16
16
|
export * from "./footer-registry.ts";
|
|
17
17
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
# @mrclrchtr/supi-lsp
|
|
8
8
|
|
|
9
|
+
[](https://github.com/mrclrchtr/supi/stargazers)
|
|
10
|
+
|
|
9
11
|
Language Server Protocol runtime library for the [pi coding agent](https://github.com/earendil-works/pi).
|
|
10
12
|
|
|
11
13
|
This package is library-only. It registers no model-callable tools; `@mrclrchtr/supi-code-intelligence` owns the public `code_*` family.
|
|
@@ -37,17 +39,26 @@ The table records an initialize-handshake audit performed on 2026-08-21 against
|
|
|
37
39
|
| SQL | `sql-language-server` 1.7.1 | No (confirmed) | Push | No `diagnosticProvider` in the initialize result. |
|
|
38
40
|
| R | `R` 4.6.1 (languageserver) | No (confirmed) | Push | No `diagnosticProvider` in the initialize result. |
|
|
39
41
|
|
|
40
|
-
SuPi advertises static and dynamic pull support
|
|
42
|
+
SuPi advertises static and dynamic pull support. It advertises server-requested refresh support too:
|
|
43
|
+
|
|
44
|
+
- `textDocument.diagnostic.dynamicRegistration: true`
|
|
45
|
+
- `workspace.diagnostics.refreshSupport: true`
|
|
46
|
+
|
|
47
|
+
A server gets pull diagnostics when it declares a valid `diagnosticProvider` during initialization. A server also gets pull diagnostics after it registers `textDocument/diagnostic`. The pull support stays active until the server removes the registration. SuPi validates registration parameters. Invalid parameters do not enable pull support. SuPi ignores other registration methods.
|
|
48
|
+
|
|
49
|
+
When a server sends `workspace/diagnostic/refresh`, SuPi returns `null` immediately. It then refreshes the owning client's tracked documents in the background. The refresh covers open, cached, and failed tracked documents. SuPi does not add workspace-wide `workspace/diagnostic` pulls.
|
|
50
|
+
|
|
51
|
+
Protocol support is separate from the configured mode. A server may support pull diagnostics and still use SuPi's push mode because the built-in configuration does not enable pull mode.
|
|
41
52
|
|
|
42
53
|
The LSP 3.18 specification adds `Diagnostic.message` markup content, guarded by the client capability `textDocument.diagnostic.markupMessageSupport`; SuPi's validator already accepts plaintext and markdown messages but does not advertise the capability. Other 3.18 features (snippet text edits, inline completion, folding-range refresh, multi-range formatting) are outside the diagnostic surface and are not implemented.
|
|
43
54
|
|
|
44
55
|
Pull diagnostics use `textDocument/diagnostic`, so the client can tie a report to the current request. Push diagnostics are asynchronous and can omit a document version. After a workspace change, SuPi may report push-only diagnostics as partial or unavailable when it cannot prove that the result matches the current document. It does not treat missing fresh evidence as a clean file.
|
|
45
56
|
|
|
46
|
-
Unversioned pushes are accepted for an open document when they arrive after the document's sync moment (the client-side instant the `didChange` or `didOpen` that produced them was sent) and are re-stamped with the current synchronization; unversioned pushes for closed or untracked URIs, and pushes that arrive before a sync moment, stay fail-closed. On push-only routes, a
|
|
57
|
+
Unversioned pushes are accepted for an open document when they arrive after the document's sync moment (the client-side instant the `didChange` or `didOpen` that produced them was sent) and are re-stamped with the current synchronization; unversioned pushes for closed or untracked URIs, and pushes that arrive before a sync moment, stay fail-closed. On push-only routes, the first valid publication for a synchronization is tentative. A later valid publication for the same synchronization confirms it, and every publication restarts the quiet period. If no publication arrives, the existing bounded reopen path may ask the server to publish on `didOpen`; a tentative timeout does not reopen that document. In a mixed batch, silent documents can still use the reopen path. Non-empty tentative diagnostics are visible as partial evidence, but they do not enter the confirmed path. An empty tentative publication cannot establish a clean file and stays unavailable until a diagnostic republish arrives. Repeated unchanged queries share the tentative publication's wait age instead of starting a new full wait. A late republish promotes the cache without a new refresh.
|
|
47
58
|
|
|
48
59
|
Server readiness follows LSP work-done progress: a created progress token is pending and never blocks readiness; an observed `begin` marks active work and makes the client not ready until its `end` or the bounded per-token timeout.
|
|
49
60
|
|
|
50
|
-
A workspace diagnostic refresh returns exact coverage counts for requested, confirmed, unconfirmed, failed, and removed tracked documents. `code_health` marks tracked-file diagnostics as complete
|
|
61
|
+
A workspace diagnostic refresh returns exact coverage counts for requested, confirmed, unconfirmed, failed, and removed tracked documents. `code_health` marks tracked-file diagnostics as complete when each requested document is confirmed or known to be removed. It shows non-empty tentative diagnostics as partial entries, reports the same coverage counts in summary and detailed views, and explains when a diagnostic republish is needed. A refresh attempt does not prove fresh evidence by itself. A removed file is reported by the refresh that finds it and is not retained in later tracked-file snapshots.
|
|
51
62
|
|
|
52
63
|
An explicit recovery pass restarts a push-only client only on a protocol-stall signal (a readiness stall, or repeated JSON-RPC request failures) — never on unconfirmed evidence alone, because the reopen-resync fallback recovers unconfirmed documents without discarding warm server state. It never restarts a pull-capable client because push evidence is absent, and it never restarts a client during passive health display. Each client route restarts at most once per workspace invalidation generation. The replacement process has a fixed startup bound of 5 seconds; exceeding the bound fails closed as start-failed without retry. Recovery telemetry records the outcome, elapsed time, attempted clients, restart count, the bounded server names involved, and the stall signal that triggered a restart, without changing the evidence semantics of the result.
|
|
53
64
|
|
|
@@ -71,6 +82,28 @@ Configuration overrides merge with the built-in server definitions. Use `.pi/sup
|
|
|
71
82
|
|
|
72
83
|
Gopls pull diagnostics stay opt-in while golang/go#70199 is open; without the option the built-in Go configuration stays in push mode. Kotlin's `--stdio` argument is already part of the built-in configuration and needs no override.
|
|
73
84
|
|
|
85
|
+
### Custom server configuration
|
|
86
|
+
|
|
87
|
+
A custom server needs a command and at least one file type:
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"lsp": {
|
|
92
|
+
"servers": {
|
|
93
|
+
"custom": {
|
|
94
|
+
"command": "custom-lsp",
|
|
95
|
+
"args": ["--stdio"],
|
|
96
|
+
"fileTypes": ["custom"],
|
|
97
|
+
"env": { "CUSTOM_LSP_LOG": "debug" },
|
|
98
|
+
"initializationOptions": { "mode": "project" }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
File types do not include a leading dot. If `rootMarkers` is omitted, the server uses the session root.
|
|
106
|
+
|
|
74
107
|
The `lsp.enabled` and `lsp.active` settings are deprecated and ignored. Disable one language with `lsp.servers.<language>.enabled: false`. Use `/supi-ci-status` from `@mrclrchtr/supi-code-intelligence` to see detected, running, and missing servers.
|
|
75
108
|
|
|
76
109
|
## What it provides
|
package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/README.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
# @mrclrchtr/supi-code-runtime
|
|
8
8
|
|
|
9
|
+
[](https://github.com/mrclrchtr/supi/stargazers)
|
|
10
|
+
|
|
9
11
|
Shared workspace context, capability contracts, and canonical types for the SuPi code-understanding stack.
|
|
10
12
|
|
|
11
13
|
This is a **library-only package** — it has no pi extension surface, no user-facing tools, and no UI. It is not installed directly — `supi-lsp`, `supi-tree-sitter`, and `supi-code-intelligence` bundle it and use its shared abstractions to communicate capability availability.
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
|
|
7
7
|
# @mrclrchtr/supi-core
|
|
8
8
|
|
|
9
|
+
[](https://github.com/mrclrchtr/supi/stargazers)
|
|
10
|
+
|
|
9
11
|
Shared infrastructure for SuPi extensions.
|
|
10
12
|
|
|
11
13
|
This is a **pure library** — it does not register any pi commands or tools. The `/supi-settings` command is now available through `@mrclrchtr/supi-settings`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.1",
|
|
4
4
|
"description": "Shared settings, configuration, reporting, and session infrastructure",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"./api": "./src/api.ts",
|
|
54
54
|
"./config": "./src/config.ts",
|
|
55
55
|
"./context": "./src/context.ts",
|
|
56
|
-
"./debug": "./src/debug
|
|
56
|
+
"./debug": "./src/debug.ts",
|
|
57
57
|
"./evidence-badge": "./src/evidence-badge.ts",
|
|
58
58
|
"./footer-registry": "./src/footer-registry.ts",
|
|
59
59
|
"./llm": "./src/llm.ts",
|
|
@@ -11,7 +11,7 @@ export * from "./config.ts";
|
|
|
11
11
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
12
12
|
export * from "./context.ts";
|
|
13
13
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
14
|
-
export * from "./debug
|
|
14
|
+
export * from "./debug.ts";
|
|
15
15
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
16
16
|
export * from "./evidence-badge.ts";
|
|
17
17
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts
CHANGED
|
@@ -4,9 +4,6 @@
|
|
|
4
4
|
// supi-debug extension owns policy/configuration and exposes events through a
|
|
5
5
|
// command/tool while this module stays dependency-free for producers.
|
|
6
6
|
|
|
7
|
-
// biome-ignore lint/performance/noReExportAll: preserve the stable debug domain entry point
|
|
8
|
-
export * from "./debug-timing.ts";
|
|
9
|
-
|
|
10
7
|
export type DebugLevel = "debug" | "info" | "warning" | "error";
|
|
11
8
|
export type DebugAgentAccess = "off" | "sanitized" | "raw";
|
|
12
9
|
export interface DebugRegistryConfig {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
// Debug domain entry for `@mrclrchtr/supi-core/debug`.
|
|
2
|
+
//
|
|
3
|
+
// Kept separate from debug-registry.ts so debug-timing.ts can import the
|
|
4
|
+
// registry without creating an import cycle through the barrel re-export.
|
|
5
|
+
|
|
6
|
+
// biome-ignore lint/performance/noReExportAll: preserve the stable debug domain entry point
|
|
7
|
+
export * from "./debug-registry.ts";
|
|
8
|
+
// biome-ignore lint/performance/noReExportAll: preserve the stable debug domain entry point
|
|
9
|
+
export * from "./debug-timing.ts";
|
|
@@ -11,7 +11,7 @@ export * from "./config.ts";
|
|
|
11
11
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
12
12
|
export * from "./context.ts";
|
|
13
13
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
14
|
-
export * from "./debug
|
|
14
|
+
export * from "./debug.ts";
|
|
15
15
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
16
16
|
export * from "./footer-registry.ts";
|
|
17
17
|
// biome-ignore lint/performance/noReExportAll: intentional convenience barrel
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-lsp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.0.1",
|
|
4
4
|
"description": "Language Server Protocol runtime for SuPi code intelligence",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -37,8 +37,8 @@
|
|
|
37
37
|
"vscode-jsonrpc": "^9.0.0",
|
|
38
38
|
"vscode-languageserver-protocol": "^3.18.0",
|
|
39
39
|
"vscode-languageserver-types": "^3.18.0",
|
|
40
|
-
"@mrclrchtr/supi-code-runtime": "
|
|
41
|
-
"@mrclrchtr/supi-core": "
|
|
40
|
+
"@mrclrchtr/supi-code-runtime": "6.0.1",
|
|
41
|
+
"@mrclrchtr/supi-core": "6.0.1"
|
|
42
42
|
},
|
|
43
43
|
"bundledDependencies": [
|
|
44
44
|
"@mrclrchtr/supi-code-runtime",
|
|
@@ -8,7 +8,16 @@ export {
|
|
|
8
8
|
hasDeprecatedLspKeys,
|
|
9
9
|
loadLspSettings,
|
|
10
10
|
} from "./config/lsp-settings.ts";
|
|
11
|
-
export {
|
|
11
|
+
export {
|
|
12
|
+
clearTsconfigCache,
|
|
13
|
+
type FileScopeDecision,
|
|
14
|
+
type FileScopeStatus,
|
|
15
|
+
getFileScopeDecision,
|
|
16
|
+
invalidateTsconfigCacheForConfig,
|
|
17
|
+
invalidateTsconfigCacheForConfigDir,
|
|
18
|
+
isProjectConfigFileName,
|
|
19
|
+
type ScopeDecisionBasis,
|
|
20
|
+
} from "./config/tsconfig-scope.ts";
|
|
12
21
|
export type {
|
|
13
22
|
CodeAction,
|
|
14
23
|
Diagnostic,
|
|
@@ -28,6 +37,7 @@ export type {
|
|
|
28
37
|
} from "./config/types.ts";
|
|
29
38
|
export { FileChangeType } from "./config/types.ts";
|
|
30
39
|
export { toLspPosition, toOneBasedPosition } from "./coordinates.ts";
|
|
40
|
+
export { TENTATIVE_PUSH_UNAVAILABLE_REASON } from "./diagnostics/evidence.ts";
|
|
31
41
|
export { isLikelyStaleDiagnostic } from "./diagnostics/stale-diagnostics.ts";
|
|
32
42
|
export {
|
|
33
43
|
scanWorkspaceSentinels,
|
|
@@ -50,6 +60,7 @@ export type {
|
|
|
50
60
|
RecoverDiagnosticsResult,
|
|
51
61
|
RoutedMutationResponse,
|
|
52
62
|
SemanticReadinessResult,
|
|
63
|
+
WorkspaceDiagnosticReport,
|
|
53
64
|
WorkspaceDiagnosticSnapshot,
|
|
54
65
|
WorkspaceDiagnosticSummaryEntry,
|
|
55
66
|
WorkspaceLspDiagnosticSurface,
|
|
@@ -2,6 +2,7 @@ import type { PublishDiagnosticsParams } from "../config/types.ts";
|
|
|
2
2
|
import { uriToFile } from "../utils.ts";
|
|
3
3
|
import {
|
|
4
4
|
type DiagnosticCacheEntry,
|
|
5
|
+
isTentativePushEntry,
|
|
5
6
|
isValidPublishDiagnosticsParams,
|
|
6
7
|
} from "./client-diagnostic-evidence.ts";
|
|
7
8
|
import type { ClientDiagnosticSnapshot, OpenDocumentState } from "./client-document-state.ts";
|
|
@@ -20,27 +21,44 @@ interface ApplyPushOptions {
|
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
|
-
* Apply one valid push publication
|
|
24
|
+
* Apply one valid push publication.
|
|
24
25
|
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* pushes
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
26
|
+
* Returns whether the publication was accepted and whether it promoted a
|
|
27
|
+
* tentative entry to confirmed. Fail-closed policy (ADR 0020): unversioned
|
|
28
|
+
* pushes are rejected for closed and untracked URIs and for arrivals before
|
|
29
|
+
* the URI's sync moment; versioned pushes for a URI closed by a lifecycle
|
|
30
|
+
* operation are rejected because their version cannot be verified. An
|
|
31
|
+
* unversioned push that arrives after the sync moment of an open document
|
|
32
|
+
* is accepted and re-stamped with that document's current synchronization
|
|
33
|
+
* state. The first valid publication for a synchronization is tentative; a
|
|
34
|
+
* later valid publication for the same synchronization ID and evidence
|
|
35
|
+
* revision promotes the cache to confirmed (ADR 0021).
|
|
31
36
|
*/
|
|
32
|
-
export function applyPushDiagnostics(options: ApplyPushOptions):
|
|
33
|
-
|
|
37
|
+
export function applyPushDiagnostics(options: ApplyPushOptions): {
|
|
38
|
+
accepted: boolean;
|
|
39
|
+
promoted: boolean;
|
|
40
|
+
} {
|
|
41
|
+
if (!isValidPublishDiagnosticsParams(options.params)) return { accepted: false, promoted: false };
|
|
34
42
|
const openDocument = options.openDocuments.get(options.params.uri);
|
|
35
43
|
if (options.params.version !== undefined) {
|
|
36
|
-
if (!Number.isInteger(options.params.version)) return false;
|
|
37
|
-
if (openDocument && options.params.version !== openDocument.version)
|
|
38
|
-
|
|
44
|
+
if (!Number.isInteger(options.params.version)) return { accepted: false, promoted: false };
|
|
45
|
+
if (openDocument && options.params.version !== openDocument.version) {
|
|
46
|
+
return { accepted: false, promoted: false };
|
|
47
|
+
}
|
|
48
|
+
if (!openDocument && options.closedVersionedBarrier)
|
|
49
|
+
return { accepted: false, promoted: false };
|
|
39
50
|
}
|
|
40
|
-
if (!Array.isArray(options.params.diagnostics)) return false;
|
|
41
|
-
if (!acceptUnversionedPush(options, openDocument)) return false;
|
|
42
|
-
|
|
43
|
-
|
|
51
|
+
if (!Array.isArray(options.params.diagnostics)) return { accepted: false, promoted: false };
|
|
52
|
+
if (!acceptUnversionedPush(options, openDocument)) return { accepted: false, promoted: false };
|
|
53
|
+
const entry = buildPushCacheEntry(options, openDocument);
|
|
54
|
+
const previous = options.store.get(options.params.uri);
|
|
55
|
+
const promoted =
|
|
56
|
+
entry.source === "push" &&
|
|
57
|
+
entry.publications !== undefined &&
|
|
58
|
+
entry.publications >= 2 &&
|
|
59
|
+
isTentativePushEntry(previous);
|
|
60
|
+
options.store.set(options.params.uri, entry);
|
|
61
|
+
return { accepted: true, promoted };
|
|
44
62
|
}
|
|
45
63
|
|
|
46
64
|
/** Gate one unversioned push publication against the sync-moment policy. */
|
|
@@ -69,13 +87,27 @@ function buildPushCacheEntry(
|
|
|
69
87
|
): DiagnosticCacheEntry {
|
|
70
88
|
const currentRevision = openDocument?.evidenceRevision === options.evidenceRevision;
|
|
71
89
|
const unversioned = options.params.version === undefined && openDocument !== undefined;
|
|
90
|
+
const synchronizationId =
|
|
91
|
+
unversioned || currentRevision ? openDocument?.synchronizationId : undefined;
|
|
92
|
+
const evidenceRevision = openDocument?.evidenceRevision;
|
|
93
|
+
const previous = options.store.get(options.params.uri);
|
|
94
|
+
// A later valid publication for the same synchronization ID and evidence
|
|
95
|
+
// revision continues the entry's publication count: a confirmed pull
|
|
96
|
+
// entry keeps the synchronization confirmed, and a tentative push entry
|
|
97
|
+
// is promoted. Any other publication starts a fresh count.
|
|
98
|
+
const sameSynchronization =
|
|
99
|
+
synchronizationId !== undefined &&
|
|
100
|
+
previous?.synchronizationId === synchronizationId &&
|
|
101
|
+
previous.evidenceRevision === evidenceRevision;
|
|
102
|
+
const publications = sameSynchronization ? Math.min((previous.publications ?? 1) + 1, 2) : 1;
|
|
72
103
|
return {
|
|
73
104
|
diagnostics: options.params.diagnostics,
|
|
74
105
|
receivedAt: Date.now(),
|
|
75
106
|
source: "push",
|
|
76
|
-
synchronizationId
|
|
77
|
-
evidenceRevision
|
|
107
|
+
synchronizationId,
|
|
108
|
+
evidenceRevision,
|
|
78
109
|
version: options.params.version,
|
|
110
|
+
publications,
|
|
79
111
|
};
|
|
80
112
|
}
|
|
81
113
|
|
|
@@ -119,17 +151,18 @@ function collectOpenDocumentFreshness(options: {
|
|
|
119
151
|
const uris = new Set<string>();
|
|
120
152
|
for (const [uri, document] of options.openDocuments) {
|
|
121
153
|
uris.add(uri);
|
|
154
|
+
const entry = options.store.get(uri);
|
|
122
155
|
const fileState = getDiagnosticFileState(uriToFile(uri));
|
|
123
|
-
const current = hasCurrentDiagnosticEvidence(
|
|
124
|
-
document,
|
|
125
|
-
options.store.get(uri),
|
|
126
|
-
options.evidenceRevision,
|
|
127
|
-
);
|
|
156
|
+
const current = hasCurrentDiagnosticEvidence(document, entry, options.evidenceRevision);
|
|
128
157
|
const failed = options.failedDocuments.has(uri);
|
|
158
|
+
// A current tentative push maps to unconfirmed, not to a new public
|
|
159
|
+
// status: it matches the synchronization but cannot confirm it yet, so
|
|
160
|
+
// it must not claim current snapshot or document state (issue #351).
|
|
161
|
+
const tentative = current && isTentativePushEntry(entry);
|
|
129
162
|
documents.push({
|
|
130
163
|
uri,
|
|
131
|
-
current: fileState === "present" && current && !failed,
|
|
132
|
-
status: statusForDocument(fileState, current, failed),
|
|
164
|
+
current: fileState === "present" && current && !failed && !tentative,
|
|
165
|
+
status: statusForDocument(fileState, current, failed, tentative),
|
|
133
166
|
});
|
|
134
167
|
}
|
|
135
168
|
return { documents, uris };
|
|
@@ -174,10 +207,12 @@ function statusForDocument(
|
|
|
174
207
|
fileState: ReturnType<typeof getDiagnosticFileState>,
|
|
175
208
|
current: boolean,
|
|
176
209
|
failed: boolean,
|
|
210
|
+
tentative: boolean,
|
|
177
211
|
): "confirmed" | "unconfirmed" | "failed" | "removed" {
|
|
178
212
|
if (fileState === "removed") return "removed";
|
|
179
213
|
if (fileState === "unreadable" || failed) return "failed";
|
|
180
|
-
|
|
214
|
+
if (tentative || !current) return "unconfirmed";
|
|
215
|
+
return "confirmed";
|
|
181
216
|
}
|
|
182
217
|
|
|
183
218
|
function collectCachedDiagnostics(options: {
|
|
@@ -197,9 +232,13 @@ function collectCachedDiagnostics(options: {
|
|
|
197
232
|
entry,
|
|
198
233
|
options.evidenceRevision,
|
|
199
234
|
);
|
|
200
|
-
current
|
|
235
|
+
// A current tentative error is useful partial evidence. Keep its entry
|
|
236
|
+
// non-current so it cannot establish a clean or settled result (ADR 0021).
|
|
237
|
+
const tentative = entryCurrent && isTentativePushEntry(entry);
|
|
238
|
+
const confirmed = entryCurrent && !tentative;
|
|
239
|
+
current &&= confirmed;
|
|
201
240
|
if (entry.diagnostics.length > 0) {
|
|
202
|
-
entries.push({ uri, diagnostics: entry.diagnostics, current:
|
|
241
|
+
entries.push({ uri, diagnostics: entry.diagnostics, current: confirmed });
|
|
203
242
|
}
|
|
204
243
|
}
|
|
205
244
|
return { entries, current };
|
|
@@ -3,10 +3,12 @@ import {
|
|
|
3
3
|
type CodeRequestControl,
|
|
4
4
|
completedCodeQuery,
|
|
5
5
|
isCodeRequestInterruption,
|
|
6
|
+
partialCodeQuery,
|
|
6
7
|
throwIfCodeRequestInterrupted,
|
|
7
8
|
unavailableCodeQuery,
|
|
8
9
|
} from "@mrclrchtr/supi-code-runtime/api";
|
|
9
10
|
import type { Diagnostic } from "../config/types.ts";
|
|
11
|
+
import { TENTATIVE_PUSH_UNAVAILABLE_REASON } from "../diagnostics/evidence.ts";
|
|
10
12
|
import {
|
|
11
13
|
type DiagnosticSynchronization,
|
|
12
14
|
incompleteDiagnosticResult,
|
|
@@ -25,6 +27,8 @@ interface FileDiagnosticCollectionOptions {
|
|
|
25
27
|
readonly waiters: DiagnosticWaitRegistry;
|
|
26
28
|
readonly current: () => boolean;
|
|
27
29
|
readonly freshPush: () => boolean;
|
|
30
|
+
/** Receive time of a current tentative push that is already cached. */
|
|
31
|
+
readonly currentPushReceivedAt: () => number | undefined;
|
|
28
32
|
readonly diagnostics: () => Diagnostic[];
|
|
29
33
|
readonly pullDiagnostics: (timeoutMs: number, signal: AbortSignal) => Promise<boolean>;
|
|
30
34
|
/** Observe the push wait outcome without finishing the observer; the caller finishes once. */
|
|
@@ -58,22 +62,75 @@ export async function collectSynchronizedFileDiagnostics(
|
|
|
58
62
|
return completedCodeQuery(options.diagnostics());
|
|
59
63
|
}
|
|
60
64
|
|
|
61
|
-
const push = await options
|
|
62
|
-
options.request.uri,
|
|
63
|
-
Math.max(0, options.maxWaitMs - (Date.now() - options.syncStart)),
|
|
64
|
-
control,
|
|
65
|
-
);
|
|
65
|
+
const push = await waitForConfirmedPush(options, control);
|
|
66
66
|
if (options.onPushWait) options.onPushWait(push);
|
|
67
67
|
else options.observer.pushWaitCompleted(1, push);
|
|
68
|
-
if (push === "published"
|
|
68
|
+
if (push === "published") {
|
|
69
69
|
return completedCodeQuery(options.diagnostics());
|
|
70
70
|
}
|
|
71
|
+
if (push === "tentative") {
|
|
72
|
+
// A current tentative error is useful partial evidence, but an empty
|
|
73
|
+
// publication cannot establish that the document is clean (ADR 0021).
|
|
74
|
+
const diagnostics = options.diagnostics();
|
|
75
|
+
return diagnostics.length > 0
|
|
76
|
+
? partialCodeQuery(diagnostics, TENTATIVE_PUSH_UNAVAILABLE_REASON)
|
|
77
|
+
: unavailableCodeQuery(TENTATIVE_PUSH_UNAVAILABLE_REASON);
|
|
78
|
+
}
|
|
71
79
|
return incompleteDiagnosticResult(
|
|
72
80
|
options.cachedDiagnostics,
|
|
73
81
|
push === "released" ? "released" : "timed-out",
|
|
74
82
|
);
|
|
75
83
|
}
|
|
76
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Wait until a confirmed push publication settles the synchronization.
|
|
87
|
+
*
|
|
88
|
+
* Every accepted publication releases the waiter. A confirmed push ends the
|
|
89
|
+
* wait as "published". A publication that stays tentative (no republish
|
|
90
|
+
* arrived for the same synchronization) ends as "tentative" when the budget
|
|
91
|
+
* expires. A wait with no publication at all keeps the waiter's
|
|
92
|
+
* "timed-out", and a lifecycle release stays "released".
|
|
93
|
+
*/
|
|
94
|
+
async function waitForConfirmedPush(
|
|
95
|
+
options: FileDiagnosticCollectionOptions,
|
|
96
|
+
control?: CodeRequestControl,
|
|
97
|
+
): Promise<DiagnosticPushWaitOutcome> {
|
|
98
|
+
// A tentative publication may already sit in the cache when the wait
|
|
99
|
+
// starts (ADR 0021). Do not grant repeated callers a new full wait window:
|
|
100
|
+
// bound them by the first operation window or the cached publication age.
|
|
101
|
+
const initialPushReceivedAt = options.currentPushReceivedAt();
|
|
102
|
+
const waitDeadline = Math.min(
|
|
103
|
+
options.syncStart + options.maxWaitMs,
|
|
104
|
+
initialPushReceivedAt === undefined
|
|
105
|
+
? Number.POSITIVE_INFINITY
|
|
106
|
+
: initialPushReceivedAt + options.maxWaitMs,
|
|
107
|
+
);
|
|
108
|
+
let observedPublication = initialPushReceivedAt !== undefined;
|
|
109
|
+
for (;;) {
|
|
110
|
+
throwIfCodeRequestInterrupted(control);
|
|
111
|
+
// A republish can arrive after the previous wait released but before the
|
|
112
|
+
// next waiter registers; re-check the store on every loop pass.
|
|
113
|
+
if (options.freshPush()) return "published";
|
|
114
|
+
const push = await options.waiters.waitForPush(
|
|
115
|
+
options.request.uri,
|
|
116
|
+
Math.max(0, waitDeadline - Date.now()),
|
|
117
|
+
control,
|
|
118
|
+
);
|
|
119
|
+
// A lifecycle release ends the wait definitively: a tentative
|
|
120
|
+
// publication observed earlier must not reclassify the release.
|
|
121
|
+
if (push === "released") return "released";
|
|
122
|
+
// A promotion can land while the timed-out waiter tears down; the cache
|
|
123
|
+
// must complete the wait before the timeout classifies the outcome.
|
|
124
|
+
if (options.freshPush()) return "published";
|
|
125
|
+
if (push === "timed-out") {
|
|
126
|
+
return observedPublication || options.currentPushReceivedAt() !== undefined
|
|
127
|
+
? "tentative"
|
|
128
|
+
: "timed-out";
|
|
129
|
+
}
|
|
130
|
+
observedPublication = true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
77
134
|
async function collectPullEvidence(
|
|
78
135
|
options: FileDiagnosticCollectionOptions,
|
|
79
136
|
control?: CodeRequestControl,
|