- records every decision made without stopping to ask, with what each costs if wrong, since the SDD workspace is deleted on completion - R9 is marked WRONG and overturned by R14: WO-E222 fires on the class Pool, not on multi, so an actor can hold slots: multi PoolSlot. My ruling shipped a README prescribing a permanent 1-actor pool - R6 records that my own brief caused a security bug: trust_proxy with an absent XFF collapsed every client onto one shared bucket - R15 parks the one residual: pool_slots/pool_of have zero call sites, so real N-actor sharding is compile-proven but gate-unproven - measured the gate over 10 runs: it is NOT stably green. Most runs fail idempotent-stop; one lost 6 checks with 000 status codes - traces the flake to the C-runtime hang/segfault, now localised by gdb to wo_arena_alloc / wo_str_new / vm_run Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> (cherry picked from commit a919ab104ce44754949d364e35c88b6964b81fee)
4.9 KiB
porch 1 — decisions taken during execution
Fifteen rulings made while executing the plan without stopping to ask. Each says what was decided, why, and what it costs if wrong. One is known wrong and was overturned mid-flight (R9/R14).
| # | Ruling | Cost if wrong |
|---|---|---|
| — | Work on dev, not a fresh worktree — dev is the project's designated development branch, so this is not the "never implement on master" case |
Interleaves with other dev commits; separable by the porch-store prefix |
| R1 | The pool takes ONE message class with a kind: Int discriminator, not a receive per message type. An actor handle is typed to a single message class, so a second receive compiles but is unreachable. Verified by fixture before ruling |
Task 2's protocol restructured; contained to keypool.wo |
| R2 | Task 4's file list had to include keypool.wo — its step 4 edits the actor arm that lives there |
None; a documentation correction |
| R3 | "Never delete-then-insert" bans that pair as an UPDATE. Deleting an expired row is required and is not covered | An implementer leaves rows unpruned, leaking one per key ever seen |
| R4 | call's reply must be a copyable scalar (WO-E226) and every receive program-wide must agree, so the response travels through the @table, not the mailbox |
Task 4's protocol changes shape again |
| R5 | Task 3 calls the pool_count wrapper, never call() directly |
Fails to typecheck immediately — cheap to detect |
| R6 | trust_proxy: true with an absent X-Forwarded-For collapsed every client onto the key "ip:". Falls back to net.peer. My brief caused this |
None identified; the fallback is strictly safer than a shared bucket |
| R7 | Kept the Limiter.after() added beyond brief scope — "headers on both paths" cannot be met by stashing values nothing consumes |
A little surface to remove later |
| R8 | Kept curl in the gate legs, with an up-front presence check so its absence fails loudly |
One more host dependency for the gate |
| R9 | WRONG — see R14. Ruled that Pool being traced forces a one-slot re-wrap |
The feature's headline property was unreachable as documented |
| R10 | The hardcoded Retry-After: 1 on 503 stands; the brief requires the header present, not computed |
Clients retry marginally too eagerly under sustained saturation |
| R11 | Idempotent becomes a Handler decorator, not a Middleware — the actor needs the route's handler, and only that slot exposes it |
Users register it differently; caught at compile time |
| R12 | Overrode a reviewer's Minor to Important: caching any status meant a transient 500 replayed for 24h and a retry could never succeed | One extra fix round |
| R13 | Sent two Minors to a fix round against the usual rule, because one inflated the check count with an assertion that could never fail | One cheap extra round |
| R14 | Overturns R9. WO-E222 fires on the class Pool, not on multi; an actor can hold slots: multi PoolSlot. Tested by the final reviewer |
Already paid: a README shipped prescribing a permanent 1-actor pool |
| R15 | PARKED: pool_slots/pool_of are compile-proven and precedent-backed but have zero call sites, so real N-actor sharding is gate-unproven |
The sharding path could hold a runtime defect nobody has exercised |
The gate is not stably green
Measured over ten consecutive runs of scripts/web-app-accept.sh after the
final fix wave: most runs report 0 or 1 failures, and the failing check is
almost always idempotent-stop / idempotent-stop-2 — the SIGTERM teardown
assertions. One run lost six checks at once, with 000 status codes
meaning the server returned nothing at all.
Both symptoms trace to the C-runtime defect below, not to this feature's logic: the failures are teardown timeouts and, when the runtime crashes, whole sections. Do not read "83 checks, 0 failures" as a stable result — it is one outcome of a distribution.
Three C-runtime defects found, none fixed
They live in the C runtime, not in porch, and each is worked around here:
try EXPR catch (e) nilcannot distinguish a literalInt 0reply from a trap. Worked around by never packing a zero outcome code.- A
Text/map value read offjson.decode(...) as Tis corrupted once embedded in a struct crossing a function-return boundary. Worked around by forcing fresh text with.. ""on every field copied out of a decoded record. - Under concurrent
call()-parked callers with real table I/O, the process sometimes hangs aftermain()returns, with an observed SIGSEGV. Localised bygdbtowo_arena_alloc/wo_str_new/vm_run, and it fires more readily at higher sequential insert+delete volume against one key (N=4/5 crashed; N=1-3 clean over 12+ trials).
The third is the reason the gate flakes, and it is the highest-value thing on this branch to fix next — ahead of any remaining porch work.