Commit graph

147 commits

Author SHA1 Message Date
cd779afa7f docs(rv2-tls): phase G1 signing landed (RSA-PSS + ECDSA-P256)
§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)
2026-09-15 01:15:52 +02:00
82446652e4 docs(rv2-tls): brainstorm phase G (inbound TLS server) to ready
- §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)
2026-09-15 01:15:52 +02:00
cf31bb4ebd docs(rv2-tls,jarvis,status): outbound TLS client complete — jarvis unblocked
- 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)
2026-09-15 01:15:52 +02:00
53485a748c docs(rv2-tls): lock two more F3c-net requirements from the gofiber/Go comparison
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)
2026-09-15 01:15:31 +02:00
f1e355c4d4 docs(rv2-tls): brainstorm §F3c-net to ready — four integration forks locked
- §F3c-net rewritten to READY (decisions locked 2026-09-09), grounded in
  the runtime not assumed:
  1. blocking connect+handshake then park the data plane (mirrors
     net.connect's own "tolerable while rare" stance); park-based
     handshake a named follow-up
  2. per-shard fd-keyed wo_tls_conn slot table, no locks (the wo_child /
     one-thread-per-shard pattern); slot holds driver state + partial-record
     + leftover-plaintext buffers
  3. failures trap WO_T_IO loudly incl. chain + hostname (no silent nil)
  4. per-shard lazy read-only CA bundle (/etc/ssl/certs, WO_CA_BUNDLE)
- builtin surface: net.connect_tls/read_tls/write_tls (ids 115-117,
  WO_B_MAX->117), acceptance criteria (incl. concurrent-shard TSan),
  out-of-scope (park handshake, TlsConn object, HTTP layer, inbound G)
- frontmatter review_pending + phase-F row + status NEXT PLAN updated:
  F3c-net spec ready, next action is BUILD (live-gated)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
(cherry picked from commit 8b6e72171c5db9dfe5b7a5122be123bf7cc3cdd9)
2026-09-15 01:15:31 +02:00
2391553658 docs(rv2-tls,status): F3c-net plan + net.connect_tls object-model default; session NEXT PLAN
- 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)
2026-09-15 01:15:31 +02:00
ad088163cc docs(rv2-tls,jarvis): TLS ladder through F3c-core + SAN landed
- 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)
2026-09-15 01:15:31 +02:00
c84d33c9ba docs(rv2-tls): rv2 9 phase F1-F3b landed (record, key schedule, messages, offline verify)
- 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)
2026-09-15 01:15:31 +02:00
bb64278aa9 docs(rv2-tls): rv2 9 phase E (X.509 core) landed
- phase-E ladder row: core landed (DER reader + cert parse + verify_one
  + parse_spki + check_validity), KAT'd on real RSA + EC chains
- review_pending frontmatter: forks auto-approved 2026-09-08, SAN/
  hostname + CA-bundle walk deferred to phase F

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
EOF2
git log --oneline -2

(cherry picked from commit 796ed88d76f1cf25ced1eb5e7bdb4e4ab3cf4e9b)
2026-09-15 01:15:31 +02:00
9d1689be55 docs(jarvis): create iteration stories 1 (ready) + 2/3 (refine)
- 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)
2026-09-15 01:15:31 +02:00
ca572086ee docs(rv2-tls): rv2 9 phase D complete (RSA + ECDSA-P256 verify)
- ECDSA-P256 verify landed; phase D done (both signature verifiers)
- rv2 9 story, board ladder, jarvis 00-story deps, and dependency-graph §7
  synced: A-D landed, E-F remain

(cherry picked from commit 3eab98cc268e524c7b4e2ab72a4b093692a67d03)
2026-09-15 01:15:31 +02:00
4e70509001 docs(rv2-tls): rv2 9 phase D part 1 (RSA verify) landed
- RSA PKCS#1 v1.5 + PSS verify over SHA-256, bignum Montgomery modexp,
  KAT-gated vs python RSA-2048. ECDSA-P256 (D2) remains. Board synced

(cherry picked from commit ae42943c97949224d883e9ddcf5ae2117fef62b3)
2026-09-15 01:15:31 +02:00
d02befd6bf docs(jarvis): sync jarvis dependencies to landed state + dependency graph
- jarvis 00-story: net.connect marked landed (id 110); outbound-TLS blocker
  now rv2 9 in-progress (A AEAD / B HKDF / C X25519 done; D-G remain);
  architecture + blocker table updated
