Commit graph

93 commits

Author SHA1 Message Date
f430bff5c4 docs: iteration 17 — library projects + dependency privacy (needs refinement)
Brainstorm outcome, deliberately NOT implemented (developer decision: keep
as an iteration needing further refinement). Records:
- the two gaps iterations 15/16 exposed: library-ness is implicit (a
  no-main project fails woc <dir> build mode — the framework is verified
  via an --emit workaround) and the dep boundary leaks internals (pub has
  no dep-private tier: parse_request is as importable as Handler).
- the conventions corpus: Go (package decides program-ness; cmd/;
  internal/ = directory-shaped privacy, zero keywords) vs Rust ([lib]/
  [[bin]] manifest targets; pub(crate)-family keyword visibility). Doctrine
  fit points at Go's shape with an explicit manifest key (writeonce HAS a
  manifest; explicit beats inference in errors).
- four open forks for the spec: kind declaration form; internal/ vs
  pub(lib) vs export-allowlist; dep-boundary-only vs Go's subtree rule;
  lib+bin duality. Draft acceptance criteria; web-app 14/0 as the
  regression gate. Roadmap + board rows added.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 01:23:10 +02:00
d6025e131d docs: iteration 16 closeout (Task 6)
Board row 16 -> landed (web-app 14/0), pending row removed; story header
records the landing + the two as-built discoveries (idle-keep-alive
starvation policy; the two compiler gaps the chain exposed and fixed);
README gains the framework+web-app sample entry; plan checkboxes ticked.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:58:13 +02:00
24c991069d feat: web-app storefront + dep-relative use resolution (iter 16 Task 4)
- docs/examples/web-app: Product (@unique name, backlink orders) / Order
  (ref Product) as @table classes; handlers as Handler classes —
  ListProducts (ordered query -> JSON array), ShowProduct (unique-index
  probe, 404), CreateProduct (checked json.decode -> 400; @unique trap ->
  409), CreateOrder (FK insert), DeleteProduct (FK restrict trap -> 409);
  Auth middleware reads WA_TOKEN. Entry validates port + token honestly.
- The [deps] KEY is the module name `use` imports: hyphens are not
  identifier characters, so the app keys the dep `framework` while the
  repository keeps its long name (recorded in the manifest comment).
- driver fix (real gap the chain exposed): a dependency's INTERNAL `use`
  paths are written against its own root (`use http` inside the framework)
  but compile under `<depname>/...` — compile_image now prefixes dep files'
  use paths with the dep name (stdlib namespaces stay bare; a path already
  starting with the dep name is untouched).

