Files
kandev/docs/plans/lsp-file-intelligence-progress/task-01-progress-protocol.md
T
2026-08-24 11:09:03 +00:00

121 lines
14 KiB
Markdown

---
id: "01-progress-protocol"
title: "Work-done progress protocol"
status: completed
wave: 1
depends_on: []
plan: "plan.md"
spec: "../../specs/platform/requirements/lsp-file-intelligence.md"
---
# Task 01: Work-Done Progress Protocol
## Acceptance
- The initialize request advertises work-done support and carries a connection-generation token before any server progress can arrive.
- Valid `begin`, `report`, and `end` notifications update only their registered string or number token; malformed, unknown, and stale-generation frames do nothing.
- Initialization, active work, and the most recently completed server item are observable through one stable manager snapshot and clear with connection ownership.
## Verification
```bash
cd apps && pnpm --filter @kandev/web test -- --run lib/lsp/lsp-progress.test.ts lib/lsp/lsp-client-manager.test.ts
cd apps/web && pnpm run typecheck
```
## Files Likely Touched
- `apps/web/lib/lsp/lsp-progress.ts`
- `apps/web/lib/lsp/lsp-progress.test.ts`
- `apps/web/lib/lsp/lsp-client-types.ts`
- `apps/web/lib/lsp/lsp-json-rpc.ts`
- `apps/web/lib/lsp/lsp-client-manager.ts`
- `apps/web/lib/lsp/lsp-client-manager.test.ts`
## Dependencies
None.
## Parallelism
Sequential; Task 02 consumes this snapshot.
## Inputs
- Spec sections: Browser LSP progress contract, Readiness and progress state, Failure modes.
- Existing generation checks in `lsp-client-manager.ts`.
## Output Contract
Record RED/GREEN evidence, files changed, exact tests run, remaining risks, and update this task plus `plan.md`.
## Result
- RED: the manager test proved `window.workDoneProgress` and the initialize token were absent; transition tests then proved begin/report/end state was unimplemented.
- GREEN: the client now advertises and registers generation-owned tokens, tracks initialize timing and immutable work snapshots, and ignores malformed, unknown, or stale progress.
- Review hardening: unexpected closes after readiness now retain an error status and close reason for Retry; explicit stop and idle cleanup still clear to disabled.
- Review hardening: a dedicated synchronous registration guard now distinguishes LSP TypeScript providers from Monaco's lazy built-ins, and built-in suppression waits until Monaco's wrappers are installed.
- Review hardening: a generic WebSocket close no longer overwrites a detailed install failure already reported by agentctl.
- Review hardening: completion providers forward Monaco trigger context using LSP enum values, and managed installer caches resolve from the merged task `HOME`.
- Review hardening: cold SSH, Sprites, and remote-Docker sessions are rejected through a read-only runtime lookup before LSP can create or resume an execution.
- Review hardening: both Monaco save paths now flush the newest live content before emitting capability-gated `textDocument/didSave` after successful persistence. Unchanged buffers retain canonical repo-aware `includeText` snapshots; buffers edited during persistence remain dirty and omit stale optional save text instead of rewinding LSP state.
- Review hardening: LSP navigation into an attached repository preserves an existing task-root-relative editor identity, preventing duplicate tabs and keeping cursor reveal on the tree-opened file.
- Review hardening: command-palette content-search selection now carries the active session through pending and immediate cursor reveal, so identical open repository paths in other task sessions cannot make the jump ambiguous.
- Review hardening: installer failures without details, WebSocket errors, and reasonless pre-bridge or post-bridge closes now resolve their fallback copy through the active locale instead of leaking English into localized status UI and toasts.
- Review hardening: categorical task-host close codes for unsupported executors, capacity, and stream failures ignore English transport prose and resolve their status through the active frontend locale.
- Review hardening: completion items without `textEdit` use Monaco's current-word range while `InsertReplaceEdit` keeps its dual range, live LSP JSON settings update the reused connection and its configuration request handler, and task hosts without an installer close with localized manual-install guidance through `4007` before or after preference opt-in.
- Review hardening: TypeScript built-in suppression now follows per-connection model ownership instead of a global flag, so unrelated sessions keep Monaco intelligence; valid empty semantic-token arrays return an empty payload without periodic client polling.
- Review hardening: Go post-install discovery now includes the task environment's `USERPROFILE\go\bin`, covering default Windows Local PC setups without explicit `GOBIN`, `GOPATH`, or `HOME`.
- Review hardening: the browser-facing LSP capacity slot is acquired before `GetOrEnsureExecution`, so an over-cap request cannot start or resume a supported task host; all resolution failures release the provisional slot.
- Review hardening: missing-binary close code `4001` now ignores backend prose and resolves through the active locale, Editors cards expose install prerequisites inline for pointer, keyboard, and touch users, and the test-only duplicate language table was removed in favor of the localized Settings metadata source.
- Review hardening: the Monaco editor opener now opens regular targets inside the active task workspace and propagates `false` for unresolved targets, while agentctl reports unexpected server exit as categorical `4006` with no English close reason so the browser localizes it.
- Review hardening: JSON-RPC initialize errors now preserve their `message` instead of rendering `[object Object]`, while task-host process-launch failures retain details in backend logs and close through localized categorical `4008` with no English transport reason.
- Review hardening: task teardown during auto-install keeps the standard `1001` going-away close but omits the English `task stopping` reason, allowing the browser's existing localized pre-bridge fallback to own the user-visible status.
- Review hardening: Monaco completion registration now uses only the trigger characters advertised by the active server, and a named 25-value mapping preserves every standard LSP completion category in Monaco instead of relying on mismatched numeric enums.
- Review hardening: Monaco signature help is absent when the server omits that capability and otherwise preserves the server's trigger and retrigger characters; a bare install-failure close ignores generic task-host prose and resolves through the active locale while a preceding detailed installer payload remains authoritative.
- Review hardening: LSP completion-list `isIncomplete` state reaches Monaco so continued typing can request refreshed results through trigger kind `3` instead of retaining stale partial suggestions.
- Review hardening: completion, hover, definition, references, and signature help register only when advertised; TypeScript built-in suppression now follows the exact overlapping provider set, preserving built-in fallbacks for omitted capabilities and unwired rename, code-action, highlight, and inlay features.
- Review hardening: every task-host LSP WebSocket frame now uses the same five-second write deadline, so an unread pre-bridge status or ready frame cannot pin the handler or leak its owned language-server process.
- Review hardening: a terminating stdout forwarder closes language-server stdin to release active writes immediately, while a cross-platform 30-second cutoff closes a pipe whose server remains alive but stops reading; both paths converge on owned-process cleanup.
- Review hardening: a single pending task-host WebSocket read cancels connection-owned auto-install work when the browser stops or disconnects, then hands the first client frame into the bridge after a successful install without concurrent reads or a dropped initialize request.
- Review hardening: a failed or timed-out initial `installing` WebSocket write now closes the stream before agentctl acquires auto-install work; the forced-write-failure regression proves the installer is never entered without a consumer.
- Review hardening: Go post-install lookup now rejects relative `GOBIN`, every relative `GOPATH` entry, `HOME`, and `USERPROFILE`, preventing repository-controlled directories from supplying the launched `gopls` binary while retaining absolute task-host fallbacks.
- Verified:
- `pnpm --filter @kandev/web test -- --run lib/lsp/lsp-progress.test.ts lib/lsp/lsp-client-manager.test.ts`
- `pnpm exec vitest run lib/lsp/lsp-providers.test.ts --reporter=dot`
- `pnpm exec vitest run lib/lsp/lsp-providers.test.ts lib/lsp/lsp-client-manager.test.ts lib/lsp/lsp-client-manager.progress.test.ts` (45 passed, including advertised completion triggers and all 25 standard completion kinds)
- `pnpm exec vitest run lib/lsp/lsp-providers.test.ts lib/lsp/lsp-language-mapping.test.ts lib/lsp/lsp-client-manager.progress.test.ts` (33 passed, including signature-help capability gating/triggers and localized bare `4003` fallback)
- `pnpm exec vitest run lib/lsp/lsp-providers.test.ts lib/lsp/lsp-client-manager.test.ts lib/lsp/lsp-client-manager.progress.test.ts lib/lsp/lsp-language-mapping.test.ts` (55 passed, including incomplete completion-list preservation)
- `pnpm exec vitest run lib/lsp/lsp-providers.test.ts components/editors/monaco/builtin-providers.test.ts lib/lsp/lsp-client-manager.test.ts lib/lsp/lsp-client-manager.progress.test.ts lib/lsp/lsp-client-manager.document-sync.test.ts lib/lsp/lsp-language-mapping.test.ts` (63 passed, including optional-provider gating and feature-scoped TypeScript fallback)
- `pnpm exec vitest run lib/lsp/lsp-providers.test.ts components/editors/monaco/builtin-providers.test.ts lib/lsp/lsp-client-manager.test.ts lib/lsp/lsp-client-manager.progress.test.ts lib/lsp/lsp-client-manager.document-sync.test.ts` (49 passed)
- `pnpm run typecheck`
- `pnpm exec eslint lib/lsp/lsp-providers.ts lib/lsp/lsp-providers.test.ts e2e/tests/lsp/lsp-file-intelligence.spec.ts`
- `go test ./internal/agentctl/server/api ./internal/lsp/installer ./internal/tools/installer`
- `go test ./internal/tools/installer ./internal/lsp/installer` (Windows USERPROFILE result discovery included)
- `GOOS=windows GOARCH=amd64 go test -c ./internal/tools/installer`
- `go test ./internal/agent/runtime/lifecycle ./internal/gateway/websocket`
- `go test -race ./internal/gateway/websocket -run 'Test(HandleLSPConnectionChecksCapacityBeforeEnsuringExecution|ResolveLSPExecution)'` (capacity ordering and release)
- `make lint`
- `GOOS=windows GOARCH=amd64 go test -c ./internal/lsp/installer` and `./internal/agentctl/server/api`
- `pnpm e2e:run -- --project=chromium tests/lsp/lsp-file-intelligence.spec.ts` (13 passed)
- `pnpm e2e:run tests/lsp/lsp-file-intelligence.spec.ts -- --grep "runs Kotlin intelligence through the task host"` (1 production-build Chromium scenario passed)
- `pnpm exec vitest run lib/lsp/lsp-document-sync.test.ts lib/lsp/lsp-client-manager.test.ts lib/lsp/lsp-client-manager.document-sync.test.ts hooks/use-file-save-delete.test.ts components/task/task-center-panel-restoration.test.ts` (46 passed)
- `pnpm exec vitest run hooks/use-file-save-delete.test.ts hooks/use-lsp-file-opener.test.ts lib/lsp/lsp-client-manager.document-sync.test.ts components/task/task-center-panel-restoration.test.ts lib/lsp/lsp-document-sync.test.ts` (35 passed)
- `pnpm test` (1,122 files; 8,657 passed, 4 skipped)
- `pnpm lint`, `pnpm run typecheck`, `pnpm i18n:check`, and `pnpm i18n:ratchet`
- `pnpm exec vitest run lib/lsp/lsp-language-mapping.test.ts components/settings/lsp-language-cards.test.tsx components/settings/lsp-language-options.test.ts` (13 passed)
- `pnpm exec eslint lib/lsp/lsp-json-rpc.ts lib/lsp/lsp-language-mapping.test.ts components/settings/lsp-language-cards.tsx components/settings/lsp-language-cards.test.tsx components/settings/lsp-language-options.ts components/settings/lsp-language-options.test.ts`
- `pnpm exec vitest run hooks/use-lsp-file-opener.test.ts components/editors/monaco/monaco-init.test.ts lib/lsp/lsp-language-mapping.test.ts components/settings/lsp-language-cards.test.tsx components/settings/lsp-language-options.test.ts` (23 passed)
- `go test ./internal/agentctl/server/api -count=1` (categorical server-exit close included)
- `pnpm exec vitest run lib/lsp/lsp-client-manager.test.ts lib/lsp/lsp-client-manager.progress.test.ts lib/lsp/lsp-language-mapping.test.ts hooks/use-lsp-file-opener.test.ts components/editors/monaco/monaco-init.test.ts components/settings/lsp-language-cards.test.tsx components/settings/lsp-language-options.test.ts` (56 passed)
- `go test ./internal/agentctl/server/api -count=1` (categorical process-start failure included)
- `go test ./internal/agentctl/server/api -run 'TestHandleLSPStream(BridgesFramesAndStopsOwnedProcess|StopsProcessWhenForwardingToWebSocketFails|PeerCloseReleasesBlockedForwarderWrite)' -count=1` (ready-frame deadline and bridge cleanup)
- `go test ./internal/agentctl/server/api -count=1` (bounded task-host status and ready writes included)
- `go test ./internal/agentctl/server/api -run 'Test(RunLSPBridgeForwarderExitUnblocksStdinWrite|WriteLSPStdinWithTimeoutClosesBlockedWrite)' -count=1` (forwarder cleanup and virtual-time stdin cutoff)
- `go test -race ./internal/agentctl/server/api -run 'Test(RunLSPBridgeForwarderExitUnblocksStdinWrite|WriteLSPStdinWithTimeoutClosesBlockedWrite)' -count=1`
- `go test ./internal/agentctl/server/api -run 'Test(HandleLSPStreamAutoInstallHandsFirstClientMessageToBridge|LSPAutoInstallIsCanceledByClientDisconnect|LSPAutoInstallIsCanceledAndDrainedByInstanceTeardown)' -count=1`
- `go test -race ./internal/agentctl/server/api -run 'Test(HandleLSPStreamAutoInstallHandsFirstClientMessageToBridge|LSPAutoInstallIsCanceledByClientDisconnect)' -count=1`
- `go test ./internal/tools/installer -run 'Test(FindGoBinaryWithRunnerRejectsRelativeDirectories|FindGoBinaryWithRunnerUsesUserProfileFallback|GoInstallStrategyUsesRunnerEnvironmentForLookupAndResult)' -count=1`
- `go test ./internal/tools/installer -count=1`
- `node --test scripts/validate-public-docs.test.mjs scripts/notify-docs-workflow.test.mjs && node scripts/validate-public-docs.mjs`