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": "cache-is-safe", "kind": "claim",
  "body": "The new cache layer is safe to ship today." }

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": "stale-reads-opposes-cache-is-safe",
  "from": "stale-reads", "relation": "opposes", "to": "cache-is-safe" }

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

Stance

{ "type": "stance", "id": "ops-agent-holds-cache-is-safe",
  "agent": "ops-agent", "posture": "holds", "target": "cache-is-safe",
  "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": "incident-742", "includes": ["metric-shift", "…"] }

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. deploy-change-causes-metric-shift)
    • 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.