Skip to content

fix(platform): address Cloudflare end-to-end deployment failures - #399

Merged
Brooooooklyn merged 8 commits into
mainfrom
codex/platform-e2e-0261
Oct 12, 2026
Merged

Brooooooklyn merged 8 commits into
mainfrom
codex/platform-e2e-0261

Conversation

@Brooooooklyn

@Brooooooklyn Brooooooklyn commented Oct 9, 2026 •

Copy link
Copy Markdown
Member

Problem

A live end-to-end test of the platform on Cloudflare found these failures:

void platform install + deploy on Cloudflare
 ├─ setup      Sandbox needs Containers + Cloudchamber Edit; the runtime token link did not ask for them
 ├─ static     standalone static/SPA deploy without a routing config dropped _headers and _redirects
 │             fallback rules did not run when the site had a custom 404.html
 ├─ email API  the domains app's use('*') became /projects/* → 501 on email routes of a core platform
 ├─ postgres   sslmode=verify-full URL → Cloudflare 400 "ca_certificate_id cannot be blank" → 502
 │             Hyperdrive query cache → a read right after a write could be stale for up to ~75 s
 ├─ installer  a Wrangler upload conflict printed only "(exit 1)"
 ├─ email      same-key retry of the same SDK message → false IDEMPOTENCY_CONFLICT
 ├─ MX text    the error told users to use a subdomain, which the platform always refuses
 └─ admin UI   team and invitation table rows were clipped

What this PR changes

Static routing

request ─► resolveStaticPath
             ├─ real file ──────────────────────────► 200
             └─ miss ─┬─ before: 404.html exists ───► 404 page (fallback skipped)
                      └─ now: fallback rule? ─┬─ target found ───► 200 target
                                              └─ target missing ─► 404 page

A standalone static/SPA deploy without a routing config now reads _headers and _redirects, like the native deploy.

Email idempotency

The SDK builds new MIME on each send, with a new random boundary:
  Content-Type: multipart/alternative;
   boundary="=_void_alternative_<random>"      ← folded onto a continuation line

before: only line 1 is normalized → digest differs on each retry → CONFLICT
now:    unfold (SP/HTAB only), normalize the token → same digest → replay
        changed content, sender or recipients still conflict

Inbound forward/reply uses the same digest. Each receipt runs its handler once, so deduplication does not change.

PostgreSQL / Hyperdrive

URL sent to the platform            main             this PR
sslmode=require&sslrootcert=<file>  require          default require (no TLS settings sent)
sslmode=verify-*                    CF 400 → 502     default require
sslmode=verify-*&sslrootcert=<any>  CF 400 → 502     default require
sslmode=disable                     400              400

void db set-url on an existing Hyperdrive → keeps the TLS settings already on it
  • Hyperdrive query caching is off on create and update, and each deploy turns it off for an existing project database, so reads see completed writes.
  • sslrootcert is not sent to Hyperdrive, as on main and in the native deploy. The URL is stored as given.
  • The PostgreSQL guide now says that Void-created Hyperdrive uses Cloudflare's default require mode.

Other fixes

  • The runtime token link adds Containers and Cloudchamber Edit when Sandbox is on. The credentials table lists both.
  • The custom-domain capability middleware applies only to /:id/domains routes.
  • Installer errors show the bounded, redacted Wrangler conflict reason.
  • The MX refusal recommends an unused domain apex or a platform shared address.
  • Admin team tables reset inherited container padding inside their scroll containers.
  • The Sandbox acceptance probe closes timed-out sockets. The Analog playground drops two links to missing assets.

Decisions

