# Changelog 0.19

## v0.19.9 — 2026-09-14

### 🐛 Fixed

#### Dev, Keel & create-oke

- `oke dev` prompts for Vault boot gaps again when the app calls `oke()` at module load (Keel / `OPENROUTER_API_KEY`). The probe now reads `app.$options.secrets` — the same list boot uses — instead of the drained `secretRegistry`. A failed app-child boot exits immediately instead of waiting 30s under `bun --hot`.

- Keel `prepare` on Windows uses a directory junction (absolute target) instead of a symlink that needs Developer Mode (`EPERM` / `-4048`). Scripts invoke the checkout CLI (`bun ../../src/cli/index.ts`) so PowerShell cannot pick a global `oke` that then fails with `Cannot find package 'zod'`.

## v0.19.8 — 2026-09-14

### 🐛 Fixed

- `oke dev` recovers when Postgres rejects `oke` after you delete and recreate the project folder at the same path. Compose volumes keep the first init password; a new `.env.local` does not. Dev now reuses `~/.oke/stacks/<id>.env` and, on `password authentication failed`, resets that project's volumes once.

## v0.19.7 — 2026-09-14

### 🐛 Fixed

- create-oke no longer deletes a newly created project when you press Ctrl+C during `bun install` or `bun run dev`. That wipe often failed mid-tree on Windows (`EBUSY`) and left an empty or half-deleted folder. Cancel still removes an unfinished folder if you interrupt before scaffold finishes.

## v0.19.6 — 2026-09-14

### 🐛 Fixed

- Console on Windows served the "Shell assets not built" fallback at `:6533` even when `ui-next/dist` was present. SPA lookup used `file://` URL `.pathname` (`/C:/…`) instead of a filesystem path.

### ♻️ Changed

#### Docs

- CLI troubleshooting covers Console "Shell assets not built" on Windows.

## v0.19.5 — 2026-09-14

### ♻️ Changed

#### Dev, Keel & create-oke

- Starters run the CLI through `bunx --bun oke` (`bun run dev` / `bun run test`) so Windows PowerShell does not need a local `oke` on PATH. Next-steps, README, and `AGENTS.md` show `bun run dev` / `bunx oke`. Scaffolded `.vscode/settings.json` prepends `node_modules/.bin` in integrated terminals.

#### Docs

- Try It and CLI Quick start use `bun run dev`. CLI troubleshooting covers Windows `oke` not found and Compose **OKE1020**.

### 🐛 Fixed

#### Runtime

- Manifest extract skips `node_modules` / `.git` as path segments after POSIX normalize, so Windows `node_modules\…` is not parsed. Scanning those trees threw during extract and Docker-first `oke dev` hard-failed **OKE1020** on `main.health`. Failed extract is now part of the OKE1020 cause (not only a suppressible `oke boot:` warn). `oke dev` writes the parent Manifest to a temp file and the app child boots with it (`OKE_MANIFEST_PATH`).

- `src/flows/generated.ts` replace falls back to write+unlink when `rename` cannot overwrite on Windows.

## v0.19.4 — 2026-09-12

### ✨ Added

#### Runtime

- Nameless Signal / Clock consumers fail **OKE1072** at `oke()` construction and extract (`A {kind} flow on "{trigger}" has no name.`). Same posture as **OKE1045** for HTTP: nameless `flow({ do })` is allowed until the file-tree stamp, then construction fails. Cause names the trigger; the fix points at `flow("…")` or a `src/flows/<unit>/` export.

### 💥 Breaking Changes

- Nameless `flow({ do })` no longer inherits a Signal or Clock trigger name. Flow names must be explicit `flow("…")` or tree-derived `unit.export`. `oke()` / extract throw **OKE1072**. Payload type inheritance from the Signal schema is unchanged.

### ♻️ Changed

#### Dev, Keel & create-oke

- Stop tracking `src/console/ui-next/dist/` in git. The Console SPA still ships in the npm/JSR tarball via `prepack` / `scripts/publish.ts`. Local create-oke `file:` staging builds the SPA when the folder is missing.

- create-oke Signal / Clock consumers pass an explicit Flow name: `on(noteCreated, flow("notes.onCreated", { do }))` and `on(digestClock, flow("notes.digest", { do }))`. HTTP tree files stay nameless `flow({ do })`.

#### Runtime

- Export gzip regression baselines refreshed in `budgets.json` after OKE1072 / explicit Signal-Clock names (kernel edge 16.98 kB and client 4.96 kB still under the 17 kB / 5 kB absolute caps).

- **OKE1070** now only fires for genuinely colliding names (two explicit `flow("…")` strings or two tree exports that stamp the same `unit.export`). Two nameless consumers no longer collide by inheriting the same trigger name — they fail **OKE1072** first.

#### Docs

- Signal **Inline or named export** and **Flow name** (explicit vs tree) are gone. Signal stays named-export only: export the const, bind with `on(handle, flow("explicit.name", { do }))`. Clock **Inline or named export** is back: write `clock.every(…)` inside `on()` for a single consumer, or export the const when several Flows share one schedule. Both Clock styles still require `flow("name", { do })` (**OKE1072**). Tree `unit.export` stays on Routing for HTTP. OKE1070 / OKE1072 troubleshooting remains.

### 🐛 Fixed

#### Runtime

- Extract no longer stamps a nameless Signal / Clock consumer from a bare `export const` outside `src/flows/<unit>/` (e.g. `inbound.ts` or `src/signals/inbound.ts`). That path now fails **OKE1072**, matching `oke()` and the documented tree-stamp contract.

## v0.19.3 — 2026-09-12

### ✨ Added

#### Runtime

- Duplicate `signal.once` consumers fail **OKE1071** at `oke()` construction and extract when two or more **different** Flow names bind the same once signal. `signal.broadcast` / `signal.live` stay unrestricted. Cause names both Flows and the signal; the fix points at `signal.broadcast` vs a single Flow (replicas of one Flow are still one `on()` in source).

### 💥 Breaking Changes

- Two different Flows bound to the same `signal.once` no longer race silently. `oke()` / extract throw **OKE1071**. Use `signal.broadcast` when every Flow should receive a copy, or bind a single Flow.

### ♻️ Changed

#### Docs

- CDC consumers docs add **Bare or enriched** (Style / When table, Bare vs Enriched worked examples, same-object honesty) to match Signal / Clock **Inline or named export**.

- Signal and Clock **Inline or named export** now include complete Inline vs Named worked examples — one-file `on(signal.once / clock.every, flow({ do }))` versus an exported handle — matching CDC **Bare or enriched**.

- Signal and Clock **Flow name** documents inherit vs explicit vs tree (`flow({ do })` takes the trigger name only with no unit folder; `flow("…")` and `src/flows/<unit>/` `unit.export` overwrite; collision is **OKE1070**).

- Signal **Smallest Example** presents declare / bind / emit as independent uses of one shared handle — not a numbered sequence. Same framing on Once, Broadcast, Live, and Consumers.

- Signal **Competing consumers** documents `signal.once` as a work queue: three differently-named Flows, one emit, exactly one race winner — and names `signal.broadcast` as the fix when every Flow should get a copy. Two different Flows on one `once` signal fail **OKE1071**.

### 🐛 Fixed

#### Runtime

- Extract **OKE1071** throws a plain `Error` (code plus `signal.broadcast` fix) instead of `OkeError`, so `okengine/compiler` stays off the kernel error and i18n graph. `oke()` still throws the full `OkeError`.

## v0.19.2 — 2026-09-12

### ✨ Added

#### Runtime

- Inline `on(signal.once(…), flow)` / `on(clock.every(…), flow)` now stamp Manifest `flow.trigger` the same as a named const binding.

- Nameless `flow({ do })` inherits a named Signal or Clock trigger name. Explicit `flow("…")` and file-tree `unit.export` still win.

- `fx.emit(handle, payload)` type-checks `payload` when `handle` is `SignalDecl<T>`. A string name stays `unknown` (runtime `schema` still applies).

- Duplicate Flow names fail **OKE1070** at construction (and extract), matching HTTP / live / MCP duplicate-registration posture.

