feat(stack): replace remote runtime protocol with Effect RPC - #6303
feat(stack): replace remote runtime protocol with Effect RPC#6303jgoux wants to merge 20 commits into
Conversation
Supabase CLI previewnpx --yes https://pkg.pr.new/supabase/cli/supabase@530175d9162781fc9ce64b50abc7b418413bada7Preview package for commit |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2f99a0c82c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2639cfb636
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 530175d916
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return stats.size <= MAX_UNTRACKED_FILE_BYTES | ||
| ? { path, content: readFileSync(absolutePath) } | ||
| : { path, size: stats.size, mtimeMs: stats.mtimeMs }; |
There was a problem hiding this comment.
Hash large untracked files by content
When an untracked file exceeds 1 MiB, its build identity depends only on path, size, and mtime. Replacing that file's contents while preserving its size and timestamp—for example via a timestamp-preserving copy—therefore produces the same build ID even though the source checkout changed. A new CLI process can then attach to an older daemon as if it were the same build and issue RPCs across an incompatible runtime boundary; stream the file contents into the digest instead of substituting metadata.
Useful? React with 👍 / 👎.
| Effect.catchCause((cause) => | ||
| Effect.all([ | ||
| SubscriptionRef.set(errorRef, Cause.pretty(cause)), | ||
| SubscriptionRef.set(phaseRef, "failed"), | ||
| ]).pipe(Effect.asVoid), |
There was a problem hiding this comment.
Preserve interruption when the dashboard watcher shuts down
When the foreground scope closes normally, such as after the user exits or presses Ctrl-C, stack.dispose() closes the RPC scope and interrupts this forked watcher while the dashboard registry is still mounted. This unrestricted catchCause converts that expected interruption—and any defect—into a successful fiber that marks the dashboard failed, overwriting the normal stopping state and potentially rendering a spurious failure during teardown. Handle only expected stream failures and propagate interruption and defects unchanged.
AGENTS.md reference: AGENTS.md:L160-L165
Useful? React with 👍 / 👎.
| const existingLayer = yield* connectLayer({ | ||
| buildIdentity, | ||
| cwd: runtimeInfo.cwd, | ||
| cacheRoot: cliConfig.supabaseHome, | ||
| projectDir: projectHome.projectRoot, | ||
| name: stackName, | ||
| }); | ||
| yield* Effect.scoped(Effect.provide(Stack, existingLayer).pipe(Effect.asVoid)); |
There was a problem hiding this comment.
Allow branch switching to recover a stale running document
When a supervisor crashes after leaving stack.json with lifecycle: "running", resolveManagedStack returns that stale document and this new connectLayer call fails with NoRunningStackError because no ready control owner can be probed. The command therefore never reaches stopDaemon, which previously acquired ownership, cleaned up the stale runtime, and allowed the stack to restart for the selected branch. Restrict the compatibility preflight to a live owner or let this specific no-owner result continue into stale-owner recovery.
Useful? React with 👍 / 👎.
| waitForControlSessionEnd( | ||
| endpoint, | ||
| ownershipId, | ||
| ownerSessionId, | ||
| readControlOwnerStatus(endpoint, ownershipId, transport.read), | ||
| ), |
There was a problem hiding this comment.
Recognize a foreign rebind as completion of the old session
After the verified owner accepts the stop and releases its listener, another stack whose deterministic candidates collide can bind this endpoint before the next poll. readControlOwnerStatus then fails with ControlAddressConflictError before waitForControlSessionEnd can observe that the captured session is gone, so supabase stop or an upgrade replacement reports a failure even though its targeted owner already stopped. Treat a valid different-ownership response as completion of this captured session and let identity-scoped callers rescan their own candidates.
Useful? React with 👍 / 👎.
Summary
Linked issue
None.
Reviewer context
This intentionally has no legacy protocol window or compatibility adapter. Runtime RPC is a same-build boundary; owner discovery and session-fenced shutdown remain the stable cross-build protocol.