# log-watcher gap closure — spec amendments + plan reconciliation **Date:** 2026-08-10 **Status:** approved design, pre-implementation **Scope:** amending the systems-track spec with four surface gaps the log-watcher sample revealed, recording three scope cuts, correcting two false status claims, and retiring a parallel roadmap **Amends:** [`2026-08-01-systems-track-design.md`](2026-08-01-systems-track-design.md) (Part 1 verdict table, Part 3 stdlib table) **Supersedes:** `docs/00-code-review.md` (extracted, then reduced to a stub) **Companion specs:** [`2026-08-01-oop-compiler-vm-design.md`](2026-08-01-oop-compiler-vm-design.md) **Affected plans:** [plan 8](../../plan/compiler/2026-08-01-haxe-parity-language.md), [plan 9](../plans/2026-08-01-program-mode-stdlib.md), [plan 10](../plans/2026-08-01-log-watcher-sample.md) ## Motivation `docs/00-code-review.md` was written as a gap analysis: what must exist before the log-watcher sample compiles. Verified against the code, most of its "missing" rows are correct — the front end genuinely cannot lex or parse roughly 200 constructs the sample uses. But it also carried three false claims, omitted the single largest piece of work, and proposed a Phase 1–4 roadmap that competes with the approved story iterations. Its real contribution is four surface gaps that **no approved spec ever named**. Those are the substance of this amendment. The competing roadmap is retired; the false claims are corrected at their sources. Measured baseline, `woc docs/examples/log-watcher` over 7 files: **307 diagnostics** — 167 `WO-E101` (parse) + 140 `WO-E207` (unknown type). Iteration 7 closes when that number is zero. ## Decisions locked during brainstorming | Question | Decision | | --- | --- | | Status of `docs/00-code-review.md` | **Scratch input.** Findings extracted here; the file becomes a stub pointing at `docs/00-status.md`. Its Phase 1–4 roadmap is retired — story iterations 4→5→6→7 remain the only sequence. | | Shape of the text/collection builtins | **Bare globals, no import.** `len`, `push`, `split_ws` … are always in scope, like the existing `print`/`now`/`count`/`latest`. Capability modules (`fs`, `proc`, `net`, `time`, `json`, `env`) stay `use`-imported and qualified. | | `throw` | **Cut** from the critical path — 0 uses in the sample. | | `time.mono` | **Cut** — 0 uses. | | `is` | **Cut** — 0 uses. Empties plan 8 Task 7, whose `abstract` half was rejected 2026-08-10, so the task is deleted rather than deferred. | | `#if` build flags | **Kept** in plan 8 Task 9 despite 0 uses. | | Emitter sequencing | **Unchanged: iteration 4 before iteration 5.** Plan 8's tasks state that features "lower onto existing opcodes" and name exactly three fenced VM changes — wording that presupposes an emitter. Growing the surface first would force a much larger emitter later. | ## 1. Amendment — core builtins (new Part 3 section) The sample calls **22 unqualified builtin names across ~176 sites**, none of them in any spec: `len` ×55, `push` ×17, `byte_at` ×11, `starts_with` ×9, `index_of` ×8, `has` ×7, `split` ×6, `split_ws` ×6, `join` ×5, `parse_int` ×5, `trim` ×4, `slice` ×4, `substr` ×4, `pop` ×3, `ends_with` ×2, `last_index_of` ×2, `to_lower` ×2, `sort` ×2, `char_of` ×1, `shift` ×1, `remove` ×1, `reverse` ×1. Part 3 gains a **core builtins** section, distinct from the capability modules: - **Always in scope.** No `use` line, no namespace — the same status the existing `print`, `print_int`, `now`, `words`, `count`, `latest` builtins already have, and the same flat `WO_B_*` id space in the VM's builtin table. - **Rationale.** The sample committed to this spelling at 176 sites before the spec had an opinion, and "samples force the grammar" (principle 8) makes that binding. Qualifying them (`text.len`) would add an import line to every file and buy nothing: these are language primitives, not capabilities — they touch no syscall, need no audit, and cannot be refused. - **Grouping** (documentation only, not namespaces): text operations, collection operations, map operations. - **Contract per builtin** is fixed arity with typed parameters, resolved at compile time like every other builtin; out-of-range indices trap (`T_BOUNDS`), never return a sentinel. - `print_err` joins this set (Part 2 already named it; Part 3 never listed it). **Deliberately not adopted:** iteration/closure builtins (`map`, `filter`, `reduce`) — the sample uses explicit loops throughout, and adding higher-order functions would require a function-value type the language does not have. ## 2. Amendment — `time` gains calendar surface Part 3's `time` row lists `now()`, `mono()`, `sleep(ms)`. The sample also calls: | Builtin | Sites | Why | | --- | --- | --- | | `time.iso(ms) -> Text` | 2 | JSONL detection timestamps and MCP response fields need a stable textual instant | | `time.local(ms) -> {year, month, day, hour, minute, dow}` | 1 | cron next-fire computation needs broken-out calendar fields, including day-of-week | Both are added. `mono()` is **cut** (0 uses) and returns when a workload needs monotonic math. The record `time.local` returns is a plain value record, so it needs no new type machinery beyond the `typedef` records plan 8 Task 4 already lands. ## 3. Amendment — boolean operators (new verdict-table row) The sample uses `and` at 35 sites and `or` at 20. Neither exists in the language, and — the reason this went unnoticed — **neither the verdict table nor any plan ever mentioned boolean operators at all**, in either spelling: there is no `&&`/`||` row, and Haxe's own operators were never enumerated. The verdict table gains one row: **`and` / `or` — adopt**, spelled as words rather than `&&`/`||`. - **Spelling rationale.** The sample chose words; the lexer has no `&` case at all (a bare `&` reports `WO-E001`), so words cost nothing to add as keywords, and they read better in the conditional-heavy code the sample is full of. - **Precedence.** One new level **below** comparison and above assignment: `or` binds loosest, then `and`, then comparison, then the existing arithmetic ladder. This makes `if a == 1 and b == 2` parse as intended without parentheses — the sample's dominant shape. - **Semantics.** Short-circuit, `Bool`-typed operands only, `Bool` result. No truthiness — a non-`Bool` operand is a type error, consistent with principle 13. - **Lowering.** Compare-and-jump on existing opcodes (`JZ` plus a jump), no new opcode and no VM change. ## 4. Amendment — `env` is the sixth module Part 2 specifies `env.args()`, `env.get(name)`, `env.exit(code)`, `env.stopping()`; Part 3's table omits `env` while calling itself "five builtin modules". The sample uses `env.get` ×1 and `env.stopping` ×4. Part 3's table gains an `env` row and the count becomes six. ## 5. Corrections to false status claims Both were asserted in the review doc; both are corrected at their real source rather than in the retired file. **Structural interface satisfaction is not implemented.** `WO-E205` is declared and never emitted. The review listed satisfaction checking as *implemented*, which is false. This amendment originally also called it *unreachable by design, not owed* — that half is wrong, and corrected here (plan 3 Task 4 review, 2026-08-11): the check's one legal home, a site where a value is used at an interface-typed position, **is** exercised by the milestone grammar. An interface-typed parameter accepting a concrete class that doesn't structurally satisfy it compiles clean today, then reaches `wovm` as an `ICALL` with no matching vtable slot, which traps `WO_T_BOUNDS` at runtime instead of failing to compile — even though the violation is statically provable (the class's method set is fully known). See [`01-error-catalog.md`](../../plan/oop-vm/01-error-catalog.md)'s "Reachable but unenforced" section for the repro and `tests/corpus/trap/unsatisfied-interface/` for the pinned current behavior. This is an owed gap, not a design decision — it stays off the log-watcher critical path only because the sample declares no interfaces, not because the gap doesn't exist. **`?T` is plumbed, not enforced.** The review listed "Nullable types `?T`" as implemented — the same conflation already corrected in `docs/plan/compiler/nullable-types-implementation.md`. No new action; noted here so the two documents agree. ## 6. What the review omitted — the emitter The review is titled "Compilation Requirements" and its Phase 4 promises "end-to-end compile + run", but it never lists the bytecode emitter as work. There is no `emit.ml`, no byte-writing anywhere in the compiler, no `--emit` or `-o` flag, and `wob_kind_of_typ` exists but is never called. That is [plan 3](../../plan/compiler/2026-08-01-wob-emit-e2e-single-binary.md) — story iteration 4, an entire slice — and it is the current NEXT PLAN. Consequence for iteration 4's scope, to be stated on the board: the emitter proves the pipeline on the **milestone grammar only**. The sample's ~200 unparseable constructs are iterations 5–6 work; iteration 4 must not be judged against the sample. ## 7. Plan updates | Plan | Change | | --- | --- | | [plan 8](../../plan/compiler/2026-08-01-haxe-parity-language.md) | Task 2 gains `and`/`or` (keywords, new precedence level, compare-and-jump lowering, short-circuit, `Bool`-only). Task 5 loses `throw` — catch frames ship without the explicit-raise half, and the error-payload slot plus its `.wob` version note go with it. **Task 7 is deleted** (`is` cut, `abstract` rejected); later tasks renumber. The Goal line drops `is`. | | [plan 9](../plans/2026-08-01-program-mode-stdlib.md) | Gains a core-builtins task covering the 22 bare globals plus `print_err`. `time` task gains `iso` and `local`, loses `mono`. `env` is named as a module rather than loose Part-2 prose. | | [plan 3](../../plan/compiler/2026-08-01-wob-emit-e2e-single-binary.md) | Unchanged. | | [plan 10](../plans/2026-08-01-log-watcher-sample.md) | Acceptance gains the diagnostic-count gate: 307 → 0. | | [`01-error-catalog.md`](../../plan/oop-vm/01-error-catalog.md) | `WO-E205` re-filed as **reachable but unenforced** — corrected 2026-08-11, see § 5 — with its repro; `WO-E208`/`E210`/`E211`–`E213` keep their existing reserved entries. | | [`docs/00-status.md`](../../stories/00-status.md) | NEXT PLAN gains the milestone-grammar-only note; pending list gains the three cuts under the parked section. | | `docs/00-code-review.md` | Reduced to a stub: one paragraph saying its findings landed here and in the plans, pointing at `docs/00-status.md`. | ## Error handling Nothing in this amendment adds an error-handling mechanism. `and`/`or` produce ordinary type errors on non-`Bool` operands (reusing `WO-E201` once that code is wired). Core builtins trap on out-of-range access rather than returning sentinels, matching the existing container builtins. Cutting `throw` leaves the uncaught-trap surface exactly as it is today. ## Testing - **Per amendment, corpus fixtures in the task that lands it:** `and`/`or` get precedence goldens (including `a == 1 and b == 2` without parens), short-circuit behavior, and a must-fail for a non-`Bool` operand. Each core builtin gets a golden exercising it plus a bounds-trap fixture where indices apply. `time.iso`/`time.local` get fixtures against a fixed injected clock so output is deterministic. - **The sample is the integration test.** `woc docs/examples/log-watcher` is run at the end of every iteration from 5 onward and its diagnostic count recorded; the number must fall monotonically from 307 and reach 0 at iteration 7. - **No new VM tests** from this amendment except the core builtins' own, since `and`/`or` add no opcode and `time`/`env` extend an existing module pattern. ## Success criteria 1. The systems-track spec's Part 1 has a boolean-operator row and its Part 3 lists six modules plus a core-builtins section covering all 22 names. 2. Plans 8 and 9 reflect every addition and cut; plan 8 Task 7 is gone. 3. `WO-E205` is documented as reachable but unenforced (corrected 2026-08-11, see § 5 — an earlier "unreachable-by-design" claim here was wrong), and `types.ml`'s header no longer claims a satisfaction set is produced. 4. `docs/00-code-review.md` is a stub; no second roadmap exists in the repo. 5. The 307-diagnostic baseline is recorded in plan 10 as its acceptance gate. ## Out of scope `throw`, `time.mono`, `is`, higher-order/iteration builtins, and interface satisfaction enforcement — each parked with its reason above. Implementing any amendment is the plans' job, not this spec's.