writeonce/docs/plan/exploration/blue-green-vm/00-vision.md
shoney.arickathil 49872a4b11 docs: six-bucket status board; recover lost vision doc; stop ignoring docs/
- 00-kanban.md rebuilt: ▶ NEXT PLAN pointer (iteration 4 — emitter, corpus,
  `woc build`) then six buckets — stories, in progress, done, pending,
  discarded, learnings. It tracked only the Rust runtime before, so the whole
  OOP track (wovm shipped, woc Tasks 1-8 shipped) was invisible.
- Board now records iteration 3's known gaps instead of silently owing them:
  `?T` plumbed but unenforced; E205/E201/E203/E204 dead, so structural
  interface satisfaction is unchecked.
- New discarded.md — settled rejections with reasons so they are not
  re-proposed: inheritance, `abstract` newtypes, Money/SKU/Float, Dynamic/cast/
  macro/extern, AOT-to-C, Menhir, shared engine state, external deployer.
- RECOVERED docs/plan/exploration/blue-green-vm/00-vision.md — gone from disk,
  never committed (gitignored path), cited by five docs incl. principle 12.
- Root cause was broader: all seven forward-roadmap plans in
  docs/superpowers/plans/ were untracked and ignored, on one disk only. Rules
  were half-fiction — 33 of 34 exploration files were already tracked, so they
  swallowed only *new* files.
