nandi/frqpublic Fork 0
438b247afa2e97c6a6954709771915e2e72203f6
Commits
Clone
git clone https://git.rickub.com/nandi/frq.git
git clone ssh://git@rickub.com/nandi/frq.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

CLAUDE.md · 134 lines · 6.2 KBmarkdown Blame HistoryRaw
Read the clock in the reader's zone when /etc/localtime is a file bffe879 nandi 18d ago1# Working in this repo
2
3## Nix
4
Build the window somewhere with room for it 1451151 nandi 5d ago5**STOP BUILDING LOCALLY. Build on Modal.** This machine is for editing and for
6evaluating — `nix flake check`, `nix eval`, `nix build --dry-run`, `nix repl`
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago7— and not for realising a derivation. A local build of the whole graph gets
8killed for memory long before it finishes, and the minutes spent finding that
9out are minutes not spent on the change. So:
Read the clock in the reader's zone when /etc/localtime is a file bffe879 nandi 18d ago10
11```bash
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago12modal run .modal/flutter-web/container.py # the web bundle, on Modal
13modal run .modal/flutter-dev/container.py # the incremental Flutter loop
Read the clock in the reader's zone when /etc/localtime is a file bffe879 nandi 18d ago14```
15
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago16The containers in `.modal/` are not a third source tree: they are CI config
17that happens to live here, the way `.github/` would be.
Point the docs at .modal/, and at the file they now build 972ab37 nandi 5d ago18
Build the window somewhere with room for it 1451151 nandi 5d ago19`--dry-run` locally to see what *would* be built, then hand the build to Modal.
20The one exception is a derivation you already know is trivial and already
21substitutable; if you are unsure, it is not the exception.
22
23`modal app logs` is no substitute for watching that command: it resolves
24deployed apps by name, not the ephemeral one a `modal run` creates, and carries
25nothing until the Sandbox starts — the image build streams to the client and
26nowhere else.
27
28You are already running inside the Arch distrobox, where `nix` lives, so run
29the evaluating commands directly — do not wrap them in `distrobox enter`.
30
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago31The containers run as Modal **Sandboxes on a real VM**, which is what makes a
32build work out there at all: the ptyshim that used to stand in for a working
33pty under gVisor is deprecated, and nothing here should reintroduce it. Neither
34container carries nix: `tools/toolchain.sh` fetches Flutter, a JDK and the
35Clojure CLI by sha256 onto the `devshell` Volume, and the build runs out of
36those.
Build the window somewhere with room for it 1451151 nandi 5d ago37
38A remote builder (`eu.nixbuild.net`) is also configured here, for the case
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago39where you want a derivation built somewhere other than Modal: `--store
Build the window somewhere with room for it 1451151 nandi 5d ago40ssh-ng://eu.nixbuild.net --eval-store auto` rather than a `builders` entry, so
41the whole graph stays there and only .drv files go up.
Read the clock in the reader's zone when /etc/localtime is a file bffe879 nandi 18d ago42
Follow the xorg renames, and stop wrapping nix in distrobox a400a00 nandi 16d ago43One thing this container is *not* representative of: `/etc/localtime` is a
44regular file here rather than a symlink, so anything that reads the zone out
Read the clock in the reader's zone when /etc/localtime is a file bffe879 nandi 18d ago45of its path sees nothing. That is a real deployment shape, not an artefact —
46frq.clock handles it.
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago47
Build the window somewhere with room for it 1451151 nandi 5d ago48## Never pipe a long task through `tail`
49
50`tail` and `head` do not emit anything until their input ends, so a build, a
51test run or a deploy piped through one shows nothing at all until it is over —
52and if it is killed or times out first, its output is lost with it. That is the
53opposite of what you want from the commands that take longest.
54
55Let them write to the terminal, or `tee` them if you want a copy to grep
56afterwards:
57
58```bash
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago59modal run .modal/flutter-web/container.py 2>&1 | tee /tmp/frq-build.log
Build the window somewhere with room for it 1451151 nandi 5d ago60```
61
62Trim afterwards, on the file, where the whole run is still there to re-read.
63The same goes for `grep` and `awk` in a live pipeline: they buffer when their
64output is not a terminal, so pass `--line-buffered` / `fflush()` or watch the
65file instead.
66
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago67## The Nim core
68
69`nim/` is the portable logic, moving out of `common/` one module at a time as
70a native library the Dart side calls through FFI. Read `nim/README.md` before
71touching it — in particular the status section, which says what is actually
72wired up (the Nim half and its ABI) and what is not (the Dart binding).
73
74Two rules the ABI has, both of which cost a segfault to rediscover:
75
76* Every string the core returns is the **caller's** to free, with `frq_free`.
77 Nim's allocator is not Dart's.
78* `frq_init` runs once before anything else.
79
80`just nim-test` needs no Flutter and no Dart, which is most of the point.
81
82A module is not deleted from `common/` when its Nim version lands: the web
83target cannot load a native library, so the ClojureDart original is the web's
84implementation until there is a wasm build. Deleting one would take the web
85build with it.
86
87## The two source trees
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago88
89```
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago90common/ .cljc portable — every target compiles it
91flutter/ .cljd the Flutter half, and the host implementations
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago92```
93
94The extension is the boundary, not a convention: ClojureDart reads `.cljd` and
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago95`.cljc` and never `.clj`. The rule for anything under `common/` is that it may
96not require a `dart:` library — if it needs the host, it asks `frq.io`, and the
97implementation that installed itself answers. `frq.io.dart` is installed by
98`flutter/src/frq/main.cljd`, which has to await the storage directory first;
99`frq.io.web` by `main_web.cljd`.
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago100
101Adding a host call means adding it to the seam in `common/frq/io.cljc` and to
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago102every implementation. Name it for the result rather than the mechanism — the
Split the tree three ways, and let the phone be Flutter's 7070931 nandi 7d ago103seam has `write-private-file!` and not a chmod, because Dart has no chmod.
104
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago105There used to be a third tree, `src/`, and a second runtime under it: jolt,
106glimmer, and a libcosmic desktop window painting the same screens. It is gone,
107along with `just cosmic`, `just tui`, the AV/MoQ media plane and the native
108objects they loaded. Flutter is the only frontend now, and `common/` is
109compiled by one compiler rather than two — which is why the `#?(:jolt ...)`
110reader conditionals that used to be scattered through it are not there any
111more. `tools/check-common.py` still guards the seam, and CI still runs it on
112every push.
113
114`flutter/` builds three things, from one `clojure -M:cljd compile`:
Paint the desktop with Flutter too, beside libcosmic fc5f63c nandi 7d ago115
116`just apk`, out of the flake's own `.#flutter` shell (clojure, jdk17, flutter)
117and its `.#android-sdk` package. Impure on purpose: Gradle fetches its own
118dependencies and writes into `ANDROID_HOME`, so the recipe copies the store SDK
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago119to `flutter/.home` and lets it finish there.
Paint the desktop with Flutter too, beside libcosmic fc5f63c nandi 7d ago120
121`just flutter-desktop`, out of `.#flutter-desktop` (the same clojure and
122flutter, with cmake, ninja, pkg-config and gtk3 where the JDK and the SDK are).
One frontend where there were three, and a core that is not Clojure 438b247 nandi 21h ago123Impure for the network half of the same reasons and no writable-SDK dance,
124since nothing writes into the store. nixGL off NixOS.
125
126`just flutter-web`, out of no nix shell at all — `tools/toolchain.sh` fetches
127the three pinned tarballs it needs, which is what lets `.modal/flutter-web/`
128run the same script on a plain Debian image.
129
130The consequence for `common/` is that "the phone" is not a synonym for "the
131ClojureDart side": three targets compile it. An implementation that branches on
132the platform has to ask (`Platform.isAndroid`) rather than assume; see
Paint the desktop with Flutter too, beside libcosmic fc5f63c nandi 7d ago133`frq.io.dart/write-private-file!`, where assuming cost a token its file mode.
134See flutter/README.md.