Skip to content

feat(cli): opt the vercel target out of vercel.json reconciliation - #697

Merged
blove merged 2 commits into
mainfrom
blove/nice-zhukovsky-ff4229
Sep 17, 2026
Merged

blove merged 2 commits into
mainfrom
blove/nice-zhukovsky-ff4229

Conversation

@blove

@blove blove commented Sep 17, 2026

Copy link
Copy Markdown
Contributor

Closes #681

What

Adds build.vercel.reconcileVercelJson (default true) to B4Config. With it set to false, the vercel target no longer requires, writes, inspects, or reports an app-root vercel.json. A prebuilt flow (vercel deploy --prebuilt from CI, no Vercel Git integration) never runs the buildCommand that file exists to carry, so the file was only there to satisfy the reconciler.

export default { build: { targets: ["vercel"], vercel: { reconcileVercelJson: false } } }

b4 build prints one stdout notice when the opt-out is active and drops vercel.json from the artifact list. Fluid compute guidance is unchanged: the docs still tell the reader to keep it enabled in the Vercel project.

Validation

b4 check and b4 build share one validator that rejects a non-boolean value with B4_E1003. The emitter reads the flag with !== false, so a mistyped "false" would otherwise keep reconciliation on while looking configured.

One note on the issue text: as written today, b4 check never reads vercel.json; the reconciler runs in b4 build, creates the file when missing, warns when an authored file differs, and fails only on fluid: false. The opt-out is wired where the behaviour lives, and the check-side tests guard against a future check-time gate ignoring it.

Tests

  • vercel-target.test.ts: opt-out emits the full Build Output tree with no root or reference vercel.json and no vercel.json in the report; an authored { "fluid": false } is left unread and untouched under the opt-out; reconcileVercelJson: true keeps the reconciled contract.
  • build-targets.test.ts: check passes with the opt-out and no committed vercel.json; check and build both reject a non-boolean value with B4_E1003, and build does so before creating .vercel/.

