Skip to content

Atoms, Facts, and Intents

JacqOS’s runtime containment works because evidence, derived truth, and proposed actions are kept strictly separate. When a runtime agent proposes an intent, the platform can check it against invariants precisely because facts are derived from observations through explicit, traceable rules — not mixed together in mutable state. When a development-time AI generates those rules, you can verify behavior through fixtures and provenance precisely because each plane is independently inspectable.

Everything flows through one cycle:

Observation → Atoms → Facts → Intents → Effects → (new Observations)

Each stage is a separate durable plane — a distinct storage layer with its own semantics. Keeping them separate is what makes both runtime safety and authoring verification possible.

PlaneWhat it holds
ObservationAppend-only evidence that something happened
BlobRefContent-addressed handle for large raw payloads
AtomBatchDeterministic flattening of one observation into semantic atoms
FactDerived truth record with provenance (assertions and retractions)
IntentDerived request to perform an external action
EffectExecution lifecycle of an intent plus resulting observations

Observations are the ground truth. A booking webhook, an API response, a timer tick, an LLM completion — each arrives as an immutable observation appended to the lineage log.

Every observation carries:

  • A unique observation reference
  • A kind classifier (e.g. "booking.request", "slot.status")
  • A raw payload (JSON, text, or a BlobRef for large data)
  • Ingestion metadata (timestamp, source)

Observations are never modified or deleted. They are the evidence layer — they record what the outside world said, not what the system believes about it.

Rhai mappers turn each observation into a batch of atoms — typed key-value pairs that form the structural boundary between messy external data and the semantic logic layer.

fn map_observation(obs) {
let atoms = [];
if obs.kind == "booking.request" {
let body = parse_json(obs.payload);
atoms.push(atom("booking.email", body.email));
atoms.push(atom("booking.slot_id", body.slot_id));
atoms.push(atom("booking.name", body.patient_name));
}
atoms
}

Atom extraction is deterministic. The same observation always produces the same atoms. A mapper cannot read facts, derive intents, or access external resources — it receives one observation and returns atoms, nothing more.

The canonical mapper export is the portable contract for this boundary. Two different mapper implementations that produce the same canonical export converge on the same mapper_output_digest. You can refactor mapper code freely without changing semantics.

Atoms enter the ontology through the built-in atom() relation:

rule booking_request(req, email, slot) :-
atom(req, "booking.email", email),
atom(req, "booking.slot_id", slot).

Facts are what the system currently believes, derived from atoms and other facts through .dh ontology rules. Every fact carries full provenance — which observations, atoms, and rules contributed to its existence.

Facts support two operations:

  • Assertions — adding new derived truth
  • Retractions — removing previously derived truth when evidence changes
rule assert booking_confirmed(req, slot) :-
atom(obs, "reserve.succeeded", "true"),
atom(obs, "reserve.request_id", req),
atom(obs, "reserve.slot_id", slot).
rule retract slot_available(slot) :-
booking_confirmed(_, slot).

When new observations arrive, the evaluator reaches a new fixed point and materializes fact deltas — which facts were asserted, which were retracted, and why. The provenance chain for every fact traces all the way back to the raw observations that produced it.

This is where invariant review operates. Humans declare constraints over facts, and the evaluator proves they hold across every scenario. You read the invariant, not the rules that derive the facts.

Intents are facts with a special prefix — intent. — that the shell intercepts as requests for external action. They are derived exactly like any other fact, through ontology rules.

rule intent.reserve_slot(req, slot) :-
booking_request(req, _, slot),
not slot_reserved(slot).

Intents go through a strict lifecycle:

  1. Derived — the evaluator produces the intent from current facts
  2. Admitted — the shell durably records the intent before any external execution
  3. Executing — the effect runner begins the external action
  4. Completed — the effect result is captured as a new observation

Every intent is durably admitted before execution begins. No external action fires from a transient derivation.

Effects are the execution lifecycle of admitted intents. They use declared effect capabilities:

CapabilityPurpose
http.fetchDeclared outbound HTTP
llm.completeExplicit model call for LLM-assisted agents
blob.put / blob.getLarge raw body storage
timer.scheduleRequest a future timer observation
log.devDeveloper diagnostics (never canonical state)

Guest code never mutates facts directly and never appends observations directly. Every external action goes through a declared capability. Undeclared capability use is a hard load error.

Effect results — success or failure — are captured as new observations, which closes the derivation loop and starts the cycle again.

Retry policy is explicit. Idempotent effects auto-retry on failure. Ambiguous mutations (non-idempotent requests that failed mid-execution) enter reconcile_required status for explicit human resolution. No silent auto-retry of mutations whose outcome is uncertain.

The six durable planes describe the evidence and execution loop. JacqOS also keeps separate audit surfaces so current truth never gets confused with historical explanation.

SurfaceWhat it answers
Committed semantic snapshotWhat does this evaluator currently believe at the committed head?
Attempt reportWhy did one evaluated head commit or reject?
Fact planeWhich fact memberships were added or removed when a head committed?
Intent planeWhich intents entered or exited, and which effect records are attached to them?

These surfaces stay distinct on purpose:

  • The committed semantic snapshot is the current truth surface.
  • Every evaluated head writes one attempt report, whether it commits or rejects.
  • Attempt reports explain one evaluation attempt. They are not ontology facts.
  • The Fact plane appends committed add and remove deltas for facts and contradictions.
  • The Intent plane appends committed enter and exit deltas plus the latest effect linkage for each intent contract.
  • Fact and Intent planes are append-only audit history, not alternate worldviews.
  • Studio, verification, and crash-recovery tooling inspect these audit surfaces without turning them into hidden state.

The derivation pipeline is not a one-shot sequence — it is a continuous loop:

┌─────────────┐
│ Observation │ ← new evidence arrives (webhook, API response, effect result)
└──────┬──────┘
│ Rhai mappers
┌─────────────┐
│ Atoms │ ← deterministic semantic extraction
└──────┬───────┘
│ .dh ontology rules
┌─────────────┐
│ Facts │ ← derived truth with full provenance
└──────┬───────┘
│ intent. rules
┌─────────────┐
│ Intents │ ← derived action requests
└──────┬───────┘
│ effect runner + declared capabilities
┌─────────────┐
│ Effects │ ← execution lifecycle
└──────┬───────┘
│ results captured as observations
└──────────→ back to Observation

Each pass through the cycle:

  1. New observations arrive — either from external sources or from effect results
  2. Mappers extract atoms deterministically
  3. The evaluator reaches a new fixed point, materializing fact deltas
  4. New intents may be derived from the updated fact set
  5. The shell admits and executes intents through declared capabilities
  6. Effect results become new observations, and the cycle continues

The loop terminates naturally when a fixed point produces no new intents. Until then, each effect result feeds back as evidence for the next evaluation pass.


JacqOS uses a small set of explicit, non-interchangeable identities to track what changed and why:

IdentityPurpose
lineage_idNames one observation history
evaluator_digestSemantic identity for fact derivation — hash of ontology IR, mapper semantics, and helper digests
package_digestFrozen runtime handoff identity
mapper_output_digestCross-shell evidence equivalence

evaluator_digest is the primary semantic identity. When ontology rules, mapper logic, or helper code changes, a new digest is produced. Prompt-only changes do not affect the evaluator digest — semantic identity tracks logic, not presentation.