Prototype C: Prototype A with retry removed#120
Open
kyleve wants to merge 2 commits into
Open
Conversation
Explores what removing retry buys, branched off Prototype A (#117). Retry's original customer — the fresh-install store-open race — was fixed structurally by injection (#99/#116), so on-device local work that fails is either terminal or belongs to a layer that retries internally; a launch-level Try Again is redundant. Removing it is more than deleting a method, because retry was the only thing that *resumed* a drive: - Deleted from the runner: retry(), the FailedSite tracking (its sole purpose was picking which function to re-run), the RetainedTeardown struct + retained-teardown var, the separate teardownMemo, and the injectFailureForTesting SPI. Teardown now runs inline once through a throwaway memo — no retain, so the release-on-success dance for the captured session is gone too. The launch memo stays: it's promotion's requirement (an enterForeground re-run must skip completed work), not retry's. - .failed is now terminal. LifecycleFailureView drops its button (and the failure.launch.retry string); the container's failure closure loses its retry action; LifecycleProxy and the LifecycleDriving seam drop retry(). An unregistered-gate still fails onto that terminal surface. - Teardown-failure safety is preserved without retry: a thrown erase never reaches the session drop, so state stays intact and relaunching the app 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 suite; the 200-seed model-parity fuzz stays. Added a terminal-failure test (a second run() doesn't re-drive) and reshaped the promotion run-once test to not lean on retry. UI/proxy tests updated for the terminal failure surface + enterForeground forwarding. Full Stuff-iOS-Tests scheme green; swiftformat --lint clean.
Plan step: noretry-docs-pr (docs half). LifecycleKit README/AGENTS: retry removed from the API and lifecycle; the memo is reframed as promotion's requirement (not retry's); failure (launch and teardown) is terminal; teardown runs once through a throwaway store. LifecycleKitUI README/AGENTS: the failure surface is terminal, LifecycleFailureView loses its button, the proxy forwards only enterForeground/teardown. ./sync-agents run.
This was referenced Jul 22, 2026
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 Prototype A (#117). It answers the question from the design discussion: now that "invalid states are unrepresentable" (the store-open race that retry once caught is fixed structurally by injection in #99/#116), how much does removing retry simplify the async-function engine?
Changes
Retry is gone, and because it was the only thing that ever resumed a drive, removing it deletes a cluster of machinery — not just a method:
retry(), theFailedSitetracking (its only job was picking which function to re-run), theRetainedTeardownstruct + retained-teardown var, the separateteardownMemo, and theinjectFailureForTestingSPI. Teardown now runs inline, once, through a throwaway memo — no retain, so the release-on-success dance for the captured session is gone too.enterForeground()re-run must skip completed work), not retry's — the point I'd flagged in the design chat, now demonstrated: stripping retry doesn't touch it..failedis terminal.LifecycleFailureViewloses its button (and thefailure.launch.retrystring); the container'sfailureclosure drops its retry action;LifecycleProxyand theLifecycleDrivingseam dropretry(). An unregistered gate still fails onto that terminal surface.What it buys, precisely
launchMemo+teardownMemo+RetainedTeardown+failedSitelaunchMemoThe honest read: it's a clean, real simplification (a quarter of the engine's bookkeeping was retry-only), but it confirms rather than overturns the earlier analysis — the memo, the one-ID-one-call-site discipline, and the promotion re-run machinery are all promotion's, so the conceptual weight of the engine is barely reduced. The visible win is the terminal failure surface and the fire-once teardown; the fuzz coverage drops by the retry-drain suite.
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 step doesn't re-drive) 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 pre-booted iPhone 17 / iOS 26.2 sim — the shared runner's "Application failed preflight checks" flake needed booting the exact target by UDID);./swiftformat --lintclean.Recommendation context
With two more apps planned, my standing recommendation is still Prototype A's reusable engine over B (Where-only). This branch is the sharper form of that recommendation: A with retry removed — same reusable async-function engine, terminal failure, ~250 fewer lines. If you want a launch-level "Try Again" back for a specific app later, it's re-addable; the point of this branch is that nothing in the current app needs it.