Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Records and the canonical model

A document’s source is written as records (headers + indented blocks). The parser desugars them into canonical objects — a flat, insertion-ordered array of typed objects, serialized as JSON.

Record headers

HeaderFormBecomes
focusfocus <id>a Focus
linklink [alias:] <from> <relation> <to>a Link
stancestance [alias:] <agent> <posture> <target>a Stance
questionquestion <id>a Question
scopescope <id>a Scope
profileprofile <name>a Profile
importimport <name> as <ns>(resolved as a project; no object)
action<agent> <posture> …desugars to foci / links / stances

Anything whose first token isn’t a reserved keyword is parsed as an action header — <agent> <posture> <target> — which is how the readable surface works. If the second token isn’t a known posture, you get unknown record kind or header.

The canonical objects

The JSON model is { "objects": [ … ], "timeline"?, "audit"? }. Each object has a type tag. There are seven:

Focus

{ "type": "focus", "id": "conditions-are-fine", "kind": "claim",
  "body": "Conditions on site are fine to pour the slab this afternoon." }

Optional fields: kind, quantity, formula, body, free-form fields, and the opt-in derived ones (computed_quantity, superseded_by, derived_confidence, argument_status, expected_value, decision).

{ "type": "link", "id": "overnight-freeze-opposes-conditions-are-fine",
  "from": "overnight-freeze", "relation": "opposes", "to": "conditions-are-fine" }

Optional: weight, probability, basis, body, fields, plus derived superseded_by, derived_confidence, leverage, argument_status.

Stance

{ "type": "stance", "id": "site-engineer-holds-conditions-are-fine",
  "agent": "site-engineer", "posture": "holds", "target": "conditions-are-fine",
  "confidence": { "kind": "number", "value": 0.9 }, "basis": "assumed" }

Question

{ "type": "question", "id": "throughput-benchmark",
  "body": "Can Postgres sustain 50k events/s?", "expects": "number", "status": "open" }

Plus asks_about (a list of ids).

Scope

{ "type": "scope", "id": "kabir-hat-survey", "includes": ["cases-clustered", "…"] }

Profile

{ "type": "profile", "name": "risk-analysis",
  "kinds": ["risk"], "relations": ["aggravates"], "postures": ["flags"] }

Act

Provenance objects for readable actions, emitted only under --acts. Each records the verb, args, and the canonical ids it expands_to.

Ids

Every object has an id, used for cross-references.

  • For focus, question, scope, profile — you write the id in the header.
  • For link and stance — you can supply an alias (link foo: a causes b), or let the parser generate one:
    • a link → <from>-<relation>-<to> (e.g. dead-starter-causes-flat-loaf)
    • a stance → <agent>-<posture>-<target>
    • on collision, a -2 suffix is appended.

Reusing an id across records is flagged (duplicate id / id is reused across records). A focus mentioned twice is merged, not duplicated — see Foci.

Order is preserved

The objects array is in declaration order, and field maps keep author order. A document without any opt-in derivations serializes byte-for-byte stably — which is what lets the test suite assert that every bundled example is strict-clean.