Prototype D: #116's declarative engine with retry removed#121
Open
kyleve wants to merge 2 commits into
Open
Conversation
…ative API Explores #116 (the typed LaunchPlan) minus retry, keeping the declarative surface that is the mark of that PR — LaunchPlan, the then/thenKeeping/gate/detached combinators, the LifecycleStep/ LifecycleGate protocols, nodeIDs, and the producing-step modes precondition are all untouched. Retry was the only thing that resumed a drive, so removing it deletes a cluster of runner machinery, not just a method: - Deleted the FailedResume struct + failedResume var (its only job was picking the resume site/index), the from-startIndex parameter threaded through drive/runLaunchPlan/walk (a re-drive now always walks from 0), the RetainedTeardown struct + retention (teardown runs inline once), and the injectFailureForTesting SPI. - The walk's site param goes too — it only tagged FailedResume; failures now just park .failed at the node's id. - The finding-1 cross-plan disjointness precondition (assertDisjointFromLaunchPlan) is gone: teardown clears the memo at its start (the launch attempt is over), so a teardown node may freely reuse a launch id — no shared-live-memo collision, because there is no retry re-walk to consult it. The single memo stays; it's promotion's requirement. - .failed is terminal: LifecycleFailureView loses its button (and the failure.launch.retry string), the container's failure closure drops its retry action, and LifecycleProxy + LifecycleDriving drop retry(). - Teardown-failure safety is preserved without retry: a thrown erase never reaches the session drop, so relaunching returns to the working app rather than a half-erased one. Tests: dropped the retry/resume unit tests, the injected-failure test, and the 120-seed retry-drain fuzz; kept the 200-seed model-parity fuzz. Added a terminal-failure test (a second run() doesn't re-drive), a teardown-reuses-launch-ids test (proving the precondition is unnecessary now), and reshaped the promotion run-once test off retry. UI/proxy tests updated for the terminal surface + enterForeground forwarding. Full Stuff-iOS-Tests scheme green; swiftformat --lint clean.
LifecycleKit README/AGENTS: retry removed from the API and lifecycle; the memo is reframed as promotion's requirement; failure (launch and teardown) is terminal; teardown starts from an empty run-once set so it may reuse launch node IDs (the finding-1 disjointness precondition is gone). LifecycleKitUI README/AGENTS: terminal failure surface, LifecycleFailureView loses its button, the proxy forwards only enterForeground/teardown. ./sync-agents run.
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.
Problem
A prototype for comparison, branched off #116 (the typed
LaunchPlan— the declarative combinator engine). It answers the same question we explored for the function-style engine in #120, now for the engine you'd actually keep with two more apps coming: #116 minus retry, keeping the declarative API intact.LaunchPlan, thethen/thenKeeping/gate/detachedcombinators, theLifecycleStep/LifecycleGateprotocols,nodeIDs, and the compile-checked data flow are all untouched — this only strips retry from the runner beneath them.Changes
Retry was the only thing that resumed a drive, so removing it deletes a cluster of runner machinery, not just a method:
FailedResumestruct +failedResumevar (its only job was picking the resume site/index), thefrom startIndexparameter threaded throughdrive/runLaunchPlan/walk(a re-drive now always walks from 0), thewalk'ssiteparam (it only taggedFailedResume), theRetainedTeardownstruct + retention (teardown runs inline once), and theinjectFailureForTestingSPI.assertDisjointFromLaunchPlanexisted because the shared memo was walked live by both launch and teardown (a retry could re-walk teardown against a populated launch memo). Without retry, teardown clears the memo at its start — the launch attempt is over — so a teardown node may freely reuse a launch ID with no collision. This is demonstrated by a newteardownNodesMayReuseLaunchNodeIDstest, replacing what used to be an untestable death-test precondition.enterForeground()re-walk must skip completed work), not retry's. That was the point worth confirming: the declarative API and its safety rest on promotion + the compile-checked plan, not on retry..failedis terminal:LifecycleFailureViewloses its button (and thefailure.launch.retrystring), the container's failure closure drops its retry action, andLifecycleProxy+LifecycleDrivingdropretry().What it buys, precisely
memo+RetainedTeardown+failedResume+from/sitethreadingmemoprecondition(finding 1)The parallel to #120 (the function-style, no-retry prototype) is exact: removing retry shaves ~250 lines and a quarter of the runner's bookkeeping in both engines, but leaves the engine's conceptual weight (the memo, the walk, promotion) intact — because that weight is promotion's, not retry's. The difference is that here the declarative combinator surface — the mark of #116 — is fully preserved.
Testing
Dropped the retry/resume unit tests, the injected-failure test, and the 120-seed retry-drain fuzz suite; kept the 200-seed model-parity fuzz. Added a terminal-failure test (a second
run()after a thrown node doesn't re-drive), theteardownNodesMayReuseLaunchNodeIDstest, and reshaped the promotion run-once test off retry. UI/proxy tests updated for the terminal failure surface andenterForegroundforwarding. FullStuff-iOS-Testsscheme green (against a freshly-erased, pre-booted iPhone 17 / iOS 26.2 sim by UDID — the shared runner host's "preflight Busy" / "server died" flakes needed a clean boot of the exact target);./swiftformat --lintclean.Recommendation context
With two more apps planned, this is the concrete form of my standing recommendation: the reusable declarative engine of #116, with retry removed — same compile-checked
LaunchPlanAPI app authors would learn once, terminal failure, ~250 fewer lines and the finding-1 precondition retired. #120 is the same simplification applied to the function-style engine (#117); this is it applied to the combinator engine. Pick the rung by whether you value the combinator API's static introspection (nodeIDs, parity tests) over the function style's readability — retry is off the table either way.