A recorded trunk, a fork at now, and two simulated branchesA solid line runs left to right and stops at a point marked NOW. The solid stroke is the RECORDED class: it is what actually happened, tagged observed. At NOW a bar stands across the line — nothing runs until a human accepts. Past NOW the line forks into two dotted rays that share that one baseline. Dotted is the PINNED class: byte-identical on replay. One ray, named main, rises to more violations; the other, named governed, stays flat on zero. Both are tagged simulated. The angle between the two rays, marked at the fork, is the measurement — which is what the word parallax means.RECORDEDobserved · it happenedNOWnothing runs until you acceptmaingovernedPINNED · simulatedPINNED · simulatedΔ the measurement
Parallaxrun bef312a9 · class PINNED

scroll — you are scrubbing a log

Skip to the product
  1. Every operating decision is taken once.You choose, the world moves, and the alternative is never observed.
  2. Parallax gives you the one you did not take.Point it at a context. It proposes a model of what is actually in there, and hands it to you before anything runs.
  3. You accept it, or nothing happens.A model nobody reviewed should not be able to produce numbers that look authoritative. While a blocking question is open, it refuses to activate.
  4. Then fork the history and change one decision.Same initial state, same seed, one policy different. The log is append-only, so a branch costs nothing to create.
  5. Two lines out of one baseline.The angle between them is the measurement. That is what the word parallax means.
  6. And every number says how much of it was real.Typed observed or simulated at birth, carried into the receipt and into the API.
Apache-2.0bstack · ontology simulation layer hub deployed

Simulation results you accept before they are active.

Point Parallax at a context. It proposes a model built from what is actually in there, waits for a human to accept it, and only then rolls it forward under the decisions you are considering. Every answer is typed observed or simulated.

git clone github.com/broomva/skillsthencd skills/skills/simulation/parallax/runtimethenbun install && bun run demo

The design target is not a simulator that is right. It is a simulator that cannot lie about being a simulator — and all three of the things that follow from that are enforced in code, not in documentation.

What it enforces

Four guarantees, each one a runtime check rather than a promise.

A simulator’s output is unfalsifiable by default: it produces confident numbers about a world that does not exist. The usual response is to claim more fidelity, which cannot be checked from the outside at all. These four can.

01 · The gate

An ontology nobody accepted cannot run.

Parallax reads your context and proposes a model of it — state, actions, and the questions it could not answer from what was there. Slots it cannot support come back empty rather than plausible.

The accept gate is a runtime check, not a type-system convention. It refuses while any blocking question is open, and a unit on a numeric quantity is always blocking, because a number with no unit fails closed at materialisation and the proposer will not invent one.

activate() → BLOCKING_QUESTIONS_OPEN. The accepted object is minted behind a module-private symbol and checked at runtime, so it cannot be forged and does not survive a JSON round-trip.

See the gate mechanics

02 · The fork

Fork the history and change exactly one thing.

Your operations already run on an append-only history, and a history can be forked. Copy-on-write means a branch costs nothing to create, so the question stops being whether to spend a week testing it.

Same initial state, same seed, same twelve steps — one policy different. The angle between the two branches is the measurement, which is what the word parallax means.

Under an ungoverned sales agent the storefront oversells stock it does not have ten times in twelve steps. Forked at the step before the damage, with a governor installed, the same twelve steps produce zero.

See the trajectory figure

03 · The typing

No number leaves without saying how much of it was real.

Every value is typed observed or simulated at birth, and derivation joins the tags: an answer is observed only if every input it came from was observed. One simulated input makes the answer simulated, all the way to the top line.

That is why a Parallax figure cannot be quoted as a measurement by accident. The origin is welded to the number, into the receipt and out through the API.

The receipt states its own split unprompted — this run reports 0 of 12 steps observed — alongside the branch class and what the policy declared against what it demonstrated.

See what the receipt says

04 · The proof

A policy is not allowed to certify its own reproducibility.

Determinism is checkable in five seconds, so Parallax checks it rather than claiming it. certifyPolicy runs a policy repeatedly against an identical probe and compares trace hashes. Replay is a hash comparison, not an assurance.

A policy that cannot reproduce its own output under a fixed seed is demoted whatever it declares about itself — and the demotion is written onto the branch rather than reported once and forgotten.

Same seed → identical hash. Different seed → diverges, which is a different world and not a defect. An unpinned actor → the branch withdraws its own reproducibility claim, PINNED down to STABLE.

See the reproducibility lattice

How it works

Five steps, and one of them is the product.

