feat: program mode (argv + exit code); reject + on Text; nil compares by word
docs/examples/log-watcher now COMPILES AND RUNS: `wovm lw.wob watch app.log 2 1`
tails a live file, classifies levels and fires its alert
("last entry is error, quiet for 2s"). corpus 71/0, woc 565/0, wovm gates green.
- program mode: the entry is `fn main` taking nothing or one `multi Text`;
runtime/src/main.c builds that list from the program's own arguments (not
the program name, not the image path) and the entry's return value is the
process exit code (low byte); the loader accepts a 0- or 1-arg free-fn entry
- `+` on Text is now WO-E201 pointing at `..`. This was a memory-safety hole,
not a style nit: the emitter lowered it to ADD on two heap pointers, and the
workload's own `out = out + char_of(c)` produced a wild pointer that
segfaulted the VM inside starts_with. Reported off confident types only
- `x == nil` / `x != nil` lower to EQ (a word compare), never EQS: nil is the
zero word and EQS dereferences its operands, so a nil guard would trap
instead of answering
- docs/examples/log-watcher: seven `+`-on-Text sites corrected to `..`
(logtail sanitize, mcp header/body/carry assembly, supervisor detections
line) — the sample was carrying the Haxe habit, and the language reserves
`+` for arithmetic by doctrine
- wovm CLI takes arguments after the image path (`wovm <file.wob> [args...]`)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ebf82ca9c0
commit
940ad99c40
8 changed files with 191 additions and 99 deletions
|
|
@ -1339,6 +1339,10 @@ let container_imm (p : pctx) (expected : Ast.field_ty option) (map : bool) : int
|
|||
| _ -> None)
|
||||
| None -> None
|
||||
|
||||
(* `nil` written literally on either side of a comparison — see emit_binary's
|
||||
Eq/Ne cases for why the distinction matters. *)
|
||||
let is_nil_lit (e : Ast.expr) : bool = match e.Ast.kind with Ast.NilLit -> true | _ -> false
|
||||
|
||||
let rec emit_expr (p : pctx) (f : fstate) (v : views) ~(dst : int) ?expected (e : Ast.expr) : unit =
|
||||
f.f_cur_line <- e.pos.line;
|
||||
(match e.kind with
|
||||
|
|
@ -1600,14 +1604,26 @@ and emit_binary (p : pctx) (f : fstate) (v : views) ~(dst : int) (op : Ast.binop
|
|||
| Le -> simple op_le
|
||||
| Gt -> swapped op_lt
|
||||
| Ge -> swapped op_le
|
||||
| Eq -> if is_text p f l || is_text p f r then simple op_eqs else simple op_eq
|
||||
(* A comparison against `nil` is a WORD compare, never a content compare:
|
||||
nil is the zero word, and EQS would dereference it as a `wo_str*` (the
|
||||
VM's str_check traps on that, so an `x != nil` guard would trap instead
|
||||
of answering). Text-vs-Text still uses EQS. *)
|
||||
| Eq ->
|
||||
if is_nil_lit l || is_nil_lit r then simple op_eq
|
||||
else if is_text p f l || is_text p f r then simple op_eqs
|
||||
else simple op_eq
|
||||
| Ne ->
|
||||
(* no NE opcode in the v1 set: `a != b` is `(a == b) == 0`. The
|
||||
format doc governs, so this is a lowering, not a new opcode. *)
|
||||
let a = emit_operand p f v l in
|
||||
let b = emit_operand p f v r in
|
||||
let t = alloc_temp p f pos in
|
||||
put f (ins_abc (if is_text p f l || is_text p f r then op_eqs else op_eq) t a b);
|
||||
put f
|
||||
(ins_abc
|
||||
(if is_nil_lit l || is_nil_lit r then op_eq
|
||||
else if is_text p f l || is_text p f r then op_eqs
|
||||
else op_eq)
|
||||
t a b);
|
||||
let z = alloc_temp p f pos in
|
||||
put f (ins_abx op_loadk z (check_bx p f pos "constant" (const_int p 0)));
|
||||
put f (ins_abc op_eq dst t z)
|
||||
|
|
@ -3623,10 +3639,19 @@ let emit ~(syms : Types.symbols) ~(module_of : string -> string)
|
|||
mr_code = [||]; mr_lines = []; mr_drops = [] }
|
||||
:: !methods;
|
||||
bodies := (u, None, m, !nmethods) :: !bodies;
|
||||
(* the entry point is the zero-argument free fn `main` —
|
||||
the loader's own rule for an entry (a zero-arg free fn)
|
||||
plus one fixed name so `wovm image.wob` needs no flag *)
|
||||
if m.name = "main" && m.params = [] then begin
|
||||
(* Program mode: the entry is the free fn `main`, taking either
|
||||
nothing or one `multi Text` of command-line arguments (the
|
||||
runtime builds it — runtime/src/main.c). One fixed name, so
|
||||
`wovm image.wob` needs no flag, and its return value is the
|
||||
process exit code. *)
|
||||
let entry_shaped =
|
||||
m.name = "main"
|
||||
&& match m.params with
|
||||
| [] -> true
|
||||
| [ (pa : Ast.param) ] -> ( match pa.Ast.ty with Ast.Multi "Text" -> true | _ -> false)
|
||||
| _ -> false
|
||||
in
|
||||
if entry_shaped then begin
|
||||
entry := !nmethods;
|
||||
(match m.ret with
|
||||
| Some ty when ty <> Ast.Scalar "Int" ->
|
||||
|
|
|
|||
|
|
@ -1314,6 +1314,36 @@ let typecheck_program ~file ~(module_of : string -> string)
|
|||
())
|
||||
| _ -> ());
|
||||
{ typ = TScalar "Bool"; is_nil = false }
|
||||
| Binary (((Add | Sub | Mul | Div | Mod) as op), left, right) ->
|
||||
let lres = typecheck_expr env cenv left in
|
||||
let rres = typecheck_expr env cenv right in
|
||||
(* `+` is arithmetic, never string addition (docs/plan/oop-vm/
|
||||
08-builtin-surface.md's operator table) — and a Text operand here
|
||||
is not a harmless type slip: the emitter would lower it to ADD on
|
||||
two heap pointers, producing a wild pointer with no diagnostic. So
|
||||
this is reported off CONFIDENT types (the same "stay silent when
|
||||
underivable" contract as every other check here) and names the
|
||||
operator that does concatenate. *)
|
||||
let text_side (e : expr) (r : expr_type_result) : bool =
|
||||
match confident_typ cenv e with
|
||||
| Some t -> unwrap_nullable t = TScalar "Text"
|
||||
| None -> ( match e.kind with StrLit _ | Interp _ -> true | _ -> ignore r; false)
|
||||
in
|
||||
if text_side left lres || text_side right rres then
|
||||
Diag.Collector.add collector
|
||||
(Diag.error ~code:type_mismatch_code ~file ~line:e.pos.line ~col:e.pos.col
|
||||
~message:
|
||||
(Printf.sprintf
|
||||
"`%s` is arithmetic and never joins text — use `..` to concatenate"
|
||||
(match op with
|
||||
| Add -> "+"
|
||||
| Sub -> "-"
|
||||
| Mul -> "*"
|
||||
| Div -> "/"
|
||||
| _ -> "%"))
|
||||
());
|
||||
{ typ = (match confident_typ cenv left with Some t -> t | None -> TScalar "Int");
|
||||
is_nil = false }
|
||||
| Binary (_, left, right) ->
|
||||
let _ = typecheck_expr env cenv left in
|
||||
let _ = typecheck_expr env cenv right in
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ banner and this board change. Every plan and phase doc opens with a
|
|||
discarded/learnings registers carry none by design.
|
||||
|
||||
Update this board in the same change that finishes work — move the item to done
|
||||
with *what actually landed*, set the next in-progress item, and record any
|
||||
with _what actually landed_, set the next in-progress item, and record any
|
||||
rejection in [`discarded.md`](plan/discarded.md) with its reason.
|
||||
|
||||
Statuses: ✅ **done** · 🔄 **in progress** · ⬜ **pending** · ⏸ **parked**
|
||||
Statuses: ✅ **done** · 🔄 **in progress** · ⬜ **pending** · ⏸ **hold**
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -34,7 +34,7 @@ optionals throughout in place of the Haxe original's sentinel values, so
|
|||
nothing else in this plan can land ahead of it.
|
||||
|
||||
Two tracks run in this repo. The critical path is the **language track**:
|
||||
iterations 3 → 4 → 5 → 6 → 7, ending at *compile and run log-watcher*. The
|
||||
iterations 3 → 4 → 5 → 6 → 7, ending at _compile and run log-watcher_. The
|
||||
Rust-runtime track is shipped-and-maintained, not advancing.
|
||||
|
||||
---
|
||||
|
|
@ -46,29 +46,29 @@ Rust-runtime track is shipped-and-maintained, not advancing.
|
|||
an unsplittable slice with Given/When/Then acceptance and a pointer to the plan
|
||||
that sequences its tasks. Read one, approve, then the next starts.
|
||||
|
||||
| # | Iteration | State |
|
||||
| --- | --- | --- |
|
||||
| 1 | [Principles doc](stories/language-runtime-database/01-principles-doc.md) | ✅ |
|
||||
| 2 | [VM core (`wovm`)](stories/language-runtime-database/02-vm-core.md) | ✅ |
|
||||
| 3 | [Compiler front (`woc`)](stories/language-runtime-database/03-compiler-front.md) | ✅ (known gaps below) |
|
||||
| 4 | [Single binary end-to-end](stories/language-runtime-database/04-single-binary-e2e.md) | ✅ (known gaps below) |
|
||||
| 5 | [Language surface](stories/language-runtime-database/05-language-surface.md) | 🔄 **next** |
|
||||
| 6 | [Program mode + stdlib](stories/language-runtime-database/06-program-mode-stdlib.md) | ⬜ |
|
||||
| 7 | [log-watcher proof](stories/language-runtime-database/07-logwatcher-proof.md) | ⬜ acceptance |
|
||||
| 7b | [Inferred GC + mark-sweep](stories/language-runtime-database/07b-inferred-gc-mark-sweep.md) | ⬜ closes iteration 4's gate |
|
||||
| 8 | [Shard-actor runtime](stories/language-runtime-database/08-shard-actor-runtime.md) | ⬜ |
|
||||
| 9 | [Database engine](stories/language-runtime-database/09-database-engine.md) | ⬜ |
|
||||
| 9b | [`@table`, relations, query](stories/language-runtime-database/09b-table-relations-query.md) | ⬜ needs a spec first |
|
||||
| 10 | [HTTP service layer](stories/language-runtime-database/10-http-service.md) | ⬜ |
|
||||
| 11 | [Fibers](stories/language-runtime-database/11-fibers.md) | ⬜ |
|
||||
| 12 | [Blue-green deploy](stories/language-runtime-database/12-blue-green-deploy.md) | ⬜ |
|
||||
| # | Iteration | State |
|
||||
| --- | -------------------------------------------------------------------------------------------- | ---------------------------- | ---- |
|
||||
| 1 | [Principles doc](stories/language-runtime-database/01-principles-doc.md) | ✅ |
|
||||
| 2 | [VM core (`wovm`)](stories/language-runtime-database/02-vm-core.md) | ✅ |
|
||||
| 3 | [Compiler front (`woc`)](stories/language-runtime-database/03-compiler-front.md) | ✅ (known gaps below) |
|
||||
| 4 | [Single binary end-to-end](stories/language-runtime-database/04-single-binary-e2e.md) | ✅ (known gaps below) |
|
||||
| 5 | [Language surface](stories/language-runtime-database/05-language-surface.md) | 🔄 **next** |
|
||||
| 6 | [Program mode + stdlib](stories/language-runtime-database/06-program-mode-stdlib.md) | ⬜ |
|
||||
| 7 | [log-watcher proof](stories/language-runtime-database/07-logwatcher-proof.md) | ⬜ acceptance |
|
||||
| 7b | [Inferred GC + mark-sweep](stories/language-runtime-database/07b-inferred-gc-mark-sweep.md) | ⬜ closes iteration 4's gate |
|
||||
| 8 | [Shard-actor runtime](stories/language-runtime-database/08-shard-actor-runtime.md) | ⬜ |
|
||||
| 9 | [Database engine](stories/language-runtime-database/09-database-engine.md) | ⬜ |
|
||||
| 9b | [`@table`, relations, query](stories/language-runtime-database/09b-table-relations-query.md) | ⬜ needs a spec first |
|
||||
| 10 | [HTTP service layer](stories/language-runtime-database/10-http-service.md) | ⬜ | Hold |
|
||||
| 11 | [Fibers](stories/language-runtime-database/11-fibers.md) | ⬜ | Hold |
|
||||
| 12 | [Blue-green deploy](stories/language-runtime-database/12-blue-green-deploy.md) | ⬜ | Hold |
|
||||
|
||||
---
|
||||
|
||||
## In progress
|
||||
|
||||
| Track | Item | Where |
|
||||
| --- | --- | --- |
|
||||
| Track | Item | Where |
|
||||
| -------- | ---------------------------------------------------------------------- | ---------------------------------------------------------- |
|
||||
| Language | Iteration 5 — Haxe-parity language surface, `?T` forced handling first | [plan 8](plan/compiler/2026-08-01-haxe-parity-language.md) |
|
||||
|
||||
Nothing else should be started until iteration 5 lands. Off-critical-path work
|
||||
|
|
@ -80,16 +80,16 @@ is parked by explicit scope directive (2026-08-08).
|
|||
|
||||
### Language track — compiler + VM (OOP track)
|
||||
|
||||
| Status | Item | Doc | What actually landed |
|
||||
| --- | --- | --- | --- |
|
||||
| ✅ | Principles | [`../00-principles.md`](00-principles.md) | 13 principles, each with a why and a link to the doc that enforces it |
|
||||
| ✅ | `wovm` VM core | [plan 1](superpowers/plans/2026-08-01-wob-format-and-vm-core.md) | `.wob` v1 loader with full static validation, register interpreter (computed-goto + ISO-C fallback), arena with size-class free lists, borrow word, RC + budgeted Bacon–Rajan cycle collector, drop-map trap unwinding, containers, builtins, ICALL, CLI. 13 suites × 2 dispatch flavors + CLI smoke, ASan/UBSan clean |
|
||||
| ✅ | `.wob` format contract | [`oop-vm/00-wob-format.md`](plan/oop-vm/00-wob-format.md) | Normative; twinned with `runtime/src/wob.h` |
|
||||
| ✅ | `woc` compiler front | [plan 2](plan/compiler/2026-08-01-woc-compiler-front.md) | Tasks 1–8: dune scaffold, `diag` (WO-E codes, two-site related errors, ordered dedup), newline-significant lexer at rt parity, declaration + statement/expression parser with skip-on-block and multi-error recovery, typechecker (field kinds, `?T` plumbing, W201, E225, E214), MVS ownership pass with the four emitter tables, driver with directory discovery + cross-file programs. 14 + 264 checks |
|
||||
| ✅ | Error catalog | [`oop-vm/01-error-catalog.md`](plan/oop-vm/01-error-catalog.md) | 14 emitted codes + 10 reserved, each with the reason it is not yet emitted |
|
||||
| ✅ | log-watcher `.wo` sample | [`../examples/log-watcher/`](examples/log-watcher/README.md) | Eight-file port authored docs-first with its `.hx` mapping table; compiles for real in iteration 7 |
|
||||
| ✅ | Scalar cleanup | [`discarded.md`](plan/discarded.md) | `Money`/`SKU`/`Float` and the abstract allowlist removed; `abstract` flipped adopt → reject |
|
||||
| ✅ | `woc` emitter, corpus, single binary | [plan 3](plan/compiler/2026-08-01-wob-emit-e2e-single-binary.md) | Tasks 1–6 + 8 (Task 7, a parity harness against the Rust runtime, **deferred by explicit user decision** — the two stacks diverge by design). Bytecode emitter (`emit.ml`) + disassembler (`disasm.ml`, `--dump-bc`); three-kind conformance harness (`scripts/oop-e2e.sh`, `just oop-e2e`) over `tests/corpus/{run,compile-fail,trap,gc}`; pricing-demo + ownership/trap corpora (19 fixtures); `@gc` cycle collector's post-exit pump (`WO_GC_BUDGET`/`WO_GC_TRACE`) + 2 gc fixtures (`gc/held-cycle` retired — see criterion-3 closure below); `woc build` single-binary output + relocation/corrupt-trailer smoke; `WO-E405` closing criterion 3's ASan leak (entry must return `Int`); `just oop-accept` wiring all five spec criteria + both unit gates into one command. 14 + 399 compiler checks; `oop-e2e` 25/25 against the release `wovm`. **Milestone-1 acceptance gate is fully green — all five criteria met** (see the dated acceptance note in `docs/superpowers/specs/2026-08-01-oop-compiler-vm-design.md`) |
|
||||
| Status | Item | Doc | What actually landed |
|
||||
| ------ | ------------------------------------ | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| ✅ | Principles | [`../00-principles.md`](00-principles.md) | 13 principles, each with a why and a link to the doc that enforces it |
|
||||
| ✅ | `wovm` VM core | [plan 1](superpowers/plans/2026-08-01-wob-format-and-vm-core.md) | `.wob` v1 loader with full static validation, register interpreter (computed-goto + ISO-C fallback), arena with size-class free lists, borrow word, RC + budgeted Bacon–Rajan cycle collector, drop-map trap unwinding, containers, builtins, ICALL, CLI. 13 suites × 2 dispatch flavors + CLI smoke, ASan/UBSan clean |
|
||||
| ✅ | `.wob` format contract | [`oop-vm/00-wob-format.md`](plan/oop-vm/00-wob-format.md) | Normative; twinned with `runtime/src/wob.h` |
|
||||
| ✅ | `woc` compiler front | [plan 2](plan/compiler/2026-08-01-woc-compiler-front.md) | Tasks 1–8: dune scaffold, `diag` (WO-E codes, two-site related errors, ordered dedup), newline-significant lexer at rt parity, declaration + statement/expression parser with skip-on-block and multi-error recovery, typechecker (field kinds, `?T` plumbing, W201, E225, E214), MVS ownership pass with the four emitter tables, driver with directory discovery + cross-file programs. 14 + 264 checks |
|
||||
| ✅ | Error catalog | [`oop-vm/01-error-catalog.md`](plan/oop-vm/01-error-catalog.md) | 14 emitted codes + 10 reserved, each with the reason it is not yet emitted |
|
||||
| ✅ | log-watcher `.wo` sample | [`../examples/log-watcher/`](examples/log-watcher/README.md) | Eight-file port authored docs-first with its `.hx` mapping table; compiles for real in iteration 7 |
|
||||
| ✅ | Scalar cleanup | [`discarded.md`](plan/discarded.md) | `Money`/`SKU`/`Float` and the abstract allowlist removed; `abstract` flipped adopt → reject |
|
||||
| ✅ | `woc` emitter, corpus, single binary | [plan 3](plan/compiler/2026-08-01-wob-emit-e2e-single-binary.md) | Tasks 1–6 + 8 (Task 7, a parity harness against the Rust runtime, **deferred by explicit user decision** — the two stacks diverge by design). Bytecode emitter (`emit.ml`) + disassembler (`disasm.ml`, `--dump-bc`); three-kind conformance harness (`scripts/oop-e2e.sh`, `just oop-e2e`) over `tests/corpus/{run,compile-fail,trap,gc}`; pricing-demo + ownership/trap corpora (19 fixtures); `@gc` cycle collector's post-exit pump (`WO_GC_BUDGET`/`WO_GC_TRACE`) + 2 gc fixtures (`gc/held-cycle` retired — see criterion-3 closure below); `woc build` single-binary output + relocation/corrupt-trailer smoke; `WO-E405` closing criterion 3's ASan leak (entry must return `Int`); `just oop-accept` wiring all five spec criteria + both unit gates into one command. 14 + 399 compiler checks; `oop-e2e` 25/25 against the release `wovm`. **Milestone-1 acceptance gate is fully green — all five criteria met** (see the dated acceptance note in `docs/superpowers/specs/2026-08-01-oop-compiler-vm-design.md`) |
|
||||
|
||||
**Known gaps carried out of iteration 3** — recorded, not silently owed:
|
||||
|
||||
|
|
@ -139,7 +139,7 @@ is parked by explicit scope directive (2026-08-08).
|
|||
a class-returning `main` was never legal — `WO-E405`
|
||||
(`compiler/src/emit.ml`, `01-error-catalog.md`) now rejects it at
|
||||
compile time, and `gc/held-cycle` is retired because its premise (an
|
||||
externally-held cycle survives a *post-exit* pump) is no longer
|
||||
externally-held cycle survives a _post-exit_ pump) is no longer
|
||||
expressible — see `oop-vm/02-corpus.md`'s "Retired" note for why, and
|
||||
for where the scenario it meant to cover is actually proven
|
||||
(`runtime/test/test_cycle.c`, plus a proper in-flight fixture scheduled
|
||||
|
|
@ -148,23 +148,23 @@ is parked by explicit scope directive (2026-08-08).
|
|||
|
||||
### Rust runtime track — Stage 2 shipped, maintained
|
||||
|
||||
| Status | Phase | Doc | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| ✅ | 01 crate scaffolding | [done/01](plan/done/01-scafolding-crates.md) | 15 crates |
|
||||
| ✅ | 02 epoll event loop | [done/02](plan/done/02-event-loop-epoll.md) | `runtime/netpoll_epoll.rs` |
|
||||
| ✅ | 03 hand-rolled HTTP | [done/03](plan/done/03-hand-rolled-http.md) | + keep-alive & pipelining |
|
||||
| ✅ | 04 tokio/axum cutover | [done/04](plan/done/04-cutover-remove-tokio-axum.md) | deps now: anyhow, serde, serde_json, libc |
|
||||
| ✅ | 09a thread-per-core | [09](plan/09-concurrency-scaleout.md) | `scheduler.rs`, `SO_REUSEPORT`, pinned `wo-shard-<t>` workers |
|
||||
| ✅ | 09b sharded engine | [09](plan/09-concurrency-scaleout.md) | `shard.rs` bus; `Arc<Mutex<Engine>>` deleted; interleaved ids |
|
||||
| ✅ | 09c per-shard WAL | [09](plan/09-concurrency-scaleout.md) | ack-after-fsync; boot replay; `meta` shard guard |
|
||||
| ✅ | — keep-alive follow-up | [09](plan/09-concurrency-scaleout.md) | reads ×3.4 → 770k/s |
|
||||
| ✅ | — io_uring group commit | [09](plan/09-concurrency-scaleout.md) | raw ring; 4.7× durable writes on real disk |
|
||||
| ✅ | 16a PG wire client | [16](plan/16-postgres-mirror.md) | hand-rolled protocol v3, zero crates |
|
||||
| ✅ | 16b PG backup mirror | [16](plan/16-postgres-mirror.md) | async JSONB upserts behind the WAL ack; RAM authoritative |
|
||||
| ✅ | 13a class surface | [13](plan/13-class-model-live-pricing.md) | `class` parses, CRUD serves |
|
||||
| ✅ | 13b method execution | [13](plan/13-class-model-live-pricing.md) | row-scoped txn per call; abort → 409 rollback |
|
||||
| ✅ | — `@table` + indexed DML | [13](plan/13-class-model-live-pricing.md) | secondary indexes, `find_by`, `select Type{…}`, REST filters |
|
||||
| ✅ | C proving ground A–F | [exploration/c-runtime/00-plan.md](plan/exploration/c-runtime/00-plan.md) | 859k reads/s, 618k durable commits/s; found the ack-ordering + fd-ABA bugs the Rust port avoided |
|
||||
| Status | Phase | Doc | Notes |
|
||||
| ------ | ------------------------ | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
|
||||
| ✅ | 01 crate scaffolding | [done/01](plan/done/01-scafolding-crates.md) | 15 crates |
|
||||
| ✅ | 02 epoll event loop | [done/02](plan/done/02-event-loop-epoll.md) | `runtime/netpoll_epoll.rs` |
|
||||
| ✅ | 03 hand-rolled HTTP | [done/03](plan/done/03-hand-rolled-http.md) | + keep-alive & pipelining |
|
||||
| ✅ | 04 tokio/axum cutover | [done/04](plan/done/04-cutover-remove-tokio-axum.md) | deps now: anyhow, serde, serde_json, libc |
|
||||
| ✅ | 09a thread-per-core | [09](plan/09-concurrency-scaleout.md) | `scheduler.rs`, `SO_REUSEPORT`, pinned `wo-shard-<t>` workers |
|
||||
| ✅ | 09b sharded engine | [09](plan/09-concurrency-scaleout.md) | `shard.rs` bus; `Arc<Mutex<Engine>>` deleted; interleaved ids |
|
||||
| ✅ | 09c per-shard WAL | [09](plan/09-concurrency-scaleout.md) | ack-after-fsync; boot replay; `meta` shard guard |
|
||||
| ✅ | — keep-alive follow-up | [09](plan/09-concurrency-scaleout.md) | reads ×3.4 → 770k/s |
|
||||
| ✅ | — io_uring group commit | [09](plan/09-concurrency-scaleout.md) | raw ring; 4.7× durable writes on real disk |
|
||||
| ✅ | 16a PG wire client | [16](plan/16-postgres-mirror.md) | hand-rolled protocol v3, zero crates |
|
||||
| ✅ | 16b PG backup mirror | [16](plan/16-postgres-mirror.md) | async JSONB upserts behind the WAL ack; RAM authoritative |
|
||||
| ✅ | 13a class surface | [13](plan/13-class-model-live-pricing.md) | `class` parses, CRUD serves |
|
||||
| ✅ | 13b method execution | [13](plan/13-class-model-live-pricing.md) | row-scoped txn per call; abort → 409 rollback |
|
||||
| ✅ | — `@table` + indexed DML | [13](plan/13-class-model-live-pricing.md) | secondary indexes, `find_by`, `select Type{…}`, REST filters |
|
||||
| ✅ | C proving ground A–F | [exploration/c-runtime/00-plan.md](plan/exploration/c-runtime/00-plan.md) | 859k reads/s, 618k durable commits/s; found the ack-ordering + fd-ABA bugs the Rust port avoided |
|
||||
|
||||
Ecommerce sample (verified 2026-06-13): `api.rest` 17/17 expected statuses pass.
|
||||
|
||||
|
|
@ -174,18 +174,18 @@ Ecommerce sample (verified 2026-06-13): `api.rest` 17/17 expected statuses pass.
|
|||
|
||||
### Language track — sequenced, on the critical path
|
||||
|
||||
| # | Item | Plan |
|
||||
| --- | --- | --- |
|
||||
| 5 | Haxe-parity language surface — **`?T` forced handling first**, then switch expressions, records, enum payloads, try/catch, statics, `using`, modules, `is`, `pub(read)`, `#if` | [plan 8](plan/compiler/2026-08-01-haxe-parity-language.md) |
|
||||
| 6 | Program mode + systems stdlib — `fn main`, exit codes, `fs`/`proc`/`net`/`time`/`json` | [plan 9](superpowers/plans/2026-08-01-program-mode-stdlib.md) |
|
||||
| 7 | log-watcher proof — the sample compiles and detects a silent death live | [plan 10](superpowers/plans/2026-08-01-log-watcher-sample.md) |
|
||||
| 7b | Inferred GC + incremental mark-sweep — `@gc` removed, GC-ness inferred, RC retired | [spec](superpowers/specs/2026-08-11-inferred-gc-mark-sweep-design.md) — plan to be written |
|
||||
| 8 | Shard-actor runtime | [plan 4](superpowers/plans/2026-08-01-shard-actor-vm-runtime.md) |
|
||||
| 9 | Database engine binding | [plan 5](superpowers/plans/2026-08-01-db-engine-binding.md) |
|
||||
| 9b | `@table` + relations + language-integrated query | **no spec yet** — three open forks recorded in the iteration; brainstorm before planning |
|
||||
| 10 | HTTP service layer | [plan 6](superpowers/plans/2026-08-01-http-service-layer.md) |
|
||||
| 11 | Fibers | vision §3, [blue-green exploration](plan/exploration/blue-green-vm/00-vision.md) |
|
||||
| 12 | Blue-green deploy | [spec](superpowers/specs/2026-08-03-blue-green-vm-design.md) — plan authored after iterations 9–10 |
|
||||
| # | Item | Plan |
|
||||
| --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- |
|
||||
| 5 | Haxe-parity language surface — **`?T` forced handling first**, then switch expressions, records, enum payloads, try/catch, statics, `using`, modules, `is`, `pub(read)`, `#if` | [plan 8](plan/compiler/2026-08-01-haxe-parity-language.md) |
|
||||
| 6 | Program mode + systems stdlib — `fn main`, exit codes, `fs`/`proc`/`net`/`time`/`json` | [plan 9](superpowers/plans/2026-08-01-program-mode-stdlib.md) |
|
||||
| 7 | log-watcher proof — the sample compiles and detects a silent death live | [plan 10](superpowers/plans/2026-08-01-log-watcher-sample.md) |
|
||||
| 7b | Inferred GC + incremental mark-sweep — `@gc` removed, GC-ness inferred, RC retired | [spec](superpowers/specs/2026-08-11-inferred-gc-mark-sweep-design.md) — plan to be written |
|
||||
| 8 | Shard-actor runtime | [plan 4](superpowers/plans/2026-08-01-shard-actor-vm-runtime.md) |
|
||||
| 9 | Database engine binding | [plan 5](superpowers/plans/2026-08-01-db-engine-binding.md) |
|
||||
| 9b | `@table` + relations + language-integrated query | **no spec yet** — three open forks recorded in the iteration; brainstorm before planning |
|
||||
| 10 | HTTP service layer | [plan 6](superpowers/plans/2026-08-01-http-service-layer.md) |
|
||||
| 11 | Fibers | vision §3, [blue-green exploration](plan/exploration/blue-green-vm/00-vision.md) |
|
||||
| 12 | Blue-green deploy | [spec](superpowers/specs/2026-08-03-blue-green-vm-design.md) — plan authored after iterations 9–10 |
|
||||
|
||||
### Language track — parked until after iteration 12
|
||||
|
||||
|
|
@ -207,27 +207,27 @@ log-watcher proof.
|
|||
|
||||
### Rust runtime track — not advancing while the language track runs
|
||||
|
||||
| Status | Phase | Doc | Notes |
|
||||
| --- | --- | --- | --- |
|
||||
| ⬜ | 05 hand-rolled JSON | [05](plan/05-hand-rolled-json.md) | removes serde/serde_json |
|
||||
| ⬜ | 06 bespoke error type | [06](plan/06-bespoke-error.md) | removes anyhow |
|
||||
| ⬜ | 07 inotify content watcher | [07](plan/07-inotify-content-watcher.md) | `wo dev` hot reload |
|
||||
| ⬜ | 08 sendfile static assets | [08](plan/08-sendfile-static-assets.md) | needed by the parked UI track |
|
||||
| ⬜ | 09d cross-shard subscriptions | [09](plan/09-concurrency-scaleout.md) | LIVE fan-out; pairs with Stage 3 |
|
||||
| ⬜ | 09e cross-shard transactions (2PC) | [09](plan/09-concurrency-scaleout.md) | needed by `fn checkout` spanning shards |
|
||||
| ⬜ | 09f observability & reshard | [09](plan/09-concurrency-scaleout.md) | per-shard metrics, `WO_RESHARD` |
|
||||
| ⬜ | 10–12 storage completion | [10](plan/10-storage-foundations.md), [11](plan/11-wal-and-recovery.md), [12](plan/12-engine-disk-cutover.md) | snapshots, compaction, WAL rotation, mmap arena engine |
|
||||
| ⬜ | 13c LIVE pricing push · Stage 3 wire layer · 13e at scale | [13](plan/13-class-model-live-pricing.md) | replaces the 501 stub |
|
||||
| ⬜ | 15a–15e MCP over streamable HTTP | [15](plan/15-mcp-streamable-http.md) | 15e needs 13c + 09d |
|
||||
| ⬜ | 16c–16f typed columns, lossless resync, restore, SCRAM | [16](plan/16-postgres-mirror.md) | |
|
||||
| Status | Phase | Doc | Notes |
|
||||
| ------ | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ |
|
||||
| ⬜ | 05 hand-rolled JSON | [05](plan/05-hand-rolled-json.md) | removes serde/serde_json |
|
||||
| ⬜ | 06 bespoke error type | [06](plan/06-bespoke-error.md) | removes anyhow |
|
||||
| ⬜ | 07 inotify content watcher | [07](plan/07-inotify-content-watcher.md) | `wo dev` hot reload |
|
||||
| ⬜ | 08 sendfile static assets | [08](plan/08-sendfile-static-assets.md) | needed by the parked UI track |
|
||||
| ⬜ | 09d cross-shard subscriptions | [09](plan/09-concurrency-scaleout.md) | LIVE fan-out; pairs with Stage 3 |
|
||||
| ⬜ | 09e cross-shard transactions (2PC) | [09](plan/09-concurrency-scaleout.md) | needed by `fn checkout` spanning shards |
|
||||
| ⬜ | 09f observability & reshard | [09](plan/09-concurrency-scaleout.md) | per-shard metrics, `WO_RESHARD` |
|
||||
| ⬜ | 10–12 storage completion | [10](plan/10-storage-foundations.md), [11](plan/11-wal-and-recovery.md), [12](plan/12-engine-disk-cutover.md) | snapshots, compaction, WAL rotation, mmap arena engine |
|
||||
| ⬜ | 13c LIVE pricing push · Stage 3 wire layer · 13e at scale | [13](plan/13-class-model-live-pricing.md) | replaces the 501 stub |
|
||||
| ⬜ | 15a–15e MCP over streamable HTTP | [15](plan/15-mcp-streamable-http.md) | 15e needs 13c + 09d |
|
||||
| ⬜ | 16c–16f typed columns, lossless resync, restore, SCRAM | [16](plan/16-postgres-mirror.md) | |
|
||||
|
||||
### Frontend — parked
|
||||
|
||||
| Status | Phase | Doc |
|
||||
| --- | --- | --- |
|
||||
| ⏸ | 13d pricing UI | [13](plan/13-class-model-live-pricing.md) |
|
||||
| ⏸ | 14 MVC UI implementation (14a–f) | [14](plan/14-mvc-ui-implementation.md) |
|
||||
| ⏸ | UI exploration track | [exploration/ui/00-overview.md](plan/exploration/ui/00-overview.md) |
|
||||
| Status | Phase | Doc |
|
||||
| ------ | -------------------------------- | ------------------------------------------------------------------- |
|
||||
| ⏸ | 13d pricing UI | [13](plan/13-class-model-live-pricing.md) |
|
||||
| ⏸ | 14 MVC UI implementation (14a–f) | [14](plan/14-mvc-ui-implementation.md) |
|
||||
| ⏸ | UI exploration track | [exploration/ui/00-overview.md](plan/exploration/ui/00-overview.md) |
|
||||
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ pub fn sanitize(line: Text) -> Text {
|
|||
}
|
||||
continue;
|
||||
}
|
||||
if c >= 32 or c == 9 { out = out + char_of(c); }
|
||||
if c >= 32 or c == 9 { out = out .. char_of(c); }
|
||||
i = i + 1;
|
||||
}
|
||||
return out;
|
||||
|
|
|
|||
|
|
@ -125,9 +125,9 @@ class Mcp {
|
|||
}
|
||||
let resp = self.handle(req);
|
||||
let head = "HTTP/1.1 ${resp.status} ${status_text(resp.status)}\r\n";
|
||||
head = head + "Content-Type: application/json\r\nConnection: close\r\n";
|
||||
head = head .. "Content-Type: application/json\r\nConnection: close\r\n";
|
||||
head = head + "Content-Length: ${len(resp.body)}\r\n\r\n";
|
||||
try net.write(c, head + resp.body) catch (e) {}
|
||||
try net.write(c, head .. resp.body) catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -140,7 +140,7 @@ class Mcp {
|
|||
while header_end == -1 {
|
||||
let got = net.read(c, 8192);
|
||||
if len(got) == 0 { return nil; } -- peer closed mid-headers
|
||||
buf = buf + got;
|
||||
buf = buf .. got;
|
||||
header_end = index_of(buf, "\r\n\r\n");
|
||||
if len(buf) > BODY_MAX * 2 { return nil; } -- runaway header block
|
||||
}
|
||||
|
|
@ -169,7 +169,7 @@ class Mcp {
|
|||
while len(body) < want {
|
||||
let got = net.read(c, want - len(body));
|
||||
if len(got) == 0 { break; }
|
||||
body = body + got;
|
||||
body = body .. got;
|
||||
}
|
||||
let method = "";
|
||||
if len(req_line) > 0 { method = req_line[0]; }
|
||||
|
|
@ -329,7 +329,7 @@ class Tools {
|
|||
if want > CHUNK { want = CHUNK; }
|
||||
let chunk = fs.read_at(path, offset, want);
|
||||
if len(chunk) == 0 { break; }
|
||||
let lines = split(carry + chunk, "\n");
|
||||
let lines = split(carry .. chunk, "\n");
|
||||
carry = pop(lines); -- last piece has no newline yet
|
||||
for line in lines {
|
||||
if index_of(to_lower(line), lc) >= 0 {
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ class Supervisor {
|
|||
}
|
||||
let line = json.encode(Detection { ts: time.iso(now), path: log_path,
|
||||
source: source, event: event, lastError: last_error });
|
||||
try fs.append(self.cfg.detections, line + "\n") catch (e) {
|
||||
try fs.append(self.cfg.detections, line .. "\n") catch (e) {
|
||||
print("DETECTIONS-SINK-ERROR ${self.cfg.detections}: ${e.msg}");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -482,9 +482,12 @@ int wo_load_buf(wo_module *m, const uint8_t *buf, size_t len, char *err,
|
|||
/* entry: a zero-arg free fn */
|
||||
if (entry != WOB_NONE) {
|
||||
if (entry >= m->method_cnt) BAIL("entry method out of range");
|
||||
if (m->methods[entry].arg_cnt != 0 ||
|
||||
/* program mode: an entry is a free fn taking nothing, or one
|
||||
argument — the `multi Text` of command-line arguments the runtime
|
||||
builds (runtime/src/main.c). */
|
||||
if (m->methods[entry].arg_cnt > 1 ||
|
||||
m->methods[entry].class_id != WOB_NONE)
|
||||
BAIL("entry must be a zero-arg free fn");
|
||||
BAIL("entry must be a free fn taking no arguments or one argv `multi Text`");
|
||||
}
|
||||
m->entry = entry;
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "cont.h"
|
||||
#include "gc.h"
|
||||
#include "vm.h"
|
||||
|
||||
|
|
@ -128,9 +129,10 @@ int main(int argc, char **argv) {
|
|||
return 2;
|
||||
}
|
||||
if (self_rc == 0) {
|
||||
/* no embedded image: today's contract, unchanged */
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "usage: wovm <file.wob>\n");
|
||||
/* no embedded image: the image path is argv[1], and program mode
|
||||
passes everything after it to the program itself */
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "usage: wovm <file.wob> [args...]\n");
|
||||
return 2;
|
||||
}
|
||||
if (wo_load_file(&mod, argv[1], err, sizeof err) != 0) {
|
||||
|
|
@ -155,14 +157,46 @@ int main(int argc, char **argv) {
|
|||
wo_module_free(&mod);
|
||||
return 2;
|
||||
}
|
||||
/* Program mode: an entry that declares one parameter gets the program's
|
||||
* OWN arguments as a `multi Text` — not the program name, and not the
|
||||
* image path a plain `wovm image.wob args...` invocation carries. So
|
||||
* `args[0]` is the first real argument (the workload's own contract:
|
||||
* `args[0] == "watch"`, `args[1]` the log file). An entry with no
|
||||
* parameters is called exactly as before. */
|
||||
uint64_t argv_val = 0;
|
||||
uint32_t entry_argc = mod.methods[mod.entry].arg_cnt;
|
||||
if (entry_argc == 1) {
|
||||
wo_multi *args = wo_multi_new(&VM.rt, WO_K_TEXT);
|
||||
if (!args) {
|
||||
fprintf(stderr, "wovm: cannot allocate the argument list\n");
|
||||
wo_vm_destroy(&VM);
|
||||
wo_module_free(&mod);
|
||||
return 2;
|
||||
}
|
||||
int first = self_rc == 0 ? 2 : 1; /* skip the image path when there is one */
|
||||
for (int i = first; i < argc; i++) {
|
||||
wo_str *s = wo_str_new(&VM.rt, argv[i], (uint32_t)strlen(argv[i]));
|
||||
if (!s || wo_multi_push(args, (uint64_t)(uintptr_t)s) != 0) {
|
||||
fprintf(stderr, "wovm: cannot allocate the argument list\n");
|
||||
wo_vm_destroy(&VM);
|
||||
wo_module_free(&mod);
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
argv_val = (uint64_t)(uintptr_t)args;
|
||||
}
|
||||
uint64_t ret = 0;
|
||||
wo_err terr;
|
||||
int rc = wo_vm_call(&VM, mod.entry, NULL, 0, &ret, &terr);
|
||||
int rc = wo_vm_call(&VM, mod.entry, entry_argc == 1 ? &argv_val : NULL, entry_argc, &ret,
|
||||
&terr);
|
||||
if (rc != 0)
|
||||
fprintf(stderr, "trap %u in %s at line %u: %s\n", (unsigned)terr.code,
|
||||
terr.method, (unsigned)terr.line, terr.msg);
|
||||
/* the entry's return value IS the exit code (docs/plan/oop-vm/
|
||||
* 08-builtin-surface.md's "Program entry"): 0..255, a trap is 1 */
|
||||
int exit_code = rc == 0 ? (int)((uint64_t)ret & 0xFF) : 1;
|
||||
gc_pump(&VM);
|
||||
wo_vm_destroy(&VM);
|
||||
wo_module_free(&mod);
|
||||
return rc == 0 ? 0 : 1;
|
||||
return exit_code;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue