- WO-E226: call's reply must be a copyable scalar, and every receive
program-wide must declare the same return type. Verified by fixture:
"call's reply type `Out` is not a copyable scalar"
- the spec had the actor return the response object, which cannot cross
the mailbox. Corrected: the actor stores the response and returns an
outcome code; the middleware reads the row and builds the Resp
- owner and duplicate now read the SAME durable row, so byte-identical
replay is structural rather than careful copying
- blocking, exactly-once execution and the mailbox queue are unchanged
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit 77e06c1690b92d456a9bc53503695fdaa2b4b44e)
- the spec's blocking design was unimplementable: call's reply IS the
return value of receive, so an actor cannot hold a waiter. Holding
means never returning, and an actor that never returns cannot process
the completion it waits for — deadlock
- corrected shape: the actor RUNS the handler inside its own receive, so
a duplicate waits in the mailbox and is served after the owner. The
queue blocking needs is the mailbox; nothing is held
- verified before adopting it, not after: an actor can receive a message
carrying an interface-typed value and invoke it, so the route's
Handler passes through the mailbox
- spec History records the reasoning error — "the primitives landed" was
taken as "blocking needs no new surface", which does not follow
- plan: 5 tasks. Counting and replay live in one new keypool.wo; both
middlewares become thin key-choosers, so porch 2 and 3 inherit one
serialization convention instead of re-implementing it
- self-review added two legs it was missing: exact counting under real
concurrency (the criterion the pool exists for), and pruning an
elapsed limiter row rather than resetting it, which otherwise leaks a
row per IP ever seen
- plan is code-free per house convention; the writing-plans skill wants
code blocks and the project rule overrides it
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit f079455755a189a86bb12e07cc11549ed7a78b91)
- supersedes Phases B and C as built: a store-after-completion
middleware cannot satisfy three of the story's seven criteria
- in-flight collision is undetectable (the row is written after the
handler ran, so concurrent duplicates both miss and both execute)
- the 10s in-flight heuristic is inverted: created_at is stamped at
store time, so it fires on legitimate fast replays and never on a
genuinely concurrent request
- "reused key, different body is refused" is unreachable while the
digest is folded into the key — nothing looks the bare key up
- design: sharded actor pool serializes per key, @table persists;
actors own volatile state, tables own durability. Inherited by
porch 2 and 3
- limiter joins the pool for exact counting, writes through instead of
delete+insert, keys on net.peer unless trust_proxy is declared, and
uses monotonic ticks for arithmetic but wall clock for the header
- idempotency blocks rather than answering 409: call parks the
duplicate until the owner reports. Digest becomes a column
- saturation fails closed with 503 for both: saturating the pool must
not become the limiter bypass
- records that the story's "time.after is still reserved" is stale;
spawn/send/call/monitor/time.after all landed
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
(cherry picked from commit fc09e94373db65837ff5eb620fec67bab02c1931)