v0.8.0
✨ Added
- Postgres Clock CronStore (
drivers.clock: "postgres") — multi-host leader election via the sameFOR UPDATE SKIP LOCKED+ lazy lease reclaim pattern as Signaloncedelivery. Tableoke_cronsholds schedule rows withlocked_by/lease_expires_at. Chaos-proven across OS processes against a real Postgres (OKE_TEST_POSTGRES_URL).fileremains for local / single-host shared volumes. defineSeed({ essential, dev, prod })+oke db seed— explicit seed runs (never at boot).essentialevery env;devonlocal/docker;prodonprodonly. Docker/prod print the DB target and require typing the env name (--forceskips). create-oke standard + advanced ship realsrc/seed/(welcome note + sample notes).fx.store(db).upsert(table, matchOn, values, options?)— insert when no match; existing rows untouched unless{ onExisting: "update" }.- Store docs **Seeding** section (categories, upsert, confirm, migration boundary) and CLI reference for
oke db seed. <StoreSeeding />teaching figure — env→block lighting + upsert outcomes under Store docs Seeding.- Phone-number OTP delivery via Taqnyat Verify —
fx.sendOtp/fx.verifyOtpon Channel; whendrivers.channel.smsistaqnyat,phoneNumber()uses Taqnyat's provider-managed Verify API (no self-generated code). Non-Taqnyat SMS drivers fail loudly; no SMS driver keeps the local hashed path +exposeDevOtp. taqnyat-mailChannel email driver (additive) — magic-link and everychannel.emailtemplate can deliver through Taqnyat Mail (TAQNYAT_MAIL_TOKEN+TAQNYAT_CAMPAIGN); SMTP/Mailpit stays the docker default.- Opt-in Taqnyat live test suite — real
sendOtp(success code5) and real Taqnyat Mail magic-link sends, double-gated behind the global per-medium flagsOKE_SMS_LIVE=1/OKE_EMAIL_LIVE=1**plus** the real credentials; credential presence alone never sends. Skips are always visible. - Postgres durable journal (
drivers.journal: "postgres") — durable-flow runs persist inoke_journal_runswithlocked_by/lease_expires_at, reusing the Signal/ClockFOR UPDATE SKIP LOCKED+ lazy lease reclaim pattern.memory(default) andfilestay for local / single-host. - Boot-time orphan discovery — a booted app scans the shared journal for
running/ duesleepingruns without a live lease and resumes them, no caller-suppliedrunId; the scheduler tick also claims due sleeps and sweeps expired-lease orphans from the shared store. - Run-level lease coordination — every start / resume / wake acquires the run lease first (renewed on each journal persist, released on park / complete / fail), so two instances never execute the same run under a live lease; an in-process in-flight guard stops overlapping sessions (orphan scan vs. scheduler tick) inside one instance.
- Durable-journal chaos proofs across OS processes — SIGKILL mid-run with survivor takeover (completed
fx.stepnever re-runs) and a two-process race on one due sleep (exactly one executes), gated onOKE_TEST_POSTGRES_URL.
♻️ Changed
- Booted apps bind
drivers.journalfrom config instead of a hardcoded in-memory store.postgreswithoutDATABASE_URL/OKE_STORE_SQL_URLfails loud at boot; the postgres journal pulls thestore.sqldocker image like clock does. - Template / create-oke: standard + advanced gain
drivers.journal—memoryfor local/test,postgresfor docker/prod. JournalStoregains an optional lease surface —JournalLeaseStore(acquireLease/releaseLease/claimDueSleep/listOrphans),hasJournalLease,JournalLeaseBusy, andJOURNAL_DEFAULT_LEASE_MS(30s) are exported; memory and file stores implement it too.- Flow / Clock docs: durable crash-recovery claims are now scoped to a shared journal —
postgresresumes after restarts and across instances; thememoryjournal remains same-process only. At-least-once for in-flight steps and the client-retry boundary are documented. - Template / create-oke docker+prod clock default:
file→postgresnow that the real CronStore exists (filestill valid for local/single-host). sently1.0.0 → 1.2.0 — Taqnyat OTP helpers (sendOtp/verifyOtp) unchanged in shape; 1.1/1.2 add vendor extras + the opt-in live-suite pattern mirrored by OKE's new live tests.- Split
JournalSuspend/isJournalSuspendintojournal-suspend.tsso the kernel edge profile (retry filter) no longer pulls Node journal persistence into the browser bundle (~15.01 kB → ~13.4 kB gzip). fx.store(sqlDecl)typed asSqlStoreHandle(facet overloads) so.upsert/.insert/.selecttypecheck; SQL decls without a store runtime throw instead of a stub missing those methods.- Store Seeding docs: full env matrix, simple vs complex file forms, upsert outcome table, CLI flags, seed-vs-migrate Callout.
- Store docs: short **Multiple environments** note under SQL schema sync — local
pushvs staging/prod versioneddrizzle/+migratecatch-up. oke dbadversarial test: multi-filegenerate+ re-migrateskips applied migrations; simulated env lag applies only the pending file (drizzle-kit__drizzle_migrationshistory through the CLI wrapper).
🐛 Fixed
- Durable three-layer gap closed: with a shared journal, restarts no longer lose every durable run, crashed runs resume via the boot orphan scan, and the same run can no longer execute on two instances concurrently under a live lease.
fx.store(db).upsertthrough realoke()→createTestApp→app.fetch()(regression test); SQLStoreDeclwithout a store runtime no longer silently returns a stub that made.upsertlook like “not a function”.