From 7c10df67a3852941d311aa81ddfc4426cf505562 Mon Sep 17 00:00:00 2001 From: "shoney.arickathil" Date: Fri, 14 Aug 2026 22:16:14 +0200 Subject: [PATCH] docs: reprioritise to log-watcher-executable only; update stories and plans MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Every remaining item is now traced to a measurement on the sample; anything the sample does not exercise is deferred by name with the measurement that says so. - new plan docs/plan/compiler/2026-08-14-logwatcher-executable.md — six tasks between "it runs" and "you can leave it running": the ownership pass learning stdlib return types (>1 MB leaked in 8s of `run` mode, one fs.read_all result), dropping a projected temporary (`for e in parse_dir(d).entries` leaks the shell per rescan), the runtime's own argv container (128 B every run), honouring the stop signal in blocking calls (a server in accept ignores SIGTERM), closing accepted connections (net.close exists, unused), and a soak that would have caught all of it. Opens with the measured starting point and closes with an explicit out-of-scope list - story 7 (log-watcher proof): status banner separating the met compile-and-run half from the executable half, plus a new Given/When/Then — clean SIGTERM exit, zero leaks, flat RSS and descriptors across a soak - story 5: grammar half landed, strictness half deliberately deferred - story 6: landed for the surface the workload uses, with the two lifetime defects it exposed pointed at the new plan - story 7b: recorded as off this workload's path, measured — the sample has no @gc class, 0 RC_INC/RC_DEC against 78 DROPs - 00-status.md: NEXT PLAN is the executable list; story table and in-progress row point at the new plan; deferrals carry their evidence - plan 8 (haxe-parity): banner now says on hold behind the executable plan, and its task states are corrected — Task 5 shipped, Tasks 6 and 7 are half done Co-Authored-By: Claude Opus 5 (1M context) --- docs/00-status.md | 68 ++++--- .../2026-08-01-haxe-parity-language.md | 2 +- .../2026-08-14-logwatcher-executable.md | 183 ++++++++++++++++++ .../05-language-surface.md | 12 ++ .../06-program-mode-stdlib.md | 12 ++ .../07-logwatcher-proof.md | 30 ++- .../07b-inferred-gc-mark-sweep.md | 9 + 7 files changed, 287 insertions(+), 29 deletions(-) create mode 100644 docs/plan/compiler/2026-08-14-logwatcher-executable.md diff --git a/docs/00-status.md b/docs/00-status.md index eda66c0..6a86acb 100644 --- a/docs/00-status.md +++ b/docs/00-status.md @@ -19,29 +19,47 @@ Statuses: ✅ **done** · 🔄 **in progress** · ⬜ **pending** · ⏸ **hold* ## ▶ NEXT PLAN -**Close the gaps the log-watcher milestone left open.** The acceptance target -of the whole language track — _compile and run log-watcher_ — is **met** as of -2026-08-14: `docs/examples/log-watcher` (1285 lines, 7 files) compiles with -zero diagnostics, and the image runs (`wovm lw.wob watch app.log 2 1` tails a -live file, classifies levels and fires `ALERT … last entry is error, quiet for -2s`). What remains is the *strictness* half of iteration 5 plus one runtime -gate, in this order: +**Make log-watcher executable — nothing else.** The compile-and-run half of the +language track is met (2026-08-14): the sample compiles with zero diagnostics, +`woc build` produces a 106 KB standalone binary, and all three modes work — +`watch` alerts on a live file, `run` schedules a cron.d entry, `mcp` answers +JSON-RPC with all four tools returning `isError:false`. `just log-watcher` +gates it: 6 checks, 0 failures. -1. **`?T` forced handling** (plan 8 Task 6's diagnostics half, `WO-E211`–`E213` - still dead). Optionals are currently **lenient**: `nil` is the zero word, a - `?T` is usable where `T` is expected, and nothing narrows. The - representation and the comparisons are right; the refusals are missing. -2. **`pub(read)` write enforcement** — parsed and recorded on the field; the - typechecker does not yet refuse a write from outside the declaring class. -3. **`using` extensions and `#if` build flags + reject-row diagnostics** (plan 8 - Tasks 7–8's remainder). Nothing in the workload needs them, so they are the - tail of the plan, not a blocker. -4. **ASan over the workload** — the corpus is ASan-clean, but log-watcher's own - run has never been under the sanitizer, and iteration 4's `gc/held-cycle` - leak is still open (see the known-gaps section). +What is left is the difference between "it runs" and "you can leave it +running", and every item below came from a measurement on the sample itself: -Plan: [`plan/compiler/2026-08-01-haxe-parity-language.md`](plan/compiler/2026-08-01-haxe-parity-language.md) · -Story slice: [`docs/stories/language-runtime-database/05-language-surface.md`](stories/language-runtime-database/05-language-surface.md) +1. **The ownership pass does not know what the stdlib returns** — so a binding + holding a fresh `fs.read_all`/`fs.list`/`net.read`/`json.encode` result is + classified Copy and never dropped. Measured: >1 MB leaked in eight seconds + of `run` mode, the largest single allocation being one `fs.read_all` result. +2. **A projected temporary is never dropped** — `for e in parse_dir(d).entries` + keeps the elements (correct) and leaks the record shell, once per rescan. +3. **The runtime leaks its own argv container** — 128 bytes in 2 allocations on + every run, `main.c`'s `multi Text` of arguments. +4. **A stopping program does not stop** — `env.stopping()` sets a flag, but + `net.accept`/`net.read` restart on `EINTR`, so a server parked in `accept` + ignores SIGTERM and needs `kill -9`. +5. **The MCP server never closes an accepted connection** — `net.close` exists + and is unused; every request costs a descriptor. +6. **Nothing soaks** — every check is seconds long, which is exactly the window + where a leak hides. The acceptance script needs a soak mode measuring RSS + and descriptors across a real duration. + +Plan: [`plan/compiler/2026-08-14-logwatcher-executable.md`](plan/compiler/2026-08-14-logwatcher-executable.md) · +Story slice: [`docs/stories/language-runtime-database/07-logwatcher-proof.md`](stories/language-runtime-database/07-logwatcher-proof.md) + +**Deferred by name, with the measurement that says so:** + +- Iteration 5's *strictness* half (`?T` forced handling, `pub(read)` write + enforcement, `using`, `#if`, reject rows) — it makes the language refuse + more; it does not make this program run. Plan 8 stays open for it. +- Everything `@gc`: iteration 7b, `set`'s `@gc` retention gap, iteration 4's + `gc/held-cycle` leak. The sample declares **no `@gc` class** — 35 classes, + none with the gc flag, 0 `RC_INC`/`RC_DEC` against 78 `DROP`s — so none of it + can affect this workload. +- Iterations 8–12 (shard-actor runtime, database engine, `@table`/query, HTTP + layer, fibers, blue-green): unchanged, and unblocked by this plan. 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 @@ -62,10 +80,10 @@ that sequences its tasks. Read one, approve, then the next starts. | 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) | 🔄 grammar done, strictness open | +| 5 | [Language surface](stories/language-runtime-database/05-language-surface.md) | 🔄 grammar done, strictness ⏸ deferred | | 6 | [Program mode + stdlib](stories/language-runtime-database/06-program-mode-stdlib.md) | ✅ (the surface log-watcher uses) | -| 7 | [log-watcher proof](stories/language-runtime-database/07-logwatcher-proof.md) | ✅ compiles and runs | -| 7b | [Inferred GC + mark-sweep](stories/language-runtime-database/07b-inferred-gc-mark-sweep.md) | ⬜ closes iteration 4's gate | +| 7 | [log-watcher proof](stories/language-runtime-database/07-logwatcher-proof.md) | 🔄 **runs; executable in progress** | +| 7b | [Inferred GC + mark-sweep](stories/language-runtime-database/07b-inferred-gc-mark-sweep.md) | ⏸ off the workload's path (no `@gc`) | | 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 | @@ -79,7 +97,7 @@ that sequences its tasks. Read one, approve, then the next starts. | Track | Item | Where | | -------- | --------------------------------------------------------------------------- | ---------------------------------------------------------- | -| Language | Iteration 5's strictness half — `?T` forced handling, `pub(read)` writes, `using`, `#if` | [plan 8](plan/compiler/2026-08-01-haxe-parity-language.md) | +| Language | Iteration 7 — make log-watcher executable (leaks, stop signal, fd lifetime, soak) | [executable plan](plan/compiler/2026-08-14-logwatcher-executable.md) | Off-critical-path work is parked by explicit scope directive (2026-08-08). diff --git a/docs/plan/compiler/2026-08-01-haxe-parity-language.md b/docs/plan/compiler/2026-08-01-haxe-parity-language.md index 2c8d9aa..8ae7cee 100644 --- a/docs/plan/compiler/2026-08-01-haxe-parity-language.md +++ b/docs/plan/compiler/2026-08-01-haxe-parity-language.md @@ -1,6 +1,6 @@ # Haxe-Parity Language Adoptions Implementation Plan -> **Status: 🔄 in progress** (story iteration 5) — Tasks 1–4 ✅ shipped and review-verified: modules (`use`/`pub`), language surface (`and`/`or`, `${}` interpolation, `const`, break/continue, do-while), switch expressions, typedef records + enum payload variants. Task 5 (try/catch over the trap system) ⬜ started, not landed. Tasks 6 (`?T` forced handling), 7 (statics, `using`, `pub(read)`), 8 (`#if` + reject rows) ⬜. Board: [00-status.md](../../00-status.md) +> **Status: ⏸ on hold behind [the executable plan](2026-08-14-logwatcher-executable.md)** (story iteration 5) — the grammar half of this plan is what let the driving workload compile, and the rest of it makes the language *refuse* more rather than making that program *run*, so it waits. Original status follows. — Tasks 1–4 ✅ shipped and review-verified: modules (`use`/`pub`), language surface (`and`/`or`, `${}` interpolation, `const`, break/continue, do-while), switch expressions, typedef records + enum payload variants. Task 5 (try/catch over the trap system) ✅ shipped 2026-08-14 — VM catch frames, expression and block catch arms, the `{code, line, method, msg}` record. Task 6 (`?T`) 🔶 half: the representation, `nil`, comparisons and narrowing-free use all work — the forced-handling diagnostics (`WO-E211`–`E213`) do not exist. Task 7 🔶 half: `static` members and `pub(read)` syntax landed, `using` and the `pub(read)` write check did not. Task 8 (`#if` + reject rows) ⬜. Board: [00-status.md](../../00-status.md) > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > diff --git a/docs/plan/compiler/2026-08-14-logwatcher-executable.md b/docs/plan/compiler/2026-08-14-logwatcher-executable.md new file mode 100644 index 0000000..3a18f96 --- /dev/null +++ b/docs/plan/compiler/2026-08-14-logwatcher-executable.md @@ -0,0 +1,183 @@ +# log-watcher Executable Implementation Plan + +> **Status: 🔄 in progress** (story iteration 7) — the sample compiles and its +> three modes run; this plan is everything still between "it runs" and "you can +> leave it running". Board: [00-status.md](../../00-status.md) + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. +> +> **Style rule (user convention):** concept, reason, and required behavior in words only; the executor writes the code. + +**Spec:** [`docs/superpowers/specs/2026-08-01-systems-track-design.md`](../../superpowers/specs/2026-08-01-systems-track-design.md) (Part 1 verdict table, normative), amended by [`docs/superpowers/specs/2026-08-10-logwatcher-gap-closure-design.md`](../../superpowers/specs/2026-08-10-logwatcher-gap-closure-design.md). + +**Goal:** `docs/examples/log-watcher` is **executable** — not merely compilable. +Each of its three modes runs indefinitely without growing, stops when told to, +and ships as one self-contained binary. Nothing else is in scope: every task +below exists because a measurement on the sample demanded it, and anything the +sample does not exercise is deferred by name in "Out of scope". + +**Architecture:** the compiler front (`compiler/src/`), the VM's ownership +tables (`owner.ml` ↔ `emit.ml`) and the runtime's process surface +(`runtime/src/main.c`, `sysio.c`). No new language features — the four +compiler-side tasks are missing *ownership knowledge*, not missing grammar. + +**Tech Stack:** OCaml stdlib (compiler), C11 libc (runtime), the conformance +corpus as regression, `scripts/log-watcher-accept.sh` as acceptance. + +## Where this plan starts (measured 2026-08-14) + +- `woc --emit docs/examples/log-watcher` → **0 diagnostics**, 35 KB image. +- `woc build …` → a **106 KB standalone binary** that runs its three modes. +- `just log-watcher` → **6 checks, 0 failures** (compile, watch alert, cron + schedule, MCP initialize / tools/list / 401). +- All four MCP tools answer with `isError:false`. +- **Under ASan, both long-running modes leak**: `watch` 128 bytes in 2 + allocations; `run` over 1 MB across 6 allocations in eight seconds — the + 1 MiB one is a single `fs.read_all` result. +- The sample uses **zero `@gc`**: 35 classes, none with the gc flag, 0 `RC_INC` + / 0 `RC_DEC`, 78 `DROP`s. Deterministic ownership is the whole memory story + here, which is why the leaks above are compiler bugs, not collector gaps. + +## Global Constraints + +- **The sample is the test.** No new corpus fixtures for this plan (user + direction, 2026-08-14); `just oop-e2e` must stay green as a regression, and + `just log-watcher` is the acceptance gate. +- **No new language surface.** A task that finds itself wanting one has found a + defect report against this plan, not a feature — stop and ask. +- Every task ends with the sample rebuilt and `just log-watcher` green, and + with the ASan measurement re-run so the number moves in writing. +- Commits are local only; never push. + +## File Structure + +``` +compiler/src/owner.ml stdlib return types; temporary-value drops (Tasks 1, 2) +compiler/src/emit.ml the drop sites those tables imply (Tasks 1, 2) +runtime/src/main.c argv container lifetime; stop-signal exit (Tasks 3, 4) +runtime/src/sysio.c blocking calls observing the stop flag (Task 4) +docs/examples/log-watcher/ mcp.wo: close what accept opened (Task 5) +scripts/log-watcher-accept.sh the soak check (Task 6) +``` + +### Task 1: The owner pass must know what the stdlib returns + +**Concept & reason:** `owner.ml`'s `expr_ty`/`resolve_callee` have no stdlib +table — `types.ml` and `emit.ml` each got one, the ownership pass did not. So a +binding whose value comes from `fs.read_all`, `fs.list`, `net.read`, +`json.encode`, `time.iso` or `proc.run` falls back to the `Scalar "Int"` +default, is classified **Copy**, and never gets a scope-end drop. That is the +1 MiB leak measured in `run` mode: `parse_file`'s `let content = try +fs.read_all(path, FILE_CAP) catch (e) nil` holds a fresh Text nobody frees. +The fix is to read the same `Types.stdlib_members` table the other two passes +read, including through a `try`'s arms, so the classification matches reality. + +- [ ] Failing measurement first: record the current ASan totals for `watch` and + `run` (eight seconds each, clean exit via SIGTERM) so the drop is proven, + not assumed. +- [ ] Teach the ownership pass the stdlib return shapes; every stdlib member + that yields a fresh Text, `multi` or record is Owned at its binding. +- [ ] Re-measure: the `fs.read_all` and `fs.list` allocations disappear from + both modes' reports; `just oop-e2e` and `just woc-test` stay green. + +### Task 2: A temporary whose field is projected must still be dropped + +**Concept & reason:** `for e in parse_dir(self.cron_dir).entries` compiles to +"call, keep the record in a register, read its field, iterate" — and the record +itself is never dropped, because the drop tables only track *bindings*, not the +anonymous receiver a projection borrows from. The elements stay alive (the loop +is correct), the shell leaks, once per rescan. The same shape appears wherever a +call result is projected without a `let`. The temporary must be owned by the +statement that created it and dropped at that statement's end, after every use +of the projection. + +- [ ] Failing measurement: the `run` mode's per-rescan growth over ~60 seconds, + with the rescan interval shortened, as the number to beat. +- [ ] Give a projected temporary a real owner and a drop at the end of its + statement, including when the projection feeds a loop that outlives the + expression. +- [ ] Re-measure: rescan no longer grows the process; corpus and unit gates + stay green. + +### Task 3: The runtime's argv container has no owner + +**Concept & reason:** program mode builds the `multi Text` of arguments in +`runtime/src/main.c` and hands it to the entry method, which borrows it. Nobody +frees it — ASan reports it on every run (128 bytes in 2 allocations). It is +bounded, so it is not the reason a daemon grows, but it is the runtime leaking +its own allocation, and it pollutes every future ASan reading of the sample. +The runtime owns that container and must release it after the entry returns, +before the heap is torn down. + +- [ ] Drop the argument container once the entry method has returned (both the + plain `wovm image.wob …` path and the single-binary path). +- [ ] `watch` under ASan reports **zero** leaks for a clean exit. + +### Task 4: A stopping program must actually stop + +**Concept & reason:** `env.stopping()` installs SIGTERM/SIGINT handlers that set +a flag, and `net.accept`/`net.read` retry on `EINTR` — so a server parked in +`accept` never observes the flag and TERM does nothing; only `kill -9` ends it. +`watch` and `run` stop correctly today only because they sleep between polls. +A service that cannot be stopped is not executable in any operational sense +(no clean restart, no deploy, no supervisor integration). The decision to make +and record: when a blocking stdlib call is interrupted **and** the stop flag is +set, the runtime stops the program rather than restarting the syscall — the +exit is the entry's normal one, with the same status a clean `return 0` gives. +The alternative (surface the interruption to the source) is rejected here: it +would put a trap in the middle of every accept loop the language will ever +write, and the shard-actor runtime (iteration 8) replaces these blocking calls +with an event loop anyway. + +- [ ] Failing measurement: `mcp` mode ignores SIGTERM and needs `kill -9`. +- [ ] Blocking stdlib calls observe the stop flag on interruption; the process + exits cleanly, flushing output. +- [ ] `just log-watcher` no longer needs `kill -9` in teardown, and the script's + hard-kill fallback becomes belt-and-braces rather than the mechanism. + +### Task 5: The MCP server must close what it accepts + +**Concept & reason:** `Mcp.serve` accepts a connection per request and never +calls `net.close` — the builtin exists, the sample does not use it. Every +request costs a descriptor; a long-lived server dies at the process limit. This +is the sample's own bug, and fixing it is in scope precisely because the sample +is the acceptance workload. The connection is a value the loop owns for one +iteration; it must be closed on every exit path from that iteration, including +the malformed-request path that answers 400. + +- [ ] Failing measurement: descriptor count for the server process across a few + hundred requests. +- [ ] Close the connection on every path out of the serve loop's body. +- [ ] Re-measure: the descriptor count is flat. + +### Task 6: Soak — the acceptance a daemon actually has to pass + +**Concept & reason:** every check today is a few seconds long, which is exactly +the window in which a leak is invisible. The claim this plan exists to support +is "you can leave it running", and nothing verifies it. Add a soak mode to the +acceptance script: run each of the three modes under load for a fixed duration, +sample RSS and descriptor count at the start and the end, and fail when either +grows beyond a stated tolerance. Keep it opt-in (an environment variable or a +flag) so the default `just log-watcher` stays fast for the ordinary loop. + +- [ ] Soak the three modes with a stated duration, load pattern and tolerance; + report the measured deltas whether it passes or fails. +- [ ] Run the soak against an ASan build once and record the result in the + status board's known-gaps section. +- [ ] `just log-watcher` (fast path) stays green and stays under a minute. + +## Out of scope — deferred by name + +- **`?T` forced handling, `pub(read)` write enforcement, `using`, `#if`, + reject-row diagnostics** (plan 8 Tasks 6–8's remainder). They make the + language *stricter*; they do not make this program run. Plan 8 stays open for + them behind this plan. +- **Anything `@gc`**: iteration 7b (inferred GC + incremental mark-sweep), + `set`'s `@gc` retention gap, iteration 4's `gc/held-cycle` leak. Measured: + the sample declares no `@gc` class and emits no `RC_INC`/`RC_DEC` at all, so + none of it can affect this workload. +- **json's `Bool` renders as `0`/`1`** and fractional numbers truncate — both + documented format consequences; the MCP client the sample targets reads them + fine. +- **Iterations 8–12** (shard-actor runtime, database engine, `@table`/query, + HTTP layer, fibers, blue-green) — unchanged, and unblocked by this plan. diff --git a/docs/stories/language-runtime-database/05-language-surface.md b/docs/stories/language-runtime-database/05-language-surface.md index ae395fe..b986f34 100644 --- a/docs/stories/language-runtime-database/05-language-surface.md +++ b/docs/stories/language-runtime-database/05-language-surface.md @@ -3,6 +3,18 @@ > Format: `product/story-iteration-template`. Part of > [Story — one language, one runtime, one database, one binary](00-story.md). + +> **Status (2026-08-14):** the *grammar* half landed — modules, `and`/`or`, +> interpolation, `const`, loop control, switch expressions, typedef records, +> enum payloads, try/catch, `nil`/`?T`, statics, `pub(read)` syntax, container +> literals, `for k, v in m`, and `as` — which is what let the driving workload +> compile. The *strictness* half (`?T` forced handling `WO-E211`–`E213`, +> `pub(read)` write enforcement, `using`, `#if`, reject-row diagnostics) is +> **deliberately deferred** behind +> [`plan/compiler/2026-08-14-logwatcher-executable.md`](../../plan/compiler/2026-08-14-logwatcher-executable.md): +> it makes the language refuse more, not the program run. Plan 8 stays open +> for it. + ## Goals - The language grows from milestone grammar to a daily-driver surface: the diff --git a/docs/stories/language-runtime-database/06-program-mode-stdlib.md b/docs/stories/language-runtime-database/06-program-mode-stdlib.md index bcac662..6c98b81 100644 --- a/docs/stories/language-runtime-database/06-program-mode-stdlib.md +++ b/docs/stories/language-runtime-database/06-program-mode-stdlib.md @@ -3,6 +3,18 @@ > Format: `product/story-iteration-template`. Part of > [Story — one language, one runtime, one database, one binary](00-story.md). + +> **Status (2026-08-14):** ✅ landed for the surface the driving workload uses — +> program mode (`fn main(args: multi Text) -> Int`, argv from the runtime, the +> return value as the exit code), the text/container builtins, and the OS half +> (`fs`, `time`, `env`, `net`, `proc`) with predeclared `Stat`/`TimeParts`/ +> `Proc` records, plus `json` encode/decode over `.wob` v2 class metadata. +> What the workload never calls was not written. Two lifetime defects found +> here are being fixed as part of +> [`plan/compiler/2026-08-14-logwatcher-executable.md`](../../plan/compiler/2026-08-14-logwatcher-executable.md): +> the runtime's own argv container is never freed, and blocking `accept`/`read` +> ignore the stop signal. + ## Goals - writeonce stops being server-only: a project with a free diff --git a/docs/stories/language-runtime-database/07-logwatcher-proof.md b/docs/stories/language-runtime-database/07-logwatcher-proof.md index e4714c1..d27908a 100644 --- a/docs/stories/language-runtime-database/07-logwatcher-proof.md +++ b/docs/stories/language-runtime-database/07-logwatcher-proof.md @@ -13,6 +13,17 @@ systems track's acceptance bar: nothing in a real daemon exceeded the language. +> **Status (2026-08-14):** the compile-and-run half is **met** — the sample +> compiles with zero diagnostics, `woc build` produces a 106 KB standalone +> binary, and all three modes work (`watch` alerts, `run` schedules a cron.d +> entry, `mcp` answers JSON-RPC with all four tools returning `isError:false`). +> The remaining half is **executable**: under ASan both long-running modes leak +> (watch 128 B, run >1 MB in eight seconds), the MCP server never closes an +> accepted connection, and a server parked in `accept` ignores SIGTERM. That +> work is sequenced in +> [`plan/compiler/2026-08-14-logwatcher-executable.md`](../../plan/compiler/2026-08-14-logwatcher-executable.md) +> and nothing else blocks this iteration. + ## Acceptance Criteria - What to achieve? @@ -33,6 +44,11 @@ - **when** the iteration closes, - **then** every row names its `.hx` sibling and deliberate divergences, and the could-not-express column is empty. +- What to achieve? *(added 2026-08-14 — compiling is not running)* + - **Given** any of the three modes started under a sanitizer build, + - **when** it is signalled to stop after a soak, + - **then** it exits cleanly on SIGTERM alone, reports zero leaks, and + its resident size and descriptor count are flat across the soak. ## Out Of Scope @@ -52,6 +68,14 @@ ## Proposed Solution -- Execute the existing plan: `docs/superpowers/plans/2026-08-01-log-watcher-sample.md` - (five tasks: tail state machine, cron, probes+supervisor, MCP subset, - main + README + live acceptance scenario in the `oop-accept` gate). +- The authoring plan (`docs/superpowers/plans/2026-08-01-log-watcher-sample.md`) + is spent: the `.wo` files exist and compile. +- What remains is + [`plan/compiler/2026-08-14-logwatcher-executable.md`](../../plan/compiler/2026-08-14-logwatcher-executable.md) + — six tasks, every one traced to a measurement on this sample: the ownership + pass learning stdlib return types, dropping a projected temporary, the + runtime's own argv container, honouring the stop signal in blocking calls, + closing accepted connections, and a soak that would have caught all of it. +- `just log-watcher` (`scripts/log-watcher-accept.sh`) is this iteration's gate: + compile, watch alert, cron schedule, and three MCP checks today; the soak + joins it in the last task. diff --git a/docs/stories/language-runtime-database/07b-inferred-gc-mark-sweep.md b/docs/stories/language-runtime-database/07b-inferred-gc-mark-sweep.md index c70c667..647f570 100644 --- a/docs/stories/language-runtime-database/07b-inferred-gc-mark-sweep.md +++ b/docs/stories/language-runtime-database/07b-inferred-gc-mark-sweep.md @@ -8,6 +8,15 @@ > (iterations 3–7) must not be delayed, and because the collector should be > settled before iteration 8 multiplies shards. + +> **Status (2026-08-14):** **not on the driving workload's path**, measured: +> `docs/examples/log-watcher` declares no `@gc` class — 35 classes in its image, +> none with the gc flag, and 0 `RC_INC` / 0 `RC_DEC` instructions against 78 +> `DROP`s. Its memory story is arena + deterministic drops end to end, so this +> iteration (and iteration 4's open `gc/held-cycle` leak, and `set`'s `@gc` +> retention gap) cannot affect whether log-watcher runs. It stays queued for +> workloads that build cycles; the `gc/` corpus fixtures remain its only users. + ## Goals - **The developer stops deciding which types are garbage collected.** `@gc`