Fix three control-plane durability gaps (0411) — and decline Temporal - #662
Merged
Conversation
added 6 commits
July 30, 2026 11:08
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Signed-off-by: xNet Test <test@xnet.dev>
Signed-off-by: xNet Test <test@xnet.dev>
Signed-off-by: xNet Test <test@xnet.dev>
Signed-off-by: xNet Test <test@xnet.dev>
Signed-off-by: xNet Test <test@xnet.dev>
Signed-off-by: xNet Test <test@xnet.dev>
Contributor
|
Preview removed for PR #662. |
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.
Implements exploration 0411.
The decision
No Temporal. Recorded as ADR-28. Temporal solves durable execution; xNet's hard problem is durable state. It cannot run on the device (Node-only deterministic sandbox, needs a cluster), and it must not run in
packages/hub— that would break the Charter §6 receipt "the hub is a single self-contained process" and fail the BATNA test. A standing prohibition is now inpackages/AGENTS.md.That left three real bugs the survey turned up, which this PR fixes without new infrastructure.
G1 —
provisionTenantwas an uncompensated sagaIf the AI-key mint failed after Cloud Run provisioned, the function threw with no
TenantRecordwritten: a billable service was orphaned, and the retry provisioned a second one. Stripe retries the webhook path, so it recurred on every redelivery.New
apps/cloud/src/saga.ts(~66 code lines) runs the steps with compensations. Failed compensations are reported viaSagaFailure.leakedResourcesrather than swallowed, and the cause message leads so the HTTP routes still surface what actually went wrong.G2 — background jobs scheduled off process uptime
setInterval+unref()meant a revision deployed at 02:59 skipped the 03:00 restore drill entirely — and since the drill only logs on failure, a skipped run was indistinguishable from a passing one.apps/cloud/src/jobs/schedules off stored completion time: overdue jobs run on the first tick of a fresh process, a second replica sees the lease and skips, and a failed run keeps the old completion time so it stays due. NewGET /internal/fleet/jobsreports staleness, so a job that silently stops is loud.G3 — rollout wave state lived in local variables
rollWaveaccumulatedpromoted/rolledBackinconstarrays, so a restart mid-rollout left the fleet split-version with no capturedpriorVersionto roll back to. Now checkpointed per tenant; a resumed run replays decided tenants instead of re-upgrading them.Verification
pnpm test— 11,395 passedpnpm typecheck— 99/99 cleanpnpm lint— 0 errorsapps/cloud. The G1 suite was mutation-checked: 4 of its tests fail against the pre-fix code.Two things to flag
[-]: auditing the live GCP project for pre-existing orphans needs credentials this branch doesn't have. The audit logic is built and unit-tested (orphan-audit.ts); only the "list live services" call is missing.🤖 Generated with Claude Code