- Dropped the docs ignore rules (exploration, oop-vm, superpowers/plans,
  examples/agent-loop, examples/mcp-think) with a do-not-re-add comment; added
  __pycache__/*.pyc. `tests/` stays ignored but warns that the next plan lands
  the corpus there.
2026-08-10 21:52:59 +02:00

180 lines
9.3 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.

# Blue/Green VMs — a self-hosting, agent-managed runtime
> **Partially superseded (2026-08-03):** the deployment subsystem (§5–§6 here)
> is now specified in
> [`docs/superpowers/specs/2026-08-03-blue-green-vm-design.md`](../../../superpowers/specs/2026-08-03-blue-green-vm-design.md)
> — developer + `wo` CLI as the management client (agent/MCP becomes a later
> wrapper), schema migration folded into the approval step (additive-only
> auto-diff in v1), fixed slots with alternating activity, HTTP+JSON+SSE.
> §1–§4 (transports, recipe box, fibers, source-in-binary) remain current
> thinking feeding plans 3/4/6.
> Thought-process capture (2026-08-02). Not a phase plan yet — the vision that
> shapes how the wovm runtime grows past milestone 1, recorded before the
> details harden. Related: the OOP spec
> ([`../../../superpowers/specs/2026-08-01-oop-compiler-vm-design.md`](../../../superpowers/specs/2026-08-01-oop-compiler-vm-design.md)),
> plan 4 (shard-actor runtime), plan 15 (MCP streamable HTTP), and the
> single-binary trailer already shipped by `woc build`.
## The idea, in five sentences
The writeonce executable is a **systemd service that never stops**. It embeds
its own **source code**, not just its bytecode. An external **Claude agent**
reads and edits that source through a managed channel; an approved change is
compiled **inside the runtime** and loaded into the idle VM slot. The runtime
holds **exactly two VMs — Blue (active) and Green (previous version)** — and
deployment is an atomic switch between them. Rollback is the same switch in
reverse, because the previous version never left memory.
## 1. A runtime is not a port
The runtime core is the VM pair + engine + scheduler — it must run with zero
listeners. Ports are **transports**, attached at boot like modules: an HTTP
listener, a unix socket, an MCP endpoint, stdio. Consequences:
- The same binary serves as web app, CLI batch runner, or agent-managed
service depending on which transports the deployment attaches — one of the
recipes a custom web framework builds from (§2).
- **systemd socket activation** fits exactly: the unit owns the socket
(`LISTEN_FDS`), the runtime accepts on whatever fds it inherits. The
"always running" property (§6) and the "no port of its own" property come
from the same mechanism.
## 2. The runtime is a recipe box for web frameworks
Everything a custom web framework needs in later phases must exist as a
separable runtime capability, not a monolith: transports (§1), fibers (§3),
routing surface (plan 6), the subscription registry (plan 7), the DB engine
(plan 5), and the deploy/rollback machinery (§5). A "framework" in a later
phase is a `.wo` library that composes these recipes — the runtime itself
stays framework-agnostic.
## 3. Fibers (green threads)
Concurrency inside a shard is **cooperative fibers scheduled by the VM**, not
OS threads — the Erlang shape on the wovm substrate:
- A fiber is exactly the execution state `wo_vm` already isolates: a register
window stack + frame stack + a current pc. Making that state per-fiber
instead of per-VM turns the interpreter into a fiber scheduler almost for
free.
- **Preemption by reduction budget**: the dispatch loop decrements a counter
per instruction (or per call/back-edge); at zero, the fiber parks and the
scheduler picks the next runnable one. No signals, no stack switching
tricks, deterministic and debuggable.
- Fibers **park on I/O**: a blocked read hands the fd to the shard's event
loop (`wo-rt.c`'s epoll/io_uring machinery) and the fiber resumes when the
completion arrives. One OS thread per core (plan 4's shard), thousands of
fibers per shard.
- Fits the ownership model: a fiber is an actor mailbox owner; cross-fiber
sends follow the same ownership-move rule as cross-shard sends.
## 4. The binary contains its source
`woc build` already appends the `.wob` image to a copy of `wovm` with an
offset trailer. The trailer grows one more section: **the `.wo` source tree**
(paths + contents, compressed). Why:
- The deployed artifact is self-describing — no "which commit is prod
running?" class of question. `wovm --dump-source` can always reproduce
exactly what is executing.
- The agent workflow (§5) needs a source of truth that travels with the
binary, not a checkout that can drift from it.
- After a deployment, the runtime rewrites its own source section (write to
temp, fsync, rename) so the artifact on disk always matches the Blue VM.
## 5. Agent-managed source — how Claude fits
The runtime exposes a **management transport** (MCP over streamable HTTP —
plan 15's machinery, localhost + bearer token, the log-watcher posture).
Claude Code connects as an MCP client. Tools the runtime serves:
| Tool | What it does |
| --- | --- |
| `source_list` / `source_read` | browse the embedded source tree of the running (Blue) version |
| `source_propose` | submit a changed file set as a **proposal** — staged, never applied |
| `proposal_diff` | render the pending proposal against Blue's source |
| `proposal_check` | run `woc check` on the proposal inside the runtime — diagnostics come back to the agent |
| `proposal_approve` | **human-only gate** (separate credential or out-of-band confirmation) — approval triggers compile + green-slot load |
| `deploy_switch` | atomic Blue↔Green switch after health checks |
| `deploy_rollback` | the same switch back — Green still holds the previous version |
| `deploy_status` | which version is Blue, which is Green, in-flight drain state |
Properties worth pinning now:
- **The agent proposes; a human approves.** `proposal_approve` is not
reachable with the agent's token. Approval is the compile trigger, not the
edit.
- **Every step is WAL-logged** — proposals, diagnostics, approvals, switches,
rollbacks form an audit trail that survives crashes like any other commit.
- **The compiler lives with the runtime** for this loop to work: either
`woc` embedded in the binary (adds OCaml runtime weight) or shipped beside
it in the service directory (lighter; the systemd unit owns both files).
Open question in §8 — start with "beside it".
## 6. Blue/Green VM lifecycle
Exactly **two VM slots** per runtime, never more:
- **Blue** — the active VM: all new requests/fibers dispatch into it.
- **Green** — the previous version, loaded and warm: the instant-rollback
target. After a successful deploy the roles swap; the old Blue becomes the
new Green.
The critical separation: **VMs own code, the engine owns data.** Tables,
WAL, subscriptions, and the arena slabs live in the engine layer beneath both
VMs; a switch swaps which bytecode handles requests, never the data. That is
what makes the switch cheap and rollback safe — no state migration on the
happy path (and schema changes are exactly the hard part, §8).
Deploy sequence:
1. Approved proposal compiles (`woc emit`) — failure ends the deploy,
Blue untouched.
2. New image loads + validates into the idle slot (loader is the same
validation battery as always — a bad image cannot boot).
3. Health gate: entry smoke / conformance subset runs against the idle VM.
4. **Switch at the dispatch boundary**: new work enters the new Blue;
in-flight fibers on the old VM drain to completion (bounded timeout).
5. Old Blue becomes Green (rollback target); the binary's source section is
rewritten to match (§4).
6. `deploy_rollback` at any later point is step 4 in reverse — no compile,
no load, the code is already resident.
## 7. Always running
The executable maps to a **systemd service**: `Restart=always`, socket
activation for the transports (§1), the hardening posture proven in the
log-watcher units (unprivileged user, read-only system, `StateDirectory`
for WAL/data). Deployment never restarts the unit — that is the whole point
of the VM pair. The unit restarting (crash, host reboot) boots Blue from the
binary's current source/bytecode section and reloads Green only when the
next deploy happens.
## 8. Open questions (deliberately unresolved here)
1. **Schema migrations.** Code switches atomically; data does not. A
proposal that changes a class's fields needs a migration story between
Green-shaped and Blue-shaped rows — the wo-seg migration doc's
dual-write thinking applies inside one process. Hardest problem in this
vision; needs its own exploration.
2. **Live subscriptions across a switch.** Do WebSocket subscribers survive
a deploy (registry lives in the engine layer → yes, by design), and what
do they see mid-drain?
3. **`woc` placement** — beside the binary vs embedded (§5).
4. **Fiber preemption granularity** — per-instruction counter vs
call/back-edge only (cheaper, coarser).
5. **Does Green count against the heap budget** (two arenas resident) or
does Green hibernate (bytecode resident, heap lazily rebuilt on
rollback)?
## 9. Where this lands in the plan sequence
- Fibers (§3): extends **plan 4** (shard-actor runtime) — same scheduler
work, one more scheduling unit.
- Transports-not-ports (§1): shapes **plan 6** (HTTP/service layer) — the
listener becomes one attachable transport among several.
- Management MCP (§5): builds on **plan 15**'s streamable-HTTP machinery.
- Source-in-binary (§4): extends plan 3's `woc build` trailer.
- Blue/Green switch (§6) + agent loop (§5): a new phase after those land —
needs spec + plan of its own once this vision stabilizes.