docs(porch-store): the fifteen rulings taken during execution
- 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)
This commit is contained in:
parent
484402a156
commit
5e619cf9de
1 changed files with 56 additions and 0 deletions
|
|
@ -0,0 +1,56 @@
|
|||
# porch 1 — decisions taken during execution
|
||||
|
||||
Fifteen rulings made while executing
|
||||
[the plan](2026-08-29-porch-store-backed-middleware.md) 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:
|
||||
|
||||
1. `try EXPR catch (e) nil` cannot distinguish a literal `Int 0` reply from a
|
||||
trap. Worked around by never packing a zero outcome code.
|
||||
2. A `Text`/map value read off `json.decode(...) as T` is **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.
|
||||
3. Under concurrent `call()`-parked callers with real table I/O, the process
|
||||
sometimes hangs after `main()` returns, with an observed **SIGSEGV**.
|
||||
Localised by `gdb` to `wo_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.
|
||||
Loading…
Reference in a new issue