Locally: lint, core + cli typecheck, check-docs, core tests (533), the eight cli test files touched by or timing out in the full run (211, all pass in isolation; the full-suite failures were 5s timeouts under concurrent load), web tests (630 pass; api-reference-inventory's 60s spawn timeout tripped on this loaded machine while the underlying script exits 0 with zero failures in 74s wall / 24s CPU).

Docs

  • docs/cli: new vercel target bullet covering artifacts, the vercel.json contract, and the prebuilt opt-out.
  • docs/configuration: build.vercel.reconcileVercelJson in the build key reference; schema-path pin updated in scripts/check-docs.mjs.
  • SEO lastmod manifest regenerated for the two changed pages.

Changeset: patch for @b4run/cli and @b4run/core. Not releasing.

🤖 Generated with Claude Code

@vercel

vercel Bot commented Sep 17, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
b4-run Ready Ready Preview Sep 17, 2026 4:19pm UTC

Request Review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated approval: this PR received an intelligent (AI) code review. See the review comments on this PR.

@blove

blove commented Sep 17, 2026

Copy link
Copy Markdown
Contributor Author

The advisory review job could not run on this PR: its preflight failed with HTTP 400, Your credit balance is too low to access the Anthropic API. That is the recurring org-billing outage, not a signal about this diff, and it needs a top-up to clear. Only validate is a required check, so it does not block the merge, but it does mean nothing automated reviewed this.

I substituted an independent adversarial review of the diff, and it found a real fail-open in my own change, now fixed in a58f521.

The defect. The validator checked only the type of the reconcileVercelJson leaf. Because the emitter treats anything other than exactly false as "reconcile", three configs read as configured while the target kept reconciling and writing vercel.json into the repo:

  • build: { vercel: true } — the optional-chain read yields undefined
  • build: { reconcileVercelJson: false } — correct flag, missing the vercel nesting
  • build: { vercel: { reconcileVercelJSON: false } } — case typo

Nothing else caught these: the config path applies no runtime schema, and a b4.config.js author gets no compiler help. The doc comment asserting the opt-out "cannot look configured while doing nothing" was false for exactly these inputs.

The fix. resolveVercelBuildConfig now validates the shape and returns the boolean the emitter consumes, so the validated shape and the honored value cannot diverge. It rejects a non-object vercel, unknown keys inside it, and the misplaced flag, each with B4_E1003 and guidance. Six new tests cover these; all six fail against the previous validator, so they bind.

Two honesty fixes the review also caught. The opt-out notice claimed the "committed Vercel project settings own buildCommand" — project settings are Dashboard state, not committed, and a prebuilt deploy runs no buildCommand at all. And the opt-out silently retired the fluid: false build failure, which the docs now state. The check-side test is also renamed: b4 check never reconciled vercel.json, so that case is a forward guard, not evidence the opt-out works.

Also rebased onto main, which had typed build.targets as a BuildTargetName union in the same files.

blove and others added 2 commits September 17, 2026 09:16
Add `build.vercel.reconcileVercelJson` (default `true`). With it set to
`false`, the `vercel` target no longer requires, writes, inspects, or
reports an app-root `vercel.json`: a prebuilt flow (`vercel deploy
--prebuilt` from CI with no Vercel Git integration) never runs the
`buildCommand` that file exists to carry. Fluid compute guidance is
unchanged for the deployed project.

`b4 check` and `b4 build` share one validator that rejects a non-boolean
value with B4_E1003, because the emitter reads the flag with `!== false`
and a mistyped `"false"` would otherwise keep reconciliation on silently.

Closes #681

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Review of the opt-out found that validating only the leaf type let three
configs read as configured while the target kept reconciling and writing
`vercel.json`: a non-object `build.vercel` (whose optional-chain read
yields `undefined`), the flag misplaced directly on `build`, and a
case-typo key. The config path applies no runtime schema, and a
`b4.config.js` author gets no compiler help, so nothing else caught them.

Replace the assertion with `resolveVercelBuildConfig`, which validates the
shape and returns the boolean the emitter consumes, so the validated shape
and the honored value cannot diverge. Rejects a non-object `vercel`, an
unknown key inside it, and the misplaced flag, each with guidance.

Also correct the opt-out notice, which claimed the "committed Vercel
project settings own buildCommand" — project settings are Dashboard state,
not committed, and a prebuilt deploy runs no buildCommand at all. Document
that the `fluid: false` build failure is dropped under the opt-out, and
that both commands enforce the option, not just `b4 check`.

Name the check-side test for what it proves: `b4 check` never reconciled
`vercel.json`, so that case is a forward guard, not evidence the opt-out
works. The six new validation tests all fail against the previous loose
validator.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@blove
blove force-pushed the blove/nice-zhukovsky-ff4229 branch from a58f521 to 1f72130 Compare September 17, 2026 16:18
@blove
blove merged commit 10a6cbb into main Sep 17, 2026
29 of 30 checks passed
@blove
blove deleted the blove/nice-zhukovsky-ff4229 branch September 17, 2026 16:34
blove added a commit that referenced this pull request Sep 17, 2026
Adopts #697's build.vercel resolver instead of the parallel one this
branch added:

- functionName moves into resolveVercelBuildConfig beside
  reconcileVercelJson, so one resolver validates the whole build.vercel
  shape and b4 check/b4 build reject a bad name through the path that
  already rejects a bad flag. The separate check.ts hook is gone.
- The emitter reads ctx.buildConfig (#697) rather than the ctx.config
  field this branch had added, and resolves both options in one call.
- types.ts, the config reference, and the CLI page carry both options.
- #697's new prebuilt test asserted the old index.func path; updated,
  plus a test that both options are honored together.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
blove added a commit that referenced this pull request Sep 17, 2026
The build target context carries buildConfig from #697, so the config
field this branch had added is gone. Spreads skip excess-property
checks, so the leftover spread compiled silently.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
blove added a commit that referenced this pull request Sep 17, 2026
…cing

Two more vercel-target changes landed while this branch was open, and
both contradicted the guide:

- #697 closed #681 by adding `build.vercel.reconcileVercelJson`. The
  guide said flatly that there is no opt-out. It now documents the flag,
  what `b4 build` stops doing when it is off, the four `B4_E1003` near
  misses that are rejected rather than read as configured, and that
  Fluid must then be enabled in the project settings because nothing
  checks it from source.
- #699 added `B4_PG_SCHEMA` and `B4_PG_TABLE_PREFIX`. The guide said a
  generated app requires an app-dedicated database. It now carries the
  `B4_PG_SCHEMA=$VERCEL_ENV` recipe, which is the Vercel-shaped reason
  the feature exists, and links to the edge page's table for the rest.

cli.mdx conflicted and takes main's `vercel` bullet, which already
covers the opt-out, with a link to this page appended.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
blove added a commit that referenced this pull request Sep 18, 2026
…721)

NATIVE_REMOTE_BUILD_SIGNATURE is consumed as an exact-ordered, all-present
signature, and since #697 its last entry, `wrote vercel.json`, is conditional:
the vercel target drops the root config from its emitted artifacts when
build.vercel.reconcileVercelJson is false, which is the prebuilt-CI
configuration.

- parseNativeBuildProvenance takes reconcilesVercelJson (default true, matching
  the target's default) and follows it in both directions: the marker is
  required when reconciliation is on and must be absent when it is off, since
  reconciliation that happens anyway means the opt-out never reached the builder
- a signature mismatch names the offending marker and how it offended (missing,
  duplicated, out-of-order, unexpected) instead of reporting only "native Vercel
  source build provenance is incomplete"
- the prebuilt branch still asserts the absence of the full marker list

Test-only; no package behaviour changes.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

This branch was successfully deployed

1 active and 1 inactive deployments
Preview — 1f721304 Deployed Sep 17, 2026 by vercel[bot]
vercel-preview — 1f721304 Deployed Sep 17, 2026 by blove via vercel-native #1952
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.

vercel target: opt out of vercel.json reconciliation for prebuilt deploys

1 participant