Handoff — 2026-08-31 — the Go menu, and a data race it exposed
State
Ticket 0017 is partly done and green, on branch feature/go-format-lint, uncommitted.
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.
… Window Snippets Go Help
┌───────────────┐ ┌──────── go vet ./... — exit 1 ────────┐
│ Format │ │ main.go:6:2: unreachable code │
│ Lint │ │ │
│ Build │ │ [ Close ] │
│ Test │ └───────────────────────────────────────┘
│ Run │
├───────────────┤
│ Create tools… │
└───────────────┘
- New
internal/tools(95.0 %, both the file and the running of a command) andinternal/projectfile(75.0 %). terminal.Options.Args,terminal.ViewOptions,View.Exited();Buffer.ReloadwithErrModified;NewOutputDialog;App.tick.- 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. - 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). - Verified in a real terminal:
go build ./... — okwith(no output),go vet ./... — exit 1with its diagnostic,gofmt -l -w . — oklisting the file it rewrote, and the buffer reloading afterwards.
Earlier the same day, PR #6 merged the snippets work.
In flight
Nothing. Finished through Phase 8.
Next steps
- Commit and open the PR.
feature/go-format-lintis the branch. - 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. - Tickets. 0002, 0003, 0006, 0007, 0009, 0013, 0014 are implemented and open; 0017 is partly implemented.
Open questions / blockers
-
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. -
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. -
go mod initis 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 atools.tomlentry. -
Nothing jumps to a compile error.
go buildprintsinternal/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. -
A menu panel still does not scroll (carried over from the snippets handoff). A
tools.tomlwith twenty entries draws a panel taller than the terminal and the bottom is clipped.
Watch out for
-
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:
waitForLoopTurncalledreloadAfterToolsitself (so the test passed with that step deleted from the loop — henceApp.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. -
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()andkillGroup()are build-tagged;cmd.WaitDelayis the backstop for anything that escapes. -
A constructor that starts a goroutine must take its callbacks as parameters.
terminal.NewViewstarted the reader and callers assignedOnChange/OnExitafterwards — a data race present since the terminal feature that-racenever 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.ViewOptionsfixes it structurally; do not add an assignable callback back. -
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-Wcould never close the window.View.HandleKeyreturns false afterExited(), except for the scrolling keys.TestAFinishedTerminalStopsTakingKeyscovers it. -
Buffer.Reloadmust refuse over unsaved work. That restriction is the whole safety of it.TestReloadRefusesToThrowAwayUnsavedWorkpins it, andTestAToolLeavesAModifiedBufferAlonepins the app end of it. -
internal/buffer's atomic write was deliberately left out ofprojectfile. It preserves the mode of the file it replaces, because it is saving over something the user already had;projectfilecreates a file with a fixed0644. They look alike and are not the same operation — merging them would mean parameterising the mode and losing the different error text. -
projectfile.Writealready doesMkdirAll. The threeCreatefunctions used to do it themselves; do not add it back. -
The app tests for tools drive the event loop by hand.
Runis not running, sowaitForLoopTurncallsa.reloadAfterTools()itself. A test that waits for a reload without calling it will hang until its deadline.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
|