Skip to content

v0.2.0

  • v0.2.0
  • 2a4235c
  • Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
  • Choose a tag to compare

  • v0.2.0
  • 2a4235c
  • Choose a tag to compare

  • Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
@nicu-chiciuc nicu-chiciuc tagged this 05 Oct 23:34
* Convex.Project: never replace, retain by default, adopt only with --adopt

Deleting a Convex project deletes its deployments and data, so the
project resource now protects them:

- A name change renames the project in place with PATCH
  /projects/{project_id}. The id and slug stay the same.
- A team, deploymentType, or deploymentRegion change fails with
  ProjectTeamChange or ProjectDeploymentChange, which name the fix.
- The default removal policy is retain. Only RemovalPolicy.destroy()
  deletes the project.
- With state, reconcile uses output.projectId only. Without state, the
  name lookup reads every page, passes q, matches the exact name, fails
  with AmbiguousProject on two matches, and takes a project over only
  when adoption is on (else OwnedBySomeoneElse).
- The created project id comes from the create response.

Engine tests run the provider through Alchemy beta.80 against a fake
Convex API built from recorded payloads. The new fixtures were recorded
live on 2026-10-06.

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

* Deploy keys: unique names per resource, delete by secret only

Two resources with one key name stored the newest listed key, so a
delete could revoke the other resource's key. Now:

- Each DeployKey and PreviewDeployKey requests its name plus a 12 hex
  hash of the resource fqn and instance id, so two resources never
  request the same name.
- Before the create, a listed key with that name fails with
  DeployKeyRecoveryRequired. After the create, exactly one listed key
  must match, else the new key is revoked by its secret and the same
  typed error lists the candidate ids.
- State keeps the numeric key id. State from 0.1.x without the id still
  reads by listed name.
- Delete sends the secret as id, never a listed name. A key without a
  secret in state is left in place with a warning.
- Matching accepts the plain requested name: Convex now adds a
  " (uuid)" suffix only when the name is taken (verified live).
- Key deletes retry on a write conflict or a 5xx. Two parallel
  delete_preview_deploy_key calls returned 500 in a live run.

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

* Environment variables need --adopt to overwrite; preview deploys always push

- EnvironmentVariable and DefaultEnvironmentVariable: read returns
  Unowned for a value that exists with no state, and reconcile without
  state refuses an existing value unless adoption is on (this covers a
  rename, which the engine does not read in plan). An overwrite loses
  the old value, so it needs --adopt.
- Both retry the write on a write conflict or a 5xx, with exponential
  backoff, jitter, and at most four retries. The write is an idempotent
  set. Two parallel writes on one deployment returned 503
  OptimisticConcurrencyControlFailure in a live run; the fixture is that
  recorded body.
- Deploy: with previewName or previewCreate the diff always returns
  update, because an expired preview keeps the same hash.
- Deploy: whitespace in an argument and a missing deployed line are the
  typed errors DeployArgumentError and DeployOutputError, not defects.

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

* Live data-safety test, docs, and version 0.2.0

- test/live/safety.live.test.ts runs the safety contract through the
  Alchemy engine against a disposable tmp-alchemy-convex-<8 hex>
  project: create, adopt from fresh state, rename with PATCH, two keys
  with one name and delete by secret, parallel variable writes, removal
  with the default policy, and destroy with RemovalPolicy.destroy(). A
  fetch guard refuses to delete any project whose name does not start
  with tmp-alchemy-convex-, and cleanup deletes the project also after a
  failure.
- scripts/test-live.ts: a file argument runs that live file alone.
- README: a Data safety section, the 0.1 upgrade step, and the corrected
  resource notes. The example now gives the project its own logical id;
  it shared "Backend" with Convex.Deploy, and the engine returns the
  first resource for a repeated id.
- CHANGELOG entry and version 0.2.0. Fixture notes record the live
  facts from 2026-10-06.

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

* Deploy keys: never revoke on a failed create, refuse an OAuth pass-through key

With an OAuth token as the Management API credential, create_deploy_key
and create_preview_deploy_key create no key. They return the OAuth token
with a new prefix (documented in @convex-dev/platform 0.1.11). The
failed-create cleanup could then send that shared credential to the
delete endpoint.