- dependency-graph: new §7 jarvis dependency graph (phase-level TLS ladder +
  porch framework path); §5a net.connect node marked landed

(cherry picked from commit a615ee80238fb384c0b33fc2b54bcd6bd4078078)
2026-09-15 01:15:31 +02:00
8e652800fe docs(rv2-tls): rv2 9 phase C (X25519) landed
- constant-time X25519 (RFC 7748), curve25519-donna radix-2^51; KAT-gated incl.
  the 1000-iteration vector. Ladder A+B+C done; next D. Board synced

(cherry picked from commit b929a20b7482d7137a45b77a2179fe7ef03c52b6)
2026-09-15 01:15:31 +02:00
aff8ffdf14 docs(rv2-tls): rv2 9 phase B (HKDF key schedule) landed
- HKDF-Extract/Expand + Expand-Label over hmac_sha256, KAT-gated (RFC 5869 +
  8446); status -> in-progress; ladder A+B done. Board synced

(cherry picked from commit e24b8ec6d838fc66848864c2a0974205aaa9b4be)
2026-09-15 01:15:31 +02:00
2db3766b66 docs(rv2-aead): rv2 8 phase C (software AES-GCM fallback) landed
- 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)
2026-09-15 01:15:31 +02:00
6a38ad7cb0 docs(rv2-aead): rv2 8 phase B (AES-GCM) landed
- 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)
2026-09-15 01:15:31 +02:00
da840a5be6 docs(rv2-aead): rv2 8 phase A (ChaCha20-Poly1305) landed
- status -> in-progress; phase A marked landed (matches RFC 8439 §2.8.2,
  KAT-gated in test_crypto, ASan clean). Remaining B/C/D/E. Board synced

(cherry picked from commit db5bdf3c3cac31c0d2be60027e0e2bf9fe8309c1)
2026-09-15 01:15:31 +02:00
7f7a601e2f docs(rv2-aead): brainstorm runtime-v2 8 (AEAD ciphers) to ready
- 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)
2026-09-15 01:15:31 +02:00
51addb504c docs(rv2-tls): brainstorm runtime-v2 9 (in-process TLS) to ready — hand-rolled
- 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)
2026-09-15 01:15:31 +02:00
cc1c82b2ef docs: jarvis track, runtime-v2 7/8/9, lang-41 fix design, fiber scope-gap
- 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)
2026-09-15 01:15:31 +02:00
b932e0cb87 docs(porch-static): brainstorm story 8 (static + lifecycle) to ready
- 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)
2026-09-15 01:15:31 +02:00
4d3e4261e1 docs(porch-sse): brainstorm story 7 (SSE + compression) to ready
- 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)
2026-09-15 01:15:31 +02:00
641703903c docs(porch-streaming): brainstorm story 6 (streaming core) to ready
- 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)
2026-09-15 01:15:31 +02:00
6b820fdd5f docs(porch-routing): brainstorm story 5 (routing + response ergonomics) to ready
- 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)
2026-09-15 01:15:31 +02:00
274f7c5361 docs(porch-csrf): brainstorm stories 3 (sessions) + 4 (CSRF) to ready
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)
2026-09-15 01:15:31 +02:00
6a67db252b docs(porch-cookies): brainstorm story 2 (randomness+cookies) to ready
- 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)
2026-09-15 01:15:30 +02:00
5e8e0960bc feat(rt2): term.size + term.width — the wmux ladder's last runtime asks
- term.size(fd) -> ?TermSize{cols,rows}: TIOCGWINSZ, resize's read twin;
  nil = not a tty (expected answer, never a trap)
- term.width(cp): libc wcwidth under C.UTF-8 (LC_CTYPE set on first
  use, host-locale fallback): -1 control, 0 combining, 1, 2
- ids 108/109 (all four registrations); TermSize predeclared
- legs: PTY sized 77x33 from outside answers exactly that, pipe answers
  nil, widths a/CJK/combining/BEL = 1/2/0/-1; test_term 81/0, woc 557/0