Parallax reads what is already there. It does not ask you to model your operation first, because the model is the thing it is supposed to produce.

  1. 01PointAt a directory, an agent’s own workspace, or a set of business tables.
  2. 02ProposeAn ontology assembled from what is in there — state, actions, and the questions it could not answer. Slots it cannot support come back empty.
  3. 03AcceptA human answers the blocking questions and accepts. This is the product, not a formality. Nothing runs before it.
  4. 04RollFork the log at a point, change one decision, replay the same steps under the new policy.
  5. 05TypeEvery value carries observed or simulated, plus a class saying whether it can be re-derived at all.

Three context classes, one intake

Class 01 — business data

A schema and its rows

Orders, ledgers, inventory, tickets. One state field and one insert action per table, and a blocking question per numeric parameter. The conservation identity is usually already in there — money, stock, hours, seats.

Class 02 — agent workspace

An agent’s own directory

The session is spawned with its working directory already set to the tenant’s own folder, and the confinement keys off exactly that. Passing a derived path instead is denied.

Class 03 — local filesystem

An arbitrary directory

Files, commits, exports, logs. No integration and no schema agreed in advance. Same proposer as the workspace class; the difference is who is allowed to name the root.

You are approving a model of your own operation, not a model’s opinion of it. An empty slot is the correct answer when the context does not support one; a plausible guess is not.

For agents

The agent is a user, not a client library.

Every capability a human can reach is reachable programmatically, over the same handler functions. Every failure is a value with a stable machine-readable code rather than a thrown string a caller has to parse, and the error types are per-operation: a plugin failure inside a rollout carries a partial trajectory, the same failure at registration carries nothing, and a single error type cannot express that difference.

$ parallax propose --kind filesystem --root ./ --json
$ parallax accept --proposal p-4c1e --answer src.count=files --by carlos --json
$ parallax run --horizon 12 --seed 42 --governed --json
$ parallax receipt --run bef312a9 --out out/run.html

# --ontology is optional: omitted means the newest acceptance

Seven commands. Every one of them takes --json and prints a value, so a shell script never has to parse prose.

The surfaces diverge in exactly two places, and both are confinement rather than capability. --root: an arbitrary absolute root is safe at a terminal, because the person typing the path is the confinement. It is absent from every tool schema, because inside a sandboxed session a derived path is denied and a denied read comes back as an empty directory rather than an error — so a wrong path would look like an empty workspace. And --out on receipt, which writes the page to a path the tool surface returns but never sends, because a receipt is tens of kilobytes and does not belong in a context window.

That count is not a promise in prose. A test asserts every tool has exactly one CLI command, every command has a tool behind it, and every flag maps to a tool field or to one of those two named divergences — so widening the claim means editing a test that says so. It was written because the claim had been wrong: three tools had no CLI command at all.

The channel

It reaches people where the business already runs.

An operator does not open a console. A real WhatsApp message already runs an agent turn in a confined workspace on our own hardware, and the hub already answers the route that turn calls. Each hop below carries its own status, because three of the four run today and the fourth does not.

  1. 01channel

    WhatsApp → Kapso

    Kapso is the Cloud API gateway. A thread id arrives as kapso:<phoneNumberId>:<waId>, and the principal is the second segment — the first is our own number and is identical on every message.

    runs in production

  2. 02ingress

    Tailscale Funnel

    The webhook port is published and nothing else. SSH stays closed. AllowFunnel is keyed by host:port rather than by path, so the port is separate from the one the web app listens on.

    runs in production

  3. 03runtime

    Genesis session

    One agent session per thread, in a dedicated workspace directory rather than the whole home. A fail-closed allowlist on waId: an unknown sender is refused, and the refusal is the default rather than the exception.

    runs in production

  4. 04engine

    Parallax hub

    POST /api/whatsapp/turn takes {from, text, threadId} and nothing else. Every ontology, run and receipt is authored server-side, so there is no path here that hands the hub content to publish.

    runs, driven by the demo

The first three hops carry real WhatsApp traffic today. The fourth is the one that is not joined in production: the hub answers /api/whatsapp/turn and serves /r/:id, and bun run demo:live drives that whole thread against the deployed hub — but no Genesis session is calling it for a real number yet. Parallax’s own channel layer is pure functions with no transport attached, which is exactly why it can be pointed at one.

See the whole loop as one product — the multi-site operator →

Domains

A domain arrives as data. The runtime never changes.

Six operators are closed over one record — step, observe, check, rollout, diff, traceHash. Adding a domain adds a record. Adding a capability adds an operator, and there are six of those. That asymmetry is what separates a simulation runtime from a pile of bespoke simulators.

ONTOLOGY — the five slots, and who is allowed to compute each one
SlotSuppliesWho computes it
statetyped fields, units mandatoryschema
actionsname, actor, paramsschema
transitionhow an action changes the statecode, never a model
invariantswhat must always holdcode, never a model
initialwhere it startsdata

