Skip to content

Fix three control-plane durability gaps (0411) — and decline Temporal - #662

Merged
crs48 merged 6 commits into
mainfrom
claude/0411-temporal-durable-execution
Jul 30, 2026
Merged

Fix three control-plane durability gaps (0411) — and decline Temporal#662
crs48 merged 6 commits into
mainfrom
claude/0411-temporal-durable-execution

Conversation

@crs48

@crs48 crs48 commented Jul 30, 2026

Copy link
Copy Markdown
Owner

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 in packages/AGENTS.md.

That left three real bugs the survey turned up, which this PR fixes without new infrastructure.

G1 — provisionTenant was an uncompensated saga

If the AI-key mint failed after Cloud Run provisioned, the function threw with no TenantRecord written: 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 via SagaFailure.leakedResources rather 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. New GET /internal/fleet/jobs reports staleness, so a job that silently stops is loud.

G3 — rollout wave state lived in local variables

rollWave accumulated promoted/rolledBack in const arrays, so a restart mid-rollout left the fleet split-version with no captured priorVersion to roll back to. Now checkpointed per tenant; a resumed run replays decided tenants instead of re-upgrading them.

Verification

  • pnpm test — 11,395 passed
  • pnpm typecheck — 99/99 clean
  • pnpm lint — 0 errors
  • 59 new tests in apps/cloud. The G1 suite was mutation-checked: 4 of its tests fail against the pre-fix code.

Two things to flag

  • LOC came in at 496, not the ~300 estimated — directly on the doc's own ~500-line T7 tripwire rather than below it. Recorded in the doc; any further growth in these four files should re-open the orchestrator question rather than be absorbed.
  • One checklist item is deferred, so the doc is marked [-]: 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

xNet Test 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>
@crs48
crs48 temporarily deployed to pr-662 July 30, 2026 18:11 — with GitHub Actions Inactive
github-actions Bot added a commit that referenced this pull request Jul 30, 2026
github-actions Bot added a commit that referenced this pull request Jul 30, 2026
@github-actions

github-actions Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Preview removed for PR #662.

@crs48
crs48 merged commit c2371bb into main Jul 30, 2026
19 checks passed
@crs48
crs48 deleted the claude/0411-temporal-durable-execution branch July 30, 2026 18:24
github-actions Bot added a commit that referenced this pull request Jul 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant