| 🛟 Updated. 28d5985 k33g 13h ago | 1 | # Handoff — 2026-09-18 — first launch: LSP dead until save + relaunch → fixed |
| 2 | |
| 3 | ## State |
| 4 | |
| 5 | The user reported that on turbo-go's first launch the LSP does not work, and that saving, quitting and relaunching fixes it. Diagnosed by reading the code, confirmed by the user (the window had started **Untitled**), and **fixed in this session** through a full `/methodical-dev` cycle. Not committed. |
| 6 | |
| 7 | The defect: a buffer that gains its path through Save As was never announced to the language server. `Language.DidOpen` skips path `""` (an Untitled window has nothing to announce), `afterSave` sent only `didSave`, and `announceOpenDocuments` runs once (`a.announced`) — and a server ignores `didChange`/`didSave` for a document it was never told is open, so every LSP feature stayed dead in that window until a restart reopened the file under its name. |
| 8 | |
| 9 | The fix, all in `app`: |
| 10 | |
| 11 | - `app/language.go` — the `documents` map is keyed by absolute path (`pathKey`, the renamed `diagnosticKey`), so `Knows` does not depend on how a path is spelt. Without this, the next change would re-announce a document opened relative and saved absolute. |
| 12 | - `app/actions_file.go` — `announceSaved`, called from `afterSave` so both save paths get it: `didOpen` for a path the server does not `Knows`, `didSave` otherwise. And `save` remembers the buffer's path before `SaveAs` rewrites it; `renamed` (absolute comparison) triggers `DidClose` of the old document on a genuine rename, so no ghost document stays open on the server. |
| 13 | |
| 14 | Tests: 5 new (one in `app/diagnostics_test.go`, four in the new `app/save_test.go`), each verified by breaking the code it covers. Suite green, `-race` green on `app`. Quality run #30 PASS 0/0/0, complexity 2308. Docs EN + FR: `reference/app.md` (a `Knows` row, a paragraph on what saving sends), `how-to/talk-to-a-language-server.md` (a "file created inside the editor" variant), plus `app/README.md`. |
| 15 | |
| 16 | ## In flight |
| 17 | |
| 18 | Nothing half-written. The tree also still carries the **uncommitted Windows terminal work of 2026-09-17** (disjoint files — see that day's handoff); this session's change sits on top of it on `main`. |
| 19 | |
| 20 | ## Next steps |
| 21 | |
| 22 | 1. Commit (the user does this, or asks for it): the fix, its tests, the docs, and `.memory/`. |
| 23 | 2. Drive it once against a real gopls: start turbo-go, type into an Untitled window, save as `x.go`, complete — the one verification the fake server cannot give. |
| 24 | 3. Tag and release turbo-core, then re-pin the six editors so they inherit the fix. |
| 25 | |
| 26 | ## Open questions / blockers |
| 27 | |
| 28 | - The user's report ("la 1ère fois") may also have had a second ingredient: gopls's very first run on a machine populates its caches and answers empty completions while loading, even once `LSP: ready` shows. Unmeasured; worth remembering if a report comes back after this fix ships. |
| 29 | |
| 30 | ## Watch out for |
| 31 | |
| 32 | - Do not move the announcement into `Buffer.SaveAs`: it would put the language server underneath the text type. Rejected on purpose. |
| 33 | - Autosave never renames (it rewrites the buffer's own path), which is why `DidClose` lives in `save` alone. If autosave ever learns to rename, that assumption breaks. |
| 34 | - `waitForMethod` in the app tests polls up to 2 s; a test asserting a notification was *not* sent must wait for a later one first (see `TestSaveAsToTheSamePathClosesNothing`), or it asserts on a race. |