History
Append only. One dated entry per session. Never rewrite or delete an entry, including your own.
2026-09-01 — Extracted from turbo-go
- Goal: ticket 0001 in
turbo-editors— extract the code common to Turbo Go and a future Turbo Rust into a shared, versioned library. This repository is the result. Options chosen by the user up front: the library holdsapptoo (so an editor is a command, a profile and a scanner); the language scanner lives in its own editor; the editors depend on it withrequireplus a committedreplace; per-editor configuration directories rather than a shared one. - Changes: fourteen packages moved from
turbo-go/internal/*to the root of this module and made public, plus two new ones.profileis new and is the seam.syntaxgained a stringLanguage, aDefinition/Registerextension point, and an exported scanner toolkit, and lost the Go scanner.themetook auserDir stringin place of reading an environment variable itself.settings,snippets,toolsandlsptook aprofile.Profile.apptook one too, gainedProjectRoot, and exportedTick,HandleandActiveView.lsp.NewClienttook the editor's name. - Decisions: a struct rather than an interface for the profile, because a third editor should be able to fill in a literal from the reference rather than read somebody else's implementation. The language an editor is for stays out of the library, so that "what does it register?" remains the first question about a new editor.
syntax.Registeris package-level state, followingimage.RegisterFormat, because the alternative threads a registry through every call site that asks what language a file is.themetakes a directory while everything else takes a profile — deliberately inconsistent, because it needs exactly one thing. - Tests: every suite moved with its package and was reworked onto a fictional editor called Turbo Test, with its own slug, menu and templates. That is the point: a test in the library that expected the Go menu would be testing an editor's choices from inside the library those choices are made outside of. New:
profile(8 tests),app.ProjectRoot(6), thesyntaxregistry (6), and threelsp.FindServertests covering the profile's search directories. Moved out to turbo-go: the Go scanner's 12 tests, the three templates' content tests, and both real-gopls tests. - Two tests were falsified on purpose before being trusted: a deliberately unreadable
syntax.commentin a new theme produced the expected failure, andTestRegisteredListsWhatThisPackageColourswas rewritten to assert presence rather than an exact count after an example in the same test binary registered a language of its own and made the count order-dependent. - Verified end to end: the whole suite green and green under
-race; the tutorial run verbatim in a throwaway module, producing a working Zig editor whose menu bar readsFile Edit Search Run Options Window Snippets Zig Helpand whose keywords, strings and numbers come out coloured in a real pty. - Quality: PASS, no refactoring needed. 0 errors, 0 warnings, 0 smells, complexity 1584.
- Docs: a new documentation set, 12 pages × EN + FR — a tutorial that builds an editor, five how-to guides, four reference pages and two explanations — plus a
README.mdper package and a drawio diagram generated fromgo listand checked against it edge for edge (41 = 41, nothing stale).
2026-09-01 — Catppuccin Latte and Frappé
- Goal: user request, mid-session — "ajoute un theme facon catppuccin/vscode la version frappé et la version latte".
- Changes:
theme/themes/catppuccin-frappe.tomlandtheme/themes/catppuccin-latte.toml, each stating all 67 style keys. No Go code changed: themes are embedded with//go:embed themes/*.toml, so adding one is adding a file. - Decisions: the published palettes are used unchanged — a theme called Catppuccin that is not those exact values is a different theme with a borrowed name. Catppuccin's own style guide is followed where it has an opinion (mauve keywords, green strings, peach numbers, blue functions, yellow types, overlay comments) and the palette's surface ladder does the rest. Latte needed a different mapping from Frappé rather than different numbers: its accents are far more saturated, because they have to carry against paper, and using Frappé's mapping with Latte's numbers gives a legible page and an illegible menu bar.
- Tests: no new test files — the five existing theme rules now run over eight themes instead of six. The rules were falsified for the new themes specifically: a
#e9ebf0comment in Latte producedsyntax.comment: text is 6 from its background, want at least 64, and a temporary test confirmedAvailable()really returns all eight, so nothing passed vacuously. - Verified on the wire in a real pty, reading back the true colours: Frappé draws
fnin#ca9ee6, a raw string in#a6d189,println!in#e78284,3u8in#ef9f76and a type in#e5c890; Latte the same mapping in#8839ef,#40a02b,#d20f39,#fe640band#df8e1d. Those are the published values exactly. - A pre-existing documentation defect surfaced and was fixed. Both editors' tutorials said "press ↓ five times to reach turbo-dark". Driving the real binary through a pty showed the Theme dialog opens on the current theme —
turbo-classic, highlighted97;44while every other row is30;47— so it was always one press, not five. Adding two themes would have made a wrong number wronger; it now says one, and says why. - Quality: PASS. 0/0/0, complexity 1584 — unchanged, the themes are data.
- Docs: the themes table and the embedded list in both editors'
reference/themes.md, the inherit-from-a-similar-ground advice inhow-to/write-a-theme.md, and the theme count in this library's README, package reference, what-belongs-here page and tutorial — all in both languages.
2026-09-01 — Tool parameters, and the release tooling
- Goal: two things the user asked for together — a tool whose command needs a value (
go mod init <module>) must be able to ask for it, and turbo-core must become versionable. They asked where the parameters belonged; the answer is here, becausetoolsparses the file andappruns the command and neither knows what language it is editing, so putting it in an editor means writing it twice. - Options chosen by the user up front:
{{label}}placeholders only, with no new TOML keys; shell-quoted by default with a{{label...}}escape hatch for verbatim; and release tooling plus av0.1.0tag rather than only one or the other. - Changes (parameters): new
tools/placeholder.go—Placeholder,Tool.Placeholders(),Tool.Fill(),ShellQuote, and a load-time check wired intocheck. Newapp.ParametersDialogandapp.MaxParameterFieldsinapp/dialogs.go;RunToolsplit intoaskForParametersandrunFilledTool, withApp.toolValuesremembering per tool for the session.runCapturednow takes the filled command, so the popup's title shows what actually ran. - Changes (release):
01-release.tag.sh, aversiontarget, anddocs/*/how-to/release-the-library.mdrewritten around the script. Both editors' starter tools templates gained a comment block teaching the syntax. - Decisions: double braces, not single —
awk '{print $1}'andfind . -exec rm {} +are ordinary tools-file entries, and a falsification run showed exactly what single braces would do to the first (awk ''''). Quoted by default because the failure mode of raw substitution is silent. Nothing persisted to disk: the project's directory holds what the project decided. Refused at load, not at run, for an unclosed{{. A dialog that will not fit is refused with a message rather than drawn with its OK button off-screen. - A recursion, found by it hanging. The release script runs
make check,make checkruns the suite, and the suite contains tests that run the release script. It hung the first time it was run — and it was not a test-only hazard: a real release would have recursed identically. The script now exportsTURBO_CORE_RELEASINGbefore calling make, and those two tests skip themselves when they see it. - Tests: 18 in
tools/placeholder_test.go, 13 inapp/parameters_test.go, 10 in a new rootrelease_test.go(two of which run the script for real against a throwaway bare remote), and 2 per editor for the templates. Six were falsified before being trusted — single braces, no quote escaping, no load-time check, RunTool ignoring placeholders, values not remembered, and the unfilled command being run — each producing the expected failure. - A latent test bug surfaced on the way:
TestTheInstalledBinaryDoesNotReportAnUnknownVersionasserted before checking whether it was in a git checkout at all, so it failed in a tree with no.gitwhereunknownis the correct answer. Fixed in both editors. - Verified end to end in a real pty:
Alt-G, Enter, a box titled Echo with a field labelledwho, typingworld→ the popup readsecho hello 'world' — okand printshello world; a{{words...}}tool reaches the shell asecho -n a b c; and running the same tool twice opens the box already holdingagain. - Verified the release end state: a local file:// module proxy carrying turbo-core
v0.1.0was built, both editors were switched torequireit with thereplacedropped, and both suites pass. So dropping the directives is known to work. - Quality: PASS in all three. 0 errors, 0 warnings, 0 smells; turbo-core complexity 1584 → 1610, the editors unchanged at 37 and 98.
- Docs: a reference section, a how-to section and an explanation section in each editor, both languages; the
appreference and the release how-to in turbo-core, both languages;tools/README.mdandapp/README.md. The diagram was re-checked againstgo listand is unchanged — no package was added.
2026-09-01 — v0.1.0 published, and the release page script
- Goal: the user ran
01-release.tag.shwithTAG=v0.1.0, then asked for a script that creates the release itself, and what "drop the two replace directives" meant. - Changes: new
02-release.publish.sh;docs/*/how-to/release-the-library.mdgained a step for it and had its "developing across the three repositories" section rewritten, since the directives are gone; both editors'go.modlost theirreplaceand theirgo.sumgained the module's checksum. - Decisions: the script builds its JSON with jq, which both escapes the notes properly and sidesteps the
read -r -d ''idiom that stops the editors' own02from takingset -e. It tells the HTTP statuses apart — 201, 409, 401/403, 404 — because the API's raw JSON leaves the reader to guess which of four problems this is. It takes--dry-run, which is the only way it can be exercised here: the real call publishes on somebody's behalf. There is no 03 or 04: those build and attach binaries, and a library's artefact is its tag. - The release-page links are absolute and pinned to the tag. A release page is not inside the repository tree, so a relative
docs/en/README.md404s, and a link to the branch rots as the branch moves. - Two defects found by running it. The tag guard conflated "the tag is not on origin" with "I could not reach origin" and refused a perfectly good release from a machine with no ssh key —
ls-remoteexits 2 for the first and 128 for the second, and it now says which. AndTestThePublishScriptBuildsItsJSONWithJqfailed on the script's own comment, which mentions the idiom it avoids; it reads code lines only now. - A checksum mismatch that was mine, not the release's. Dropping the replaces gave
SECURITY ERROR … checksum mismatchagainst sum.golang.org. The proxy'sv0.1.0pointed at exactly the tagged commit; what differed was the local module cache, poisoned by the hand-madefile://proxy zip an earlier verification had built. Clearing that one module put it right. Worth knowing before anybody concludes a release was tampered with. - Tests: 8 more in
release_test.go(18 total), two of which run the publish script's dry run against a throwaway copy — including one that checks a quote in the notes survives as data. One was falsified by putting the forbidden idiom back as code. - Quality: PASS in all three, 0/0/0.
2026-09-01 — v0.1.0 released, and the family tagged
- Goal: the user committed and released everything, and asked for the record to be brought up to date. This entry is what was verified rather than what was intended.
- Verified from the repositories and the Codeberg API, not from the session: turbo-core v0.1.0 at
f1f0375with a release page; turbo-go v0.2.2 atd64410c, exactly its HEAD; turbo-rust v0.1.0 at2d5dbec, exactly its HEAD and its first release ever. All three working trees clean, all three onmain. - turbo-core's HEAD is one commit past its tag.
bc4a464— the publish script, its tests and the release how-to — landed after v0.1.0 was cut. No API changed, so no consumer is affected; the v0.1.0 release page's doc links are pinned to the tag and therefore show the older how-to. Recorded rather than fixed: moving a published tag is the one thing the release script exists to refuse. - Both editors depend on the published module.
require codeberg.org/turbo-editors/turbo-core v0.1.0, no activereplace,go.sumcarrying the checksum that matches sum.golang.org. - One wart left in both editors'
go.mod: the old replace block is commented out rather than deleted, and its text now says "drop it once the version above is tagged and published", which is already done. It is inside a released commit, so it was left alone and written down instead. - Nothing was built or changed in this entry — no code, no tests, no docs. The suites and the gate were last measured at the previous entry and are unchanged.
2026-09-01 — Dockerfile, compose, YAML and XML colouring (ticket 8)
- Goal: ticket 8 — "add syntax for Dockerfile, compose file, yaml, xml", in three tasks: the support in turbo-core, then its use in turbo-go, then in turbo-rust.
- Changes (core):
syntax/yaml.go,syntax/yaml_block.go,syntax/yaml_key.go,syntax/dockerfile.go,syntax/xml.go, and their three test files.Definitiongained aFilenames []stringfield andLanguageOfa name step between the extension and the shebang. Three newLanguageconstants and threeRegistercalls; eight languages are now built in. - Changes (editors): both editors' snippets template lists the nine language names the editor now knows, with a new test that iterates
syntax.Registered()so the list cannot fall behind the registry again. - Decisions: a compose file is just YAML — a dialect would be Docker's schema, kept here, going stale. XML gets its own scanner rather than borrowing HTML's, for CDATA: HTML colours
<not>inside a CDATA section as a tag, which is backwards in exactly the files that have one; the carry says which construct is open so a-->inside CDATA does not close it.Filenamesmatches the stem as well as the whole name, soDockerfileanswers forDockerfile.dev; an all-extension name such as.gitignorehas an empty stem and matches nothing, which is what stops the rule becoming a wildcard. - Two defects found by reading real output, not by a test.
image: nginx:1.27andurl: http://xcame out in three spans each: a YAML colon is a separator only when a space or the end of the line follows it. Thengolang:1.26split at the colon while YAML keptnginx:1.27whole — the Dockerfile scanner needed:in its word runes for the same reason. Both now have a test that walks every column of the span. - Tests:
yaml_test.go,dockerfile_test.go,xml_test.go, plusTestTheEightBuiltInLanguagesAreRegisteredandTestEveryBuiltInLanguageActuallyColoursSomethinginhighlight_test.go; 275 assertions pass insyntax. One name clash fixed —TestABareAmpersandIsLeftAlonealready existed inhtml_test.go. - Quality: first run FAILed with 4 smells — three
qlty:boolean-logicandqlty:file-complexityonyaml.goat 52. Fixed by extracting the rune sets to named constants (yamlFlowRunes,yamlNumberRunes,dockerfileWordRunes) and splitting the YAML scanner across three files. Final: PASS 0/0/0 in all three; turbo-core complexity 1610 → 1718, the editors unchanged at 37 and 98. - Verified in a real pty in both editors: a
Dockerfile, acompose.yamland apom.xmlopened and coloured, with the CDATA contents arriving as green (string) rather than as markup. - Docs: the three new sections in each editor's
reference/languages.mdin both languages, with the recognition and class tables brought up to date; the name-lookup step added to turbo-core'show-to/add-a-language.mdand thesyntaxreference in both languages; the language counts corrected across both editors' architecture and colouring explanations, READMEs and snippets references;syntax/README.md. The diagram was re-checked againstgo list— no package was added, so it is unchanged. - Left for the user: turbo-core needs a v0.2.0 tag and release before the editors' branches build, because
Definition.Filenamesand the three new constants are new public API. Both editors'go.modnowrequire v0.2.0with no activereplace.
2026-09-01 — The About box named the wrong language
- Goal: the user reported that Turbo Rust's About box talks about Go.
- Changes:
app/actions_view.go—aboutTexttakes the language andShowAboutpassesa.profile.Language. The sentence was a literal"A Turbo C-style editor for Go,"left behind by the extraction: correct in turbo-go, drawn unchanged by every editor built on the library.app/README.mdanddocs/*/reference/profile.mdsay so now. - Tests:
TestTheAboutTextNamesTheLanguageTheEditorIsFor, falsified by putting the literal back — it reproduces the reported bug exactly. - Verified in a real pty: Turbo Rust's About box reads "A Turbo C-style editor for Rust, / written in Go."; Turbo Go's still reads "for Go". The second line is right in both — both editors are written in Go.
- Worth noting: turbo-rust's own documentation already showed the corrected text. The docs were right and the code was wrong, which is why nobody caught it by reading.
- Quality: PASS 0/0/0, complexity unchanged at 1718.
2026-09-01 — Tickets 9 to 14: settings applied on save, symmetric project menus, readable comments
- Goal: tickets 9–14. Four of them land here; ticket 9 is editor-side.
- Ticket 12 — settings did not take effect until a restart.
UseSettingswas called once, frommain.app/project.gogainedreapplySettings, run from a newafterSavethatApp.saveandApp.writeQuietlynow share. It matches on the path rather than ona.settingsPath, so creating the file counts; only autosave is re-applied; a file that no longer parses saysSaved, but not applied:and the old values stay. The status bar now saysApplied .turbo-go/settings.toml — autosave on (2s), because the report was "it does not work" and an invisible fix invites it again. - Tickets 11, 13, 14 — the three project files behave alike.
projectFiledescribes one of them;openProjectFile,hasProjectFileandnotdo the rest. Six menu items: create greyed once the file exists, open greyed until it does. New public API:OpenTools,OpenSnippets,HasProjectTools,HasProjectSnippets. Both branches ofcreateProjectFilestay reachable from the API, since a caller that is not a menu has no greying-out. - Ticket 10 — comments were unreadable. Measured before touching anything:
syntax.commentwas the worst or near-worst reading pairing in six of the eight shipped themes, and under 4.5:1 WCAG in every one. turbo-classic 4.05, borland-light 3.91, cappuccino 3.63, cobalt 3.62, monochrome 3.57, turbo-dark 3.25. All six raised past 4.5 while staying the quietest colour in their theme. The two Catppuccin themes (2.87, 2.83) are left alone: their palettes are published, which is a decision already in force. - Decisions: the theme is not re-applied on save — Options ▸ Theme is the live path and already writes back here, and a
-themeflag is the more explicit statement for its session. The new contrast test covers comments only — punctuation is quieter still in several themes and stays that way, being recognised by shape rather than read. A blanket WCAG floor was rejected on evidence: 84 of 432 theme/key pairings are under 4.5, most of them Catppuccin's published palette, so the honest scope is the one class the ticket named. - Two measures, both kept.
channelDistanceanswers "can the eye see these are two colours";contrastRatioanswers "can this be read". Turbo Classic's comments cleared the first by a mile — 128 against a floor of 64 — and failed the second. That is why nothing caught this for as long as it existed. - Tests: 7 in
app/project_test.gofor the re-apply, 5 more for the menu pairs and the open actions, 2 ineditor/view_test.gofor contrast and for the exemption list. All falsified: removing the singlereapplySettingscall fails exactly 7; dropping either half of a menu pair fails the pair test; restoringgrayfails turbo-classic at 4.05; naming an unshipped theme in the exemption list fails. Two first drafts passed vacuously and were rewritten. - Verified in a real pty, both editors: all six menu items flipping between
30;47(available) and90;47(greyed); the new comment colour arriving as38;2;143;143;143; the created settings file holdingautosave = true; andApplied .turbo-go/settings.toml — autosave on (2s)on the status bar afterF2. - Quality: PASS 0/0/0 in all three; turbo-core complexity 1718 → 1724, the editors unchanged at 37 and 98.
- Docs: the app reference and
app/README.md,theme/README.md,editor/README.md,settings/README.md, the starter-files how-to; and a newhow-to/test-without-publishing.mdin EN and FR, which the release how-to now points at instead of explainingreplaceitself.go.workgitignored in all three. - Also fixed, found while sweeping: turbo-rust's French tools reference said its own menu items "restent dans Go".
2026-09-02 — Code navigation: eight more LSP questions, and the problems the server already sends
- Goal: after a survey of what else the language server could answer, the user chose the largest of three scopes — navigation, symbol search and diagnostics in one cycle.
- Changes (
lsp):References,Implementation,TypeDefinitionsharing onelocationRequestwithDefinition; newsymbol.gowithDocumentSymbols,WorkspaceSymbols,SymbolandSymbolKind; the matching client capabilities. - Changes (
app):code.go(the four location questions,chooseLocation,locationLabels),symbols.go,problems.go, a Code menu holding all eight items,Shift-F12andCtrl-Twired, and theLanguagefaçade extended with five requests plusAllDiagnostics. - Changes (
editor):marks.go—Severity,SetMarks, and the gutter glyph, drawn in the threediagnostic.*theme keys that had been defined in every shipped theme and drawn nowhere at all. - Decisions: one answer jumps, several list —
GoToDefinitionused to takelocations[0], so an interface with four implementations sent you to one of them at random.editordoes not importlsp; a mark is aneditor.Severityandapptranslates. The mark uses the gutter's separator column, so it costs no layout, at the accepted price that hiding line numbers hides the marks. The two symbol shapes are told apart byselectionRange, notchildren— children are optional and the mistake is silent. A symbol is located by its name's range, not its declaration's. Nothing needs a selection: the protocol takes positions, so requiring one would be an invented step. - A pre-existing defect found by the pty run, not by any test. The Problems window listed two errors and the gutter showed none: a server publishes absolute URIs and a buffer opened as
turbo-go main.goholds the relative path, so the two never met. The status bar had been failing the same way since long before marks existed — invisibly, because no error to show and no error findable look identical. Fixed withdiagnosticKey; every unit test had passed because every unit test opened its file by an absolute path. - A shortcut withdrawn on evidence: Symbol in file was to be
Ctrl-Shift-O, and a terminal cannot tell that fromCtrl-O. It ships with no key rather than one that does nothing. - Tests: 12 in
lsp(four requests × three answer shapes,includeDeclaration, the declared capabilities), 11 inlsp/symbol_test.go(the three decoding decisions each have their own), 20 inapp/code_test.go, 3 inapp/diagnostics_test.go, 7 ineditor/marks_test.go. Every one falsified. One passed vacuously — a zero severity draws NUL, which a simulated screen shows as a blank — and was rewritten to assert onmarkOfinstead. - Verified against a real gopls, installed for the purpose:
Implementations (2)listingFrenchandEnglishwith their line text;References (2);Symbols in file (6)with kind tags;Problems (2)on a file that does not compile; and×marks on lines 6 and 7 drawn in91;44;1— turbo-classic'sdiagnostic.error. - Quality: PASS 0/0/0 in all three; turbo-core complexity 1724 → 1802, the editors unchanged.
- Docs:
lsp/README.md,app/README.md,editor/README.md, the app reference in EN+FR; in both editors a newhow-to/ask-about-code.mdin EN+FR, the Code menu in the menus reference, the two new keys in the keyboard reference, and a "Nine questions, one connection" section in the colouring-and-completion explanation. - A near miss worth recording: the new how-to was first written over the existing
navigate-code.md, which is a different guide about moving around a file. Restored from git and written under its own name, with the old page linking to it. - Follow-up the same day: the user asked whether the LSP features were documented for users. They were —
how-to/ask-about-code.md, EN and FR, both editors — but the neighbouringenable-completion.mdstill had a "what else the server gives you" section listing three keys and no mention of the Code menu, Problems, or the gutter marks. Fixed in all four files. That is the "adapting is not substituting" trap from theturbo-new-editorskill, met on a page I had not thought to re-read: a new feature makes its neighbours stale, and the neighbours are where a user already is.
2026-09-02 — Ticket 19: better code editing
- Goal: ticket 19 — double-click to select a word, insert line, delete line. Stacked on
feature/code-navigationat the user's choice, so one release carries both. - Changes (
buffer):InsertLineAbove,DeleteLineandSelectWord, all throughReplaceRangeso each is one undo step. - Changes (
editor):click.go— click counting against an injectable clock;InsertLineandDeleteLineon the view;Ctrl-N,Ctrl-YandCtrl-Rin the key switch. - Changes (
app):InsertLineandDeleteLine, and two new Edit-menu items. - Decisions:
Ctrl-Ydeletes, redo moves toCtrl-R— put to the user, who chose the Turbo C binding over the existing habit.Ctrl-Shift-Zwas ruled out on the terminal's own limits, the same wayCtrl-Shift-Owas last cycle. Insert goes above with the cursor staying on its text, also the user's choice and also Turbo C's. The new line is blank, not indented. A double click off a word selects nothing. After a double click a drag extends from the word's start, by character — not word-by-word, which was out of scope, but better than the word collapsing to one character at the first twitch. - Tests: 12 in
buffer/line_test.go, 10 ineditor/click_test.go, 3 more ineditor/events_test.go, 2 inapp. Falsified throughout. - One falsification did not bite, and is recorded rather than dressed up: removing the one-line case from
DeleteLinechanges nothing, because the range it would otherwise build starts on line −1 andclamppulls it back to 0. The case is kept for saying what it means, and the code now explains why. - Two tests were wrong, not the code: a column index off by one in a fixture, and a "drag after a double click" that released the button before moving — which is not a drag. Both corrected.
- Verified in a real terminal:
Ctrl-G 2,Ctrl-Ytwice,Ctrl-N,F2on a four-line file leaves exactly the two lines and the blank one expected; and a real SGR double click onalphafollowed by typingXleavesX beta. - Quality: PASS 0/0/0 in all three; turbo-core complexity 1802 → 1810.
- Docs: the keyboard and menus references in both editors and both languages — including that
Ctrl-Yno longer redoes, which is the half a user notices; a new section inhow-to/navigate-code.md;buffer/README.md,editor/README.md, and the app reference in EN+FR.
2026-09-03 — A third editor exists: the family count, and a sweep for hardcoded languages
- Goal: register turbo-python in the family. The library itself was not changed — turbo-python was built against published
v0.4.0and needed nothing new, which is the point of the seam. - Changes: the editors list in
README.md; the counting sentence indocs/{en,fr}/README.md;profile/profile.go's package comment; "both editors" → "every editor" inhow-to/release-the-library.md,how-to/write-the-starter-files.mdandexplanation/architecture.md, EN and FR;how-to/test-without-publishing.mdrewritten around a four-module workspace, with a new paragraph on listing nested modules in theuseblock — the trap that reads as a broken project rather than a workspace problem. - History was left alone. "
tools.DefaultMenuwas correct for a year and wrong the moment there were two editors" is a true sentence about the past; rewriting it to say three would make it false. Only live claims changed. - The
.gostrings were swept for hardcoded language names, which is what a third editor is for. Every hit was a false positive ("Go to definition","Ctrl-G Go to line"), a doc-comment example of the seam (// "Go", "Rust" — which a profile chooses), or a true statement about the implementation ("written in Go."). Nothing to fix: the About box's language had already been moved into the profile after Turbo Rust exposed it, and the pty run confirms it now reads "A Turbo C-style editor for Python". - Quality: PASS 0/0/0, complexity 1810, unchanged. Suite green.
- Not committed. The changes are in the working tree on
mainat14aae7c.
2026-09-09 — a fourth editor, and no library change
- Goal: register turbo-moonbit in the family, and find out whether a fourth editor needed anything from the library.
- Changes:
README.md's editors list; the counting sentence indocs/{en,fr}/README.md;profile/profile.go's package comment; the four-module workspace indocs/{en,fr}/how-to/test-without-publishing.md; the "Four editors are built on it" and extension-point entries in.memory/summary.md. No code change. - The sweep found nothing.
grep -rnE '"[^"]*\b(Go|Rust|Python|golang|cargo|gopls|rust-analyzer|pylsp)\b[^"]*"' --include='*.go'over the whole library: every hit was a verb ("Go to line","F12 Go to definition"), a doc-comment example of the seam (profile.go,tools.go,lsp/client.go,app/toolchain.go), an import path, or the true statement"written in Go."— which every editor in this family is. The About box that said "an editor for Go" was fixed when Turbo Rust exposed it; nothing of that shape remains. - What a fourth editor did expose is not in this repository's code but is worth recording here, because both are the library's to decide:
workspace/didChangeWatchedFilesis never sent. moon-lsp works a package's file list out once, so a.mbtcreated after the server started is never diagnosed — File ▸ New, save, and the gutter stays blank until a restart. Confirmed at the raw protocol level. Sending the notification would fix it for every editor.turbo-classicdrawssyntax.attributeandsyntax.identifierin the same plain yellow. The other seven themes distinguish them. It makes Turbo MoonBit's labelled arguments and attributes invisible as such, and Turbo Rust's#[derive]equally so.
- History was left alone. "
tools.DefaultMenuwas correct for a year and wrong the moment there were two editors" is a true sentence about the past.
2026-09-09 (later) — a light monochrome, and a name that had to keep working
- Goal: the user asked for a white counterpart to the
monochrometheme. - Changes:
theme/themes/monochrome.tomlrenamed tomonochrome-dark.toml; newtheme/themes/monochrome-light.toml;retiredNamesintheme/load.gosomonochromestill loads; six new tests intheme/theme_test.go;theme/README.md. Nine themes ship now. - Decisions:
- Renamed to a symmetric pair rather than adding an asymmetric one, at the user's choice — but a theme's name is what somebody wrote in a settings file their team shares, so the rename came with an alias rather than with breakage. Rejected: leaving
monochromeas the dark one's name, which the user considered and turned down; and renaming without an alias, which the option they chose explicitly flagged as needing one. - The alias resolves after the user's directory, not before. Every other name obeys "a file in userDir wins"; a retired name must not be the exception, or somebody's own
monochrome.tomlwould silently stop being read the day the alias was added. - The alias is not listed by
Available, so-list-themesand Options ▸ Theme… show each theme once. The cost, written down where the map is: a retired name can never be reused for a different theme. - The light theme is not a mirror of the dark one. sRGB luminance is not symmetric, so mirrored values read worse: the dark theme's
#808080comment is 4.74:1 on its ground and 3.40:1 on paper. The class ordering is preserved exactly and the scale is compressed into#626262…#000000. The comment is#626262, 5.26:1 —#767676would have looked right beside the rest of the scale and reads at 3.91:1.
- Renamed to a symmetric pair rather than adding an asymmetric one, at the user's choice — but a theme's name is what somebody wrote in a settings file their team shares, so the rename came with an alias rather than with breakage. Rejected: leaving
- Tests: six new, and the five existing readability rules picked the new theme up by themselves because they iterate
theme.Available(""). Ten mutations run — alias removed, alias applied too early, alias pointing at nothing, the light theme copied from the dark, a failing comment contrast, a cursor that is a plain reversal, an invisible current line, two classes drawn alike, text too close to its background, a key left to inheritance — all caught. - Quality: PASS, run #20 — 0 errors, 0 warnings, 0 smells; complexity 1810 → 1811.
- Docs: the themes reference and the theme how-to in all four editors and both languages — the embedded list, the table (a new row), the inherit-from advice, the shipped-theme count, and a new "a name a theme used to answer to" section.
theme/README.md, the packages reference,what-belongs-here, and the-list-themessamples in four tutorials. History was left alone: "comments were the dimmest reading colour in six of the eight shipped themes" is a true sentence about when that rule was written, ineditor/view_test.go,theme/README.mdand the four editors'write-a-theme.md. - Verified in a pty through Turbo MoonBit built against this working tree:
-list-themesshows nine withmonochromeabsent;-theme monochrome-lightdraws the page atrgb(238,238,238)withstructblack and bold, types atrgb(48,48,48)and punctuation atrgb(88,88,88);-theme monochromedraws the dark theme unchanged. - Not released. The editors pin turbo-core v0.4.2, which still ships eight themes, so their documentation is ahead of their binaries until this is tagged and they bump.
2026-09-09 (later still) — two JetBrains themes
- Goal: the user asked for an IntelliJ-like theme, then for both of its schemes.
- Changes:
theme/themes/darcula.tomlandtheme/themes/intellij-light.toml. Eleven themes ship now. No code change — the five readability rules and the completeness test picked both up by themselves, because they iteratetheme.Available(""). - The colours were fetched, not remembered. JetBrains publishes them: the editor schemes in
platform/platform-resources/src/DefaultColorSchemesManager.xml(Darcula) andplatform/platform-resources/src/themes/Light.xml(the modern IntelliJ Light), and Darcula's chrome inthemes/darcula.theme.json. Every value in both files is one of theirs except the three below. - Decisions:
- "After" rather than faithful, at the user's choice. Three values had to move: Darcula's comment
#808080reads 3.59:1 on its own page and IntelliJ Light's#8c8c8creads 3.36:1 on white, both below the 4.5:1 this project holds prose to; and Darcula's caret row#323232is seven channel values from#2b2b2b, below the sixteen the current-line rule requires. Rejected: the Catppuccin route of copying exactly and adding an exemption — the user chose readable over faithful, sopalettesWeDoNotOwnis untouched and both themes describe themselves as "after", the waycobaltdoes. - Each lift is the smallest that clears the rule, and the numbers are in the files:
#949494at 4.67 (where#909090gives 4.44),#767676at 4.54 (where#787878gives 4.42). The caret row borrows#3c3f41, JetBrains' own IDE window background, rather than an invented grey. - The modern
IntelliJ Lightscheme was used, not the olderDefault.Defaultdraws classes, methods and identifiers all in plain black, which cannot satisfy the rule that adjacent syntax classes differ — it would have meant inventing three colours.Light.xmlpublishes a distinct function, type-parameter and hyperlink colour, so nothing had to be invented. - Darcula's punctuation is the keyword orange, because
DEFAULT_COMMAandDEFAULT_SEMICOLONreally are#cc7832there. It is one of the things that makes the theme recognisable, and it survived rather than being normalised.
- "After" rather than faithful, at the user's choice. Three values had to move: Darcula's comment
- Tests: none added. Eleven mutations run — including two that restore JetBrains' own comment and caret-row values — all caught, which is the evidence that the three departures are necessary rather than taste.
- Quality: PASS. Verified in a pty through Turbo MoonBit built against this tree:
darculadrawsletat#cc7832, strings#6a8759, parentheses#cc7832,println#9876aa, line numbers#606366;intellij-lightdrawslet#0033b3bold, strings#067d17bold,println#0033b3, punctuation#080808. - Docs: the themes reference and the theme how-to in all four editors and both languages — the embedded list, two new table rows, the inherit-from advice, the count; plus
theme/README.md, the packages reference,what-belongs-here, the build-an-editor tutorial, and the-list-themessamples in four tutorials.
2026-09-12 — read-only: are any dependencies non-Go?
- Asked: whether turbo-core depends on any non-Go library.
- Answer, verified not remembered: no. No
import "C"anywhere in the tree,CGO_ENABLED=0 go build ./...passes, and no dependency ships C sources exceptx/sys'scpu_gccgo_x86.candunix/gccgo_c.c, both gccgo-only and never compiled by thegctoolchain. The runtime does exec external programs —/bin/sh(tools/run.go:19,app/toolchain.go:35) and the profile's LSP binary (lsp/server.go:92) — which is a packaging fact, not a link-time one. - Changes: none.
summary.mdalready states the dependency policy correctly (the "two direct dependencies plus one" decision); left untouched.
2026-09-14 — a fifth editor, Turbo Golo, registered in the family
- Asked: nothing of this repository directly.
turbo-golowas built beside it (/turbo-new-editor), pinningv0.5.0, and its Phase E is to make the library's live claims about the family true again. - Changes:
README.md(the list of editors gains Turbo Golo),docs/{en,fr}/README.md(five editors, five values of one type),docs/{en,fr}/how-to/test-without-publishing.md(the workspace line gains./turbo-golo; "any of the four" → five),profile/profile.go(the package comment counts five),.memory/summary.md("Five editors are built on it", and the extension point exercised by six counting the tutorial's Zig editor). History left as it was. - Decision: no library change. The
.gostrings were swept again for hardcoded language names against v0.5.0; every hit is a doc-comment example of the seam ("Turbo Go","gopls"inprofile.go,"Go"/"Rust"intools.goandtoolchain.go), a false positive ("Go to line","Go to definition"), or the true"written in Go."in the About box. Turbo Golo needed nothing from the library — not even a root marker: itsRootMarkersisnilandapp.ProjectRootanswers with the file's directory, which is the behaviour the seam already had. - Tests:
go build ./profileandgofmt -l profile/after the comment change; nothing else touched.
2026-09-15 — ACP: the Agent Client Protocol, and jsonrpc extracted to make room for it
- Goal: the user asked for ACP support in turbo-go — an agent window with an input area and a rendered transcript with syntax colouring for code, several agents configurable in TOML, one window per agent. Put to them at the start: the window, the menu and the event loop are all turbo-core's, so building it in turbo-go alone would have meant adding a vague "let an editor add a window and a menu from outside" seam to the library and using it exactly once. They chose turbo-core + the starter file in turbo-go. They also chose the largest protocol scope offered (conversation + permissions + the client filesystem), the user+project config rule, and reusing existing theme keys.
- Changes, turbo-core: new
jsonrpc(the protocol layer, extracted fromlsp) and newacp(config, framing, protocol, process, session, transcript, render, view) — 18 packages now.lsp/conn.gofell from 336 lines to ~50: the framing, and the names it re-exports.appgainedagents.go,agent_permissions.go, an~A~gentmenu onAlt-A, and two hooks intick.profile.TemplatesgainedAgents. - Changes, turbo-go:
internal/golang/acp.toml.tmplembedded beside the other three starter files; four stale assertions intemplates_test.gocorrected first, because the suite had been red atHEADsince 2026-09-03 and a red suite proves nothing about anything that follows. - Decisions: recorded in
summary.mdunder Decisions in force — seventeen of them. The three that cost the most to learn: a JSON-RPC request has to be answerable later (a permission comes from a dialog, which belongs to the drawing goroutine), the two id directions are separate spaces (docker agent really does sendid: 1against our ownid: 1), and every write to an agent goes off the caller's goroutine (writing blocks until the agent reads; an agent that stops reading would otherwise freeze the editor on the very keystroke meant to escape it). - Method: the protocol was not implemented from the specification alone. A recorded conversation with the user's own
docker agent+ llama.cpp was captured first, and the fake agent in the tests replays those exact shapes. That is what caught three things the documentation does not say: the permission request arrives before thetool_callit is about,contentis a single object on a message chunk and an array on a tool call, andavailable_commands_update/usage_updateexist at all. - Tests: 13 in
jsonrpc, 47 inacp, 12 inapp, 4 in turbo-go. Both suites green, andacp/jsonrpc/app/lspgreen under-race. The three riskiestjsonrpcassertions were falsified — broken deliberately, seen red, restored. - Quality: turbo-core PASS after refactoring three
return-statementssmells the new code introduced (0/0/0, complexity 1811 → 2119); turbo-go PASS 0/0/0, unchanged at 37. - Docs: EN + FR in turbo-go —
how-to/talk-to-an-agent,reference/acp,explanation/agent-windows— plus the packages reference and architecture explanation in turbo-core,acp/README.md,app/README.md, anddocs/diagrams/packages.drawioregenerated fromgo listand verified edge for edge (18 packages, 38 edges). - A documentation trap this cycle walked into on purpose: the user asked for the docs before the code existed, so they were written as the agreed design with a status banner on every page. Two claims in them turned out false once the code was real —
syntax.errordoes not exist (diagnostic.errordoes) and the output cap is per entry, not 10 000 lines — and the documented refusal messages did not match the ones the code emits. All corrected against the running code before the banners came off. A page written ahead of the code is a design document, and must be re-read against the code line by line before it becomes documentation. - Not committed.
2026-09-15 (later) — a spinner while the agent thinks, and copying out of the conversation
- Goal: the user, having used the agent windows, asked for two things — something turning beside the word thinking, and a way to take text out of a conversation and paste it elsewhere.
- Changes:
acp/spinner.go(new);acp/view_select.go(new — the transcript's cursor, its selection, andCopy);Line.Regionin the layout; selection drawing and the caret bar inview_draw.go;Shift-arrows,Ctrl-C/Ctrl-Insand mouse dragging inview_events.go;Session.pulse;App.copyFromAgent.acp/render.gowas split intorender.go,blocks.goandwrap.gowhen the quality gate flagged its total complexity. - Decisions: recorded in
summary.md. The three that mattered: the spinner is drawn from the clock so it needs no state; copying goes to both clipboards because "use it elsewhere" usually means outside the editor entirely; and with nothing selectedCtrl-Ccopies the region under the cursor, because the thing somebody wants is almost always a code block and the editor already knows where it starts and ends. - A defect found by driving the real binary, not by a test: the first version copied the speaker's label with the code block. It was caught by copying from the running editor and base64-decoding the OSC 52 payload off the pty.
TestCopyingABlockLeavesTheSpeakersLabelBehindcovers it now, and writing that test found a second defect — atool_callarriving with its output already attached lost it, because onlytool_call_updateread the content field. - Tests: 8 new in
acp. Spinner, selection and Escape's two meanings were each falsified — broken deliberately, seen red, restored. - Quality: PASS after refactoring
ruleLabel(six returns) and splittingrender.go(file complexity 51). 0/0/0. - Docs: the how-to gained a section on taking text out of a conversation; the reference gained the per-pane key tables, a Copying section and the
editor.selectionrow; the explanation gained three sections — the two clipboards, why copying with nothing selected takes a whole block, and why the spinner is drawn from the clock. EN + FR, then carried to the four other editors. - A hazard that cost a file. The sandbox's
cpcorruption struck twice more, once destroyingacp/view_events.goand its backup in the same command, because the backup had been made withcp. The file was rewritten from scratch. Never usecpor a cross-filesystemmvto back up a file in this sandbox; usecat. - Not committed.
2026-09-15 (night) — slash commands and @ file mentions in agent windows
- Goal: the user runs a second agent under Zed —
mini-me, started asmm -acp— that exposes commands Zed discovers, and asked for "the necessary ACP changes" so this client does the same; mid-task they added@as a file selector. Both are what Zed does with the same two characters. - Changes,
acp:protocol.go—Command.Input/CommandInput.Hint,TakesInput(),Hint();ContentBlockgrowsURI,Name,MimeType,Resource *EmbeddedResource;InitializeResult.PromptCapabilities()readsembeddedContextout of the raw capabilities.mention.go(new) —Mention,blocksFor(text /resource/resource_link, in place of the name),mimeOf.picker.go+picker_draw.go(new) —Choice,CommandChoices,FileChoices, the word under the cursor, the keys, the click, the drawing.session.go—Prompt(text, mentions...),pendingqueue,embeds,EmbedsContext(); its file methods moved tosession_files.go(new) when the gate flagged the file's complexity.view.go—Fileshook,Sendcollects mentions.view_events.go— the picker gets the key first;Esc's dismissal lasts until the text changes; a click on the popup takes a line.view_draw.go— the rule says· / commands · @ filesonce there is something to list. - Changes,
app:agent_files.go(new) —projectFiles/listProjectFiles, wired asview.Files; the status dialog lists each command with its description and hint. - Decisions: four, recorded in
summary.md— a command is text and the picker a convenience;Entercompletes-or-sends by whether the word is finished; a mention replaces its name with the file, embedded when the agent takes it, linked otherwise, never dropped; media types from a table of our own. Rejected: a menu of commands; sending the mention beside the text as an attachment (the agent would get the name twice); a stored popup state (recomputed from the word under the cursor every frame instead). - Tests: 15 new in
acp(picker_test.go, plus the hint andEmbedsContextasserted insession_test.go;fakeAgent.handshakeWithfor an agent declaring less than docker agent), 1 inapp. Green, and green under-race. Two guards were falsified —complete()made to always take the choice, and the mention's end-of-word check removed — each seen red on its test, then restored. - Quality: FAIL on the first run (4 smells: seven returns in the walk, six parameters on
drawChoice, file complexity 50 and 51 insession.goandpicker.go) → refactored → PASS 0/0/0, complexity 2254 → 2257. - Docs:
acp/README.md(two new files in the table, two new "easy to get wrong"),app/README.md. In the five editors, EN + FR:reference/acp.md(seven key rows, a Commands and mentions section,session/promptandavailable_commands_updaterows, the Limits bullet),how-to/talk-to-an-agent.md(two sections),explanation/agent-windows.md(the left-out bullet corrected, two sections appended), and the starteracp.toml.tmpl(two key lines). Applied by one script with an anchor check that had to hit exactly once per file — it did, 5 editors × 2 languages × 3 pages. - Method note: the user is not watching this session, so the plan-approval gate
methodical-devasks for was not held; the scope was taken as stated and the decisions above are the ones to review. - Not verified: no real agent in the sandbox. See the handoff.
- Not committed. On
main, not on a branch.
2026-09-16 — "I don't see the agent's / commands": a wire trace and a decode diagnostic
- Asked: the user built turbo-go, pointed it at mini-me, typed
/and saw no commands; asked whether this is handled in turbo-core. It is, and their binary has it (go version -m bin/turbo-go→ turbo-core(devel)throughgo.work; the· / commandslabel string is in the binary). So either mini-me never sendsavailable_commands_update, or sends one this client cannot decode — and until now the two were indistinguishable:onNotificationdropped an undecodable update silently. - Changes:
acp/trace.go(new) —TURBO_ACP_TRACE=<file>appends every line in both directions, stamped and marked->/<-, wrapped around the child's pipes inStart; a file that cannot be opened means no trace.Session.unreadableUpdatecounts an undecodable update and keeps "kind: error" inUnreadable(); the status dialog shows it and says how to set the trace. Tests: 2 new inacp/trace_test.go. - Docs: EN + FR × 5 editors — a "commands do not appear" bullet in the how-to's Variants, a "Tracing the conversation" section in the reference.
- Quality: PASS 0/0/0, complexity 2268.
- Not resolved: the actual cause. It needs the user's trace from their Mac. Not committed.
2026-09-16 — a sixth editor, Turbo JS, registered in the family
- Asked: nothing of this repository directly.
turbo-jswas built beside it (/turbo-new-editor), pinningv0.8.0, and its Phase E is to make the library's live claims about the family true again. - Changes:
README.md(the list of editors gains Turbo JS),docs/{en,fr}/README.md(six editors, six values of one type),docs/{en,fr}/how-to/test-without-publishing.md(the workspace line gains./turbo-js; "any of the five" → six),profile/profile.go(the package comment counts six),.memory/summary.md("Six editors are built on it", the extension point exercised by seven counting the tutorial's Zig editor, and a new bullet under Not yet established). History left as it was. - Decision: no library change. The
.gostrings were swept again for hardcoded language names against v0.8.0; every hit is a doc-comment example of the seam, a false positive ("Go to line","Go to definition"), or the true"written in Go."in the About box. Turbo JS is the first editor to replace a built-in scanner — it registers its own JavaScript undersyntax.LanguageJavaScript— and the library'sRegisterallowed it without a change, as its doc comment promised. - Found, not fixed (each a cycle of its own):
lsp/client.gosendslanguageId: "go"in everydidOpen, whatever the editor — a hardcoded language in a string a server reads; tsserver goes by extension so nothing broke. And the client readspublishDiagnosticsonly, so a server offering pull diagnostics (textDocument/diagnostic) — TypeScript 7's nativetsc --lsp --stdio— leaves every gutter blank; measured by running turbo-js's nine end-to-end tests against it (eight pass, diagnostics never arrive). Both are insummary.mdunder Not yet established. - Tests:
go build ./profileandgofmt -l profile/after the comment change; nothing else touched.
2026-09-17 — terminal windows and tools on Windows: a pseudo-console, cmd.exe, job objects
- Goal: the user asked for "le terminal pour l'ensemble des éditeurs" — Windows had no terminal windows (
pty_other.goreturnedErrUnsupported) and, as it turned out, no working tools menu either:tools/run.goandapp/toolchain.goboth ran/bin/sh -c. Put to the user first: the shell (cmd.exe via%COMSPEC%, chosen over PowerShell), that no Windows machine exists to test on (accepted: compiled and vetted, documented as unrun), the branch (main), the pace (run to the end). - Changes,
terminal:pty.gokeepsSession,Options,TermName,environmentand gains achildinterface;session_unix.go(new,linux || darwin) holds the/dev/ptmx+exec.Cmdpath that used to be inpty.go;pty_windows.go(new) creates the pipes and the pseudo-console, builds the attribute list, callsCreateProcess, resizes withResizePseudoConsole, waits for the shell on a goroutine and closes the console so the reader sees EOF;windows.go(new, untagged) holds the pure parts —windowsShell,environmentBlock,windowsCommandLinewith cmd.exe's/S /C "…"special case and C-runtime quoting for everything else,programName;pty_other.gonow excludes windows.pty_test.go's Unix tests skip on Windows explicitly;TestShellOrDefaultmoved tosession_unix_test.go;windows_test.go(10 tests) runs on every platform. - Changes,
tools:Shellconst →Shell()andShellArgs()(shell.go), withshell_unix.go(/bin/sh -c,Setpgid, process-group kill),shell_windows.go(%COMSPEC% /S /C,SysProcAttr.CmdLinewritten verbatim,CREATE_NEW_PROCESS_GROUP, job object withKILL_ON_JOB_CLOSE) andgroup_other.go;run.goholds agroupand releases it infinish.group_unix.godeleted (folded intoshell_unix.go). - Changes,
app:toolchain.godrops its own/bin/shconstant and usestools.Shell()/tools.ShellArgs();terminals.go's unsupported-platform message names Windows among the platforms that have terminals. - Decisions: recorded in
summary.md— thechildinterface, the by-handCreateProcessand theLazyProcforUpdateProcThreadAttribute, the console-closing goroutine, cmd.exe's verbatim command line, job objects,%COMSPEC%. Rejected: PowerShell as the default (the user chose cmd.exe);os/execfor the pseudo-console child (no attribute support);taskkill /Tinstead of a job object; aCommandLinefield onterminal.Options(Windows leaking into the API — the cmd.exe case is recognised from the program name instead). - Tests: Linux suite green (
terminal,tools,appalso under-race);GOOS=windows go vet ./...,GOOS=windows go build ./...for amd64 and arm64,GOOS=windows go test -cforterminal,tools,app;GOOS=darwin go vet ./....release_test.gofails in this sandbox for the documentedcp-NUL reason, unrelated. Nothing ran on Windows. - Quality: run #28 FAIL (six returns in
createProcess→ split intopseudoConsoleAttributesandencodedArguments), run #29 PASS 0/0/0, complexity 2306. - Docs:
terminal/README.md(platform table, the three Windows facts, the untested note),tools/README.md(Shell()/ShellArgs(), job objects, cmd.exe),docs/{en,fr}/reference/packages.md(the x/sys row). In all six editors, EN + FR:README.md(Linux, macOS and Windows),reference/terminal.md(shell, controlling terminal, platform table, error row),explanation/terminal-windows.md(the Windows section rewritten: a pseudo-console and why it is a file of its own, built-not-run),how-to/use-a-terminal.md(COMSPEC, the five things to try first on Windows),reference/<lang>-tools.md(shell row, cmd.exe's globs and;, error row),explanation/<lang>-tools.md(cmd.exe /S /C). Applied by one script asserting each anchor once per file; the four FR tools references whose error row was worded differently were fixed by hand. - Not committed. Not tagged. The editors' docs are ahead of their binaries until turbo-core is tagged and re-pinned.
2026-09-18 — "first launch: no LSP" diagnosed, then fixed: saving announces what the server does not know
- Asked: the user reported that on turbo-go's first launch the LSP does not work; saving, quitting and relaunching fixes it. Diagnosed first (read-only), then the user confirmed the window had started Untitled and asked for the fix — a
/methodical-devcycle, onmain, on top of the still-uncommitted Windows work of 2026-09-17 (disjoint files). - Found: a buffer that gains its path through Save As was never
didOpen'd.Language.DidOpenskips path""(Untitled),afterSavesent onlydidSave, andannounceOpenDocumentsruns once (a.announced) — and a server ignoresdidChange/didSavefor a document it was never told is open, so the window had no LSP until a restart. The alternative cold-gopls-cache hypothesis is in the handoff, unmeasured. - Changes:
app/language.go— thedocumentsmap is keyed by absolute path (pathKey, formerlydiagnosticKey;Knowsis now spelling-independent).app/actions_file.go—announceSavedinafterSavesendsdidOpenfor a path the server does not know,didSaveotherwise;saveremembers the buffer's previous path beforeSaveAsrewrites it andrenamed(absolute comparison) triggersDidCloseof the old document on a genuine rename.app/README.mdupdated. - Decisions: the announcement lives in
app, not inBuffer.SaveAs, which would put the language server underneath the text type. A change of spelling of the same file is not a rename. Autosave inherits the fix through the sharedafterSavetail and never renames, soDidCloselives insavealone. - Tests: 5 new —
TestKnowsDoesNotDependOnTheSpellingOfThePath(app/diagnostics_test.go) and four in the newapp/save_test.go(Untitled announces; known document reports the write; rename closes the old document; same path under another spelling closes nothing). Each verified by breaking the code it covers. Whole suite green,-racegreen onapp. - Quality: run #30 PASS 0/0/0, complexity 2308 (+2).
- Docs: EN + FR —
reference/app.md(aKnowsrow and a paragraph on what saving sends) andhow-to/talk-to-a-language-server.md(a "file created inside the editor" variant). Diagram untouched: no package added or re-wired. - Not done: not committed; not driven against a real gopls; the six editors inherit the fix only when turbo-core is tagged and re-pinned.
2026-09-19 — canonical paths: moon-lsp on macOS knew nothing about a file opened as /var/…
- Origin: the user ran turbo-moonbit's
./01-release.tag.shon their Mac;make checkfailed onTestCompletionEndToEndWithRealMoonLSP("No completions here", after 60 s) andTestDiagnosticsForAFileThatDoesNotCompileWithRealMoonLSP(nothing in 30 s), the other real-server tests passing. turbo-moonbit's memory said "never run on macOS" — this was the first time. - Diagnosed in the sandbox with the MoonBit toolchain of 2026-09-15 installed (
moon 0.1.20260915,moonc v0.10.13): the fixture and manifests are fine (moon checkreports the expected error); the tests pass under/tmp; they fail identically withTMPDIRpointing through a symbolic link. An LSP probe againstmoon-lsp --stdioshowed why: opened through a link,publishDiagnosticscarries the real path's URI, and a completion asked after adidChangethat typestext.is never answered (40 requests) — through the real path it is answered at once. macOS's/var/foldersis a link to/private/var, so everyt.TempDir()there is the linked spelling. - Changes:
lsp/uri.go—CanonicalPath(new, exported):Abs, thenEvalSymlinks, else the deepest existing directory resolved and the rest re-appended;PathToURIuses it.app/language.go—pathKeyislsp.CanonicalPath(thepath/filepathimport went).app/fakelsp_test.go— the fake records the params of the lasttextDocument/didOpen;lastOpenedURI(). Tests:TestPathToURIResolvesSymbolicLinks,TestCanonicalPathOfAFileNotYetOnDiskResolvesItsDirectory(lsp),TestDiagnosticsPublishedUnderTheRealPathReachAFileOpenedThroughALink(app) — all three skip where a symlink cannot be created. Falsified: withapp/language.goandlsp/uri.gostashed, the app test fails on all three assertions. - Verified:
make checkgreen (whole suite). In turbo-moonbit throughgo.work(local core), all six…WithRealMoonLSPtests pass withTMPDIRunder a symlink, and the whole suite passes. - Docs:
app/README.md(section renamed canonical, a paragraph on moon-lsp and macOS),docs/{en,fr}/reference/app.md(Knowsrow),lsp/README.md(CanonicalPathrow). - Not done: not committed, not tagged —
release.envis set toTAG="v1.0.1"; the user runs./01-release.tag.sh, then re-pins the editors (go get rickub.com/turbo-editors/turbo-core@v1.0.1 && go mod tidy && GOWORK=off make check). Not run on macOS by anyone yet; the reproduction is a Linux symlink.
2026-09-19 (later) — the canonical-path fix, second pass: three tests that compared as spelt
- Origin: the user's
./01-release.tag.shfor v1.0.1 stopped inmake checkon the Mac:TestTheListPrefersAnOpenWindowOverTheDisk(app),TestDiagnosticsReachTheEditorandTestPathAndURIRoundTrip(lsp). All three compared a path as spelt (/var/folders/…) with what now comes back canonical (/private/var/…); green on Linux because/tmpis no link. Reproduced withTMPDIRunder a symlink. - Changes:
app/actions_file.go—windowForcompareslsp.CanonicalPathof both sides, notfilepath.Abs; this is the real defect the first test caught — a location the server sends back could not find the window it was about, so the references list quoted the disk and a jump would have opened the file twice.lsp/lsp_test.go— the two tests now expectCanonicalPath(path), with the reason in a comment. NewTestWindowForFindsAFileOpenedThroughALink(app), falsified against the oldwindowFor. - Verified:
make checkgreen; whole suite green withTMPDIRunder a symlink (TURBO_CORE_RELEASING=1to keep the release scripts out of it). - Not done: still not committed or tagged;
release.envstill says v1.0.1. Run./01-release.tag.shagain.
2026-09-19 (later) — a save that creates a file tells the server: moon-lsp never diagnosed a new .mbt
- Origin: turbo-core v1.0.1 published, turbo-moonbit re-pinned; its
make checkon the Mac then failedTestAFileCreatedAfterTheServerStartedIsNotDiagnosed— the test pinning "moon-lsp never diagnoses a file created after it started" — because on macOS moon-lsp does. Here (Linux, moon 0.1.20260915) the test still passed, so the platforms disagree; the documentation it protected had in fact already been overtaken (turbo-moonbit'senable-completion.mdhas promised error marks from the first save since 2026-09-18), andlanguages.mdno longer mentions it. - Diagnosed with an LSP probe against
moon-lsp --stdio: a new file announced bydidOpen(anddidSave) is never diagnosed on Linux; the momentworkspace/didChangeWatchedFilesnames it — in any order relative to the document notifications, type 1 or 2 — its diagnostics arrive. The server advertises no watcher registration, so nothing ever asked the editor to send this. turbo-moonbit's own summary had already written the fix down as turbo-core's to make. - Changes:
lsp/protocol.go—FileChangeType(FileCreated/FileChanged/FileDeleted),FileEvent,DidChangeWatchedFilesParams.lsp/client.go—FileCreated(path).app/language.go—FileCreated.app/actions_file.go—saveasksfileExistsbefore writing;afterSaveandannounceSavedtakecreated; the file event goes after the document is announced or saved.app/autosave.go—writeQuietlythe same.app/fakelsp_test.go— records the last file event and the order of methods (lastFileEvents,methodsSeen). Tests:TestSavingANewFileTellsTheServerTheFileExists,TestSavingAnExistingFileDoesNotClaimItWasCreated,TestAutosaveOfANewlyNamedFileTellsTheServerTheFileExists; the lsp notification-order test includes the new one. ThefirstIndexOfhelper:actions_view.goownsindexOf. - Verified:
make checkgreen; whole suite green withTMPDIRunder a symlink. In turbo-moonbit (throughgo.work), the rewrittenTestAFileCreatedInTheEditorIsDiagnosedFromItsFirstSaveWithRealMoonLSPpasses (plain and symlinkedTMPDIR) and fails against the published v1.0.1 — the falsification. A first version of that test failed for a reason of its own: itstypeTextdropped\n, so the four-line fixture became one///|doc-comment line and compiled; traced with a tee wrapper aroundmoon-lsp. The helper now sends Enter for a newline. - Docs:
app/README.md,docs/{en,fr}/reference/app.md,docs/{en,fr}/how-to/talk-to-a-language-server.md,lsp/README.md. - Not done: not committed, not tagged;
release.envsays v1.0.2. Whether moon-lsp on macOS watches the directory itself is inferred from the user's run, not measured.
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 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 |
|