- CDC `do` input (`CdcPayload`) always includes `table`, `action` (`"created"` / `"updated"` / `"deleted"` from before/after presence), and `id` (the table's declared primary-key value). Destructure only `{ before, after }` when that's all you need.

### 💥 Breaking Changes

- Two Flows that share a name no longer last-write-wins. `oke()` throws **OKE1070**; extract hard-fails the same collision.

### ♻️ Changed

#### Docs

- `fx.id()` docs and JSDoc now say **OKID**, aligned with `okengine/okid` and the fx / OKID reference cross-links.

- Signal, Clock, Consumers, Routing, Errors, and fx docs cover inline `on(signal.* / clock.*, flow)`, name inheritance, typed `fx.emit(handle)`, and **OKE1070**.

- Clock examples across docs, Keel, and create-oke advanced prefer named helpers (`clock.every` / `daily` / `weekly`); bare `clock(name, opts)` stays valid and is called out where the generic form is deliberate.

- CDC consumers docs show both `{ before, after }` and the enriched `{ table, action, id }` form; the audit-log example uses the enriched fields.

- Scaffold `AGENTS.md` and the oke-docs skeleton link to The Architecture.

- Dropped all permanent redirects from `site/next.config.ts`.

- Site `headerGeometry` home assertion matches the centered full-bleed hero (no left-pane `lg:w-[44%]` on the homepage).

## v0.19.1 — 2026-09-11

### ✨ Added

#### Docs

- Homepage hero gains a prism backdrop (`PrismBackdrop`, ogl WebGL, adapted from React Bits — see `site/NOTICE`): a desaturated light cone masked away from the copy, capped at 1.25× device pixel ratio, suspended offscreen, and frozen on one frame under `prefers-reduced-motion`. The cone is tinted by the element the walk is on — `--oke-el-*` resolved to sRGB and eased into the shader — so pinning the `.gate(…)` line turns the whole hero Gate emerald.

### ♻️ Changed

#### Docs

- Homepage hero law chip is just `on(Trigger) → Effects` (drops the muted "the one law" label beside the formula). The trailing arrow stays hidden until hover, then fades and slides in.

- Homepage hero CTA row matches heights: both actions are `h-10`, same horizontal padding and `text-sm`, stretched on `sm+` instead of centered at unequal intrinsic sizes.

- Homepage hero is one composed surface instead of a bare headline: chip, two-count headline with a spring settle, the positioning line, and two ways in (**Read the architecture** plus a copyable `bunx create-oke@latest`). Below it, one merged stage frame joins the live `create.ts` fx walk to the eight-element lattice — same beat, hover a line or a slab to pin it — over a shared foot rail carrying the lit effect on the left and the CI-measured `budgets.json` facts (cold start, client runtime, routing p99) plus `Bun ≥ 1.4.2 · MIT · v…` on the right. The walk is `hidden lg:flex`; below `lg` the lattice carries the stage.

- Homepage hero walk is real TypeScript: one create Flow through all eight elements (`fx.ask` / `fx.send` join the starter `create.ts` calls), tokenised at build time by the same Shiki themes as the handbook code blocks (`loadHeroCodeLines`, `site/lib/hero-code.ts`), keeps its indentation, and dims by opacity so the lit beat still reads in syntax colour. The stage frame grows with it — `max-w-7xl`, a 26 rem walk column, and 13 px code.

- Features eight-element cards match shipped API kinds: **Flow** `http · signal · cron · cdc`; **Clock** `cron · every · sleep · now` (TTL is Store KV, not Clock); **Vault** drops aspirational `fp`; **AI** `model · prompt · embed · agent` (not `noPii` / `RAG` kinds); **Channel** `whatsapp` label; **Gate** `policy · scope · rate · public`. Descriptions and zoo Clock concern `every` (was `TTL`) stay aligned.

- **Understand is now one page: The Architecture** (`/docs/understand/the-architecture`). Merges The Problem, The Model, The Vocabulary, and The Anatomy into a single narrative — drift → one rule → eight elements → five-piece Flow anatomy — with the teaching figures placed where they prove the claim (`SixSystemsDrift`, `FlowShape`, `Features`, `FlowTriggers`). Pain-first hook, "what OKE is — and isn't" positioning block, then/now timeline table, single next step (Try It). Old URLs redirect. Try It stays separate.

- Homepage restacks around a Farm-style 2×2 deck (Flow simulator, live `fx` walk, typed `createClient`, `oke dev` terminal) plus a Cloudflare-style measured claim strip from `budgets.json` and a tabbed starter stage (`create.ts` / `on-created.ts` / client). Hero stays the 44/56 split with the lattice; Install lives in the deck instead of a late ship band.

- Homepage drops the late “Measured, not marketed” budgets band (`BudgetsGraph`, proof strip, built-with / works-with). Four hard caps stay in the hero claim strip from `budgets.json`.

- Homepage hero removes the redundant "Backend" pill from the hero trust strip, keeping TypeScript and the release version badge.

- Homepage is hero-only: a single centered column — copy first, lattice below (law chip, two-count headline, then the eight slabs); the headline is set large (`5xl → 7xl`) with a wider measure so it reads as the page's single statement. The hero fills the viewport (`100svh` minus the nav) with the column vertically centered. claim strip, deck bands, code stage, collapse diagram, surfaces, elements, and ship CTA are removed from `/`. The hero copy is stripped to essentials: no kicker (the chip says it), no TypeScript/version pills, and the lattice caption is one centered line (element, what it replaces, docs link).

- Homepage hero tagline leads with the programming model: define behavior once, then OKE derives the runtime, client, Console, and infrastructure.

### 🐛 Fixed

#### Runtime

- Hybrid search LSH recall: query-time candidate retrieval ranked stored K=64 SimHash packs by Hamming distance (`bit_count` of XOR, `ORDER BY` then oversample `LIMIT`) instead of equality against the exact bucket plus Hamming-1 neighbors. Write/query hashing was already identical; the probe never reached true neighbors (typical distance ~5–16 bits), which is why G17 precision@10 collapsed to ~0. Hamming-1 equality is no longer the candidate set. See `src/bench/REPORT.md` G17 for before/after numbers.

#### Docs

- Homepage hero CTAs share a fixed `h-10`, matched 1px borders (transparent on the primary), and `leading-none` so **Read the architecture** and the copyable `bunx create-oke@latest` sit at the same height (was 40px vs 39px from `text-sm` / `text-xs` line boxes).

- Homepage code stage keys each Shiki pane before passing the array to the client tabs — clears the React “unique key” warning on `/`.

- Docs MCP tools and tests resolve `understand/the-architecture` after the Understand section consolidation (was stale `understand/the-problem`).

- **Try It** lives at `/docs/understand/try-it` (after The Anatomy). The old `/docs/ai/try-it` URL redirects. Sidebar no longer splices it out of AI Resources.

- Store Search G17 table and LSH retrieval copy match Hamming-rank query (was Hamming-1 / near-zero P@10).

- Element pages still showed invoke contracts on `flow()` after the v0.19.0 move. Examples now put `in` / `out` / `errors` on `http.*` / `call()`.

- Removed unpublished `site/content/_archive/get-started-legacy`.

## v0.19.0 — 2026-09-10

Pre-1.0 minor for deliberate breakages since v0.18.5: exposure-owned invoke

contracts, Signal declaration reshape, and the full OKE error-code renumber —

plus Realtime, built-in hybrid search (G17 measured), OAuth / MCP authorization,

external-call tracing, and the AI provider registry. Scan by area below.

### ✨ Added

#### Runtime

- **`call(name, options)`** — call-only Flow sugar with invoke contract on the same bag (`in` / `out` / `errors` / `breaking` + `do` + runtime). Exported from `okengine` and `okengine/http`.

- **`BoundaryContract`** — shared `{ in?, out?, errors?, breaking? }` on HTTP verb bags and `mcp.tool(name, bag)`.

- **OKE1605 `CHANNEL_SCHEMA`** — `fx.send` rejects template `data` that fails the template's Standard Schema (parity with Signal emit **OKE1250**).

#### Console — Flows & traces

- Trace waterfall marks egress effects with a dashed outline when `EffectEntry.external` is present; tooltips and event rows show host / provider / `third-party` vs `infrastructure`.

#### Runtime

- **`EffectEntry.external`** — optional `{ host, provider?, kind?: "third-party" | "infrastructure" }` on ledger entries when an effect left the process. Driver-reported (Channel failover, AI complete/embed, Store network facets); never hostname-guessed at the ledger layer. In-process drivers (PGlite, mock AI, console Channel) omit the field.

- **`fx.fetch(url, init?)`** — first-class outbound HTTP through `fx` (EffectKind `"fetch"`, Manifest `effects.fetches` host refs, dry-run stub, `UNDECLARED_FETCH` / OKE1008). Always stamps `external: { host, kind: "third-party" }`. Compose with `fx.step` / `fx.retry` like other irreversible effects.

- Browser JSON page: **Request** rail leveled to Console Call API dock IA — Routes fill the top as primary nav; Params · Body · Cookies · Headers · Path sit in a docked **Request** strip (Reset · **Send**). Body supports **Form** (`application/x-www-form-urlencoded`) or **JSON** (`application/json`) with live syntax highlight (same key/string/number colors as the response view) and pretty-print on blur; Send posts when the body is set (or the selected route method allows a body). Route leaf clicks only select the target (method / path); the response view updates on **Send**. One Send persists option edits and refetches; per-section Apply removed. Path chapter hides until a parameterized route is selected. Header shows the handled-request auth mark beside status, latency, and cache. Collapsed thin label is **Request**.

- **`createClient({ auth })` → `api.auth`** — session options on `createClient` attach `AuthClient` (merged over the `auth` unit Flows). `createAuthClient` + `bind` remains the escape hatch. Thin `createServerClient` + `tokenFromRequestCookies` for SSR.

- **UI authorize DX** — `api.auth.authorize({ all | any })`, React `Can` / `Cannot` / `useAuthorize`, `forbiddenScopes` helper. Gate on Flows remains real authz.

- **Binary responses** — per-call `{ response: "blob" | "arrayBuffer" }` on Flow invokes.

- **CSRF soft-require** — when `gate.auth.cookies.enabled`, prod refuses boot without the `csrf` plugin; dev/test warn. `csrf` exported from `okengine/plugins`.

- **Client auth level-up** (`okengine/client/auth`): `createAuthClient` with secure-by-default `mode: "bearer" | "cookie"`, memory persist (optional `sessionStorage` / loud `localStorage`), `signIn.*` / `signUp.*` / `completeChallenge` / `signOut`→`auth.revoke`, UI-only `hasScope` / `can`, tenancy header helpers, denial narrowers, and `memorySession.subscribe` / `persistSession`. Cookie mode refuses Storage dual-store and warns unless `csrfConfigured`.

- **`auth.me` enrichment** — returns `scopes`, `tenantId`, `apiKeyId`, optional `sessionFresh` for UI chrome (Gate on Flows remains real authz).

- **Typed JSON streams** — `flow({ stream: true })` stamps `$routes.stream`; `createClient` yields `AsyncIterable` for non-live SSE (`fx.json.stream`). Shared `sse.ts` pump.

- **Honest ambient client** — `GET /_oke/client.json` emits type strings + live/stream stamps; `oke client add` writes `oke-client.d.ts` **and** `oke-client.routes.ts`.

- Transport: `credentials`, AbortSignal / timeout via `AbortSignal`, raw `BodyInit` / `FormData` / `Blob` bodies. `useLiveQuery` accepts `listPath` to derive `/live`.

- JSR exports `./client/auth` and `./client-react`; optional `react` peerDependency.

#### Dev, Keel & create-oke

- create-oke Notes starters ship `src/vault.ts` with stack + app `vault.secret` / `vault.config` contracts (`NOTES_VAULT`) and pass them to `oke({ secrets })`, so Console Vault lists DATABASE_URL, SMTP, Redis, files, console secret, and cleartext configs — values still resolve from `.env.local`, process env, `oke vault set`, or `dev:` / `vault.fromDocker` fallbacks. `oke ai setup` prefers `src/vault.ts` (and inserts into `NOTES_VAULT`) when wiring provider API keys.

- **Advanced starter** — cookie `gate.auth`, `csrf` + `cors` + `passkey`, web `createClient({ auth: { mode: "cookie" } })` + `<Can>` chrome demo.

- `oke dev` asks once to run `oke db seed` when a seed module exists and `.oke/state.json` has not recorded that seed identity (TTY only). Successful `oke db seed` (including live **`s`**) marks the identity so the prompt does not repeat.

- `oke dev` prompts one-by-one for Vault boot gaps (same set as `VaultBootError`) and writes values into `.env.local` before the app starts.

#### Docs

- `fx` reference documents `fx.fetch`, `effects.fetches`, and driver-reported `EffectEntry.external` (third-party vs infrastructure) on traces.

- Errors reference lists **OKE1008** (`UNDECLARED_FETCH`) and **OKE1009** (`UNDECLARED_EMBED`); the undeclared-effect Callout covers 1001–1009.

- Vault overview notes create-oke Notes `src/vault.ts` contracts + `oke({ secrets: NOTES_VAULT })` so Console lists secrets and configs while values stay in `.env.local` / built-in vault / `oke vault set`.

- CLI / Vault / Store seeding docs cover first-boot seed confirm and interactive Vault gap fill; OpenRouter recipe notes create-oke / `oke ai setup` write the API token to `.env.local` + `vault.secret`, and `oke dev` asks when still missing.

- Client Auth / Calling / React / CSRF / ClientLoop updated for `createClient({ auth })`, `authorize` / `Can`, binary downloads, CSRF soft-require, and SSR cookie helper.

- Client Auth handbook rewritten for `createAuthClient` (secure defaults, methods, UI-only scopes). Calling / React / plugins notes updated for routes module, streams, and auth DX.

- New **04 Client** handbook group after Extend (Reference → **05**, AI Resources → **06**): Overview, Calling, Auth, Live, React at [`HTTP`](/docs/elements/flow/http) page depth; permanent redirect from `/docs/reference/client` → `/docs/client`; hub Cards + llms index + sidebar icons; Client folder unwrapped flat under `04 CLIENT` (same as Understand / Reference). Examples use a commerce domain (bookings, orders, shipments, inbox) instead of health / notes scaffolding; `createClient` base URLs use `vault.env` from `okengine/vault` (`PUBLIC_API_URL`) — subpath imports, not the root barrel.

- Elevated [`Reference`](/docs/reference) to [`HTTP`](/docs/elements/flow/http) page depth: created missing [`CLI`](/docs/reference/cli) and [`Security`](/docs/reference/security) (Host/Origin/`allowedHosts`, planes, MCP posture); upgraded fx / Errors / Environment Variables / Configuration skeleton (one rule, Steps, Troubleshooting, Next); aligned hub Cards + `meta.json` + llms index order.

- Elevated [`AI`](/docs/elements/ai) (overview, Models, Prompts, Agents, MCP) to [`HTTP`](/docs/elements/flow/http) page depth: Smallest Example + Progressive Patterns, Declaration / `fx` tables, teaching figures (`AiBlocks`, `AiGuardrails`, `AiPiiEgress`), per-environment drivers, and Troubleshooting with verbatim errors (OKE1005, `AiSchemaValidationError`, budgets, PII build gate). Corrects invented surfaces (`ai.prompt`, `template:`, agent `instructions` / top-level `maxCostPerRun` / `in`/`out`, MCP at `:6530/api/mcp`) to real APIs (`model.prompt`, `budget.maxCostPerRun`, `fx.ask` / `fx.run`, `mcp.tool` on `:6535/mcp`, `ai.mcpServer` allowlists).

- AI teaching figures raised to GatePipeline quality: `AiGuardrails` (scenario strip, status pips, footer holds “Checking guardrails…” until outcome), `AiPiiEgress` (full ask → check → verdict phase strip + packet gate + footer), `AiBlocks` (accurate openrouter bind, `out` validate, Flow tool names, maxSteps halt pips).

- Elevated [`Channel`](/docs/elements/channel) (overview, Email, SMS, WhatsApp, Push, Receipts) to [`HTTP`](/docs/elements/flow/http) page depth: Smallest Example + Progressive Patterns, Declaration / `fx.send` tables, `ChannelPhysics` teaching figure, per-environment drivers, and Troubleshooting with verbatim boot / OTP errors. Corrects invented surfaces (`channel.email("name", { subject, body })`, `fx.sendWebPush`, `fx.channel.getReceipt`, `oke_receipts`, Twilio / SES driver ids) to real APIs (`channel.email().template`, catalogs, `fx.send` / `sendOtp` / `verifyOtp` / `deliverOtp`). Splits WhatsApp into its own page; documents push drivers as boot-manual (`openFcmChannel` / `openWebPushChannel`).

- Elevated [`Vault`](/docs/elements/vault) (overview, Secrets, Config, Key Rotation) to [`HTTP`](/docs/elements/flow/http) page depth: Smallest Example + Progressive Patterns, Declaration / Options / `fx.vault` tables, resolution chain + Redacted teaching figures (`VaultResolution`, `VaultRedacted`), per-environment drivers, `vault.env` helpers, version vs master-key rotate, CLI reference, and Troubleshooting with verbatim `VaultBootError` / seal / backend errors. Corrects invented surfaces (`vault.rotation`, config `default:`) to real APIs (`fx.vault.rotate`, `dev:`).

- Vault teaching figures raised to GatePipeline quality: `VaultResolution` (contract strip, status pips, footer holds “Resolving…” until outcome), `VaultRedacted` (full phase strip + substring scrub), new `VaultRotate` contrast (version DEK vs master KEK) on Key Rotation.

- Elevated [`Gate`](/docs/elements/gate) (overview, Authentication, Authorization, RLS, Rate Limits, Tenancy) to [`HTTP`](/docs/elements/flow/http) page depth: Evaluation Order + denial envelopes, Sessions & Cookies / API Keys accordions, Module:Action permissions, Gate→SQL identity stamp (`oke.gate` / `oke.user` / `oke.has_scope` / `oke.tenant`), per-strategy rate tabs, Resolution Sources, Store RLS helpers, and Troubleshooting with verbatim `GateBootError` / i18n messages. Sidebar: Auth → Authorization → RLS → Rate Limits → Tenancy.

- `GatePipeline` teaching figure: footer stays on “Evaluating chain…” until the final beat (no premature “Every gate passed” / “Denied”); status pips mark pending · pass · deny · skipped across the left-to-right walk.

- Elevated [`Clock · Durable Sleep`](/docs/elements/clock/sleep) to HTTP-trigger docs depth: curl in Smallest Example, Sleep Reference + duration units, Park Physics, With Steps tabs (checkpoint / HTTP body + sleep / Signal consumer), Nested Calls, Wake Early, Non-durable & Tests (`createTimeTravel`, journal drivers), and expanded Troubleshooting (`JournalLeaseBusy`, orphan scan, unknown durations, nested sleeper).

- Elevated [`Clock · Schedules`](/docs/elements/clock/schedules) to HTTP-trigger docs depth: Helper Reference, Timezone Resolution, Binding & Input, per-helper tabs (`daily` / `hourly` / `weekly` / `monthly` / `cron` / `every`), Per-tenant

- Leader Lock detailed sections, Store Lifecycle & Console accordions (status, Console actions, DST, effective vs declared), and expanded Troubleshooting (`cron at`, paused rows).

- Clock schedules + intervals docs merged into one [`/docs/elements/clock/schedules`](/docs/elements/clock/schedules) page (helpers, `every`, per-tenant, catch-up, DST); overview teaching figure shows `clock.every` / `clock.daily` + zone from `oke({ clock })`.

- Elevated Clock element docs (`/docs/elements/clock`) to HTTP / Store depth: overview + Schedules / Intervals / Durable Sleep with Smallest Example, Progressive Patterns, options tables, teaching figures (`ClockSchedules`, `ClockCatchUp`, `ClockSleep`), real APIs (`timezone`, `clock.perTenant`, `fx.clock.sleep(label, duration)`), catch-up `"one"`, leader locks, and Troubleshooting. Removes invented surfaces (`tz`, one-arg sleep, jitter on `clock()`).

- Store element docs (`/docs/elements/store`) rewritten to the HTTP / Search page bar: overview + SQL / KV / Files with Smallest Example, Progressive Patterns, reference tables, teaching figures (`StoreFacets`, `StoreKvTtl`, `StoreFilesVariants`, `StoreSeeding`), real `fx.store(decl)` APIs, and Troubleshooting. Removes invented surfaces (`fx.store.kv`, `presignPut`, `transform`, Drizzle `.query` on the handle).

- Elevated [`Store · KV`](/docs/elements/store/kv) to HTTP-trigger docs depth: response envelope in Smallest Example, method tabs (`get` / `set` / `delete` / `list` / `ttlMs`), Namespaces, Durable Namespaces accordions, TTL Physics, Tenant Scoping (OKE1810), key-prefix / effects layers, and expanded Troubleshooting (`SCAN`, invented `incr` / `setNx`).

- Elevated [`Store · Files`](/docs/elements/store/files) to HTTP-trigger docs depth: response envelope in Smallest Example, blob-op tabs (`put` / `get` / `delete` / `list`), Object Keys, putImage / Image Pipeline accordions (variant naming, decode guards, encode fallback), drivers + S3 env notes, and expanded Troubleshooting (`Invalid object key`, missing image source, single-encode variant rule).

- Elevated [`Store · Search`](/docs/elements/store/search) to HTTP-trigger docs depth: Declaring Columns / Running Search tabs, Fusion + Backfill accordions, dedicated Rerank + Requirements, corrected `meta.engine` vs `fusedBy` honesty, and expanded Troubleshooting (`lsh` without fusion).

- OKID reference documents `prefix` / `OKID_MAX_PREFIX_LENGTH` and the prefix + sortable composition.

- Consolidated local-inference docs into OpenRouter + Models (BYO `openai-compatible`); removed the standalone Local AI / Ollama recipe pages. Added OpenRouter recipe recommending `openrouter/free`; rewrote Models docs (Verified providers / Limited compatibility).

- OpenRouter recipe documents router aliases (`openrouter/free`, `auto`, `pareto-code`, `fusion`, `bodybuilder`, `~…-latest`) with OKE `ai.model` examples and links to OpenRouter’s router guides.

- create-oke template `.env.example` / `core.ts` AI notes and Keel example `src/core/ai.ts` document registry cloud (`openrouter`) vs self-host `openai-compatible` + explicit `baseUrl`.

- Docs sidebar OpenRouter recipe uses the official OpenRouter glyph from [openrouter.ai/brand](https://openrouter.ai/brand) instead of Lucide Globe.

#### Dev, Keel & create-oke

- Notes starters (`standard` / `advanced`) declare `.searchable()` on `title` / `body`. When `oke ai setup` / create-oke picks an embed model, setup stamps bare `body.embed()` plus `oke({ store: { search: { embed: { model: embedModel, dims: 768 } } } })` (distinct from the index-facet `ai.embed("docs", …)` pipeline).

### 💥 Breaking Changes

#### Runtime

- **Invoke contracts leave `flow()`** — `in` / `out` / `errors` / `breaking` are authored on the exposure, not on `flow()`. Use `http.post({ in, out, errors })` (or `http.get("/path", { … })`), `call("unit.export", { in, out, do, … })` for call-only work, and `mcp.tool("name", { in, out, errors })` for MCP. Signal / Channel keep emit `schema` (Channel `schema` is enforced at `fx.send` as **OKE1605** / `CHANNEL_SCHEMA`). Manifest still stores flat `flows.*.in/out/errors/breaking` as a projection. `flow()` throws if those keys are passed.

#### Docs

- Flow / HTTP / Consumers / Signal / errors reference updated for exposure-owned invoke contracts vs emit `schema`, plus `call()` call-only sugar.

- **OKE error codes renumbered into domain ranges** (full break; no legacy carve-out). Every framework code is reassigned under: Kernel `1000–1099` · Store `1100–1199` · Signal `1200–1299` · Clock `1300–1399` · Gate `1400–1499` · Vault `1500–1599` · Channel `1600–1699` · AI `1700–1799` · MCP+Tenancy `1800–1899` · Compiler `1900–1999`. Definitions now carry a `domain` field; registry tests enforce uniqueness **and** range-correctness, discovering lazy `errors-*.ts` chunks automatically (no hand-maintained `LAZY_DEFS` list). Historical `## v…` changelog text still cites pre-renumber numbers. **OKE1020 changed meaning:** before this renumber it was `UNDECLARED_EMBED`; after this renumber it is `NO_EFFECTS_DECLARED`. Anyone matching or bookmarking “OKE1020” without a key name must re-check — embed is now **OKE1009**. | Key                       | Old  | New  | Domain      | | ------------------------- | ---- | ---- | ----------- | | `UNDECLARED_READ`         | 1001 | 1001 | kernel      | | `UNDECLARED_WRITE`        | 1002 | 1002 | kernel      | | `UNDECLARED_EMIT`         | 1003 | 1003 | kernel      | | `UNDECLARED_SEND`         | 1004 | 1004 | kernel      | | `UNDECLARED_ASK`          | 1005 | 1005 | kernel      | | `UNDECLARED_SECRET`       | 1006 | 1006 | kernel      | | `UNDECLARED_CALL`         | 1007 | 1007 | kernel      | | `UNDECLARED_FETCH`        | 1019 | 1008 | kernel      | | `UNDECLARED_EMBED`        | 1020 | 1009 | kernel      | | `NO_EFFECTS_DECLARED`     | 1008 | 1020 | kernel      | | `ADOPT_BARREL_STALE`      | 1009 | 1030 | kernel      | | `HTTP_PATH_UNRESOLVED`    | 1010 | 1040 | kernel      | | `HTTP_ROUTE_DUPLICATE`    | 1011 | 1041 | kernel      | | `HTTP_FLOW_UNNAMED`       | 1012 | 1045 | kernel      | | `LIVE_EXPOSURE_DUPLICATE` | 1013 | 1050 | kernel      | | `MCP_TOOL_DUPLICATE`      | 1018 | 1060 | kernel      | | `DOMAIN_SCHEMA_MISSING`   | 1101 | 1110 | store       | | `LIVE_RESUME_GAP`         | 1014 | 1210 | signal      | | `ORPHAN_EMIT`             | 1042 | 1240 | signal      | | `SIGNAL_SCHEMA`           | 1043 | 1250 | signal      | | `TENANT_REQUIRED`         | 1015 | 1810 | mcp_tenancy | | `TENANT_NOT_MEMBER`       | 1016 | 1820 | mcp_tenancy | | `TENANT_UNKNOWN_SCOPE`    | 1017 | 1830 | mcp_tenancy |

- `UNDECLARED_EMBED` no longer shares a number with `TENANT_REQUIRED` (tenant codes moved to **OKE1810–1830**; embed is **OKE1009**).

- **`flow()` invoke contracts** — `in` / `out` / `errors` / `breaking` must live on the exposure (`http.*(…)`, `call(…)`, `mcp.tool(…)`). Passing them to `flow()` throws at definition time. Call-only flows use `call(name, { in, out, do, … })` (exported from `okengine` and `okengine/http`).

### ♻️ Changed

#### Runtime

- Export gzip regression baselines refreshed in `budgets.json` after the dependency refresh (kernel edge 16.98 kB and client 4.96 kB still under the 17 kB / 5 kB absolute caps).

- Dependency refresh: React `^19.3.0`, zod `^4.6.1`, Vite `^8.3.0`, oxc `^0.149.0`, oxlint `^1.82.0`, oxfmt `^0.67.0`, `@clack/prompts` `^1.8.0` (`oke docker clean` narrows 1.8 cancel symbols), plus Console patches (TanStack Router/Virtual, happy-dom, Hugeicons). Drizzle stays on intentional `1.0.0-rc.5-*` (npm `latest` is still `0.x`).

- Kernel edge gzip budget is **17 kB** (was 16 kB). Measured ~17.0 kB after hybrid-search embed effects, signal surface growth, and `fx.fetch` / `EffectEntry.external` (fetch body lazy-loaded off the edge profile). AGENTS.md / `KERNEL_EDGE_BUDGET_BYTES` aligned.

- Store-only `oke()` lazy-loads the browser JSON page (`json-code-block`) on `app.fetch` so graphs that never serve HTTP do not pin the traces-language HTML/CSS chunk (~65 kB → ~51 kB gzip).

- `fx.fetch` host parsing + dry-run stub live in a lazy `fx-fetch` chunk so cold edge / Store-only graphs that never call outbound HTTP avoid that code.

- `createClient({ auth })` `api.auth` Proxy implements `has` so `"me" in api.auth` (and other unit Flow checks) match get semantics.

- Dependency refresh (2026-09-05): Console / tooling minors + patches (`zod` `^4.5.4`, TanStack Query/Router/Form, Vite `^8.2.2`, Playwright `^1.63.0`, oxlint / oxfmt / `oxc-parser` `^0.148.0`, PGLite `^0.5.8`, …); majors `@tanstack/react-table` `^9.2.4`, `shadcn` `^4.21.0`, `shiki` `^4.4.3`. Drizzle stays on intentional `1.0.0-rc.5-*` (npm `latest` is still `0.x`).

- Client gzip budget raised to **5 kB** (was 4 kB) so shared SSE + stream open fits the measured `okengine/client` graph; AGENTS.md budget table aligned.

- create-oke Vite proxy includes `/auth` alongside `/notes`, `/health`, `/_oke`.

- Minimum Bun is ≥ 1.4.2 (`engines.bun`, CI `bun-version`, feature-gate error messages). `@types/bun` / `bun-types` track `"latest"`. Generated Dockerfiles stay on the `oven/bun:1.4` line.

#### Dev, Keel & create-oke

- create-oke and Notes templates track the same React 19.3, zod, Vite, and oxc bumps; Keel PGLite aligned to `^0.5.8` / pgvector `^0.0.9`.

- `bun run budgets:core` runs the kernel-edge + client gzip gates. Required after `src/kernel/` / `src/client/` / `src/compiler/` / `src/validation/` / `src/release/limits.ts` / `measure.ts` changes (agent workflow + oke-ship). Optional git hook: `git config core.hooksPath .githooks`.

- Keel flows, `bindCrud`, auth HTTP bindings, Console seed invoke host, and remaining kernel / test call sites finish the invoke-contract migration off `flow({ in, out, … })`.

- `oke dev` streams Docker Compose pull / create / start progress into the boot status lines (and live keyboard **up**) instead of a silent `docker compose up…` wait.

- Compose default image pins: RustFS `1.0.0-rc.5`, Mailpit `v1.31.1`, PgDog `v0.1.57` (Meilisearch `v1.53`, Traefik `v3.7`, nginx `1.31-alpine`, floating postgres/redis/caddy unchanged). No `images.ai` / local inference pins — AI is OpenRouter (recommended) or BYO `openai-compatible` URL. create-oke templates + Keel stack aligned for the non-AI pins.

- **Keel default AI** is OpenRouter · `openrouter/free` (no Compose AI service). `OPENROUTER_API_KEY` is a Vault contract without a `dev:` stub so first `oke dev` asks for it. Self-host = set `OKE_AI_URL` / `baseUrl` on `openai-compatible` yourself.

- create-oke / Notes starter dependency refresh (PGLite, Vite, oxc-parser, zod; Drizzle RC pins unchanged).

- `oke ai setup` (and create-oke AI wizard) emit real registry `provider` names on `ai.model` (e.g. `openrouter`, `groq`) with matching API key env vars; known cloud base URLs are omitted so the registry resolves them. OpenRouter defaults to `openrouter/free`. Menu lists verified registry providers plus Gemini (limited compatibility). Anthropic stays native `driverId: "anthropic"`.

- create-oke **AI setup → Recommended** (and `--yes --ai`) defaults to OpenRouter · `openrouter/free`. Customize / `oke ai setup` offer cloud registry providers plus `lmstudio` / `custom` (BYO OpenAI-compatible URL) — not local Docker engines.

- create-oke **Recommended** prompts for `OPENROUTER_API_KEY` (same as Customize cloud path) and writes it into `.env.local`.

- **AI Provider** select (create-oke Customize + `oke ai setup`) leads with OpenRouter and lists the full cloud registry set; OpenRouter **Select model** includes router aliases (`free` / `auto` / `pareto-code` / `fusion`).

- create-oke `engines.bun` and Notes starter GitHub Actions use Bun 1.4.2.

- PgDog Compose healthcheck polls every **2s** (was 5s) with a **2s** start period; compose health wait timeout raised to **90s** so cold Postgres + PgDog can finish before the board moves on.

#### Docs

- Site deps: fumadocs `16.15.8`, lucide `^1.44.0`, React 19.3, zod `^4.6.1`.

- Scrubbed leftover "ten exports" / "ten words" marketing: landing vocabulary band, docs hub Vocabulary card, and site `EXPORTS` now use "core programming vocabulary" and include `call` (11 names, matching `AGENTS.md`).

- CLI docs note that `oke dev` streams Compose pull / create / start progress into boot status lines.

- Models docs drop the incorrect `meta` registry row; Cloudflare and Meta are listed as omitted hosts that always need an explicit `baseUrl`.

- PgDog recipe healthcheck table matches the 2s interval / 20 retries.

- CLI docs drop the old `oke dev` TTY live-keys bar (`?`/`r`/`s`/`q`/`u`/`x`); quit with Ctrl+C; seed via `oke db seed` or the first-boot prompt.

- Aligned machine-facing agent surfaces with the live handbook IA: `llms-txt` When-to-use prose, docs MCP `oke.docs.get` examples, `AGENTS.md` authority table (Understand · Elements · Client · Reference · AI Resources), skills path maps (`oke-ship` / `oke-docs` / `oke-docs-update`), and create-oke scaffold ports (adds Docs MCP **6536**). Removed empty `docs/concepts/`.

- Site deps: fumadocs `16.15.7`, `framer-motion` `^13.2.0`, `cnfast` `^0.2.0`, lucide / zod / PostCSS aligned; recipe + configuration docs mirror the new RustFS / Mailpit / PgDog pins.

- HTTP docs default to pathless triggers and nameless Flows — `on(http.get(), flow({ do }))` — with an explicit [When to omit · when to pass](/docs/elements/flow/routing#when-to-omit--when-to-pass) table on Routing. Teaching examples across Flow, Gate, Vault, Store, Clock, Signal, and recipes omit path/name unless control requires them (barrels, `http.resource`, custom live SSE, auth plugin URLs).

- Docs sidebar **Reference** is flat under `05 REFERENCE` (no accordion group); the `/docs/reference` Overview landing is hidden from the nav list.

- Docs sidebar drops stage **Build**; stages renumber to Extend `03` · Operate `04` · Reference `05` · **AI Resources** `06` (MCP · Skills · llms.txt). **Try It** sits under Understand after The Anatomy; `/docs/ai` Overview is hidden from the nav list.

- Docs sidebar stage **Extend** is `03` again (Plugins / Providers / Recipes after Elements); Operate → `04`. Handbook index card group updated to match.

- Tightened [`Clock · Schedules`](/docs/elements/clock/schedules) in place (still three Clock pages): collapsed duplicate Helpers deep-dive into Helper Reference, compressed per-tenant / leader lock / Store lifecycle & Console. Nav stays Overview · Schedules · Durable Sleep.

- Site Next / PostCSS configs are `next.config.ts` (typed `NextConfig`) and `postcss.config.ts` instead of `*.mjs`.

- Moved docs sidebar stage **Extend** from `05` to `03` (Plugins / Providers / Recipes after Elements); Build → `04`, Operate → `05`. Handbook index card groups updated to match.

- Renamed Signal delivery docs to match helpers: `/docs/elements/signal/once` (was queues), `/broadcast` (was pubsub), `/live` (was streams). Cards, sidebar, and cross-links updated.

- Installation / Try it / README badges and agent contracts document Bun ≥ 1.4.2.

- Rewrote Signal element docs (`/docs/elements/signal` + Once / Broadcast / Live) for `signal.once` / `broadcast` / `live` (HTTP-shaped helpers): progressive patterns, options tables, lease / retention / drivers honesty, real error codes (OKE1240 · OKE1250 · OKE1210), teaching figures, and Troubleshooting. Corrected `deadLetter` (boolean, not a queue name) and live exposure via `http.live` / `api.live` (not invented EventSource paths).

- Elevated [`signal.once`](/docs/elements/signal/once) to HTTP-trigger docs depth: delivery / binding / emit reference, lease reclaim and ordering accordions, failure-reason + DLQ shapes, idempotency with `durable` + `fx.step`, and expanded Troubleshooting (OKE1001 · OKE1240 · OKE1250). Dropped inaccurate “exponential” retry wording — attempts requeue with no delay backoff.

- Signal · Live (`elements/signal/live`): rewritten to the HTTP page’s depth — three-step Smallest Example, Progressive Patterns, Options Reference, Exposure (filtered / custom match / live queries / uniqueness), Emit, Retention, Resume, Client subscription (`api.live` / `useLive`), What live is not, expanded Troubleshooting.

- Signal · Broadcast (`elements/signal/broadcast`): rewritten to the HTTP page’s depth — three-step Smallest Example, Progressive Patterns, delivery reference + options, Fan-out Physics, Emit and Effects, Subscribers tabs, Choosing Physics accordions, and expanded Troubleshooting (OKE1240 · OKE1250 · retention type error · browser vs Flow consumers).

- Signal · Broadcast teaching figure (`SignalBroadcastFanout`): ambient demo that one emit fans an independent copy to every active subscriber while an offline listener misses the event (no retained tape) — peers `SignalOnceLease` / `SignalLiveReplay` on once / live.

### 🔥 Removed

#### Runtime

- Browser JSON page removes interactive Authentication (header key + rail Inherit/Custom). Handled-request auth mark in the strip remains. Re-add later with a clean plan.

- Dropped incorrect `meta` OpenAI-compat registry entry (`https://api.meta.ai/v1`). Meta’s Llama OpenAI-compat API was retired; the hardcoded host was never the historical Llama URL and is not a verified chat-completions endpoint. `provider: "meta"` without `baseUrl` now fails loud (same posture as Cloudflare). Removed from `oke ai setup` / create-oke menus.

### 🐛 Fixed

#### Runtime

- Kernel edge gzip is back under the 17 kB cap after exposure-contract landing (+395 B gzip over the cap). OKE1605 `CHANNEL_SCHEMA` lives in a lazy `errors-channel` chunk (channel runtime is not on the edge profile); `on()` inlines exposure-contract stamping so `boundary-contract` helpers stay off that graph. `lookupOkeError(1605)` still resolves.

- **`$routes` / typed client after exposure-owned contracts** — `http.*` / `mcp.tool` preserve the authored `{ in, out, errors }` bag type, and `on()` projects it onto `FlowDef` (parity with `call()`). Fixes `createClient<typeof app>` treating inputs as `ClientCallOpts` and dropping declared error narrowing (e.g. Notes `NotFound`).

- Shared Postgres holder `.close()` test awaits the async no-op (Bun.SQL returns a Promise; pool identity is unchanged).

- SqlStoreHandle surface allowlist includes `search` (hybrid SQL search).

- `oke doctor` PII ask fixture uses a third-party provider (`anthropic`) — `openai-compatible` is infrastructure and correctly skips the gate.

- **OKE1810** no longer collides with `UNDECLARED_EMBED` (embed is **OKE1009**).

#### Dev, Keel & create-oke

- Keel `attachments` upload/delete: restore missing `).gate(member),` closers from the exposure-contract migration (syntax broke `examples/keel` typecheck).

- create-oke Notes `PUBLIC_API_URL` no longer uses `dev: ""` — vault boot treats empty strings as gaps, so `oke dev` failed with `VaultBootError: PUBLIC_API_URL`. Templates now default to `http://127.0.0.1:6530` (same as `OKE_APP_URL`); Vite web still leaves `VITE_API_URL` unset for the same-origin proxy.

- create-oke Customize / Reuse no longer drops the AI API token after the wizard: the live `aiApply` (with `apiKey`) is applied so `.env.local` gets `OPENROUTER_API_KEY=…` (etc.) instead of leaving `# OPENROUTER_API_KEY=` empty. Reuse re-prompts for the token (prefs never store secrets). `oke ai setup` / create-oke also declare `vault.secret(<apiKeyEnv>)` (no `dev:` stub) so a missing key is a Vault gap on `oke dev` and shows in Console Vault.

- `oke dev` quit / compose teardown no longer floods the TTY with `ERR_POSTGRES_CONNECTION_*` / `SharedPostgresPausedError`: shared Bun.SQL pools pause with a fail-soft facade, and fleet scheduler heartbeats swallow pause / disconnect errors.

- First-boot `oke db seed` inside `oke dev` no longer leaves a dead shared Bun.SQL pool: journal / clock / instances `close()` on the shared client is a no-op (process-owned via `closeSharedPostgresClients`). Previously seed stop closed the cached pool, then Console reuse hit `PostgresError: Connection closed` and exited the session.

#### Docs

- Store Files / KV wording no longer trips the competitor-mention gate on an accidental peer-name substring in ordinary English (“honour” / past tense).

### 💥 Breaking Changes

#### Runtime

- Signal declarations use HTTP-shaped helpers: `signal.once(name, opts?)`, `signal.broadcast(name, opts?)`, and `signal.live(name, opts?)`. The callable `signal(name, { delivery })` form is removed. Manifest `delivery` is unchanged (compiler extracts it from the helper name).

- Removed the native `ollama` AI driver (`driverId: "ollama"` / `drivers.ai: "ollama"`). Package paths `okengine/drivers/ai-ollama` and `okengine/drivers/ollama` are gone. Use `openai-compatible` + your own `baseUrl` / `OKE_AI_URL` for any OpenAI-shaped `/v1` (including former Ollama / llama.cpp / vLLM / SGLang servers).

- Dropped first-class Compose / wizard support for llama.cpp, Ollama, vLLM, and SGLang: no Docker recipes, no `images.ai` default pin, no `oke ai setup --provider llama-cpp|ollama|vllm|sglang`, no `--pull` / Ollama detect helpers. Migration: remove `images.ai`, set `drivers.ai` / `ai.model` to `openai-compatible` with `OKE_AI_URL` (or a cloud registry provider such as OpenRouter). Leftover `images.ai` pins fail `recipeFor()` until removed.

#### Kernel

- Require named clock declarations instead of bare `every(interval)` trigger constructors: `on(clock.cron(...), flow)`, `on(clock.every(...), flow)`, etc.

- Require store table receivers (`db.table(handle).changed(column)` / `table.changed(...)`) for CDC triggers instead of bare `table(name, store?)`.

#### Runtime

- When `twoFactor()` is plugged and the account has 2FA enabled, email/password and username sign-in withhold session tokens and return `{ twoFactorRequired, challengeId, method, userId }` instead.

- `auth.twoFactorVerify` requires `{ challengeId, code }` (bound to the server-issued login challenge). Unbound `{ userId, code }` is no longer accepted.

- `passkey` register / authenticate options now return a ceremony `sessionId`; register and authenticate bodies must echo that `sessionId` with the challenge (challenge is bound to the ceremony session and expires in ≤5 minutes).

### 🔥 Removed

#### Dev, Keel & create-oke

- `oke dev` TTY live controls bar (`?` help · `r` refresh · `s` seed · `q` quit · `u` up · `x` stop) and the Ink `DevLive` / `dev-controls` helpers. Session stays up after Ready; quit with **Ctrl+C**. Seed with `oke db seed` or the first-boot prompt. Compose lifecycle stays on boot / session quit.

- Docker recipes + pins for llama.cpp, Ollama, vLLM, and SGLang (`src/docker/recipes/{llama-cpp,ollama,vllm,sglang}.ts`, ollama-pull/url, create-oke `LLAMA_CPP_IMAGE` / `OLLAMA_IMAGE` / `VLLM_IMAGE` / `SGLANG_IMAGE`). Compose no longer manages inference.

- Local AI wizard paths, RAM catalogs, and `oke ai setup --pull` / `--no-pull`.

#### Docs

- Deleted [Local AI](/docs/recipes/local-ai); `/docs/recipes/local-ai` redirects to [OpenRouter](/docs/recipes/openrouter). AI docs point at OpenRouter / registry cloud / BYO `OKE_AI_URL`.

- Dropped [`Manifest`](/docs/reference/manifest) and [`Architecture`](/docs/reference/architecture) reference pages — mental model lives under **01 Understand**; permanent redirects → [`The Model`](/docs/understand/the-model). Ports / planes stay on [`Security`](/docs/reference/security); hub Cards, `meta.json`, sidebar icons, and llms index updated.

- Removed **04 Operate** and the entire Deployment section (`/docs/deployment` — Docker, Docker Swarm, Kubernetes, reverse proxy). Sidebar sections renumbered: Reference is now **04**, AI Resources **05**. Cross-links retargeted to Recipes / Configuration / Security.

- Dropped all permanent redirects from `site/next.config.ts` (legacy Signal delivery paths, Flow jobs → consumers, and old Local AI recipe URLs).

#### Kernel

- Removed bare top-level `every(interval)` export from kernel triggers and public entrypoints (`okengine`, `okengine/full`).

- Removed bare top-level `table(name, store?)` export from kernel triggers and public entrypoints (`okengine`, `okengine/full`).

### ✨ Added

#### Runtime

- Built-in hybrid SQL search on `store.schema.table()` columns: `field.text().searchable({ weight? })` (BM25F) and separate `.embed({ model?, dims })` (async LSH). `fx.store(db).search(table, { query, fuse?, rerank?, …listFilters })` reuses `parseListQuery`. Default fusion is RRF with **k = 60** (Cormack et al., SIGIR 2009); weighted fusion is opt-in. `fx.embed(model, text)` is a distinct effect kind from `fx.ask` (`effects.embeds`). CDC embed flows auto-register at boot when the Manifest has any `.embed()` column (writer flows stay embed-free). `oke db search-backfill <table>` opens a live SQL connection and rebuilds corpus stats / embeddings (never auto on push). PostgreSQL 15+, zero required extensions. **G17** (live Postgres): latency + precision@10 vs exact cosine published in `src/bench/REPORT.md` — LSH recall vs exact is near-zero on that corpus; treat LSH as a candidate hint, not HNSW.

- Project default for field `.embed()` via `oke({ store: { search: { embed: { model, dims } } } })`. Bare `.embed()` inherits; per-field `{ model?, dims? }` overrides. Extract stamps concrete `{ model, dims }` on Manifest columns and fails loud (`SearchConfigError`) when either is still missing.

- `twoFactor` step-up / change-method / confirm-change / request-email-otp surfaces; shared pending-challenge + step-up stores on Gate auth context; email OTP as a configurable second-factor method.

#### Kernel

- Added `ClockTrigger` interface and `isClockDecl` type guard for binding named clock declarations directly to `on(clockDecl, flow)`.

#### Docs

- Rewrote Store Search docs for built-in hybrid search, with a prominent side-by-side of list-grammar `?search=`/`?q=` (LIKE) vs hybrid `query` (BM25/LSH).

- Published G17 measured latency / LSH precision@10 / corpus-size guidance on Store Search (honest near-zero LSH recall vs exact cosine; Seq Scan EXPLAIN at 100k).

- Documented project-wide `oke({ store: { search: { embed } } })` default for bare `.embed()` plus per-field overrides.

- Added "The Anatomy" documentation page under Understand (`/docs/understand/the-anatomy`) detailing the five components of `on(trigger, flow)` (`on`, `trigger`, `flow`, `do`, `fx`) and mapping the five element triggers with timeline resolution.

- Added modular subpages for all eight core elements (`Flow`, `Signal`, `Store`, `Clock`, `Gate`, `Vault`, `Channel`, `AI`) covering architecture, execution patterns, and driver bindings.

- Added new "Understand" section (`/docs/understand`) covering the problem, the model, the vocabulary, and architectural drift.

- Added interactive `SixSystemsDrift` component illustrating architectural drift across fragmented backend stacks.

- Added `ScrollToTop` helper component to the documentation layout.

### ♻️ Changed

#### Compiler

- Updated compiler trigger extraction (`extract.ts`, `effects-infer.ts`) to infer triggers from named clock bindings and store table receivers (`table.changed()`).

#### Docs

- Merged "See It Work" into "The Anatomy" (`/docs/understand/the-anatomy`) by incorporating the timeline resolution section into the anatomy guide and retiring `/docs/understand/see-it-work`.

- Dissolved legacy Concepts section into modular Element guides (`elements/flow`, `elements/flow/routing`) and Reference (`reference/fx`, `reference/manifest`, `reference/architecture`).

- Upgraded `FlowShape` figure (`flow-shape.tsx`) with interactive stage physics, active element triggers (including MCP), and nameless flow auto-derivation annotations.

- Enhanced Flow documentation with interactive trigger visualizer (`flow-triggers.tsx`), step-by-step quickstart, comprehensive execution options (`compensate`, `retry`, `cache`, `plane`), schema validation guides (Standard Schema, Drizzle ORM v1 subpaths, `shapes.ts`), and dedicated Shiki focus/highlight styles.

- Updated agent CI guidelines across `.cursorrules`, `AGENTS.md`, and skill definitions to prevent automatic CI execution after implementations.

- Flow Overview (`elements/flow`): rewritten to the HTTP page’s depth — Smallest Example Steps, Progressive Patterns, trigger table + capability cards, verified `flow()` options (`cache` auto for read-only, `NotFound` → 400 not 404), `fx` door, call-only / `internal`, plane / retry / OKE1020 troubleshooting.

#### Dev, Keel & create-oke

- Migrated all examples (`keel`), starters (`create-oke` standard and advanced templates), and test fixtures to the consolidated clock and table trigger syntax.

### 🐛 Fixed

#### Console — Observability

- Rebuilt Manifest signal declarations through `signal.once` / `broadcast` / `live` in Console bus bind and `projectSignalsList` so `bun run typecheck` passes after the callable `signal(name, { delivery })` removal.

#### Runtime

- SQL insert/update/upsert coerces epoch-ms numbers to `Date` for `timestamp` / `date` columns so `fx.clock.now()` and seed literals like `createdAt: 1` bind on Postgres (was `column "created_at" is of type timestamp … but expression is of type integer`).

- Hybrid search: encode LSH buckets as signed int64 for Postgres `bigint`, insert `real[]` via `{…}` text, and store hyperplane seeds without NUL bytes so Bun.SQL + Postgres 15+ accept plane/embedding writes.

- `oke db search-backfill <table> [--batch]` opens live SQL (compose env / `DATABASE_URL`), extracts the Manifest, and runs `runSearchBackfill` (supports `AbortSignal` for interrupt + safe re-run).

- Boot auto-registers `_oke_search_embed_*` CDC bindings when the Manifest has `.embed()` columns (`bindSearchEmbedFlows` via `adoptBinding`).

- Built-in Gate auth / plugin Flows no longer need hand-declared `effects: {}`. When a Manifest is present (`oke dev` / `oke build` extract) but a Flow is absent from it (framework code outside the app tree), `mintCapabilities` stamps an empty least-privilege token automatically — fixing **OKE1020** on `auth.refresh` for advanced Docker scaffolds without reverting to open tokens.

- Auth / plugin opaque ids (users, accounts, API keys, passkey ceremonies, verifications, tenants, invites, …) use OKID instead of `crypto.randomUUID`. Session refresh secrets stay `crypto.getRandomValues` hex (not OKID — ids are not credentials).

- Client typecheck under root `lib: ["ESNext"]` (no DOM): local `ClientCredentials` / `ClientBodyInit`, duck-typed passkey / `location`, narrowed `auth.getToken` on live / stream, explicit `createClient` proxy return types, mutable `buildClientDescriptor` routes bag, void-input binary call opts in transport tests, and legacy `useSession` token gate (cookie path is AuthClient-only).

- `oxc-parser` is a hard `okengine` dependency again (no longer an optional peer). Published scaffolds with Docker Compose were dying on **OKE1020** for `main.health` because Manifest extract could not resolve `oxc-parser` and boot treated that as “no Manifest.” Strict boots now also warn `Manifest extract failed — …` before throwing OKE1020.

- Threaded EffectKind `"embed"` through dry-run, signal replay stubs, Console run/effect schemas, Manifest diff `EFFECT_KEYS`, and Fx test doubles so `bun run typecheck` is clean after hybrid search. Restored concrete `dims` on Manifest `DeclaredColumn.embed` (project defaults still resolve at extract); runtime schema search fails loud when `.embed()` dims are unresolved. Fixed `search-bind` to import `OkeApp`, `SearchConfigError` `override` on `name`, and related compiler/bench typecheck noise.

#### Dev, Keel & create-oke

- Notes starter seeds (`standard` / `advanced`) use readable calendar `Date`s (e.g. `2026-01-15T10:00:00.000Z`) for `createdAt` so `oke db seed` matches `field.timestamp()` on Postgres.

- Notes starters (`standard` / `advanced`) list `oxc-parser` so create-oke installs it even against older published `okengine` builds.

- `oke ai setup` / create-oke no longer treats template comment examples of `ai.model("smart", …)` as an already-configured core — that path skipped the `ai` import and `smart` binding, leaving only `local` + `summarizeNote` and crashing boot with `ReferenceError: ai is not defined`. A second setup pass now also repairs those incomplete stubs (strip + rewrite `smart` / `local` / `summarizeNote` with the `ai` import) instead of leaving them.

- Cloud AI (OpenRouter / OpenAI / …) no longer pins `images.ai` to llama.cpp. Compose only starts a local AI container for self-hosted providers (`llama-cpp` · `ollama` · `vllm` · `sglang`). Switching to cloud clears a leftover `images.ai` pin.

- `oke boot` no longer requires `OKE_AI_URL` for `openai-compatible` just because Docker is up for Postgres — cloud models resolve `baseUrl` from the provider registry. `oke dev` also uses `compose up --remove-orphans` and ignores orphaned exited AI containers when painting element status.

- `oke dev` arms BYO `OKE_AI_URL` + `OKE_AI_MODEL` readiness watch again after Compose AI recipes were removed (unassigned `pendingAiModelWatch` left typecheck as `never`). Vault-gap tests widen `process.env` after `delete` so assertions typecheck under strict narrowing.

#### Docs

- Fixed trigger item row alignment, badge visibility, height symmetry, ambient auto-cycling (`useTick`), interactive tabbed contract inspector (`in`, `out`, `errors`, `do`), and unified domain pipeline examples (`orders.*`) in `FlowTriggers` (`flow-triggers.tsx`).

- Fixed broken consumers doc example calling `users.changed()` without a store table receiver.

- OKE1020 troubleshooting notes missing `oxc-parser` when Manifest extract fails.

### ♻️ Changed

#### Console — Store

- Store browse / query / row detail format `*_at` / `*At` epoch-ms cells and `Date` values as ISO-8601 instead of raw integers.

#### Dev, Keel & create-oke

- Notes starters (`standard` / `advanced`) expose `createdAt` / `archivedAt` (and advanced digest `at`) as ISO-8601 strings on the HTTP wire (`z.iso.datetime()`), write `new Date(fx.clock.now())` into `field.timestamp()`, and seed with readable calendar instants.

- Keel daily-digest mail schema + payload use ISO `at`; seed `T0` is a named calendar anchor (`2025-07-31T00:00:00.000Z`).

#### Docs

- Store seed/upsert samples and Clock digest / cleanup receipts use readable ISO instants on the wire; `fx.clock.now()` remains epoch-ms for math.

- Store · SQL (`elements/store/sql`): rewritten to the HTTP page’s depth — Smallest Example with response envelope, Progressive Patterns, field / store declare reference, session-handle method tabs (select → upsert), `store.resource` Define/Mount + option accordions (list grammar, live, subset), schema extras / RLS / relations, seeding, CDC, drivers, and expanded Troubleshooting (including no RQB on `fx.store`).

- Store Search (`elements/store/search`): rewritten to the HTTP page’s depth — Smallest Example, Progressive Patterns, field/query reference, two-surface distinction (`?search=` LIKE vs hybrid `query` vs `store.index`), embedding pipeline, fusion, backfill, external indexes, troubleshooting.

- Two-factor docs: method-locked login challenges, step-up enrollment / method change, `Forbidden` mid-challenge; OTP docs clarify primary `/auth/otp` vs second-factor email OTP.

### 🔒 Security

#### Runtime

- Session access JWT verify now allowlists `alg: HS256` before HMAC and rejects spoofed algorithms (`none`, `RS256`, …) and non-JWS compact shapes (JWE’s 5 parts), closing algorithm-confusion gaps even when the crypto path never switched algorithms.

- 2FA method lock during active login challenges: the configured method (`totp` | `email_otp`) is recorded on the pending challenge; TOTP enrollment / method change / disable while an unresolved challenge exists returns `Forbidden` (`active_2fa_challenge`). Closes the July–August 2026 method-switching bypass pattern.

- TOTP re-enrollment and method change require a successful step-up verification of the current factor; the old method is invalidated only after the new one is confirmed active.

- Magic-link and email OTP verification reclaim an existing **unverified** email account (CVE-2026-67327 pre-account hijack): revoke all sessions for that principal, clear planted password hashes, mark `emailVerified`, then issue the passwordless session. Already-verified owners re-auth without a credential purge. Shared helper: `completeVerifiedEmailSignIn`.

- Hardened `passkey` WebAuthn verification against Pass-the-Passkey / Golden Pass-ta-key class failures: require authenticatorData UV (reject UV=false), bind challenges to a ceremony `sessionId` (≤5m TTL), enforce `clientDataJSON.type`, and treat non-increasing `signCount` (when stored ≠ 0) as a cloned authenticator — log a warning, delete that credential, return `reregister_required`.