Decision Why
Send no Hyperdrive TLS settings New configs get Cloudflare's default require. void db set-url keeps TLS settings already on the Hyperdrive, such as a CA an operator set by hand. Hyperdrive's verify modes need an uploaded CA ID, which a URL cannot carry. Rejecting would fail PlanetScale and Neon URLs as pasted. Wrangler and the native deploy also send none.
Check Hyperdrive caching on each deploy (GET; PATCH only when on) Configs created earlier still cache reads. The PATCH needs no database password. The installer does the same read-then-PATCH for the platform's own Hyperdrive.
Ignore sslrootcert for Hyperdrive; do not reject it RDS, Supabase and similar providers tell users to add a CA file. Main accepted those URLs, and local pg needs the file. A rejection gave no production security: the only accepted URL makes the same require Hyperdrive.
Docs note, no CLI warning, for the TLS mode The routes return only { ok: true }. A warning would fire for every PlanetScale or Neon user, who cannot act on it.
List Containers and Cloudchamber in the core runtime token table Every packaged runtime enables Sandbox (sandboxDrain: 3), so a default install's token link already asks for both.
One digest algorithm, with no compatibility path for rows from 0.27.0 or earlier The SDK rebuilds the MIME on every call, so a retry of a text+HTML send from before the upgrade conflicts with or without such a path. Only a hand-sent, byte-identical resend would differ, and neither a conflict nor a replay sends mail. No upgrade step reads digests.
Run fallback rules before the custom 404 page This matches the routing docs and the native runtime.

Rollout

