Repository navigation
item 6d (partial): the continuation store, and resumption stripping - #10
Merged
Merged
Conversation
SPEC-v0.2 §6.9.2's StateStore half and §6.2's resumption rule (T30b). §6.9's gateway-side held reservations are NOT here, for a structural reason recorded as a `# SPEC:` note in server.py and set out in the PR. hold_continuation / take_continuation live in their own `continuations` table rather than as new columns on `effects`: §2.2 records that v0.2 has no migration story, and CREATE TABLE IF NOT EXISTS gives an existing database the new table for free where ALTER TABLE would need one. The request_state is compared with hmac.compare_digest and consumed in the same transaction that admits the continuation, so one elicitation admits exactly one. Two continuations racing on one held key is the duplicate execution this product exists to prevent, wearing an inputResponses field as a disguise. The record checks come first, so an expired or resolved reservation is refused as what it is rather than as an unknown continuation. The held state survives a new store on the same file, which is the point: a gateway that restarted mid-elicitation would otherwise strand a reservation nobody can continue. legacy.py strips SSE id: fields from an intercepted response stream, line by line rather than event by event — the gateway must relay each event as it arrives, so it cannot wait for an event to end before deciding what to forward. Only id: is removed; event:, data:, retry: and comments pass through. EXECUTION_SUSPENDED and EXECUTION_RESUMED join the event set (§11), named for what happens to the attempt rather than for the transport that caused it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Last stage of item 6 — and it is deliberately incomplete. §6.9's gateway-side held reservations are not here. What is here is built, tested and green; what is missing needs a decision from you, set out below.
What ships
hold_continuation/take_continuation(§6.9.2, §11), in their owncontinuationstable rather than as new columns oneffects— §2.2 records that v0.2 has no migration story, andCREATE TABLE IF NOT EXISTSgives an existing database the new table for free whereALTER TABLEwould need one.The
requestStateis compared withhmac.compare_digestand consumed in the same transaction that admits the continuation, so one elicitation admits exactly one. Two continuations racing on one held key is the duplicate execution this product exists to prevent, wearing aninputResponsesfield as a disguise. The record checks run first, so an expired or resolved reservation is refused as what it is rather than as an unknown continuation. The held state survives a new store on the same file — a gateway that restarted mid-elicitation would otherwise strand a reservation nobody can continue.legacy.pyand T30b. SSEid:fields are stripped from an intercepted response stream, line by line rather than event by event: the gateway must relay each event as it arrives, so it cannot wait for an event to end before deciding what to forward. Onlyid:goes;event:,data:,retry:and comments pass through untouched.EXECUTION_SUSPENDED/EXECUTION_RESUMEDjoin the event set (§11).What does not, and why
§6.9.2 requires the effect record to stay
EXECUTINGwith no outcome written while the client answers the elicitation. That spans two HTTP requests, and therefore twoControl.executecalls.Control.executewrites a terminal outcome for any executor exception (v0.1 §5.5), and there is no way for an executor to say "suspend, record nothing". So aninput_requiredtoday falls to §6.8's unrecognized-resultTyperow and is recordedAMBIGUOUS— which is §6.9.3's fail-closed floor applied to every elicitation rather than only to the ones with norequestState. That is safe and it is not what §6.9.2 asks for.I built the executor branch for it and reverted it, because closing this needs one of two decisions and both are wider than §6.9's own text:
Controlgrows a resume path — an explicit way to hand a held reservation back to a secondexecute. Keeps ARCHITECTURE §6 intact; adds public API §11 does not list.Controlis the only module that composes the others", and duplicates the outcome handling that item 6c was careful not to duplicate.I'd take (1): the duplication in (2) is exactly the kind that drifts, and the asymmetry in
Control.executeis the one piece of logic in this codebase that must have a single implementation. But it is a public-API decision and a spec amendment, so it is yours.I stopped rather than guess, at the end of a long session, on a change that touches the kernel's composition rule.
Where that leaves v0.2
The gateway is usable today for every tool that does not elicit. A tool that does elicit gets an
AMBIGUOUSeffect per call — correct, and expensive.Verification
1098 tests pass (1082 → 1098),
mypy --strict src/,ruff checkandruff format --checkclean.No mutation table for this PR: the store half's mutations belong with the gateway half that consumes them, and running one over code whose consumer does not exist yet would be the false-green problem from item 5 in a new costume.