- story runtime-v2 6 recorded done; board row appended

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit 1514fb46c21c4318856cfcb3ca2b4d430caba72b)
2026-09-15 01:15:30 +02:00
6e997759e5 docs(rt2): close out runtime-v2 1-5
- 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)
2026-09-15 01:15:30 +02:00
3190b609af docs(rt2): track-wide brainstorm — all five iterations ready, graph remapped
- 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)
2026-09-15 01:15:30 +02:00
ab8ef64cd9 docs(rt2): runtime-v2 track — the runtime beyond sockets
- five stories under docs/stories/runtime-v2/: 1 streaming subprocess
  (42's follow-up; five forks incl. the mailbox-cap collision), 2 PTY,
  3 signals-as-events (signalfd lean), 4 termios adoption, 5 SCM_RIGHTS
  fd passing; 00-story states the arc — the plane learned sockets in
  8/11/35, files in 6, this adds processes/terminals/signals
- build order 1 -> 2 -> 3; 4 and 5 startable alone; all readiness:
  refine, brainstormed on demand
- board: Five tracks; "▸ runtime-v2" pending section; wmux section now
  points at it; graph section 6 nodes carry runtime-v2 numbers + links
- wmux stories re-reference the track; prefix `rt2` claimed
- linkcheck: 0 broken

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit e0451cb4889bb3d03429c0276d03c090269a5ced)
2026-09-15 01:15:30 +02:00
b9ce271b3d fix(lang41): an unadopted shard must not impersonate shard 0
- 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)
2026-09-15 01:15:30 +02:00
84cfbb1885 docs(porch-store): saturation gate leg closes out porch 1
- 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)
2026-09-15 01:15:30 +02:00
5941a092f7 feat(query-corpus): iteration 9g corpus #1 — skillhost needs no new query grammar
- 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)
2026-09-15 01:15:30 +02:00
b17b848403 docs(lang42): close out iteration 42
- 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>
2026-09-01 22:19:25 +02:00
8bcd24969e docs(lang42): story, spec and plan for bounded subprocess
- claim the lang42 prefix; iteration 42 story (readiness: ready), approved
  spec, and the 11-task implementation plan
- board: pending row for 42; graph: node 42 with green edges (11, 24)
- graph: porch track section added (same sweep)
- parity studies that motivated 42: alacritty, tmux, zen-browser under
  docs/plan/exploration/ — staged path, gap lists, refused routes

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 22:19:25 +02:00
4ad24d6381 docs(db2-migrate): close out iteration 12
- 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)
2026-08-31 21:54:27 +02:00
1b6d633ed1 docs(db2-migrate): spec + story for schema migrations v1
- 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)
2026-08-31 21:54:27 +02:00
710325b94a docs(db2-chain): close out iteration 11 on the board
- 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)
2026-08-30 20:38:03 +02:00
68dd3d88b8 test(db2-chain): cover flattening, and drop a ceiling no input could reach
- 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)
2026-08-30 20:38:03 +02:00
2cad84b7c6 feat(db2-chains): bound a keys-resident row's delta chain
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)
2026-08-30 20:38:03 +02:00
841f6c8f3f feat(db2-keys): gate the residency measurement, close out task 7
- 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)
2026-08-30 20:38:03 +02:00
882c1f7d24 feat(db2-keys): task 7 — measure resident: keys against swapping
- 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)
2026-08-30 20:38:03 +02:00
cfd660a5e6 docs(db2-chains): spec + story for bounding a row's delta chain
- 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)
2026-08-30 20:38:03 +02:00
64035bf177 docs(db2-delta): resident:keys has storage; move done criteria to Met
- "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)
2026-08-30 20:38:03 +02:00
e08c26309a feat(db2-delta): lift the resident:keys refusal, prove it end to end
- 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)
2026-08-30 20:37:49 +02:00
7b39da7eb6 fix(db2-keys): a logged delete must replay on a keys-resident table
- wo_row_remove's keys arm borrows the row from the log to find its
  index entries, and a borrow reads through db->rt->wal. At boot that
  pointer is not wired yet: main.c replays first (main.c:226) and
  assigns rt.wal afterwards (main.c:268)
- so the borrow found no log, the remove failed, and replay reported a
  valid tombstone as CORRUPTION. An UPDATE record would have failed the
  same way, since replay applies it as remove-then-recreate
- replay now lends the runtime a read-only view over the fd it already
  has open, for the replay's duration only, and restores what was there