deploy dispatch     → existing static sites with 404.html + a fallback rule (e.g. /* → /index.html)
                      now serve the fallback (200) instead of the 404 page; no redeploy needed
deploy proxy + API  → retries of sends made after the upgrade replay; a retry of a text+HTML send
                      made before the upgrade still returns IDEMPOTENCY_CONFLICT, as on 0.27.0
apps on old SDKs    → keep working; the SDK→proxy request is unchanged and tenant Workers are not redeployed
existing Hyperdrive → caching turns off on the project's next deploy; `void db set-url` is not needed

Known limits

  • Both genuine Access identities omitted a verified-email claim. Safe email-invitation denial was confirmed. Invitation delivery and acceptance need a suitable genuine identity and remain untested. Team membership was exercised through supported user-ID administration.
  • Ordinary synchronous Sandbox and outbound-email requests can exceed the unchanged 10-second dispatch deadline. Streaming acceptance passed; that does not make the original synchronous paths pass.
  • The first outbound message was delivered while its request timed out, which left its historical attempt unresolved. Supported resolution becomes eligible at 2026-10-10T09:16:39.513Z. The record is preserved. Do not resend the message under a new key.
  • Per-domain secret rotation correctly refuses the installation-owned shared gateway. The installer still manages that secret.
  • This is evidence for the exercised paths, not certification of every feature combination. Early propagation failures remain recorded. Analog's unconfigured missing route returns 200, and matching conditional asset requests returned 200 rather than 304.
  • Not changed here: the installer still rejects private sslrootcert paths; Node pg reads sslrootcert=system as a file path; void dev request handlers run pg in workerd, which cannot read sslrootcert files; GET /projects/:id/usage still sits behind the managedBuilds capability on a core platform; rollbackSafeFrom has no 0.26.1 or 0.27.0 entry, so a rollback to those versions is refused; direct void deploy --platform cloudflare still creates Hyperdrive with query caching on (Direct Cloudflare deploys create Hyperdrive with query caching on #404). Other URL TLS gaps that are also on main are tracked in PostgreSQL/MySQL URL TLS options: gaps between Void's clients and Hyperdrive #405.

Testing

Live, on an isolated Cloudflare installation upgraded to 84706062d:

Area Result
Login Genuine administrator and second-user login, restricted signup denial, then allowed signup
Managed apps 23 apps passed 227 HTTP/protocol checks: raw static/SPA/API, custom SSR, four Pages adapters, static/Markdown/zero-JS/layout/ISR, Nuxt, Astro, SvelteKit, React Router, TanStack Start, Analog, both Vinext routers, WebSockets. 13 extra HEAD/compression/redirect/static assertions passed. Browser checks covered forms, hydration, navigation, two-tab SSE/WebSockets and notification acknowledgement/stop.
PostgreSQL 18 checks, including independent persistence, immediate reads after writes, and deletion
Teams and admin Role denials, collaborator deploy/rollback, secrets, scoped-token renewal/revocation, ownership transfer, suspension/restoration, quota denial/restoration. Team rows visible; the administrator session is preserved.
Access Protection/public overrides, canonical and retained aliases, new-project defaults, interrupted-operation reconciliation, global disable, provider cleanup that keeps the login configuration
Queues 7 acknowledgement/retry scenarios; the UI upgrade kept all queue bindings and consumer IDs
Sandbox All 12 groups through a streaming fixture: files, processes, PTY, cancellation, HTTP ports, Internet controls, isolation, restart
Email Plaintext, HTML and attachment delivery with SPF/DKIM/DMARC passing. An identical replay reused the operation and gave one Gmail copy; changed content conflicted. Invalid sender, unverified/mixed recipients, shared/custom inbound routes, threaded replies and genuine SMTP rejection bounces. Removing the custom domain kept shared delivery.

No additional subscriptions were enabled.

Local, at 0d9c8a1af (this branch merged with main at 2fc172db0, Release 0.27.0):

Command Result
vp run check 31 packages pass
vp run test (affected, full fallback) 708 files passed, 1 skipped; 13,788 tests passed, 30 skipped
Changed-file vp fmt --check and vp lint pass
cf 1.0.0-beta.14 output Same "Aborting the upload operation because of conflicts" and script-API warning text, and the same ■/▲ symbols, as beta.13. The installer diagnostic still matches.

The email tests show that a rebuilt SDK boundary replays without a second provider send or quota debit, that changed content conflicts, and that only SP/HTAB counts as folding whitespace. The real MIME builders of all 21 released void versions (0.20.0–0.27.0) were also run against the new digest: retries of text+HTML and inline-image mail replay, and every changed message conflicts. The commits after 84706062d were verified locally only. The live E2E job runs on pushes to main.

Hyperdrive follow-up commits (486c12fd7, edc53b5c3):

Check Result
vp run test --base 0d9c8a1af 208 files passed, 1 skipped; 4,238 tests passed
vp run check pass
Mutation checks each removed or changed behavior turns its tests red
Live read-only GET of Hyperdrive configs caching.disabled is in the response; configs created before this PR show false, which the deploy check turns off

Live run of the real cloudflare-api.ts functions against Cloudflare, with a temporary Neon PostgreSQL database and scratch Hyperdrives (all deleted afterwards):

1. Hyperdrive made like main (mtls require, caching on)
   1st deploy check:  GET + PATCH → caching off; TLS settings and origin unchanged; no password sent
   2nd deploy check:  GET only
2. createHyperdrive with ?sslmode=verify-full&sslrootcert=system
   → Cloudflare connects to the database; caching off; mtls {} (default require)
3. set-url on a Hyperdrive with verify-full and a CA set by hand
   this PR:        CA and verify-full kept
   before the fix: CA removed, Hyperdrive downgraded to require, no error returned

🤖 Generated with Claude Code

https://claude.ai/code/session_0123r5HnanmW2VUrZ7VWDufH

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

🚀 Deploying Preview to Cloudflare 🚀

Preview URL: https://codex-platform-e2e-0261-void-cloud.voidzero-docs.workers.dev (commit edc53b5)

This URL reflects your latest Preview deployment

Preview Deployments by commit

Status Deployment URL Commit Updated (UTC) See this deployment's details
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://54aa2022-void-cloud.voidzero-docs.workers.dev edc53b5 2026-10-12T06:23:04.775Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://3b8509f6-void-cloud.voidzero-docs.workers.dev 0d9c8a1 2026-10-10T12:31:03.505Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://f1606380-void-cloud.voidzero-docs.workers.dev f43edd5 2026-10-10T09:02:18.712Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://7e58fc9c-void-cloud.voidzero-docs.workers.dev 0fb0852 2026-10-10T08:40:48.740Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://96d66d51-void-cloud.voidzero-docs.workers.dev 0512376 2026-10-10T00:26:11.327Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://13964456-void-cloud.voidzero-docs.workers.dev 8470606 2026-10-09T15:42:43.862Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://dcbf13ef-void-cloud.voidzero-docs.workers.dev 50efd53 2026-10-09T15:14:42.680Z Visit the dashboard ↗
  • Build: Success ✅
  • Deployment: Success ✅

View logs ↗
https://4f722d47-void-cloud.voidzero-docs.workers.dev 25f0271 2026-10-09T06:52:38.969Z Visit the dashboard ↗

@Brooooooklyn
Brooooooklyn force-pushed the codex/platform-e2e-0261 branch from 25f0271 to 50efd53 Compare October 9, 2026 15:12
@Brooooooklyn Brooooooklyn changed the title fix(cli): include Sandbox runtime token permissions fix(platform): address Cloudflare end-to-end deployment failures Oct 9, 2026
@Brooooooklyn
Brooooooklyn marked this pull request as ready for review October 9, 2026 15:53
@Brooooooklyn
Brooooooklyn requested a review from a team as a code owner October 9, 2026 15:53
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

cpojer and others added 4 commits October 10, 2026 08:25
…-ups

- Stop rejecting sslrootcert file paths when Void creates Hyperdrive.
  Main accepted these URLs, providers such as RDS and Supabase document
  them, and the only accepted alternative made the same require-mode
  Hyperdrive. The old advice (sslrootcert=system) also broke Void's own
  pg clients with ENOENT.
- Document that Void-created Hyperdrive uses Cloudflare's default
  require mode.
- List Containers and Cloudchamber Edit in the core runtime token table;
  every packaged runtime enables Sandbox.
- Unfold MIME boundary headers on SP/HTAB only.
- Correct the reason given for the inbound digest algorithm.
- Cover legacyDigest through the signed admission route.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Remove the versioned digest and the second digest the proxy sent for
direct sends. The API compares one digest again, and inbound actions use
the same unfolding algorithm.

The SDK rebuilds the MIME, with a new random boundary, on every send. So
a retry of a text+HTML send recorded before the upgrade conflicts with or
without a second digest; only a hand-sent, byte-identical resend would
differ. A conflict and a replay both return before anything is sent or
counted, and no upgrade step reads digests.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@Brooooooklyn

Copy link
Copy Markdown
Member Author

@cpojer Thanks for 0512376. We decided to remove its digest compatibility path in 0d9c8a1: legacyDigest, the 'legacy' | 'current' digest versions, and the 'legacy' inbound digest. Here is why. Please say if you know a caller we missed.

What the path changed

row written by 0.27.0 or earlier + retry after the upgrade, same key:

  SDK retry (MIME rebuilt, new random boundary) → CONFLICT with or without the path
  exact same bytes posted to __VOID_PROXY        → REPLAY with the path, CONFLICT without it

Why we removed it

  • The project has had no public release yet, so we keep one digest algorithm instead of versioned ones.
  • The SDK rebuilds the MIME on every sendEmail call and never resends the same bytes. In 500 rebuilt retries, the path rescued 0. It only helps a hand-written call that posts identical raw bytes to __VOID_PROXY, within the 30-day ledger window.
  • A conflict and a replay both return before anything is sent or counted. So the removal cannot cause duplicate mail, lost mail, stuck operations or quota changes.
  • No upgrade step reads digests, and there is no schema change. A mixed proxy/API rollout is safe, because the API only compares strings.
  • Inbound: each receipt runs its handler once. Native receipts get a new ID on each run, and relayed receipts are claimed once. So one algorithm for inbound changes nothing.
  • We ran the real MIME builder of all 21 released void versions (0.20.0 to 0.27.0) against the unfolding digest. Text+HTML and inline-image retries now replay, and every changed message still conflicts.

What stays

  • The unfolding fix, which now strips only SP/HTAB folding whitespace.
  • The tests that rebuilt SDK boundaries replay without a second provider send or quota debit, and that changed content conflicts. The digest still covers the sender and recipients.

Visible effect: a retry of a text+HTML send made before the upgrade still returns IDEMPOTENCY_CONFLICT, as on 0.27.0. Retries of sends made after the upgrade replay.

Brooooooklyn and others added 2 commits October 12, 2026 14:10
…se URL

Bug:
When a database URL had an sslmode, the platform API sent Hyperdrive
TLS settings set to require. On `void db set-url`, or when it adopted
an existing Hyperdrive by name, this replaced TLS settings that an
operator set by hand, such as verify-full with a CA. Cloudflare does
not allow require with a CA, so the update could also fail with a 502.

Cause:
parseHyperdriveConnectionString mapped each accepted sslmode to an
mtls block. Both create (POST) and update (PUT) sent that block.

Fix:
Send no mtls block. A new Hyperdrive gets Cloudflare's default require
mode, which is the same Hyperdrive as before. A PUT keeps the TLS
settings that are already on the Hyperdrive. The API still returns 400
for disable, prefer and no-verify, before it calls Cloudflare. Remove
the mtls type field, variable and spread.

Test:
Unit tests check that no mtls is sent for each accepted sslmode, on
create and on update. New rows check that disable, prefer and no-verify
fail before fetch. Remove a create test that repeated the main create
test, and move its sslrootcert URL into that test. The route tests for
create and update compare the exact request body, now with a
verify-full URL in both.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0123r5HnanmW2VUrZ7VWDufH
… databases on deploy

Bug:
A PostgreSQL or MySQL project whose Hyperdrive was created before
caching was turned off still cached reads. Hyperdrive does not drop
cached reads when the app writes, so a read right after a write could
return old rows for up to about 75 seconds. Only `void db set-url`
fixed it, and that asks for the full database URL again.

Cause:
The platform sent `caching: { disabled: true }` only on create and on
update. A deploy reused the project's Hyperdrive id as it was and never
looked at its caching setting.

Fix:
Add ensureHyperdriveCachingDisabled. It reads the Hyperdrive config
and sends a PATCH with only `caching: { disabled: true }` when caching
is still on. The PATCH carries no origin, so no password is needed.
Each deploy of a project with an existing Hyperdrive calls it, and the
binding resolves only after it succeeds, so a Cloudflare error fails
the deploy at the provisioning step. Create and update still turn
caching off. The database guide now says an existing connection is
updated on the project's next deploy.

Test:
Unit tests check that a config with caching off gets one GET and no
write, and that a config with caching on gets a PATCH whose body is
only `{ caching: { disabled: true } }`. provisionBindings tests check
that an existing Hyperdrive row runs the check before it returns the
binding, and that a failed check fails the deploy.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0123r5HnanmW2VUrZ7VWDufH
@Brooooooklyn
Brooooooklyn removed this pull request from stack #401 October 12, 2026 08:38
@Brooooooklyn
Brooooooklyn merged commit ad7e6ca into main Oct 12, 2026
20 checks passed
@Brooooooklyn
Brooooooklyn deleted the codex/platform-e2e-0261 branch October 12, 2026 08:38
Brooooooklyn added a commit that referenced this pull request Oct 12, 2026
…en source (#400)

## Problem

Before open source, a review of the SDK, framework and CLI packages
(`packages/*`) found 737 cleanup issues of four kinds:

```
over tests      322   tests of repo tooling, tests for tests, duplicate tests
slop            241   needless defensive code, copies of helpers, pass-through wrappers, test-only seams
over comments   113   bug-fix history, essays, stale line references, comments that repeat the code
dead code        61   unused exports, branches, params, files
```

This PR removes them. It does not fix bugs or change behavior. The real
bugs from the same review go in separate PRs.

## What this PR changes

```
packages/*   620 files   +22.9k  −63.1k  (net −40k lines)
             ├─ tests    +15.3k  −43.7k
             └─ source    +7.6k  −19.5k

737 issues ──► 555 fixed
               117 partly fixed (the safe part only)
                56 skipped (public API, behavior change, or needs a new public export)
                 9 left for an owner decision
```

Larger items:

```
provision.ts      4,975 → 3,224 lines. A 440-line Proxy-safe redaction for a path that production
                  never used is now a small helper.
client.ts         2,479 → 1,918 lines. About 55 endpoints share one private request helper.
installer phases  11 get/set accessor pairs → one run-state object
config readers    duplicate Wrangler config readers and record guards → shared internal ones
tooling tests     10 SDK test files that tested repo scripts or test config → removed.
                  vp run check now typechecks root test/ and .github/scripts instead.
```

## Decisions

| Topic | Decision |
| --- | --- |
| Behavior | No reachable change. Return values, thrown errors and
messages, CLI output, exit codes, files written and network calls stay
the same. |
| Path patterns | `@void/edge` matches header, revalidate and routing
patterns with a small parser instead of a RegExp built from the pattern.
Results stay the same for every pattern the old code could run. Patterns
too large for V8 now match instead of failing, and catastrophic
backtracking is gone. |
| User-visible text | Kept as it is on main, even where the wording is
out of date (for example `void.json`, "Wrangler"). Wording changes need
their own PRs. |
| Public API | Every published export and documented feature stays. The
generated Pages client entry still exports `router`. |
| Deleted tests | Each one either tested tooling or config, or another
test still asserts the same behavior. Unique assertions moved into the
kept test. |
| Shared helpers | Only internal modules. No new public exports, and no
`packages/*` import from `platform/`. |

## Rollout

```
main ── #399 (Cloudflare e2e fixes) ── this PR (cleanup)
```

Stacked on #399, because both PRs touch the same installer and deploy
files. Merge #399 first.

## Known limits

- In one middle commit the typecheck fails until a later commit
(`test(cli): pass explicit access ...`). This matters only for bisect,
because the PR is squash-merged.
- Independent review rounds found 35 places where the cleanup changed
behavior or lost unique coverage. All 35 are restored here.
- The removed tooling tests leave three scripts without tests:
`.github/scripts/production-baseline.ts`,
`.github/scripts/schema-compat-tests.ts` (CI only) and
`packages/platform/scripts/source-revision.mjs` (build time, display
only).

## Testing

| Check | Result |
| --- | --- |
| `vp run build:all` + `node test/check-playground-build-artifacts.mjs`
| pass |
| `vp run check` (31 typecheck tasks) | pass |
| `vp lint`, `vp fmt --check .` | pass (2,477 files formatted) |
| `vp test run` (full suite) | 685 files, 12,942 tests pass (1 file, 30
tests skipped) |
| `vp run test --base <previous head>` (latest restores) | 13 files, 497
tests pass |
| Restored tests | Each one fails when its code is broken on purpose |

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_0123r5HnanmW2VUrZ7VWDufH

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Brooooooklyn added a commit that referenced this pull request Oct 12, 2026
## Problem

The pre-open-source review of the SDK, framework and CLI packages
confirmed 17 high-severity bugs. Each one breaks a documented command or
a common setup:

```
database   void db reset / seed / export on MySQL 8 · db reset after `void db connect` · TS seeds
init       pnpm build approvals lost · Windows paths with spaces
deploy     pinned-secret Cloudflare deploys · custom binding names on managed deploys
runtime    remote D1 sessions · MySQL migration breakpoints · MySQL auth migrations
adapters   React SSR errors hang · Solid remounts on every update · Svelte island CSS
config     BOM-prefixed wrangler.jsonc · Astro dev ignores src/middleware.ts for good
```

## What this PR fixes

One commit per bug. Each commit message has `Bug`, `Repro`, `Cause`,
`Fix` and `Test` sections for review.

The review found 17 bugs; this PR has 16 fixes. The Vite+ starter fix
(pnpm pinned with no build policy) was dropped: `main` now installs
Vite+ projects through `vp` and keeps the project's package manager
(328c186), so the bug no longer occurs.

| Area | What a user hits today | After this PR |
| --- | --- | --- |
| `void db reset` / `seed` | After `void db connect`, drops every table
in the deployed PostgreSQL/MySQL database with no prompt | Asks first on
a non-local host (`--yes` skips; localhost skips) |
| `void db reset` (MySQL 8) | Drops nothing, still prints "Database
reset" | Drops all tables |
| `void db seed` (TS) | Crashes on `void gen model` barrels after the DB
is already wiped | Loads extensionless imports |
| `void db export` (MySQL 8) | `Table 'app.undefined' doesn't exist` |
Exports every table; restore runs with FK checks off |
| `void init` (pnpm) | Rewrites the user's `allowBuilds` /
`onlyBuiltDependencies` | Merges into the user's list |
| `void init` (Windows) | `node` spawned through a shell breaks on paths
with spaces | Spawns `node` without a shell |
| `void deploy --platform cloudflare` | Pinned-secret deploys reject
`limits` / `placement` / `cache` | Uploads limits, smart placement and
cache with the version |
| Managed `void deploy` | Ignores custom D1/KV/R2 binding names | Sends
them, as `docs/reference/config.md` says |
| Astro dev | Leaves `src/middleware.ts` in `.gitignore` for good | Adds
the line only while dev runs |
| Remote mode (`remote: true`) | `void/db` and auth crash: no
`withSession` | Remote D1 binding supports sessions |
| MySQL migrations | Sends `--> statement-breakpoint` lines to MySQL |
Splits on breakpoints |
| MySQL auth migrations | Re-runs fail on MySQL 8 (uppercase info-schema
keys) | Idempotent on MySQL 8 |
| React Pages SSR | A page error hangs the request | Rejects → 500 page
or dev overlay |
| Solid Pages | Remounts page and layouts on every navigation and
deferred update | Keeps layouts; GET navigation re-creates the page
(same as Vue) |
| Svelte islands | Production ships raw, unscoped `<style>` CSS (or the
build fails) | Bundles the compiled CSS |
| Root `wrangler.jsonc` with BOM | Read fails, file treated as missing |
BOM is stripped, as Wrangler does |

## How each fix was made

```
verify   reproduce on the #400 head (failing test or script)
fix      regression test first, then the smallest fix, one commit
test     independent tester: the test fails with the fix reverted, affected tests, typecheck
review   adversarial review of the commit → a verifier checks each claim → fix-up if needed
```

## Decisions

| Topic | Decision |
| --- | --- |
| Reset on a remote DB | `void db connect` still writes the URL to
`.env`. Only reset/seed ask before they drop tables on a non-local host.
|
| Managed custom binding names | Projects that already set custom names
get new resources under those names on the next deploy. The old
`DB`/`KV`/`STORAGE` resources stay. |
| Pinned-secret deploys | Targeted placement (`region`, `host`,
`hostname`) still fails, now before any API request: Cloudflare's
version endpoint can drop the target, and an `--atomic` upload is live
at once. The error names the key and says what to do: remove
`cloudflare.placement`, or set it to `{ mode: 'smart' }`. `logpush` and
`tail_consumers` are accepted but not sent. No Cloudflare version upload
applies them, with or without secrets (same as `observability`). With
secrets, `--atomic` deploys and the first deploy after `void secret put`
also keep the live values. Without secrets, those two paths apply them
through `cf deploy`. |
| React SSR errors | Errors before the shell is sent reject the render.
Errors after the shell keep React's default client-render fallback. |
| Solid navigation | Same as Vue: GET navigation re-creates the page;
mutations, `router.refresh()` and `preserveState` keep it. |

## Known limits (not in this PR)

- Astro: after a hard kill (SIGKILL, crash), or in apps that ran dev
with an older Void, the `src/middleware.ts` line stays in `.gitignore`
until you delete it by hand.
- `void db seed`: the database is reset before the seed file loads, so a
seed that fails to load leaves an empty database. Base has this too.
- Remote D1 sessions: `withSession()` ignores constraints and bookmarks
(#403).
- Svelte islands: CSS of child components that render only on the server
is still missing.
- MySQL migrations: a failed multi-statement migration cannot resume.
Base has this too.

## Rollout

```
main ── #399 (Cloudflare e2e fixes) ── #400 (cleanup) ── this PR (high-severity bugs)
```

Merge #399 and #400 first.

## Testing

| Check | Result |
| --- | --- |
| `vp run build:all` + `node test/check-playground-build-artifacts.mjs`
| pass |
| `vp run check` | pass |
| `vp run test --base <#400 head>` | pass (480 files, 10,261 tests) |
| Windows CI (`test: windows`) | pass |
| Regression tests | One per bug; each fails when its fix is reverted |

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_0123r5HnanmW2VUrZ7VWDufH

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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.

2 participants