- README: shipped concurrency/HTTP/WebSockets sat in the roadmap as "not yet available"; "no package manager" contradicted [deps]; the deps example would not have compiled (the key IS the module name) - runtime/README: leads with wovm, wo-rt.c demoted to a historical section; dropped 2 nonexistent recipes, crates/rt, @gc refcounting, 13 suites -> 18 - employee + log-watcher READMEs claimed "does not compile"; both are gates - error catalog: +10 emitted codes incl WO-E250, the only diagnostic the shipped query surface raises; recorded why the sweep rotted - language-surface: group-by parses, then the typechecker refuses it - 00-code-review + 00-link-audit re-run; history kept, not rewritten - 48 dead Rust-era exploration links de-linked rather than re-pointed (their prose names the retired plan by number); successor map -> discarded.md - 08-project-structure: compiler/plan/ never existed; corpus has 9 dirs, 5 empty - releasing.md: dropped a --draft step the workflow never had - new docs/00-doc-audit.md: findings + disposition, incl one row where the audit was wrong and the doc it accused was right - status folders removed: 34 stories flat, status only in frontmatter; 252 links recomputed from resolved paths; board/board-views/structure retaught - story 24 -> in-progress, since frontmatter is now the only truth - new iteration 38: fs mutation verbs + net.connect, the two capability families no iteration owned - new iteration 39: gofiber/fiber v3.5.0 parity study. The ledger called CSRF/sessions unblocked by iteration 34's HMAC, but the runtime has no source of randomness at all - linkcheck skips .dev/.superpowers: 0 broken paths, 0 bad anchors Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
8.8 KiB
08 — Project structure: the writeonce monorepo
Canonical map of the repository: what every root directory is and who writes to
it. Companion to CLAUDE.md (working rules), the status board
(00-status.md), and the story arc
(stories/language-runtime-database/00-story.md).
writeonce is one compiled language, one runtime, one embedded database, one
binary: OCaml woc compiles .wo source to a .wob image, the C wovm
runs it, and woc <dir> bakes the two into a single standalone executable. The
prior Rust wo runtime was removed 2026-08-18 (see
plan/discarded.md); the tree below is only the current
project.
The one-page map
writeonce-all/
├── compiler/ OCaml `woc` — lexer→parser→types→gcinfer→owner→emit; produces the compiler binary
├── runtime/ C `wovm` — the register VM that runs .wob images (src/); retired io_uring reference (wo-rt.c, bench/)
├── database/ C embedded engine — class-shaped tables, secondary indexes, typed WAL + recovery
├── tests/ corpus/ — conformance fixtures: run / compile-fail / trap / gc (+ five reserved, still empty)
├── scripts/ the corpus runner, packaging, linkcheck, and one acceptance script per sample
├── bench/ baseline.json (the db-bench gate's thresholds) + results/ + compare/ (Go+SQLite peer)
├── docs/ ALL documentation: numbered docs, stories/, plan/, examples/, guides/, superpowers/
├── dist/ `just dist` output: writeonce-<ver>-linux-amd64.tar.gz + .sha256
├── .github/ workflows/release.yml — builds, verifies and publishes on a `v*` tag push
├── .claude/ agents/ — project subagent definitions (see docs/guides/database-developer-subagent.md)
├── .dev/ gitignored per-developer links + reference study trees (v1 crates, colibri, llama-cpp)
├── justfile task runner: woc-/wovm-build, the *-test gates, oop-accept, dist, install-accept
├── VERSION single-sourced toolchain version (stamped into woc/wovm; asserted by `just dist`)
└── README.md the getting-started front door (also the writeonce.de landing content)
target/ and .vscode/ are local build and editor state, not part of the
project layout.
Root directories in detail
compiler/ — the OCaml woc compiler
compiler/
├── dune-project
├── README.md orientation: pipeline map, build/test commands
├── src/ one module per stage: diag, token, lexer, ast, parser,
│ types, gcinfer, owner, emit, disasm, dump
├── bin/main.ml the woc executable — check / build-from-manifest / --emit /
│ build / version / --update-deps / the --dump-* modes / -D
└── test/ golden runner + golden/ fixtures per stage (tokens, ast,
owner, owner-err, bc) + fixtures/driver/ CLI-smoke cases
The compiler-track plan docs live under
plan/compiler/ in this docs/ tree, not
inside compiler/ — the repo rule below applies to the compiler like everything
else.
Doctrine: OCaml stdlib only — no Menhir, no ppx, no opam libraries; handwritten
lexer and recursive-descent parser. Build: just woc-build; gate:
just woc-test. Architecture map:
plan/compiler/architecture.md.
runtime/ — the C wovm VM
The register interpreter that loads and runs .wob images, plus the arena,
inferred GC, and the systems stdlib (fs/time/env/net/proc/json).
runtime/src/main.c also reads an image embedded in its own binary
(/proc/self/exe), which is how woc build produces a standalone executable.
wo-rt.c + bench/ are the phase A–F io_uring event-loop reference that fed
the design (see plan/exploration/c-runtime/00-plan.md).
Doctrine: C11, libc only, direct syscalls; computed-goto dispatch with
-DWO_ISO_C fallback; ASan/UBSan gates. Build: just wovm-build; gate:
just wovm-test (both dispatch flavors + cli_smoke).
database/ — the embedded engine
Statically linked into every wovm (and every test binary) — one binary, no
separate database process. Class-shaped row slabs, an open-addressing id hash,
secondary-index multimaps with @unique enforcement, foreign-key restrict, and
a typed write-ahead log (len|crc|payload|mark, ack-after-fsync, torn-tail
drop, boot replay). Every class is a table; durability is opt-in via WO_DATA.
See database/src/CODE-LOGIC.md. The query surface that drives it lives in the
compiler (emit.ml), lowered to engine builtins — no SQL text in the image.
tests/, scripts/
tests/corpus/— the conformance spine. Four directories carry fixtures:run/(60),compile-fail/(46),trap/(5),gc/(2). Five more —actor/,db/,lang/,sys/,sample-logwatcher/— are reserved slots from the original plan and still empty; seetests/corpus/README.mdfor which plan each was to be filled by. Exact-outcome matching: byte-equal stdout, exactWO-E###, exact trap code. Driven byscripts/oop-e2e.sh(just oop-e2e).scripts/— the corpus runner (oop-e2e.sh) andsingle-binary-smoke.sh; packaging (mkdist.sh,install-accept.sh); the docs gate (linkcheck.py); the benchmark campaign (db-bench.py); and one acceptance script per sample —employee-accept.sh,log-watcher-accept.sh,web-app-accept.sh,site-accept.sh,fibers-accept.sh,db-actor-accept.sh,deps-accept.sh.
docs/ — all documentation
docs/
├── 00-*.md, 01-problem.md, 08-*.md principles / code-review / dependency-graph /
│ link-audit / doc-audit / problem / structure
├── stories/ 00-status.md (the board) + board-views.md +
│ the canonical iteration arc (language-runtime-database/,
│ FLAT — status lives in each story's frontmatter)
├── active-slice-*.md the live slice's one marker doc, deleted when it lands
├── guides/ runbooks: releasing, language-surface, subagents
├── examples/ 13 sample projects, 8 of them wired to a `just` recipe
├── plan/ compiler/ plans, oop-vm/ contracts, exploration/ studies,
│ perf-targets.md, discarded.md + learnings.md registers
└── superpowers/ specs/ (approved designs) + plans/ (implementation plans)
Repo rule: documentation belongs here; code directories keep one orientation README each.
.dev/ — developer-local + reference
Gitignored per-machine tooling state plus reference/ study trees: the v1
wo-* crates workspace, colibri/llama-cpp vendored studies, and
linux//go/ source symlinks. Read-only; nothing here is built by the main
gates.
Build & test flow
just woc-build + just wovm-build produce the two binaries; just oop-accept
runs the full milestone gate (compile-time budget, conformance corpus under
ASan, single-binary smoke, both unit gates). Sample acceptance: just employee
(database), just log-watcher (systems stdlib), just web-app and just site
(the framework consumed through [deps]), just fibers and just db-actor
(the concurrency arc), just deps-accept (the package manager),
just db-bench / db-bench-quick (the benchmark campaign, gated against
bench/baseline.json). Docs gate: just linkcheck. Packaging: just dist →
writeonce-<ver>-linux-amd64.tar.gz, proven by just install-accept;
publishing is .github/workflows/release.yml on a v* tag
(see guides/releasing.md).
Naming conventions
- Binaries:
woc(OCaml compiler),wovm(C VM); awoc build/woc <dir>output is named by the project'swo.toml. - Story iterations:
<NN>-<topic>.md, flat indocs/stories/language-runtime-database/. No directory encodes status (directive 2026-08-26) — each story'sstatus:frontmatter key is the only place state is recorded, so a status change is a one-line edit and never moves a file or breaks a link. - Plan/spec files:
YYYY-MM-DD-<topic>.mdunderdocs/superpowers/{specs,plans}/; compiler plans underdocs/plan/compiler/; normative contracts underdocs/plan/oop-vm/. - A project's dependencies (iteration 15):
wo.toml [deps]declares exact-rev git deps; they fetch to.wo-deps/<name>/(gitignored) and pin inwo.lock(committed). - Sample projects live under
docs/examples/<name>/with their ownwo.tomland modulejustfile.