Atlas 0.3.0
Released 2026-10-12 · v0.3.0 on GitHub · install with "atlas": "github:AndreyDAraya/atlas#v0.3.0"
Safe cascade: a fact cycle only data shows no longer runs a worker until the process dies, a failed query stays a query (no exception, no delivery budget), and the shop example consumes every fact it emits.
Breaking
Section titled “Breaking”StoredFactgaineddepth; thecommitinput facts accept it.depthis the length of an entry’s cause chain: 0 for what a dispatched command or a released timer writes, the cause’s depth + 1 for what a delivery writes. A commit’s facts carry it as an optional field (absent: 0);factsAfter,causedByandcommitreturn it as a number, and the runtime treats a missing one as 0. The Postgres adapter adds the column itself:migrate()runsschema.sql, which now carriesALTER TABLE atlas_facts ADD COLUMN IF NOT EXISTS depth int NOT NULL DEFAULT 0, so a boot that migrates adds it once and rows from before it read asdepth: 0; nothing is rewritten. Migration: customStoreadapters persistdepthfrom the commit’s facts and return it on every fact they read back (a fact committed without it reads back asdepth: 0); the contract suite intest/store-contract.tsexercises it. (d4b7eaa)- New delivery outcome:
failedwith codecascade-exceeded. A delivery of an entry whosedepthis pastmaxDepth(below) settles like a poisoned one: afailedtrace event with codecascade-exceeded(thereasonnames the type, its depth andmaxDepth), the consumer’s cursor advances, nothing is written, nothing is retried; the entry’s cause chain stays in the outbox for the operator. It is a delivery code only: a dispatch or a query never answers it. Migration: observers and dashboards that enumerate the framework codes addcascade-exceedednext tostore-rejected; one in the trace means a fact cycleatlas checkATLAS011 could not see in the declarations, or a paged fan-out of more thanmaxDepthpages. (d4b7eaa)
createRuntime({ maxDepth })(default 1000): the deepest cause chain a delivery accepts, the fuse for a cascade that never ends instart()workers, wheremaxSteps(a budget perdrain()call) means nothing. The trade-off: a paged fan-out counts one level per page, so a fan-out of more thanmaxDepthpages needs a largermaxDepth(1000 pages of 1000 rows is a million rows per fact).drain()keepsmaxStepsfor tests and CLIs. (d4b7eaa)- Shop example:
order-summarykeeps acustomersview (userId,name,ordersPlaced) projected fromUserRegisteredandOrderPlaced, with an admin-onlyCustomersview query ordered by name. (d091cbb)
- A data-dependent fact cycle (one ATLAS011 cannot see in the declarations) delivered by
start()ran until the process died: onlydrain()bounded a cascade, withmaxSteps, and a worker runs forever. Deliveries are now bounded bymaxDepth. (d4b7eaa) - A failed query (
store-failed,upcast-failed,query-failed) was recorded as a delivery step: anobservecallback that threw turned the query’s rejection into an exception out ofquery(), and each failure during adrain()spent one of itsmaxSteps. Failed queries are traced and observed outside delivery: the observer’s throw is swallowed and no budget is spent. (3bb9e46) - Shop example:
UserRegisteredhad no consumer (atlas checkATLAS010). (d091cbb)
From CHANGELOG.md. All versions: Releases.