From 5310636f6bf31e23d6bca2138178e5fde334aeec Mon Sep 17 00:00:00 2001 From: sebi Date: Mon, 17 Aug 2026 17:46:42 -0500 Subject: [PATCH 1/2] =?UTF-8?q?release:=202.0.0=20=E2=80=94=20the=20first?= =?UTF-8?q?=20major?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `[Unreleased]` carried 33 entries marked `BREAKING —`, so semver makes this a major, not a minor with a long changelog. Issue #87 called it. Cut with `scripts/release.ts --version 2.0.0`, which rewrote 46 workspace manifests and docker/helm/Chart.yaml; the changelog half was done by hand, because `insertRelease` would have appended a generated commit-subject section BELOW the hand-written `[Unreleased]` block and left all 33 entries unreleased. The catch the script could not make: `dummy/social-media-clone` pinned `@ultimat3/*` at `^1.2.0` — 19 caret ranges. `EXACT_PIN` deliberately leaves a caret alone ("a caret is somebody's intent") and `checkLockstep` exempts private app packages. But `^1.2.0` cannot resolve 2.0.0, so `bun install` would have pulled the deployed demo off the REGISTRY at 1.2.0 instead of the workspace — the tracked app silently testing the last release instead of this one. Both files are now exact, which is what CHANGELOG.md and wiki/Upgrading.md already required of a generated app. No hardcoded framework version exists outside package.json and Chart.yaml: the Dockerfile reads packages/core/package.json at build time and passes --define, and frameworkVersion() reads the manifest first. Verified rather than assumed. Also swept: 18 "fixed on `main`, unreleased" rows in Known-Gaps, and every "v2" used as a synonym for "a later major" — v2 has now shipped without those features, so the label was about to become false. Left alone in docs/idea and docs/architecture, where v1/v2 is design-era vocabulary rather than a release promise. NOT PUBLISHED, and deliberately so. `@ultimat3/flags` has never reached npm — the registry answers 404 — and it is tier 1 and in the derived publish list, so a release run would abort on it with core and schema already at 2.0.0 on the registry. npm publishes cannot be undone. That bootstrap, the `npm-publish` environment, its branch rule and the per-package Trusted Publisher are four human steps in the npm and GitHub UIs; PUBLISHING.md says none of them is done. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01RBwWKBJkiogA4mDaJiJf3D --- CHANGELOG.md | 14 +++-- CLAUDE.md | 12 ++-- README.md | 14 ++--- docker/helm/Chart.yaml | 10 ++-- docs/idea/19-mechanism-not-convention.md | 2 +- docs/idea/README.md | 2 +- dummy/social-media-clone/package.json | 36 +++++------ .../packages/i18n/package.json | 2 +- examples/dummy/apps/admin/package.json | 10 ++-- examples/dummy/apps/web/package.json | 36 +++++------ examples/dummy/package.json | 10 ++-- examples/dummy/packages/core/package.json | 6 +- examples/dummy/packages/db/package.json | 6 +- examples/dummy/packages/domain/package.json | 4 +- examples/dummy/packages/i18n/package.json | 2 +- examples/dummy/packages/mcp/package.json | 8 +-- examples/dummy/packages/ui/package.json | 6 +- framework.manifest.json | 60 +++++++++---------- llms.txt | 6 +- packages/action/package.json | 12 ++-- packages/admin/package.json | 32 +++++----- packages/ai/package.json | 18 +++--- packages/auth/package.json | 8 +-- packages/cache/package.json | 4 +- packages/cli/package.json | 48 +++++++-------- packages/core/package.json | 2 +- packages/create-ultimate/package.json | 4 +- packages/db/package.json | 4 +- packages/entity/package.json | 8 +-- packages/flags/package.json | 4 +- packages/http/package.json | 10 ++-- packages/i18n/package.json | 4 +- packages/jobs/package.json | 10 ++-- packages/mail/package.json | 12 ++-- packages/manifest/package.json | 12 ++-- packages/mcp/package.json | 16 ++--- packages/money/package.json | 6 +- packages/policy/package.json | 4 +- packages/pwa/package.json | 4 +- packages/query/package.json | 12 ++-- packages/realtime/package.json | 6 +- packages/render/package.json | 10 ++-- packages/schema/package.json | 2 +- packages/seo/package.json | 4 +- packages/storage/package.json | 4 +- packages/testing/package.json | 16 ++--- packages/time/package.json | 4 +- packages/ui/package.json | 10 ++-- wiki/Actions.md | 2 +- wiki/Admin-Dashboard.md | 2 +- wiki/Building-Your-Own-Base.md | 2 +- wiki/Caching-And-Invalidation.md | 2 +- wiki/Configuration.md | 6 +- wiki/FAQ.md | 8 +-- wiki/Getting-Started.md | 2 +- wiki/Home.md | 2 +- wiki/Jobs-And-Workflows.md | 10 ++-- wiki/Known-Gaps.md | 46 +++++++------- wiki/Migrations-And-Backfills.md | 4 +- wiki/Queries-And-Live-Queries.md | 4 +- wiki/Realtime.md | 2 +- wiki/Tutorial-02-First-Feature.md | 8 +-- wiki/Tutorial-04-Jobs-And-Realtime.md | 4 +- wiki/Tutorial-06-Growing-Up.md | 4 +- wiki/Upgrading.md | 8 +-- wiki/_Footer.md | 2 +- 66 files changed, 331 insertions(+), 323 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 085aaf65..9d5dcfee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,12 @@ Semver applies from 1.0.0. A breaking change to a documented API needs a major ## [Unreleased] +## 2.0.0 - 2026-08-17 + +**The first major.** 33 entries below are marked `BREAKING —`, and each one changes a surface semver covers: a primitive field, an export, a CLI flag, an `app.config.ts` key, or a tier edge. Semver applies from 1.0.0, so none of them could ship as a minor. Read [Upgrading](https://github.com/developerz-ai/ultimate/wiki/Upgrading), then the `BREAKING —` entries in order — **no codemod ships with this release**, so each one is a manual edit, and the entry names it. + +All 29 workspaces move together — 28 `@ultimat3/*` plus the unscoped `create-ultimate`, one version, one commit, one tag. Publication is not in lockstep: **`@ultimat3/flags` is still not on the registry**, and its first publish is a manual bootstrap by an npm org member ([PUBLISHING.md](PUBLISHING.md), step 1) because a trusted publisher cannot attach to a package that does not exist. The workflow's publish list is derived from `scripts/list-workspaces.ts`, so no package can be silently absent from a release again. + ### Fixed - **An unregistered currency arriving over HTTP answered 500 and paged the on-call.** @@ -2606,10 +2612,10 @@ Not fixed, and each one hit by actually running the command. Full list in [Known ### Known gaps -- `x build --target binary` compiles but crashes at import: `FRAMEWORK_VERSION` reads `package.json` at module scope and a single-file executable has none. -- `docker-compose.prod.yml` declares a host port and `replicas: 3` together — two processes cannot bind one port. This is the rung-1 ceiling. **Closed in [Unreleased]**: `web` and `sync` are `replicas: 1` in all four files, and the ceiling is declared with the two ways up named. -- The shared cache tier's Lua invalidation `DEL`s keys it never declared in `KEYS`, so it fails on Dragonfly and on Redis Cluster. **Closed in [Unreleased]**. -- `resolveEnvironment` now exists in both `core` and `seo` with different return types. **Closed in [Unreleased]**, as a breaking change: `@ultimat3/seo` exports neither it nor `SeoEnvironment`. +- `x build --target binary` compiles but crashes at import: `FRAMEWORK_VERSION` reads `package.json` at module scope and a single-file executable has none. **Closed in 2.0.0**: the read is lazy, `x build` and `docker/Dockerfile` both pass `--define ULTIMATE_FRAMEWORK_VERSION`, and the image build ends in `/out/app --version`. +- `docker-compose.prod.yml` declares a host port and `replicas: 3` together — two processes cannot bind one port. This is the rung-1 ceiling. **Closed in 2.0.0**: `web` and `sync` are `replicas: 1` in all four files, and the ceiling is declared with the two ways up named. +- The shared cache tier's Lua invalidation `DEL`s keys it never declared in `KEYS`, so it fails on Dragonfly and on Redis Cluster. **Closed in 2.0.0**. +- `resolveEnvironment` now exists in both `core` and `seo` with different return types. **Closed in 2.0.0**, as a breaking change: `@ultimat3/seo` exports neither it nor `SeoEnvironment`. ## [1.0.0] - 2026-08-10 diff --git a/CLAUDE.md b/CLAUDE.md index 0ceb715f..df6c8d2d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,8 +12,10 @@ This repo is the framework itself: a monorepo of `@ultimat3/*` packages, the `x` CLI binary: `x`. npm scope: `@ultimat3`. Import paths: `@ultimat3/`. -**Status:** 1.2.0, `As of 2026-08`. 28 `@ultimat3/*` packages plus the unscoped `create-ultimate` — -29 in all — **versioned** in lockstep: one version, one commit, one tag. Publication is not in +**Status:** 2.0.0, `As of 2026-08`. 28 `@ultimat3/*` packages plus the unscoped `create-ultimate` — +29 in all — **versioned** in lockstep: one version, one commit, one tag. 2.0.0 is the **first +major**: the 2.0.0 section of [`CHANGELOG.md`](CHANGELOG.md) carries 33 entries marked +`BREAKING —` and ships no codemod, so each one is a manual edit its own entry names. Publication is not in lockstep and the repo said it was until 2026-08: **`@ultimat3/flags` has never been published** — the registry answers 404, not a stale version — so 28 of the 29 are on npm. It is not opting out (`packages/flags/package.json` declares the same `publishConfig` as the rest) and nothing in the @@ -54,13 +56,13 @@ Open: roadmap milestone 11's two-platform deploy proof — 1.1.0 gave a scaffold deployable artifact (`packages/cli/src/serve.ts`; `x new` writes `apps/web/server.ts`, `prerender.ts`, a Dockerfile and `docker-compose.prod.yml`; `ROLE=migrate` runs release-phase migrations), but the demo app on Compose **and** K8s from one image with an invisible rolling -restart is still not demonstrated — and until this branch the chart could not have demonstrated it, +restart is still not demonstrated — and until 2.0.0 the chart could not have demonstrated it, because `sync`'s readiness probe polled a port the process never opened. Of the four known gaps -named in [`CHANGELOG.md`](CHANGELOG.md), **all four are now closed**, `As of 2026-08`: +named in [`CHANGELOG.md`](CHANGELOG.md), **all four are closed in 2.0.0**, `As of 2026-08`: | Gap | State | |---|---| -| `x build --target binary` compiled and crashed at import | **fixed, and now proven** — the version read is lazy and `x build` passes `--define ULTIMATE_FRAMEWORK_VERSION`. `docker/Dockerfile` passes it too as of this branch; it had not, so the target was fixed everywhere except in the artifact the framework ships. The image build now ends in `/out/app --version`, so a binary that cannot answer fails the build rather than the first command an operator runs | +| `x build --target binary` compiled and crashed at import | **fixed, and now proven** — the version read is lazy and `x build` passes `--define ULTIMATE_FRAMEWORK_VERSION`. `docker/Dockerfile` passes it too as of 2.0.0; it had not, so the target was fixed everywhere except in the artifact the framework ships. The image build now ends in `/out/app --version`, so a binary that cannot answer fails the build rather than the first command an operator runs | | the shared cache tier's Lua invalidation `DEL`s keys it never declares in `KEYS` | **fixed** — the script returns the member list and the tier deletes value keys client-side, one key per `DEL`, so it is slot-local on Redis Cluster and Dragonfly | | `docker-compose.prod.yml` pairs a published host port with `replicas` above 1 | **fixed** — a published host port has exactly one binder (reproduced: the second replica dies with `Bind for 0.0.0.0:3000 failed: port is already allocated`), so `web` and `sync` declare `replicas: 1` in all four files — framework, both tracked apps, and `x new`'s scaffold. Scaling either is the reverse proxy you add or the chart's per-role HPA, both named in the file header: Compose is the ladder's single-node rung and the box is the availability story | | `resolveEnvironment` exists in both `core` and `seo` with different return types | **fixed** — seo's is deleted; core's is the one reader of `ULTIMATE_ENV`, and `'preview'` is now core's `'staging'`. The half that was not obvious: `ULTIMATE_ENV` is **not in the env schema**, so nothing validates it at boot and a `robots.txt` render can be its first reader — hence `tryResolveEnvironment()` in core, which answers `undefined` rather than throwing, instead of a second resolver in seo | diff --git a/README.md b/README.md index d855d26e..13e9efd6 100644 --- a/README.md +++ b/README.md @@ -13,11 +13,11 @@ [![CI](https://github.com/developerz-ai/ultimate/actions/workflows/ci.yml/badge.svg)](https://github.com/developerz-ai/ultimate/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![Bun](https://img.shields.io/badge/bun-%E2%89%A5%201.3-black.svg?logo=bun)](https://bun.sh) -[![Version](https://img.shields.io/badge/version-1.2.0-blue.svg)](CHANGELOG.md) +[![Version](https://img.shields.io/badge/version-2.0.0-blue.svg)](CHANGELOG.md) -> **Status: 1.2.0**, `As of 2026-08`. 28 `@ultimat3/*` packages plus the unscoped `create-ultimate` — 29 in all — versioned in lockstep: one version, one commit, one tag. **`@ultimat3/flags` has never reached npm** and the rest sit at 1.2.0, so the registry is not yet in lockstep with the repo ([#84](https://github.com/developerz-ai/ultimate/issues/84)); it needs the one-time manual bootstrap every package gets before a trusted publisher can attach. 1.1.0 was the **first release published by the workflow**, over OIDC trusted publishing with provenance attached; 1.0.0 was the manual bootstrap. Semver applies — a breaking change to a documented API needs a major. That is what the version number means: a stable API under semver, not a promise about your infrastructure. +> **Status: 2.0.0**, `As of 2026-08`. 28 `@ultimat3/*` packages plus the unscoped `create-ultimate` — 29 in all — versioned in lockstep: one version, one commit, one tag. **The first major**: [CHANGELOG.md](CHANGELOG.md)'s 2.0.0 section carries 33 entries marked `BREAKING —`, and no codemod ships with them, so each is a manual edit its entry names ([Upgrading](https://github.com/developerz-ai/ultimate/wiki/Upgrading)). Publication is not in lockstep: **`@ultimat3/flags` has never reached npm** ([#84](https://github.com/developerz-ai/ultimate/issues/84)), and it needs the one-time manual bootstrap every package gets before a trusted publisher can attach. 1.1.0 was the **first release published by the workflow**, over OIDC trusted publishing with provenance attached; 1.0.0 was the manual bootstrap. Semver applies — a breaking change to a documented API needs a major. That is what the version number means: a stable API under semver, not a promise about your infrastructure. ## Built by agents, for agents, maintained by agents @@ -51,13 +51,13 @@ are the difference between an agent that ships and one that thrashes. Reproduce it: `bun run scripts/bench/restart-bench.ts --clients 10000 --probe-interval-ms 200` — the committed report and the run's own transcript are in [`scripts/bench/results/`](scripts/bench/results/). -**Not claimed at 1.1.0:** +**Not claimed at 2.0.0:** | Open | Where it stands | |---|---| | **Two-platform deploy proof** | 1.1.0 gave a scaffolded app a real deployable artifact — `x new` writes `apps/web/server.ts`, `prerender.ts`, a Dockerfile and `docker-compose.prod.yml`, and `ROLE=migrate` runs release-phase migrations. The **proof** is still open: the demo app on Compose **and** K8s from one image, with an invisible rolling restart, is [milestone 11](docs/idea/14-roadmap.md) and has not been demonstrated | -| **Known gaps shipped in 1.1.0** | all four are fixed on `main`, unreleased — but a fix and a proof are different things, and one of the four still lacks the proof. `x build --target binary` no longer crashes at import — the version read is lazy and `x build` passes `--define ULTIMATE_FRAMEWORK_VERSION`, and [`docker/Dockerfile`](docker/Dockerfile) passes it too and ends in `/out/app --version`, so a binary that cannot answer fails the image build rather than the first command an operator runs. The target is still unproven end to end: booting is not serving, and no scaffolded app has been compiled and served from a bare VM · `docker-compose.prod.yml` no longer pairs a published host port with `replicas` above 1: `web` and `sync` declare `replicas: 1` in all four files and each header names the two ways up, which makes the one-box ceiling declared rather than broken, not lifted · the shared cache tier's Lua invalidation no longer `DEL`s keys it never declares in `KEYS` · `resolveEnvironment` exists only in `core`, and `@ultimat3/seo` exports neither it nor `SeoEnvironment` — a **breaking** change, unreleased. Detail in [CHANGELOG.md](CHANGELOG.md), per-row workarounds for the published packages in [Known gaps](https://github.com/developerz-ai/ultimate/wiki/Known-Gaps) | -| **Deferred to v2** | realtime tier 3 local-first (`persist: true`), the plugin API, multi-region replication, the Redis/NATS **job** drivers — each behind the interface that ships today. The job drivers throw `X_NOT_IMPLEMENTED` with a runnable `fix:` rather than pretending to work | +| **Known gaps shipped in 1.1.0** | all four are fixed in 2.0.0 — but a fix and a proof are different things, and one of the four still lacks the proof. `x build --target binary` no longer crashes at import — the version read is lazy and `x build` passes `--define ULTIMATE_FRAMEWORK_VERSION`, and [`docker/Dockerfile`](docker/Dockerfile) passes it too and ends in `/out/app --version`, so a binary that cannot answer fails the image build rather than the first command an operator runs. The target is still unproven end to end: booting is not serving, and no scaffolded app has been compiled and served from a bare VM · `docker-compose.prod.yml` no longer pairs a published host port with `replicas` above 1: `web` and `sync` declare `replicas: 1` in all four files and each header names the two ways up, which makes the one-box ceiling declared rather than broken, not lifted · the shared cache tier's Lua invalidation no longer `DEL`s keys it never declares in `KEYS` · `resolveEnvironment` exists only in `core`, and `@ultimat3/seo` exports neither it nor `SeoEnvironment` — a **breaking** change, which is part of why 2.0.0 is a major. Detail in [CHANGELOG.md](CHANGELOG.md), per-row workarounds for the 1.x packages in [Known gaps](https://github.com/developerz-ai/ultimate/wiki/Known-Gaps) | +| **Deferred past 2.0.0** | realtime tier 3 local-first (`persist: true`), the plugin API, multi-region replication, the Redis/NATS **job** drivers — none of them ships in 2.0.0, each behind the interface that ships today. The job drivers throw `X_NOT_IMPLEMENTED` with a runnable `fix:` rather than pretending to work | **Never claimed:** no adoption numbers, no production deployments, no testimonials. None exist yet, and this file will say so until they do. @@ -223,7 +223,7 @@ Render mode is a route-level property, not a global one. A landing page is stati ## Realtime — a ladder, not a cliff -Three tiers, the same mutator shape at every rung. Tier 2 → tier 3 is a config flag, not a rewrite. Tiers 1–2 ship today; tier 3 lands in v2, behind the interfaces that are already here. +Three tiers, the same mutator shape at every rung. Tier 2 → tier 3 is a config flag, not a rewrite. Tiers 1–2 ship today; tier 3 is not in 2.0.0 and lands in a later major, behind the interfaces that are already here. | Tier | What | Covers | |---|---|---| @@ -260,7 +260,7 @@ The same app code on one PaaS dyno and on a replicated cluster. Climbing is a dr | Auth | **Better Auth**, wrapped | MIT, self-hosted, with our policy layer on top | | Frontend | **SolidJS 2** + our own router | fine-grained reactivity; we vendor the router rather than track an alpha | | Styling | **SCSS modules + design tokens** | no Tailwind (diff noise), no CSS-in-JS (runtime cost) | -| Jobs | Postgres queue default; Redis/NATS drivers in v2 | zero-infra start, a real scale path behind one interface | +| Jobs | Postgres queue default; Redis/NATS drivers not in 2.0.0 | zero-infra start, a real scale path behind one interface | | Observability | **OpenTelemetry, always on** | one trace across HTTP → job → live query | **Excluded on purpose:** GraphQL · multi-runtime · multi-ORM · a second CSS solution · React Server Components · a plugin API in 1.x · vendor edge/KV primitives. diff --git a/docker/helm/Chart.yaml b/docker/helm/Chart.yaml index 3319475f..3296e80b 100644 --- a/docker/helm/Chart.yaml +++ b/docker/helm/Chart.yaml @@ -6,11 +6,11 @@ type: application # (values.yaml `image.tag: ""`), so 0.0.1 named a tag that has never existed for any release — a # fresh `helm install` with the shipped values pulled `ultimate-app:0.0.1` and got ImagePullBackOff. # -# Nothing rewrites these on release yet: `scripts/release.ts` walks workspace package.json files and -# a chart is not a workspace. Until it does, this is a hand-kept number, which is the same defect -# class as the publish list in .github/workflows/release.yml — reported, not left silent. -version: 1.2.0 -appVersion: "1.2.0" +# `scripts/release.ts` rewrites both on every release through `setChartVersions` — a chart is not a +# workspace, so the manifest loop cannot reach it and the script writes it by path instead. Never +# hand-edit them: the chart moving on its own is how `appVersion` named an image tag nobody pushed. +version: 2.0.0 +appVersion: "2.0.0" kubeVersion: ">=1.27.0-0" keywords: [bun, postgres, solidjs, ultimate] home: https://ultimate.dev diff --git a/docs/idea/19-mechanism-not-convention.md b/docs/idea/19-mechanism-not-convention.md index 43d14c8c..b7afaf09 100644 --- a/docs/idea/19-mechanism-not-convention.md +++ b/docs/idea/19-mechanism-not-convention.md @@ -39,7 +39,7 @@ made three of them wrong. ## The seam is the wrapper -An app composes primitives into its own base and builds on that. Verified against 1.2.0: +An app composes primitives into its own base and builds on that. Verified against 1.2.0, not re-run against 2.0.0: ```ts // apps/web/shared/base/tenant-entity.ts — the app's convention, written once diff --git a/docs/idea/README.md b/docs/idea/README.md index b1f2b851..dede5c56 100644 --- a/docs/idea/README.md +++ b/docs/idea/README.md @@ -60,7 +60,7 @@ Consequences of each in [`00-thesis.md`](./00-thesis.md); axiom 8 in full in [`1 ## Status -`As of 2026-08`: **1.2.0, shipped.** 28 `@ultimat3/*` packages plus the unscoped `create-ultimate` — 29 in all — versioned in lockstep and published over OIDC trusted publishing. `@ultimat3/flags` has never reached npm and still needs its one-time manual bootstrap ([#84](https://github.com/developerz-ai/ultimate/issues/84)), so the registry is not yet in lockstep with the repo. Docs `00`–`15`, `18` and `19` describe what exists; `16` and `17` are design only and say so in every claim. +`As of 2026-08`: **2.0.0, shipped** — the first major, 33 changelog entries marked `BREAKING —`. 28 `@ultimat3/*` packages plus the unscoped `create-ultimate` — 29 in all — versioned in lockstep and published over OIDC trusted publishing. `@ultimat3/flags` has never reached npm and still needs its one-time manual bootstrap ([#84](https://github.com/developerz-ai/ultimate/issues/84)), so the registry is not in lockstep with the repo. Docs `00`–`15`, `18` and `19` describe what exists; `16` and `17` are design only and say so in every claim. Milestone order and the "done when" bar for each live in [`14-roadmap.md`](./14-roadmap.md); the honest accounting of what could kill the project is in [`15-risks.md`](./15-risks.md) — read it before the roadmap, not after. diff --git a/dummy/social-media-clone/package.json b/dummy/social-media-clone/package.json index c369f4d4..a01f96d9 100644 --- a/dummy/social-media-clone/package.json +++ b/dummy/social-media-clone/package.json @@ -22,27 +22,27 @@ "@biomejs/biome": "^2.4.15", "@electric-sql/pglite": "^0.5.4", "@types/bun": "^1.3.14", - "@ultimat3/testing": "^1.2.0", + "@ultimat3/testing": "2.0.0", "typescript": "^7.0.2" }, "dependencies": { - "@ultimat3/action": "^1.2.0", - "@ultimat3/admin": "^1.2.0", - "@ultimat3/cache": "^1.2.0", - "@ultimat3/cli": "^1.2.0", - "@ultimat3/core": "^1.2.0", - "@ultimat3/db": "^1.2.0", - "@ultimat3/entity": "^1.2.0", - "@ultimat3/http": "^1.2.0", - "@ultimat3/i18n": "^1.2.0", - "@ultimat3/jobs": "^1.2.0", - "@ultimat3/mcp": "^1.2.0", - "@ultimat3/policy": "^1.2.0", - "@ultimat3/pwa": "^1.2.0", - "@ultimat3/query": "^1.2.0", - "@ultimat3/realtime": "^1.2.0", - "@ultimat3/render": "^1.2.0", - "@ultimat3/ui": "^1.2.0", + "@ultimat3/action": "2.0.0", + "@ultimat3/admin": "2.0.0", + "@ultimat3/cache": "2.0.0", + "@ultimat3/cli": "2.0.0", + "@ultimat3/core": "2.0.0", + "@ultimat3/db": "2.0.0", + "@ultimat3/entity": "2.0.0", + "@ultimat3/http": "2.0.0", + "@ultimat3/i18n": "2.0.0", + "@ultimat3/jobs": "2.0.0", + "@ultimat3/mcp": "2.0.0", + "@ultimat3/policy": "2.0.0", + "@ultimat3/pwa": "2.0.0", + "@ultimat3/query": "2.0.0", + "@ultimat3/realtime": "2.0.0", + "@ultimat3/render": "2.0.0", + "@ultimat3/ui": "2.0.0", "solid-js": "1.9.14" }, "engines": { diff --git a/dummy/social-media-clone/packages/i18n/package.json b/dummy/social-media-clone/packages/i18n/package.json index effddad5..06a4b02f 100644 --- a/dummy/social-media-clone/packages/i18n/package.json +++ b/dummy/social-media-clone/packages/i18n/package.json @@ -11,6 +11,6 @@ "typecheck": "tsc --noEmit -p ../../tsconfig.json" }, "dependencies": { - "@ultimat3/i18n": "^1.2.0" + "@ultimat3/i18n": "2.0.0" } } diff --git a/examples/dummy/apps/admin/package.json b/examples/dummy/apps/admin/package.json index acf9dd50..c7acd371 100644 --- a/examples/dummy/apps/admin/package.json +++ b/examples/dummy/apps/admin/package.json @@ -19,10 +19,10 @@ "dependencies": { "@postly/db": "0.0.1", "@postly/web": "0.0.1", - "@ultimat3/action": "1.2.0", - "@ultimat3/admin": "1.2.0", - "@ultimat3/core": "1.2.0", - "@ultimat3/policy": "1.2.0", - "@ultimat3/schema": "1.2.0" + "@ultimat3/action": "2.0.0", + "@ultimat3/admin": "2.0.0", + "@ultimat3/core": "2.0.0", + "@ultimat3/policy": "2.0.0", + "@ultimat3/schema": "2.0.0" } } diff --git a/examples/dummy/apps/web/package.json b/examples/dummy/apps/web/package.json index 4661caa2..becf3675 100644 --- a/examples/dummy/apps/web/package.json +++ b/examples/dummy/apps/web/package.json @@ -25,24 +25,24 @@ "@postly/domain": "0.0.1", "@postly/i18n": "0.0.1", "@postly/ui": "0.0.1", - "@ultimat3/action": "1.2.0", - "@ultimat3/ai": "1.2.0", - "@ultimat3/auth": "1.2.0", - "@ultimat3/core": "1.2.0", - "@ultimat3/entity": "1.2.0", - "@ultimat3/flags": "1.2.0", - "@ultimat3/jobs": "1.2.0", - "@ultimat3/mail": "1.2.0", - "@ultimat3/policy": "1.2.0", - "@ultimat3/query": "1.2.0", - "@ultimat3/realtime": "1.2.0", - "@ultimat3/render": "1.2.0", - "@ultimat3/schema": "1.2.0", - "@ultimat3/seo": "1.2.0", - "@ultimat3/storage": "1.2.0", - "@ultimat3/testing": "1.2.0", - "@ultimat3/time": "1.2.0", - "@ultimat3/ui": "1.2.0", + "@ultimat3/action": "2.0.0", + "@ultimat3/ai": "2.0.0", + "@ultimat3/auth": "2.0.0", + "@ultimat3/core": "2.0.0", + "@ultimat3/entity": "2.0.0", + "@ultimat3/flags": "2.0.0", + "@ultimat3/jobs": "2.0.0", + "@ultimat3/mail": "2.0.0", + "@ultimat3/policy": "2.0.0", + "@ultimat3/query": "2.0.0", + "@ultimat3/realtime": "2.0.0", + "@ultimat3/render": "2.0.0", + "@ultimat3/schema": "2.0.0", + "@ultimat3/seo": "2.0.0", + "@ultimat3/storage": "2.0.0", + "@ultimat3/testing": "2.0.0", + "@ultimat3/time": "2.0.0", + "@ultimat3/ui": "2.0.0", "solid-js": "1.9.14" } } diff --git a/examples/dummy/package.json b/examples/dummy/package.json index cbaee6a3..c3900a6b 100644 --- a/examples/dummy/package.json +++ b/examples/dummy/package.json @@ -24,13 +24,13 @@ "manifest": "x manifest --json" }, "devDependencies": { - "@ultimat3/cache": "1.2.0", - "@ultimat3/entity": "1.2.0", - "@ultimat3/testing": "1.2.0", + "@ultimat3/cache": "2.0.0", + "@ultimat3/entity": "2.0.0", + "@ultimat3/testing": "2.0.0", "typescript": "7.0.2" }, "dependencies": { - "@ultimat3/cli": "1.2.0", - "@ultimat3/core": "1.2.0" + "@ultimat3/cli": "2.0.0", + "@ultimat3/core": "2.0.0" } } diff --git a/examples/dummy/packages/core/package.json b/examples/dummy/packages/core/package.json index 877cfe6b..74108596 100644 --- a/examples/dummy/packages/core/package.json +++ b/examples/dummy/packages/core/package.json @@ -17,8 +17,8 @@ }, "dependencies": { "@postly/domain": "0.0.1", - "@ultimat3/core": "1.2.0", - "@ultimat3/money": "1.2.0", - "@ultimat3/time": "1.2.0" + "@ultimat3/core": "2.0.0", + "@ultimat3/money": "2.0.0", + "@ultimat3/time": "2.0.0" } } diff --git a/examples/dummy/packages/db/package.json b/examples/dummy/packages/db/package.json index 50d9913a..4271c9dc 100644 --- a/examples/dummy/packages/db/package.json +++ b/examples/dummy/packages/db/package.json @@ -20,8 +20,8 @@ }, "dependencies": { "@postly/domain": "0.0.1", - "@ultimat3/cache": "1.2.0", - "@ultimat3/core": "1.2.0", - "@ultimat3/entity": "1.2.0" + "@ultimat3/cache": "2.0.0", + "@ultimat3/core": "2.0.0", + "@ultimat3/entity": "2.0.0" } } diff --git a/examples/dummy/packages/domain/package.json b/examples/dummy/packages/domain/package.json index c7573359..9a0b4f50 100644 --- a/examples/dummy/packages/domain/package.json +++ b/examples/dummy/packages/domain/package.json @@ -16,7 +16,7 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "1.2.0", - "@ultimat3/money": "1.2.0" + "@ultimat3/core": "2.0.0", + "@ultimat3/money": "2.0.0" } } diff --git a/examples/dummy/packages/i18n/package.json b/examples/dummy/packages/i18n/package.json index 21502179..0931fe82 100644 --- a/examples/dummy/packages/i18n/package.json +++ b/examples/dummy/packages/i18n/package.json @@ -17,6 +17,6 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/i18n": "1.2.0" + "@ultimat3/i18n": "2.0.0" } } diff --git a/examples/dummy/packages/mcp/package.json b/examples/dummy/packages/mcp/package.json index 74141a7d..809af2f8 100644 --- a/examples/dummy/packages/mcp/package.json +++ b/examples/dummy/packages/mcp/package.json @@ -19,9 +19,9 @@ "dependencies": { "@postly/core": "0.0.1", "@postly/domain": "0.0.1", - "@ultimat3/action": "1.2.0", - "@ultimat3/core": "1.2.0", - "@ultimat3/mcp": "1.2.0", - "@ultimat3/testing": "1.2.0" + "@ultimat3/action": "2.0.0", + "@ultimat3/core": "2.0.0", + "@ultimat3/mcp": "2.0.0", + "@ultimat3/testing": "2.0.0" } } diff --git a/examples/dummy/packages/ui/package.json b/examples/dummy/packages/ui/package.json index b3f7d0a9..6b71523a 100644 --- a/examples/dummy/packages/ui/package.json +++ b/examples/dummy/packages/ui/package.json @@ -18,9 +18,9 @@ "dependencies": { "@postly/domain": "0.0.1", "@postly/i18n": "0.0.1", - "@ultimat3/money": "1.2.0", - "@ultimat3/render": "1.2.0", - "@ultimat3/ui": "1.2.0", + "@ultimat3/money": "2.0.0", + "@ultimat3/render": "2.0.0", + "@ultimat3/ui": "2.0.0", "solid-js": "1.9.14" } } diff --git a/framework.manifest.json b/framework.manifest.json index ec085b3e..1d468af7 100644 --- a/framework.manifest.json +++ b/framework.manifest.json @@ -1,6 +1,6 @@ { "version": 1, - "buildId": "2668099932ade88316631d4dd4f52c0532b9d83f0ad473eff04dd1fdf6fe05d6", + "buildId": "8428866f7617200e4c62f25817c926645b0605047f88aa5f1edbf72cd1d7016e", "tiers": { "0": [ "core", @@ -46,175 +46,175 @@ "packages": [ { "name": "@ultimat3/core", - "version": "1.2.0", + "version": "2.0.0", "tier": 0, "private": false }, { "name": "@ultimat3/schema", - "version": "1.2.0", + "version": "2.0.0", "tier": 0, "private": false }, { "name": "@ultimat3/cache", - "version": "1.2.0", + "version": "2.0.0", "tier": 1, "private": false }, { "name": "@ultimat3/db", - "version": "1.2.0", + "version": "2.0.0", "tier": 1, "private": false }, { "name": "@ultimat3/flags", - "version": "1.2.0", + "version": "2.0.0", "tier": 1, "private": false }, { "name": "@ultimat3/i18n", - "version": "1.2.0", + "version": "2.0.0", "tier": 1, "private": false }, { "name": "@ultimat3/money", - "version": "1.2.0", + "version": "2.0.0", "tier": 1, "private": false }, { "name": "@ultimat3/seo", - "version": "1.2.0", + "version": "2.0.0", "tier": 1, "private": false }, { "name": "@ultimat3/storage", - "version": "1.2.0", + "version": "2.0.0", "tier": 1, "private": false }, { "name": "@ultimat3/time", - "version": "1.2.0", + "version": "2.0.0", "tier": 1, "private": false }, { "name": "@ultimat3/auth", - "version": "1.2.0", + "version": "2.0.0", "tier": 2, "private": false }, { "name": "@ultimat3/entity", - "version": "1.2.0", + "version": "2.0.0", "tier": 2, "private": false }, { "name": "@ultimat3/http", - "version": "1.2.0", + "version": "2.0.0", "tier": 2, "private": false }, { "name": "@ultimat3/policy", - "version": "1.2.0", + "version": "2.0.0", "tier": 2, "private": false }, { "name": "@ultimat3/action", - "version": "1.2.0", + "version": "2.0.0", "tier": 3, "private": false }, { "name": "@ultimat3/jobs", - "version": "1.2.0", + "version": "2.0.0", "tier": 3, "private": false }, { "name": "@ultimat3/query", - "version": "1.2.0", + "version": "2.0.0", "tier": 3, "private": false }, { "name": "@ultimat3/realtime", - "version": "1.2.0", + "version": "2.0.0", "tier": 3, "private": false }, { "name": "@ultimat3/ai", - "version": "1.2.0", + "version": "2.0.0", "tier": 4, "private": false }, { "name": "@ultimat3/mail", - "version": "1.2.0", + "version": "2.0.0", "tier": 4, "private": false }, { "name": "@ultimat3/manifest", - "version": "1.2.0", + "version": "2.0.0", "tier": 4, "private": false }, { "name": "@ultimat3/mcp", - "version": "1.2.0", + "version": "2.0.0", "tier": 4, "private": false }, { "name": "@ultimat3/pwa", - "version": "1.2.0", + "version": "2.0.0", "tier": 4, "private": false }, { "name": "@ultimat3/render", - "version": "1.2.0", + "version": "2.0.0", "tier": 4, "private": false }, { "name": "@ultimat3/admin", - "version": "1.2.0", + "version": "2.0.0", "tier": 5, "private": false }, { "name": "@ultimat3/cli", - "version": "1.2.0", + "version": "2.0.0", "tier": 5, "private": false }, { "name": "@ultimat3/testing", - "version": "1.2.0", + "version": "2.0.0", "tier": 5, "private": false }, { "name": "@ultimat3/ui", - "version": "1.2.0", + "version": "2.0.0", "tier": 5, "private": false }, { "name": "create-ultimate", - "version": "1.2.0", + "version": "2.0.0", "tier": 6, "private": false } diff --git a/llms.txt b/llms.txt index 2f765fe2..d1120387 100644 --- a/llms.txt +++ b/llms.txt @@ -1,8 +1,8 @@ # Ultimate -> A full-stack, Bun-only, opinionated web framework: Rails' philosophy applied to Bun + Postgres + SolidJS, where the primary developer is an AI agent and the secondary developer is a tired senior engineer working through their own AI agent and AI reviewer. Eight primitives, one authz system, and errors that carry an exact fix command. 1.2.0 as of 2026-08 — 28 `@ultimat3/*` packages plus the unscoped `create-ultimate`, 29 in all, are versioned in lockstep at one version, and semver applies from here: a breaking change to a documented API needs a major. `@ultimat3/flags` has never reached npm and the rest sit at 1.2.0, so the registry is not yet in lockstep with the repo (issue #84). Install with `bunx create-ultimate myapp`. +> A full-stack, Bun-only, opinionated web framework: Rails' philosophy applied to Bun + Postgres + SolidJS, where the primary developer is an AI agent and the secondary developer is a tired senior engineer working through their own AI agent and AI reviewer. Eight primitives, one authz system, and errors that carry an exact fix command. 2.0.0 as of 2026-08 — 28 `@ultimat3/*` packages plus the unscoped `create-ultimate`, 29 in all, are versioned in lockstep at one version, and semver applies from 1.0.0: a breaking change to a documented API needs a major, which is what 2.0.0 is — 33 entries in its changelog section are marked `BREAKING —` and none of them ships a codemod. Publication is not in lockstep: `@ultimat3/flags` has never reached npm and needs a one-time manual bootstrap before a trusted publisher can attach (issue #84). Install with `bunx create-ultimate myapp`. -Everything in the framework is one of eight primitives: `entity`, `policy`, `action`, `mutator`, `query`, `job`, `route`, `task`. One `action` declaration projects into six artifacts (HTTP route, OpenAPI operation, typed client function, job handle, MCP tool, test scaffold) that all share the same `policy` — there is never a second authorization system. The CLI binary is `x`; every command and every error has a `--json` form, and every framework error carries a stable `X_*` code, a concrete cause, and the exact command that fixes it. `x verify` is the single gate, 17 steps in cost order: typecheck, lint, boundaries, filesize, package-shape, errors, unit, contract, live, job, e2e, eval, drift, contract-diff, budgets, manifest, roadmap. The stack is locked (Bun >= 1.3, Postgres with no ORM dependency — `entity()` projects to hand-written SQL through `postgresDriver()`, Standard Schema behind `t` with a dependency-free builtin provider as the shipped default, Better Auth, SolidJS 2, SCSS modules + tokens, OpenTelemetry always on) and the exclusions are permanent (GraphQL, multi-runtime, multi-ORM, a second CSS system, RSC, vendor edge/KV primitives, a plugin API in 1.x). An app extends the framework by **wrapping**, never by forking, patching or petitioning: `entity()`, `action()`, `mutator()`, `query()` and `job()` are plain functions returning values, registration happens inside the call, `isAction` is structural, and no `x verify` step matches source text for a primitive call — so an app's own factory (`tenantEntity`, `auditedMutator`) yields primitives the registry, the manifest, the five projections, admin and MCP treat identically. Axiom 8: Ultimate ships mechanism; your app ships convention — mechanisms and *structural* conventions (file naming, the four surfaces, the tier order, one flat i18n catalog per locale) ship as build errors, *business* conventions (an org model, a plan tier, what an audit row says) never do. Deployment is containers only: one image, six roles selected by `ROLE`. +Everything in the framework is one of eight primitives: `entity`, `policy`, `action`, `mutator`, `query`, `job`, `route`, `task`. One `action` declaration projects into six artifacts (HTTP route, OpenAPI operation, typed client function, job handle, MCP tool, test scaffold) that all share the same `policy` — there is never a second authorization system. The CLI binary is `x`; every command and every error has a `--json` form, and every framework error carries a stable `X_*` code, a concrete cause, and the exact command that fixes it. `x verify` is the single gate, 17 steps in cost order: typecheck, lint, boundaries, filesize, package-shape, errors, unit, contract, live, job, e2e, eval, drift, contract-diff, budgets, manifest, roadmap. The stack is locked (Bun >= 1.3, Postgres with no ORM dependency — `entity()` projects to hand-written SQL through `postgresDriver()`, Standard Schema behind `t` with a dependency-free builtin provider as the shipped default, Better Auth, SolidJS 2, SCSS modules + tokens, OpenTelemetry always on) and the exclusions are permanent (GraphQL, multi-runtime, multi-ORM, a second CSS system, RSC, vendor edge/KV primitives, a plugin API). An app extends the framework by **wrapping**, never by forking, patching or petitioning: `entity()`, `action()`, `mutator()`, `query()` and `job()` are plain functions returning values, registration happens inside the call, `isAction` is structural, and no `x verify` step matches source text for a primitive call — so an app's own factory (`tenantEntity`, `auditedMutator`) yields primitives the registry, the manifest, the five projections, admin and MCP treat identically. Axiom 8: Ultimate ships mechanism; your app ships convention — mechanisms and *structural* conventions (file naming, the four surfaces, the tier order, one flat i18n catalog per locale) ship as build errors, *business* conventions (an org model, a plan tier, what an audit row says) never do. Deployment is containers only: one image, six roles selected by `ROLE`. ## Docs @@ -61,7 +61,7 @@ Everything in the framework is one of eight primitives: `entity`, `policy`, `act - [action](https://raw.githubusercontent.com/developerz-ai/ultimate/main/packages/action/src/index.ts): server-authoritative mutation; six generated artifacts per declaration. - [query](https://raw.githubusercontent.com/developerz-ai/ultimate/main/packages/query/src/index.ts): reads, optionally `live: true`; requires deterministic, bounded SQL. - [jobs](https://raw.githubusercontent.com/developerz-ai/ultimate/main/packages/jobs/src/index.ts): durable steps, required `idempotencyKey`, `pg` driver by default. -- [realtime](https://raw.githubusercontent.com/developerz-ai/ultimate/main/packages/realtime/src/index.ts): channels, live-query transport, mutator rebase, cursors. Tiers 1–2 ship at 1.0.0; tier 3 local-first (`persist: true`) is deferred to v2 behind the same interfaces. Capacity is measured and published for **one node only**: 50,000 WebSocket clients on a single `sync` node over `InProcessTransport`, `SIGKILL`ed with no drain — all 50,000 reconnected, 49,981 received a channel patch in-window, time-to-consistent p50 54.0s / p90 105.5s / max 145.7s, 156,851 connect attempts shed by the `AcceptBudget` ([restart-bench.ts](https://raw.githubusercontent.com/developerz-ai/ultimate/main/scripts/bench/restart-bench.ts), [50k-restart.json](https://raw.githubusercontent.com/developerz-ai/ultimate/main/scripts/bench/results/50k-restart.json)). Per-node recovery, not throughput; the run never crossed NATS, so it is not a multi-node result. Multi-node figures remain targets. +- [realtime](https://raw.githubusercontent.com/developerz-ai/ultimate/main/packages/realtime/src/index.ts): channels, live-query transport, mutator rebase, cursors. Tiers 1–2 ship at 1.0.0; tier 3 local-first (`persist: true`) is not in 2.0.0 and stays deferred behind the same interfaces. Capacity is measured and published for **one node only**: 50,000 WebSocket clients on a single `sync` node over `InProcessTransport`, `SIGKILL`ed with no drain — all 50,000 reconnected, 49,981 received a channel patch in-window, time-to-consistent p50 54.0s / p90 105.5s / max 145.7s, 156,851 connect attempts shed by the `AcceptBudget` ([restart-bench.ts](https://raw.githubusercontent.com/developerz-ai/ultimate/main/scripts/bench/restart-bench.ts), [50k-restart.json](https://raw.githubusercontent.com/developerz-ai/ultimate/main/scripts/bench/results/50k-restart.json)). Per-node recovery, not throughput; the run never crossed NATS, so it is not a multi-node result. Multi-node figures remain targets. - [render](https://raw.githubusercontent.com/developerz-ai/ultimate/main/packages/render/src/index.ts): `defineRoute`, the five render modes, hydration timing, budgets. - [cache](https://raw.githubusercontent.com/developerz-ai/ultimate/main/packages/cache/src/index.ts): four tiers and the entity-tag invalidation graph. - [seo](https://raw.githubusercontent.com/developerz-ai/ultimate/main/packages/seo/src/index.ts): typed `meta`, `ld.*` JSON-LD helpers, sitemap/robots/feeds. diff --git a/packages/action/package.json b/packages/action/package.json index a99adedb..42b47ca3 100644 --- a/packages/action/package.json +++ b/packages/action/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/action", - "version": "1.2.0", + "version": "2.0.0", "description": "The action primitive: one declaration projected to route, OpenAPI, client, MCP tool, job handle, tests", "license": "MIT", "type": "module", @@ -31,10 +31,10 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/cache": "1.2.0", - "@ultimat3/core": "1.2.0", - "@ultimat3/http": "1.2.0", - "@ultimat3/policy": "1.2.0", - "@ultimat3/schema": "1.2.0" + "@ultimat3/cache": "2.0.0", + "@ultimat3/core": "2.0.0", + "@ultimat3/http": "2.0.0", + "@ultimat3/policy": "2.0.0", + "@ultimat3/schema": "2.0.0" } } diff --git a/packages/admin/package.json b/packages/admin/package.json index a3305590..feec076e 100644 --- a/packages/admin/package.json +++ b/packages/admin/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/admin", - "version": "1.2.0", + "version": "2.0.0", "description": "Two dashboards: the /_x framework dev panels and the generated, AI-first app admin", "license": "MIT", "type": "module", @@ -32,20 +32,20 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/action": "1.2.0", - "@ultimat3/ai": "1.2.0", - "@ultimat3/cache": "1.2.0", - "@ultimat3/core": "1.2.0", - "@ultimat3/db": "1.2.0", - "@ultimat3/entity": "1.2.0", - "@ultimat3/i18n": "1.2.0", - "@ultimat3/jobs": "1.2.0", - "@ultimat3/mcp": "1.2.0", - "@ultimat3/money": "1.2.0", - "@ultimat3/policy": "1.2.0", - "@ultimat3/query": "1.2.0", - "@ultimat3/render": "1.2.0", - "@ultimat3/schema": "1.2.0", - "@ultimat3/ui": "1.2.0" + "@ultimat3/action": "2.0.0", + "@ultimat3/ai": "2.0.0", + "@ultimat3/cache": "2.0.0", + "@ultimat3/core": "2.0.0", + "@ultimat3/db": "2.0.0", + "@ultimat3/entity": "2.0.0", + "@ultimat3/i18n": "2.0.0", + "@ultimat3/jobs": "2.0.0", + "@ultimat3/mcp": "2.0.0", + "@ultimat3/money": "2.0.0", + "@ultimat3/policy": "2.0.0", + "@ultimat3/query": "2.0.0", + "@ultimat3/render": "2.0.0", + "@ultimat3/schema": "2.0.0", + "@ultimat3/ui": "2.0.0" } } diff --git a/packages/ai/package.json b/packages/ai/package.json index feabbd9e..279635ff 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/ai", - "version": "1.2.0", + "version": "2.0.0", "description": "LLM gateway, versioned prompts, evals as tests, embeddings, hybrid vector search, RAG", "license": "MIT", "type": "module", @@ -31,13 +31,13 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/action": "1.2.0", - "@ultimat3/cache": "1.2.0", - "@ultimat3/core": "1.2.0", - "@ultimat3/db": "1.2.0", - "@ultimat3/money": "1.2.0", - "@ultimat3/policy": "1.2.0", - "@ultimat3/schema": "1.2.0", - "@ultimat3/time": "1.2.0" + "@ultimat3/action": "2.0.0", + "@ultimat3/cache": "2.0.0", + "@ultimat3/core": "2.0.0", + "@ultimat3/db": "2.0.0", + "@ultimat3/money": "2.0.0", + "@ultimat3/policy": "2.0.0", + "@ultimat3/schema": "2.0.0", + "@ultimat3/time": "2.0.0" } } diff --git a/packages/auth/package.json b/packages/auth/package.json index b984196b..264b85a0 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/auth", - "version": "1.2.0", + "version": "2.0.0", "description": "Sessions, passwords, OAuth, MFA and api keys — resolved to one Actor", "license": "MIT", "type": "module", @@ -31,8 +31,8 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "1.2.0", - "@ultimat3/db": "1.2.0", - "@ultimat3/schema": "1.2.0" + "@ultimat3/core": "2.0.0", + "@ultimat3/db": "2.0.0", + "@ultimat3/schema": "2.0.0" } } diff --git a/packages/cache/package.json b/packages/cache/package.json index 38e5ef25..11c111c9 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/cache", - "version": "1.2.0", + "version": "2.0.0", "description": "Tagged caching: request memo, LRU, Redis, CDN — one invalidation graph", "license": "MIT", "type": "module", @@ -31,6 +31,6 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "1.2.0" + "@ultimat3/core": "2.0.0" } } diff --git a/packages/cli/package.json b/packages/cli/package.json index 87c47bbf..1c8e65ab 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/cli", - "version": "1.2.0", + "version": "2.0.0", "description": "The `x` binary: new, dev, build, verify, generate, db, mcp, doctor, deploy", "license": "MIT", "type": "module", @@ -35,28 +35,28 @@ "dev": "bun run src/bin.ts dev" }, "dependencies": { - "@ultimat3/action": "1.2.0", - "@ultimat3/admin": "1.2.0", - "@ultimat3/ai": "1.2.0", - "@ultimat3/cache": "1.2.0", - "@ultimat3/core": "1.2.0", - "@ultimat3/db": "1.2.0", - "@ultimat3/entity": "1.2.0", - "@ultimat3/http": "1.2.0", - "@ultimat3/i18n": "1.2.0", - "@ultimat3/jobs": "1.2.0", - "@ultimat3/mail": "1.2.0", - "@ultimat3/manifest": "1.2.0", - "@ultimat3/mcp": "1.2.0", - "@ultimat3/policy": "1.2.0", - "@ultimat3/pwa": "1.2.0", - "@ultimat3/query": "1.2.0", - "@ultimat3/realtime": "1.2.0", - "@ultimat3/render": "1.2.0", - "@ultimat3/schema": "1.2.0", - "@ultimat3/seo": "1.2.0", - "@ultimat3/storage": "1.2.0", - "@ultimat3/testing": "1.2.0", - "@ultimat3/time": "1.2.0" + "@ultimat3/action": "2.0.0", + "@ultimat3/admin": "2.0.0", + "@ultimat3/ai": "2.0.0", + "@ultimat3/cache": "2.0.0", + "@ultimat3/core": "2.0.0", + "@ultimat3/db": "2.0.0", + "@ultimat3/entity": "2.0.0", + "@ultimat3/http": "2.0.0", + "@ultimat3/i18n": "2.0.0", + "@ultimat3/jobs": "2.0.0", + "@ultimat3/mail": "2.0.0", + "@ultimat3/manifest": "2.0.0", + "@ultimat3/mcp": "2.0.0", + "@ultimat3/policy": "2.0.0", + "@ultimat3/pwa": "2.0.0", + "@ultimat3/query": "2.0.0", + "@ultimat3/realtime": "2.0.0", + "@ultimat3/render": "2.0.0", + "@ultimat3/schema": "2.0.0", + "@ultimat3/seo": "2.0.0", + "@ultimat3/storage": "2.0.0", + "@ultimat3/testing": "2.0.0", + "@ultimat3/time": "2.0.0" } } diff --git a/packages/core/package.json b/packages/core/package.json index a0a96bc9..d7f68741 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/core", - "version": "1.2.0", + "version": "2.0.0", "description": "Ultimate's foundation: errors, context, env, config, clock, ids, logging, telemetry, lifecycle", "license": "MIT", "type": "module", diff --git a/packages/create-ultimate/package.json b/packages/create-ultimate/package.json index 20671ada..bce9619c 100644 --- a/packages/create-ultimate/package.json +++ b/packages/create-ultimate/package.json @@ -1,6 +1,6 @@ { "name": "create-ultimate", - "version": "1.2.0", + "version": "2.0.0", "description": "bunx create-ultimate myapp — scaffold an Ultimate monorepo", "license": "MIT", "type": "module", @@ -34,6 +34,6 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/cli": "1.2.0" + "@ultimat3/cli": "2.0.0" } } diff --git a/packages/db/package.json b/packages/db/package.json index 37c37a7a..4f09d112 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/db", - "version": "1.2.0", + "version": "2.0.0", "description": "Postgres access, transactions, migrations and drift detection", "license": "MIT", "type": "module", @@ -31,7 +31,7 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "1.2.0" + "@ultimat3/core": "2.0.0" }, "peerDependencies": { "@electric-sql/pglite": ">=0.5.0" diff --git a/packages/entity/package.json b/packages/entity/package.json index a5ae311e..9be03b60 100644 --- a/packages/entity/package.json +++ b/packages/entity/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/entity", - "version": "1.2.0", + "version": "2.0.0", "description": "A table + its domain type + invariants the database also enforces", "license": "MIT", "type": "module", @@ -31,8 +31,8 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "1.2.0", - "@ultimat3/db": "1.2.0", - "@ultimat3/schema": "1.2.0" + "@ultimat3/core": "2.0.0", + "@ultimat3/db": "2.0.0", + "@ultimat3/schema": "2.0.0" } } diff --git a/packages/flags/package.json b/packages/flags/package.json index b95edb80..29bc7bea 100644 --- a/packages/flags/package.json +++ b/packages/flags/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/flags", - "version": "1.2.0", + "version": "2.0.0", "description": "Feature flags: permanent switches, and temporary ones that cannot be forgotten", "license": "MIT", "type": "module", @@ -31,6 +31,6 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "1.2.0" + "@ultimat3/core": "2.0.0" } } diff --git a/packages/http/package.json b/packages/http/package.json index 19b27eca..caaf3547 100644 --- a/packages/http/package.json +++ b/packages/http/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/http", - "version": "1.2.0", + "version": "2.0.0", "description": "Owned request lifecycle over Bun.serve: router, ordered pipeline, problem+json errors", "license": "MIT", "type": "module", @@ -31,9 +31,9 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "1.2.0", - "@ultimat3/i18n": "1.2.0", - "@ultimat3/schema": "1.2.0", - "@ultimat3/time": "1.2.0" + "@ultimat3/core": "2.0.0", + "@ultimat3/i18n": "2.0.0", + "@ultimat3/schema": "2.0.0", + "@ultimat3/time": "2.0.0" } } diff --git a/packages/i18n/package.json b/packages/i18n/package.json index ea2f668e..cb4c1e08 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/i18n", - "version": "1.2.0", + "version": "2.0.0", "description": "Dependency-free translator, catalog flattening, locale negotiation and loud missing-key rendering", "license": "MIT", "type": "module", @@ -31,6 +31,6 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "1.2.0" + "@ultimat3/core": "2.0.0" } } diff --git a/packages/jobs/package.json b/packages/jobs/package.json index 364b012d..119f62cf 100644 --- a/packages/jobs/package.json +++ b/packages/jobs/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/jobs", - "version": "1.2.0", + "version": "2.0.0", "description": "Durable background work: steps, transactional outbox, cron tasks, one driver interface", "license": "MIT", "type": "module", @@ -32,9 +32,9 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "1.2.0", - "@ultimat3/entity": "1.2.0", - "@ultimat3/schema": "1.2.0", - "@ultimat3/time": "1.2.0" + "@ultimat3/core": "2.0.0", + "@ultimat3/entity": "2.0.0", + "@ultimat3/schema": "2.0.0", + "@ultimat3/time": "2.0.0" } } diff --git a/packages/mail/package.json b/packages/mail/package.json index 66ab1dd8..36ea4503 100644 --- a/packages/mail/package.json +++ b/packages/mail/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/mail", - "version": "1.2.0", + "version": "2.0.0", "description": "Transactional email as data: one template renders HTML and text, sent through a job.", "license": "MIT", "type": "module", @@ -31,10 +31,10 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "1.2.0", - "@ultimat3/i18n": "1.2.0", - "@ultimat3/jobs": "1.2.0", - "@ultimat3/schema": "1.2.0", - "@ultimat3/time": "1.2.0" + "@ultimat3/core": "2.0.0", + "@ultimat3/i18n": "2.0.0", + "@ultimat3/jobs": "2.0.0", + "@ultimat3/schema": "2.0.0", + "@ultimat3/time": "2.0.0" } } diff --git a/packages/manifest/package.json b/packages/manifest/package.json index adddac3b..466741bc 100644 --- a/packages/manifest/package.json +++ b/packages/manifest/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/manifest", - "version": "1.2.0", + "version": "2.0.0", "description": "x.manifest.json: deterministic generated facts, contract diff, AGENTS.md budget", "license": "MIT", "type": "module", @@ -31,10 +31,10 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/action": "1.2.0", - "@ultimat3/core": "1.2.0", - "@ultimat3/entity": "1.2.0", - "@ultimat3/jobs": "1.2.0", - "@ultimat3/query": "1.2.0" + "@ultimat3/action": "2.0.0", + "@ultimat3/core": "2.0.0", + "@ultimat3/entity": "2.0.0", + "@ultimat3/jobs": "2.0.0", + "@ultimat3/query": "2.0.0" } } diff --git a/packages/mcp/package.json b/packages/mcp/package.json index 08cd2862..dd5c4dcf 100644 --- a/packages/mcp/package.json +++ b/packages/mcp/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/mcp", - "version": "1.2.0", + "version": "2.0.0", "description": "MCP server, dev tools, and the action-to-tool projection — one authz system, two surfaces", "license": "MIT", "type": "module", @@ -31,12 +31,12 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/action": "1.2.0", - "@ultimat3/core": "1.2.0", - "@ultimat3/entity": "1.2.0", - "@ultimat3/jobs": "1.2.0", - "@ultimat3/policy": "1.2.0", - "@ultimat3/query": "1.2.0", - "@ultimat3/schema": "1.2.0" + "@ultimat3/action": "2.0.0", + "@ultimat3/core": "2.0.0", + "@ultimat3/entity": "2.0.0", + "@ultimat3/jobs": "2.0.0", + "@ultimat3/policy": "2.0.0", + "@ultimat3/query": "2.0.0", + "@ultimat3/schema": "2.0.0" } } diff --git a/packages/money/package.json b/packages/money/package.json index dfe43405..78263fdf 100644 --- a/packages/money/package.json +++ b/packages/money/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/money", - "version": "1.2.0", + "version": "2.0.0", "description": "Integer minor units with an attached currency: arithmetic, allocation, rounding, Intl formatting", "license": "MIT", "type": "module", @@ -31,7 +31,7 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "1.2.0", - "@ultimat3/schema": "1.2.0" + "@ultimat3/core": "2.0.0", + "@ultimat3/schema": "2.0.0" } } diff --git a/packages/policy/package.json b/packages/policy/package.json index ff7f39ba..5327b84d 100644 --- a/packages/policy/package.json +++ b/packages/policy/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/policy", - "version": "1.2.0", + "version": "2.0.0", "description": "The one authz rule, evaluated identically in every surface", "license": "MIT", "type": "module", @@ -31,6 +31,6 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "1.2.0" + "@ultimat3/core": "2.0.0" } } diff --git a/packages/pwa/package.json b/packages/pwa/package.json index df4b7ea9..f643ac6f 100644 --- a/packages/pwa/package.json +++ b/packages/pwa/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/pwa", - "version": "1.2.0", + "version": "2.0.0", "description": "Generated service worker, web manifest, icons, push and version-skew handling.", "license": "MIT", "type": "module", @@ -31,6 +31,6 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "1.2.0" + "@ultimat3/core": "2.0.0" } } diff --git a/packages/query/package.json b/packages/query/package.json index f199a412..2ccde74e 100644 --- a/packages/query/package.json +++ b/packages/query/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/query", - "version": "1.2.0", + "version": "2.0.0", "description": "The query primitive: a policy-checked read, optionally live, with cursor pagination and an incremental matcher", "license": "MIT", "type": "module", @@ -31,10 +31,10 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/cache": "1.2.0", - "@ultimat3/core": "1.2.0", - "@ultimat3/http": "1.2.0", - "@ultimat3/policy": "1.2.0", - "@ultimat3/schema": "1.2.0" + "@ultimat3/cache": "2.0.0", + "@ultimat3/core": "2.0.0", + "@ultimat3/http": "2.0.0", + "@ultimat3/policy": "2.0.0", + "@ultimat3/schema": "2.0.0" } } diff --git a/packages/realtime/package.json b/packages/realtime/package.json index 4bca639a..8d3d6831 100644 --- a/packages/realtime/package.json +++ b/packages/realtime/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/realtime", - "version": "1.2.0", + "version": "2.0.0", "description": "Three-tier realtime: channels, live queries, local-first sync — one protocol, one mutator shape", "license": "MIT", "type": "module", @@ -32,8 +32,8 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "1.2.0", - "@ultimat3/query": "1.2.0", + "@ultimat3/core": "2.0.0", + "@ultimat3/query": "2.0.0", "nats": "2.29.3" } } diff --git a/packages/render/package.json b/packages/render/package.json index e6028c67..13a822b4 100644 --- a/packages/render/package.json +++ b/packages/render/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/render", - "version": "1.2.0", + "version": "2.0.0", "description": "The route primitive and the five render modes: static, isr, ssr, stream, spa.", "license": "MIT", "type": "module", @@ -31,10 +31,10 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/cache": "1.2.0", - "@ultimat3/core": "1.2.0", - "@ultimat3/i18n": "1.2.0", - "@ultimat3/seo": "1.2.0", + "@ultimat3/cache": "2.0.0", + "@ultimat3/core": "2.0.0", + "@ultimat3/i18n": "2.0.0", + "@ultimat3/seo": "2.0.0", "sass": "1.102.0" } } diff --git a/packages/schema/package.json b/packages/schema/package.json index 578e4988..3de1216a 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/schema", - "version": "1.2.0", + "version": "2.0.0", "description": "Ultimate's validation seam: Standard Schema interface, the t namespace, JSON Schema output", "license": "MIT", "type": "module", diff --git a/packages/seo/package.json b/packages/seo/package.json index 63d33546..fb1d8260 100644 --- a/packages/seo/package.json +++ b/packages/seo/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/seo", - "version": "1.2.0", + "version": "2.0.0", "description": "Enforced SEO: typed meta, JSON-LD, sitemap, robots, feeds, responsive images, perf budgets", "license": "MIT", "type": "module", @@ -31,6 +31,6 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "1.2.0" + "@ultimat3/core": "2.0.0" } } diff --git a/packages/storage/package.json b/packages/storage/package.json index ab5be244..653c7b91 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/storage", - "version": "1.2.0", + "version": "2.0.0", "description": "Named disks over Bun.file and Bun.s3: safe keys, signed URLs, sniffed uploads", "license": "MIT", "type": "module", @@ -31,6 +31,6 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "1.2.0" + "@ultimat3/core": "2.0.0" } } diff --git a/packages/testing/package.json b/packages/testing/package.json index d3481613..b0bb52a7 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/testing", - "version": "1.2.0", + "version": "2.0.0", "description": "Test harness: cloned template DBs per worker, frozen clock, sealed network, 6 test types", "license": "MIT", "type": "module", @@ -33,12 +33,12 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/cache": "1.2.0", - "@ultimat3/core": "1.2.0", - "@ultimat3/db": "1.2.0", - "@ultimat3/entity": "1.2.0", - "@ultimat3/jobs": "1.2.0", - "@ultimat3/mail": "1.2.0", - "@ultimat3/time": "1.2.0" + "@ultimat3/cache": "2.0.0", + "@ultimat3/core": "2.0.0", + "@ultimat3/db": "2.0.0", + "@ultimat3/entity": "2.0.0", + "@ultimat3/jobs": "2.0.0", + "@ultimat3/mail": "2.0.0", + "@ultimat3/time": "2.0.0" } } diff --git a/packages/time/package.json b/packages/time/package.json index 1361d20f..7cca2cf5 100644 --- a/packages/time/package.json +++ b/packages/time/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/time", - "version": "1.2.0", + "version": "2.0.0", "description": "UTC instants, DST-correct zone math, cron, durations and Intl formatting with an explicit timezone", "license": "MIT", "type": "module", @@ -31,6 +31,6 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "1.2.0" + "@ultimat3/core": "2.0.0" } } diff --git a/packages/ui/package.json b/packages/ui/package.json index 10ffd031..aebbe28c 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/ui", - "version": "1.2.0", + "version": "2.0.0", "description": "SolidJS design system: semantic design tokens, dark/RTL-ready SCSS modules, a11y primitives", "license": "MIT", "type": "module", @@ -39,10 +39,10 @@ "icons": "bun run src/icons/build-icons.ts" }, "dependencies": { - "@ultimat3/core": "1.2.0", - "@ultimat3/i18n": "1.2.0", - "@ultimat3/money": "1.2.0", - "@ultimat3/time": "1.2.0" + "@ultimat3/core": "2.0.0", + "@ultimat3/i18n": "2.0.0", + "@ultimat3/money": "2.0.0", + "@ultimat3/time": "2.0.0" }, "peerDependencies": { "solid-js": "^1.9.0" diff --git a/wiki/Actions.md b/wiki/Actions.md index c3de9a2e..5f3015f4 100644 --- a/wiki/Actions.md +++ b/wiki/Actions.md @@ -125,7 +125,7 @@ export const likePost = mutator({ | Authz | the `server` half carries the policy; the `local` half is presentation only and never a security boundary | | Never | let `local` do I/O, randomness, or `Date.now()` | -**Replayability rule:** `local` is re-executed on every rebase — after each server confirmation, on reconnect, and when a conflicting remote write arrives. It must be a pure function of `(tx, input)`. I/O, `Math.random()`, `crypto.randomUUID()`, or a wall-clock read makes the local timeline diverge from the server's, and the divergence surfaces as flicker, then as wrong data. Ids and timestamps come from the input, generated once at call time. Tier 3 local-first (`persist: true`) is v2; mutators work today at realtime tiers 1–2 ([Realtime](Realtime)). +**Replayability rule:** `local` is re-executed on every rebase — after each server confirmation, on reconnect, and when a conflicting remote write arrives. It must be a pure function of `(tx, input)`. I/O, `Math.random()`, `crypto.randomUUID()`, or a wall-clock read makes the local timeline diverge from the server's, and the divergence surfaces as flicker, then as wrong data. Ids and timestamps come from the input, generated once at call time. Tier 3 local-first (`persist: true`) is not in 2.0.0; mutators work today at realtime tiers 1–2 ([Realtime](Realtime)). ## Errors diff --git a/wiki/Admin-Dashboard.md b/wiki/Admin-Dashboard.md index 40bb01a8..93a6ffa6 100644 --- a/wiki/Admin-Dashboard.md +++ b/wiki/Admin-Dashboard.md @@ -104,7 +104,7 @@ Own routes in your own app. Never fork the framework. | Branding | `defineTheme({ colors, radius, font })` — the one override seam, validated not escaped → [Theming](Theming) | | Hide an entity | the entity's policy denies `admin:read` — visibility is authz, not configuration | -No plugin API in v1 ([axiom](Home)). The extension point is that the admin is your app. +No plugin API, in 1.x or 2.0.0 ([axiom](Home)). The extension point is that the admin is your app. ## Deployment diff --git a/wiki/Building-Your-Own-Base.md b/wiki/Building-Your-Own-Base.md index 815f0732..5605158f 100644 --- a/wiki/Building-Your-Own-Base.md +++ b/wiki/Building-Your-Own-Base.md @@ -7,7 +7,7 @@ wrapping and exporting your own factory. No fork, no monkey-patch, no plugin API wait for. Everything downstream treats the result identically — registry, manifest, projections, admin, -MCP, `x verify`. Verified against 1.2.0. +MCP, `x verify`. Verified against 1.2.0; not re-run against 2.0.0, whose 33 breaking entries are in [`CHANGELOG.md`](https://github.com/developerz-ai/ultimate/blob/main/CHANGELOG.md). Put the factory in a surface both `app/` and `api/` may import — `apps/web/shared/base/` in the generated layout ([Project layout](Project-Layout)). diff --git a/wiki/Caching-And-Invalidation.md b/wiki/Caching-And-Invalidation.md index 01e6797b..9a9a0876 100644 --- a/wiki/Caching-And-Invalidation.md +++ b/wiki/Caching-And-Invalidation.md @@ -81,7 +81,7 @@ export const publishPost = action({ Fan-out runs **after the handler resolves, in the same call** — `bustAfterCommit` awaits `invalidateTags()` directly (`packages/action/src/cache-gate.ts`), never through the outbox `As of 2026-08`. A handler that throws never reaches it, so a rolled-back write never purges; a process that dies between the commit and the fan-out leaves those entries until their TTL. -> **Tier 3 invalidation is slot-local on `main`, and was not through 1.2.0.** The Lua script `DEL`ed keys it never declared in `KEYS`, which single-node Redis tolerates and **Dragonfly and Redis Cluster reject** — a cluster cannot route a key it was not told about. The script now returns the member list and the tier deletes the value keys client-side, one key per `DEL`, so every delete is slot-local. On 1.2.0 and earlier: use single-node Redis, or drop the shared tier → [Known gaps](Known-Gaps). +> **Tier 3 invalidation is slot-local as of 2.0.0, and was not through 1.2.0.** The Lua script `DEL`ed keys it never declared in `KEYS`, which single-node Redis tolerates and **Dragonfly and Redis Cluster reject** — a cluster cannot route a key it was not told about. The script now returns the member list and the tier deletes the value keys client-side, one key per `DEL`, so every delete is slot-local. On 1.2.0 and earlier: use single-node Redis, or drop the shared tier → [Known gaps](Known-Gaps). There is exactly one fan-out entry point in the implementation (`invalidateTags()`); no caller reaches a tier directly. Tier failures are collected into an invalidation report — **a cache tier may never fail a business write.** diff --git a/wiki/Configuration.md b/wiki/Configuration.md index 20b28a90..1e73d0f6 100644 --- a/wiki/Configuration.md +++ b/wiki/Configuration.md @@ -73,7 +73,7 @@ Better Auth, wrapped. Sessions live in Postgres. Authorization is **not** here | field | type | default | notes | |---|---|---|---| -| `jobs.driver` | `'postgres' \| 'redis' \| 'nats'` | `'postgres'` | `postgres` needs no extra infra and is the only shipped production driver. **`redis` and `nats` are v2** — the stubs throw `X_NOT_IMPLEMENTED` ([Jobs and workflows](Jobs-And-Workflows)) | +| `jobs.driver` | `'postgres' \| 'redis' \| 'nats'` | `'postgres'` | `postgres` needs no extra infra and is the only shipped production driver. **`redis` and `nats` are not in 2.0.0** — the stubs throw `X_NOT_IMPLEMENTED` ([Jobs and workflows](Jobs-And-Workflows)) | | `jobs.queues` | `string[]` | `['default']` | a `worker` runs one pool per queue in `WORKER_QUEUES` | | `jobs.concurrency` | `number` | `8` | per pool, per process | | `jobs.retry.attempts` | `number` | `5` | per-job `retry` overrides | @@ -90,7 +90,7 @@ Better Auth, wrapped. Sessions live in Postgres. Authorization is **not** here | field | type | default | notes | |---|---|---|---| | `realtime.enabled` | `boolean` | `false` | off unless the app turns it on | -| `realtime.tier` | `'channels' \| 'live-queries' \| 'local-first'` | `'channels'` | **names, not numbers**. `channels` and `live-queries` in v1; `local-first` in v2 ([Realtime](Realtime)) | +| `realtime.tier` | `'channels' \| 'live-queries' \| 'local-first'` | `'channels'` | **names, not numbers**. `channels` and `live-queries` ship; `local-first` is not in 2.0.0 ([Realtime](Realtime)) | | `realtime.transport` | `'memory' \| 'nats' \| 'redis'` | `'memory'` | `memory` = in-process, single node, dev and small deploys. `redis` type-checks and is never built — `selectTransport` resolves in-process or NATS only | | `realtime.urlEnv` | `string` | — | the **env key name**, never a URL. Required unless `memory`; missing → `X_CONFIG_INVALID` | | `realtime.heartbeatMs` | `number` | `15000` | **read by nothing** `As of 2026-08` → [Known gaps](Known-Gaps). The socket heartbeat is the client's: `new LiveClient({ heartbeatMs })`, same 15s default, kept equal by hand because browser code cannot read server config ([Realtime](Realtime)) | @@ -303,7 +303,7 @@ tryResolveEnvironment() ?? 'development'; // the same, `undefined` instead `@ultimat3/core` is the **only** reader of `ULTIMATE_ENV` and `Environment` is the only spelling of a deploy, `As of 2026-08`. `@ultimat3/seo` exported a second `resolveEnvironment` with its own union -through 1.2.0; it exports neither that nor `SeoEnvironment` on `main`, and its `'preview'` is +through 1.2.0; as of 2.0.0 it exports neither that nor `SeoEnvironment`, and its `'preview'` is `'staging'` → [Known gaps](Known-Gaps). ## `.env.example` — a projection, never a second list diff --git a/wiki/FAQ.md b/wiki/FAQ.md index 27309050..6b99b387 100644 --- a/wiki/FAQ.md +++ b/wiki/FAQ.md @@ -8,7 +8,7 @@ Honest answers. Where something is not built yet, it says so. **`As of 2026-08`.** Stable API, semver from here. 28 `@ultimat3/*` packages plus the unscoped `create-ultimate` — **29 in all** — are **versioned** in lockstep: one version, one commit, one tag. -**Publication is not in lockstep.** 28 of the 29 are on npm; `@ultimat3/flags` has never been published — the registry answers 404 at every version, verified `As of 2026-08` — and nothing in the repo notices, because every consumer resolves it through the workspace ([Known gaps](Known-Gaps)). 1.1.0 was the first release the workflow published over OIDC trusted publishing, with provenance; 1.0.0 was the manual bootstrap. +**Publication is not in lockstep.** 28 of the 29 are on npm; `@ultimat3/flags` has never been published at any version, 2.0.0 included — the registry answers 404 at every version, verified `As of 2026-08` — and nothing in the repo notices, because every consumer resolves it through the workspace ([Known gaps](Known-Gaps)). 1.1.0 was the first release the workflow published over OIDC trusted publishing, with provenance; 1.0.0 was the manual bootstrap. That is exactly what the version claims — a stable API under semver, not a promise about your infrastructure. @@ -18,7 +18,7 @@ What it does **not** claim: |---|---| | A multi-node realtime result | the 50k forced-restart benchmark **is** measured and committed, but on **one** `sync` node over `InProcessTransport` — it never crossed NATS. Fanout across nodes, throughput, and per-node socket capacity are all still targets, not results ([Realtime](Realtime)) | | The two-platform deploy proof | all three build targets ship — `x build --target docker`, `x build --target binary`, `x build --target static` — and so do the compose files and the Helm chart. The demo app running on Compose **and** K8s from one image, with a rolling restart invisible to connected clients, is milestone 11's remaining item ([Deployment](Deployment)) | -| The v2 set | realtime tier 3 (`persist: true`, local-first), the plugin API, multi-region replication, and the Redis/NATS **job** drivers — all behind the interfaces that ship today. The job drivers throw `X_NOT_IMPLEMENTED` with a runnable `fix:` line rather than pretending to work | +| Not in 2.0.0 | realtime tier 3 (`persist: true`, local-first), the plugin API, multi-region replication, and the Redis/NATS **job** drivers — all behind the interfaces that ship today. The job drivers throw `X_NOT_IMPLEMENTED` with a runnable `fix:` line rather than pretending to work | ### What is actually finished? @@ -108,7 +108,7 @@ Yes. `realtime.tier: 1` with `transport: 'memory'` is the default, and a tier-1 ### What happens if the sync engine doesn't work out? -It is roughly **70% of total effort** and the single largest risk. Tiers 1–2 shipped in milestone 6 and are under semver; tier 3 local-first is v2. The reconnect benchmark that gated topology — 50k sockets, a forced `sync` restart, recovery time and DB load — **is measured at 1.1.0**: all 50,000 reconnected, 49,981 received a channel patch inside the window, p50 54.0s / p90 105.5s, 156,851 connect attempts shed before any query path ([Realtime](Realtime)). That is **reachability** — first patch on the reconnected socket — not consistency; the delivery half is a separate 10,000-client run, **1,666,882 patches received, 0 observed sequence gaps** — a lower bound, since a hole is only visible between two frames one connection received ([Realtime](Realtime)) — and `As of 2026-08` the only run that counts lost patches at all. Both were run on **one** node, so multi-node fanout is still unproven. If the incremental matcher turns out to be the bottleneck, wrapping an existing protocol (Zero's) is an accepted fallback. +It is roughly **70% of total effort** and the single largest risk. Tiers 1–2 shipped in milestone 6 and are under semver; tier 3 local-first is not in 2.0.0. The reconnect benchmark that gated topology — 50k sockets, a forced `sync` restart, recovery time and DB load — **is measured at 1.1.0**: all 50,000 reconnected, 49,981 received a channel patch inside the window, p50 54.0s / p90 105.5s, 156,851 connect attempts shed before any query path ([Realtime](Realtime)). That is **reachability** — first patch on the reconnected socket — not consistency; the delivery half is a separate 10,000-client run, **1,666,882 patches received, 0 observed sequence gaps** — a lower bound, since a hole is only visible between two frames one connection received ([Realtime](Realtime)) — and `As of 2026-08` the only run that counts lost patches at all. Both were run on **one** node, so multi-node fanout is still unproven. If the incremental matcher turns out to be the bottleneck, wrapping an existing protocol (Zero's) is an accepted fallback. ### Why ship realtime last if it's the differentiator? @@ -122,7 +122,7 @@ Stated risk, not a hidden one. `As of 2026-08` long-running Bun processes are le ### Where do plugins fit? -Nowhere in 1.0 — the plugin API is v2. Semver covers the documented surface, not internals, and a plugin API freezes internals permanently. Fork the blessed path if you need something else; extension points earn their existence from real forks, not from speculation. +Nowhere — the plugin API is not in 1.x and not in 2.0.0. Semver covers the documented surface, not internals, and a plugin API freezes internals permanently. Fork the blessed path if you need something else; extension points earn their existence from real forks, not from speculation. ### Will you add an adapter for my host or my ORM? diff --git a/wiki/Getting-Started.md b/wiki/Getting-Started.md index 0a7a0d5f..63b920b6 100644 --- a/wiki/Getting-Started.md +++ b/wiki/Getting-Started.md @@ -177,4 +177,4 @@ $ x verify ## Status -`As of 2026-08`. Stable API — semver from here ([Upgrading](Upgrading)). The repository holds 28 `@ultimat3/*` packages plus the unscoped `create-ultimate` and versions all 29 in lockstep; **28 of them are on npm**, all at 1.2.0. **`@ultimat3/flags` has never been published** — verified against the registry `As of 2026-08`, where it is a 404 — so `bun add @ultimat3/flags` fails and feature flags are reachable only from a checkout. Milestones 0–10 are ✅; milestone 11 is 🚧, open on its two-platform deploy proof. Realtime tiers 1–2 are v1, tier 3 (local-first) is v2. The 50k-socket forced-restart benchmark **is measured and committed** — first patch on the reconnected socket at p50 54.0s / p90 105.5s, on one node; delivery is a second run, 10,000 clients, 1,666,882 patches, 0 observed sequence gaps ([Realtime](Realtime)). Status markers come from [`docs/idea/14-roadmap.md`](https://github.com/developerz-ai/ultimate/blob/main/docs/idea/14-roadmap.md). See [FAQ](FAQ). +`As of 2026-08`. Stable API — semver from here ([Upgrading](Upgrading)). The repository holds 28 `@ultimat3/*` packages plus the unscoped `create-ultimate` and versions all 29 in lockstep at **2.0.0** — the first major, 33 entries marked `BREAKING —` and no codemod ([Upgrading](Upgrading)). Publication is not in lockstep: **28 of them are on npm**, and **`@ultimat3/flags` has never been published** — verified against the registry `As of 2026-08`, where it is a 404 — so `bun add @ultimat3/flags` fails and feature flags are reachable only from a checkout. Milestones 0–10 are ✅; milestone 11 is 🚧, open on its two-platform deploy proof. Realtime tiers 1–2 ship; tier 3 (local-first) is not in 2.0.0. The 50k-socket forced-restart benchmark **is measured and committed** — first patch on the reconnected socket at p50 54.0s / p90 105.5s, on one node; delivery is a second run, 10,000 clients, 1,666,882 patches, 0 observed sequence gaps ([Realtime](Realtime)). Status markers come from [`docs/idea/14-roadmap.md`](https://github.com/developerz-ai/ultimate/blob/main/docs/idea/14-roadmap.md). See [FAQ](FAQ). diff --git a/wiki/Home.md b/wiki/Home.md index 4fc3ed04..616d0e73 100644 --- a/wiki/Home.md +++ b/wiki/Home.md @@ -2,7 +2,7 @@ A full-stack, Bun-only, opinionated framework: Rails' philosophy applied to Bun + Postgres + SolidJS, where the primary developer is an AI agent and the secondary developer is a tired senior engineer working through their own AI agent and AI reviewer. -**`As of 2026-08`.** 28 `@ultimat3/*` packages plus the unscoped `create-ultimate` — 29 in all — version in lockstep; **28 are on npm**, all at 1.2.0, and `@ultimat3/flags` is not published at all ([Known gaps](Known-Gaps)). The API is stable and semver applies from here ([Upgrading](Upgrading)). 1.1.0 was the **first release published by the workflow**, over OIDC trusted publishing with provenance attached; 1.0.0 was the manual bootstrap. Milestones 0–10 are ✅; milestone 11 is 🚧, still open on the two-platform deploy proof — 1.1.0 gave a scaffolded app a real deployable artifact, which is progress toward it, not the proof. +**`As of 2026-08`.** 28 `@ultimat3/*` packages plus the unscoped `create-ultimate` — 29 in all — version in lockstep at **2.0.0**, the first major: 33 changelog entries are marked `BREAKING —` and none ships a codemod ([Upgrading](Upgrading)). Publication is not in lockstep — **28 are on npm** and `@ultimat3/flags` is not published at all ([Known gaps](Known-Gaps)). 1.1.0 was the **first release published by the workflow**, over OIDC trusted publishing with provenance attached; 1.0.0 was the manual bootstrap. Milestones 0–10 are ✅; milestone 11 is 🚧, still open on the two-platform deploy proof — 1.1.0 gave a scaffolded app a real deployable artifact, which is progress toward it, not the proof. The realtime restart numbers are **measured and committed**, in two halves that answer different questions ([`scripts/bench/results/`](https://github.com/developerz-ai/ultimate/tree/main/scripts/bench/results)). **Reachability:** 50,000 real WebSocket clients against a **single** `sync` node over `InProcessTransport`, `SIGKILL`ed with no drain — all 50,000 reconnected, 49,981 received a channel patch inside the window, p50 **54.0s** / p90 **105.5s** / max **145.7s**, and 156,851 connect attempts shed by the `AcceptBudget` before any query path. That times the first patch on the reconnected socket and nothing after it; it was published as "time-to-consistent" until 2026-08 and could not see a lost patch, so the name changed and the timings did not. **Delivery:** 10,000 clients, same forced restart, a probe every 200ms — **1,666,882 channel patches received, 0 observed sequence gaps**, `As of 2026-08` the only run with delivery accounting. That counts holes between frames a connection actually received, so it is a lower bound: no client observed a lost frame, which is not the same claim as none was lost. Both are **per-node recovery**: neither crossed NATS, so neither is a multi-node result and neither is a throughput figure → [Realtime](Realtime). This wiki is the only public documentation surface; there is no separate site. diff --git a/wiki/Jobs-And-Workflows.md b/wiki/Jobs-And-Workflows.md index 1da165c0..5b936306 100644 --- a/wiki/Jobs-And-Workflows.md +++ b/wiki/Jobs-And-Workflows.md @@ -172,18 +172,18 @@ export interface JobDriver { The three optional members degrade rather than refuse: no `introspect` is `x jobs ls` with nothing to list, no `backfills` is a `backfill()` pass that runs with no bookkeeping, and no `close` is a driver holding nothing to hand back. -Two implementations ship in 1.0.0. Two more are **v2** — interface-complete stubs, so an app typechecks against them, and every method throws `X_NOT_IMPLEMENTED` with a runnable `fix:` rather than silently dropping a job. +Two implementations ship in 1.0.0. Two more are **not in 2.0.0** — interface-complete stubs, so an app typechecks against them, and every method throws `X_NOT_IMPLEMENTED` with a runnable `fix:` rather than silently dropping a job. | Driver | Status `As of 2026-08` | When | Trade-off | |---|---|---|---| | `postgres` (default) | **shipped** | always, up to ~thousands of jobs/sec. `x dev` runs it too, against the embedded PGlite | outbox is free (same DB, same tx); `SELECT ... FOR UPDATE SKIP LOCKED` claiming; zero extra infra | | `memory` | **shipped**, not a `jobs.driver` value | tests and fixtures — reached through `createMemoryDriver()`, and as `x jobs drain --to memory` | in-process; nothing survives a restart | -| `redis` | **v2 — throws `X_NOT_IMPLEMENTED`** | high-throughput, short jobs | would need the outbox relay; loses "queue state in one backup" | -| `nats` | **v2 — throws `X_NOT_IMPLEMENTED`** | very high fanout, multi-region, JetStream retention | strongest delivery semantics, most operational surface | +| `redis` | **not in 2.0.0 — throws `X_NOT_IMPLEMENTED`** | high-throughput, short jobs | would need the outbox relay; loses "queue state in one backup" | +| `nats` | **not in 2.0.0 — throws `X_NOT_IMPLEMENTED`** | very high fanout, multi-region, JetStream retention | strongest delivery semantics, most operational surface | `jobs.driver` in `app.config.ts` accepts `'postgres' | 'redis' | 'nats'` — and only `'postgres'` runs. Setting it to `redis` or `nats` typechecks and boots, then throws on the first enqueue: deliberate, and why the stubs exist instead of an absent export. -`x jobs drain --to ` moves in-flight rows between drivers, and `--to memory` is the only target that completes today: `--to redis` and `--to nats` construct the target and fail on the first enqueue with `X_NOT_IMPLEMENTED`. The cross-driver migration procedure is v2 — see [Upgrading](Upgrading). +`x jobs drain --to ` moves in-flight rows between drivers, and `--to memory` is the only target that completes today: `--to redis` and `--to nats` construct the target and fail on the first enqueue with `X_NOT_IMPLEMENTED`. The cross-driver migration procedure is not in 2.0.0 — see [Upgrading](Upgrading). ## Dead letter @@ -224,7 +224,7 @@ Every command supports `--json`. See [CLI reference](CLI-Reference). | `X_IDEMPOTENCY_CONFLICT` | same key, different payload, or still in flight | fresh key for a different payload; otherwise retry after the first settles | | `X_DRAINING` | claim attempted on a worker that received SIGTERM | none — the job stays queued and another worker claims it | | `X_FORBIDDEN` | the job's actor fails the originating action's policy | grant the permission, or enqueue as a system actor | -| `X_NOT_IMPLEMENTED` | the `redis` or `nats` driver was reached — both are v2 | set `jobs.driver: 'postgres'` in `app.config.ts` (it is already the default) | +| `X_NOT_IMPLEMENTED` | the `redis` or `nats` driver was reached — neither is in 2.0.0 | set `jobs.driver: 'postgres'` in `app.config.ts` (it is already the default) | Full index: [Error codes](Error-Codes). Verbatim error shapes live in each package's `src/errors.ts`. diff --git a/wiki/Known-Gaps.md b/wiki/Known-Gaps.md index 4588ddc5..8fa7ad9b 100644 --- a/wiki/Known-Gaps.md +++ b/wiki/Known-Gaps.md @@ -1,30 +1,30 @@ # Known gaps -Defects and unfinished seams in the **published** release, named here rather than left to be discovered. `As of 2026-08`. +Defects and unfinished seams named here rather than left to be discovered — each row carries the release it was fixed in, if it was, and the workaround for anyone still pinned to 1.x. `As of 2026-08`. A reference manual that hides these is lying to the reader. Source of truth is the *Known gaps* section of [`CHANGELOG.md`](https://github.com/developerz-ai/ultimate/blob/main/CHANGELOG.md); anything below that the changelog does not carry is noted as such. -The version is the footer's, once — a row that says "on 1.2.0, do X" is naming the release **you can install**, and "Fixed on `main`, unreleased" is a fix waiting for the next tag. A past-tense claim about a shipped version (1.0.0's manual bootstrap, 1.1.0's first OIDC publish) is a fact and stays written that way. +The version is the footer's, once — **Fixed in 2.0.0** means the fix is in the release you can install, and a row that says "on 1.2.0, do X" is the workaround for anyone still pinned there. A past-tense claim about a shipped version (1.0.0's manual bootstrap, 1.1.0's first OIDC publish) is a fact and stays written that way. | Gap | Symptom | Work around it by | |---|---|---| -| `docker-compose.prod.yml` | declares `ports: ['3000:3000']` on `web` **and** `replicas: 3` — one host port has exactly one binder, so the second container dies on `Bind for 0.0.0.0:3000 failed: port is already allocated`. **Fixed on `main`, unreleased**: `web` and `sync` declare `replicas: 1` in all four files — the framework's, both tracked apps', and the one `x new` writes — and each header names the two ways up. `sync`'s `PORT` was wrong in the same file and fixed with it: the role binds `PORT + 1`, so `PORT: 3001` opened 3002 while publishing 3001 | on 1.2.0, set `replicas: 1`. To scale those two anyway: drop `ports:` and put your own reverse proxy on the compose network, or climb to the chart's per-role HPA. This is the rung-1 ceiling → [`docs/idea/17-scale-ladder.md`](https://github.com/developerz-ai/ultimate/blob/main/docs/idea/17-scale-ladder.md) | -| MCP tool names are published in `snake_case` and served verbatim | `openapi.json` carries `"x-ultimate": { "mcpTool": "publish_post" }` and `x actions describe --json` reports `mcp.tool: "publish_post"`, while the only name `tools/call` accepts is the export name — `publishPost` ([`packages/mcp/src/from-action.ts`](https://github.com/developerz-ai/ultimate/blob/main/packages/mcp/src/from-action.ts)). An agent handed either catalog calls a tool the server answers ToolNotFound (`-32601`) for. **15 of the 17** `mcpTool` values in the two tracked apps' committed specs name no served tool; the two that work are single-word exports (`summarize`, `health`), which are already their own snake_case form. **Fixed on `main`, unreleased, and BREAKING**: `toToolName` is deleted from `@ultimat3/action` and `@ultimat3/query` and exported by neither, all three publishers spell the export name, and `packages/mcp/src/cross-surface.test.ts` drives a `tools/call` with the name OpenAPI published so a re-derivation is a failing test | on 1.2.0, ignore the published `mcpTool` / `mcp.tool` value and call the **export name** — `x actions list --json` prints it as `name`, and `tools/list` on the app's own MCP surface is the authoritative catalog either way → [MCP and AI](MCP-And-AI) | -| Shared cache tier invalidation | the Lua script `DEL`s keys it never declared in `KEYS`, so it fails on **Dragonfly** and on **Redis Cluster** — a cluster cannot route a key it was not told about. **Fixed on `main`, unreleased**: the script returns the member list and the tier deletes the value keys client-side, one key per `DEL`, so every delete is slot-local | on 1.2.0, single-node Redis, or a cache tier that is not the shared one → [Caching and invalidation](Caching-And-Invalidation) | -| `resolveEnvironment` twice | the name exists in both `@ultimat3/core` and `@ultimat3/seo` with different parameters and different return unions. **Fixed on `main`, unreleased, and BREAKING**: `@ultimat3/seo` exports neither `resolveEnvironment` nor the type `SeoEnvironment`; `RobotsConfig.environment` and `isIndexable()` take core's `Environment`, `'preview'` is spelled `'staging'`, and core gains `tryResolveEnvironment()` for the callers that must answer rather than throw | on 1.2.0, import one with an alias → [Configuration](Configuration) | -| HPAs read `` without a metrics adapter | **the chart half is fixed on `main`, unreleased** — `values.yaml` declares `metricsPort: 9090`, `_helpers.tpl` emits a `metrics` containerPort on every role but `migrate`, `service.yaml` publishes it by name and `templates/servicemonitor.yaml` ships the scrape target. Two things still stand: `serviceMonitor.enabled` defaults **false**, because a cluster without the Prometheus operator has no such CRD and `helm install` would fail on an unknown kind; and turning scraped series into the `Pods` metrics an HPA reads needs a **custom-metrics adapter**, which is the cluster's and never the chart's. App-side this is now closed on `main`, unreleased: `otlpSpanExporter()` and `otlpMetricExporter()` speak OTLP/HTTP JSON and read `OTEL_EXPORTER_OTLP_ENDPOINT` — the variable the chart's `env` already sets — so on 1.2.0 it is read by nothing and traces go to a no-op, and on `main` it works against a collector's `:4318`. gRPC `:4317` is refused by design, and there is no logs signal | set `serviceMonitor.enabled: true` and install a metrics adapter; until then disable the HPAs and pin `replicas`. **Do not hand-add a metrics container port** on `main` — the chart already emits one and a duplicate is rejected by the API server → [Observability](Observability) | +| `docker-compose.prod.yml` | declares `ports: ['3000:3000']` on `web` **and** `replicas: 3` — one host port has exactly one binder, so the second container dies on `Bind for 0.0.0.0:3000 failed: port is already allocated`. **Fixed in 2.0.0**: `web` and `sync` declare `replicas: 1` in all four files — the framework's, both tracked apps', and the one `x new` writes — and each header names the two ways up. `sync`'s `PORT` was wrong in the same file and fixed with it: the role binds `PORT + 1`, so `PORT: 3001` opened 3002 while publishing 3001 | on 1.2.0, set `replicas: 1`. To scale those two anyway: drop `ports:` and put your own reverse proxy on the compose network, or climb to the chart's per-role HPA. This is the rung-1 ceiling → [`docs/idea/17-scale-ladder.md`](https://github.com/developerz-ai/ultimate/blob/main/docs/idea/17-scale-ladder.md) | +| MCP tool names are published in `snake_case` and served verbatim | `openapi.json` carries `"x-ultimate": { "mcpTool": "publish_post" }` and `x actions describe --json` reports `mcp.tool: "publish_post"`, while the only name `tools/call` accepts is the export name — `publishPost` ([`packages/mcp/src/from-action.ts`](https://github.com/developerz-ai/ultimate/blob/main/packages/mcp/src/from-action.ts)). An agent handed either catalog calls a tool the server answers ToolNotFound (`-32601`) for. **15 of the 17** `mcpTool` values in the two tracked apps' committed specs name no served tool; the two that work are single-word exports (`summarize`, `health`), which are already their own snake_case form. **Fixed in 2.0.0, and BREAKING**: `toToolName` is deleted from `@ultimat3/action` and `@ultimat3/query` and exported by neither, all three publishers spell the export name, and `packages/mcp/src/cross-surface.test.ts` drives a `tools/call` with the name OpenAPI published so a re-derivation is a failing test | on 1.2.0, ignore the published `mcpTool` / `mcp.tool` value and call the **export name** — `x actions list --json` prints it as `name`, and `tools/list` on the app's own MCP surface is the authoritative catalog either way → [MCP and AI](MCP-And-AI) | +| Shared cache tier invalidation | the Lua script `DEL`s keys it never declared in `KEYS`, so it fails on **Dragonfly** and on **Redis Cluster** — a cluster cannot route a key it was not told about. **Fixed in 2.0.0**: the script returns the member list and the tier deletes the value keys client-side, one key per `DEL`, so every delete is slot-local | on 1.2.0, single-node Redis, or a cache tier that is not the shared one → [Caching and invalidation](Caching-And-Invalidation) | +| `resolveEnvironment` twice | the name exists in both `@ultimat3/core` and `@ultimat3/seo` with different parameters and different return unions. **Fixed in 2.0.0, and BREAKING**: `@ultimat3/seo` exports neither `resolveEnvironment` nor the type `SeoEnvironment`; `RobotsConfig.environment` and `isIndexable()` take core's `Environment`, `'preview'` is spelled `'staging'`, and core gains `tryResolveEnvironment()` for the callers that must answer rather than throw | on 1.2.0, import one with an alias → [Configuration](Configuration) | +| HPAs read `` without a metrics adapter | **the chart half is fixed in 2.0.0** — `values.yaml` declares `metricsPort: 9090`, `_helpers.tpl` emits a `metrics` containerPort on every role but `migrate`, `service.yaml` publishes it by name and `templates/servicemonitor.yaml` ships the scrape target. Two things still stand: `serviceMonitor.enabled` defaults **false**, because a cluster without the Prometheus operator has no such CRD and `helm install` would fail on an unknown kind; and turning scraped series into the `Pods` metrics an HPA reads needs a **custom-metrics adapter**, which is the cluster's and never the chart's. App-side this is now closed in 2.0.0: `otlpSpanExporter()` and `otlpMetricExporter()` speak OTLP/HTTP JSON and read `OTEL_EXPORTER_OTLP_ENDPOINT` — the variable the chart's `env` already sets — so on 1.2.0 it is read by nothing and traces go to a no-op, and in 2.0.0 it works against a collector's `:4318`. gRPC `:4317` is refused by design, and there is no logs signal | set `serviceMonitor.enabled: true` and install a metrics adapter; until then disable the HPAs and pin `replicas`. **Do not hand-add a metrics container port** in 2.0.0 — the chart already emits one and a duplicate is rejected by the API server → [Observability](Observability) | | `x deploy --method helm` | throws `X_NOT_IMPLEMENTED` in a scaffolded app, because `x new` never writes `docker/helm` | copy `docker/helm` from the framework repo, or `--method compose` → [Deployment](Deployment) | | Index predicates and direction in drift | `x db migrate`'s post-migrate check compares an index's **columns and uniqueness**, never its `where` predicate or its `desc` direction — the catalog returns its own rewriting of an expression (`(deleted_at IS NULL)`) and the snapshot holds the author's spelling, so comparing the text would call two identical indexes drift. A predicate changed on the database by hand is therefore invisible to it. **Not in `CHANGELOG.md`'s list** | `x db gen` compares all five fields, so regenerate and inspect the diff → [Entities and migrations](Entities-And-Migrations) | -| `on delete` is declared and reaches no SQL | `references(() => orgs.id, { onDelete: 'cascade' })` type-checks, and the rule is dropped one layer below the declaration: [`packages/entity/src/describe.ts:110`](https://github.com/developerz-ai/ultimate/blob/main/packages/entity/src/describe.ts) renders the reference as the string `"."` and carries no `onDelete`, so `generateMigration` records `onDelete: null` on every key and no generated `add constraint` has ever spelled a rule. Drift cannot catch it either: the catalog answers `a`/`c`/`r` and a snapshot has nothing truthful to hold there, so a hand-added `on delete cascade` and its absence both read `ok: true`. Everything else about a generated key is **fixed on `main`, unreleased** — `x db gen` emits `alter table … add constraint` after every table statement, including for a `references()` added to a column that already exists (which used to generate an **empty** migration, so the key never landed and drift stayed red behind a fix that did nothing), and drift matches a key on where it points rather than on its name. **Not in `CHANGELOG.md`'s list** | add the clause to the `add constraint` statement `x db gen` emitted, **before** the migration is applied — `… references "orgs" ("id") on delete cascade;`. The `.snapshot.json` beside it stays correct, because neither the generator nor drift records the rule. Editing it after it is applied moves the checksum: `X_MIGRATION_CONFLICT` → [Entities and migrations](Entities-And-Migrations) | +| `on delete` is declared and reaches no SQL | `references(() => orgs.id, { onDelete: 'cascade' })` type-checks, and the rule is dropped one layer below the declaration: [`packages/entity/src/describe.ts:110`](https://github.com/developerz-ai/ultimate/blob/main/packages/entity/src/describe.ts) renders the reference as the string `"."` and carries no `onDelete`, so `generateMigration` records `onDelete: null` on every key and no generated `add constraint` has ever spelled a rule. Drift cannot catch it either: the catalog answers `a`/`c`/`r` and a snapshot has nothing truthful to hold there, so a hand-added `on delete cascade` and its absence both read `ok: true`. Everything else about a generated key is **fixed in 2.0.0** — `x db gen` emits `alter table … add constraint` after every table statement, including for a `references()` added to a column that already exists (which used to generate an **empty** migration, so the key never landed and drift stayed red behind a fix that did nothing), and drift matches a key on where it points rather than on its name. **Not in `CHANGELOG.md`'s list** | add the clause to the `add constraint` statement `x db gen` emitted, **before** the migration is applied — `… references "orgs" ("id") on delete cascade;`. The `.snapshot.json` beside it stays correct, because neither the generator nor drift records the rule. Editing it after it is applied moves the checksum: `X_MIGRATION_CONFLICT` → [Entities and migrations](Entities-And-Migrations) | | A `references()` removed from a column emits nothing | dropping the `.references()` call generates no `drop constraint`, exactly as a removed index generates no `drop index`. The key stays on the database, and drift judges only the **declared** side, so nothing reports it. **Not in `CHANGELOG.md`'s list** | write the drop into the next generated migration by hand, before applying it: `alter table "posts" drop constraint "posts_org_id_fkey";` — the generator's own name for the key is `__fkey` | -| `@ultimat3/flags` is not on npm | the repository versions all 29 packages in lockstep, but **28 are published**: the registry answers 404 for `@ultimat3/flags` at every version, verified `As of 2026-08`, while the other 28 resolve at 1.2.0. So `bun add @ultimat3/flags` fails, and `defineFlag` / `isEnabled` / `flagsReport` are reachable only from a checkout — the reference app depends on it through the workspace, which is why nothing in the repo notices. Nothing in the package opts out: it declares `publishConfig.access: public`. **Not in `CHANGELOG.md`'s list** | vendor the ten source files of `packages/flags/src` into your app, or wrap your own switch behind one function and swap it later — the primitive shape is `(subject) => boolean`, so an app-local `isEnabled` is a drop-in → [Configuration](Configuration) | -| External branches made before the `psql` shell-out was removed carry no marker | `x db branch ls` finds an external branch by the `comment on database` marker `createBranch()` writes ([`packages/db/src/branch.ts`](https://github.com/developerz-ai/ultimate/blob/main/packages/db/src/branch.ts)). The old path issued `CREATE DATABASE … TEMPLATE` through `psql` and wrote **no comment**, so a branch made by a 1.2.0 or earlier CLI is absent from `ls` — and because `drop` may only remove what `ls` shows, `x db branch drop ` refuses it too. The database is still there, still holding a copy of whatever it was cloned from. **Fixed on `main`, unreleased**, for branches made from here on: `create` runs through `@ultimat3/db` and the marker lands with the clone. Nothing back-fills the comment on the old ones. **Not in `CHANGELOG.md`'s list** | the refusal names the exact database it would have touched, so drop it by hand from that string: `psql "$DATABASE_URL" -c 'DROP DATABASE "_branch_"'` — `` is the branch name with every character outside `[A-Za-z0-9_]` replaced by `_`, so `feat-new-billing` is `feat_new_billing`. Adopting it instead is possible — the comment is `ultimate:branch:` followed by an ISO timestamp — but `BRANCH_MARKER` is a module-private constant with no export, so a hand-written comment is a string this framework never promised to keep. Prefer the drop → [Entities and migrations](Entities-And-Migrations) | +| `@ultimat3/flags` is not on npm | the repository versions all 29 packages in lockstep, but **28 are published**: the registry answers 404 for `@ultimat3/flags` at every version, verified `As of 2026-08`, and 2.0.0 does not change that — publication is not in lockstep, and the first publish of `flags` is a manual bootstrap by an npm org member, because a trusted publisher cannot attach to a package that does not exist. So `bun add @ultimat3/flags` fails, and `defineFlag` / `isEnabled` / `flagsReport` are reachable only from a checkout — the reference app depends on it through the workspace, which is why nothing in the repo notices. Nothing in the package opts out: it declares `publishConfig.access: public`. **Not in `CHANGELOG.md`'s list** | vendor the ten source files of `packages/flags/src` into your app, or wrap your own switch behind one function and swap it later — the primitive shape is `(subject) => boolean`, so an app-local `isEnabled` is a drop-in → [Configuration](Configuration) | +| External branches made before the `psql` shell-out was removed carry no marker | `x db branch ls` finds an external branch by the `comment on database` marker `createBranch()` writes ([`packages/db/src/branch.ts`](https://github.com/developerz-ai/ultimate/blob/main/packages/db/src/branch.ts)). The old path issued `CREATE DATABASE … TEMPLATE` through `psql` and wrote **no comment**, so a branch made by a 1.2.0 or earlier CLI is absent from `ls` — and because `drop` may only remove what `ls` shows, `x db branch drop ` refuses it too. The database is still there, still holding a copy of whatever it was cloned from. **Fixed in 2.0.0**, for branches made from here on: `create` runs through `@ultimat3/db` and the marker lands with the clone. Nothing back-fills the comment on the old ones. **Not in `CHANGELOG.md`'s list** | the refusal names the exact database it would have touched, so drop it by hand from that string: `psql "$DATABASE_URL" -c 'DROP DATABASE "_branch_"'` — `` is the branch name with every character outside `[A-Za-z0-9_]` replaced by `_`, so `feat-new-billing` is `feat_new_billing`. Adopting it instead is possible — the comment is `ultimate:branch:` followed by an ISO timestamp — but `BRANCH_MARKER` is a module-private constant with no export, so a hand-written comment is a string this framework never promised to keep. Prefer the drop → [Entities and migrations](Entities-And-Migrations) | | A flag that does not exist is refused before the command runs | the parser reads flags against the spec first, so an unknown one is `X_CLI_BAD_FLAG` and never the command's own error. On a **planned** command — whose spec declares no flags at all — that means `x logs tail --follow` reports the bad flag rather than the honest `X_NOT_IMPLEMENTED`. Verified `As of 2026-08`; `x env` is **shipped**, and its `--fix` is refused for the ordinary reason that no such flag exists | run the flagless form to see the real message → [CLI reference](CLI-Reference) | -| No test file is typechecked | all 29 package `tsconfig.json`s carry `"exclude": ["src/**/*.test.ts"]`, so `bun run typecheck` — a `tsc -b` — never reads a `.test.ts` in `packages/`, and the gate's `typecheck` step reports green over every one of them. Measured `As of 2026-08`: dropping the exclusion surfaces **282 errors across 110 files in 24 packages** (worst: `entity` 60, `cli` 55, `render` 36), overwhelmingly mechanical — `TS4111` index-signature access, `TS2345`/`TS2769` argument and overload mismatches, `TS2379` under `exactOptionalPropertyTypes`. `packages/*/e2e/**` is in no package's `include` either, so those three directories compile nowhere at all. **Recorded in `CHANGELOG.md` under `[Unreleased]`.** `scripts/` is exempt as of this change — it has no such `exclude`, so its tests do typecheck | nothing to work around at runtime: the tests run, they are simply not compiler-checked. Typecheck one package's tests directly with `bunx tsc --noEmit` over a config that drops the `exclude` | +| No test file is typechecked | all 29 package `tsconfig.json`s carry `"exclude": ["src/**/*.test.ts"]`, so `bun run typecheck` — a `tsc -b` — never reads a `.test.ts` in `packages/`, and the gate's `typecheck` step reports green over every one of them. Measured `As of 2026-08`: dropping the exclusion surfaces **282 errors across 110 files in 24 packages** (worst: `entity` 60, `cli` 55, `render` 36), overwhelmingly mechanical — `TS4111` index-signature access, `TS2345`/`TS2769` argument and overload mismatches, `TS2379` under `exactOptionalPropertyTypes`. `packages/*/e2e/**` is in no package's `include` either, so those three directories compile nowhere at all. **Recorded in `CHANGELOG.md` under 2.0.0.** `scripts/` is exempt as of this change — it has no such `exclude`, so its tests do typecheck | nothing to work around at runtime: the tests run, they are simply not compiler-checked. Typecheck one package's tests directly with `bunx tsc --noEmit` over a config that drops the `exclude` | | The cache fill fence is **per process** | a read-through fill re-checks a fence before writing, so an invalidation landing mid-read can no longer be overwritten by pre-write rows — but the fence is one process's memory. Two pods still interleave: a load on pod A, a write and a bust on pod B, and A's fill is not covered. On the shared Redis tier the membership re-check narrows that to "the value is deleted rather than orphaned", so the stale row does not outlive its own invalidation there; a **process-local** tier on pod A can still hold it for the TTL. A genuine cross-node fence needs a Redis-side epoch and a wire-format change, which is a bigger change than the one this closes | give a `cache:` query a TTL you can afford to be wrong for on a single pod, or run the shared tier as the only cached tier for reads that must not go stale across nodes | | `invoke()`'s cache bust is not transaction-aware | an app that wraps `invoke()` in `db.transaction(...)` busts the tags in `cache.invalidates` before the commit lands, so a concurrent read can repopulate the entry from the pre-commit row and the stale value survives to its TTL. The fix is a transaction hook in the action path — a real design change, and a `db` seam `@ultimat3/action` deliberately does not import (it is tier 3 and holds no driver). No tracked app does this: neither `examples/dummy` nor `dummy/social-media-clone` calls `transaction(` in `apps/` | do the transactional work *inside* the handler, which is already inside the one commit boundary the framework knows about — or, if the transaction must wrap the call, invalidate yourself with `invalidateTags` after it returns and drop `cache.invalidates` from the declaration, so there is one bust and it is after the commit | | `MemoryAdapter` does not enforce the email uniqueness Postgres does | it is a `Map` keyed by id, so `register()` twice at one address silently makes two users under `x dev`, where the `x_users.email` unique index raises a driver-level violation in production. **Not fixed deliberately**: every available fix mints a refusal Postgres does not throw, swapping one divergence for another, and the defensible home is a coded check in `register()` — which is an **account-enumeration** decision, and `@ultimat3/auth`'s whole `loginFailed()` doctrine exists to avoid exactly that oracle. The database is the enforcement, and it is the right one | develop against Postgres for signup flows, or treat a second `register()` at one address as undefined in `x dev`. Email normalisation itself is fixed — all four doors now go through one `normaliseEmail` | -| Two head serializers, one of them weaker | `@ultimat3/seo`'s `renderHeadTags` emitted head tags to HTML escaping ``, which moves the tokenizer into script-data-escaped state where the element's own `` no longer closes it — and it applied that code rule to a JSON body. **Nothing called it.** Meanwhile `@ultimat3/render`'s `renderHead`, the path every `x dev` and every build takes, emitted `