| index 85bf82c..3b19264 100644 |
| --- a/copy-review/strings.json |
| +++ b/copy-review/strings.json |
| @@ -11,7 +11,7 @@ |
| "id": "page.intro", |
| "section": "Top", |
| "label": "Introduction", |
| - "text": "PROSE_PENDING", |
| + "text": "We built the same small app nine times, once in each of nine language setups, and put every one on Carloku, the hosted CARLOS Platform. The app is [TodoMVC](https://todomvc.com): a to-do list. Here it's a page the server builds, which works with JavaScript switched off, plus a little JavaScript on top so it feels like the familiar TodoMVC.\n\nThe point is to show that CARLOS apps, and the platform they run on, aren't tied to Go. So this page compares the nine; it doesn't rank them.\n\nOne AI model, Opus 5.5, built every app from the same brief. A second, Astra, reviewed each one. A third, Fable, scored how readable the code is without being told which app it was reading. All nine apps are live, and their code is public at [amadan.net/carlos/todomvc-bakeoff](https://amadan.net/carlos/todomvc-bakeoff).", |
| "context": "The opening paragraphs under the heading. They say what was built, by whom, where it runs, and that the page compares rather than ranks.", |
| "notes": "Markdown: paragraphs, links, **bold**." |
| }, |
| @@ -281,7 +281,7 @@ |
| "id": "chart.build_cold.note", |
| "section": "Charts", |
| "label": "Cold build: note", |
| - "text": "Every library compiled from already-downloaded sources. Median of three.", |
| + "text": "Every library compiled from already-downloaded sources. One run each, because some take close to an hour under emulation.", |
| "context": "The line under the chart title that says exactly what was measured.", |
| "notes": "One sentence or two." |
| }, |
| @@ -507,12 +507,84 @@ |
| "notes": "One or two words." |
| }, |
| { |
| - "id": "apps.status", |
| + "id": "stack.go", |
| "section": "The apps", |
| - "label": "Label: Status", |
| - "text": "Status", |
| - "context": "A label in each app's card. Whether the app is running.", |
| - "notes": "One or two words." |
| + "label": "Go: stack", |
| + "text": "Go's standard web server and templates, with SQLite through modernc.org/sqlite (pure Go).", |
| + "context": "The 'Stack' line in the Go card: what the builder chose to build the app with.", |
| + "notes": "One sentence." |
| + }, |
| + { |
| + "id": "stack.go-rastrillo", |
| + "section": "The apps", |
| + "label": "Go + Rastrillo: stack", |
| + "text": "Rastrillo 0.28, the CARLOS web framework: chi for routing and GORM over SQLite.", |
| + "context": "The 'Stack' line in the Go + Rastrillo card: what the builder chose to build the app with.", |
| + "notes": "One sentence." |
| + }, |
| + { |
| + "id": "stack.rust", |
| + "section": "The apps", |
| + "label": "Rust: stack", |
| + "text": "axum on tokio, Maud for HTML, and rusqlite with SQLite built in.", |
| + "context": "The 'Stack' line in the Rust card: what the builder chose to build the app with.", |
| + "notes": "One sentence." |
| + }, |
| + { |
| + "id": "stack.zig", |
| + "section": "The apps", |
| + "label": "Zig: stack", |
| + "text": "Zig's standard-library HTTP server, with SQLite 3.54 compiled in.", |
| + "context": "The 'Stack' line in the Zig card: what the builder chose to build the app with.", |
| + "notes": "One sentence." |
| + }, |
| + { |
| + "id": "stack.ocaml", |
| + "section": "The apps", |
| + "label": "OCaml: stack", |
| + "text": "OCaml 5 with Eio and cohttp-eio, and sqlite3-ocaml over SQLite 3.54.", |
| + "context": "The 'Stack' line in the OCaml card: what the builder chose to build the app with.", |
| + "notes": "One sentence." |
| + }, |
| + { |
| + "id": "stack.haskell", |
| + "section": "The apps", |
| + "label": "Haskell: stack", |
| + "text": "warp and WAI, Lucid for HTML, and sqlite-simple.", |
| + "context": "The 'Stack' line in the Haskell card: what the builder chose to build the app with.", |
| + "notes": "One sentence." |
| + }, |
| + { |
| + "id": "stack.swift", |
| + "section": "The apps", |
| + "label": "Swift: stack", |
| + "text": "Hummingbird 2 on SwiftNIO, with SQLiteNIO.", |
| + "context": "The 'Stack' line in the Swift card: what the builder chose to build the app with.", |
| + "notes": "One sentence." |
| + }, |
| + { |
| + "id": "stack.nim", |
| + "section": "The apps", |
| + "label": "Nim: stack", |
| + "text": "Nim's standard-library async HTTP server (a patched copy, to serve a Unix socket and cap request bodies), with SQLite through db_connector.", |
| + "context": "The 'Stack' line in the Nim card: what the builder chose to build the app with.", |
| + "notes": "One sentence." |
| + }, |
| + { |
| + "id": "stack.spinel", |
| + "section": "The apps", |
| + "label": "Spinel: stack", |
| + "text": "tep, a Sinatra-style layer, compiled to a native program by Spinel, with SQLite 3.50 compiled in.", |
| + "context": "The 'Stack' line in the Spinel card: what the builder chose to build the app with.", |
| + "notes": "One sentence." |
| + }, |
| + { |
| + "id": "note.spinel", |
| + "section": "The apps", |
| + "label": "Spinel: note", |
| + "text": "Its server process exited 16 times during the load test, and Carloku started it again each time. Requests that arrived meanwhile got an error.", |
| + "context": "A note in the Spinel card about what happened under heavy load.", |
| + "notes": "" |
| }, |
| { |
| "id": "method.heading", |
| @@ -525,7 +597,7 @@ |
| "id": "method.body", |
| "section": "How we measured", |
| "label": "Body", |
| - "text": "PROSE_PENDING", |
| + "text": "- **The same starting point.** Every builder got the same brief, the same rules for routes and forms, TodoMVC's own page and styles, and the same tests. It chose its own libraries.\n- **The same tests.** TodoMVC's 29 standard tests, run with JavaScript on and again with it off, plus checks for unsafe input, foreign characters, very long titles, one visitor reaching another's list, and a screenshot comparison with TodoMVC's own page.\n- **One build each.** Opus 5.5 built each app in a single unattended session, with a four-hour limit.\n- **One round of review.** Astra reviewed each app for bugs, missed requirements and security problems, and the same builder fixed what it found. A second round was allowed only for security problems; Zig and Nim needed one.\n- **Readability.** Fable scored each app's code out of 10 three times, without knowing which app it was. We show the median.\n- **Speed.** Measured from one test machine over the internet, one app at a time, each on a fresh copy of the app with an empty database. Time to first byte is the median of 30 page loads. The load test doubles the number of simultaneous connections from 1 upwards, 10 seconds per step, first loading a 50-item list, then ticking a to-do on and off, then loading the list again.\n- **Waking up.** Carloku puts an app to sleep after 15 quiet minutes. We timed the first request after each app fell asleep, three times.\n- **Builds.** Every app is built for the 64-bit ARM servers Carloku runs on. A cold build compiles every library from scratch, once per app. A warm build and a one-line change are each the median of three.\n- **Cost.** What each build and its fixes cost at list price, from the builders' own session records.", |
| "context": "Explains the process: same brief, same model, same tests, review and fix rounds, readability scoring, and how the numbers were taken.", |
| "notes": "Markdown." |
| }, |
| @@ -540,7 +612,7 @@ |
| "id": "caveats.body", |
| "section": "Caveats", |
| "label": "Body", |
| - "text": "PROSE_PENDING", |
| + "text": "- **The network is in every speed number.** Requests travelled from our test machine to Carloku over the internet, and the base delay swung between about 17 and 37 milliseconds during the runs. Small differences in first-byte times mean little.\n- **Carloku caps traffic at the edge.** When a request reaches an edge server that isn't the one running the app, Carloku lets at most 64 such requests be in flight at once and turns the rest away with a \"try again shortly\" response. Above 64 connections, the load test partly measures that limit, and whether it applied depended on how our traffic was routed at the time.\n- **Every app ran on Carloku's default size.** Throughput limits reflect that size, not what each language could do on a bigger server.\n- **One build per language.** Another session with the same model could produce a different app, faster or slower.\n- **Readability scores cluster.** Most apps scored 8 out of 10. Treat the score as a rough signal.\n- **Three apps were built under emulation.** OCaml, Haskell and Spinel had no straightforward way to compile for ARM from our x86 machine, so they were built inside an emulated ARM system. Their build times are much longer for that reason, and don't compare with the others.\n- **Spinel restarted under heavy load.** Its server process exited during the load test and Carloku started it again each time.\n- **Mojo was dropped.** Mojo can't produce a single self-contained program file, which is what Carloku runs, so we replaced it with Nim. The platform question is filed as [an idea](https://amadan.net/carlos/platform/-/discussion/360).\n- **Fresh copies for measuring.** To give every app an empty database, each was measured on a new copy of itself, which is the one the live links point to.", |
| "context": "Plain list of what to be careful about when reading the numbers.", |
| "notes": "Markdown list." |
| } |