- broken by the delete fix in 76b8fd9 — deletes worked in-process but
  their tombstones broke the next boot. Unreachable in production only
  because the loader still refuses the annotation
- pinned by test_keys_resident_delete_then_replay, verified failing
  against the unfixed code (2 failures) and clean with it
- found by asking whether the read-modify-append plan was ready, not by
  a gate

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit dc25462461b9f79d70c803f7174adc90fa16c90e)
2026-08-30 20:37:27 +02:00
516bd8362d docs(db2-keys): a runnable example for per-table storage
- docs/examples/residency: one program, two tables filled by the same
  loop, differing only in the annotation. Run twice against one WO_DATA
  and orders replay while sessions do not
- the example checks its own claim (exits 1 if a durable:false table
  survives, or a durable:true one fails to replay) rather than narrating
  it in a print
- resident: keys is written out as a commented block with the loader's
  exact refusal, so the frontier is visible in the example rather than
  only in a story. It documents WHERE the refusal happens: woc compiles
  it and emits a .wob; wovm exits 2, because the annotation is a
  load-time property
- residency-accept gains two legs: the example runs and its restart
  claim holds, and the refusal message the README quotes is checked so
  doc and code cannot drift apart
- the gate writes the example's output to /tmp/residency.log,
  banner-separated, for tail -F
- README commands verified verbatim; they needed mkdir -p because wovm
  will not create WO_DATA

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit c9c7e03e62c3918cb65ef7994d1e33a0c5337b71)
2026-08-30 20:37:27 +02:00
7ad52937b2 fix(db2-keys): delete on a keys-resident table was memory corruption
- wo_row_remove read the id map's value as a slot, but on a keys table
  that value is a LOG OFFSET (hput(t, id, wal_off + 1)). slot_row does
  no bounds check, so a delete indexed t->slabs[] with a byte offset and
  then called db_val_free on whatever it landed on — arbitrary frees,
  not a wrong answer
- keys tables now take their own arm: no slab slot, no bitmap bit, no
  free-list entry to return. The index hook needs the row's values, so
  the row is borrowed from the log for exactly that long
- wo_row_ptr carried the same trap and is public. It cannot refuse keys
  tables outright (insert legitimately calls it while the map still
  holds a slot), so it now detects the offset case — index past the
  slabs, or bitmap bit clear — and returns NULL. Callers all handle NULL
- test_keys_resident_delete pins it; it SEGVs against the old code,
  verified by reverting the fix rather than assumed
- found while auditing every hget() reader before narrowing the loader
  refusal to allow benchmarking. The refusal was justified in the docs
  by "updates are unimplemented" while actually standing in front of
  this too: a guard whose stated reason is narrower than its real one
  gets removed by someone who believes the stated reason

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 76b8fd944af9ed062467bdf9ab93c2e96dd198cf)
2026-08-30 20:37:27 +02:00
8311330531 docs(db2-keys): reconcile databasev2 and porch markdown with the code
- loader's resident:keys refusal said "rows are still fully resident"
  and "until tasks 5c/5d land". Both false since f606fc9. Corrected to
  name the real blocker: UPDATE needs read-modify-append
- databasev2 00-story: the sequence graph drew 2->3->4, which reads as
  3 needing 2 and 4 needing 3. Both backwards, and it still drew the
  2->5->6 path the 2026-08-27 amendment retired. Redrawn stating only
  real dependencies, with 4 and 3 shown as composing rather than
  ordered, and the execution order that actually happened
- databasev2 03: the hazard and its Outstanding entry both claimed
  nothing fails "because iteration 2's storage half is unimplemented".
  Marked discharged, and recorded that the hazard named only half the
  danger — the bitmap walk would have dropped keys rows outright
- databasev2 06: pending -> hold (largely superseded, revisit only on
  a measurement); dated its 5c/5d references
- porch 01: rewritten to the settled shape. readiness ready, status
  in-progress, phases B and C marked superseded with why
- porch 01 claimed time.after "is still a reserved builtin id". False —
  builtin 90, implemented. That claim is what made the iteration look
  cheaper than it is
- porch README gains honest ledger rows for both features (partial,
  being rebuilt), not shipped
- skill-catalog README pointed at a story path that moved tracks;
  linkcheck now 0 broken

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit b3d8c403e1d19ac27ec966de85cb293e0765795c)
2026-08-30 20:36:55 +02:00