carlos / todomvc-bakeoff Public

Clone
git clone https://amadan.net/carlos/todomvc-bakeoff

Plain git — no account needed to clone.

Download

Download this file

1# đŸ¤– Builder brief: TodoMVC on CARLOS
2
3You're building one entry in a bakeoff. The same TodoMVC app is built in
4nine language setups and deployed to Carloku, and the results are
5compared on a public report page. The point is to show that CARLOS apps,
6and the CARLOS Platform, aren't tied to Go. It isn't a race to crown a
7winner, so write the app the way an experienced developer in your
8language would write it well the first time.
9
10Your setup, your slug and anything specific to it are in the prompt that
11handed you this brief.
12
13## Read first
14
151. `CONTRACT.md`: the routes, forms and markup every app shares. It's
16 binding.
172. `shared/reference.html`, `shared/base.css`, `shared/index.css`: the
18 TodoMVC page and styles you serve.
193. `vendor/carlos-skills/getting-started/SKILL.md` and
20 `vendor/carlos-skills/building-carlos-apps/SKILL.md` with
21 `references/platform.md`: what a CARLOS app is and how the `carlos`
22 CLI deploys one. Read them for the platform. Where they assume Go,
23 Rastrillo, amadan hosting, canary reviews or Rastrillo's UI, this
24 brief overrides them.
254. The TodoMVC app spec, adapted by the contract:
26 [https://github.com/tastejs/todomvc/blob/master/app-spec.md](https://github.com/tastejs/todomvc/blob/master/app-spec.md).
27 The server holds the data in SQLite, not `localStorage`, and the
28 routes are real paths, not `#/`.
29
30## What to build
31
32A server-rendered TodoMVC that works fully with JavaScript turned off,
33with a small amount of JavaScript layered on top so it behaves like the
34reference TodoMVC when JavaScript is on. There's no client framework and
35no JS build step. Plain ES modules are fine, and so is vendored VanJS.
36
37You choose the stack: HTTP server, router, templating, SQLite binding,
38and anything else, as your best recommendation for this language. Explain
39the choices in your README.
40
41## The platform contract
42
43- One binary for **linux-arm64**, started as
44 `<bin> --socket <path> --db <path>`. It listens on that unix socket.
45 There is no `$PORT`.
46- For local runs and tests it also accepts `--listen 127.0.0.1:<port>`
47 in place of `--socket`. Never bind anything other than `127.0.0.1`.
48- It serves `GET /healthz` and `GET /api/version` (see `CONTRACT.md`).
49- When it needs its own hostname, it reads `CARLOS_HOST`. It never parses
50 the socket path.
51- **Linked statically, or depending on nothing but libc.** Instances run
52 sandboxed and see only their own build, so don't count on any other
53 shared library being there.
54- SQLite in WAL mode, one file at `--db`. Migrations only ever add; never
55 drop or rewrite data.
56- Starts fast (instances sleep when idle and wake on the next request)
57 and exits cleanly on SIGTERM.
58- Don't set any memory, CPU or tier fields. Every app runs at the
59 platform default.
60
61## Your directory
62
63Work only in `apps/<slug>/`. Don't read other apps' directories or the
64harness source. Everything else in the repo is shared and read-only to
65you.
66
67Write these files:
68
69- `AGENTS.md`, **first, before code.** It holds the one rule this app
70 lives by, and the tenancy declaration: one instance for the whole app,
71 each visitor's list kept separately by an anonymous cookie, no
72 sign-in, trust class C (the todo text is stored as plain text; that's
73 chosen and written down here).
74- `README.md`: the stack you chose and why, and how to build, run, test
75 and deploy.
76- `DEPS`: one direct third-party dependency per line. Leave the file
77 empty if there are none.
78- `Makefile` with these targets:
79
80| Target | Does |
81|---|---|
82| `make build` | Builds for this machine into `dist/app` |
83| `make build-arm64` | Builds the shipped binary into `dist/app-linux-arm64` |
84| `make test` | Runs your own tests |
85| `make clean` | Removes build output only. Leaves dependency caches alone |
86| `make clean-deps` | Also removes every compiled third-party artifact, but keeps downloaded sources, so the next `make build-arm64` compiles every library from scratch. If your toolchain keeps compiled libraries in a shared per-user store (cabal, opam, zig, swiftpm and so on), point that store inside `apps/<slug>/.cache/` from the Makefile so this target can delete it without touching anything else. **Go is the exception:** never change `GOCACHE`. Instead, `make build-arm64 COLD=1` passes `-a` |
87| `make bump` / `make unbump` | `bump` changes the text of one string constant in the file that holds the `GET /` handler, so the next build must recompile that file. `unbump` puts it back |
88| `make deploy` | Deploys `dist/app-linux-arm64` with `carlos deploy` (see below) and needs no prompts |
89
90- `STATUS.md`, written last. Its first line is exactly one of:
91 - `LIVE https://todo-<slug>.bfj.oncarlos.com <version>`
92 - `BLOCKED <one-line reason>`, followed by a section headed
93 `## Where it got stuck`.
94
95 Use `BLOCKED needs-arm64-host` only when the app builds and passes
96 locally but you can't produce a linux-arm64 binary on this machine.
97 Use `BLOCKED platform: <what's missing>` when Carloku lacks something
98 the app needs. Don't work around the platform.
99
100`.cache/`, `dist/` and database files are git-ignored.
101
102## Testing
103
104- Your own tests, in whatever form is normal for your language.
105- The shared behaviour suite. Run your app with
106 `dist/app --listen 127.0.0.1:<port> --db $TMPDIR/<slug>.db`, then run
107 `scripts/test-app.sh http://127.0.0.1:<port> $TMPDIR/<slug>-suite.json`.
108 It runs every test with JavaScript on and again with it off. It must
109 pass before you deploy.
110- `qemu-aarch64-static` is on `PATH` (see `docs/toolchains.md`) if you
111 want to run the arm64 binary locally first.
112
113## Deploying to Carloku
114
115Account `bfj`, app `todo-<slug>`, host `todo-<slug>.bfj.oncarlos.com`.
116The first time:
117
118```
119carlos apps create --account bfj --app todo-<slug> --build-probe /api/version
120carlos instances enable --account bfj --app todo-<slug>
121carlos instances create --account bfj --app todo-<slug> --host todo-<slug>.bfj.oncarlos.com --channel edge
122```
123
124After that, `make deploy` runs
125`carlos deploy --account bfj --app todo-<slug> --target linux-arm64 --message "<what changed>" dist/app-linux-arm64`.
126The version is the git short SHA of your commit. Before the first
127deploy, run `scripts/boot-check.sh dist/app-linux-arm64`. It starts the
128arm64 binary under qemu on a unix socket, checks `/healthz` and
129`/api/version`, and checks for a clean exit on SIGTERM. (`carlos vet`
130needs operator access, which you don't have.) After deploying, run the
131shared suite against `https://todo-<slug>.bfj.oncarlos.com`. It must pass
132there too.
133
134If a `carlos` command refuses or behaves unexpectedly, read its `-h` and
135`references/platform.md` before trying something else.
136
137## Scratch files
138
139`$TMPDIR` is shared with other sessions. Make your own directory with
140`mktemp -d -p "$TMPDIR"`, keep every scratch file in it, and delete only
141that directory. Never delete with a glob in `$TMPDIR`.
142
143## Git
144
145Commit on your branch (`app/<slug>`) as you go, with clear messages
146ending in `Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>`.
147Don't push and don't merge.
148
149## Limits
150
151You have 4 hours of wall-clock time. If you're stuck before then, stop,
152write `BLOCKED` in `STATUS.md` with where you got stuck, and end.
153