Verified end to end through the full chain (temp git remote of the
framework, file:// substituted, fetch -> lock -> build -> serve): 401
without the token; [] empty list; 201 create; 409 duplicate (@unique);
400 malformed json; list/show payloads exact; 404 unknown product; 201
order; 409 delete-while-referenced (FK restrict) with the server still
serving; SIGTERM clean; the product survives a process restart (WAL
replay). Gates: woc-test 540/0, oop-e2e 88/0, deps-accept 8/0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:56:18 +02:00
67484f799a feat(framework): router + Handler/Middleware + App (iter 16 Task 3)
- router/router.wo: Handler (handle(req) -> Resp) and Middleware
  (before(req) -> ?Resp; nil = continue) structural interfaces; Route/Mw
  record classes built as ctor literals at the registration site — the
  ownership shape the corpus pins (run/container-owned-move);
  route_match with :param captures over '/'-split segments (empties
  dropped, first mismatch wins).
- app.wo: App holds the middleware chain + route table (take-push),
  satisfies http's Dispatcher, dispatches middleware-then-first-match,
  fills the captures onto the borrowed request in place (Dispatcher takes
  `mut req` — the borrow checker rightly refused rebuilding a Req from
  borrowed maps; captures collect locally so a failed match never touches
  the request), 404 fallback, serve(host, port) delegation.

Verified against a throwaway app (not committed): middleware 401
short-circuit without the token; /things/:id captures 42 into the body;
unknown path 404; a DIV0 handler answers 500 and the next request is
served; SIGTERM clean. Framework image emits at 12161 bytes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:53:19 +02:00
994d151a44 feat(framework): HTTP/1.1 parse + serialize + serve loop (iter 16 Task 2)
- http/parse.wo: bounded-read buffering to the header terminator, then
  exactly Content-Length body bytes; %XX decoding ('+' = space in query
  strings only, malformed escapes pass through — parsing stays total);
  path/query split with decoded pairs; header names lowercased; the
  three-state Parsed record (closed / malformed / request) with keep-alive
  carry-over — bytes past this request belong to the next one on the
  connection.
- http/serve.wo: Dispatcher interface (the router's seam), status/reason
  serialization with computed Content-Length, and the blocking loop:
  malformed -> 400 + close; a trapping handler -> 500 AND the loop lives;
  fds closed on every path; env.stopping() honored.
- Connection policy discovered by probing, not assumed: a parked keep-alive
  connection BLOCKS accept on a single-threaded server (probe: client 1
  idles open, client 2 starves). Policy: serve PIPELINED requests on one
  connection (carry non-empty), close when the client would idle; a proxy
  reconnects. README states it.

Verified against a throwaway echo app (not committed): %20 query decode;
two pipelined requests -> two responses on one connection; DIV0 handler ->
500 and the NEXT connection served; GARBAGE -> 400; SIGTERM stops clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:50:55 +02:00
20322d4905 feat(examples): framework skeleton + web-app scaffold (iter 16 Task 1)
- docs/examples/writeonce-framework: library project (no fn main) — wo.toml,
  README (what it is + the honest v1 limits + the proxy TLS/h2 story), and
  http/types.wo: pub Req/Resp records + the response builders (ok_text/
  ok_json/created/not_found/bad_request/unauthorized/conflict/server_error/
  redirect). Typechecks + emits entry-less via woc --emit (1767-byte image).
- docs/examples/web-app: manifest with the real [deps] entry (future GitHub
  URL as documentation; the gate substitutes a file:// remote) + README
  (routes table, run instructions, nginx h2-in-front sketch). Code lands
  with Task 4.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:43:19 +02:00
4c75ba4fbd docs: implementation plan for iteration 16 (web framework + web-app)
6 tasks, words-only: (1) framework skeleton — Req/Resp records + builders,
standalone-typechecking project; (2) HTTP/1.1 parse/serialize + keep-alive
serve loop with the try-bounded dispatch seam (400-close on malformed, 500-
survive on handler traps, fd/stop clean); (3) router with :param captures +
Handler/Middleware interfaces + App.serve; (4) web-app storefront —
@table Product/Order, auth middleware, json routes, [deps] manifest carrying
the future GitHub URL; (5) scripts/web-app-accept.sh — temp git remote from
the framework dir, file:// substitution into a temp app copy, full curl
matrix + restart persistence + SIGTERM + opt-in soak, wired as just web-app;
(6) docs closeout. Both enabling risks retired pre-plan (interface-field
dispatch probe; owned-move container fix pinned by run/container-owned-move).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:38:21 +02:00
f6b5333d40 docs: iteration 15 closeout (Task 5)
- error catalog: WO-E106 (dependency fetch/shape failures, one code, message
  names dep + step) and WO-E107 (dep/local module-name collision) rows.
- README: a Dependencies subsection under the manifest docs — [deps] syntax,
  .wo-deps/wo.lock behavior, offline-when-locked, --update-deps, flat-only.
- board: iteration 15 row -> landed (deps-accept 8/0), pending row removed;
  story 15 header records the landing; 08-project-structure notes
  .wo-deps (gitignored) + wo.lock (committed); plan checkboxes all ticked.

(One self-inflicted casualty during this task, restored from git before
commit: a buggy doc-edit script truncated 08-project-structure.md; the file
was recovered intact and the intended one-liner applied by hand.)

Gates at closeout: deps-accept 8/0, woc-test 540/0, oop-e2e 87/0,
log-watcher 7/0, employee 8/0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:23:18 +02:00
8fcebe60f9 docs: implementation plan for iteration 15 (deps)
5 tasks, words-only per house rule, each with its verify step: (1) manifest
grows the [deps] section + one-line inline-table value (only under [deps]);
(2) resolver — git-binary fetch into .wo-deps/, wo.lock pinning, warm-path
offline guarantee, drift diagnostic, --update-deps, guard rails (transitive
refusal, non-writeonce dep, name collision WO-E107, all fetch failures
WO-E106); (3) multi-root discovery + module_of prefixing dep roots by dep
name + entry restricted to the app's own files; (4) scripts/deps-accept.sh
gate over file:// remotes (8 checks, network-free) + just recipe; (5) docs
closeout (catalog E106/E107, README deps subsection, board/story/structure).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:11:05 +02:00
976ccda225 docs: web framework + deps design — spec + iterations 15/16
Brainstorm outcome (forks locked with the developer):
- TLS: proxy-terminated (nginx/caddy gives browsers TLS+ALPN+h2; the
  framework speaks HTTP/1.1 behind it) — zero TLS in the toolchain, no
  doctrine fight; homegrown TLS refused outright.
- Dependencies: a real mini package manager — wo.toml [deps] with exact-rev
  git deps, wo.lock, .wo-deps cache, `use <dep>` as a module root; fetch by
  shelling to the git binary (no network code in woc); flat-only v1.
- HTTP/2: v1 is HTTP/1.1 keep-alive; h2c is the parked successor behind
  iterations 8/9f/11 (multiplexing needs a scheduler to pay off); the
  bytes/buffer type rides with it, not v1.
- Handler model: no function values by doctrine, so Handler/Middleware are
  structural interfaces (ICALL dispatch, WO-E205-checked); middleware returns
  ?Resp and rides the shipped ?T narrowing.
- Incubation: framework at docs/examples/writeonce-framework/, consuming
  storefront at docs/examples/web-app/ importing it THROUGH [deps] — the
  sample exercises fetch -> lock -> build -> serve -> durable-restart.
- Iteration 10 relationship: service blocks later LOWER ONTO this library.

Files: specs/2026-08-18-web-framework-design.md (A deps normative, B
framework normative, C h2c parked); stories 15-deps-package-manager.md +
16-web-framework.md; roadmap + board rows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 19:07:16 +02:00
f282451867 feat(json): Bool encodes true/false; fraction/exponent decode fails honestly
Closes json's two documented fidelity limits (iteration 5 strictness):

- field_class gains WOB_FIELD_BOOL (a plain `Bool` field) and
  WOB_FIELD_NIL_BOOL (a `?Bool`: WO_NIL_SCALAR nil + bool encoding) — the
  kind byte alone cannot tell a Bool slot from an Int slot, so the metadata
  carries it. Emitter writes them (field_class_meta); loader whitelists
  them; json.c encodes `true`/`false` (and `null` for a ?Bool nil), decode's
  null/omitted-key pre-write covers NIL_BOOL.
- A JSON number with a fraction or exponent is MALFORMED for an Int field:
  the checked decode (`json.decode(t) as T`) yields nil for the whole
  document instead of silently truncating 3.7 to 3 — the language has no
  float, and corrupting data quietly was the one thing a "checked decode"
  must never do. Floats stay representable through a raw `json.Value` field.
- corpus: run/json-bool-fidelity pins the round-trip (true/false both ways,
  ?Bool null both ways, fraction AND exponent rejected).
- Board's two known-gap entries struck; format doc's field_class marker list
  extended.

Verified: oop-e2e 87/0; runtime test + test-iso OK; woc-test 540/0;
log-watcher 7/0; employee 8/0.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 18:04:15 +02:00
a43232dc72 feat(compiler): doctrine reject rows — WO-E105 (iter 5 strictness)
The verdict table's reject half is enforced: a Haxe habit fails loudly at
its own position with the doctrine reason, instead of a generic syntax
error — or, worst, compiling clean: `return super.f()` used to exit 0 (the
unresolved ident placeholder swallowed it).

- parser.ml: doctrine_reject_reason maps each rejected word to its spec
  reason (inheritance quartet -> principle 4; cast; Dynamic/untyped ->
  principle 13; macro; extern -> principle 10; operator). Fired at three
  chokepoints: `class B extends A` (with skip-to-brace recovery so the body
  still parses), an expression head (`super`, `cast 3`, `untyped x`), and a
  top-level declaration head (`macro fn`, `extern fn`).
- types.ml: `Dynamic`/`untyped` as a TYPE name keep their WO-E225 site but
  carry the doctrine message.
- corpus: compile-fail/{reject-inheritance,reject-cast,reject-dynamic}.
- catalog WO-E105 row; plan 8 Task 8 reject half ticked (#if still open);
  board updated.

Verified: woc-test 540/0 + test_diag 14/0; oop-e2e 86/0; log-watcher 7/0;
employee 8/0; legit identifiers (`extended`) untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 17:57:36 +02:00
8f6ff1e865 feat(compiler): WO-E205 structural interface satisfaction + call-arg checks
The hybrid-boundary inversion is closed: a statically provable interface
violation now fails at COMPILE time instead of reaching wovm as an ICALL
that traps WO_T_BOUNDS at runtime.

- types.ml class_satisfies: the same rule emit.ml's `satisfies` builds
  vtable rows from (instance method with matching name + parameter count
  for every interface method; `static fn` never satisfies) — one rule, two
  consumers, so the check and the vtable can never disagree.
- check_iface_boundary fires wherever a confidently class-typed value flows
  into an interface-typed slot: call arguments against the callee's declared
  parameters (free fns, methods off confident receivers, interface-method
  sigs, statics — resolved exactly as confident_typ resolves returns),
  annotated `let`s, and `return`s. Silent when underivable.
- The same per-argument pass extends the ?T boundary to CALL ARGUMENTS
  (the previous slice covered stores/returns/operands): nil into a
  non-nullable parameter is WO-E212, an unnarrowed ?T argument is WO-E211.
- tests/corpus/trap/unsatisfied-interface -> compile-fail/ with
  fixture.code WO-E205, per the fixture's own standing instruction; its
  header comment rewritten to the wired reality.
- The new arg checks caught a real mistyped signature in the sample:
  log-watcher's rpc_error/rpc_result/call_tool declared `id: json.Value`
  while every caller legitimately passes nil (JSON-RPC id-absent) — now
  `?json.Value`; dispatch/call_tool/cron-row sites moved to the
  bind-then-narrow idiom (including an `or`-guard narrowing:
  `if spath == nil or spat == nil { return }`).
- Catalog: E205 gains its main-table row; the "owed gap" section is
  rewritten as closed. Board known-gap struck through.

Verified: woc-test 540/0 + test_diag 14/0; oop-e2e 83/0 (fixture now
compile-fail, satisfying-class negative probe compiles clean); oop-accept
ALL MET; log-watcher 7/0; employee 8/0; gc-cycle clean.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 17:53:40 +02:00
934780c54c feat(compiler): ?T forced handling — WO-E211/E212/E213 + narrowing (iter 5)
The type system now keeps its nullability promise: a `?T` value cannot be
used, stored, or dereferenced as a plain `T` without narrowing. The canonical
evidence probe (return b.v where v: ?Int, fn -> Int) that compiled clean for
months now fails with WO-E211.

- WO-E211 (un-narrowed use): arithmetic and </<=/>/>= operands, and/or
  operands (?Bool), interpolation segments, for-iterables, and returns whose
  declared type is not nullable.
- WO-E212 (boundary): nil or ?T stored into a non-nullable slot — annotated
  let, assignment to a confidently-typed local (cenv, never the placeholder
  env — a placeholder target must stay silent) or a resolvable class field.
- WO-E213 (deref): field/index access through a possibly-nil base.
- Narrowing (locals only — a field place can be re-assigned between check
  and use, so chains bind to a local first): `if x != nil { }` narrows the
  branch; a DIVERGING then-branch (`if x == nil { return }`) narrows after
  the if; `x != nil and x.n > 3` narrows and/or right operands
  (short-circuit); `while x != nil` narrows the body. The narrow is
  un-applied when an else-less then-env leaks out un-diverged (the existing
  env-leak convention must not leak the narrow).
- No false positives by construction: env/cenv types are declared or
  confidently inferred; the placeholder fallbacks are plain scalars, never
  ?T. The whole golden suite passed untouched (540/0).
- Samples updated to the bind-then-narrow idiom (log-watcher config decode +
  supervisor lock/next_fire, gc-cycle ring print) — 22 genuine unnarrowed-nil
  sites; employee needed zero changes. All acceptances green.
- Corpus: compile-fail/{nullable-unnarrowed-use,nullable-nil-into-plain,
  nullable-deref-unchecked} + run/nullable-narrowing (all four forms) — 83/0.
- Catalog: E211/E212/E213 move from "Reserved, not yet emitted" to the main
  table; nullable-types-implementation.md status flipped to ENFORCED
  (historical record kept); plan 8 Task 6 ticked (boxed scalar cells
  superseded by WO_NIL_SCALAR); board updated.

Verified: woc-test 540/0 + test_diag 14/0; oop-e2e 83/0; oop-accept ALL MET;
log-watcher 7/0; employee 8/0; gc-cycle ring prints + reclaims.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 17:47:13 +02:00
28485a271c docs: iteration 7b migration — amend the normative docs (Phase 4)
The spec's §8 migration table, applied:

- 00-principles.md P3: "@gc is a per-class opt-in, reference-counted" ->
  GC-ness is inferred; incremental per-shard mark-sweep in budgeted slices;
  still no global pause by construction.
- OOP spec: decision-table GC row -> inferred (hybrid rule named); §3 rule 5
  -> traced classes alias freely, which classes is inferred; §4 memory model
  -> the RC + Bacon-Rajan paragraph replaced by tracing (snapshot roots,
  Yuasa barrier, born-black, budgeted slices); header rc comment -> union'd
  sweep link; mixing rule restated for tracing.
- 00-wob-format.md: header says version 4; opcodes 27-28 -> reserved (loader
  rejects); the owned-temporary rule's @gc exclusion restated for tracing.
- 08-builtin-surface.md: the push RC_INC special case and the set(m,k,v)
  retention gap DELETED — neither exists without RC; the corpus cycle is
  collected by tracing.
- story 07b: status -> LANDED 2026-08-18 (with the historical note kept);
  board: 7b row ✅ (supersedes iteration 2's RC memory model), pending row
  removed.
- gc-cycle README: Phase 3 flipped to landed (the ring runs, is reclaimed,
  ASan-clean; the ?Node RC_DEC-on-nil trap no longer exists); the barrier
  prose corrected to the as-built design (snapshot-at-beginning + deletion
  barrier + born-black, not per-slice root re-reads).
- plan 2026-08-18: all checkboxes ticked + a completion banner recording the
  four deviations from the plan as written.

(Error catalog was already amended with the keyword-removal commit: WO-E104
added, WO-W201 retired.)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-19 17:11:35 +02:00
3f842f854b feat(compiler): remove @gc from the language — GC-ness is fully inferred (7b)
`@gc` is no longer part of the language: a developer never writes or mentions
it. GC-ness is decided entirely by inference (structural cycles + demand
promotion), which the earlier 7b commits made complete and precise.

- parser: `@gc` on a class is now WO-E104 ("GC-ness is inferred; run
  `woc --dump-gc`. Remove it."). `is_gc` stays false; the class classifies by
  inference. No `.wo` in the repo carries `@gc` anymore.
- types.ml: retired the WO-W201 machinery (suggest_gc_annotation,
  has_recursive_structure(_type), has_unique_field, gc_suggestion_code) — it
  suggested `@gc`, now obsolete since inference traces exactly those classes.
- runner.ml: deleted the 8 WO-W201 gc-suggestion test blocks; the @gc-exemption
  test's `Cache` is made self-referential so inference classifies it gc without
  an annotation.
- fixtures: dropped `@gc` from rc.wo (Cache demand-promotes via its escape),
  elision.wo (Cache given a self-ref to stay structurally gc for the rc-elision
  dump), pricing-demo.wo (PriceCache doesn't escape -> now owned), and the
  abandoned-cycle/budget-steps corpus (Node is structurally gc). rc.wo keeps a
  placeholder comment line so its line-indexed rc assertions hold. Goldens
  re-blessed.
- docs: error catalog gains WO-E104 and marks WO-W201 retired; gc-cycle README
  records the keyword removal.

Verified: woc-test 553/0 (was 566 minus the 13 retired WO-W201 checks),
test_diag 14/0, oop-e2e 79/0, employee 8/0, log-watcher 7/0. `git grep '@gc'`
finds only comments — success criterion 1 met.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 19:19:09 +02:00
6d589824e7 feat(compiler): demand promotion — GC-ness fully inferred (7b Phase 2b core)
Structural inference (2a) covers cyclic classes; this adds the DEMAND half for
the acyclic-but-aliased case, so @gc is now redundant everywhere.

- owner.ml: a `promote` sink on ctx. In collect mode, a class value that would
  raise WO-E304 (escape) records its class instead of erroring — because a
  class that MUST escape cannot be owned (second-class borrows can't be stored
  or returned), so it must be traced. `class_of_ty` extracts the class from the
  escaping local's type. analyze/analyze_fn take ?promote.
- main.ml typecheck_all: after structural injection, a fixpoint runs ownership
  in collect mode over every file, unioning promotions into syms.traced (and
  every module table), before owner/emit see it. Terminates (promotions only
  grow, bounded by class count).
- gcinfer.render_final: --dump-gc now reads the authoritative is_gc_class
  (structural + demand + the remaining @gc bridge), with the reason.

Effect: a class that escapes is inferred `gc` with no annotation — e.g. `Cache`
(rc.wo sans @gc) shows `gc (alias escape (demand))`; `Box` returned out of
`leak` is promoted and the program is valid. No false positives: employee's
Department/Employee stay owned; the 566 goldens + 14 test_diag unchanged.

Corpus: compile-fail/borrow-escape-return reclassified to run/ (prints 1) —
returning a borrowed class is now legal under demand promotion; the fixture
encoded pre-7b behavior.

Verified: woc-test 566/0 + test_diag 14/0; oop-e2e 79/0; employee 8/0;
log-watcher 7/0.

NOT in this slice: removing the `@gc` KEYWORD (parser rejection + rewriting the
RC/@gc golden + test_diag assertions + moving the inference injection into the
library so unit tests see it) — coupled to Phase 3, which deletes the RC
machinery those tests cover. The ring still needs Phase 3 to RUN (nullable
`?Node` gcref path).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 18:53:08 +02:00
484a3259b5 feat(compiler): is_gc_class is inference-first (7b Phase 2a)
GC-ness now comes from the inference pass, not only the annotation. A class is
traced if the structural SCC put it in `syms.traced`, OR (temporary bridge
until demand-promotion lands) it still carries `@gc`.

- Types.symbols gains a `traced : StringSet.t`; is_gc_class reads it (union'd
  with the surviving @gc annotation). All symbols literals + both merges carry
  the field.
- typecheck_all injects the classification once (Gcinfer.classify -> traced)
  into the merged table AND every module table, before typecheck/owner/emit.
- emit.ml routes the class gc-flag and the union/drop decision through
  is_gc_class instead of the raw `.is_gc`, so structurally-inferred gc classes
  get the runtime flag. Field-kind derivation already routed through is_gc_class.
- gcinfer.traced_names exposes the traced set for injection.

Effect: docs/examples/gc-cycle now COMPILES with no annotation (the WO-E301
use-after-move at the ring-closing store is gone) — traced classes alias
freely. Bytecode is byte-identical to writing `@gc class Node`.

Verified: woc-test 566/0 (goldens unchanged — every current @gc class stays gc
via the annotation branch, and no golden has a structural-gc-non-annotated
class); oop-e2e 79/0 (gc corpus green).

Not in this slice: demand-promotion (the acyclic-aliased PriceCache case still
needs the @gc bridge) and @gc-in-source-as-error (Phase 2b); the ring RUNNING
(the RC runtime doesn't implement nullable-gcref `?Node` fields — Phase 3).
WO-W201 still fires on gc-cycle (retired in Phase 4).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 16:51:06 +02:00
335a797479 docs: implementation plan for inferred GC + mark-sweep (iteration 7b)
Phased plan argued from the 2026-08-11 spec: Phase 1 additive inference +
--dump-gc (golden-neutral), Phase 2 demand promotion + rewire field kinds to
the inferred set (+ @gc-in-source error), Phase 3 the runtime collector swap
(header rewrite, traced list, tri-color mark + Yuasa barrier, retire RC, .wob
bump), Phase 4 doc migration. Each phase has file targets + verify commands.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 16:33:19 +02:00
2a18860260 docs: gc-cycle sample — inferred GC + mark-sweep address/pointer flow
Design-first deliverable for iteration 7b (no runtime/compiler code yet).
docs/examples/gc-cycle explains, by example, how pointers flow through the heap
and the collector's mark-sweep logic:

- types.wo: Node (self-referential ?Node -> inferred `gc`/traced) vs Segment
  (acyclic -> `owned`, deterministically dropped)
- main.wo: ring_demo builds a->b->c->a and abandons it; owned_demo shows the
  drop path with no collector
- README.md: the model (ownership frees the 99%, tracing only the cyclic/
  aliased residue, inference decides), the 16-byte header rewrite (retire
  rc+borrow -> 8-byte sweep-list link, colors in flag bits), where a traced
  pointer lives (root via pc gc-mask / GCREF field / container), and the
  tri-color incremental algorithm with the Yuasa deletion barrier. Two mermaid
  step diagrams (heap+roots, collector cycle) + the owned contrast.

Grounded in the approved spec (2026-08-11-inferred-gc-mark-sweep-design.md) and
the real runtime structures (obj.h/wob.h: wo_hdr, WO_K_GCREF, arena, wo_obj_size).

Run status: honest — the sample does NOT build today; woc reports WO-E301
(use-after-move at the ring-closing store), which is exactly the aliasing that
"traced classes alias freely" unblocks under 7b. README records this.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 14:14:50 +02:00
1a3df8cfac update docs 2026-08-18 02:04:58 +02:00
41422d316b chore: remove the old Rust runtime track from master
Master now reflects only the current woc/wovm project. The Rust `wo` runtime
was the prior, abandoned architecture; it is fully independent of the woc/wovm
stack (dune + make, no Cargo dependency), so it lifts out cleanly. Recoverable
via git history.

Removed:
- crates/ (29 files) + Cargo.toml + Cargo.lock — the Rust runtime workspace
- prototypes/ — wo-rt-c (a stale duplicate of runtime/) + wo-db C++ ref
- justfile: the rt-c-demo / rt-c-bench recipes (drove prototypes/wo-rt-c)
- docs/plan/05..16 (11 Rust engineering plans) + docs/plan/done/ (4 Rust
  Stage-2 done plans)
- docs/runtime/ (11): the old runtime overview + 7-phase DB design series +
  async/fibers/gc/surreal concept essays
- docs/cm.md — legacy scratch note

Kept: compiler/, runtime/ (C VM), database/, the current-track docs
(stories, superpowers, plan/{oop-vm,compiler,exploration}, examples), the
discarded/learnings registers, and the syscall/postgres/assembly/c-runtime
studies. Follow-up commits fix the status board, project-structure doc, and
any dangling links to the removed docs.

Verified: woc + wovm still build; woc/wovm --version green.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 01:50:20 +02:00
7648bc79ce docs: scrub remaining plan-7/UI mentions from project-structure
The ##ui/.htmlx/LIVE track (plan 7) was removed as stale, so drop its dangling
references in 08-project-structure.md: the `ws, sub, htmlx, assets (plan 7)`
runtime/src line, the `06-ui-live` oop-vm contract entry, the `plan 7
UI/.htmlx/LIVE` server-track step, and `ui` in the build rule-of-thumb.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 01:43:59 +02:00
2f0e242e0c docs: scrub the phantom client/ dir from project-structure
`client/` (wo-live.js live-patch runtime) never existed and was tied to the
removed plan 7 (##ui/.htmlx UI track). Dropped its tree entry, its proof-layer
section header + bullet, and its lifecycle-table mention (plans 4–7 -> 4–6,
http/ui -> http).

Left as-is: the Rust-track phase prose still names a `ui` crate scaffold and
plan-7 sequencing — that describes the maintained (non-advancing) Rust
roadmap, not a removed file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 01:40:04 +02:00
48044d6cf7 chore: remove stale old-runtime cruft and v1-blog frontend assets
Cleanup off master. None of this is referenced by the current woc/wovm
toolchain or the maintained Rust crates; it belongs to abandoned/scratch
state.

Removed:
- .planboard/ + .planboard.json — planboard task-tracking state for the
  haxe-parity plan (tooling scratch)
- prompt.md — a stray one-line note about the old runtime's 13c task
- infra/ (deploy.sh, setup.sh, sync.sh) — deploy scripts for the old Rust
  runtime (cargo build -p wo-rt, scp to writeonce.de, nginx/SSL/systemd)
- static/ + templates/ — the v1 blog's .htmlx frontend assets (svg/css +
  about/article/home/layout templates); the UI track that used them was
  already removed as stale
- content/ data/ wo-data/ — empty untracked v1 runtime dirs

Updated docs/08-project-structure.md: dropped the "v1 blog operating assets"
tree line and section. (cm.md keeps a historical mention of infra/sync.sh as
a legacy note — left as-is.)

Nothing kept references the removed paths (verified tree-wide; crates/rt hits
were `'static` lifetimes, not file reads).

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 01:33:19 +02:00
464b61ba65 fix(log-watcher): portable wo.toml — no pinned runtime path
The manifest pinned `[build] runtime = "../../../runtime/wovm"`, a repo-only
relative path that overrides woc's runtime resolution — so `woc <copied-dir>`
failed off-repo (e.g. an installed toolchain on a test server) with "runtime
binary not found".

- Drop the [build] section: the project no longer hardcodes a machine path, so
  an installed `woc` self-locates `wovm` beside its own binary.
- The module justfile's `build` recipe now sets WO_RUNTIME=<repo>/runtime/wovm
  so the in-repo build still uses the freshly built VM.
- Acceptance is unaffected (it compiles via `woc --emit` + an explicit $WOVM,
  never the manifest [build] key).

Verified: just log-watcher::build OK; just log-watcher 7/0; and building a
copied tree with the installed-layout woc from an unrelated cwd self-locates
the sibling wovm and produces a runnable binary.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 01:19:20 +02:00
531b0283c6 docs: remove stale old-runtime docs; abandon the ##ui frontend track
Analyzed the full 131-file docs tree (4 parallel classifiers) against the
shipped woc/wovm toolchain. Removed 21 stale docs, kept all intentional
history (Rust-track plans/done, the runtime/database design series cited by
current specs, syscall/postgres/assembly/c-runtime studies, discarded/
learnings). Deleted:

- old-runtime "front door": writeonce-pl.md, runtime/wo-language.md
  (pitched the Rust wo runtime -- REST/LiveView/SQL+Cypher -- as the current
  language; contradicted the new README)
- v1 design set: 02-recovery, 03-data, 04-ui, 05-datalayer,
  06-markdown-render, 07-ssl; runtime/database/05-go-sdk
- future-scope/ai-agents-content-management (unfinished old-runtime CMS)
- the ##ui/.htmlx LiveView frontend track (product decision to abandon):
  9 plan/exploration/ui/*, plan/14-mvc-ui-implementation,
  superpowers/plans/2026-08-01-ui-htmlx-live; 13d pricing-UI board row

Tree left link-clean: 46 dead links to the removed docs neutralized to plain
text or deleted as pure see-also bullets across 20 kept docs; whole-tree
link-resolving scan reports zero links to any deleted file. Removal recorded
in discarded.md; board Frontend section + project-structure tree updated.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 20:06:36 +02:00
db45bfb4da docs: replace stale root README with the woc/wovm front door
- README.md now IS the getting-started page (moved from
  docs/writeonce.md; single source, no duplication): current
  woc -> .wob -> wovm toolchain, system requirements, build, hello-
  world, language + stdlib, embedded database, samples, roadmap
- deletes the old README's Rust `wo` runtime pitch (cargo run, axum
  REST, Postgres mirror, blog/ecommerce) — that runtime is not
  advancing and no longer the project's front door
- content unchanged from the verified doc (hello-world + switch
  compiled live before the prior commit)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 19:17:11 +02:00
a0d0b97b8f docs: public getting-started page for writeonce.de
- docs/writeonce.md — external-developer front door for the current
  woc -> .wob -> wovm toolchain (NOT the stale root README's old Rust
  wo runtime): what writeonce is, system requirements, how to build
  the toolchain, hello-world + the two build paths, language surface,
  stdlib, the embedded database, project/manifest layout, samples
- shipped-only by decision: every feature described compiles and runs
  today; hello-world + switch snippet verified live before commit;
  employee/log-watcher cited as the working acceptance samples
- unshipped roadmap (aggregates, HTTP service, concurrency/fibers,
  cross-program attach + keypair auth, blue-green, @derive) kept in a
  clearly-separated Roadmap section, plus named current limits (net
  TCP-only, proc.run no timeout, no stdin/stdout, no FFI)
- note: root README.md is stale (documents the older Rust axum/REST
  runtime, no mention of woc/wovm); left untouched, flagged for the
  developer

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-17 19:14:05 +02:00
b6151333c3 docs: iteration 14 — skillhost as a host-shaped driving workload
- frames a writeonce port of ~/projects/skillhost (C++ MCP host that
  links libllama in-process, discovers filesystem skills, runs their
  scripts confined) as the sample that drives host capabilities into
  the open — the way log-watcher drove the systems stdlib
- names each gap as a candidate iteration (surveyed 2026-08-16 vs the
  running compiler + skillhost source):
    - Blocker A: in-process native-lib FFI (no FFI today) — fork:
      FFI-as-language vs out-of-process model driver over proc/net+json
      (llama-server, needs nothing new); leaning out-of-process
    - Blocker B: stdio transport — no stdin/stdout builtins; port uses
      a TCP socket meanwhile; io.stdin_read/stdout_write a candidate
    - Blocker C: bounded/killable subprocess — proc.run has no timeout/
      signal/process-group kill; smallest + most broadly useful, do 1st
    - partials: recursive fs walk, exec-bit check, symlink-resolving
      confinement (realpath) — one small fs-metadata iteration
- records what is already expressible (catalog via @table/9g skill-
  catalog, discovery, frontmatter text-parse, config, single-thread
  serve, context-gate arithmetic — no VRAM query needed)
- out of scope: in-process libllama/CUDA, VRAM introspection, exact
  sampler chain / per-turn memory clear
- roadmap + board rows added

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 19:53:06 +02:00
86e1fd4a4e docs: iteration 9g story -- query grammar from real embedded-DB corpora
- method: an embedded-SQL app is a grammar corpus; catalogue what it
  actually uses and add only that, translating its statements 1:1 as
  the acceptance (the postgres/System.Linq reference pattern applied to
  a whole application)
- corpus #1 = ~/projects/skillhost (C++ MCP host, embedded SQLite skill
  catalog): surveyed, entire SQL footprint is one file — 1 table, a
  single-row parameterized INSERT, 4 SELECTs. 3 of 5 statements already
  run on the 9b surface (insert, where name==?, order by name; PK ≈
  @unique). Exactly 2 are the real gap:
    - whole-query `count` (group-free; the degenerate aggregate, NOT
      the parked group-by)
    - correlated `not exists` subquery (skillhost's roots-of-the-tree)
- notable finding: skillhost's NOT EXISTS is naturally a `backlink`
  emptiness in writeonce (children backlink + len==0), so the corpus
  may be fully expressible once len(query) is confirmed — the iteration
  may collapse to "confirm len(query) + add exists"; forks record this
- explicitly parks everything skillhost does NOT use (join/having/
  offset/distinct/CTE/window/union/upsert/returning/json/fts/triggers)
  and the full group-by; each enters only when a corpus demands it
- acceptance: docs/examples/skill-catalog mirroring skillhost's schema
  + its 5 catalog ops as writeonce translations
- roadmap + board rows added

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 19:28:25 +02:00
0a8ca03ccd chore: untrack employee build artifact, gitignore its target/
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 19:01:33 +02:00
edd6091579 Merge branch 'query-surface' into database-engine 2026-08-16 19:01:16 +02:00
2677c1457e feat: FK restrict on delete + employee sample runs; group-by parked (9b)
- FK restrict: deleting a row a non-nullable `ref` still points at traps
  WO_T_FK (11), catchable. The compiler now records a `ref` field's
  target class in the class-table field_class metadata; the engine
  (wo_row_has_referrers) scans referencing scalar columns before a
  delete. Correctness-first full scan; the backlink-index optimization
  is recorded for later
- docs/examples/employee now COMPILES AND RUNS all six modes against a
  WAL-durable database: seed (+@unique trap across restart), report
  (per-dept aggregates + payroll), staff (unique probe + backlink +
  ref nav), raise (update-through-row), drop (FK restrict), and
  persistence via replay
- group-by SYNTAX parked to a future iteration (user decision): the
  report mode is hand-rolled from the shipped primitives meanwhile
  (same numbers). "table relations and FK" is complete
- scripts/employee-accept.sh (8 checks) + a `just employee` module;
  manifest parser tolerates iteration 9c's [share]/[[share.clients]]
  sections so `woc .` builds the sample on this branch
- fixtures trap/db-fk-restrict (code 11) + run/db-fk-restrict-catch;
  oop-e2e 79/0, woc-test 566/0, 15 runtime suites, log-watcher 7/0,
  employee-accept 8/0
- 9b story + status board updated

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 18:37:23 +02:00
ad8cfb456e docs: iteration 13 story -- compile-time metaprogramming (derive)
- captures the toCSV/reflection thread: principle 13 forbids runtime
  reflection, so a generic serializer can't be a user-written function;
  Rust answers with derive macros (compile-time codegen), and
  json.encode is already a single hand-built instance of exactly that
- iteration generalizes json.encode's mechanism into a reusable derive
  facility: @derive(Json/Csv/Eq/Hash/Show) -> the compiler generates
  per-type routines from the class-table metadata it already emits,
  monomorphic, no runtime type tag, no dynamic dispatch
- closes the query-result-serialization gap
  (csv.encode(from e in Employee ... select e)) that has no expression
  today; acceptance requires json.encode retrofitted onto the framework
  with byte-identical output, and disassembly proving no reflection
- four forks: request surface (lean @derive annotation), invocation
  (lean compiler-recognized encode builtins, no UFCS/methods), Eq/Hash
  sharing the engine's key comparison, static applicability checking
- out of scope: full trait/typeclass system, user proc-macros, general
  generics, cross-channel derive -- a CLOSED compiler-known derivable
  set, the pragmatic 80% without the type-system weight
- numbered 13 to echo the principle it lives inside; roadmap + board
  rows added

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-16 04:57:00 +02:00
71d3985e81 docs: performance arc as story iterations (9e measure, 9f io_uring)
- 9e durability/throughput/scale: the measurement backbone -- run the
  employee program, restart to prove persistence, benchmark read/write
  through compiled .wo, ~1M-row mixed load with throughput floor + p99
  ceiling + flat RSS; the gate every optimization signs (before/after
  delta required, no measured delta = not accepted)
- 9f io_uring group-commit: replace fsync-per-commit with batched
  io_uring durability overlapped on shard threads; same ack-after-
  durable contract, crash battery unchanged, automatic fsync fallback
  on kernels without it; deliberately LAST (needs 8's threads to
  overlap and 9e's baseline to beat)
- wired the existing levers into the arc: iteration 8 (thread-per-core)
  = "optimize multithreading", 7b (mark-sweep) = "implement GC" --
  each now gated by re-running 9e and recording the delta
- explicit sequence recorded in 9e: 9b lands -> 9e baseline -> 7b
  re-bench -> 8 re-bench -> 9f re-bench
- roadmap + board rows for 9e/9f; four forks each for the specs
  (load generator, absolute vs relative budgets, what "1M" means,
  durable vs RAM headline; ring model, liburing vs raw, batch
  boundary, fallback testing)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 22:12:48 +02:00
77b3b06e77 docs: board — 9c/9d milestones landed on their branches
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 13:31:34 +02:00
dafc7c5cf7 docs: iteration 9 status — engine complete for single-shard; Task 6 incremental
- Task 6 note: db fixtures live in existing corpus kinds; crash battery
  proven at unit level; select fixtures wait on 9b's read surface
- board row updated

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 13:02:49 +02:00
44c77ff5bf feat: update-point + delete engine half (iteration 9, Task 5 engine)
- wo_row_update_field: encode new value, unique re-check against a
  shadow BEFORE any mutation (violating update leaves the row
  untouched, DB_ERR_UNIQUE), index entries moved old-hash -> new-hash,
  old engine value freed; proven by test_table (unique refusal keeps
  the row, released key becomes insertable)
- WAL UPDATE record: full-row re-log, replay = replace (remove +
  re-create same id); prefix/suffix delta recorded as later
  optimization; test_wal replays insert+update to the updated state
- builtins 62 DB_UPDATE_FIELD (cid,id,field,value) and 63 DB_DELETE
  (cid,id), commit-before-ack like insert, WO_T_UNIQUE/WO_T_DB/WO_T_IO
  mapping; dispatch range 61..63; loader arities; runner mirror
- plan Task 5 marked superseded-in-part with the recorded deviation:
  the language surface (reads, queries, row views, delete statement)
  is 9b's, where the comprehension design put it -- no interim brace-
  select grammar to retire later
- gates: test_table 839/0, test_wal 102/0, 15 suites, oop-e2e 73/0

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 13:01:05 +02:00
2e1041daed docs: employee-list sample -- program B's manifest pair (9c/9d target)
- docs/examples/employee-list: attaches to the running employee
  program; modes list / report (byte-identical to A's own) /
  staff <dept> / probe-write (registered read-only, insert must trap
  access-denied, exit 4, A unchanged)
- the manifests ARE the design, written as a pair: A's [share] gains
  listen = unix socket beside WO_DATA plus [[share.clients]] naming
  B's public-key fingerprint with rights = "read"; B's
  [connect.employee] carries A's ipc string, A's PINNED fingerprint,
  and project = ../employee for compile-time shapes -- the connect
  section's name is the code's namespace (employee.Employee)
- fingerprints are PASTE-HERE placeholders by design: keys generate
  into WO_DATA at first boot (9d), tomls carry fingerprints only,
  printed by --identity
- sample-first: compiles after 9/9b/9c/9d; README maps each mode to
  the acceptance line it exists for; 9c/9d stories now name this
  sample as their workload

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 12:34:19 +02:00
fe56ba0944 docs: iteration 9d story -- keypair authentication for attach
- promotes 9c's identity fork to its own iteration: program identity
  is a keypair (first-boot generated into WO_DATA, 0600, printable
  fingerprint); A's [share] grants name PUBLIC KEYS, B pins A's key
  in [connect.a]; mutual challenge-response, fresh nonces, transcript-
  hash signing (protocol tag + fingerprints + nonces + channel)
- acceptance criteria: registered-key attach carries 9c rights
  unchanged; unregistered key refused pre-statement with fingerprint
  logged; same-uid-wrong-key refused (uid SUPERSEDED, not
  supplemented); impostor on A's socket path aborted by B's pinned-key
  check; handshake replay refused; rotation = manifest change
- four forks recorded: crypto provenance (lean: vendored compact
  Ed25519 as the one sanctioned vendored component), keygen home
  (lean: first-boot into WO_DATA), signed-transcript layout, uid
  survival (lean: keys only, peer-cred demoted to log enrichment)
- out of scope: transport encryption, CA machinery, key escrow,
  root-attacker protection
- plan folds into 9c's when specced (neither ships alone); 9c fork 3
  marked superseded-as-end-state; roadmap + board rows added

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 12:26:49 +02:00
b92357b6b9 docs: iteration 9c story -- cross-program tables (attach over IPC)
- program B attaches to running program A's persistent database via an
  IPC string in B's wo.toml [connect.<name>]; A registers clients by
  name with read / read+write rights in its [share] manifest section;
  unregistered = refused at connect, under-privileged = catchable trap
- doctrine preserved: A stays the single writer -- B's statements
  execute inside A through the same choke-point row API, B never
  touches A's WAL or slabs; typed statements checked by B's compiler
  against A's table shapes, schema handshake at attach
- four forks recorded for the spec: channel carrier (lean: unix
  socket + SO_PEERCRED), how B's compiler learns A's shapes (lean:
  project reference + live handshake), grant granularity (lean:
  whole-db rights, name+uid identity), blocking semantics (lean:
  blocking round-trip, stop-flag rule applies)
- acceptance sketch: employee sample as A, a thin employee-report
  client as B (read-only GroupBy over the wire) + audit-log writer
  exercising the rights matrix
- slots after 9b (shares its typed surface), before 10 (HTTP is the
  external face; this is the writeonce-native one); prior art:
  04-client-api.md wire protocol + the WAL's value encoding
- roadmap + status board rows added

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 12:13:38 +02:00
30ef8d8533 feat: insert executes (iteration 9, Task 3) -- DB_STUB retires for insert
- compiler: `insert Class { ... }` is a typed Ast.Insert in statement
  AND expression position, sharing the ctor literal's field grammar;
  typechecked with the ctor's omittable rule; result = the row id (Int)
- owner pass: the engine copies at the row API, so an insert BORROWS
  its field values -- no transfer, no E304; node is trap-capable and
  carries a live-mask drop entry like DbStub did
- emit: builtin 61 window = class-id const + one slot per DECLARED
  field in declaration order; omitted defaults emitted, omitted ?scalar
  gets WO_NIL_SCALAR, other omitted optionals the zero word; fresh
  argument values reaped after (the push/set copy semantics)
- runtime: database/src/db.c executes via the choke-point row API;
  rt.db/rt.wal opaque handles on wo_rt; WO_DATA=<dir> = replay
  <dir>/shard-0.wal at boot + commit-before-ack per statement (the
  builtin's return IS the ack until iteration 8 ticks); failed commit
  un-applies the row and traps WO_T_IO; loader validates the class-id
  slot (variable window documented in wob.h + format doc)
- the promised diff: trap/pricing-set-price-db-stub is now
  run/pricing-set-price-insert printing engine-allocated ids;
  durability smoke prints 1,2 then 3,4 across two WO_DATA runs
- old "bare insert is an Ident" unit test rewritten to the new
  contract; runner's loader mirror accepts id 61; goldens re-blessed
- gates: oop-accept ALL CRITERIA MET, oop-e2e 71/0, woc-test 566/0,
  wovm-test green, log-watcher 7/0

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 11:15:06 +02:00
c3741262cb feat(database): typed WAL + boot replay (iteration 9, Task 2)
- database/src/wal.{c,h}: framed records len|crc32|payload|mark
  ("WOL1" written last -- no mark, no record), typed-row payloads
  walking the class-table kinds (nested records, containers, nil
  encodings), little-endian like the loader
- commit order verbatim from the shipped phase-D pattern: RAM apply,
  stage, ONE pwrite + ONE fdatasync for the batch, ack after -- group
  commit is everything staged riding one sync
- replay decodes straight into engine-owned values (no VM at boot)
  and re-enters rows through the choke-point row API, so Task 4's
  indexes will rebuild for free; next_id advances past replayed ids
  this shard owns (wo_row_create_raw)
- torn tail = short/CRC-fail/no-mark/zero-len: intact prefix applies,
  tear dropped whole, wo_wal_open positions AT the tear so the next
  commit overwrites it; CRC-valid-but-undecodable = corruption, loud
- wo_wal_check: offline oracle, no engine needed -- the crash
  battery's verifier
- test_wal 90/0 ASan+UBSan incl. five crash-battery rounds (fork,
  insert/commit/ack-over-pipe, SIGKILL mid-stream: zero acked-but-
  missing, zero acked-but-wrong); all runtime suites green, oop-e2e
  71/0; binding doc WAL section + CODE-LOGIC + plan Task 2 checked

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 11:01:41 +02:00
936bd14bff feat(database): class-shaped row storage (iteration 9, Task 1)
- database/src/table.{c,h}: per-shard per-class slabs (256 rows,
  malloc'd, never moved -- row addresses stable for 9b's row views),
  occupancy bitmap, LIFO slot reuse, open-addressing id hash with
  tombstones (ids never 0, never reused)
- field encoding walks the same .wob class-table kinds the VM walks:
  scalars raw (WO_NIL_SCALAR passes through), Texts copied to db_text,
  owned objects flattened recursively to db_rec, containers
  element-wise; GCREF refused at encode (the GC bulkhead, defensively)
- two one-way copy gates: insert copies VM values in, read allocates
  fresh VM values out -- no VM pointer in a slab, no slab pointer in
  the VM, proven by mutating originals after insert
- id discipline: per table per shard, S+1 step N; owner = (id-1) % N;
  N-parametric, runs at N=1 until iteration 8, tested at N=3
- choke points: wo_row_insert/wo_row_remove carry the INDEX HOOK
  sites Task 4 attaches to; nothing else mutates storage
- runtime/Makefile links database/src into every wovm + test binary
- test_table 827/0 ASan+UBSan; oop-e2e 71/0; log-watcher 7/0;
  binding doc docs/plan/oop-vm/04-db-binding.md; CODE-LOGIC.md beside
  the code; plan Task 1 checked off

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 10:54:24 +02:00
1749440351 docs: borrow/GC analysis for the database engine, into the iterations
- 9b spec gains section 6 "Ownership, borrows, and GC across the
  engine boundary": two one-way copy gates (no VM pointer enters a
  row, everything a select returns is copied out), so the collector
  never traces engine memory and the engine never touches refcounts
- row views are borrows WITHOUT a runtime net: rows share the VM's
  field encoding but not its header, so no borrow word backs them --
  the compile-time escape rule is load-bearing alone
- cursor stability settled: scans materialize their id list before
  the body, row updates through the view stay legal (raise mode
  updates an indexed column mid-scan and is the proving fixture),
  insert/delete on a table with an open cursor is a new WO-E5xx
- GC-pause interaction recorded: collector runs between statements,
  a long scan delays slices -- accepted, documented
- iteration-7b ordering constraint: GC inference must classify before
  table-field validation, diagnostic names the inference reason --
  noted in 7b story, iteration-9 plan constraints, 9b plan tasks
- stories 09/09b Info sections point at the analysis; 9b plan Tasks
  3/5 carry the enforceable checkboxes (ASan boundary assertion)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 09:57:53 +02:00
ebcbf460df docs: employee sample (target workload) + engine moves to database/
- docs/examples/employee: Department/Employee @table classes with
  @unique, [dept] and [dept, salary] indexes, ref/backlink pair;
  modes seed/report/staff/raise/drop per the 9b spec section 6 —
  written AHEAD of the features (sample-first, like log-watcher);
  README states it does not compile on today's toolchain and links
  the plans that compile toward it
- report mode is the GroupBy showcase: group-and-reduce projection
  {headcount, avg, min, max} ordered by avg desc, whole-query sum
  for payroll; staff proves both navigation directions + index probe;
  drop proves delete restrict (trap asserted)
- engine directory decision (user, 2026-08-15): database/ is its own
  top-level dir, statically linked into wovm — file structures updated
  in the iteration-9 plan and the 9b plan
- gap found by writing the sample: iteration 9's subset lacks a
  `delete` statement and restrict needs one — added to 9b plan Task 3
- 9b plan Task 6 notes the sample is pre-authored and authoritative

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 09:49:54 +02:00
5943bf6890 docs: iteration 9b spec + plan — @table, relations, query (employee)
- spec settles 9b's three forks: SQL/Cypher layer superseded as the
  program surface (design history + wo-db engine-semantics reference);
  comprehension syntax desugared at compile time (no function values);
  System.Linq = operator vocabulary + edge cases, PostgreSQL = execution
  + integrity vocabulary (both references surveyed 2026-08-15)
- aggregate semantics normative: count/sum total 0 on empty, avg/min/max
  are ?T with nil (empty is data, not a fault); nil skipped; sum wraps
  like language arithmetic; GroupBy lowers as group-and-reduce
  (AggregateBy shape), transition/finalize ABI from nodeAgg
- relations: ref = FK with direct-index-probe check (nil passes,
  unchanged-key skips), backlink = secondary-index scan, delete is
  restrict-only; nil never joins, nil is a legal group key
- lowering: the compiler is the planner — queries become bytecode loops
  over cursor/group builtins, longest-prefix index selection, no plan
  tree, no SQL text in the image (disassembly-provable)
- plan: 6 tasks gated by a new docs/examples/employee sample
  (Department/Employee, @unique, composite index, ref/backlink,
  GroupBy report mode) with its own acceptance script + crash step;
  blocked on iteration 9's engine plan
- story 09b + status board updated; 02-wo-language.md carries the
  supersession note

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 09:10:08 +02:00
5e27b2f685 chore: keep only the log-watcher example (pre-merge cleanup)
- docs/examples/{agent-loop,blog,ecommerce,hello,mcp-think,pricing}
  removed; every deleted tree is preserved on branch
  cleanup/non-logwatcher-examples (snapshot of this branch pre-delete)
- justfile: hello/pricing/pricing-demo/pricing-pg-demo/hello-demo
  recipes removed with the examples they served (Rust-runtime demos);
  rt-c-* prototype recipes and every gate recipe stay
- crates/rt parser test article_debug: the blog fixture it read from
  disk now lives inline, same shape, so cargo test needs no example
- gates after cleanup: cargo test -p rt 69/0, oop-e2e 71/0, woc-test
  green, log-watcher 7/0

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 09:06:26 +02:00
c4d2a588ff chore: move the sample's recipes beside it (just module)
- docs/examples/log-watcher/justfile carries build/accept/soak; the
  root mounts it with `mod log-watcher`, so `just log-watcher` still
  runs the acceptance (default recipe) and every doc reference stays
  valid; `just log-watcher::build` / `::soak 60` reach the rest, and
  plain `just build` works from inside the directory
- ROOT is source_directory()-based: inside a `mod`,
  justfile_directory() names the ROOT justfile's directory and every
  path would miss

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-15 01:35:56 +02:00