diff --git a/CHANGELOG.md b/CHANGELOG.md index d8800c62..1a50a7b3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,20 @@ Semver applies from 1.0.0. A breaking change to a documented API needs a major ## [Unreleased] +## 3.0.0 - 2026-08-19 + +The first release *able* to go through the workflow since 1.2.0: every package now has an OIDC +trusted publisher, and none had one before. 2.0.0 was hand-published without provenance precisely +because there was nothing for the exchange to verify against. `@ultimat3/scraping` — 404 since it +landed after the 2.0.0 run — was bootstrapped by hand at 2.0.0 so the derived publish list can +reach it here. + +Whether this release *did* publish with provenance is a fact about the registry, not about this +file: `npm view @ultimat3/core@3.0.0 dist.attestations` answers it. + +A major because a five-agent bug sweep landed breaking changes to documented APIs. The +entries below are that sweep; each names the manual edit it costs. + ### Added - **`agent()` — the tool loop, as an action factory.** The third instance of the rule after `llm()` @@ -608,6 +622,32 @@ Semver applies from 1.0.0. A breaking change to a documented API needs a major that trusted that page called five tools the server answers ToolNotFound for. The table is now the thirteen `devTools(host)` declares, with each one's scope. +### Merged pull requests + +The prose above says what changed and why; this is which pull request carried it. + +**Added** + +- browser automation as a job factory, with zero new dependencies (#140) +- an agent's tool can be a real action, and a hive of agents is an action (#139) +- adopt an existing database, and make a seed replayable (#137) + +**Fixed** + +- an apostrophe in JSX text turned the errors gate off for a whole file (#158) +- a crafted icon executed at import, and a session cookie went to the wrong host (#152) +- the outbox claim locked nothing, and a drain released nothing (#148) +- the contracts that said "never throws" threw, and a gate that was never enforced (#147) +- an unreadable password hash was an enumeration oracle (#141) +- a terminal error is retried, because the executor never reads the classification (#138) +- the trusted publisher was attached without its environment, and the check could never see it (#136) + +**Changed** + +- the release status said the opposite of the registry, in ten files (#159) +- agents, and a migration guide an AI agent can execute (#142) + + ## 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. diff --git a/CLAUDE.md b/CLAUDE.md index 56d72806..336ade8c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -12,32 +12,50 @@ This repo is the framework itself: a monorepo of `@ultimat3/*` packages, the `x` CLI binary: `x`. npm scope: `@ultimat3`. Import paths: `@ultimat3/`. -**Status:** 2.0.0 — **versioned, tagged, pushed and on npm**, `As of 2026-08`. `v2.0.0` is on -origin and the registry's `latest` is 2.0.0. 29 `@ultimat3/*` packages plus the unscoped -`create-ultimate` — 30 in all — are **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. 1.1.0 -was the first release published by -[`.github/workflows/release.yml`](.github/workflows/release.yml) over OIDC trusted publishing, -provenance attached; 1.0.0 was the manual bootstrap. **2.0.0 was not**: no package has a trusted -publisher attached (`trust-publishers.ts --check` answers 0 of 30), so the OIDC exchange has nothing -to verify against and 2.0.0 was published by hand — `_npmUser: sebyx07`, no `dist.attestations`, -where 1.1.0 and 1.2.0 carry both. Semver applies — a breaking change to a -documented API needs a major, and the eight primitive shapes, the `x` CLI surface and the tier table -are as stable as the `X_*` codes already were. - -**Publication is not in lockstep, and one package is behind: `@ultimat3/scraping` has never been -published.** The registry answers 404, not a stale version — so 29 of the 30 are on npm and -`bun add @ultimat3/scraping` fails. It is not opting out (`packages/scraping/package.json` declares -the same `publishConfig` as the rest) and nothing in the repo notices, because every consumer -resolves it through the workspace. The cause is timing: it landed after the 2.0.0 publish run, so -the run never saw it. That is exactly the hole `@ultimat3/flags` used to be — closed at 2.0.0, which -is `flags`' only version on the registry, by the one-time manual bootstrap every package needs -before a trusted publisher can attach. **It costs the next release run.** `scraping` is **27th of -30** in the derived publish order (`bun run scripts/release-workflow.ts --json`), so the run reaches -it and dies there with **26 packages already published irreversibly**. The bootstrap is -[`PUBLISHING.md`](PUBLISHING.md) step 1; the list is derived so the failure is loud rather than a -silent skip. +**Status:** 3.0.0 in the repository, `As of 2026-08`. 29 `@ultimat3/*` packages plus the unscoped +`create-ultimate` — 30 in all — **versioned** in lockstep: one version, one commit, one tag. + +**The registry is one release behind the repository between a bump and its workflow run — that is +where this commit sits.** Never read a number here as the installable one; run the command beside it. + +| Fact | State, `As of 2026-08-19` | Read it yourself | +|---|---|---| +| Repository version | 3.0.0, every workspace stamped | `bun run scripts/release.ts --check 3.0.0` | +| Publishable workspaces | 30 | `bun run scripts/release-workflow.ts --json` | +| On the registry | **all 30**, no holes | `bun run scripts/release-workflow.ts --json # the derived list; check every name against npm view` | +| npm `latest` | **2.0.0** — the `v3.0.0` tag and the publish run follow this commit | `npm view @ultimat3/core version` | +| OIDC trusted publisher | attached to all 30 | `NPM_CONFIG_OTP= bun run scripts/trust-publishers.ts --check --json` — without a fresh OTP every package reads as missing | + +**`@ultimat3/scraping` is on the registry, and it was the last publication hole.** It was bootstrapped +by hand at 2.0.0 on 2026-08-19 — `npm publish --access public --provenance=false`, the one-time step +every package needs before a trusted publisher can attach — and npm now answers `E403 … cannot +publish over the previously published versions: 2.0.0` on a retry. `@ultimat3/flags` was the same +shape and was closed the same way at 2.0.0. Publication is a step apart from versioning; the publish +list is **derived** from `scripts/list-workspaces.ts`, which is what keeps a new package from being +silently absent from it. Step 1 of [`PUBLISHING.md`](PUBLISHING.md) comes due again for the next +package added after a release run, and nothing else. + +**3.0.0 is a major** because a five-agent bug sweep landed breaking changes to documented APIs: +`mfa.required` refused and narrowed to the literal `false`, `enrolTotp(auth, input)`, `appErrorStatus` +removed, `SocketRegistry.sweepIdle()` → `idle()`, `SyncSocket.lastSeenAt` → `lastSeenMonotonicMs`, +two `SQL_OUTBOX_*` constants gaining a parameter, `DESCRIPTION_MIN_LENGTH` deleted, and a metric +redeclared with different bounds now refused. The 3.0.0 section of [`CHANGELOG.md`](CHANGELOG.md) +carries **10** entries marked `BREAKING —` and ships no codemod, so each one is a manual edit its own +entry names. 2.0.0 was the **first** major and carried 33. + +**Every package has an OIDC trusted publisher, attached 2026-08-19 — for the first time.** +`developerz-ai` / `ultimate` / `release.yml` / environment `npm-publish`, publish permission, all 30, +verified per package with `npx -y npm@12 trust list --json` — `npm trust` shipped in **npm 12** +and Bun's bundled npm answers it as an unknown command, which is why `scripts/trust-publishers.ts` +pins the runner. **That is why 2.0.0 has no provenance**: with no publisher attached the OIDC +exchange had nothing to verify against, so the workflow could not publish and 2.0.0 went out by +hand — `_npmUser: sebyx07`, no `dist.attestations`, where 1.1.0 and 1.2.0 carry both. 1.1.0 was the +first release [`.github/workflows/release.yml`](.github/workflows/release.yml) published over OIDC; +1.0.0 was the manual bootstrap. 3.0.0 is the first release since 1.2.0 that **can** run through the +workflow — whether it did is `npm view @ultimat3/core@3.0.0 dist.attestations`, not a sentence here. + +Semver applies — a breaking change to a documented API needs a major, and the eight primitive shapes, +the `x` CLI surface and the tier table are as stable as the `X_*` codes already were. Realtime capacity is **measured on one node, in two halves that answer different questions**. @@ -225,7 +243,7 @@ Everything in the framework is one of these. **If a feature doesn't fit one of t ## CI -Free GitHub Actions runners (`ubuntu-latest`) — never a paid runner. `ci.yml` runs three jobs, each answering a question no other job answers: `verify` (the gate, `x verify` verbatim — lint, typecheck, boundaries and every suite are its steps, never a second job), `reference-app-verify` (the app gate, on its ratchet) and `scaffold-smoke` (`x new` → `bun install` → `x verify` outside the checkout). Target under 5 minutes. Every job starts with `./.github/actions/setup` — bun, the install cache, a frozen install. Releases publish to npm via **OIDC trusted publishing**, with provenance — that is what the workflow does, and it is not what 2.0.0 did: with 0 of 30 trusted publishers attached it cannot run, so 2.0.0 was hand-published without attestations. See [`PUBLISHING.md`](PUBLISHING.md), which names the two steps that are owed. +Free GitHub Actions runners (`ubuntu-latest`) — never a paid runner. `ci.yml` runs three jobs, each answering a question no other job answers: `verify` (the gate, `x verify` verbatim — lint, typecheck, boundaries and every suite are its steps, never a second job), `reference-app-verify` (the app gate, on its ratchet) and `scaffold-smoke` (`x new` → `bun install` → `x verify` outside the checkout). Target under 5 minutes. Every job starts with `./.github/actions/setup` — bun, the install cache, a frozen install. Releases publish to npm via **OIDC trusted publishing**, with provenance — which 2.0.0 did not get, because no trusted publisher existed for the exchange to verify against. All 30 were attached on 2026-08-19, so 3.0.0 is the first release since 1.2.0 that can run through the workflow. See [`PUBLISHING.md`](PUBLISHING.md). ## Note diff --git a/PUBLISHING.md b/PUBLISHING.md index 21217adc..48087897 100644 --- a/PUBLISHING.md +++ b/PUBLISHING.md @@ -8,23 +8,24 @@ Releases use **OIDC trusted publishing** from GitHub Actions mints a short-lived token from the run's OIDC identity and attaches a provenance attestation automatically. -**`As of 2026-08`: 30 workspaces publish; 29 are on the registry, all at 2.0.0.** `v2.0.0` is -tagged and pushed and npm's `latest` is 2.0.0. **`@ultimat3/scraping` has never been published** — -the registry answers 404, not a stale version. It is not opting out: -`packages/scraping/package.json` declares the same `publishConfig` as the other 29, and every -consumer resolves it through the workspace, so nothing in the repo noticed. The cause is timing, not -the workflow: `scraping` landed after the 2.0.0 publish run, so the run never saw it. Its first -publish is a human step — see -[Human steps outside this file](#human-steps-outside-this-file). - -`@ultimat3/flags` was the previous instance, for the opposite reason: the workflow listed its `-w` -flags by hand and omitted it. That list is now **derived** from `scripts/list-workspaces.ts`, so -`flags` and every package added since are in it by construction — which is why the 2.0.0 run -published `flags` for the first time, and why the next run will reach `scraping` and fail on it -loudly. - -The counts and the 404 are a snapshot and go stale the moment step 1 below is done; that the list is -derived is a rule and does not. +**`As of 2026-08-19`: 30 workspaces publish and all 30 are on the registry.** `v2.0.0` is tagged +and pushed and npm's `latest` is 2.0.0; the repository is at 3.0.0, which the workflow has not +published yet. + +**There is no publication hole today, and there have been two.** `@ultimat3/flags` was the first: +the workflow listed its `-w` flags by hand and omitted it. That list is now **derived** from +`scripts/list-workspaces.ts`, so every package is in it by construction — which is why the 2.0.0 run +published `flags` for the first time. `@ultimat3/scraping` was the second, for the opposite reason: +it landed *after* the 2.0.0 run, so no run had ever seen it. It was bootstrapped by hand on +2026-08-19 (step 1 below). + +The pattern is the point: **a package added after a release run is unpublished until someone +bootstraps it**, and the derived list guarantees the *next* run fails loudly on it rather than +skipping it silently. Expect this again for the next new package. + +The counts are a snapshot and go stale on the next release; that the list is derived is a rule and +does not. Read the state — `bun run scripts/release-workflow.ts --json` for the order, +`npm view version` for what the registry holds — never this paragraph. ## Lockstep versioning — the rule @@ -107,16 +108,24 @@ approval-gated environment, which is the half GitHub cannot enforce, and it must ## Human steps outside this file -**`As of 2026-08` steps 1 and 2 are done and steps 3 and 4 are NOT, measured against the registry.** -`bun run scripts/trust-publishers.ts --check` answers `0/30 packages trust -developerz-ai/ultimate/release.yml` — every one reports `X_TRUST_PUBLISHER_MISSING`. This file -claimed all four were done until 2026-08. +**`As of 2026-08-19` all four are done, for all 30 packages** — steps 3 and 4 for the first time. +Until then no package had a trusted publisher at all, and this file claimed otherwise. -**That is why 2.0.0 has no provenance.** With no trusted publisher attached, the OIDC exchange has -nothing to verify against, so the workflow cannot publish and 2.0.0 went out by hand instead: every +**That is why 2.0.0 has no provenance.** With no trusted publisher attached, the OIDC exchange had +nothing to verify against, so the workflow could not publish and 2.0.0 went out by hand: every `@ultimat3/*` package at 2.0.0 carries `_npmUser: sebyx07` and **no `dist.attestations`**, while -1.1.0 and 1.2.0 carry attestations and `_npmUser: GitHub Actions`. The releases that ran through -the workflow are the older ones. Read the state, never this sentence: +1.1.0 and 1.2.0 carry attestations and `_npmUser: GitHub Actions`. 3.0.0 is the first release since 1.2.0 +that *can* run through the workflow — confirm it did with +`npm view @ultimat3/core@3.0.0 dist.attestations`, never from this sentence. + +**Two traps met while doing steps 3 and 4, both worth knowing before you redo them:** + +| Trap | What happens | +|---|---| +| `npm trust list` itself needs an OTP | so `--check` without one reports **every** package missing. A `0/30` is not evidence of anything until a code is supplied — and the script takes it as `NPM_CONFIG_OTP=`, never a `--otp` flag, which it does not parse | +| One OTP does not cover 30 packages | a code lasts 30s and npm rate-limits verification (`E429 … OTP verification failed`). Attach in batches on fresh codes; an already-attached package answers `E409 … already exists`, which is a success, not a failure | + +Read the state, never this sentence: ```sh bun run scripts/trust-publishers.ts --check --json @@ -124,8 +133,8 @@ npm view @ultimat3/core@ dist.attestations ``` Step 2 is once per repository and stays done; **steps 1, 3 and 4 are once per package** and come due -again for every package added after a release run — all three are owed for `@ultimat3/scraping`, and -step 1 is the one that breaks a release. +again for every package added after a release run — all three were owed for `@ultimat3/scraping` and were done on 2026-08-19; step 1 is the +one that breaks a release. **Three of the four are scriptable, and this file said otherwise until 2.0.0.** Only step 2's *reviewer* choice is genuinely a human decision: @@ -143,27 +152,43 @@ it breaks it deep into the run, after packages have published irreversibly. **St `As of 2026-08`: it is a per-package step, and every package added after a release run needs its own bootstrap before the next one. -### 1. Publish `@ultimat3/scraping` by hand — do this first +### 1. Bootstrap a never-published package by hand — do this first + +**`As of 2026-08-19` nothing is owed here**: `@ultimat3/scraping` was bootstrapped and all 30 are on +the registry. This is the procedure for the next package that needs it, which is every package added +after a release run. ```sh -npm login # as an @ultimat3 org member -npm publish -w @ultimat3/scraping --access public --provenance=false +npm publish -w --access public --provenance=false ``` -Then attach its trusted publisher exactly as in -[the section above](#one-time-configure-the-trusted-publisher-per-package). +**npm reads the credential from an `.npmrc`, not from a bare environment variable.** Setting +`npm_config__authToken` or `NODE_AUTH_TOKEN` alone is not enough: the bootstrap of `scraping` +answered `E404 Not Found - PUT` with a full session token belonging to an org **owner**, which reads +as a permissions problem and is not one. Either `npm login`, or point npm at the variable — never a +literal token in a file: + +```sh +printf '//registry.npmjs.org/:_authToken=${NODE_AUTH_TOKEN}\n' > "$HOME/.npmrc" +``` + +npm interpolates `${NODE_AUTH_TOKEN}` at read time, so the secret stays in the environment. Keep any +`.npmrc` you create out of source control and out of the package — `files` already excludes it, and +a token written literally into one is a credential leak waiting for a `git add`. + +**A 404 immediately afterwards is propagation, not failure.** The public packument lagged the +publish by minutes. `npm access list packages @ultimat3` showed the record first, and a retry +answering `E403 … cannot publish over the previously published versions` is the confirmation. -**Why first.** `scraping` has never been on the registry — `npm view @ultimat3/scraping` answers -404, verified `As of 2026-08`. It landed after the 2.0.0 publish run, and the workflow's publish -list is derived, so it is included from the next release on. Trusted publishing cannot bootstrap a -package that does not exist yet, so the workflow will fail on it. +**Why first.** Trusted publishing cannot bootstrap a package that does not exist yet, so a release +run reaches the new package and fails on it. **Cost of skipping: an irreversible partial release.** The workflow publishes tier by tier and -aborts on the first failure. Measured against the derived list `As of 2026-08` -(`bun run scripts/release-workflow.ts --json`), `scraping` is **27th of 30**, tier 5, between `cli` -and `testing` — so **26 packages are already on the registry** at the new version when the run dies, -and npm publishes cannot be undone. You would be recovering by hand, with 26 of 30 packages a -version ahead of the other 4. +aborts on the first failure, so every package ahead of the missing one is already on the registry at +the new version when the run dies, and npm publishes cannot be undone. The ordinal is derived, never +typed out — `bun run scripts/release-workflow.ts --json` prints the order, and reading it is the +only way to know the cost. When `scraping` was the hole it was 27th of 30, i.e. 26 published before +the abort. **A bootstrap cannot carry provenance.** `publishConfig.provenance: true` is on every package, and npm can only attest from CI — a local publish dies with `EUSAGE — Automatic provenance generation diff --git a/README.md b/README.md index 26733433..ec24eba4 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-2.0.0-blue.svg)](CHANGELOG.md) +[![Version](https://img.shields.io/badge/version-3.0.0-blue.svg)](CHANGELOG.md) -> **Status: 2.0.0, tagged and on npm**, `As of 2026-08`. `v2.0.0` is tagged and pushed, and npm's `latest` is **2.0.0** — which is what `bunx create-ultimate myapp` gives you today. 29 `@ultimat3/*` packages plus the unscoped `create-ultimate` — 30 in all — are **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)). **29 of the 30 are on the registry**: `@ultimat3/scraping` has never been published — it landed after the 2.0.0 run — so `bun add @ultimat3/scraping` answers 404 and browser automation is reachable only from a checkout. It needs the one-time manual bootstrap every package gets before a trusted publisher can attach, and it is 27th of 30 in the derived publish list, so the next release run dies there with 26 packages already published irreversibly ([PUBLISHING.md](PUBLISHING.md) step 1). 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. **2.0.0 was hand-published too** — no package has a trusted publisher attached, so the workflow's OIDC exchange has nothing to verify against, and every 2.0.0 tarball carries no attestation. 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: 3.0.0 in the repository, 2.0.0 on npm**, `As of 2026-08`. 29 `@ultimat3/*` packages plus the unscoped `create-ultimate` — 30 in all — are **versioned** in lockstep at 3.0.0: one version, one commit, one tag. **`bunx create-ultimate myapp` still gives you 2.0.0** — the `v3.0.0` tag and its publish run follow this commit, and the registry is one release behind the repository until they land. Check, never trust this line: `npm view @ultimat3/core version`. **All 30 workspaces are on the registry** `As of 2026-08-19` — `@ultimat3/scraping` was the last hole and it is closed, bootstrapped by hand at 2.0.0 (`npm publish --access public --provenance=false`), the one-time step every package needs before a trusted publisher can attach. **3.0.0 is a major**: [CHANGELOG.md](CHANGELOG.md)'s 3.0.0 section carries 10 entries marked `BREAKING —` from a five-agent bug sweep, and no codemod ships with them, so each is a manual edit its entry names ([Upgrading](https://github.com/developerz-ai/ultimate/wiki/Upgrading)). 2.0.0 was the first major and carried 33. 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. **2.0.0 was hand-published** — no package had a trusted publisher attached, so the workflow's OIDC exchange had nothing to verify against, and every 2.0.0 tarball carries no attestation. All 30 publishers were attached on 2026-08-19, for the first time, which is what makes 3.0.0 able to publish over OIDC at all ([PUBLISHING.md](PUBLISHING.md)). 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 2.0.0:** +**Not claimed at 3.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 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 | +| **Deferred past 3.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 3.0.0 either, 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,13 +223,13 @@ 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 is not in 2.0.0 and lands in a later major, 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 3.0.0 and lands in a later major, behind the interfaces that are already here. | Tier | What | Covers | |---|---|---| | 1 · **Channels** | `ctx.publish(topic, msg)` over Bun's native WS pub/sub | presence, cursors, notifications | | 2 · **Live queries** | declare server-side with a policy, receive a Solid signal | **90% of "realtime app"** | -| 3 · **Local-first** *(not in 2.0.0)* | optimistic mutators, OPFS SQLite, offline queue, rebase | offline writes that reconcile | +| 3 · **Local-first** *(not in 3.0.0)* | optimistic mutators, OPFS SQLite, offline queue, rebase | offline writes that reconcile | → [Realtime design and its honest limits](docs/idea/03-realtime.md) @@ -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, pinned `1.9.14`** + our own router | fine-grained reactivity on the stable line; Solid 2 is still `2.0.0-beta.N`, and 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 not in 2.0.0 | zero-infra start, a real scale path behind one interface | +| Jobs | Postgres queue default; Redis/NATS drivers not in 3.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 3296e80b..5f066e00 100644 --- a/docker/helm/Chart.yaml +++ b/docker/helm/Chart.yaml @@ -9,8 +9,8 @@ type: application # `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" +version: 3.0.0 +appVersion: "3.0.0" kubeVersion: ">=1.27.0-0" keywords: [bun, postgres, solidjs, ultimate] home: https://ultimate.dev diff --git a/docs/idea/README.md b/docs/idea/README.md index e0a864d9..a4498f36 100644 --- a/docs/idea/README.md +++ b/docs/idea/README.md @@ -60,14 +60,15 @@ Consequences of each in [`00-thesis.md`](./00-thesis.md); axiom 8 in full in [`1 ## Status -`As of 2026-08`: **2.0.0 is versioned, tagged and on npm.** Publication is not in lockstep with versioning, and one package is behind. - -| Fact | State | -|---|---| -| Versioned | 29 `@ultimat3/*` packages plus the unscoped `create-ultimate` — 30 in all — at 2.0.0 in lockstep, one commit. The first major: 33 changelog entries marked `BREAKING —`, no codemod | -| Tagged | **yes**. `v2.0.0`, on origin | -| Published | **yes**. `npm view @ultimat3/core version` answers `2.0.0`, and that is what `bunx create-ultimate myapp` installs today | -| Not published | `@ultimat3/scraping` — 404 at every version, because it landed after the 2.0.0 publish run. It is 27th of 30 in the derived publish list, so the next release run dies there with 26 packages already published irreversibly; the one-time manual bootstrap is [`PUBLISHING.md`](../../PUBLISHING.md) step 1 | +`As of 2026-08`: **the repository is at 3.0.0 and npm's `latest` is 2.0.0.** The registry is one release behind the repository between a version bump and its publish run, which is where this commit sits. Resolve every row below rather than believing it. + +| Fact | State, `As of 2026-08-19` | Read it yourself | +|---|---|---| +| Versioned | 29 `@ultimat3/*` packages plus the unscoped `create-ultimate` — 30 in all — at 3.0.0 in lockstep, one commit. A major: 10 changelog entries marked `BREAKING —` from a five-agent bug sweep, no codemod. 2.0.0 was the first major and carried 33 | `bun run scripts/release.ts --check 3.0.0` | +| Tagged | `v2.0.0` is on origin; `v3.0.0` follows this commit | `git tag --list 'v3.*'` | +| Published | **`latest` is 2.0.0** — that is what `bunx create-ultimate myapp` installs today | `npm view @ultimat3/core version` | +| Publication holes | **none.** All 30 workspaces are on the registry. `@ultimat3/scraping` was the last one and was bootstrapped by hand at 2.0.0, exactly as `@ultimat3/flags` had been — the one-time step in [`PUBLISHING.md`](../../PUBLISHING.md) that every package needs before a trusted publisher can attach | `bun run scripts/release-workflow.ts --json` for the derived list, then `npm view` each name — one package proves one package | +| OIDC trusted publisher | attached to all 30 on 2026-08-19, for the first time — which is why 2.0.0 carries no provenance and 3.0.0 can publish over OIDC at all | `NPM_CONFIG_OTP= bun run scripts/trust-publishers.ts --check --json` — every package, and without a fresh code they all read as missing | Docs `00`–`15`, `18` and `19` describe what exists; `16` and `17` are design only and say so in every claim. diff --git a/dummy/social-media-clone/package.json b/dummy/social-media-clone/package.json index a01f96d9..329260e8 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": "2.0.0", + "@ultimat3/testing": "3.0.0", "typescript": "^7.0.2" }, "dependencies": { - "@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", + "@ultimat3/action": "3.0.0", + "@ultimat3/admin": "3.0.0", + "@ultimat3/cache": "3.0.0", + "@ultimat3/cli": "3.0.0", + "@ultimat3/core": "3.0.0", + "@ultimat3/db": "3.0.0", + "@ultimat3/entity": "3.0.0", + "@ultimat3/http": "3.0.0", + "@ultimat3/i18n": "3.0.0", + "@ultimat3/jobs": "3.0.0", + "@ultimat3/mcp": "3.0.0", + "@ultimat3/policy": "3.0.0", + "@ultimat3/pwa": "3.0.0", + "@ultimat3/query": "3.0.0", + "@ultimat3/realtime": "3.0.0", + "@ultimat3/render": "3.0.0", + "@ultimat3/ui": "3.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 06a4b02f..8cc52a77 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": "2.0.0" + "@ultimat3/i18n": "3.0.0" } } diff --git a/examples/dummy/apps/admin/package.json b/examples/dummy/apps/admin/package.json index c7acd371..b24f0ade 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": "2.0.0", - "@ultimat3/admin": "2.0.0", - "@ultimat3/core": "2.0.0", - "@ultimat3/policy": "2.0.0", - "@ultimat3/schema": "2.0.0" + "@ultimat3/action": "3.0.0", + "@ultimat3/admin": "3.0.0", + "@ultimat3/core": "3.0.0", + "@ultimat3/policy": "3.0.0", + "@ultimat3/schema": "3.0.0" } } diff --git a/examples/dummy/apps/web/package.json b/examples/dummy/apps/web/package.json index becf3675..2a8a88fc 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": "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", + "@ultimat3/action": "3.0.0", + "@ultimat3/ai": "3.0.0", + "@ultimat3/auth": "3.0.0", + "@ultimat3/core": "3.0.0", + "@ultimat3/entity": "3.0.0", + "@ultimat3/flags": "3.0.0", + "@ultimat3/jobs": "3.0.0", + "@ultimat3/mail": "3.0.0", + "@ultimat3/policy": "3.0.0", + "@ultimat3/query": "3.0.0", + "@ultimat3/realtime": "3.0.0", + "@ultimat3/render": "3.0.0", + "@ultimat3/schema": "3.0.0", + "@ultimat3/seo": "3.0.0", + "@ultimat3/storage": "3.0.0", + "@ultimat3/testing": "3.0.0", + "@ultimat3/time": "3.0.0", + "@ultimat3/ui": "3.0.0", "solid-js": "1.9.14" } } diff --git a/examples/dummy/package.json b/examples/dummy/package.json index c3900a6b..cd13362e 100644 --- a/examples/dummy/package.json +++ b/examples/dummy/package.json @@ -24,13 +24,13 @@ "manifest": "x manifest --json" }, "devDependencies": { - "@ultimat3/cache": "2.0.0", - "@ultimat3/entity": "2.0.0", - "@ultimat3/testing": "2.0.0", + "@ultimat3/cache": "3.0.0", + "@ultimat3/entity": "3.0.0", + "@ultimat3/testing": "3.0.0", "typescript": "7.0.2" }, "dependencies": { - "@ultimat3/cli": "2.0.0", - "@ultimat3/core": "2.0.0" + "@ultimat3/cli": "3.0.0", + "@ultimat3/core": "3.0.0" } } diff --git a/examples/dummy/packages/core/package.json b/examples/dummy/packages/core/package.json index 74108596..0910fc55 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": "2.0.0", - "@ultimat3/money": "2.0.0", - "@ultimat3/time": "2.0.0" + "@ultimat3/core": "3.0.0", + "@ultimat3/money": "3.0.0", + "@ultimat3/time": "3.0.0" } } diff --git a/examples/dummy/packages/db/package.json b/examples/dummy/packages/db/package.json index 4271c9dc..2e129dbf 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": "2.0.0", - "@ultimat3/core": "2.0.0", - "@ultimat3/entity": "2.0.0" + "@ultimat3/cache": "3.0.0", + "@ultimat3/core": "3.0.0", + "@ultimat3/entity": "3.0.0" } } diff --git a/examples/dummy/packages/domain/package.json b/examples/dummy/packages/domain/package.json index 9a0b4f50..764b02a6 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": "2.0.0", - "@ultimat3/money": "2.0.0" + "@ultimat3/core": "3.0.0", + "@ultimat3/money": "3.0.0" } } diff --git a/examples/dummy/packages/i18n/package.json b/examples/dummy/packages/i18n/package.json index 0931fe82..6b115c4e 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": "2.0.0" + "@ultimat3/i18n": "3.0.0" } } diff --git a/examples/dummy/packages/mcp/package.json b/examples/dummy/packages/mcp/package.json index 809af2f8..b6955b4d 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": "2.0.0", - "@ultimat3/core": "2.0.0", - "@ultimat3/mcp": "2.0.0", - "@ultimat3/testing": "2.0.0" + "@ultimat3/action": "3.0.0", + "@ultimat3/core": "3.0.0", + "@ultimat3/mcp": "3.0.0", + "@ultimat3/testing": "3.0.0" } } diff --git a/examples/dummy/packages/ui/package.json b/examples/dummy/packages/ui/package.json index 6b71523a..eb8b23a3 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": "2.0.0", - "@ultimat3/render": "2.0.0", - "@ultimat3/ui": "2.0.0", + "@ultimat3/money": "3.0.0", + "@ultimat3/render": "3.0.0", + "@ultimat3/ui": "3.0.0", "solid-js": "1.9.14" } } diff --git a/framework.manifest.json b/framework.manifest.json index 8321d914..60fe49cb 100644 --- a/framework.manifest.json +++ b/framework.manifest.json @@ -1,6 +1,6 @@ { "version": 1, - "buildId": "9af8424a4d922b1bc50b18802b09b7ba2e1a910e74d48599dc99e3e8d2f4fdeb", + "buildId": "f47fe42062833be184261d0a52a957687aedc0e86567372bd1417ff2a07d4005", "tiers": { "0": [ "core", @@ -47,181 +47,181 @@ "packages": [ { "name": "@ultimat3/core", - "version": "2.0.0", + "version": "3.0.0", "tier": 0, "private": false }, { "name": "@ultimat3/schema", - "version": "2.0.0", + "version": "3.0.0", "tier": 0, "private": false }, { "name": "@ultimat3/cache", - "version": "2.0.0", + "version": "3.0.0", "tier": 1, "private": false }, { "name": "@ultimat3/db", - "version": "2.0.0", + "version": "3.0.0", "tier": 1, "private": false }, { "name": "@ultimat3/flags", - "version": "2.0.0", + "version": "3.0.0", "tier": 1, "private": false }, { "name": "@ultimat3/i18n", - "version": "2.0.0", + "version": "3.0.0", "tier": 1, "private": false }, { "name": "@ultimat3/money", - "version": "2.0.0", + "version": "3.0.0", "tier": 1, "private": false }, { "name": "@ultimat3/seo", - "version": "2.0.0", + "version": "3.0.0", "tier": 1, "private": false }, { "name": "@ultimat3/storage", - "version": "2.0.0", + "version": "3.0.0", "tier": 1, "private": false }, { "name": "@ultimat3/time", - "version": "2.0.0", + "version": "3.0.0", "tier": 1, "private": false }, { "name": "@ultimat3/auth", - "version": "2.0.0", + "version": "3.0.0", "tier": 2, "private": false }, { "name": "@ultimat3/entity", - "version": "2.0.0", + "version": "3.0.0", "tier": 2, "private": false }, { "name": "@ultimat3/http", - "version": "2.0.0", + "version": "3.0.0", "tier": 2, "private": false }, { "name": "@ultimat3/policy", - "version": "2.0.0", + "version": "3.0.0", "tier": 2, "private": false }, { "name": "@ultimat3/action", - "version": "2.0.0", + "version": "3.0.0", "tier": 3, "private": false }, { "name": "@ultimat3/jobs", - "version": "2.0.0", + "version": "3.0.0", "tier": 3, "private": false }, { "name": "@ultimat3/query", - "version": "2.0.0", + "version": "3.0.0", "tier": 3, "private": false }, { "name": "@ultimat3/realtime", - "version": "2.0.0", + "version": "3.0.0", "tier": 3, "private": false }, { "name": "@ultimat3/ai", - "version": "2.0.0", + "version": "3.0.0", "tier": 4, "private": false }, { "name": "@ultimat3/mail", - "version": "2.0.0", + "version": "3.0.0", "tier": 4, "private": false }, { "name": "@ultimat3/manifest", - "version": "2.0.0", + "version": "3.0.0", "tier": 4, "private": false }, { "name": "@ultimat3/mcp", - "version": "2.0.0", + "version": "3.0.0", "tier": 4, "private": false }, { "name": "@ultimat3/pwa", - "version": "2.0.0", + "version": "3.0.0", "tier": 4, "private": false }, { "name": "@ultimat3/render", - "version": "2.0.0", + "version": "3.0.0", "tier": 4, "private": false }, { "name": "@ultimat3/admin", - "version": "2.0.0", + "version": "3.0.0", "tier": 5, "private": false }, { "name": "@ultimat3/cli", - "version": "2.0.0", + "version": "3.0.0", "tier": 5, "private": false }, { "name": "@ultimat3/scraping", - "version": "2.0.0", + "version": "3.0.0", "tier": 5, "private": false }, { "name": "@ultimat3/testing", - "version": "2.0.0", + "version": "3.0.0", "tier": 5, "private": false }, { "name": "@ultimat3/ui", - "version": "2.0.0", + "version": "3.0.0", "tier": 5, "private": false }, { "name": "create-ultimate", - "version": "2.0.0", + "version": "3.0.0", "tier": 6, "private": false } diff --git a/llms.txt b/llms.txt index 337bd0ab..e02ef000 100644 --- a/llms.txt +++ b/llms.txt @@ -1,6 +1,6 @@ # 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. 2.0.0 as of 2026-08 — 29 `@ultimat3/*` packages plus the unscoped `create-ultimate`, 30 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. **2.0.0 is tagged, pushed and on npm**: `v2.0.0` is on the remote and the registry's `latest` is 2.0.0, so `bunx create-ultimate myapp` installs 2.0.0 and a docs example's `@ultimat3/*` pin resolves. Publication is not in lockstep: **`@ultimat3/scraping` has never reached npm at any version** — it landed after the 2.0.0 publish, so 29 of the 30 are on the registry. It is not opting out (`packages/scraping/package.json` declares the same `publishConfig` as the rest) and nothing in the repo notices, because every consumer resolves it through the workspace. It is 27th of 30 in the derived publish order, so the next release run reaches it and aborts with 26 packages already published irreversibly; it needs the one-time manual bootstrap every package gets before a trusted publisher can attach. `@ultimat3/flags` was the previous instance of exactly this and was closed at 2.0.0 (issue #84). Separately, no package has an OIDC trusted publisher attached, so the release workflow cannot publish and 2.0.0 was hand-published with no provenance attestation, unlike 1.1.0 and 1.2.0. 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. **3.0.0 in the repository and 2.0.0 on npm** `As of 2026-08` — 29 `@ultimat3/*` packages plus the unscoped `create-ultimate`, 30 in all, versioned in lockstep at one version, one commit, one tag. The registry is one release behind the repository between a bump and its publish run, which is where this commit sits: the `v3.0.0` tag and the workflow follow it, so `bunx create-ultimate myapp` still installs 2.0.0. Resolve it, do not trust this line — `npm view @ultimat3/core version`. **All 30 workspaces are on the registry** `As of 2026-08-19`: `@ultimat3/scraping` was the last never-published package and was bootstrapped by hand at 2.0.0 (`npm publish --access public --provenance=false`), the one-time step every package needs before a trusted publisher can attach, exactly as `@ultimat3/flags` had been (issue #84). Semver applies from 1.0.0: a breaking change to a documented API needs a major, which is what 3.0.0 is — 10 entries in its changelog section are marked `BREAKING —`, from a five-agent bug sweep, and none ships a codemod; 2.0.0 was the first major and carried 33. No package had an OIDC trusted publisher until 2026-08-19, so 2.0.0 was hand-published with no provenance attestation, unlike 1.1.0 and 1.2.0; all 30 are attached now, which is what lets 3.0.0 publish over OIDC at all. 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 pinned at `1.9.14` on the stable line rather than the `2.0.0-beta.N` prerelease, 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`. @@ -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 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. +- [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 3.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, first patch on the reconnected socket at 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 42b47ca3..0d42bd89 100644 --- a/packages/action/package.json +++ b/packages/action/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/action", - "version": "2.0.0", + "version": "3.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": "2.0.0", - "@ultimat3/core": "2.0.0", - "@ultimat3/http": "2.0.0", - "@ultimat3/policy": "2.0.0", - "@ultimat3/schema": "2.0.0" + "@ultimat3/cache": "3.0.0", + "@ultimat3/core": "3.0.0", + "@ultimat3/http": "3.0.0", + "@ultimat3/policy": "3.0.0", + "@ultimat3/schema": "3.0.0" } } diff --git a/packages/admin/package.json b/packages/admin/package.json index feec076e..76e44374 100644 --- a/packages/admin/package.json +++ b/packages/admin/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/admin", - "version": "2.0.0", + "version": "3.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": "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" + "@ultimat3/action": "3.0.0", + "@ultimat3/ai": "3.0.0", + "@ultimat3/cache": "3.0.0", + "@ultimat3/core": "3.0.0", + "@ultimat3/db": "3.0.0", + "@ultimat3/entity": "3.0.0", + "@ultimat3/i18n": "3.0.0", + "@ultimat3/jobs": "3.0.0", + "@ultimat3/mcp": "3.0.0", + "@ultimat3/money": "3.0.0", + "@ultimat3/policy": "3.0.0", + "@ultimat3/query": "3.0.0", + "@ultimat3/render": "3.0.0", + "@ultimat3/schema": "3.0.0", + "@ultimat3/ui": "3.0.0" } } diff --git a/packages/ai/package.json b/packages/ai/package.json index 31b59632..7163a151 100644 --- a/packages/ai/package.json +++ b/packages/ai/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/ai", - "version": "2.0.0", + "version": "3.0.0", "description": "LLM gateway, versioned prompts, evals as tests, embeddings, hybrid vector search, RAG", "license": "MIT", "type": "module", @@ -31,14 +31,14 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/action": "2.0.0", - "@ultimat3/cache": "2.0.0", - "@ultimat3/core": "2.0.0", - "@ultimat3/db": "2.0.0", - "@ultimat3/jobs": "2.0.0", - "@ultimat3/money": "2.0.0", - "@ultimat3/policy": "2.0.0", - "@ultimat3/schema": "2.0.0", - "@ultimat3/time": "2.0.0" + "@ultimat3/action": "3.0.0", + "@ultimat3/cache": "3.0.0", + "@ultimat3/core": "3.0.0", + "@ultimat3/db": "3.0.0", + "@ultimat3/jobs": "3.0.0", + "@ultimat3/money": "3.0.0", + "@ultimat3/policy": "3.0.0", + "@ultimat3/schema": "3.0.0", + "@ultimat3/time": "3.0.0" } } diff --git a/packages/auth/package.json b/packages/auth/package.json index 264b85a0..9bb3fd6d 100644 --- a/packages/auth/package.json +++ b/packages/auth/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/auth", - "version": "2.0.0", + "version": "3.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": "2.0.0", - "@ultimat3/db": "2.0.0", - "@ultimat3/schema": "2.0.0" + "@ultimat3/core": "3.0.0", + "@ultimat3/db": "3.0.0", + "@ultimat3/schema": "3.0.0" } } diff --git a/packages/cache/package.json b/packages/cache/package.json index 11c111c9..7fff4653 100644 --- a/packages/cache/package.json +++ b/packages/cache/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/cache", - "version": "2.0.0", + "version": "3.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": "2.0.0" + "@ultimat3/core": "3.0.0" } } diff --git a/packages/cli/package.json b/packages/cli/package.json index 1c8e65ab..288133e7 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/cli", - "version": "2.0.0", + "version": "3.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": "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" + "@ultimat3/action": "3.0.0", + "@ultimat3/admin": "3.0.0", + "@ultimat3/ai": "3.0.0", + "@ultimat3/cache": "3.0.0", + "@ultimat3/core": "3.0.0", + "@ultimat3/db": "3.0.0", + "@ultimat3/entity": "3.0.0", + "@ultimat3/http": "3.0.0", + "@ultimat3/i18n": "3.0.0", + "@ultimat3/jobs": "3.0.0", + "@ultimat3/mail": "3.0.0", + "@ultimat3/manifest": "3.0.0", + "@ultimat3/mcp": "3.0.0", + "@ultimat3/policy": "3.0.0", + "@ultimat3/pwa": "3.0.0", + "@ultimat3/query": "3.0.0", + "@ultimat3/realtime": "3.0.0", + "@ultimat3/render": "3.0.0", + "@ultimat3/schema": "3.0.0", + "@ultimat3/seo": "3.0.0", + "@ultimat3/storage": "3.0.0", + "@ultimat3/testing": "3.0.0", + "@ultimat3/time": "3.0.0" } } diff --git a/packages/core/package.json b/packages/core/package.json index d7f68741..6f091621 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/core", - "version": "2.0.0", + "version": "3.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 bce9619c..4c2276bb 100644 --- a/packages/create-ultimate/package.json +++ b/packages/create-ultimate/package.json @@ -1,6 +1,6 @@ { "name": "create-ultimate", - "version": "2.0.0", + "version": "3.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": "2.0.0" + "@ultimat3/cli": "3.0.0" } } diff --git a/packages/db/package.json b/packages/db/package.json index 4f09d112..b9557e6b 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/db", - "version": "2.0.0", + "version": "3.0.0", "description": "Postgres access, transactions, migrations and drift detection", "license": "MIT", "type": "module", @@ -31,7 +31,7 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "2.0.0" + "@ultimat3/core": "3.0.0" }, "peerDependencies": { "@electric-sql/pglite": ">=0.5.0" diff --git a/packages/entity/package.json b/packages/entity/package.json index 148cde15..162ee727 100644 --- a/packages/entity/package.json +++ b/packages/entity/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/entity", - "version": "2.0.0", + "version": "3.0.0", "description": "A table + its domain type + invariants the database also enforces", "license": "MIT", "type": "module", @@ -31,9 +31,9 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "2.0.0", - "@ultimat3/db": "2.0.0", - "@ultimat3/schema": "2.0.0", - "@ultimat3/time": "2.0.0" + "@ultimat3/core": "3.0.0", + "@ultimat3/db": "3.0.0", + "@ultimat3/schema": "3.0.0", + "@ultimat3/time": "3.0.0" } } diff --git a/packages/flags/package.json b/packages/flags/package.json index 29bc7bea..5f8e8b1d 100644 --- a/packages/flags/package.json +++ b/packages/flags/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/flags", - "version": "2.0.0", + "version": "3.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": "2.0.0" + "@ultimat3/core": "3.0.0" } } diff --git a/packages/http/package.json b/packages/http/package.json index caaf3547..da69d103 100644 --- a/packages/http/package.json +++ b/packages/http/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/http", - "version": "2.0.0", + "version": "3.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": "2.0.0", - "@ultimat3/i18n": "2.0.0", - "@ultimat3/schema": "2.0.0", - "@ultimat3/time": "2.0.0" + "@ultimat3/core": "3.0.0", + "@ultimat3/i18n": "3.0.0", + "@ultimat3/schema": "3.0.0", + "@ultimat3/time": "3.0.0" } } diff --git a/packages/i18n/package.json b/packages/i18n/package.json index cb4c1e08..00ae897e 100644 --- a/packages/i18n/package.json +++ b/packages/i18n/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/i18n", - "version": "2.0.0", + "version": "3.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": "2.0.0" + "@ultimat3/core": "3.0.0" } } diff --git a/packages/jobs/package.json b/packages/jobs/package.json index 119f62cf..17e712d6 100644 --- a/packages/jobs/package.json +++ b/packages/jobs/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/jobs", - "version": "2.0.0", + "version": "3.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": "2.0.0", - "@ultimat3/entity": "2.0.0", - "@ultimat3/schema": "2.0.0", - "@ultimat3/time": "2.0.0" + "@ultimat3/core": "3.0.0", + "@ultimat3/entity": "3.0.0", + "@ultimat3/schema": "3.0.0", + "@ultimat3/time": "3.0.0" } } diff --git a/packages/mail/package.json b/packages/mail/package.json index 36ea4503..ec2fe385 100644 --- a/packages/mail/package.json +++ b/packages/mail/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/mail", - "version": "2.0.0", + "version": "3.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": "2.0.0", - "@ultimat3/i18n": "2.0.0", - "@ultimat3/jobs": "2.0.0", - "@ultimat3/schema": "2.0.0", - "@ultimat3/time": "2.0.0" + "@ultimat3/core": "3.0.0", + "@ultimat3/i18n": "3.0.0", + "@ultimat3/jobs": "3.0.0", + "@ultimat3/schema": "3.0.0", + "@ultimat3/time": "3.0.0" } } diff --git a/packages/manifest/package.json b/packages/manifest/package.json index 466741bc..040780cc 100644 --- a/packages/manifest/package.json +++ b/packages/manifest/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/manifest", - "version": "2.0.0", + "version": "3.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": "2.0.0", - "@ultimat3/core": "2.0.0", - "@ultimat3/entity": "2.0.0", - "@ultimat3/jobs": "2.0.0", - "@ultimat3/query": "2.0.0" + "@ultimat3/action": "3.0.0", + "@ultimat3/core": "3.0.0", + "@ultimat3/entity": "3.0.0", + "@ultimat3/jobs": "3.0.0", + "@ultimat3/query": "3.0.0" } } diff --git a/packages/mcp/package.json b/packages/mcp/package.json index dd5c4dcf..d3c4f383 100644 --- a/packages/mcp/package.json +++ b/packages/mcp/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/mcp", - "version": "2.0.0", + "version": "3.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": "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" + "@ultimat3/action": "3.0.0", + "@ultimat3/core": "3.0.0", + "@ultimat3/entity": "3.0.0", + "@ultimat3/jobs": "3.0.0", + "@ultimat3/policy": "3.0.0", + "@ultimat3/query": "3.0.0", + "@ultimat3/schema": "3.0.0" } } diff --git a/packages/money/package.json b/packages/money/package.json index 78263fdf..824d744c 100644 --- a/packages/money/package.json +++ b/packages/money/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/money", - "version": "2.0.0", + "version": "3.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": "2.0.0", - "@ultimat3/schema": "2.0.0" + "@ultimat3/core": "3.0.0", + "@ultimat3/schema": "3.0.0" } } diff --git a/packages/policy/package.json b/packages/policy/package.json index 5327b84d..58c6de88 100644 --- a/packages/policy/package.json +++ b/packages/policy/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/policy", - "version": "2.0.0", + "version": "3.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": "2.0.0" + "@ultimat3/core": "3.0.0" } } diff --git a/packages/pwa/package.json b/packages/pwa/package.json index f643ac6f..50bf7460 100644 --- a/packages/pwa/package.json +++ b/packages/pwa/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/pwa", - "version": "2.0.0", + "version": "3.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": "2.0.0" + "@ultimat3/core": "3.0.0" } } diff --git a/packages/query/package.json b/packages/query/package.json index 2ccde74e..bfab928a 100644 --- a/packages/query/package.json +++ b/packages/query/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/query", - "version": "2.0.0", + "version": "3.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": "2.0.0", - "@ultimat3/core": "2.0.0", - "@ultimat3/http": "2.0.0", - "@ultimat3/policy": "2.0.0", - "@ultimat3/schema": "2.0.0" + "@ultimat3/cache": "3.0.0", + "@ultimat3/core": "3.0.0", + "@ultimat3/http": "3.0.0", + "@ultimat3/policy": "3.0.0", + "@ultimat3/schema": "3.0.0" } } diff --git a/packages/realtime/package.json b/packages/realtime/package.json index 8d3d6831..537ba315 100644 --- a/packages/realtime/package.json +++ b/packages/realtime/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/realtime", - "version": "2.0.0", + "version": "3.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": "2.0.0", - "@ultimat3/query": "2.0.0", + "@ultimat3/core": "3.0.0", + "@ultimat3/query": "3.0.0", "nats": "2.29.3" } } diff --git a/packages/render/package.json b/packages/render/package.json index 13a822b4..137dde50 100644 --- a/packages/render/package.json +++ b/packages/render/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/render", - "version": "2.0.0", + "version": "3.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": "2.0.0", - "@ultimat3/core": "2.0.0", - "@ultimat3/i18n": "2.0.0", - "@ultimat3/seo": "2.0.0", + "@ultimat3/cache": "3.0.0", + "@ultimat3/core": "3.0.0", + "@ultimat3/i18n": "3.0.0", + "@ultimat3/seo": "3.0.0", "sass": "1.102.0" } } diff --git a/packages/schema/package.json b/packages/schema/package.json index 3de1216a..c38de59d 100644 --- a/packages/schema/package.json +++ b/packages/schema/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/schema", - "version": "2.0.0", + "version": "3.0.0", "description": "Ultimate's validation seam: Standard Schema interface, the t namespace, JSON Schema output", "license": "MIT", "type": "module", diff --git a/packages/scraping/package.json b/packages/scraping/package.json index 97ed1eb3..4f94227e 100644 --- a/packages/scraping/package.json +++ b/packages/scraping/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/scraping", - "version": "2.0.0", + "version": "3.0.0", "description": "Browser automation as a job: scrape() returns a JobHandle", "license": "MIT", "type": "module", @@ -30,9 +30,9 @@ "test": "bun test" }, "dependencies": { - "@ultimat3/core": "2.0.0", - "@ultimat3/jobs": "2.0.0", - "@ultimat3/schema": "2.0.0", - "@ultimat3/storage": "2.0.0" + "@ultimat3/core": "3.0.0", + "@ultimat3/jobs": "3.0.0", + "@ultimat3/schema": "3.0.0", + "@ultimat3/storage": "3.0.0" } } diff --git a/packages/seo/package.json b/packages/seo/package.json index fb1d8260..48194436 100644 --- a/packages/seo/package.json +++ b/packages/seo/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/seo", - "version": "2.0.0", + "version": "3.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": "2.0.0" + "@ultimat3/core": "3.0.0" } } diff --git a/packages/storage/package.json b/packages/storage/package.json index 653c7b91..836ce3cd 100644 --- a/packages/storage/package.json +++ b/packages/storage/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/storage", - "version": "2.0.0", + "version": "3.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": "2.0.0" + "@ultimat3/core": "3.0.0" } } diff --git a/packages/testing/package.json b/packages/testing/package.json index b0bb52a7..1bb6765f 100644 --- a/packages/testing/package.json +++ b/packages/testing/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/testing", - "version": "2.0.0", + "version": "3.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": "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" + "@ultimat3/cache": "3.0.0", + "@ultimat3/core": "3.0.0", + "@ultimat3/db": "3.0.0", + "@ultimat3/entity": "3.0.0", + "@ultimat3/jobs": "3.0.0", + "@ultimat3/mail": "3.0.0", + "@ultimat3/time": "3.0.0" } } diff --git a/packages/time/package.json b/packages/time/package.json index 7cca2cf5..442062ca 100644 --- a/packages/time/package.json +++ b/packages/time/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/time", - "version": "2.0.0", + "version": "3.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": "2.0.0" + "@ultimat3/core": "3.0.0" } } diff --git a/packages/ui/package.json b/packages/ui/package.json index aebbe28c..1688693c 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -1,6 +1,6 @@ { "name": "@ultimat3/ui", - "version": "2.0.0", + "version": "3.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": "2.0.0", - "@ultimat3/i18n": "2.0.0", - "@ultimat3/money": "2.0.0", - "@ultimat3/time": "2.0.0" + "@ultimat3/core": "3.0.0", + "@ultimat3/i18n": "3.0.0", + "@ultimat3/money": "3.0.0", + "@ultimat3/time": "3.0.0" }, "peerDependencies": { "solid-js": "^1.9.0" diff --git a/wiki/Admin-Dashboard.md b/wiki/Admin-Dashboard.md index 93a6ffa6..306f4fdc 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 1.x or 2.0.0 ([axiom](Home)). The extension point is that the admin is your app. +No plugin API, in 1.x, 2.x or 3.0.0 ([axiom](Home)). The extension point is that the admin is your app. ## Deployment diff --git a/wiki/Configuration.md b/wiki/Configuration.md index 1e73d0f6..b79aeedf 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 not in 2.0.0** — 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 3.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` ship; `local-first` is not in 2.0.0 ([Realtime](Realtime)) | +| `realtime.tier` | `'channels' \| 'live-queries' \| 'local-first'` | `'channels'` | **names, not numbers**. `channels` and `live-queries` ship; `local-first` is not in 3.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)) | diff --git a/wiki/FAQ.md b/wiki/FAQ.md index 961cacef..d7759fda 100644 --- a/wiki/FAQ.md +++ b/wiki/FAQ.md @@ -6,19 +6,19 @@ Honest answers. Where something is not built yet, it says so. ### Is it production ready? -**`As of 2026-08`.** Stable API, semver from here. 29 `@ultimat3/*` packages plus the unscoped `create-ultimate` — **30 in all** — are **versioned** in lockstep: one version, one commit, one tag. 2.0.0 is versioned, tagged and published. +**`As of 2026-08`.** Stable API, semver from here. 29 `@ultimat3/*` packages plus the unscoped `create-ultimate` — **30 in all** — are **versioned** in lockstep: one version, one commit, one tag. -**Publication is a separate step from versioning, and it is not in lockstep.** One package is behind: +**Publication is a separate step from versioning, so the registry runs one release behind the repository until a tag's workflow run lands.** That is where 3.0.0 sits. There are no publication holes left — every one of the 30 is on npm. -| Fact | State, verified against the registry `As of 2026-08` | -|---|---| -| What you can install | **2.0.0** — `npm view @ultimat3/core version` answers it, and `bunx create-ultimate myapp` gives you it | -| Tagged | `v2.0.0`, pushed to origin | -| On the registry | **29 of the 30**, all at 2.0.0 | -| `@ultimat3/scraping` | on npm at **no version** — the registry answers 404. It landed after the 2.0.0 publish run, so the run never saw it, and nothing in the repo notices because every consumer resolves it through the workspace. `bun add @ultimat3/scraping` fails ([Known gaps](Known-Gaps)) | -| What that costs | it is **27th of 30** in the derived publish list, so the next release run dies on it with 26 packages already published irreversibly. Its first publish is a manual bootstrap — a trusted publisher cannot attach to a package that does not exist | +| Fact | State `As of 2026-08-19` | Resolve it yourself | +|---|---|---| +| What you can install | **2.0.0** — `bunx create-ultimate myapp` gives you it | `npm view @ultimat3/core version` | +| Repository version | **3.0.0**, all 30 stamped in one commit | the top section of [`CHANGELOG.md`](https://github.com/developerz-ai/ultimate/blob/main/CHANGELOG.md) | +| Tagged | `v2.0.0` is on origin; `v3.0.0` follows the commit this page was written from | [the repository's tags](https://github.com/developerz-ai/ultimate/tags) | +| On the registry | **all 30**, at 2.0.0 | `npm view @ultimat3/scraping version` | +| Provenance | 1.1.0 and 1.2.0 carry an attestation; **2.0.0 does not** | `npm view @ultimat3/core@2.0.0 dist.attestations` | -1.1.0 was the first release the workflow published over OIDC trusted publishing, with provenance; 1.0.0 was the manual bootstrap. `@ultimat3/flags` was the previous never-published package and 2.0.0 closed it — `2.0.0` is its only version on the registry. +1.1.0 was the first release the workflow published over OIDC trusted publishing, with provenance; 1.0.0 was the manual bootstrap. 2.0.0 was hand-published, because **no package had a trusted publisher attached** and the OIDC exchange had nothing to verify against; all 30 were attached on 2026-08-19, for the first time, which is what lets 3.0.0 publish over OIDC at all. `@ultimat3/scraping` and `@ultimat3/flags` were the two never-published packages and both are closed — each by the one-time manual bootstrap every package needs before a trusted publisher can attach. That is exactly what the version claims — a stable API under semver, not a promise about your infrastructure. @@ -28,7 +28,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)) | -| 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 | +| Not in 3.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? @@ -118,7 +118,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 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. +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 3.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? @@ -132,7 +132,7 @@ Stated risk, not a hidden one. `As of 2026-08` long-running Bun processes are le ### Where do plugins fit? -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. +Nowhere — the plugin API is not in 1.x, not in 2.x and not in 3.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 84616667..6c21ba62 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 29 `@ultimat3/*` packages plus the unscoped `create-ultimate` and versions all 30 in lockstep at **2.0.0** — the first major, 33 entries marked `BREAKING —` and no codemod ([Upgrading](Upgrading)). **What `bunx create-ultimate myapp` installs is 2.0.0**: `v2.0.0` is tagged and pushed, and the registry's `latest` is 2.0.0 for 29 of the 30 workspaces. **`@ultimat3/scraping` has never been published at any version** — verified against the registry `As of 2026-08`, where it is a 404 — so `bun add @ultimat3/scraping` fails, browser automation is reachable only from a checkout, and its one-time manual bootstrap is owed before the next release run ([Known gaps](Known-Gaps)). 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). +`As of 2026-08`. Stable API — semver from here ([Upgrading](Upgrading)). The repository holds 29 `@ultimat3/*` packages plus the unscoped `create-ultimate` and versions all 30 in lockstep at **3.0.0** — a major, 10 entries marked `BREAKING —` from a five-agent bug sweep and no codemod ([Upgrading](Upgrading)); 2.0.0 was the first major and carried 33. **What `bunx create-ultimate myapp` installs is 2.0.0**: npm's `latest` is 2.0.0 for all 30 workspaces, and the `v3.0.0` tag and its publish run follow the commit this page was written from — the registry runs one release behind the repository in between. Resolve it, do not trust this line: `npm view @ultimat3/core version`. **All 30 workspaces are on the registry** `As of 2026-08-19`, `@ultimat3/scraping` included — it was the last never-published package and was bootstrapped by hand at 2.0.0, so `bun add @ultimat3/scraping` resolves. 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 3.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 ff68c399..9ef420fa 100644 --- a/wiki/Home.md +++ b/wiki/Home.md @@ -2,9 +2,9 @@ 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`.** 29 `@ultimat3/*` packages plus the unscoped `create-ultimate` — 30 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)). +**`As of 2026-08`.** 29 `@ultimat3/*` packages plus the unscoped `create-ultimate` — 30 in all — version in lockstep at **3.0.0**, a major: 10 changelog entries are marked `BREAKING —` from a five-agent bug sweep and none ships a codemod ([Upgrading](Upgrading)). 2.0.0 was the first major and carried 33. -**2.0.0 is tagged and on npm.** `v2.0.0` is pushed and the registry's `latest` is **2.0.0**, which is what `bunx create-ultimate myapp` installs. Every page below that says **fixed in 2.0.0** means fixed in a release you can install; the "on 1.2.0, do X" column beside it is for readers still pinned there, not a current workaround. **29 of the 30 are on the registry** — `@ultimat3/scraping` has never been published, because it landed after the 2.0.0 run, so `bun add @ultimat3/scraping` answers 404 and browser automation is reachable only from a checkout ([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 repository is at 3.0.0; npm's `latest` is 2.0.0.** The registry runs one release behind the repository between a version bump and its publish run, and that is where 3.0.0 sits: the `v3.0.0` tag and its workflow run follow the commit this page was written from. **`bunx create-ultimate myapp` installs 2.0.0 until they land** — resolve it with `npm view @ultimat3/core version` rather than believing this sentence. Every page below that says **fixed in 2.0.0** means fixed in a release you can install today; the "on 1.2.0, do X" column beside it is for readers still pinned there, not a current workaround. **All 30 workspaces are on the registry** `As of 2026-08-19` — `@ultimat3/scraping` was the last never-published package and it was bootstrapped by hand at 2.0.0, so `bun add @ultimat3/scraping` resolves and browser automation no longer needs a checkout. 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; 2.0.0 was hand-published with no attestation, because no package had a trusted publisher until 2026-08-19, when all 30 got one. 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 7ede696a..32d9cb63 100644 --- a/wiki/Jobs-And-Workflows.md +++ b/wiki/Jobs-And-Workflows.md @@ -172,14 +172,14 @@ 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 **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. +Two implementations ship in 1.0.0. Two more are **not in 3.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` | **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 | +| `redis` | **not in 3.0.0 — throws `X_NOT_IMPLEMENTED`** | high-throughput, short jobs | would need the outbox relay; loses "queue state in one backup" | +| `nats` | **not in 3.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. @@ -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 — neither is in 2.0.0 | 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 3.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 c8ee4718..0c6139aa 100644 --- a/wiki/Known-Gaps.md +++ b/wiki/Known-Gaps.md @@ -4,7 +4,9 @@ Defects and unfinished seams named here rather than left to be discovered — ea 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. **Fixed in 2.0.0** means fixed in a release you can install: `v2.0.0` is tagged and the registry's `latest` is **2.0.0**, verified `As of 2026-08`. The "on 1.2.0, do X" column is for readers still pinned to 1.x — a legacy workaround, not a current one; upgrading is the fix, and the 33 breaking entries are the cost ([Upgrading](Upgrading)). 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 fixed in a release you can install: `v2.0.0` is tagged and the registry's `latest` is **2.0.0** — resolve it with `npm view @ultimat3/core version`, `As of 2026-08-19`. The repository is at **3.0.0** and its tag and publish run follow; every row below is stated against 2.0.0, which is what the registry serves. The "on 1.2.0, do X" column is for readers still pinned to 1.x — a legacy workaround, not a current one; upgrading is the fix, and the breaking entries are the cost ([Upgrading](Upgrading)). 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. + +**Publication is no longer a gap.** All 30 workspaces are on the registry `As of 2026-08-19`: `@ultimat3/scraping` was the last never-published package, and it was bootstrapped by hand at 2.0.0 — the one-time step in [`PUBLISHING.md`](https://github.com/developerz-ai/ultimate/blob/main/PUBLISHING.md) that every package needs before a trusted publisher can attach, the same one that closed `@ultimat3/flags`. `bun add @ultimat3/scraping` resolves, so browser automation no longer needs a checkout and the `@file:` workaround this page used to carry is deleted rather than softened. Check it with `npm view @ultimat3/scraping version`. The step comes due again only for a package added after a release run. | Gap | Symptom | Work around it by | |---|---|---| @@ -17,7 +19,6 @@ The version is the footer's, once. **Fixed in 2.0.0** means fixed in a release y | 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 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/scraping` is not on npm | the repository versions all 30 workspaces in lockstep at 2.0.0 and **29 of them are published** — the registry answers 404 for `@ultimat3/scraping` at every version, verified `As of 2026-08`. It landed after the 2.0.0 publish run, so the run never saw it; nothing in the repo notices, because every consumer resolves it through the workspace. So `bun add @ultimat3/scraping` fails and browser automation is reachable only from a checkout. Nothing in the package opts out: it declares the same `publishConfig` as the other 29. Its first publish is a manual bootstrap by an npm org member, because a trusted publisher cannot attach to a package that does not exist — and it is **27th of 30** in the derived publish list, so the **next** release run aborts on it with 26 packages already published at the new version and an npm publish cannot be undone ([`PUBLISHING.md`](https://github.com/developerz-ai/ultimate/blob/main/PUBLISHING.md) step 1). `@ultimat3/flags` was the same gap and 2.0.0 closed it — `2.0.0` is `flags`' only version on the registry. **Not in `CHANGELOG.md`'s list** | depend on the directory instead of the registry: `git clone --depth 1 --branch v2.0.0 https://github.com/developerz-ai/ultimate.git` then `bun add @ultimat3/scraping@file:./ultimate/packages/scraping`. Its four runtime dependencies — `@ultimat3/core`, `@ultimat3/jobs`, `@ultimat3/schema`, `@ultimat3/storage` — are all on npm at 2.0.0, so only `scraping` itself needs the local path | | 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 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` | diff --git a/wiki/Migrating-An-Existing-App.md b/wiki/Migrating-An-Existing-App.md index 52e98f35..a92723b4 100644 --- a/wiki/Migrating-An-Existing-App.md +++ b/wiki/Migrating-An-Existing-App.md @@ -7,7 +7,7 @@ | You want | Page | |---|---| | move an app **from another framework** to Ultimate | this page | -| move an app **from `@ultimat3/*` 1.x to 2.0.0** | [Upgrading](Upgrading) | +| move an app **from `@ultimat3/*` 1.x or 2.x to the current major** | [Upgrading](Upgrading) | | what is unfinished in the release being adopted | [Known gaps](Known-Gaps) | | every `X_*` code named below | [Error codes](Error-Codes) | diff --git a/wiki/Migrations-And-Backfills.md b/wiki/Migrations-And-Backfills.md index 0fa391b6..134e02b3 100644 --- a/wiki/Migrations-And-Backfills.md +++ b/wiki/Migrations-And-Backfills.md @@ -14,7 +14,7 @@ One engine per concern. A **migration** changes the shape of a table — schema, ## Migrations: one engine, one ledger -**This page documents the code at 2.0.0, which is what the registry's `latest` resolves to** `As of 2026-08`. On a pin still at 1.x, take [Known gaps → `x db gen` / `x db migrate`](Known-Gaps) and its 1.1.0/1.2.0 workarounds instead. `CHANGELOG.md` names the release each fix shipped in. +**This page documents the code at 2.0.0, which is what `npm view @ultimat3/core version` answers** `As of 2026-08-19`. The repository is at 3.0.0, whose tag and publish run follow this page's commit; its `3.0.0` section adds `x db seed` and fixes two ledger defects (`rollback({ steps: -1 })` reverted every migration but the last; a migration deleted from the tree was invisible to the audit), none of which this page yet documents. On a pin still at 1.x, take [Known gaps → `x db gen` / `x db migrate`](Known-Gaps) and its 1.1.0/1.2.0 workarounds instead. `CHANGELOG.md` names the release each fix shipped in. `x db gen` and the `ROLE=migrate` release-phase container run the **same** engine — `packages/db`'s `migrate()`/`generateMigration()` — not two. **In 1.1.0** they did not: `x db gen`'s subcommands shelled out to `bunx drizzle-kit`, a second schema engine with its own journal, declared in no `package.json` and fetched unpinned at run time, which is why a 1.1.0 scaffold's own `bin/setup` fails. That shelling-out is gone from current source — `cmd-db.ts` calls `generateAppMigration` and `runMigrations` from `@ultimat3/db`/`@ultimat3/cli` directly, and the only remaining mention of `drizzle-kit` anywhere is a file header comment recording the history. diff --git a/wiki/Queries-And-Live-Queries.md b/wiki/Queries-And-Live-Queries.md index 4435ce90..23ad51d6 100644 --- a/wiki/Queries-And-Live-Queries.md +++ b/wiki/Queries-And-Live-Queries.md @@ -2,7 +2,7 @@ A `query` is a read. `live: true` makes it subscribable. Never writes, never enqueues, never sends mail. -`As of 2026-08`. Stable API — semver from here ([Upgrading](Upgrading)). Tiers 1–2 of [Realtime](Realtime) ship; `persist: true` (tier 3, local-first) is not in 2.0.0. +`As of 2026-08`. Stable API — semver from here ([Upgrading](Upgrading)). Tiers 1–2 of [Realtime](Realtime) ship; `persist: true` (tier 3, local-first) is not in 3.0.0. ## The canonical shape @@ -23,7 +23,7 @@ export const liveFeed = query({ | `input` | yes | Standard Schema; `t` re-exported from `@ultimat3/query`, so a query file imports one package. The shipped provider is `@ultimat3/schema`'s dependency-free builtin — ArkType, Zod and Valibot are optional swaps behind `configureSchemaProvider`, and no adapter ships. Parsed before `policy`, before `sql`. Becomes the GET query string, the client hook argument, and the MCP tool's JSON Schema | | `policy` | yes | `can('')`, optionally with a predicate over `{ input, actor }`. Evaluated at HTTP call, client hook, subscribe, **and per delivered row** | | `live` | no — default `false` | registers the query with the incremental matcher. Requires a deterministic, bounded `sql` | -| `persist` | no — default `false` | tier 3. Swaps the client result store from memory to IndexedDB and makes the mutator queue durable. Implies `live: true`. Not in 2.0.0 | +| `persist` | no — default `false` | tier 3. Swaps the client result store from memory to IndexedDB and makes the mutator queue durable. Implies `live: true`. Not in 3.0.0 | | `sql` | yes | `(input) => SqlSource`. `from()` (`@ultimat3/query`) wraps an already-resolved `@ultimat3/entity` repo call and restates `where`/`orderBy`/`limit` for the matcher to read back; `select`/`preload` happen inside that repo call, before `from()` ever sees a row. No ORM in the graph. SQL-transparent: `toSQL()` prints the statement verbatim so an agent can read it and self-correct | | `mcp` | no — default not exposed | `{ expose: true, description }` makes the read an MCP tool. Opt-in, unlike an action: a read hands rows to an agent, so silence exposes nothing | | `mcp.visibleTo` | no | roles that may see the projected tool; a caller whose role is not named gets ToolNotFound, never Forbidden — the policy still decides every call | diff --git a/wiki/Realtime.md b/wiki/Realtime.md index d48dd68b..000d8a07 100644 --- a/wiki/Realtime.md +++ b/wiki/Realtime.md @@ -2,7 +2,7 @@ Three tiers, one ladder. Same mutator shape at every rung — climbing is a config change, never a rewrite. -`As of 2026-08`. Stable API — semver from here ([Upgrading](Upgrading)). Tiers 1–2 ship. Tier 3 (local-first) is not in 2.0.0. +`As of 2026-08`. Stable API — semver from here ([Upgrading](Upgrading)). Tiers 1–2 ship. Tier 3 (local-first) is not in 3.0.0. ## The ladder diff --git a/wiki/Tutorial-04-Jobs-And-Realtime.md b/wiki/Tutorial-04-Jobs-And-Realtime.md index db4d6ce5..3eadf014 100644 --- a/wiki/Tutorial-04-Jobs-And-Realtime.md +++ b/wiki/Tutorial-04-Jobs-And-Realtime.md @@ -139,7 +139,7 @@ A dead job is never filtered out of view. | `retry --from-step ` | drops that step so it re-executes; everything before it replays from storage | | `drain --to memory\|redis\|nats` | moves `ready`/`delayed`/`suspended` jobs to another driver; enqueues on the target **before** acking the source | -The Redis and NATS **job** drivers are not in 2.0.0 — each throws `X_NOT_IMPLEMENTED` behind an interface that already ships, rather than pretending to work. Postgres is the shipped driver, and it is the one `x dev` boots. +The Redis and NATS **job** drivers are not in 3.0.0 — each throws `X_NOT_IMPLEMENTED` behind an interface that already ships, rather than pretending to work. Postgres is the shipped driver, and it is the one `x dev` boots. ## A live query @@ -230,7 +230,7 @@ bunx x dev --once --port 3100 Both are per-node recovery — neither run crossed NATS, so neither is a multi-node result nor a throughput figure. Detail and limits: [Realtime](Realtime). -Realtime tier 3 (local-first, `persist: true`) is not in 2.0.0. +Realtime tier 3 (local-first, `persist: true`) is not in 3.0.0. ## Next diff --git a/wiki/Tutorial-06-Growing-Up.md b/wiki/Tutorial-06-Growing-Up.md index 5d269a59..1e1f5936 100644 --- a/wiki/Tutorial-06-Growing-Up.md +++ b/wiki/Tutorial-06-Growing-Up.md @@ -107,7 +107,7 @@ Point `OTEL_EXPORTER_OTLP_ENDPOINT` at your collector's **HTTP** receiver, `:431 | Concern | Interface | Decided by | |---|---|---| | rows | `@ultimat3/db` · `DbClient` | `DATABASE_URL` — unset is PGlite, and PGlite is `x dev` only | -| job queue | `@ultimat3/jobs` · `JobDriver` | `jobs.driver: 'postgres'`. Redis and NATS drivers are interface-complete stubs that throw `X_NOT_IMPLEMENTED` — not in 2.0.0 | +| job queue | `@ultimat3/jobs` · `JobDriver` | `jobs.driver: 'postgres'`. Redis and NATS drivers are interface-complete stubs that throw `X_NOT_IMPLEMENTED` — not in 3.0.0 | | realtime fanout | `@ultimat3/realtime` · `Transport` | `NATS_URL` | | change feed | `@ultimat3/realtime` · `ChangeFeed` | `REPLICATION_*` | | cache | `@ultimat3/cache` · `CacheTier` | `cache.tiers` + `REDIS_URL` | @@ -139,7 +139,7 @@ Named rather than left to be discovered: | a custom-metrics adapter, which the chart's HPAs need and the framework never ships | 3–4 | | `x logs` planned — `X_NOT_IMPLEMENTED`, with `x dev` → the `/_x` timeline panel as its fix | any | | Redis and NATS **job** drivers throw `X_NOT_IMPLEMENTED` | any | -| realtime tier 3 (local-first, `persist: true`), the plugin API, multi-region replication | not in 2.0.0 | +| realtime tier 3 (local-first, `persist: true`), the plugin API, multi-region replication | not in 3.0.0 | Each sits behind an interface that ships today and fails loudly, rather than pretending to work. The full list, with a workaround per row: [Known gaps](Known-Gaps). diff --git a/wiki/Upgrading.md b/wiki/Upgrading.md index d4bf54ce..c14d5854 100644 --- a/wiki/Upgrading.md +++ b/wiki/Upgrading.md @@ -2,9 +2,36 @@ **`As of 2026-08`. Semver applies from here.** A breaking change to a documented API needs a major. Every `@ultimat3/*` version is pinned exactly and moves in lockstep — never mix versions. -**2.0.0 is that major.** [`CHANGELOG.md`](https://github.com/developerz-ai/ultimate/blob/main/CHANGELOG.md)'s `2.0.0` section carries **33** entries marked `BREAKING —`, and each one changes a surface the table below covers. Read all 33 before you move a pin from 1.x — **no codemod ships with 2.0.0**, so each is a manual edit named by its entry. +**There are two majors to cross.** [`CHANGELOG.md`](https://github.com/developerz-ai/ultimate/blob/main/CHANGELOG.md) is the source; neither ships a codemod, so every entry is a manual edit the entry itself names. -> **You can move that pin** `As of 2026-08`. 2.0.0 is tagged and on npm: `npm view @ultimat3/core version` answers **2.0.0**, and 29 of the 30 workspaces resolve at it. The exception is `@ultimat3/scraping`, which has never reached npm at any version — a `@ultimat3/scraping` pin resolves to nothing at 1.x or 2.x alike ([Known gaps](Known-Gaps)). +| From → to | Breaking entries | Read | +|---|---|---| +| 1.x → 2.0.0 | **33** | the `2.0.0` section, in order | +| 2.0.0 → 3.0.0 | **10**, all from a five-agent bug sweep | the `3.0.0` section, in order | +| 1.x → 3.0.0 | **43** | both sections, oldest first | + +Each entry changes a surface the table below covers. + +> **The pin you can move to today is 2.0.0** `As of 2026-08-19`. `npm view @ultimat3/core version` answers **2.0.0**, and **all 30** workspaces resolve at it — `@ultimat3/scraping` included, since its one-time bootstrap landed at 2.0.0 ([Known gaps](Known-Gaps)). The repository is at 3.0.0 and the `v3.0.0` tag and its publish run follow this page's commit, so a 3.0.0 pin does not resolve until they land. Resolve before you pin: `npm view @ultimat3/core version`. + +## 2.0.0 → 3.0.0, entry by entry + +Ten `BREAKING —` entries, all from one bug sweep. Each was a documented surface that did nothing, or did the wrong thing; the fix is the edit named beside it. Full rationale per row in [`CHANGELOG.md`](https://github.com/developerz-ai/ultimate/blob/main/CHANGELOG.md)'s `3.0.0` section. + +| Surface | The edit | +|---|---| +| `defineAuth({ mfa: { required: true } })` — refused at boot (`X_CONFIG_INVALID`), and `AuthMfaPolicy.required` narrowed to the literal `false` | delete `mfa.required`; enforce the requirement in your own enrolment flow. Nothing ever read the flag, so a user who never enrolled got a fully-privileged session under it | +| `enrolTotp(input)` → `enrolTotp(auth, input)`; `input.issuer` is now optional | pass the `auth` you built with `defineAuth`. The configured issuer never reached the `otpauth://` URI before | +| `@ultimat3/http` no longer exports `appErrorStatus()` | read your own registration module. `registerErrorStatus()` and `statusFor()` are unchanged | +| `SyncSocket.lastSeenAt` → `lastSeenMonotonicMs`, on `Clock.monotonic()` | rename the read. If you were formatting it as a date you were already wrong — the rename makes `new Date(...)` a compile error | +| `SQL_OUTBOX_RELEASE` and `SQL_OUTBOX_MARK_PUBLISHED` take one more parameter each (1 → 2, 2 → 3): the claimant | pass the claimant. `OutboxStore.release`/`markPublished` take it as an optional trailing argument, so an unfenced store still compiles | +| `SocketRegistry.sweepIdle()` → `idle()`, which returns the over-budget sockets and removes nothing | call `idle()` and evict through the node, or set the budget with `createSyncNode({ idleTimeoutMs })` | +| `DESCRIPTION_MIN_LENGTH` deleted from `@ultimat3/seo` | delete the import. There is no replacement and no minimum description length is checked — the constant was documented as enforced and was read by no validator | +| A metric redeclared with different `bounds` or a different `observe` is refused (`X_METRIC_NAME_INVALID`) | make the second declaration state the same `bounds`/`observe`, or fetch the handle without options — `gauge(name)` is unchanged | +| `Seed.run()` resolves with `SeedRun` instead of `void` | re-type the result if you typed it `void`. Awaiting it for the side effect alone is unaffected | +| `SeedContext.insert` skips a stored row instead of overwriting it | expect `skipped`, not an overwrite. `upsert` is the verb for a row the table keys | + +`cachedFormatter` and `canonicalLocale` moved from `@ultimat3/time` to `@ultimat3/core` and are re-exported from `time`, so **no import breaks** — it is listed here because the move is real, not because it costs an edit. ## What semver covers @@ -34,7 +61,7 @@ | Breaking changes land with codemods | if `x upgrade` cannot codemod it, the changelog carries the manual step | | Dependency upgrades are framework work | Solid is pinned to **`1.9.14`, the stable line** — Solid 2 is still prerelease (`2.0.0-beta.N`, DOM renderer split into `@solidjs/web`) and every app inherits whatever core this repo pins. Bumping it is a framework release, never an app-level `bun update`. There is no ArkType or Drizzle pin to carry: `@ultimat3/schema` ships dependency-free builtin validators (ArkType is an optional provider you adapt yourself) and `@ultimat3/entity` ships its own `postgresDriver()` | | Bun floor | `>=1.3`, target 2.0. Below the floor → `X_BUN_VERSION` | -| Not in 2.0.0, behind the interfaces that ship today | realtime tier 3 (`persist: true`, local-first), the plugin API, multi-region replication, and the Redis/NATS **job** drivers — the last throw `X_NOT_IMPLEMENTED` with a runnable `fix:` rather than pretending to work | +| Not in 3.0.0, behind the interfaces that ship today | realtime tier 3 (`persist: true`, local-first), the plugin API, multi-region replication, and the Redis/NATS **job** drivers — the last throw `X_NOT_IMPLEMENTED` with a runnable `fix:` rather than pretending to work | Do not upgrade a transitive dependency of a `@ultimat3/*` package by hand. Open an issue instead — the pin is deliberate. @@ -103,7 +130,7 @@ Server behavior on a stale build ID: Full detail: [PWA and offline](PWA-And-Offline). -## Migrating jobs between drivers — **not in 2.0.0** +## Migrating jobs between drivers — **not in 3.0.0** `jobs.driver` accepts **`postgres` \| `redis` \| `nats`**, and `postgres` is the only one implemented — `redis` and `nats` are interface-complete stubs that throw `X_NOT_IMPLEMENTED`. So **there is no driver migration to perform** `As of 2026-08`: `x jobs drain --to redis` constructs the target and fails on its first enqueue. @@ -126,7 +153,7 @@ Job code never changes across a driver: `steps` is a driver member, so step pers | From → to | Change | Notes | |---|---|---| | tier 1 → tier 2 | `live: true` on the query | needs a `replicator` role and `orderBy` + `limit` on the `sql` | -| tier 2 → tier 3 | `persist: true` on the query | not in 2.0.0. No new mutators, no new authz, no new server code | +| tier 2 → tier 3 | `persist: true` on the query | not in 3.0.0. No new mutators, no new authz, no new server code | | `memory` → `nats` transport | `realtime.transport`, and **`realtime.urlEnv`** — the env *key name*, not a URL. There is no `realtime.url` field | roll `sync` and `replicator`; clients reconnect with server-directed backoff. What actually decides the transport at boot is **`NATS_URL` being set**: `selectTransport(env)` never reads `config.realtime.transport`, so the config field documents intent and the env var makes the switch ([Configuration](Configuration)) | ## Where the facts live diff --git a/wiki/_Footer.md b/wiki/_Footer.md index 4804a16d..934be764 100644 --- a/wiki/_Footer.md +++ b/wiki/_Footer.md @@ -5,7 +5,7 @@ them. No heading, deliberately. Reported 2026-08; rejected on those grounds. --> -**Ultimate** — v2.0.0 `As of 2026-08`. Stable API, semver from here. MIT licensed. +**Ultimate** — v3.0.0 `As of 2026-08`. Stable API, semver from here. MIT licensed. What npm serves is `npm view @ultimat3/core version`, never this line. This footer is the **only** page that stamps a version. It renders under every wiki page, so one release bumps one line; a stamp on a second page is 46 hand-copies of one fact, and every one of them goes stale on the next tag.