Domain 01 — storefront

A WhatsApp storefront under a sales agent

Stock and money conserve. The demo runs it ungoverned, catches it overselling inventory it does not have, forks the history at the moment before the damage and replays with a governor installed.

Domain 02 — clinic

An appointment desk, and the generality proof

Its own transition, its own conservation law, its own fourteen tests. It is the answer to “does this only work for your toy storefront?” — the runtime did not change to accept it.

Where it fits

Bring one context and one thing that must never be true.

An ontology is a record, not a codebase: what the state is, what actions exist, how an event folds into the state, and what must always hold. Two of those four are code and always will be.

Operations

A change you only get to make once

A price, a refund policy, an escalation threshold. There is no staging environment for the way a business operates, so the usual way to find out is from a customer.

What happens to stock-outs if the sales agent can promise same-day?

Agent governance

Whether a governor would have caught it

Point it at an agent's own workspace, replay the run it already did, and install the constraint you were considering. The branch says which steps it would have refused.

Which of these twelve steps would a stock governor have stopped?

Capacity

A schedule under a load it has not seen

The clinic domain is an appointment desk with its own transition and its own conservation law. Seats, hours and slots conserve the same way money and stock do.

If two clinicians take leave, how many appointments miss their window?

Rehearsal

A directory you have not modelled yet

No integration and no schema agreed in advance. One non-recursive read and a stat per entry, and what comes back is a proposal you can argue with.

What is this repository as a state machine, and what does it already conserve?
The one we have worked out in full: an operating twin for a multi-site business →

Build state

What runs, what does not, and what is unmeasured.

Every figure on this page is printed by a command in the Parallax runtime at seed 42 over a horizon of 12 steps. There are no customer deployments, no accuracy claims and no benchmark numbers here, because we have none.

Runs today

  • The runtime and its six operators
  • Append-only log with copy-on-write forking
  • The reproducibility lattice, and the policy certifier that enforces it
  • Conservation and safety invariant checking
  • The accept gate, brand-checked at runtime
  • The conversation layer as pure functions
  • The self-contained run receipt
  • A CLI, an HTTP hub and a tool surface over the same handlers
  • A second domain — the clinic, the generality proof

Designed, not built

  • The LLM adapter — today’s actors are seeded and pure
  • A third domain, and a domain supplied by someone who is not us
  • The web console
  • A live WhatsApp number; the channel layer is pure functions with no transport attached

Not measured

  • Nothing here is calibrated against a real business
  • No accuracy percentage, because we cannot support one
  • No customer results, because there are no customers

We would rather say that than publish an accuracy number we cannot support. It is the oldest open item in this project and it cannot be closed by writing code.

Objections

Reasonable things to distrust about this.

It is still a simulation. Why would I trust the number?

Do not trust the number. Check what it says about itself. This run reports 0 of 12 steps observed in its own receipt, unprompted, and its policy’s declared class was measured against a repeated probe instead of believed. Nothing here is calibrated against a real business, and there is no accuracy figure on this page because we cannot support one.

Is a language model computing my numbers?

No. The transition function and the invariants are code — activate refuses an ontology that supplies neither. A model’s only job is reading a mess into typed observations, and nothing downstream of that projection is a model’s opinion. Today the actors are seeded and pure.

What if the proposed ontology is wrong for my business?

Then you answer the questions differently, or reject it, and nothing activates while a blocking question is open. What you cannot do today is accept part of it: acceptance is all-or-nothing plus the set of answered slots. Editing a proposal produces a new proposal with a new hash, which is the honest behaviour and also the more annoying one.

We already A/B test.

A/B testing spends real customers and real weeks, and it cannot evaluate a decision you only make once. A fork is a branch record. The two compose: use this to decide what is worth testing for real.

Our context is a mess.

Then the proposal comes back smaller and emptier. The one it made of the Parallax project directory proposed zero invariants and seven blocking questions, and said so in the message rather than filling the gaps with something plausible. An empty slot is information about your context, not a failure of the tool.

Read the full proof page — tables, receipts and verbatim output →

Start

Reproduce every number on this page.

Requires Bun. The demo runs a WhatsApp storefront under an ungoverned sales agent, catches it overselling, forks the history at the moment before the damage, replays the same twelve steps with a governor installed, and prints the difference.

$ git clone https://github.com/broomva/skills
$ cd skills/skills/simulation/parallax/runtime
$ bun install
$ bun run demo           # run, observe, check, fork, prove
$ bun run demo:whatsapp  # the same thing as one thread, ending in a receipt
$ bun run demo:live      # the same thread against the deployed hub
$ bun run mutants        # deletes a guarantee, checks whether anything goes red