fix(codegen/vercel): make create-only required fields optional in global args (swamp-club #2644) #428

Merged
stack72 merged 9 commits from 2644 into main 2026-10-02 00:29:04 +00:00
Owner

Fixes swamp-club #2644.

Problem

Generated Vercel models marked every field that the create request requires as required in GlobalArgsSchema. swamp checks that schema in full in two places:

  • swamp model create with any --global-arg;
  • swamp workflow validate, for steps that name a model type rather than a definition.

So a model set up for get, lookup or sync was rejected unless callers passed placeholder create values. For example, @swamp/vercel/projects/env with only idOrName and name failed on key, type and value. As with Cloudflare (#2645), method runs validate with .partial(), so get on an existing definition already worked. The issue's claim that read methods fail was not accurate.

Triage also found a related bug. A resource property named slug collides with the injected team slug arg, so it was silently dropped from the schema and every request body. Edge Config, feature flag and segment creates could never send their required slug, and edge-config/global-config's PUT update sent an empty body.

Fix (Vercel codegen; mirrors the Cloudflare fix, PR 344)

  • Split in the pipeline: the create body's required list becomes createRequiredProperties, limited to defined properties and excluding parent path params. No resource property stays required; parent params and the synthetic name do.
  • Create check: every resource field is .optional(), and the generated create throws create requires global arguments: <names> before any API call.
  • PUT updates: create-required fields are always filled from the live resource, using the existing live fill (#2656), and update throws update requires global arguments: <names> before the PUT if one is still missing. PATCH is unchanged.
  • slug collision: a resource property named teamId, slug or token is exposed as resource<Name> (for example resourceSlug) and mapped back to its API name in bodies, checks and lookup filters. The team slug keeps its meaning, so existing definitions are unaffected. This affects 5 models: edge-config/global-config, feature-flags/flags, feature-flags/segments, teams/teams and environment/custom-environments.
  • Upgrade field names: upgrade entries list renamed fields under their global arg name. Without this, the next regeneration would report resourceSlug as removed and strip it from stored definitions. A test keeps the list in sync with the generated schema.
  • scripts/check_upgrades.ts: the upgrade path test took a manifest on the merge base as proof of publication, but @swamp/vercel/dns and @swamp/vercel/project-members have been on main since 2026-08-03 and were never published. It now asks swamp extension info. An extension the registry reports as not found is skipped, as the conventions already promise. Any other registry failure still fails the gate, and stdout and stderr are parsed separately. Because this is a trust-root change, review-integrity will run.
  • codegen/designs/vercel.md documents all of the above.

Regeneration

deno task generate:vercel changed exactly 27 of 30 models, each with one CalVer bump; a second run changed nothing. aliases, certs and domains are unchanged. 22 models get an identity upgrade entry and 5 get Added: resourceSlug; none removes a field. This merge also publishes dns and project-members for the first time.

Testing

  • Pipeline and generator unit tests: the split; optional fields; the sorted create check; the PUT fill and check; PATCH untouched; the rename in schema, bodies, checks, lookup and upgrade field names; parent params keep precedence.
  • codegen/vercel/globalArgs_integration_test.ts against a Deno.serve mock: create fails with zero requests; resourceSlug goes in the body while team slug stays in the query; the PUT fills from live, skips the GET when set, and fails before the PUT when the live resource lacks the field. A mutation check confirmed the tests fail without the checks.
  • Installed swamp binary: model create and a type-based workflow validate for projects/env fail before and pass after. Create throws the check message without making any request.
  • Vercel emulator (vercel-labs/emulate): the same scenario ran with the published @swamp/vercel/projects and with this branch via swamp extension source add. Results are identical except for the two intended changes: a scope-only env definition is accepted, and its create stops with 0 env vars created. The emulator does not cover Edge Config, feature flags, /v1/teams or custom environments, so the slug paths are tested against mocks only.
  • verify-build 14/14, including the upgrade path test: the published instances of 14 extensions upgrade to 2026.10.01.1, and the 2 unpublished ones are skipped. verify-reviews: code, adversarial and CI security reviews pass with low findings only. The attestation is posted for c566e6850.

Behaviour changes to be aware of

  • workflow validate no longer flags a create step that is missing create-required fields; the error appears at run time, still before any API call. swamp model type describe no longer marks those fields required.
  • A new definition without its naming field gets fallback instance names. Existing definitions are unaffected.
  • On the five models above, the resource's own slug is now resourceSlug.

🤖 Generated with Claude Code

Fixes swamp-club #2644. ## Problem Generated Vercel models marked every field that the create request requires as required in `GlobalArgsSchema`. swamp checks that schema in full in two places: - `swamp model create` with any `--global-arg`; - `swamp workflow validate`, for steps that name a model type rather than a definition. So a model set up for `get`, `lookup` or `sync` was rejected unless callers passed placeholder create values. For example, `@swamp/vercel/projects/env` with only `idOrName` and `name` failed on `key`, `type` and `value`. As with Cloudflare (#2645), method runs validate with `.partial()`, so `get` on an existing definition already worked. The issue's claim that read methods fail was not accurate. Triage also found a related bug. A resource property named `slug` collides with the injected team `slug` arg, so it was silently dropped from the schema and every request body. Edge Config, feature flag and segment creates could never send their required `slug`, and `edge-config/global-config`'s PUT update sent an empty body. ## Fix (Vercel codegen; mirrors the Cloudflare fix, PR 344) - **Split in the pipeline:** the create body's required list becomes `createRequiredProperties`, limited to defined properties and excluding parent path params. No resource property stays required; parent params and the synthetic `name` do. - **Create check:** every resource field is `.optional()`, and the generated `create` throws `create requires global arguments: <names>` before any API call. - **PUT updates:** create-required fields are always filled from the live resource, using the existing live fill (#2656), and `update` throws `update requires global arguments: <names>` before the PUT if one is still missing. PATCH is unchanged. - **`slug` collision:** a resource property named `teamId`, `slug` or `token` is exposed as `resource<Name>` (for example `resourceSlug`) and mapped back to its API name in bodies, checks and lookup filters. The team `slug` keeps its meaning, so existing definitions are unaffected. This affects 5 models: `edge-config/global-config`, `feature-flags/flags`, `feature-flags/segments`, `teams/teams` and `environment/custom-environments`. - **Upgrade field names:** upgrade entries list renamed fields under their global arg name. Without this, the next regeneration would report `resourceSlug` as removed and strip it from stored definitions. A test keeps the list in sync with the generated schema. - **`scripts/check_upgrades.ts`:** the upgrade path test took a manifest on the merge base as proof of publication, but `@swamp/vercel/dns` and `@swamp/vercel/project-members` have been on main since 2026-08-03 and were never published. It now asks `swamp extension info`. An extension the registry reports as not found is skipped, as the conventions already promise. Any other registry failure still fails the gate, and stdout and stderr are parsed separately. Because this is a trust-root change, `review-integrity` will run. - `codegen/designs/vercel.md` documents all of the above. ## Regeneration `deno task generate:vercel` changed exactly **27 of 30** models, each with one CalVer bump; a second run changed nothing. `aliases`, `certs` and `domains` are unchanged. 22 models get an identity upgrade entry and 5 get `Added: resourceSlug`; none removes a field. This merge also publishes `dns` and `project-members` for the first time. ## Testing - **Pipeline and generator unit tests:** the split; optional fields; the sorted create check; the PUT fill and check; PATCH untouched; the rename in schema, bodies, checks, lookup and upgrade field names; parent params keep precedence. - **`codegen/vercel/globalArgs_integration_test.ts`** against a `Deno.serve` mock: create fails with zero requests; `resourceSlug` goes in the body while team `slug` stays in the query; the PUT fills from live, skips the GET when set, and fails before the PUT when the live resource lacks the field. A mutation check confirmed the tests fail without the checks. - **Installed swamp binary:** `model create` and a type-based `workflow validate` for `projects/env` fail before and pass after. Create throws the check message without making any request. - **Vercel emulator (`vercel-labs/emulate`):** the same scenario ran with the published `@swamp/vercel/projects` and with this branch via `swamp extension source add`. Results are identical except for the two intended changes: a scope-only `env` definition is accepted, and its create stops with 0 env vars created. The emulator does not cover Edge Config, feature flags, `/v1/teams` or custom environments, so the `slug` paths are tested against mocks only. - **verify-build** 14/14, including the upgrade path test: the published instances of 14 extensions upgrade to `2026.10.01.1`, and the 2 unpublished ones are skipped. **verify-reviews**: code, adversarial and CI security reviews pass with low findings only. The attestation is posted for `c566e6850`. ## Behaviour changes to be aware of - `workflow validate` no longer flags a create step that is missing create-required fields; the error appears at run time, still before any API call. `swamp model type describe` no longer marks those fields required. - A new definition without its naming field gets fallback instance names. Existing definitions are unaffected. - On the five models above, the resource's own slug is now `resourceSlug`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
swamp checks the full GlobalArgsSchema on `model create` with any
--global-arg and on type-based `workflow validate`, so fields only the
create request needs blocked definitions meant for get, lookup or sync.

The pipeline now records them as createRequiredProperties (limited to
defined create properties, excluding parent params). Every resource
field is optional in GlobalArgsSchema; the generated create throws
"create requires global arguments: <names>" before any API call. A PUT
update always fills create-required fields from the live resource and
throws "update requires global arguments: <names>" before the PUT when
one is still unset. PATCH is unchanged. Mirrors the Cloudflare fix
(swamp-club #2645).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A resource property named teamId, slug or token was skipped because the
same name is injected for team scoping and auth, so it never reached
GlobalArgsSchema, the create or update body, or lookup filters. Five
models have their own slug: edge-config/global-config, feature-flags/flags
and feature-flags/segments cannot create without it, and global-config's
PUT update sent an empty body.

Such a property is now exposed as resource<Name> (resourceSlug) and
mapped back to its API name in request bodies, the create and update
checks, and lookup filters. The team slug, teamId and token args keep
their meaning. Parent path params still take precedence.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Loads a generated Edge Config-shaped model against a Deno.serve mock:
the schema accepts input without create-only fields and get runs; create
fails before any request; resourceSlug is sent as the body slug while
the team slug stays a query param; a PUT update fills the slug from the
live resource, skips the read when it is set, and fails before the PUT
when the live resource lacks it.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Upgrade entries diff the old GlobalArgsSchema field names against the
pipeline's list of new ones. That list used API names, so a property
exposed as resourceSlug was listed as slug: this run's entry missed the
added field, and the next regeneration would report resourceSlug as
removed and strip it from stored definitions.

The list is now built by globalArgsFieldNames using the generator's
globalArgName, with a test that it matches what the upgrade parser reads
from the generated schema.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Regenerated with deno task generate:vercel; a second run produced no
diff. 27 of 30 models change, each with one CalVer bump: every
create-only required resource field is optional in GlobalArgsSchema and
checked by create. edge-config/global-config, feature-flags/flags,
feature-flags/segments, teams/teams and environment/custom-environments
gain resourceSlug (upgrade entry: Added: resourceSlug), and
global-config's PUT update fills and checks it. aliases, certs and
domains are unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
check_upgrades.ts took a manifest on the merge base as proof an extension
was published, then pulled it. @swamp/vercel/dns and
@swamp/vercel/project-members have been on main since 2026-08-03 but were
never published, so the pull failed and the upgrade-path-test step
failed for any change that bumps them.

Before pulling, ask swamp extension info: an extension the registry
reports as not found is skipped with a reason, as the conventions
already promise for unpublished extensions. Any other registry failure
(network, auth, unparsable output, no latestVersion) still fails the
gate.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
run() joined stdout and stderr, so any notice on either stream broke the
JSON parse and failed the gate for every published extension (found by
the adversarial review). The CLI writes metadata to stdout on success
and the not-found error to stderr on failure, so registryTarget now
parses each on its own: stdout when the exit code is 0, stderr
otherwise. A not-found message with a zero exit is not trusted.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
docs(codegen/vercel): note that a secret-named create-required field is never live-filled (swamp-club #2644)
All checks were successful
CI / Validate Attestation (pull_request) Successful in 53s
CI / Review Integrity (pull_request) Successful in 1m42s
c566e68509
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
stack72 deleted branch 2644 2026-10-02 00:29:05 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
swamp-club/swamp-extensions!428
No description provided.