# Updating an app

Moving an existing app onto a newer SDK with the read-only `app update` guide.

An app's worker code is a copy, scaffolded once and owned by you. [`app update`](/cli-reference/commands#app-update) inspects that copy and returns a checklist for the running CLI's SDK release. It never edits the app: you choose and apply every dependency, source, and policy change.

## The sequence

Ask the target CLI for guidance

```bash
npx deepspace@latest app update --json
```

Run the **target** CLI, not only the version the app has installed. Its own package version is the target, and it owns that release's migration guidance.

Read the whole checklist

Inspect `status`, `dependencies`, `migrations`, `manualInstructions`, and the ordered `steps`. The command always reports `writes: []`: it does not rewrite `package.json`, stamp migrations, install packages, or scan unrelated repository files.

Apply and validate the app-owned changes

Make each dependency edit and migration change that applies to this app. Install only after reviewing the dependency plan, then type-check and test:

```bash
npm install
npm run type-check
npx deepspace test run
```

After a migration passes, add its id to `deepspace.migrations.json`. If the named seam does not exist in this app, record the id as not applicable so a later run does not repeat it.

Review, commit, and redeploy

```bash
git diff
git commit
npx deepspace deploy
```

A deploy is what makes the updated dependency and app-owned code reach the live app. A deploy guard may still refuse an incomplete retrofit; fix the stated cause rather than marking its migration complete early.

## Reading the result

| Field                                                | Meaning                                                                                                                                                                                                                                                                              | What you do                                                                                            |
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------ |
| `status` / `ready`                                   | `aligned` is the only ready state. `guidance_available` means work is listed; `dependency_unverified` means a local/VCS SDK is developer-owned; `cli_version_behind` means this CLI is older than the app; `version_gap_too_wide` means the releases must be reviewed one at a time. | Branch on the status, then read `steps`. These are successful guidance states, not partial writes.     |
| `dependencies: [{ dependency, from, to }]`           | Manual edits needed in `package.json`. A published SDK also names the compatible direct `ai` dependency when the app declares one.                                                                                                                                                   | Make the edits together, review the manifest, then run the detected package manager's install command. |
| `migrations: [{ id, description, files, guidance }]` | Outstanding release changes according to the app-owned ledger. `files` are the seams to inspect, not files the CLI searched or changed.                                                                                                                                              | Apply the guidance where relevant, validate it, then add the id to `deepspace.migrations.json`.        |
| `manualInstructions`                                 | A choice or verification the CLI cannot safely make, such as an app-owned users-schema visibility policy or an unverifiable local SDK.                                                                                                                                               | Decide explicitly; never treat an empty write set as permission to skip it.                            |
| `steps`                                              | The ordered human checklist assembled from the fields above.                                                                                                                                                                                                                         | Follow it in order and commit only after type-checking and testing.                                    |
| `writes`                                             | Always `[]`.                                                                                                                                                                                                                                                                         | Use your normal editor and review workflow; there is no apply mode or install action.                  |

A malformed `package.json` or `deepspace.migrations.json` refuses with `invalid_package_manifest` or `invalid_migration_manifest`. The command will not repair either source of truth. A version gap that is too wide returns `ok: true`, `ready: false`, and `status: "version_gap_too_wide"`; move through the intervening releases' [changelog](https://deep.space/changelog) rather than asking one release checklist to cover the whole history.

## Scaffolds that predate server-minted ids

Older SDKs minted `DEEPSPACE_APP_ID` locally, at scaffold time, and no server ever registered it. Ids are server-minted at registration now, and an existing unregistered id **cannot be claimed**. Such an app shows up in one of two ways on the newest CLI:

* `deploy`, `push`, or `secrets` refuse **`app_not_registered`** - the id no server has registered.
* `app init` refuses **`app_not_registered`** too, instead of the old "already initialized", and ships the fix as its `action`.

The fix is the same in both cases:

```bash
npx deepspace@latest app init --new-id
```

This registers the directory as a fresh app under your account: a new id in `wrangler.toml`, new data, new secrets store, and new registration. There is no old platform state under the unregistered id. Commit `wrangler.toml` (the command offers that commit as its next action when needed), then run `app update` again.

Two related refusals from the same era are `app_id_env_mismatch` (the browser bundle still carries an id frozen at scaffold time in `src/constants.ts`) and `app_id_define_unsubstituted` (constants moved to the build-time define without the `vite.config.ts` half). The deploy refusal prints the three-file retrofit, and `app update` reports the matching `2026-08-build-injected-app-id` guidance. Apply all three parts, validate them together, then record the migration id.

`app update` never registers, deploys, or mutates the checkout. Registration is `app init`; shipping is `deploy`. If the app also needs a secrets config it never had, `deploy` refuses `secrets_config_missing` with the `secrets configs create` action - see [missing is not empty](/guides/secrets#missing-is-not-empty).

## Next steps

* [`app update` reference](/cli-reference/commands#app-update) - the current fields and statuses.
* [Refusal codes by command](/cli-reference/overview#refusal-codes-by-command) - malformed-manifest refusals and the rest of the CLI contract.
* [App identity](/guides/app-identity) - what an id is, and why `--new-id` is a fork.
* [Changelog](https://deep.space/changelog) - what changed in each SDK release.