- createTrackedKey no longer revokes anything. A listing mismatch fails
  with DeployKeyRecoveryRequired and leaves any new key for the user.
- After the create, a secret whose token part equals the credential's
  fails with DeployKeyIsCredential and is never stored.
- Delete skips, with a warning, a secret in state that is the
  credential itself (0.1.x state written under OAuth).

Found by the Codex review, round 1 (P1).

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

* Delete only verified keys; recheck ownership on every create retry

Codex review, round 2:

- P1: 0.1.x state can hold an OAuth token that Convex returned as a
  key. After a credential change, the credential comparison no longer
  matches, and a delete by that secret would revoke the OAuth token.
  Delete now runs only for a key with a keyId in state, which only a
  verified 0.2 create writes. A 0.1.x key stays, with a warning.
- P2: a stateless variable or default create checked once, then retried
  only the write after a conflict. A value that another writer created
  in between was then overwritten without --adopt. Each attempt of a
  stateless create now checks that the name is still free.

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

* Rename variables in place; check project type against the last good props

Codex review, round 3:

- P1: a variable rename was a replacement. A rename onto an existing
  name was refused after the engine recorded the replacement, and a
  revert then treated the original variable as foreign. Following the
  --adopt advice made replacement cleanup delete the live variable.
  EnvironmentVariable and DefaultEnvironmentVariable now have no
  replacement: a new name, deployment, project, or type is an update
  that writes the new value (with the ownership check) and then removes
  the old one that state names. A revert after a refusal is a plain
  update of the variable in state.
- P2: Project diff compared deploymentType and deploymentRegion with
  olds, which after a refused update are the refused props, so the
  corrective deploy failed too. The check now runs only in reconcile,
  before any API call, where olds are the props of the last successful
  deploy.

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

* Variables: resume a failed rename cleanup; delete with the key in state

Codex review, round 4 (two P2 findings in the rename-as-update path):

- When the new variable was written but the removal of the old one ran
  out of retries, reconcile failed and state kept the old identity. The
  next deploy then refused its own new variable. Now reconcile returns
  the new identity and keeps the old one in staleVariables
  (staleDefaults for defaults). A diff forces an update while that list
  is not empty, so the next deploy finishes the removal, and delete
  removes the stale entries too. A rename back to a stale name counts
  as ours.
- After a refused move, state keeps the props of the refused deployment
  and its key. Delete used olds.deployKey and sent that key to the
  first deployment. EnvironmentVariable now keeps the deploy key of its
  deployment in the attributes, and read and delete use the identity
  and key in the attributes. DefaultEnvironmentVariable read also uses
  the identity in the attributes.

The fake Convex API can now fail only matching request bodies and can
check deploy keys per deployment. The four new tests fail on ad86f54.

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

* Variables: a name or deployment change is a typed error, not a rename

Codex round 5 found two more corners in the rename-as-update path with
deferred cleanup (a revoked key kept for the stale cleanup, and drift
repair that drops the pending stale list). The coordinator decided to
remove that path instead of patching it.

- EnvironmentVariable: a change of name or deployment fails with
  VariableIdentityChange in diff, and again in reconcile for props that
  were unresolved in plan. DefaultEnvironmentVariable does the same for
  projectId, name, and deploymentType. The message says to create a new
  resource with a new logical id and remove this one.
- Removed staleVariables, staleDefaults, StaleVariable, DefaultIdentity,
  the stale-only diff, and the resume logic.
- Kept: value updates in place, Unowned adoption, the write-conflict
  and 5xx retries, and the deploy key in the attributes, which read and
  delete use. Attributes without a key (0.1.x) use the props key only
  when the props name the same deployment, else the variable stays with
  a warning.
- Tests: the stale-cleanup tests are gone. New tests: rename and move
  are VariableIdentityChange (also from reconcile with an unresolved
  name), a value change is one in-place write, delete uses the key in
  the attributes, and a 0.1.x variable without a known key stays.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Assets 2
Loading