Code is the source of truth; these claims no longer matched runtime/src:
- "net.connect does not exist" — landed 2026-09-07 (id 110); net.connect_tls /
read_tls / write_tls (115-117) + net.accept_tls (118), WO_B_MAX 118. Fixed
in jarvis 00-story (problem statement + architecture + out-of-scope), porch
00-story (proxy middleware row), rv2 7 (push-collector fork), 00-code-review
- "TLS: none / proxy-mandated forever" — retired by rv2 9 (in-process TLS both
directions). Fixed in porch + web-app + site example READMEs (proxy is now a
deployment choice; HSTS row), 00-code-review
- "no RNG anywhere in the runtime" — imprecise: the runtime has a getrandom(2)
source since rv2 9 (TLS ephemerals), but nothing exposes it to .wo yet.
Fixed in CODE-LOGIC (digests), lang 34, porch 2, status lang-39 row
- "porch 9 blocked on language 41" — lang 41 fixed 63065ff. Fixed in porch 1,
jarvis 00-story, status NEXT PLAN, dependency graph (L41 done, P9 ready)
- dependency graph §7 rewritten: the runtime side is done; jarvis 1 waits only
on porch (developer's porch-first order). Adds jarvis 1's dependency table +
the build order that satisfies it
- 00-code-review: a dated 2026-09-09 re-verification appended (record kept)
- site README lives in the writeonce-site submodule: committed there, pointer
bumped here
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit f1049dd9b7c7770da28bcabfc1cb1324621e7ee6)
Root cause (decision 1): cross-shard send/call/monitor pointer-shared the
message into the receiver's shard (e->payload = msg_val), so a worker read and
eventually dropped an object living in the sender's arena — a double free, then
a class-0 forge, then a modulo self-route livelock, all downstream of that one
broken invariant ("VM heaps are never read cross-shard", which wo_db_rpc keeps).
- actor_marshal: the sender encodes the message into an arena-independent neutral
form (wo_db_val_encode, the same marshal wo_db_rpc uses) and drops its own
original — no pointer crosses an arena boundary, so the double-free class is
gone by construction. actor_unmarshal rebuilds it in the receiver's arena
(wo_val_decode_vm) and frees the neutral. Applied to the 4 cross-shard
producers (send x2, call, monitor) + the 3 consumers (kinds 0/5/7). Same-shard
paths untouched (the WO_SHARDS=1 fast path never failed). Call replies are
scalars by contract, so kind 6 needs no marshal.
- eng_settle_inboxes: undrained kind-0/5/7 payloads at teardown are the neutral
form now — free with wo_db_val_free, not wo_drop_obj (caught by ASan mid-fix).
- decision 2: wo_route_free traps a shard_id >= nshards header (a corrupt/freed
block) instead of self-routing it into the settle livelock.
- proof: tests/regress/lang-41/cross-shard-marshal.wo (a multi<Text> sent +
called cross-shard, both sides drop) — clean 12x/5x under WO_SHARDS=4 + ASan;
shard-settle repro still clean 8x; full runtime suite 0 fail (same-shard
byte-unchanged). `just db-actor` extended with the new fixture.
- unblocks porch 9. Follow-ups: poison-on-free (decision 3), corpus fixture (4).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 63065ff75799f7f43b2bce6de61e77856799566f)
- rv2 9 story -> status: done. §G G3 landed; ladder A–G complete, live-gated
both directions (just tls 5/0, just tls-server 4/0). review_pending +
phase rows + G sub-phases updated
- doctrine retired where the story named it: language 34 ("TLS permanently
the proxy's job"), language 38 ("proxy-terminated ... no HTTPS clients"),
porch 00-story ("TLS ... proxy-terminated") — each corrected to point at
in-process TLS (net.connect_tls / net.accept_tls)
- status board: rv2 9 row DONE + a top summary; NEXT PLAN = porch then
jarvis (sequencing set: jarvis follows porch)
- jarvis 00-story: sequencing note (no longer runtime-blocked; porch first)
- CODE-LOGIC: the inbound-server section (net.accept_tls, signing, slot
refactor, RST-drain, gate)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit f3a3c962e5f288c25e505851edef4e0a5df9a85f)
§G sub-phase G2: the sans-io server handshake FSM (wo_tls_server) landed,
loopback-KAT'd against the client driver (EC + RSA identities, app
round-trip). Remaining G3: net.accept_tls + private-key parse + live gate.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 57613bddc621a90970d9443ae5a5deacffe0cfca)
§G sub-phase G1: constant-time RSA-PSS + ECDSA-P256 signing landed and
KAT'd (RSA vs python from-spec; ECDSA vs RFC 6979 A.2.5). Remaining G1c
(private-key PEM/DER parse) folded into G3 (which reads key files); the
server FSM takes raw key material.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit f02518cdabab02043a04c6bdd28a81b86bb9fbd4)
- §G written to READY (forks auto-approved, review_pending): the inbound
server rung. Grounds what's reused (record layer, role-symmetric key
schedule, X.509, slot table + data plane) vs new (server FSM, signing,
key parsing, accept surface)
- six locked decisions: (1) constant-time private-key ops — the built
modexp/scalar-mult are verify-only, not constant-time, so G adds a
constant-time fixed-window modexp + Montgomery-ladder scalar mult;
(2) both RSA-PSS + ECDSA-P256 server keys; (3) deterministic RFC 6979
ECDSA nonce; (4) net.accept_tls(listener,cert,key) w/ per-path shard
identity cache; (5) full 1-RTT server-auth only (no mTLS/resumption/HRR);
(6) sans-io wo_tls_server FSM
- sub-phases G1 signing+key-parse, G2 server FSM (loopback KAT), G3
net.accept_tls + live gate (openssl s_client); acceptance + out-of-scope
- ladder G row -> READY; may become its own runtime-v2 iteration
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 9fcb4a96a137a897f0ce2be868c18e63a979c997)
- rv2 9 §F3c-net marked LANDED + live-gated; phase-F row COMPLETE (client);
frontmatter review_pending updated (client complete, remaining = G server
+ deferred park-handshake/TlsConn/pooling + doctrine-doc corrections)
- jarvis 00-story + 01: the outbound-TLS blocker is cleared
(net.connect_tls landed) — jarvis 1 (chat loop) is now buildable
- status board: rv2 9 row + NEXT PLAN rewritten to the completed client;
next step is jarvis 1 or rv2 9 G
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 732c2216b501dd226d306f9abcbd1ddd846809dc)
Compared §F3c-net's forks against gofiber v3's client (fasthttp + Go
crypto/tls/x509, .dev/reference/fiber). Two gaps my defaults had vs Go,
now locked as decisions 5 and 6:
- (5) bounded handshake deadline: the blocking model would let a stalled
server hang the shard's one thread indefinitely (the DoS DoTimeout
closes). connect_tls now bounds connect+handshake via non-blocking
connect+poll + SO_RCVTIMEO/SNDTIMEO, default WO_TLS_HANDSHAKE_MS
(10s); expiry traps WO_T_IO. _dl variant + park handshake stay follow-ups
- (6) chain hardening: signatures+validity+SAN alone let a leaf act as a
CA. Now every non-leaf must assert basicConstraints CA:TRUE (+pathLen)
and the leaf must carry EKU serverAuth — what Go's crypto/x509 enforces
- acceptance criteria added (stalled-server timeout; leaf-as-CA + no-EKU
rejected); connect_tls bullet, frontmatter review_pending, status NEXT
PLAN updated to six locked forks
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 9662cd8b040f417b4886dae9ce97b70083e24e40)
- rv2 9 §F3c-net: the remaining live-gated slice with auto-approved
defaults — getrandom ephemeral, system CA-bundle loader, net.connect_tls
builtin returning the TCP fd (fd-keyed side table, blocking model like
net.connect) driving the sans-io driver, then wo_tls_verify_chain; plus
net.read_tls/write_tls and a live gate
- status board NEXT PLAN: the TLS client security engine landed this
session (E-F3c minus socket glue), F3c-net is the next rung, then jarvis
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit ad87974fc722268e278f957f1f3d607f5dafa50d)
- rv2 9 phase-F row + review_pending: F3c-core sans-io driver + SAN/host
landed (KAT'd vs RFC 8448 record trace); remaining F3c-net = system CA
trust-anchor walk + net.connect_tls VM plumbing (live-gated), then G
- jarvis 00-story + 01 blocker tables: crypto/handshake engine landed;
jarvis now waits only on net.connect_tls (the socket glue)
- status board rv2 9 row updated to the full ladder state
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 4fdf07196d01c32d0ab12d32d36fa4285ff34654)
- phase-F row: F1 record layer, F2 key schedule, F3a message layer,
F3b offline handshake verification all landed + KAT'd (RFC 8448 /
real certs); F3c socket FSM + net.connect_tls plumbing remaining
- review_pending updated to the current ladder state
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit d49bc3866b6b620d00a8a57137ba211da25cd05b)
- jarvis 1 (chat loop) brainstormed to ready with forks AUTO-APPROVED for
autonomous execution and flagged in `review_pending` frontmatter for the
developer's second review: Anthropic Messages API backend, env-var API key,
actor-per-conversation SSE relay, durable @table history, session-gated routes
- jarvis 2 (tool use) + 3 (retrieval/RAG) created at refine with forks named
- 00-story iterations table linked to the new files
- blocked until rv2 9 TLS reaches phase F; pure .wo on porch 2/3/6/7 + the seam
(cherry picked from commit 8e160c3fcf9c50a05af073c036c693b192c9e595)
- portable constant-time AES-GCM software path; AES-GCM now on any CPU
(hw-or-sw dispatch), NIST-KAT-gated both paths (48/0). Remaining D/E;
ARMv8 hw path deferred. Board synced
(cherry picked from commit 138de17988e6bd274abe5e1e2d444ff2689747cd)
- phases A + B done; B = AES-128/256-GCM via AES-NI/PCLMULQDQ, NIST-KAT-gated,
ASan clean, portable binary (CPUID-gated). Phase C now owns the software
fallback AND the ARMv8 hardware path (deferred, untestable on x86-64 host)
(cherry picked from commit 249b1dbd72122ed6c05f4f0aadb7e1a5e87f844c)
- a second consumer (rv2 9 TLS phase A) reshaped the forks since the draft
- locked: BOTH AES-GCM (128/256, TLS-mandatory per RFC 8446) AND
ChaCha20-Poly1305 (RFC 8439, easy constant-time, cookie default)
- AES constant-time via AES-NI/ARMv8 hardware + bitsliced software fallback
(compiler intrinsics, zero external dep)
- caller-supplied nonce (TLS builds its own per-record nonce); random-nonce
is a cookie WRAPPER (phase D) not the primitive. shape:
seal(key,nonce,aad,pt)->Bytes / open->?Bytes; AES variant by key length
- raw key + length check; hand-rolled (matches rv2 9); ids from 111
- phases A ChaCha -> B hardware AES-GCM -> C software AES -> D cookie wrapper
-> E gate (RFC 8439 + NIST GCM vectors, ASan, reference cross-check)
- risk/test: constant-time mandatory, KAT-gated, reused-nonce documented
- retires the stale "TLS proxy-terminated" OOS line (rv2 9 overturned it)
(cherry picked from commit c8a5a31c39a5d14952056cd0af1b8c1e42d4ed2d)
- decision: HAND-ROLL TLS 1.3 (no vendored lib) per developer call; keeps the
zero-external-dep single binary, and raises risk rather than lowering it —
recorded, owned, with mandatory mitigations
- 1.3-only; RSA-PSS/PKCS1 + ECDSA-P256 + full ASN.1/X.509 chain validation +
trust store + hostname (the scope needed to reach real LLM APIs)
- decomposed into a bottom-up phase ladder: A AEAD (=rv2 8, forces AES-GCM
there) -> B HKDF -> C X25519 -> D signatures/RSA -> E X.509 -> F record+FSM
client -> G inbound server; C/D/E may each split into own iterations
- risk + test strategy section: constant-time, reference-tested (openssl +
RFC 8448 vectors), negative tests first-class, no partial-trust states
- deps: rv2 8 (AEAD), lang 34 (SHA/HMAC), net.connect (110, landed). Board synced
(cherry picked from commit f1881cca8cd0cdd58b1ac844e3e3ea9c234bb99c)
- jarvis (00-story): 6th track, 2nd software built with writeonce — an AI
assistant; direct-HTTPS design; blockers named (net.connect + TLS)
- runtime-v2 7 observability + 8 symmetric cipher: moved from the language
track (were 30/43); 9 in-process TLS: created from the gap jarvis surfaces,
RETIRES the "TLS is the proxy's job" doctrine (both directions)
- language 41 (arena hang): fix design to ready — marshal cross-shard
messages (root), align the shard_id % nshards route/compare + assert bound;
poison-on-free + minimal fixture as follow-ups
- fiber scope-gap analysis (plan/exploration/fiber/01): porch vs fiber, what
porch lacks, would developers prefer porch
- board + dependency-graph synced (porch 2-8 ready; rv2 table; §5/§5a graphs)
(cherry picked from commit 203470ceb2a151fe3584931cd4237af3f96a9f29)
- whole porch track (2-8) now brainstormed and locked (all ready)
- four decisions: three hooks (on-listen/on-shutdown/on-route-registered);
healthcheck ships BOTH /livez + /readyz; directory listing off-by-default,
documented; Last-Modified via a new small time.utc(ms)->TimeParts builtin
- language enhancement: YES, one small builtin -- time.utc, a gmtime sibling
of time.local (time.local is local-tz, time.iso is UTC-but-ISO); IMS by
string-equality, no date parser. The track's third + smallest language touch
- byte ranges/large files via fs.read_at + iteration 6 writer; not lang-41-exposed
- track language bill now explicit: random_bytes (2), deflate+crc32 (7),
time.utc (8) -- each a builtin with a named consumer, none decoration
- validated against .dev/reference/fiber. Board: whole track marked ready
(cherry picked from commit 9801fceade799e25718606177f09e4306a579e98)
- five decisions: refuse incoherent heartbeat/idle_ms pair at construction;
codec = two C builtins deflate+crc32 (perf over pure-.wo; hand-rolled, no
zlib dep; gzip framing in .wo); ETag over uncompressed bytes + Vary;
Last-Event-ID explicitly unsupported (not silently ignored); Vary via
comma-join
- language enhancement: YES, two builtins -- the track's SECOND language
dependency after iteration 2's random_bytes. CRC32 finally gets its
consumer; inflate deliberately not built (request-body decompression OOS)
- corrected stale dependency: Vary uses iteration 5's comma-join, so story 7
depends on 6 + 5, NOT 2; codec is pure compute, not lang-41-exposed
- confirmed CRC32 absent + iteration 36 bit operators landed (pure-.wo was
viable, traded for hot-path speed)
- validated against .dev/reference/fiber. Board synced
(cherry picked from commit 07f53574dd90f502235b79d4920eab3d684c8b77)
- re-scoped to OUTBOUND streaming only
- three decisions: separate StreamHandler/BodyProducer parallel path (Resp
path untouched -> existing responses byte-identical); streaming routes opt
out of the after-chain, framework refuses at registration to combine with
header-mutating middleware (loud, never silent), security_headers() helper
lets handlers stamp them; chunked REQUEST bodies split into their own future
iteration (parse.wo refusal stays, smuggling cases enumerated for later)
- no language enhancement (net.write framing, fs.read_at/actor source,
interfaces for producer); rides the fiber loop not the actor pool, so not
lang-41-exposed
- fixed title inconsistency: "three iterations wait on" -> "two" (7 and 8)
- validated against .dev/reference/fiber + the app.wo/serve.wo pipeline. Board synced
(cherry picked from commit 15205408e03c3c02a38e00e5d2017a8a11f62f28)
- five decisions: head auto-registers with opt-out (+ patch/options/all);
request ids mirror limiter trust model with a NON-crypto source; per-route
body_limit is a SECOND check after routing (global BODY_MAX stays the
pre-routing ceiling, over-limit = 413); Route fields are corpus-free;
Vary accumulates by comma-join
- key finding: story 5 has NO upstream dependency, not even iteration 2 --
request ids are not secrets, so a non-crypto source (time.ticks+counter)
keeps it startable today; the one porch slice buildable right now
- three story assumptions corrected: per-route limit cannot replace the
global (body read before routing); the container-owned-move corpus fixture
has its OWN Route (adding fields is free); Vary needs no iteration 2
- validated against .dev/reference/fiber; zero language enhancement. Board synced
(cherry picked from commit 0589a13db1f3b7c220d9d9fdc76142af6a63c390)
sessions (3):
- six decisions: pure-auth-primitive row (no payload bag); wall-clock
time.now not monotonic time.ticks (restart durability); login always
mints a fresh id (fixation, no anon-session model); throttled last_seen
touch at idle/20 (not a WAL write per request); Session writes
req.principal; config refuses absolute < idle
- finding: no per-key actor pool, so NOT blocked on lang-41 (plain @table
CRUD, same path storefront uses); the no-bag rule closes the one place
fiber's Set(key,any)+msgp+RegisterType would have hit principle 13
csrf (4):
- five decisions: fiber's hybrid transport (session-stored CsrfToken
@table + double-submit cookie, both must pass; no CSRF for sessionless
apps); opt-in single-use (checkout example); double-click -> distinct
SPENT refusal, NOT coupled to lang-41-blocked idempotency; trusted
origin/referer/Sec-Fetch-Site second layer; refusal classes distinct in
logs, opaque in body
- no actor pool, not blocked on lang-41
both validated against .dev/reference/fiber (v3, 3ca9a9d); exactly ZERO
language enhancement needed beyond iteration 2's random_bytes. Board synced.
(cherry picked from commit 3a4fb4215b23d2516362e2dd0acc5bec6c9aebc0)
- five forks locked: cookies: multi SetCookie beside unchanged headers
map; bare-name random_bytes(n)->Bytes; structural-400 in parse_request
+ on-demand cookie() helper; base64(value).base64(mac) signing;
app-supplied key, no middleware (that is iteration 3)
- validated against .dev/reference/fiber (v3, 3ca9a9d): exactly ONE
language enhancement needed (the CSPRNG); repeated Set-Cookie, cookie
attributes, parsing and signing all map to existing primitives
- corrects phase A registry: random_bytes joins the crypto-family
bare-name table (emit.ml b_* + types.ml), NOT wob.h's module enum;
next free id 84/90, not 110
- board: story 2 marked ready, porch-2 row rewritten off the stale
wob.h/110 claim
(cherry picked from commit 4d31d5359436496aed40cb25611abc7ccd4d7875)
- five stories status: done; 00-story records the one-run landing
- spec History: three implementation amendments (Signal record not
scalar, caller-owned stdio fds, handler-latch instead of signalfd)
- board NEXT PLAN entry with measured findings (zero transport code
added; the tty-across-the-socket handover proven; the double-raw
refusal restoring the terminal — the "bug" that was the design
working); section rows flipped; graph nodes green
- CODE-LOGIC.md: the runtime-v2 section
- full belt quoted on the board: suites 0 fail both flavors (test_proc
193/0, test_term 60/0), woc 557/0, subprocess 12/0, site 23/0
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit bc1b4f070693eb755ad6a9fd0c853fb3e2bda347)
- spec 2026-09-01-runtime-v2-design.md: the one principle (PULL — a
child is fds, the net verbs drive them; runtime-v2 adds acquisition
verbs, never transport), the full surface (ids 97+: spawn/spawn_pty/
wait_dl/signal/resize, signal.on delivering the sig number, term.raw/
restore with runtime-guaranteed restore, send_fd/recv_fd/connect_unix),
actor-owned lifecycle, mechanics notes, refusals by name
- push transport rejected with reasons recorded (mailbox-cap collision,
new delivery machinery); death-notice verb refused (a two-line fiber
composes wait_dl)
- five stories flip readiness: ready; fork sections rewritten as settled
- graph section 6 remapped: pull broke the 1->2->3 chain — only 1->2
remains; 3, 4, 5 and the VTE grid startable alone today
- board section + registry follow; linkcheck 0 broken
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit d313cdeebbe53c83b83f31f4480631568d9d0743)
- root cause: a worker's runtime is initialised lazily on first fiber
adoption, and rt.shard_id is stamped only there — but INBOX_READY[i]
is set at thread creation. A shard that never adopts is still settled
at shutdown, carrying rt.shard_id 0 from the memset
- it then impersonated shard 0: wo_drop_obj saw 0 == 0 for anything the
primary allocated, took the "we are home" branch instead of routing,
and called class_free against rt->classes, which lazy init never
filled. &rt->classes[class_id] off a NULL base is the faulting read
- fix: stamp the runtime's real identity at thread creation. An
uninitialised shard owns nothing, so its true id makes every payload
correctly foreign and routes it to an owner that can free it
- ASan could not name this: the arena is one hand-managed malloc block,
so intra-arena reuse is invisible and it surfaces as a bare SEGV
- pinned by tests/regress/lang-41, driven from db-actor-accept. Needs
multiple shards (the corpus runner pins WO_SHARDS=1) and the ASan
build. SEGVs twice per run unfixed, clean fixed
- the HANG is a separate defect and is NOT fixed: with this in place the
harness stops losing whole sections, but idempotent-stop-2 still
fires ~1 run in 6. The story records where to look
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 9dca0b4b4727b976d326b29cb4c6522b62d48a73)
- Add saturation leg (scripts/web-app-accept.sh): one-actor pool,
WO_MAILBOX=2, 15 concurrent requests, exactly 3 served + 12 answer
503; execution count matches the 200 count, retry-after + real
cause verified on the 503s
- Guard make_pool(n<1) by clamping in make_pool itself, not
pool_select's division -- that trap runs inside the middleware's
own try/catch and would be swallowed as ordinary saturation forever
- README: rate limiting + idempotency ledger rows moved to done,
scoped to what the gate proves; documented Handler-decorator
shape, Pool aliasing (WO-E222), call's scalar-only reply (WO-E226),
pool size as a capacity decision
- Story: Progress table filled with real hashes, 7/9 acceptance
criteria marked verified with citations, 2 marked verified by
construction (never gated even in the original plan), status: done
- Status board: standup entry, porch 1 pending row updated
- Recorded a pre-existing runtime hang (main() returns cleanly, OS
process sometimes hangs under concurrent call()-parked callers)
that also reaches the new leg's teardown; contained with kill -9
rather than asserted, so it can't flake the leg's actual subject
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 21934b18910070a3f24b8bd4367fcb9d397dc1fb)
- resolved 9g's forks EMPIRICALLY against the running compiler:
- count(<query>) and len(<query>) already work (fork 2 collapses to
zero code)
- skillhost's correlated NOT EXISTS is a backlink emptiness in
writeonce (`where len(x.children) == 0`), using only 9b machinery
(fork 1) — verified on a self-referential ?ref/backlink table
=> corpus #1 forces NO new grammar; per the method ("add only what a
corpus uses"), exists/not-exists was NOT built
- docs/examples/skill-catalog: mirrors skillhost's `skills` table
(name @unique, description/location/root, parent ?ref Skill, children
backlink) and translates all five of its SQL statements 1:1
(insert+dup-trap, get-by-name, list, roots via backlink-emptiness,
count); scripts/skill-catalog-accept.sh 7/0, WAL-durable, dup trap
persists across restart
- fixture run/db-query-corpus (count(query) + backlink NOT EXISTS);
just skill-catalog module; target/ gitignored
- general exists/not-exists left unbuilt and recorded as "enters when a
corpus forces a non-relation correlation"
- gates: oop-e2e 80/0, woc-test 566/0, skill-catalog 7/0; story + board
record the finding
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 4c82461634d45f11eca1a252702031c03d999c7f)
- story frontmatter status: done, Progress section records what landed
vs the spec (everything, same day as the brainstorm)
- board: NEXT PLAN entry with the six standup answers (deadlock proven
real: 5 s hang, 8192-byte truncation; 15 ms after; ping 2 ms during a
parked child; 1000 spawns fd-flat; SIGTERM leaves no child); pending
row flipped to DONE
- graph: node 42 class done, same change as the board row
- runtime/src/CODE-LOGIC.md: the bounded-subprocess section (bundle
park, slot registry, ownership sweeps, raw pidfd syscalls)
- full belt at close: 19 runtime suites 0 fail (test_proc 128/0),
woc-test 557/0, subprocess-accept 12/0, site-accept 23/0
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- crash-before-rename test: a COMPLETE valid migrated temp beside the
untouched original is discarded and the boot re-migrates — the
sharpest point on the crash timeline, deterministic, no fault
injection needed
- story: all six tasks done with commit hashes, all eight criteria met
with the test that proves each, plus the three deviations from the
plan and why (transcode over replay, lazy head, poison forces
transcode)
- CODE-LOGIC: migration section; also corrected limitation 3, which
still claimed unbounded hot-row chains — iteration 11 closed that
- status board row 12; deploy guide's rollback section gets its real
answer (rolling back across a migration is a migration backwards:
expect the refusal, restore the .bak)
- test_wal 5966 pass, 0 fail
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 4bb6ece2531e2123eb958c91d9bef4a6528eab3b)
- brainstorm settled: declarative and automatic at boot; v1 verbs are
add and delete only; data/seed migrations deferred to v2
- added fields zero-fill by kind: the grammar has no field-default
syntax and v1 refuses to grow compiler surface for it
- same-kind delete+add refuses as a disguised rename; retype and
vanished classes refuse by name
- schema lives in the log itself: WO_WAL_SCHEMA head record, written by
fresh-log open and compaction; name-keyed diff also closes the
silent cid-renumbering hole
- story is iteration 12, board row added, db2-migrate prefix claimed
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 072e007b144ff6689b6ff920ea10665900c1a2ef)
- status: done in the story frontmatter (was the non-conventional
"complete"; the board's axis uses done/in-progress/pending/hold)
- board row 11: what landed, the WO_WAL_UPDATE correction, the ceiling
removed as unreachable, and the one criterion still weaker than
written (expected value, not a resident: all oracle)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit de39a88e81e97bf6f8b75e9f15331aae20f7ab29)
- flattened row image is WO_WAL_UPDATE, not WO_WAL_INSERT: the row's
original INSERT is already in a live log, so a second one for the same
id is a duplicate replay refuses as corruption. INSERT is right only
for compaction, which builds a fresh log
- remove WO_CKPT_MAX_GARBAGE: with the absolute term at 64 MiB, garbage
large enough to reach a 256 MiB ceiling has already tripped it, so the
branch was unreachable. Postgres needs both constants because it
thresholds on tuples with its pair at opposite ends; this thresholds
on bytes, where one constant does both jobs
- test_delta_chain_flattens_at_k: chain depth stays <= WO_DELTA_MAX_HOPS
across 2K+2 updates, and a reset is observed
- test_delta_chain_flatten_replays: a flattened chain replays correctly
- test_keys_resident_indexed_across_flatten: a delta on an indexed
column composes with flattening, checked at every step across the
bound and after restart. Found no product defect
- test_should_compact_absolute_and_ceiling: pins the absolute term, the
boundary just under it, and the small-log case the ratio still governs
- test_wal 5700 pass / 0 fail; wovm-test and woc-test green
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit f93b5d9db753305c297e868d977670e6d703684c)
TESTS DELIBERATELY HELD at the developer's instruction — logic only.
The existing suite passes (36 suites, 0 fail) but exercises NEITHER new
behaviour: nothing builds a 16-deep chain, and no checkpoint test uses a
log near 64 MiB. Green here means "did not break what existed".
- tier 1: wo_wal_fold_row_at gains hops_out. The walk already visits
every hop, so the depth is free — this is the design's pd_prune_xid,
a cheap "is work worth doing" hint taken from work already happening
- the update path branches on it: past WO_DELTA_MAX_HOPS (16) it writes
a full-row image instead of a delta, terminating the chain. `r`
already holds the complete post-update row because index maintenance
required folding it, so flattening costs bytes, not an extra read
- wo_wal_append_row_image encodes from a caller-held row, as
WO_WAL_INSERT: a chain's base must replay into a database where
nothing precedes it, so replay/compaction/fold need no change
- tier 2: should_compact gains a TRIGGERING absolute term and a ceiling.
Our `floor` SUPPRESSES on a small log — the opposite of postgres's
vac_base_thresh, which triggers on a small absolute problem the
proportion hides. We had the proportion and the suppressor and
neither real guard
- verified by construction, not test: both update entry points converge
on row_apply_field_keys; db.c captures next_offset BEFORE calling in,
so the re-point is transparent to which record type was written
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 1b808abd5942de81c3a6416714d1302384103040)
- new `residency` leg in db-bench.py driving docs/examples/residency-bench:
two tables identical except the annotation, control cap + binding cap
- ITS OWN PROGRAM, not a db-bench mode: declaring a resident: keys table
is a WHOLE-PROGRAM constraint, so the no-WO_DATA refusal fires for
every mode in the module. Putting those classes in db-bench's shared
types made growth/ceiling/randread — which run without WO_DATA —
refuse to start. Caught by running the leg, not by reading it
- gates the RATIOS, waives the absolutes: ops/sec under a cap is swap
and disk I/O and belongs to the box. Same split randread makes
- rss_ratio 2.55 floor 2.0 tol 10% (structural, like bytes_per_row);
overcap_vs_swap_x 1.53 floor 1.0; in_ram_cost_x 4.23 ceiling 8.0;
all_collapse_x 105.4 floor 2.0
- all_collapse_x exists because the leg's FIRST run silently measured
nothing: at QUICK's 40k rows a 48 MiB cap binds neither mode, so the
"over-cap" half was not over cap. The cap now scales with N and the
leg asserts it binds
- verified the gate bites: rss_ratio 1.4, overcap_vs_swap_x 0.6 and
in_ram_cost_x 12.0 are all rejected
- task 7 closed: both criteria moved to Met with how each was verified
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit a310496664982c372f51b43113465eb8ad9e9fb5)
- two tables identical except the annotation, 200k rows, 40k reads in
one key order, WAL on ext4 (not /tmp, which is tmpfs here and would
have put the log in RAM), rootless cgroup v2 cap
- WIDE shape, 2.55x smaller resident set: 34.4 MB vs 87.5 MB. That is
the real win and the thing the mode was built for
- under a 48 MB cap (between the two resident sets): keys 19635 ops/s
vs all 12854 — only 1.53x faster than letting the kernel swap
- degradation is far gentler though: all collapses 105x from its own
uncapped throughput, keys 16x
- costs 4.2x read throughput when memory is not tight, and writes are
markedly slower — the keys fill did not finish in 2 min where the
resident fill plus 40k reads did. No design doc had costed writes
- THE UNANTICIPATED FINDING: cgroup limits charge the PAGE CACHE, so
moving rows to a file does not escape a container memory limit. WAL
37 MB + RSS 34 MB cannot both live under a 48 MB cap, so every pread
reaches disk. The premise "the page cache will hold the hot rows"
fails in exactly the deployment this targets
- first attempt used Int-only rows and showed parity; recorded, because
drop_payload frees a field's VALUE and an Int's value is its inline
slot word, so that shape cannot benefit and would have condemned the
feature for the wrong reason
- verdict: keep it, to fit ~2.5x more data in given RAM — not to make
an over-capacity table fast
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 7cba9b1174b0bf581314b3147e25cc49e6f49464)
- fixes a limitation iteration 2 shipped: compaction was supposed to
bound chain length, but wo_wal_should_compact triggers on a whole-log
byte ratio and cannot see one hot row's chain
- tier 1, flatten on update: the update path ALREADY folds the row for
index maintenance and the fold already walks hop by hop, so it reports
depth for free. Past a fixed K it writes a full row instead of a
delta. Read <= K+1 reads, replay O(K^2) per row. No format change, no
per-row RAM, no new trigger
- tier 2: our compaction policy has a proportional term and a
SUPPRESSOR misleadingly called a floor; postgres's floor TRIGGERS on
small absolute garbage. Add that term and a ceiling
- design read from .dev/reference/postgresql, not recalled:
heap_page_prune_opt gates on an O(1) on-page hint then page fullness
against Max(fillfactor, BLCKSZ/10); autovacuum uses base + scale *
reltuples clamped by a max (50, 0.2, 1e8). Neither thresholds on
new-bytes-versus-old-bytes
- K deliberately does NOT scale with table size: postgres scales a
table-level aggregate with proportional harm, ours is per-row with
additive cost, so scaling up would make big databases boot worst
- the story says plainly it should NOT be next: task 7 has still never
measured whether resident: keys beats the kernel's own paging
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit f667cad2cfbe187b5973440ab1af015b2df288f8)
- "no storage behind it" / "nothing yet stores a table that way" was
false — CRUD, checkpoint survival and updates all landed; replaced
with an accurate summary naming task 6/7 as what remains
- the three checked delete/delete-replay/update criteria sat in
Outstanding despite being done; moved to Met, leaving Outstanding
holding only genuine task 6/7 work
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit b575678ee95fa3125fa4e8145320a9cdca10ba38)
- loader.c: delete the INCOMPLETE-update BAIL; durable:false +
resident:keys stays refused (nowhere to read from)
- table.c: root-cause fix for the Text-index gap — a keys-resident
borrow now holds ENGINE values, matching wo_row_ptr's contract
(table.h's "no VM pointer" doctrine), not a VM-decoded row. Fixes
idx_hash/idx_cols_equal/wo_idx_probe AND db.c's GET_FIELD/PROBE
arms with one change; reproduced pre-fix as an ASan
heap-buffer-overflow
- docs/examples/residency: Product is genuinely resident:keys;
residency-accept.sh's refusal leg replaced by proving the program
runs and stock survives a restart (11/0)
- test_wal.c: oracle test drives resident:all and resident:keys
through the same update sequence and asserts identical rows;
Text-indexed-update test catches the representation bug; five
pre-existing tests corrected to the fixed contract (4746/0)
- story, README, status board, CODE-LOGIC.md updated; three known
limitations documented: mid-drain stale reads, O(N^2) replay in
chain length, compaction blind to per-row chain length
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit b87c68f950f01aa5e572fbb86a0f374adc83d813)