# 28.x → 29.0.0, entry by entry

**Two entries** — `CHANGELOG.md`'s 29.0.0 breaking entries in their order, grouped by
package, lowest tier first. No legacy path, no codemod, no shim. **Entry 1 is the whole upgrade for
almost every app**: Bun 1.4.4 everywhere your app runs — laptop, CI, image. A later slice appends
its rows below the last one and never renumbers; each row's `CHANGELOG.md` line names it as `(#N)`
after the dash.

Why 1.4.4 is a floor: the floor names the one patch the release was tested on, and every pin site
moves with CI's (`scripts/bun-pin.test.ts`). Nothing in the framework needed 1.4.4 to compile; the
edits are your app's pins — plus two things 1.4.4 itself moves, which step 3 and 4 cover.

### The upgrade, top to bottom

| # | Do | What you see until you do | Entries |
|---|---|---|---|
| 1 | install Bun 1.4.4 (`bun upgrade`, or `curl -fsSL https://bun.sh/install \| bash -s bun-v1.4.4`) and pin it where your app pins Bun: `.bun-version`, `packageManager`, `engines.bun` (`>=1.4.4`), CI's `setup-bun` `bun-version`, every `oven/bun:` base image (`1.4.4-alpine` / `1.4.4-slim`, by digest) | `X_BUN_VERSION` from `x` and `x doctor` on 1.4.0–1.4.3 | 1 |
| 2 | pin every `@ultimat3/*` to the one new version, `bun install` | nothing yet — a mixed install is untested | — |
| 3 | an app that reaches `@ultimat3/*` through `file:` folder dependencies or overrides (a local checkout of the framework) under `linker = "hoisted"`: drop the forced linker and install with Bun's default isolated one. Under the hoisted linker 1.4.4 copies a `file:` folder per dependent instead of symlinking it, so each `@ultimat3/*` package carries its own `@ultimat3/core` and a primitive registers into the wrong one | `X_REGISTRAR_MISSING` from `x` at boot, `bun run setup`, `x db gen` | — |
| 4 | `x verify` — if `unit` reports `X_COVERAGE_BELOW_FLOOR` on functions with no test or source changed, 1.4.4's `--coverage` counts fewer functions and keeps the uncovered ones: re-measure and set `x.verify.json`'s `coverage.funcs` to the new figure, the reason in its `why` | green, or a finding whose `fix:` is the edit | — |
| 5 | **before the deploy,** read every worker Deployment that sets `WORKER_QUEUES`: each entry must be in `jobs.queues` or be a registered job's queue (`x jobs list --json`). Write patterns out (`banks-*` is a literal name, never expanded); add a queue only `enqueue({ queue })` reaches to `jobs.queues` | `X_WORKER_QUEUE_UNKNOWN` at the worker's boot, the new pods crashing and the roll stalled on the old ones | 2 |

### Entry by entry

Every package (1). Tier 5 — `@ultimat3/cli` (2).

| # | Surface | Costs you an edit if |
|---|---|---|
| 1 | `engines.bun` `>=1.4.4`, `REQUIRED_BUN`, the scaffold's images | you run any Bun below 1.4.4 anywhere: `x` and `x doctor` refuse it with `X_BUN_VERSION`. Step 1 |
| 2 | `WORKER_QUEUES` | a worker Deployment names a queue that is neither in `jobs.queues` nor a registered job's queue — a typo, a pattern (`banks-*`, `*`), or a queue only `enqueue({ queue })` reaches. It booted and claimed nothing there; it now refuses with `X_WORKER_QUEUE_UNKNOWN`, whose `fix:` is the `WORKER_QUEUES=` line to set. Step 5 |
