Repository navigation
Conversation
Move all projects to net10.0, SDK 10.0.110, and CI to 10.0.x. The client toolchain gates this. Fable 4.x deadlocks when the compiled project targets net10.0, so the client needs Fable 5, which in turn needs Feliz 3 (the Feliz 2 compiler plugin targets the Fable 4 AST). Every later Fable 5 release breaks the client in a different way: 5.1.0 made F# reflection report `option` as a union, so Fable.SimpleJson classifies it before its option case and every `option` field in a remoting response fails to deserialize; 5.5.0 does the same for `list` and additionally rejects Fable.Remoting.MsgPack's `inline private` helpers. Fable is therefore pinned to 5.0.0, which predates all three. global.json gains allowPrerelease: false so rollForward cannot silently select an installed 10.0.400-preview SDK. Feliz 3 API migration: React.fragment -> React.Fragment, Interop.createElement -> HtmlHelper.createElement, React.createDisposable -> plain cleanup lambdas, and svg.viewBox as a string (the tuple overload is no longer inferred). Copilot-Session: d46c75a1-198e-414a-b712-9d90152a7aea
There was a problem hiding this comment.
Pull request overview
Migrates Treemon to .NET 10 and the compatible Fable/Feliz toolchain.
Changes:
- Targets .NET 10 across all projects and CI.
- Pins Fable 5.0.0, upgrades Feliz 3, and updates affected client APIs.
- Updates SQLite, documentation, and generated-file exclusions.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/ci.yml |
Uses .NET 10 in CI. |
.gitignore |
Ignores Fable temporary projects. |
AGENTS.md |
Updates Fable compatibility guidance. |
docs/spec/worktree-monitor.md |
Documents the toolchain constraints. |
dotnet-tools.json |
Pins Fable 5.0.0. |
global.json |
Selects a stable .NET 10 SDK. |
review/rules/modern-fsharp-syntax.md |
Updates Fable syntax guidance. |
src/Cli/Cli.fsproj |
Targets .NET 10. |
src/Client/App.fs |
Migrates fragment API. |
src/Client/CanvasPane.fs |
Migrates fragment API. |
src/Client/CardViews.fs |
Migrates element, fragment, and SVG APIs. |
src/Client/Client.fsproj |
Targets .NET 10 and Feliz 3. |
src/Client/Components.fs |
Migrates fragments and effect cleanup. |
src/Client/OverviewBand.fs |
Migrates fragment API. |
src/Client/OverviewChart.fs |
Migrates effect cleanup. |
src/Server/Server.fsproj |
Targets .NET 10 and updates SQLite. |
src/Shared/Shared.fsproj |
Targets .NET 10. |
src/Tests/Tests.fsproj |
Runs tests on .NET 10. |
Review details
- Files reviewed: 17/18 changed files
- Comments generated: 1
- Review effort level: Medium
Feliz 3 removes React.createDisposable, so the mandatory hook example in the elmish-mvu-discipline rule no longer compiles against this project. Return a plain unit -> unit cleanup instead, matching the shape used by useFrameCoalescedHover in OverviewChart.fs. Copilot-Session: d46c75a1-198e-414a-b712-9d90152a7aea
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Treemon was pinned to
net9.0across all five projects. The spec recorded why:That blocker is real and reproducible — but the fix is not a simple TFM bump. Moving the client to
net10.0walks into a chain of three separate toolchain incompatibilities, each of which silently breaks something different.Changes
Baseline
Shared,Client,Server,Cli,Tests) ->net10.0global.json-> SDK10.0.110, plusallowPrerelease: false. WithrollForward: latestMajorand the defaultallowPrerelease: true,dotnetsilently selected an installed10.0.400-previewSDK — exactly the preview SDK the original spec note blamed..github/workflows/ci.yml->dotnet-version: 10.0.xMicrosoft.Data.Sqlite9.0.11->10.0.10The Fable version squeeze
There is exactly one viable Fable release. Each candidate was verified empirically, not assumed:
net10.0. Confirmed locally: the Fable process burned ~5s CPU over 10 minutes at a flat 59 MB — a hang, not slow compilation. Upstream fable-compiler/Fable#4326, closed with "use Fable 5". No fix will land onfable4.option_type()carryNone/Someunion cases, soFSharpType.IsUnion(typeof<'a option>)now returnstrue.Fable.SimpleJsonmatches itsUnionTypepattern beforeOptionType, so everyoptionfield in a remoting response fails to deserialize. Upstream Fable.Remoting#394 (open); the two-line fix is Fable.SimpleJson#111 (open, unmerged).list_type(Fable#4698), and adds a hard error forinlinefunctions referencing private values (Fable#4701) that rejectsFable.Remoting.MsgPack'swrite64bitNumber. Tracked as Fable.Remoting#396 (open, no maintainer response).Fable.Remotingworks with no vendored patches.So
dotnet-tools.jsonpinsfableto5.0.0(rollForward: falsewas already set). The reasoning is recorded indocs/spec/worktree-monitor.mdwith a revisit note.This is the failure mode that mattered most: with Fable 5.4.0 the solution built, Fable compiled, Vite bundled, and the server started — but the dashboard rendered only skeleton placeholders. The API returned HTTP 200 with valid JSON, and there were zero console errors, zero page errors, and zero unhandled rejections. The exception was being swallowed into Elmish's
DataFailedarm.Feliz 3 migration
Fable 5 requires Feliz 3 — the Feliz 2.x compiler plugin targets the Fable 4 AST and throws
Method not found: Fable.AST.Fable.Ident..ctor(...).Feliz2.*->3.*, with the API changes it forces:React.fragment->React.Fragment(App.fs,CanvasPane.fs,CardViews.fs,Components.fs,OverviewBand.fs)Interop.createElement->HtmlHelper.createElement(CardViews.fs)React.createDisposableremoved -> plainunit -> unitcleanup lambdas (Components.fs,OverviewChart.fs). This also resolved auseEffectoverload ambiguity. Noteignoredoes not typecheck here (it is'a -> unit), hencefun () -> ().svg.viewBoxnow infers thestringoverload rather than the(int, int, int, int)tuple one, so theiconhelper inCardViews.fstakes an annotatedviewBox: stringand call sites pass"0 0 24 24"/"0 0 18 18".Housekeeping
.gitignore:*.fable-temp.csproj(generated by Fable on every run)AGENTS.mdandreview/rules/modern-fsharp-syntax.md: the "Fable 4.28 supports this syntax" notes now read "Fable 5.0"Tests
dotnet build treemon.slnx— succeeds, 0 errors.dotnet fableandnpm run buildboth succeed (122 modules, 453 kB bundle).dotnet test)Category=FastCategory=E2EThe single failure is
warm 250-path summary appears within one second(DiffViewerTests.fs), a server-side perf assertion with a 1000 ms budget. It is pre-existing, not a regression — I stashed this branch and measured unmodifiedmain, which fails the same assertion worse:responseMs/displayMsmain(net9, Fable 4.28).NET 10 is in fact slightly faster on this path. The budget is simply too tight for this machine, so I left the threshold alone rather than silently relaxing a perf guard.
Runtime behaviour was verified separately with a Playwright probe against a server + Vite pair on isolated ports (60777/60778/60779) — the dashboard renders live data (11 cards, 11 branch names) with no console errors. Production and port 5000 were never touched.
Notes
NU1903forSQLitePCLRaw.lib.e_sqlite32.1.11 appears in the build. It is pre-existing: the advisory range is<= 2.1.11, andMicrosoft.Data.Sqlite9.0.11 already pulled the equally-affected 2.1.10. Left alone as unrelated scope.Fable.SimpleJsonships theoption/listordering fix andFable.Remotingaddresses theinline privateerror. Both upstream issues are linked above and in the spec.