[
  {
    "version": "0.354.0",
    "type": "fix",
    "title": "Dev boot passes Redis to the delivery service so notify idempotencyKey dedupes",
    "migration": "No action needed."
  },
  {
    "version": "0.353.0",
    "type": "fix",
    "title": "init-deploy fails early when private GitHub packages have no bunfig.toml or .npmrc",
    "detail": "init-deploy rejects private GitHub packages without a registry config\nAn app with `@cosmicdriftgamestudio/*` dependencies but no `bunfig.toml`/`.npmrc` used to get a Dockerfile whose `bun install` failed with a 404 only inside the image build. `scaffoldDeploy`, `renderDeployFiles` and `checkDeployDrift` now throw with a hint to add the scope entry."
  },
  {
    "version": "0.353.0",
    "type": "improvement",
    "title": "Deploy scaffold warnings distinguish broken package.json from unexpected dependency shapes"
  },
  {
    "version": "0.351.0",
    "type": "fix",
    "title": "--help prints usage instead of running the command (init-deploy, build, dev, schema-check, kumiko-testing integration)",
    "detail": "`--help` prints usage on every bin\n`kumiko-init-deploy --help` used to write the deploy files, and `kumiko-testing integration --help` crashed while parsing its arguments. Both now print their usage and exit 0, as do `kumiko-build`, `kumiko-dev`, `kumiko-schema-check` and `create-kumiko-app`."
  },
  {
    "version": "0.349.0",
    "type": "improvement",
    "title": "init-deploy reads kumiko.deploy.dbName for the migrate step's DATABASE_URL",
    "detail": "init-deploy: `package.json#kumiko.deploy.dbName`\n`migrate-step.sh` builds `DATABASE_URL` with `kumiko.deploy.dbName` as the database (default: appName), validated like `dbUser`. Apps without the key render byte-identical output."
  },
  {
    "version": "0.347.0",
    "type": "fix",
    "title": "Dev-server file watchers take an injectable event source so watcher tests no longer depend on FSEvents timing",
    "detail": "Dev-server file watchers accept an injected event source\n`watchAndRegenerate` takes `watchDirectory` and `createKumikoServer` takes `_watchDirectory`. Production behavior is unchanged (native `fs.watch`); the hot-reload and codegen-watch tests drive changes through the seam instead of macOS FSEvents, which starts asynchronously and drops early writes."
  },
  {
    "version": "0.345.0",
    "type": "fix",
    "title": "generated migrate-step.sh honours COMPOSE_PROJECT_NAME from .env",
    "detail": "The `migrate-step.sh` that `kumiko-init-deploy` generates now reads `.env` before it picks the stack network. Before, a `COMPOSE_PROJECT_NAME` set only in `.env` was ignored and the script fell back to the directory name, so on a shared host it could run the migration against another app's network and Postgres. The project name, from `.env` or the directory, is lowercased and stripped to `[a-z0-9_-]` as Compose does, and the script still stops when the network does not exist. Regenerate the script in existing apps to pick this up."
  },
  {
    "version": "0.345.0",
    "type": "fix",
    "title": "Client entry edits hot-reload instead of restarting",
    "detail": "Editing a client entry file hot-reloads instead of restarting the dev server\nOn macOS the recursive file watcher reports paths relative to the watched directory. A change to a client entry such as `client.tsx` or a custom-named entry was therefore classified as a server change and restarted the process. The watcher now resolves the path and treats every configured entry source file as hot-reload."
  },
  {
    "version": "0.341.0",
    "type": "improvement",
    "title": "buildServerBundle bundles bin/bootstrap.ts to dist-server/bootstrap.js when present"
  },
  {
    "version": "0.341.0",
    "type": "fix",
    "title": "Codegen watcher change filter is a tested pure predicate"
  },
  {
    "version": "0.341.0",
    "type": "breaking",
    "title": "Deploy migrate-step.sh always uses the exact stack network",
    "detail": "The discover variant (docker network ls | head -1) could pick another compose project's network on a shared host. The script now uses COMPOSE_PROJECT_NAME or the directory name plus _stack.",
    "migration": "Remove package.json kumiko.deploy.stackNetwork (or set it to \"directory\"), then re-run scaffoldDeploy. If the deploy directory name differs from the compose project, set COMPOSE_PROJECT_NAME for migrate-step.sh."
  },
  {
    "version": "0.340.0",
    "type": "improvement",
    "title": "Server bundle includes an optional worker entry",
    "detail": "When `bin/worker.ts` sits next to `bin/main.ts`, `buildServerBundle` builds it in the same split build as `dist-server/worker.js`. Start a separate worker process from the same image with `exec bun run worker.js`."
  },
  {
    "version": "0.338.0",
    "type": "fix",
    "title": "Scaffolded migrate step percent-encodes DB_PASSWORD",
    "detail": "dev-server: the scaffolded `deploy/migrate-step.sh` percent-encodes `DB_PASSWORD` in `DATABASE_URL`, so passwords with `@`, `:`, `/`, `#`, `%` or non-ASCII characters no longer break the URL. Re-run `kumiko init-deploy` to pick it up; apps with a hand-written `urlencode` should take the new version, which also encodes bytes >= 0x80 correctly."
  },
  {
    "version": "0.338.0",
    "type": "improvement",
    "title": "dev hostDispatch not-found can serve a custom 404 page",
    "detail": "`DevHostDispatchResult` `{ kind: \"not-found\" }` accepts `file` (served like `static-html`) or `entryName` (that entry's shell), both with status 404. A missing file answers 500 with `hostDispatch: file not found`. Without fields the plain \"Not Found\" 404 stays.",
    "migration": "No code change needed."
  },
  {
    "version": "0.338.0",
    "type": "improvement",
    "title": "New package @cosmicdrift/kumiko-samples with the corpus sources",
    "detail": "New package with the sample recipes, sample apps and bundled-features sources\n`@cosmicdrift/kumiko-samples` ships the sample recipes, sample apps and bundled-features sources in the repository layout. Tools such as the few-shot corpus build can run against a pinned version of it and get the same entry ids and source paths as in the monorepo."
  },
  {
    "version": "0.337.1",
    "type": "improvement",
    "title": "createKumikoServer handle exposes the boot's jobQueueNamePrefix",
    "detail": "dev-server: `createKumikoServer()` returns the boot's `jobQueueNamePrefix` on its handle, so callers sharing a Redis can address exactly that boot's `bull:<prefix>-*` keys.",
    "migration": "No action needed: purely additive handle field."
  },
  {
    "version": "0.337.0",
    "type": "fix",
    "title": "public/ served via realpath incl. symlinked public/, ephemeral dev queues cleaned on stop, init-deploy --out app name, compose-style migrate-step network name, config preset validates configResolver; guards report correct isolation lines and time out kumiko-upgrade",
    "detail": "The dev server serves `public/` files from the symlink-resolved path (realpath check before the read) and works when `public/` itself is a symlink. Ephemeral dev boots delete their per-boot BullMQ keys from the shared Redis on `stop()`. `kumiko-init-deploy --out <dir>` defaults the app name from that directory's `package.json`. The generated `migrate-step.sh` with `stackNetwork: \"directory\"` derives the network name like Docker Compose (lowercased, sanitised, `COMPOSE_PROJECT_NAME` wins). `setupTestStackFromFeatures` with the `config` preset throws a clear error when `extraContext.configResolver` is not a resolver. The runtime-isolation guard reports each regression at its own import line, and the upgrade-state guard no longer hangs on a stuck `kumiko-upgrade` (stdin ignored, timeout)."
  },
  {
    "version": "0.335.0",
    "type": "fix",
    "title": "Codegen validates feature-manifest.json and only keeps the stale handler block when it is missing or invalid",
    "detail": "`kumiko-build` codegen now validates `feature-manifest.json`. A valid manifest that lists no write handlers removes the generated `WriteHandlerQn` and typed-dispatcher block instead of keeping a stale one. A broken manifest keeps the block and warns with its own \"feature-manifest.json invalid\" message."
  },
  {
    "version": "0.335.0",
    "type": "fix",
    "title": "Codegen emits namespace zod imports",
    "detail": "Generated `schemas.generated.ts` and `types.generated.d.ts` now import zod as a namespace (`import * as z from \"zod\"`), matching the framework's own convention. Regenerate with `kumiko build` to pick it up."
  },
  {
    "version": "0.335.0",
    "type": "fix",
    "title": "Dev public-file fallback returns 404 for NUL or over-long paths, deploy scaffold blames appName for an invalid default DB user",
    "detail": "The dev-server public-file fallback answers a request path containing an encoded NUL byte (`%00`) or an over-long file name with the normal 404 instead of a 500. The scaffold deploy step now names `appName` as the cause when it is too long to serve as the default DB user."
  },
  {
    "version": "0.330.0",
    "type": "improvement",
    "title": "kumiko-server-runtime and kumiko-dev-server are published as compiled JavaScript plus .d.ts",
    "detail": "Both packages now ship `dist` (`.js` and `.d.ts`) instead of TypeScript source. Export keys are unchanged; only the targets behind them move to `dist`. `kumiko-dev-server` keeps its `bin/*.ts` entrypoints (they still need Bun) and ships them together with `templates`; they now import through the new `@cosmicdrift/kumiko-dev-server/cli` export instead of reaching into `src`. `kumiko-server-runtime` now declares `ioredis` and `kumiko-dev-server` declares `zod` as dependencies, which their code already imported. `ioredis` is imported as a named `{ Redis }` so the class resolves under Node's ESM loader."
  },
  {
    "version": "0.328.0",
    "type": "improvement",
    "title": "Dev server serves the renderer-web fonts",
    "detail": "GET `/assets/kumiko/fonts/<file>.woff2` streams the packaged IBM Plex files with `font/woff2` and a one day cache header. Names outside the allowlist answer 404."
  },
  {
    "version": "0.323.0",
    "type": "improvement",
    "title": "Deploy Dockerfile template: NPM_AUTH_TOKEN registry auth, manifest-first or full-tree install by detected layout, BUILD_VERSION/BUILD_TIME in the runtime stage, TZ=UTC; new kumiko-init-deploy bin with --force and a --check drift check",
    "migration": "The scaffolded `deploy/Dockerfile` now declares `ARG NPM_AUTH_TOKEN`\n(global + re-declared in the build stage) instead of `ARG GITHUB_TOKEN`,\nand exports `ENV GITHUB_TOKEN=${NPM_AUTH_TOKEN}` in the build stage —\nbunfig.toml/.npmrc still read `$GITHUB_TOKEN`, only the build-arg name\npassed by CI changes. Apps that already generated `deploy/Dockerfile`\nfrom an older template keep working unchanged; re-run\n`kumiko-init-deploy --force` (or `kumiko init-deploy --force` from the\nmonorepo) to pick up the new wiring, the runtime stage's\n`ARG BUILD_VERSION=dev`/`ARG BUILD_TIME=unknown` re-declaration (without\nit the runtime `ENV BUILD_VERSION`/`ENV BUILD_TIME` stayed empty despite\na correct `--build-arg`), and `ENV TZ=UTC`.\n\nThe install step now copies manifests first (`package.json`, `bun.lock`,\nand whichever of `bunfig.toml`/`.npmrc` exist) before `bun install`,\nunless the app's `package.json` has a `workspaces` field or a\n`file:`/`workspace:`/`link:` dependency spec, in which case it still\ndoes `COPY . .` first (bun needs the whole tree to resolve the\nlockfile).\n\n`scaffoldDeploy`/`ScaffoldDeployOptions`/`ScaffoldDeployResult` keep\ntheir existing shape and behavior. New: `renderDeployFiles` (pure,\nno writes) and `checkDeployDrift` (read-only, reports missing/differing\nfiles) are exported alongside `scaffoldDeploy`, which now calls\n`renderDeployFiles` internally. `ScaffoldDeployDetected` gained\n`installFromFullTree` and `registryConfigFiles`.\n\nNew `kumiko-init-deploy` bin (and `runInitDeployCli` export) — same\nflags as `kumiko init-deploy` (`--app`, `--port`, `--github-org`,\n`--out`, `--force`), plus `--check` (drift check, exit 1 on any\nmissing/differing file, does not write) and defaulting `--app` from\n`package.json`'s `name` (scope stripped) when omitted. `--check` and\n`--force` are mutually exclusive (exit 2). The monorepo's\n`kumiko init-deploy` command now delegates to `runInitDeployCli`\ninstead of duplicating the CLI logic."
  },
  {
    "version": "0.323.0",
    "type": "improvement",
    "title": "migrate-step.sh reads package.json#kumiko.deploy (dbUser, stackNetwork \"discover\" | \"directory\"); defaults unchanged, invalid values fail the render and the drift check",
    "migration": "No action needed: without `kumiko.deploy` the scaffolded\n`deploy/migrate-step.sh` renders byte-identical to before. Apps whose\ndatabase user is not the app name set\n`\"kumiko\": { \"deploy\": { \"dbUser\": \"<user>\" } }`; apps that want the\nexact `<dirname>_stack` network instead of the `docker network ls`\nheuristic set `\"stackNetwork\": \"directory\"`. Both `kumiko-init-deploy\n--force` and `--check` read the same config, so the drift check needs\nno extra flags. `ScaffoldDeployDetected` gained `dbUser` and\n`stackNetwork`."
  },
  {
    "version": "0.323.0",
    "type": "improvement",
    "title": "runDevApp wires auth-email-password's new invite-info query into invite.infoHandler",
    "detail": "Same wiring as runProdApp, for the dev server.",
    "migration": "No action needed: purely additive wiring, no option changes."
  },
  {
    "version": "0.323.0",
    "type": "breaking",
    "title": "kumiko-build reads client entries from package.json \"kumiko.clientEntries\"/\"kumiko.clientEntry\" instead of inferring them from filenames",
    "detail": "`kumiko-build` (and the `kumiko build` command) now call\n`readClientEntriesConfig(cwd)` and pass the result into\n`buildProdBundle`, instead of relying on `discoverClientEntries`'\nfilename-based multi-entry discovery. `@cosmicdrift/kumiko-dev-server/build`\nre-exports `resolveClientEntries`, `readClientEntriesConfig`, and\n`ClientEntryDeclaration` in place of `discoverClientEntries`.",
    "migration": "See the `@cosmicdrift/kumiko-server-runtime` changelog entry above for\nthe required package.json changes — the dev-server change is the CLI\nwiring for the same underlying behavior."
  },
  {
    "version": "0.321.0",
    "type": "improvement",
    "title": "kumiko-upgrade gained --help/-h",
    "detail": "Prints usage and exits 0 without running the upgrade report.",
    "migration": "No action needed."
  },
  {
    "version": "0.321.0",
    "type": "improvement",
    "title": "runDevApp now also honors KUMIKO_TRUSTED_PROXY_HOPS as an env fallback, symmetric to runProdApp",
    "detail": "Precedence: `options.trustedProxyHops ?? effectiveAuth?.trustedProxyHops\n?? parseTrustedProxyHopsEnv(envSource[TRUSTED_PROXY_HOPS_ENV], \"runDevApp\")`.",
    "migration": "No action needed for the default (unproxied) dev setup."
  },
  {
    "version": "0.320.0",
    "type": "breaking",
    "title": "createKumikoServer/runDevApp gained a trustedProxyHops option",
    "detail": "`CreateKumikoServerOptions`/`RunDevAppOptions` gained a top-level\n`trustedProxyHops`, forwarded into `setupTestStack`'s `buildServer` call.\nDev normally runs unproxied, so this is usually left unset (default 0).",
    "migration": "No action needed for the default (unproxied) dev setup. Set\n`trustedProxyHops` only if you run the dev server behind a reverse proxy."
  },
  {
    "version": "0.320.0",
    "type": "fix",
    "title": "setupTestStackFromFeatures' config preset now also derives _configAccessorFactory",
    "detail": "buildHandlerContext derives ctx.config from _configAccessorFactory, not configResolver directly, so a write handler under presets: [\"config\"] saw ctx.config as undefined even with a configResolver present. mergeExtraContext now also runs addConfigAccessorFactory (the same helper runProdApp/runDevApp use), keeping a base-supplied configResolver winning over the preset default. Closes #3313."
  },
  {
    "version": "0.320.0",
    "type": "breaking",
    "title": "Dev-server HTML no longer injects __KUMIKO_SCHEMA__ either",
    "migration": "Same semantics as the prod change: `DevHostDispatchResult.injectSchema`\nis deprecated and ignored. The dev-server's auto-mint mode still sets\nthe `kumiko_auth`/`kumiko_csrf` cookies on the HTML response, so a\nclient-side fetch to `/api/schema` is authenticated immediately without\na real login round-trip."
  },
  {
    "version": "0.308.0",
    "type": "improvement",
    "title": "Dev server can build prod-shaped client bundles, E2E uses them"
  },
  {
    "version": "0.307.0",
    "type": "fix",
    "title": "Dev-server job queues no longer collide across parallel boots"
  },
  {
    "version": "0.305.0",
    "type": "improvement",
    "title": "createKumikoServer/runDevApp gain stylesheetWatch (env KUMIKO_DEV_STYLESHEET_WATCH=0) to build Tailwind CSS once without a --watch process",
    "detail": "`defineAppE2eConfig` sets `KUMIKO_DEV_STYLESHEET_WATCH=0`, so the dev-server it boots builds CSS once and stops instead of keeping a Tailwind `--watch` process alive. Tailwind v4's watcher subscribes to the whole app cwd recursively with no gitignore filter, so every Playwright artifact write under `test-results/` previously counted as a rebuild trigger."
  },
  {
    "version": "0.301.0",
    "type": "fix",
    "title": "Scaffolded bunfig.toml and bunfig.ci.toml no longer contain the inert [test] concurrency key",
    "detail": "bun has no [test] concurrency key and ignores it silently; the scaffold wrote concurrency = 8, which made kumiko-testing bunfig refuse the merge on a freshly scaffolded app. Existing apps can delete the line from their bunfig files; nothing changes at runtime."
  },
  {
    "version": "0.299.0",
    "type": "fix",
    "title": "Scaffolded docker-compose.yml uses the correct Postgres 18 data path; default app mounts auth-foundation so it boots",
    "migration": "No action needed for existing apps; only newly scaffolded apps are affected."
  },
  {
    "version": "0.298.0",
    "type": "breaking",
    "title": "createKumikoServer/runDevApp take ExtraRouteDefinition[]; dev hostDispatch gets systemQuery",
    "migration": "extraRoutes on createKumikoServer/runDevApp changes from (app, deps) => void to readonly ExtraRouteDefinition[] - see the framework core changelog entry for the route-kind/dep breakdown. wire?: (deps: SystemWireDeps) => void | Promise<void> replaces non-route setup previously done inside the old extraRoutes callback. The dev hostDispatch callback now receives a second argument { systemQuery }; a dispatch implementation reading the dev db directly switches to systemQuery."
  },
  {
    "version": "0.291.0",
    "type": "fix",
    "title": "renderWriteHandlerTypes emits the WriteHandlerQn union in stable order",
    "detail": "`renderWriteHandlerTypes` now sorts `handlerQns` before rendering, like its sibling renderers in the same file. The dev-server passes registration order (`collectWriteHandlerQns`) while the CLI/build path passes manifest order, so the same feature set produced a differently ordered `WriteHandlerQn` union in `.kumiko/define.ts` and `.kumiko/types.generated.d.ts` depending on which one ran last. Consumer apps that commit those generated files no longer get order-only diffs after a local `bun dev`."
  },
  {
    "version": "0.289.0",
    "type": "improvement",
    "title": "runDevApp gains resolvePageHead for dev/e2e parity with runProdApp",
    "detail": "RunDevAppOptions/CreateKumikoServerOptions gain resolvePageHead, same PageHeadResolver signature as RunProdAppOptions. Applied to every templated HTML response (default single-entry shell and a host-dispatched \"html\" entry) via the shared @cosmicdrift/kumiko-headless/apex resolveAndInjectPageHead — same 300ms timeout, same error/null/timeout-falls-back-to-unchanged-200-shell semantics as prod (kumiko-framework#3026). The dev-only static-html hostDispatch kind (raw file passthrough, no bundle/schema injection either) is unaffected. No behavior change for apps that don't set resolvePageHead."
  }
]
