| 📦 Turbo Go 3d7798b k33g 11h ago | 1 | # Handoff — 2026-08-31 — the Go menu, and a data race it exposed |
| 2 | |
| 3 | ## State |
| 4 | |
| 5 | **Ticket 0017 is partly done and green**, on branch `feature/go-format-lint`, **uncommitted**. |
| 6 | |
| 7 | `Alt-G` opens a **Go** menu built from `.turbo-go/tools.toml`. Each entry says where its output goes — `popup` (the default), `terminal`, or `editor` — and files the command rewrote that have no unsaved changes are re-read afterwards. **Go ▸ Create tools file** writes a starter file holding the five Go commands, four `popup` and `Run` `terminal`. |
| 8 | |
| 9 | ``` |
| 10 | … Window Snippets Go Help |
| 11 | ┌───────────────┐ ┌──────── go vet ./... — exit 1 ────────┐ |
| 12 | │ Format │ │ main.go:6:2: unreachable code │ |
| 13 | │ Lint │ │ │ |
| 14 | │ Build │ │ [ Close ] │ |
| 15 | │ Test │ └───────────────────────────────────────┘ |
| 16 | │ Run │ |
| 17 | ├───────────────┤ |
| 18 | │ Create tools… │ |
| 19 | └───────────────┘ |
| 20 | ``` |
| 21 | |
| 22 | - New `internal/tools` (95.0 %, both the file and the running of a command) and `internal/projectfile` (75.0 %). |
| 23 | - `terminal.Options.Args`, `terminal.ViewOptions`, `View.Exited()`; `Buffer.Reload` with `ErrModified`; `NewOutputDialog`; `App.tick`. |
| 24 | - Whole suite green under `-race`, five consecutive runs of the two most affected packages. Quality gate **PASS**: 0/0/0, complexity **down** from 1528 to 1513. |
| 25 | - Docs complete in EN and FR — three new pages each, six existing pages updated each. Diagram re-checked against `go list` (39 edges each side). |
| 26 | - **Verified in a real terminal**: `go build ./... — ok` with `(no output)`, `go vet ./... — exit 1` with its diagnostic, `gofmt -l -w . — ok` listing the file it rewrote, and the buffer reloading afterwards. |
| 27 | |
| 28 | Earlier the same day, PR #6 merged the snippets work. |
| 29 | |
| 30 | ## In flight |
| 31 | |
| 32 | Nothing. Finished through Phase 8. |
| 33 | |
| 34 | ## Next steps |
| 35 | |
| 36 | 1. **Commit and open the PR.** `feature/go-format-lint` is the branch. |
| 37 | 2. **Finish ticket 0017.** It also asked for `go mod init` + `touch main.go` — creating a project from nothing, which is a different shape from running a command in an existing one (it needs a name, and there is no project yet). Not done, and the ticket should stay open for it. |
| 38 | 3. **Tickets.** 0002, 0003, 0006, 0007, 0009, 0013, 0014 are implemented and open; 0017 is partly implemented. |
| 39 | |
| 40 | ## Open questions / blockers |
| 41 | |
| 42 | - **A modal popup holds the whole editor while a command runs.** That was chosen knowingly and is documented, with `output = "terminal"` as the way out for any command it annoys you on. If it turns out to annoy generally, a non-modal output window is the alternative — and it is a different feature, not a tweak. |
| 43 | - **`output = "editor"` shows a popup first, then a window.** The popup is how you watch it and how you stop it; the window arrives when you close the popup. It reads oddly written down and is fine in use, but it is worth a second look with real output. |
| 44 | |
| 45 | - **`go mod init` is not in the menu**, and does not fit the current shape: every other tool runs in the project that already exists, whereas this one creates it and needs a module path typed. It wants a prompt dialog and probably its own item rather than a `tools.toml` entry. |
| 46 | - **Nothing jumps to a compile error.** `go build` prints `internal/app/app.go:42:3: …` in the terminal window and you have to open the file yourself. Parsing that output and jumping would be a real win and is a feature of its own — it needs a per-language error format, and the output lives in a terminal emulator rather than in a captured buffer. |
| 47 | - **A menu panel still does not scroll** (carried over from the snippets handoff). A `tools.toml` with twenty entries draws a panel taller than the terminal and the bottom is clipped. |
| 48 | |
| 49 | ## Watch out for |
| 50 | |
| 51 | - **Verify a new test by breaking the code it covers.** Two tests in this session passed while proving nothing, both because the test drove the step under test: `waitForLoopTurn` called `reloadAfterTools` itself (so the test passed with that step deleted from the loop — hence `App.tick`), and the process-group test killed the shell before it had forked (so it passed without the fix — it now waits for the child to print). Neither was visible by reading. |
| 52 | - **Stopping a command must take the process group.** Killing only the shell leaves a grandchild holding the output pipe, and the reading goroutine blocks until *it* ends — 20 seconds in the test suite, and for `go test ./...` it would be every test binary. `processGroup()` and `killGroup()` are build-tagged; `cmd.WaitDelay` is the backstop for anything that escapes. |
| 53 | |
| 54 | - **A constructor that starts a goroutine must take its callbacks as parameters.** `terminal.NewView` started the reader and callers assigned `OnChange`/`OnExit` afterwards — a data race present since the terminal feature that `-race` never caught, because a shell takes longer to produce its first output than an assignment takes to run. `sh -c "echo x"` finished immediately and the detector fired at once. `ViewOptions` fixes it structurally; do not add an assignable callback back. |
| 55 | - **A finished terminal must not swallow keys.** It used to write every key to the dead shell, where the write failed silently and the key was consumed anyway, so `Ctrl-W` could never close the window. `View.HandleKey` returns false after `Exited()`, except for the scrolling keys. `TestAFinishedTerminalStopsTakingKeys` covers it. |
| 56 | - **`Buffer.Reload` must refuse over unsaved work.** That restriction is the whole safety of it. `TestReloadRefusesToThrowAwayUnsavedWork` pins it, and `TestAToolLeavesAModifiedBufferAlone` pins the app end of it. |
| 57 | - **`internal/buffer`'s atomic write was deliberately left out of `projectfile`.** It preserves the mode of the file it replaces, because it is saving over something the user already had; `projectfile` creates a file with a fixed `0644`. They look alike and are not the same operation — merging them would mean parameterising the mode and losing the different error text. |
| 58 | - **`projectfile.Write` already does `MkdirAll`.** The three `Create` functions used to do it themselves; do not add it back. |
| 59 | - **The app tests for tools drive the event loop by hand.** `Run` is not running, so `waitForLoopTurn` calls `a.reloadAfterTools()` itself. A test that waits for a reload without calling it will hang until its deadline. |