writeonce/docs/superpowers/specs/2026-08-10-logwatcher-gap-closure-design.md
shoney.arickathil 79605cbb4f feat: milestone 1 complete — .wob emitter, conformance corpus, single binary; GC redesign specced
- `woc` now emits `.wob` that `wovm` runs: emit.ml lowers the typed,
  owner-annotated AST (scope-stack registers with a >64 WO-E401 diagnostic,
  Lua-style call windows, ICALL by slot, dedup const pool, drop maps, line
  tables, implicit terminators); disasm.ml backs `--dump-bc` goldens.
- Ownership lowering consumes the four owner tables verbatim; RESIDUAL is the
  only source of borrow ops, coalesced per operand. Review caught the emitter
  consuming only 2 of owner.ml's 4 residual producers — an assignment-anchored
  aliasing violation ran to exit 0 instead of trapping; fixed, plus a backstop
  raising WO-E404 for any residual region left unconsumed.
- Conformance harness `scripts/oop-e2e.sh` (`just oop-e2e`): four fixture
  kinds with exact outcomes — byte-exact stdout, one WO-E### anchored on
  `error CODE:`, numeric trap code, gc trace. 25 fixtures incl. pricing-demo
  logic, the ownership suite, and DB_STUB's parse-but-trap. `tests/` un-ignored
  so the corpus is actually tracked.
- `woc build` produces a self-contained binary: wovm copy + appended image +
  20-byte trailer, self-exec via /proc/self/exe. Verified relocated outside
  the repo, argless, and against adversarial trailer corruption.
- Milestone 1's five spec criteria all MET (`just oop-accept`). Criterion 3
  closed by WO-E405 — the entry must return `Int`, since program mode already
  says its return value is the exit code — which deletes the leak class
  without adding return-type metadata to the format. `gc/held-cycle` retired:
  an externally-held cycle is not expressible in a post-exit pump.
- New spec: inferred GC + incremental per-shard tri-color mark-sweep, retiring
  `@gc` and reference counting. Story gains iterations 7b (that work) and 9b
  (`@table`, relations, compiler-checked query); `.dev/reference` gains a
  sparse System.Linq checkout. Priority: 5→6→7 (log-watcher) then 7b, 8, 9, 9b.
2026-08-11 19:31:26 +02:00

209 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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`](../../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.