Claude Skill

semantic-spacetime

Model and analyze Semantic Spacetime (SST) graphs, distances, trajectories, drift, and model files. Do not use this skill for promise-theory vocabulary and fundamentals without SST modeling; use `promise-theory` for the substrate concepts.

LLM Mart · 0 points · 6 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download magnus919-agent-skills-semantic-spacetime-d0edebb.zip · 94 KB
Part of magnus919/agent-skills — 145 skills

Install

skills CLI npx skills add https://github.com/magnus919/agent-skills/tree/main/semantic-spacetime
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install magnus919-agent-skills@llmmart
Git git clone https://github.com/magnus919/agent-skills.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole magnus919/agent-skills collection as a plugin from our marketplace. Git is the plain clone.

README

Semantic Spacetime

Model meaning over time with Mark Burgess's Semantic Spacetime: a discrete graph method for designing shared semantic ground between agents, diagnosing semantic drift, and building coordination that converges on intended meaning.

Why Install This Skill

Multi-agent systems keep failing on meaning: two agents start from the same instructions and quietly diverge, nobody notices that a shared term no longer means the same thing to each side, and the system dead-ends in a state where information stops flowing. This skill gives your agent a working method for that problem — model the space of meaning as a graph, treat every local change as a unit of time, and measure where interpretations drift apart instead of guessing.

After installing, your agent can map a team of agents onto a semantic spacetime with typed events, things, and concepts, trace how intent propagates through promises and acceptances, diagnose drift and divergence with a bounded procedure, and write an analysis report with concrete interventions and a verification plan. The method is grounded in Burgess's arXiv series (2014-2025) and his earlier Promise Theory, and it is honest about what is verified, what is not, and what is extrapolation.

What You Get

Contents Provides
SKILL.md When to use Semantic Spacetime, when not to, and what to load for the task at hand
references/foundations.md The academic core: definitions, the γ(3,4) formalism, proper time, causality, the promise substrate, and adjacent fields
references/applications-infrastructure.md The CFEngine → IaC → Kubernetes/GitOps/IBN → MAPE-K lineage: convergence semantics, the promise-keeping-as-data gap, SLOs as semantic contracts, and the record-of-time machinery, with a citable lessons list
references/agent-coordination.md Agentic AI: Burgess's agent papers, SSTorytime and MCP-SST, drift and temporal-blindness literature, spatial-temporal world models, the MCP/A2A substrate, and five labeled synthesis patterns
references/patterns.md Ten named patterns (semantic anchor, trajectory, convergence loop, promise propagation, drift detection, absorbing states, shared manifold, γ(3,4) modeling, distance metrics, reconciliation), each with when-to-use and anti-patterns
references/diagnosis-and-debugging.md A bounded procedure for diagnosing semantic drift, divergence, dead-ends, and meaning gaps — stop after three non-converging passes and report the evidence
references/glossary.md Heading-led definitions of every term the skill uses
references/bibliography.md Annotated primary sources with URLs, organized by area
templates/ The sst-model.yaml.tmpl model format (agents, nodes, edges, acceptances, trajectories, observations) and the sst-analysis.md.tmpl report skeleton
scripts/semantic-spacetime.py A stdlib-only CLI: lint a model, map the γ(3,4) graph, measure semantic distance, trace trajectories, and diff snapshots for drift (--json and --dry-run supported)
tests/ A stdlib unittest suite (runs in CI) and trigger/anti-trigger routing probes, plus a fully-filled sample model fixture
evals/ Output-quality evals for the skill
LICENSE MIT license

Quick Start

Nothing to install: the CLI is stdlib-only Python 3.10+. From the repository root, run:

  1. Lint a model against the sst-model-v1 format — exit 0 prints a coverage summary, exit 1 prints named violations: python3 semantic-spacetime/scripts/semantic-spacetime.py model lint semantic-spacetime/tests/fixtures/sample-model.yaml
  2. Map the γ(3,4) graph (text | mermaid | json): python3 semantic-spacetime/scripts/semantic-spacetime.py model map semantic-spacetime/tests/fixtures/sample-model.yaml --format mermaid
  3. Measure semantic distance (weighted hop count, |link| + 1 per hop): python3 semantic-spacetime/scripts/semantic-spacetime.py model distance semantic-spacetime/tests/fixtures/sample-model.yaml --from report-event --to drift-concept
  4. Trace trajectories (simple paths with link types; cycles noted): python3 semantic-spacetime/scripts/semantic-spacetime.py model trajectory semantic-spacetime/tests/fixtures/sample-model.yaml --from report-event --to drift-concept
  5. Diff two snapshots — added/removed/changed regions; identical snapshots report no drift (run it on the same file twice to see the no-drift case): python3 semantic-spacetime/scripts/semantic-spacetime.py model drift semantic-spacetime/tests/fixtures/sample-model.yaml semantic-spacetime/tests/fixtures/sample-model.yaml
  6. Append --json to any command for a single machine-readable object; --dry-run is a no-op guard.

To draft your own model, copy templates/sst-model.yaml.tmpl and fill it per the inline comments — the delimited example block shows a complete model. Copy templates/sst-analysis.md.tmpl for the analysis report skeleton: system description, the semantic spacetime map, drift/divergence/absorbing-state findings, interventions, and a verification/measurement plan.

Triggers

  • Designing or analyzing shared semantic ground between agents
  • Modeling intent or meaning changing over time (trajectories, drift, convergence)
  • Designing convergent, self-healing coordination where state is measured against desired meaning
  • Diagnosing semantic drift, divergence, or dead-ends (absorbing states)
  • Mapping promises onto spacetime (trajectories, propagation, causality)
  • Analyzing temporal blindness in agents (state tracking, event ordering, causality)

Requirements

Python 3.10+ (stdlib only) for the bundled CLI; nothing else to install. The skill content is Markdown, YAML templates, and JSON evals; the bundled model format is versioned (sst-model-v1) and documented in the template itself. Works with any agent client that loads Agent Skills.

Skill manifest

Semantic Spacetime

Semantic Spacetime (SST) is Mark Burgess's discrete, graph-theoretic model of meaning over time. A semantic element is one autonomous agent plus its scalar promises; a semantic spacetime is a collection of such elements in which a local change in state, promises, or configuration is a local unit of time. Time is proper time — there is no global clock (the precedence view Burgess credits to Lamport). Causality is cooperative: every adjacency requires an offer (+) and an acceptance (−) promise on both ends, so space is made of cooperating nodes and edges. The 2025 γ(3,4) formalism types the graph: three node meta-types (events, things, concepts) connected by four link types (0 = NEAR, ±1 = LEADS TO, ±2 = CONTAINS, ±3 = EXPRESSES). Absorbing states in partial graphs leak information, and intentionality enters at the boundary. SST is built on Promise Theory — for the promise vocabulary, load promise-theory instead of re-deriving it here. This skill is a thin router: load the dense material only when a row in Load By Need matches your task.

When to use

  • When you need to design or analyze shared semantic ground between agents — model what "meaning" means in this system (what does a concept, term, or promise mean to whom), producing a γ(3,4) map of the shared semantic ground as the artifact.
  • When you need to model intent or meaning over time — trajectories, drift, and convergence of understanding between agents, agents and humans, or agents and their instructions; the artifact is a semantic trajectory with recorded observations.
  • When you need to design convergent, self-healing coordination — a loop in which state is continuously measured against a desired meaning and repaired toward it; model the loop as semantic elements whose local change is time.
  • When you need to diagnose semantic drift, divergence, or dead-ends — absorbing states, meaning gaps, and non-converging agents; the artifact is a drift finding with the leaking boundary identified.
  • When you need to map promises onto spacetime — trajectories, promise propagation, and causality between agents; model each promise as an edge and trace how intent propagates through the graph.
  • When you need to analyze temporal blindness in agents — state tracking, event ordering, and causality failures where an agent cannot tell what happened before what; model event order via proper time instead of a shared clock.

When not to use

  • Physics or relativity — SST is not a theory of quantum gravity or spacetime physics; it assumes no manifold structure and no momentum. Do not use it for physics problems; those belong to a physics domain.
  • Pure vector embeddings, RAG, or semantic search without temporal-causal structure — a static embedding index has no proper time, no causality, and no trajectories to model; route to the embedding or semantic-search tool's own skill instead.
  • Enforceable centralized control — if you can command and verify compliance directly, SST's cooperative-promise machinery is overhead, not insight (the same boundary promise-theory draws); route to promise-theory when you need the control-vs- cooperation discussion.
  • Simple single-agent prompting — one model and one prompt with no delegation or meaning space to model needs no spacetime vocabulary.
  • Tool manuals or framework documentation — routing to the tool's own skill is always better than framing the tool with SST.

Load By Need

Need Load
Re-derive the formal model: semantic element, semantic spacetime, proper time, γ(3,4) typing rules, learning/knowledge formalism, promise substrate references/foundations.md
Learn from the CFEngine and infrastructure lineage before designing convergent systems (convergence semantics, IaC/Kubernetes/GitOps/IBN lessons, promise-keeping-as-data, SLOs, the record axis) references/applications-infrastructure.md
Model an agent team in SST terms or design agent coordination (Burgess's agent papers, drift/temporal-blindness literature, MCP/A2A substrate, synthesis patterns) references/agent-coordination.md
Apply a named pattern — semantic anchor, trajectory, convergence loop, promise propagation, drift detection, absorbing-state detection, shared semantic manifold, γ(3,4) modeling, distance metrics, reconciliation references/patterns.md
Diagnose semantic drift, divergence, dead-ends (absorbing states), or meaning gaps with a bounded procedure references/diagnosis-and-debugging.md
Hit an unfamiliar term while modeling or diagnosing references/glossary.md
Find or verify a primary source — the papers, project pages, and adjacent work behind a claim references/bibliography.md

Quick Start

The bundled CLI (scripts/semantic-spacetime.py) is stdlib-only — any python3 runs it, nothing to install — and every command is read-only. Run the commands below from the repository root; the CLI resolves no files relative to its own location, so the same commands work from any directory with absolute paths.

  1. Draft an SST model. Copy templates/sst-model.yaml.tmpl to a working file (for example sst-model.yaml) and replace the example values: declare agents (id, role, promises), semantic nodes (id, type in {event, thing, concept}), edges (from, to, link in -3..3), acceptances, trajectories, and observations. The machine-delimited block between # --- example --- and # --- end example --- shows a complete, valid model to imitate; the same model is committed, fully filled, at tests/fixtures/sample-model.yaml.
  2. Lint it against the sst-model-v1 format — exit 0 prints a coverage summary, exit 1 prints named violations: python3 semantic-spacetime/scripts/semantic-spacetime.py model lint semantic-spacetime/tests/fixtures/sample-model.yaml
  3. Map the γ(3,4) graph (--format is one of text | mermaid | json): python3 semantic-spacetime/scripts/semantic-spacetime.py model map semantic-spacetime/tests/fixtures/sample-model.yaml --format mermaid
  4. Measure semantic distance — weighted hop count (each hop weighs |link| + 1): python3 semantic-spacetime/scripts/semantic-spacetime.py model distance semantic-spacetime/tests/fixtures/sample-model.yaml --from report-event --to drift-concept
  5. Trace trajectories — every simple path with link types annotated; cycles are noted and the enumeration terminates on any finite model: python3 semantic-spacetime/scripts/semantic-spacetime.py model trajectory semantic-spacetime/tests/fixtures/sample-model.yaml --from report-event --to drift-concept
  6. Diff two snapshots — added/removed/changed semantic regions; identical snapshots report no drift. Point the command at your two snapshot files (running it on the same file twice demonstrates the no-drift case): python3 semantic-spacetime/scripts/semantic-spacetime.py model drift semantic-spacetime/tests/fixtures/sample-model.yaml semantic-spacetime/tests/fixtures/sample-model.yaml
  7. Machine-readable output. Append --json to any command for a single JSON object on stdout. --dry-run is accepted everywhere as a no-op guard.
  8. Draft the analysis report. Copy templates/sst-analysis.md.tmpl to a working file (for example sst-analysis.md) and fill the skeleton: system description → semantic spacetime map → drift/divergence/absorbing-state findings → interventions → verification/measurement plan.
  9. Diagnose drift when agents disagree. If agents diverge, treat the disagreement as an observation, measure the semantic distance between their interpretations, and locate the absorbing state or leaking boundary where information stops flowing.

Available Scripts

This skill bundles one script; there are no others to discover. Every command is read-only (--dry-run is accepted everywhere as a no-op guard), and --json on any command produces a single JSON object on stdout.

Script Purpose Invocation
scripts/semantic-spacetime.py Lints, maps, and analyzes SST models in the sst-model-v1 format. Subcommands: model lint (validate against the schema), model map --format text\|mermaid\|json (render the γ(3,4) graph), model distance --from X --to Y (weighted hop count, each hop weighs |link| + 1), model trajectory --from X --to Y (enumerate simple paths with link types), and model drift file-a file-b (diff two snapshots into added/removed/changed regions). Run lint after drafting or every edit of a model until it exits clean, then use the analysis subcommands when mapping shared semantic ground, measuring distance between interpretations, tracing intent propagation, or diagnosing drift between snapshots. python3 semantic-spacetime/scripts/semantic-spacetime.py model lint <model.yaml>

Exit codes: 0 = valid/covered, 1 = named violations or missing/unreachable ids, 2 = usage or IO errors.

Related Skills

Skill Route when...
promise-theory You need the substrate vocabulary SST builds on: promises, offers and acceptances, convergence, the Downstream Principle, and coordination diagnosis (also routed from references/foundations.md)
agent-evals-and-observability You need to turn measurement and verification of semantic claims into evals, traces, and release gates (also routed from references/foundations.md)
agent-council You want structured multi-agent debate as a mechanism for negotiating shared meaning between agents
workflow-architect You want to encode a semantic-spacetime-informed workflow as a reusable skill bundle
artifact-pyramids You need to structure SST evidence — models, maps, observations — as summaries → analysis → evidence dossiers
agent-skills You are authoring or editing an Agent Skills-format skill — the format this skill follows
cli-builder You are building or refactoring the bundled CLI for SST models (it will follow cli-builder conventions: non-interactive, --json, --dry-run)

Gotchas

  1. Provenance honesty. The theory files tag every factual claim [VERIFIED] (confirmed in a primary source fetched during research) or [UNVERIFIED] (secondary or inferred), and label original synthesis EXTRAPOLATION. Preserve those markers when you reuse the material; dropping a marker silently upgrades a claim. See the provenance block in references/foundations.md.
  2. The theory is semi-formal and unrefereed. Burgess published the series as self-published notes with no intention of seeking refereed publication, and "some proofs [are] left to the reader." Use SST as a reasoning aid, not a proof system. See the status section in references/foundations.md.
  3. Local time ≠ global clock. Proper time is per semantic element: a local change is that element's unit of time. There is no shared clock ordering all events; global order is an observer-relative artifact. See the proper-time section in references/foundations.md.
  4. Semantics requires measurement. Meaning cannot be asserted before it is measured at the right scale — "dynamics always trumps semantics" (the CFEngine-lineage lesson in references/applications-infrastructure.md). SST's spacelike (repeated trials, constant state) and timelike (continuously adapting) measurements are the two ways to stabilize observation; see the measurement-duality section of references/foundations.md.
  5. Promise-keeping must be stored as data. The gap documented in the CFEngine lineage — reporting whether a promise is kept right now without ever storing promise-keeping as queryable data — is exactly the gap SST's semantic-time record axis addresses (see the promise-keeping-as-data gap in references/applications-infrastructure.md). Record observations as versioned data or trust cannot accumulate.

Prerequisites

  • Python 3 with standard library only; the CLI has nothing to install.
  • A model file to analyze: copy templates/sst-model.yaml.tmpl and replace the example values (a complete, valid example lives at tests/fixtures/sample-model.yaml).
  • The CLI resolves no files relative to its own location, so commands work from any directory — use paths relative to where you run them.

Limitations

  • The theory is semi-formal and unrefereed; the CLI is a reasoning aid for models you author, not a proof system (see Gotchas).
  • distance and trajectory exit 1 when an id is missing or no path connects two nodes; trajectory enumeration covers simple paths only (no repeated nodes) and terminates on any finite model.
  • The CLI reads and analyzes model files only: it does not observe running agents, measure live systems, or store observations — recording measurements as versioned data stays your responsibility.

Exit Conditions

Stop when the system is modeled as a semantic spacetime — semantic elements, γ(3,4) edges, trajectories, and acceptances recorded — drift/divergence/ absorbing-state findings are written down, and a verification/measurement plan is stated. When diagnosing drift, stop after three non-converging passes and report the evidence instead of re-litigating the same model.

Files (agent-skills)
  • evals
    • evals.json 7.6 KB
      {
        "schema_version": 1,
        "skill_name": "semantic-spacetime",
        "evals": [
          {
            "id": "gamma-3-4-typing-rules",
            "case_set": "release",
            "prompt": "I need to model a knowledge graph with the semantic-spacetime formalism. Explain the 2025 gamma(3,4) representation from the skill's foundations reference: the node meta-types it defines and the four link types with their integer values and meaning.",
            "expected_output": "gamma(3,4) defines exactly three node meta-types: events (temporary, timelike process agents), things (persistent, spacelike realized agents), and concepts (invariant, unrealized potential). It defines exactly four link types: 0 = NEAR, symmetric, covering equivalence, similarity, proximity, and correlation; +/-1 = LEADS TO, directed, covering temporal and causal order such as enables, causes, precedes, and depends on; +/-2 = CONTAINS, directed, covering containment, membership, generalization, and coarse-graining; +/-3 = EXPRESSES, directed, covering attribute, name/value, and property. No additional link types exist in the formalism.",
            "assertions": [
              "response_contains:LEADS TO",
              "response_contains:EXPRESSES",
              "response_contains:concepts",
              "response_not_contains:four node types",
              "activation_evidence_contains:SKILL.md",
              "exit_status:completed"
            ]
          },
          {
            "id": "proper-time-lamport-precedence",
            "case_set": "release",
            "prompt": "How does semantic spacetime define time, and how is causality established between agents? Which distributed-systems researcher is credited with the precedence view of time that semantic spacetime builds on?",
            "expected_output": "Time is proper time: a local unit of time is any local change in a semantic element's state, promises, or configuration, as observed by the agent concerned, and there is no global clock shared across the spacetime. The precedence view of time as a relative transition system goes back to Leslie Lamport, whose 1978 paper on time, clocks, and the ordering of events in a distributed system showed that time can at best be understood as a precedence relation. Causality is cooperative: each adjacency requires both an offer and an acceptance promise on both ends, so space is made up of cooperating nodes and edges.",
            "assertions": [
              "response_contains:proper time",
              "response_contains:Lamport",
              "response_contains:no global clock",
              "response_not_contains:Lorentz invariance",
              "activation_evidence_contains:SKILL.md",
              "exit_status:completed"
            ]
          },
          {
            "id": "absorbing-states-information-leak",
            "case_set": "release",
            "prompt": "My agent system sometimes reaches a state where no agent changes anything anymore and information stops flowing. What does semantic spacetime call these states, what happens to information there, and what does it say about where intent or policy can be injected?",
            "expected_output": "These are absorbing states in a partial graph. Absorbing states are non-conserving of information: a graph process leaks information at them, closely associated with division by zero, which signals a loss of closure and the need for manual injection of remedial information. The boundary information at the leak is where intentionality can enter. In diagnosis, treat them as dead-ends in the gamma(3,4) map where meaning accumulates without propagating, and plan a manual or policy injection at that boundary.",
            "assertions": [
              "response_contains:absorbing states",
              "response_contains:non-conserving",
              "response_contains:intentionality",
              "response_not_contains:fully converged success",
              "activation_evidence_contains:SKILL.md",
              "exit_status:completed"
            ]
          },
          {
            "id": "semantic-drift-diagnosis",
            "case_set": "release",
            "prompt": "Two agents in our system started from the same instructions but now produce incompatible reports: one means 'customer' as the paying account, the other as any user who ever signed up. Diagnose this using semantic spacetime vocabulary, and name the constructs you would use to model meaning changing over time.",
            "expected_output": "This is semantic drift: the shared semantic ground between the two agents has diverged over time. Model each agent's interpretation as a trajectory through semantic spacetime, and measure the semantic distance between the two 'customer' concepts at successive observations to quantify the divergence. Because semantics requires measurement, record observations of each agent's usage at successive local times (spacelike or timelike measurement) rather than assuming the two interpretations still coincide. The two concepts have drifted apart along their trajectories, so re-anchor them and re-confirm the shared ground on a refresh budget.",
            "assertions": [
              "response_contains:semantic drift",
              "response_contains:trajectory",
              "response_contains:semantic distance",
              "response_not_contains:retrain the embedding model",
              "activation_evidence_contains:SKILL.md",
              "exit_status:completed"
            ]
          },
          {
            "id": "metric-versus-semantic-distance",
            "case_set": "release",
            "prompt": "We have two ways to compare how close two concepts are in our system: coordinate similarity and interpretation similarity. Explain both from the semantic spacetime foundations, and give at least two worked examples of the interpretation-similarity kind.",
            "expected_output": "The foundations distinguish metric (quantitative) distance, a measure of coordinate-similarity in position, from semantic (qualitative) distance, a measure of similarity in interpretation. Worked examples of semantic distance from the paper include Hamming distance, hop counts in an associative network, semantic hashing, and sparse distributed representations. The two measures can disagree: two concepts may be close in coordinates yet far in interpretation, so the choice of measure must follow the question being asked.",
            "assertions": [
              "response_contains:coordinate-similarity",
              "response_contains:Hamming",
              "response_contains:hop counts",
              "response_not_contains:Euclidean only",
              "activation_evidence_contains:SKILL.md",
              "exit_status:completed"
            ]
          },
          {
            "id": "pure-embeddings-anti-trigger",
            "case_set": "release",
            "prompt": "We have a vector database with embeddings for all our documents and want to build semantic search over it. There is no temporal or causal structure, just static embeddings and similarity scores. Should we use the semantic-spacetime skill for this, and if not, why not?",
            "expected_output": "No — do not use semantic spacetime for pure vector embeddings or RAG without temporal-causal structure. Semantic spacetime is a discrete graph model of meaning over time; a static embedding index has no proper time, no cooperative-promise causality, and no trajectories to model, so the machinery is overhead rather than insight. Route to the embedding or semantic-search tool's own skill instead. Use semantic spacetime only when there is meaning changing over time, causal-temporal structure, or agents whose shared semantic ground needs to be modeled.",
            "assertions": [
              "response_contains:do not use semantic spacetime",
              "response_contains:without temporal-causal structure",
              "response_contains:shared semantic ground",
              "response_not_contains:model the embedding index as semantic elements",
              "activation_evidence_contains:SKILL.md",
              "exit_status:completed"
            ]
          }
        ]
      }
      
  • references
    • agent-coordination.md 26.3 KB
      # Agent Coordination — SST and Promise Theory for Multi-Agent Systems
      
      **Load this file when you need to design or diagnose coordination between AI agents** — modeling an agent team in SST terms, choosing a coordination substrate, detecting drift between agents' world models, or using SST's machinery to reason about delegation, shared meaning, and temporal blindness. This is the agentic-AI companion to [foundations.md](foundations.md) (formal model and γ(3,4) definitions) and [patterns.md](patterns.md) (named patterns to apply).
      
      **What belongs here:** Burgess's 2025–26 agent papers (arXiv:2604.10505, 2512.19084, 2507.10000), the working software (SSTorytime, MCP-SST), the drift and temporal-blindness literature, spatial-temporal world models and the neuroscience substrate, the MCP/A2A coordination substrate, the honest industry record (including Anthropic's documented delegation failure), and — explicitly labeled `[EXTRAPOLATION]` — five synthesis patterns for using SST as an agent-coordination model. What does **not** belong here: the CFEngine/infrastructure lineage (see [applications-infrastructure.md](applications-infrastructure.md)); the full γ(3,4) formalism (see [foundations.md](foundations.md)); the diagnosis procedure (see [diagnosis-and-debugging.md](diagnosis-and-debugging.md)); and the promise-level machinery of offers, acceptances, and trust (see [promise-theory](../../promise-theory/SKILL.md) — linked, not restated).
      
      **Provenance.** `[VERIFIED]` = confirmed in a fetched primary source (arXiv abstract/full text, official docs, GitHub); `[UNVERIFIED]` = secondary or inferred; `[EXTRAPOLATION]` = this skill's original synthesis, labeled wherever it appears and grounded in verified sources.
      
      ---
      
      ## 1. Burgess's agent-cooperation program: the four load-bearing concepts
      
      *Cooperation in Human and Machine Agents: Promise Theory Considerations* (arXiv:2604.10505, April 2026) is Burgess's explicit "revisit[ing] [of] established principles of agent cooperation, as applied to humans, machines, and their mutual interactions" in the era of AI agents [VERIFIED — arXiv:2604.10505, full text read]. Four concepts carry the paper, all `[VERIFIED]`:
      
      1. **No agent may promise anything on behalf of any agent but itself.** "Autonomy is the base state of any operational entity, human or machine," and the fundamental tenet is that no agent may promise on behalf of any other; attempts to work around this "account for almost all misunderstandings and errors in agent systems" [VERIFIED — arXiv:2604.10505]. This is the coordination-layer statement of promise-theory's autonomy axiom — for the promise-theory treatment, see [promise-theory](../../promise-theory/references/foundations.md).
      2. **The Downstream Principle (Def. 1).** "Agents downstream… have the ultimate power of decision over the outcome." For autonomous agents causality is inverted: the receiver decides what it accepts, and responsibility flows downstream (a client is responsible for its own use of a service) [VERIFIED — arXiv:2604.10505].
      3. **Offer / acceptance with an overlap.** Coordination is voluntary: offer `Ai →+bi Aj` plus acceptance `Aj →−bj Ai`; influence flows only if both are kept, and the propagated content is the overlap (mutual information) `b∩ = bi ∩ bj`. Impositions are "generally ineffective" [VERIFIED — arXiv:2604.10505]. (The offer/acceptance machinery itself is promise-theory territory — link to [promise-theory](../../promise-theory/references/foundations.md), do not re-derive here.)
      4. **Trust as energy.** Trustworthiness is a potential `V`; mistrust drives kinetic sampling at rate `v = √(2(VR−VS−risk)/ρ)` — "trust is really a form of work or energy in the physics sense," whose function is to reduce the overhead of managing a promise dependency [VERIFIED — arXiv:2604.10505; the model is developed in Burgess & Dunbar, *European Economic Review*, 2025].
      
      Two further results matter for coordination design. **Convergent fixed points as the safety pattern**: CFEngine engineered certainty via mathematical fixed points — iterative evaluation `π̂|q⟩ ↦ |qπ⟩` converges on the promised state, and "convergent fixed-point outcomes are the only plausible safeguard in safety critical goals" [VERIFIED — arXiv:2604.10505]. **Swarms vs. teams (Def. 6)**: "a swarm is an ensemble of agents, which are basically similar, and has no leader"; a team has differentiated roles and clear promises — microservices are "a team structure applied to information technology" [VERIFIED — arXiv:2604.10505]. The paper also quantifies proxy chains: fully-promised delivery through N intermediaries costs O(N²), and at minimal trust the promise graph must be complete [VERIFIED — same source]. See [promise-theory's agent-coordination reference](../../promise-theory/references/agent-coordination.md) for the operational mapping of these concepts onto multi-agent engineering practice.
      
      ## 2. γ(3,4) "Attention" in cognitive agents: graphs preserve intentionality
      
      *γ(3,4) 'Attention' in Cognitive Agents: Ontology-Free Knowledge Representations with Promise Theoretic Semantics* (arXiv:2512.19084, December 2025) applies the γ(3,4) representation to cognitive agents without relying on LLMs implicitly [VERIFIED — arXiv:2512.19084]. The load-bearing claim: **"while vectorized data are useful for probabilistic estimation, graphs preserve the intentionality of the source even under data fractionation"** [VERIFIED — arXiv:2512.19084]. The γ(3,4) graph "avoids complex ontologies in favour of classification of features by their roles in semantic processes" and "favours an approach to reasoning under conditions of uncertainty" [VERIFIED — same source]; "appropriate attention to causal boundary conditions may lead to orders of magnitude compression of data required for such context determination" [VERIFIED — same source, Burgess's claim, not independently benchmarked]. The full formal definition (3 node meta-types × 4 link types, the nine typing rules) belongs to [foundations.md](foundations.md) §2 — this file only summarizes and routes.
      
      The intentionality-vs-vectorization contrast is the key design trade for agent systems: embeddings are good for probabilistic estimation but their "interior spaces" have "inscrutable property models"; a typed γ(3,4) graph keeps the *kind* of relation (causal, containment, attribute, similarity) explicit even when data is fragmented across agents [VERIFIED — arXiv:2512.19084; arXiv:2506.07756].
      
      ## 3. Working software: SSTorytime and MCP-SST
      
      The SST line ships real, current software — evidence it is "working software, not vaporware" [VERIFIED — GitHub, fetched 2026-08-12]:
      
      - **SSTorytime** (github.com/markburgess/SSTorytime): "an independent Knowledge Graph, based on Semantic Spacetime… aims to be both easier to use and more powerful than RDF" [VERIFIED — GitHub]. A Go library + Postgres knowledge-graph store with the **N4L note query language** and the `searchN4L`, `pathsolve`, `graph_report` tool set; ~158 stars and active through 2026-08-12 [VERIFIED — GitHub]. It ships an embedded Agent Skills-format skill — a `SKILL.md` under `.claude-plugin/skills/SSTorytime/` with `name`, trigger-style `description` (TRIGGER when the user asks about notes on a subject; SKIP for RDF questions), and `allowed-tools` — a real-world instance of the Agent Skills pattern inside the SST ecosystem [VERIFIED — GitHub].
      - **MCP-SST** (github.com/markburgess/MCP-SST): "an MCP to SST proxy" — a Model Context Protocol server advertising the **`N4Lquery`** tool on `tools/list`, so "an LLM client like Claude Code can drive the SSTorytime knowledge graph in natural language — no hand-crafted JSON-RPC needed" [VERIFIED — GitHub]. The README shows an LLM generating an SVG orbit visualization of the word "brain" from one MCP tool call [VERIFIED — GitHub]. Note the direction: MCP-SST is **agent ↔ tool** (an LLM client querying a graph tool), not agent ↔ agent — it wires SST into modern agentic infrastructure as a tool substrate [VERIFIED — GitHub; MCP spec].
      - Community spinoffs: Simon Frost's Julia `SemanticSpacetime.jl` and `CQL.jl` ("From Causal SQL to Semantic Spacetime via CQL") [VERIFIED — SSTorytime README].
      
      ## 4. Intentionality, co-language, and the three-languages problem
      
      **Intentionality measurement.** *On The Role of Intentionality in Knowledge Representation: Analyzing Scene Context for Cognitive Agents with a Tiny Language Model* (arXiv:2507.10000, July 2025) applies SST as an effective Tiny Language Model: agents can detect "a degree of latent 'intentionality' in data by looking for anomalous multi-scale anomalies and assessing the work done to form them"; **scale separation** sorts content into "intended" vs "ambient context," using spacetime coherence as a measure — "at very low computational cost, without reference to extensive training or reasoning capabilities" [VERIFIED — arXiv:2507.10000]. This is the measurement arm: intentionality is detected, not assumed, by separating scales.
      
      **Co-language / three-languages.** From arXiv:2604.10505: each agent pair has three languages — the sender's, the receiver's, and the exchange *co-language*; translation between them is generically non-unitary, so "agents should expect to misunderstand one another's intentions to some level," and decompressing discourse to approximate unitarity is risky ("saying too much could make things worse"); the key line is "autonomous agents are never certain" [VERIFIED — arXiv:2604.10505]. This is the meaning-negotiation substrate of the whole SST line — and it is promise-theory's three-languages/meaning-negotiation problem. Per the no-duplication rule, the full treatment lives in [promise-theory](../../promise-theory/references/agent-coordination.md); this file uses the concept and links rather than restating the machinery.
      
      **Two labeled synthesis connections** (both `[EXTRAPOLATION]`, grounded in §4's sources): (a) the co-language machinery is the micro-mechanism underneath the shared-semantic-ground synthesis (§8.1) — agents converge on a working overlap `b∩` by negotiating a co-language, and the shared manifold is that overlap made persistent [EXTRAPOLATION — grounded in arXiv:2604.10505]; (b) it is also the diagnosis lens for delegation failure — Anthropic's documented vague-delegation failure (§7) is a small overlap `b∩` between the orchestrator's instruction language and the subagent's comprehension language, exactly what "agents should expect to misunderstand one another's intentions to some level" predicts [EXTRAPOLATION — grounded in arXiv:2604.10505 and Anthropic's engineering post].
      
      ## 5. Drift literature: the empirical evidence closest to SST
      
      Three papers form the empirical core of agent drift — each with its central construct, its metric, and an explicitly labeled SST mapping [paper facts `[VERIFIED]`; mappings `[EXTRAPOLATION]`]:
      
      ### 5.1 Context drift (arXiv:2606.21666, June 2026)
      
      *Hallucination as Context Drift: Synchronization Protocols for Multi-Agent LLM Systems* argues "a significant class of these failures arises… from context drift: the divergence of internal knowledge states between concurrent agents" [VERIFIED — arXiv:2606.21666]. Central constructs: a **Context Divergence Score (CDS)** over "spatial, temporal, and task dimensions," and a **Shared State Verification Protocol (SSVP)** in which "agents periodically exchange compressed state summaries and flag high-divergence conditions before joint reasoning" [VERIFIED — same source]. Key finding: naive full-broadcast sync *increases* hallucination by **34%** (contamination); selective sync reduces it (HR 0.463) with 58% fewer API calls — "refram[ing] hallucination mitigation as a distributed systems problem… context synchronization as a first-class primitive" [VERIFIED — same source]. **SST mapping [EXTRAPOLATION]**: context divergence is divergence between agents' world states in a semantic spacetime; the SSVP is an evaluation loop correcting toward promised (shared) states; contamination from full-broadcast sync is an information-leaking absorbing process — broadcasting unaccepted offers floods every agent with data that leaks its intentionality [EXTRAPOLATION — grounded in arXiv:2606.21666 and the absorbing-states doctrine of arXiv:2506.07756].
      
      ### 5.2 Agent drift (arXiv:2601.04170, January 2026)
      
      *Agent Drift: Quantifying Behavioral Degradation in Multi-Agent LLM Systems* defines drift as "progressive degradation of agent behavior, decision quality, and inter-agent coherence over extended interaction sequences," with three manifestations: **semantic drift** (deviation from original intent), **coordination drift** (breakdown of consensus), and **behavioral drift** (unintended strategies) [VERIFIED — arXiv:2601.04170]. Central metric: the **Agent Stability Index (ASI)** over twelve dimensions, with mitigations including episodic memory consolidation, drift-aware routing, and adaptive behavioral anchoring [VERIFIED — same source]. **SST mapping [EXTRAPOLATION]**: the three drift types are three axes of divergence in semantic spacetime — semantic drift is displacement along the meaning coordinates, coordination drift is inter-agent trajectory separation, behavioral drift is divergence between the promised and actual path [EXTRAPOLATION — grounded in arXiv:2601.04170 and §8.2].
      
      ### 5.3 Drift as bounded equilibrium (arXiv:2510.07777, 2025)
      
      *Drift No More? Context Equilibria in Multi-Turn LLM Interactions* formalizes drift as turn-wise **KL divergence** from a goal-consistent reference, evolving as "a bounded stochastic process with restoring forces"; it finds "stable, noise-limited equilibria rather than runaway degradation," and reminder interventions reliably reduce divergence [VERIFIED — arXiv:2510.07777]. **SST mapping [EXTRAPOLATION]**: bounded equilibria with restoring forces are CFEngine's fixed-point attractors in the semantic domain — the same "ball rolling into a potential well" (§1, applications-infrastructure §2) with reminders acting as reaffirmed acceptance promises [EXTRAPOLATION — grounded in arXiv:2510.07777 and arXiv:2604.10505's fixed-point convergence].
      
      ## 6. Temporal blindness, spatial-temporal world models, and the neuroscience substrate
      
      Four verified results anchor SST's claim that relational knowledge and space/time share substrate:
      
      1. **LLM agents are temporally blind** (arXiv:2510.23853, ACL 2026 Findings): agents "by default assume a stationary context, failing to account for the real-world time elapsed between messages," causing over- or under-use of stale context in tool-use decisions; in a benchmark, **no model achieving a normalized alignment rate better than 65% when given time stamp information** [VERIFIED — arXiv:2510.23853]. (The 65% figure is benchmark-specific; do not over-generalize it into "models are ≤65% at temporal tasks" [UNVERIFIED — generalization beyond the benchmark].)
      2. **LLMs build linear spatial-temporal world models** (Gurnee & Tegmark, *Language Models Represent Space and Time*, arXiv:2310.02207, ICLR 2024): Llama-2 learns linear representations of space and time across scales, robust to prompting, with identifiable "space neurons" and "time neurons"; "modern LLMs… possess basic ingredients of a world model" [VERIFIED — arXiv:2310.02207].
      3. **The Tolman-Eichenbaum Machine** (Whittington et al., *Cell* 2020) unifies spatial and relational memory: the same code supports "where" and "what relates to what" — semantics and space share a substrate [VERIFIED — Cell 2020; Behrens et al., *Neuron* 2018]. Burgess's γ(3,4) claims "human concepts ultimately derive from concepts about space and time" [VERIFIED — arXiv:2506.07756].
      4. **Grid cells furnish a Euclidean metric** (Banino et al., *Nature* 557, 2018): emergent grid-like cells provide agents "with a Euclidean spatial metric and associated vector operations" [VERIFIED — Nature 2018].
      
      The SST tie, labeled per provenance rules: these are independent lines of evidence that space and meaning share machinery — which is exactly what SST formalizes as a graph in which *both* coordinates and semantic relations live on one structure [EXTRAPOLATION — grounded in the four verified results above; the "share substrate" sentence is the neuroscience literature's own framing, the SST identity is this skill's reading].
      
      ## 7. The coordination substrate: MCP, A2A, the unformalized gap, and the honest industry record
      
      - **MCP (Model Context Protocol)** is the agent ↔ tool substrate (Anthropic, Nov 2024): JSON-RPC standardizing **Resources** (context/data), **Prompts** (templated workflows), and **Tools** (functions the model executes), plus client-side Sampling, Roots, and — in the 2026 release-candidate extensions — Tasks and MCP Apps [VERIFIED — MCP spec 2025-11-25; MCP blog 2026-07-28]. "MCP is for agent-to-tool communication" [VERIFIED — MCP spec].
      - **A2A (Agent2Agent Protocol)** is the agent ↔ agent substrate (Google, April 2025; Linux Foundation, June 2025): "an open protocol enabling communication and interoperability between opaque agentic applications" — agents "interact without needing to share internal memory, tools, or proprietary logic" [VERIFIED — a2a-protocol.org]. The **AgentCard** is the capability manifest: a machine-readable JSON document describing an agent's name, skills, endpoints, auth, and transports [VERIFIED — same source]. A2A and MCP are complementary: agent↔agent vs. agent↔tool [VERIFIED — same source].
      - **The unformalized gap**: neither substrate formalizes *what the common semantic ground between two agents is* or how to measure its absence; A2A keeps agents opaque, leaving semantics to per-exchange negotiation [EXTRAPOLATION — grounded in the A2A docs' opacity design and the drift literature's divergence measures]. This is precisely where SST adds value.
      - **Anthropic's documented delegation failure**: the orchestrator-worker engineering post reports that delegation quality depends on detailed task descriptions — without them "subagents misinterpret the task or perform the exact same searches" [VERIFIED — anthropic.com/engineering/multi-agent-research-system]. This is a documented operational failure mode, not a ranking: the research does not support a "#1" ranking of coordination substrates, and none is asserted here [VERIFIED — the research's own searches found no such ranking]. It is also subagents performing duplicated work, which in SST terms is two trajectories toward the same absorbing region without a shared anchor (§8.3, §8.5).
      - **The honest negative result**: no mainstream LLM-agent framework, observability platform, or enterprise multi-agent system uses SST or promise theory as its coordination model [VERIFIED — negative result of this research phase's searches]. The limitation must be stated with it: this is absence of evidence from a bounded search session, not proof of impossibility [VERIFIED — research report §2.8; the caveat is the report's own framing]. SST remains a small, deep-specialist program (Burgess's papers and software, a 2025 self-published book, a 2018 UiO thesis, data-pipeline startups, and 5G interest) [VERIFIED — agentic-ai report §2.8].
      
      ## 8. Synthesis: five patterns for SST as a coordination model
      
      Each pattern below is this skill's **original synthesis — labeled `[EXTRAPOLATION]`** — and each names the verified research it builds on. None is implemented or measured at scale yet; treat them as hypotheses to test (see §9).
      
      ### 8.1 Shared semantic manifold with causal-temporal structure as coordination substrate `[EXTRAPOLATION]`
      
      Give a multi-agent system a shared γ(3,4)-structured representation (nodes = events/things/concepts; links = near/leads-to/contains/expresses) as the coordination substrate, instead of raw token contexts or opaque agent cards. Each agent maintains its own projection of the shared manifold plus its interior state; coordination happens by comparing projections, not by exchanging full context. **Grounding**: γ(3,4) and "graphs preserve the intentionality of the source" (arXiv:2512.19084); A2A's opacity + AgentCard manifests (a2a-protocol.org); GraphRAG's LLM-generated graphs (arXiv:2404.16130); Tolman-Eichenbaum showing spatial and relational memory share machinery (Cell 2020); MCP-SST already demonstrating the plumbing (an LLM querying an SST graph through MCP). **Value-add**: a typed, directional shared representation lets agents agree on *what type of relation* a statement claims — the ambiguity the context-drift literature shows is costly (arXiv:2606.21666).
      
      ### 8.2 Agent trajectories through semantic space as first-class observables `[EXTRAPOLATION]`
      
      Treat every agent as tracing a trajectory through semantic spacetime: a sequence of {position, intent (promise), time} steps. Drift = displacement from the promised path; divergence = inter-agent trajectory separation; convergence = approach to a shared fixed point; absorbing state = task dead-end requiring boundary injection (human input / a new promise). **Grounding**: intent as "an agent's 'direction of travel' in some space of possibility" (arXiv:2604.10505); absorbing states and division-by-zero (arXiv:2506.07756); linear space/time coordinates in LLMs (arXiv:2310.02207); drift and divergence metrics (arXiv:2601.04170, arXiv:2606.21666); observability/evals as trajectory recording (OpenTelemetry GenAI conventions; LangSmith/Langfuse; Anthropic's end-state evals). **Value-add**: gives observability a geometry — "how far am I from my promised state?" and "how far apart are our world models?" with a defined semantic metric, going beyond turn-wise KL divergence (arXiv:2510.07777).
      
      ### 8.3 Promise propagation through semantic spacetime as inter-agent commitments `[EXTRAPOLATION]`
      
      Model delegation as promise propagation: an orchestrator's task description is an offer (+b); a subagent's acceptance is acceptance (−b); the effective task is the overlap `b∩`; the Downstream Principle makes the accepting agent responsible for the outcome; long chains inherit the O(N²) assurance cost; "trust" sets the monitoring/sampling rate. **Grounding**: offer/acceptance, Downstream Principle, proxy chains, contracts as bilateral promise collections, fixed-point convergence (arXiv:2604.10505); Anthropic's finding that vague delegation causes misinterpreted/duplicated work — i.e., low offer/acceptance overlap (multi-agent research system); A2A task delegation (a2a-protocol.org). **Value-add**: a principled diagnosis for a documented failure — the promise overlap was small; the fix is explicit negotiation/expansion of the co-language (§4).
      
      ### 8.4 Semantic-distance metrics for delegation decisions `[EXTRAPOLATION]`
      
      Use semantic distance in the shared manifold (typed, not just cosine) to route tasks: delegate to the agent whose capability region (AgentCard → concepts/things it can act on) is nearest to the task's required concepts; prefer redundant providers for critical promises (Downstream Principle); escalate when distance to a trusted solution exceeds a risk budget. **Grounding**: embeddings/RAG distance machinery (arXiv:2005.11401; Anthropic's research system); Gärdenfors conceptual spaces (convex regions, prototypes); A2A AgentCards as capability manifests; promise-theory redundancy doctrine (arXiv:2604.10505); Context Divergence Score as a proto-metric (arXiv:2606.21666). For the formal definition of semantic distance (metric vs. semantic), see [foundations.md](foundations.md) §8.
      
      ### 8.5 Detection of semantic drift between instruction, implementation, and reality `[EXTRAPOLATION]`
      
      The highest-value diagnostic: track three trajectories — *instruction* (promised state), *implementation* (the agent's actual path), *reality* (observed world state) — and alert when their pairwise semantic distances exceed a threshold, or when an agent's path enters an absorbing state (hallucination, task collapse) that leaks information. **Grounding**: semantic/agent drift (arXiv:2601.04170); context drift and synchronization protocols (arXiv:2606.21666); context equilibria and reminder interventions (arXiv:2510.07777); absorbing states as "boundary information where intentionality can enter" (arXiv:2506.07756); CFEngine fixed-point convergence — "keep applying the map until |qπ⟩" (arXiv:2604.10505); Anthropic's end-state evaluation. **Value-add**: a convergence-based correction loop (re-apply the promise map, re-affirm acceptance, inject boundary information when stuck) — the mechanism CFEngine proved at datacenter scale and the drift literature is rediscovering empirically. This is the diagnostic pattern developed in [patterns.md](patterns.md) and [diagnosis-and-debugging.md](diagnosis-and-debugging.md).
      
      ## 9. Caveats on the synthesis
      
      - SST is a formal theory with a small empirical footprint; the mappings in §8 are interpretive, not yet implemented or measured [EXTRAPOLATION].
      - Burgess labels his own strong claims as hypotheses — "this remains a hypothesis for now" for the claim that four relation types suffice (arXiv:2506.07756) [VERIFIED — arXiv:2506.07756].
      - The theory is semi-formal and deliberately unrefereed; use the synthesis here as a reasoning aid, not a proof system (full disclosure in [foundations.md](foundations.md) §5 and [applications-infrastructure.md](applications-infrastructure.md) §10) [VERIFIED — markburgess.org].
      - Adoption barriers to name honestly: SST's formalism is dense; its tooling (SSTorytime/MCP-SST) is early-stage with a small community; the mainstream stack is embedding/vector-first [VERIFIED — GitHub activity; UNVERIFIED — the market-readiness assessment is opinion].
      
      ## Sources and routing
      
      The full annotated source list with URLs is in [bibliography.md](bibliography.md). Key sources for this file: arXiv:2604.10505, 2512.19084, 2507.10000, 2506.07756, 2606.21666, 2601.04170, 2510.07777, 2510.23853, 2310.02207, 2404.16130, 2005.11401, 1803.10122; Banino et al. (Nature 2018); Whittington et al. (Cell 2020); Behrens et al. (Neuron 2018); a2a-protocol.org; modelcontextprotocol.io; anthropic.com/engineering/multi-agent-research-system; github.com/markburgess/SSTorytime; github.com/markburgess/MCP-SST. For promise-level machinery (offer/acceptance, Downstream Principle, trust calibration), load [promise-theory](../../promise-theory/SKILL.md) and its [agent-coordination](../../promise-theory/references/agent-coordination.md) and [patterns](../../promise-theory/references/patterns.md) references; for the formal γ(3,4) definitions, [foundations.md](foundations.md); for patterns to apply, [patterns.md](patterns.md); for diagnosis, [diagnosis-and-debugging.md](diagnosis-and-debugging.md).
      
    • applications-infrastructure.md 30.5 KB
      # Applications in Infrastructure — CFEngine, Convergence, and the Descendant Ecosystem
      
      **Load this file when you need the empirical record behind Semantic Spacetime (SST):** what the convergence/promise line actually did in thirty years of real infrastructure — CFEngine's mechanism set, the declarative-IaC / Kubernetes / GitOps / IBN / MAPE-K descendants that each inherited a piece of it, the time-and-space machinery (Lamport causality, event sourcing, bi-temporal records, versioned data namespaces) SST's line implies, and the distilled, citable lessons for designing convergent systems.
      
      **What belongs here:** the infrastructure history and its lessons — CFEngine, the descendant ecosystem, "dynamics always trumps semantics," SLOs as semantic contracts, promise-keeping-as-data, and the record-of-time layer (event sourcing, bi-temporal axes, arXiv:2204.00470). What does **not** belong here: the formal SST model itself (definitions, γ(3,4), proper time, the promise substrate — see [foundations.md](foundations.md)); agent-team coordination and the agentic-AI literature (see [agent-coordination.md](agent-coordination.md)); named design patterns (see [patterns.md](patterns.md)); and the diagnosis procedure (see [diagnosis-and-debugging.md](diagnosis-and-debugging.md)). Where promise-level machinery is involved (promise, offer/acceptance, assessment, convergence mechanics), this file links to [promise-theory](../../promise-theory/SKILL.md) rather than re-teaching it.
      
      **Provenance.** Every factual claim below carries exactly one marker: `[VERIFIED]` (confirmed in a primary source fetched during the research phase; source named), `[UNVERIFIED]` (secondary, opinion, or inferred; reason named), or `EXTRAPOLATION` (this skill's own synthesis, explicitly labeled). Do not drop markers when reusing this content.
      
      ---
      
      ## 1. The lineage at a glance
      
      The SST line has a concrete, traceable industrial ancestor. **CFEngine was written by Mark Burgess in 1993** at the University of Oslo, initially to automate workstation management [VERIFIED — CFEngine Wikipedia; InfoQ CFEngine article]. Its core ideas — convergence to a desired end-state, classes, promises as the unit of policy, the immunity model of self-repair, and compliance measured by the repair loop itself — are the same ideas SST later formalizes as a graph model of meaning over time. **The interpretive framing that "CFEngine is the working prototype / reference implementation of the SST/promise-theory line" is this research phase's own synthesis, not a claim in any cited source** [EXTRAPOLATION — research synthesis, infrastructure-applications report §1]. It must not be read as "CFEngine was built from SST": CFEngine (1993) predates Promise Theory (~2004–05, first presented at DSOM 2005) and SST (2014, arXiv:1411.5563) by a decade or more [VERIFIED — CFEngine Wikipedia; academic foundations]. What the lineage claim means is the reverse: SST formalizes, with time and semantics added, the machinery CFEngine already ran in production.
      
      The mechanism set itself is well documented and tagged `[VERIFIED]` throughout §2. The industry that followed inherited the machinery piecemeal (§5), and the recurring gap is that the *record* of promise-keeping was never stored as data (§6) — exactly the semantic-time capability SST's versioned record axis would supply (§8).
      
      ## 2. CFEngine: the mechanism set (all `[VERIFIED]`)
      
      Sources for this section: Burgess's own *A Tiny Overview of CFEngine: Convergent Maintenance Agent* (markburgess.org/papers/tiny_intro.pdf), the InfoQ article "CFEngine's Decentralized Approach to Configuration Management" (2014), and the CFEngine Wikipedia article. All quotes below are from those fetched sources.
      
      1. **Convergence to a desired end-state (fixed point).** A convergent operator satisfies `O(q0) = q0` and `O^2 = O` at the desired endpoint; "idempotence requires only O^2 = O, while convergence is relative to a specific policy state q0" [VERIFIED — Burgess, *A Tiny Overview*]. Convergent semantics behave "like a ball rolling into a potential well"; once converged, agent action desists [VERIFIED — same source]. The Wikipedia framing adds that convergence is "now often inaccurately just called idempotence" [VERIFIED — Burgess Wikipedia].
      2. **Statistical convergence, never exact.** "A complete specification of policy determines an approximate configuration of a software system only approximately over persistent times. There are fundamental limits to the tolerances a system can satisfy with respect to policy compliance in a stochastic environment" [VERIFIED — *A Tiny Overview*]. Desired state is a fixed-point *attractor* in a stochastic environment, not a guarantee; the approach rate is set by "the ratio of the frequency of environmental change to the rate of CFEngine execution" [VERIFIED — *A Tiny Overview*; *On the theory of system administration* via Wikipedia].
      3. **Classes.** Promises are conditioned on *classes* — OS type, time, user-defined contexts — so the same policy text applies different promises under different conditions [VERIFIED — InfoQ CFEngine article; CFEngine Wikipedia]. (SST's later framing calls coarse-grained context flags "classes" too, in Burgess's knowledge-graph essays [VERIFIED — Medium, *The Role of Intent and Context*, 2025].)
      4. **Promises as the unit of policy.** CFEngine 3's documentation is explicit that promises are the central concept and everything else is an abstraction for declaring them; agents on every host pull and cache policy and decide locally whether to keep it [VERIFIED — CFEngine 3 docs via InfoQ]. Policy is federated: "an agent cannot be forced into submission by an external authority" [VERIFIED — InfoQ].
      5. **The immunity model of self-repair.** Health = policy compliance, deviation = sickness, and repair is modeled as error correction over a noisy channel in Shannon's sense — the "Computer Immunology" (1998) manifesto for self-healing systems [VERIFIED — LISA98 *Computer Immunology*; *A Tiny Overview*]. Independent convergent operations commute: "multiple orthogonal, convergent operations will always lead to the correct configuration, no matter which part of the configuration is incorrect, or in what order things occur"; failed steps can be repeated later [VERIFIED — *A Tiny Overview*].
      6. **The default 5-minute repair loop.** Agents "verify whether these promises are kept (and usually takes measures to keep them) every five minutes, by default" [VERIFIED — InfoQ CFEngine article].
      7. **Compliance without independent monitoring.** CFEngine yields "immediate and continuous measurements of compliance based on a documented model of intent, without the need for independent monitoring" [VERIFIED — InfoQ CFEngine article].
      
      Two further CFEngine-era lessons matter for SST: (a) the "congruence" alternative — destroy-and-rebuild, proposed by S. Traugott — versus convergent repair; Burgess's rebuttal was that "only the convergent approach can be used for realtime maintenance" [VERIFIED — *A Tiny Overview*]. (b) Burgess's 2014 stance that "immutability" is "politics, not science," preferring "disposable computing — throw away a broken process rather than trying to fix it" [VERIFIED — InfoQ, *In Search of Certainty* review/interview]. Both debates re-ran later in containers and immutable infrastructure.
      
      ## 3. "Dynamics always trumps semantics" — meaning requires measurement
      
      The single most load-bearing lesson for SST practitioners: **"It is not possible to reason about semantics without taking into account the underlying dynamics"** [VERIFIED — InfoQ, *In Search of Certainty* book review and interview, 2014]. Meaning cannot be asserted before the dynamics are measured. The corollary is that measurement must happen **at the right scale**: "the ability to distinguish and separate scales is closely allied with our notions of simplicity," and different scales yield contradictory measurements — a system can look healthy at the requests-per-second scale while its disks are filling and its semantics (as users experience them) are degrading [VERIFIED — same source].
      
      This is the operational form of SST's measurement duality (spacelike ensemble vs. timelike cognitive measurement) developed in [foundations.md](foundations.md) §7; the infrastructure phrase is the earlier, engineering-tested statement of the same rule. Practically: any SST analysis that reasons about meaning without an explicit measurement plan (what to observe, at what scale, how often) is guesswork.
      
      ## 4. SST's time lineage: Lamport causality, event sourcing, bi-temporal records
      
      SST descends from **Lamport's causal time**. Lamport's 1978 *"Time, Clocks, and the Ordering of Events in a Distributed System"* (CACM 21(7):558–565) establishes that "there is no invariant total ordering of events in space-time… there is only a partial order in which an event e1 precedes an event e2 iff e1 can causally affect e2" [VERIFIED — Lamport via Microsoft Research]. Burgess explicitly places SST in this line: "The view of time as a relative transition system goes back to the work of Leslie Lamport… time can at best be understood as a precedence relation, in a discrete spacetime context" [VERIFIED — markburgess.org, *Semantic Spacetime — What is it?*]. Wall-clock time is not the ordering primitive; causality is. This section is SST's *semantic-time* territory, not a promise-theory restatement: the promise machinery of offers and acceptances lives in [promise-theory](../../promise-theory/references/foundations.md).
      
      Two further record-of-time mechanisms belong to the same lineage:
      
      - **Event sourcing** (Fowler, 2005): "capture all changes to an application state as a sequence of events" — enabling complete rebuild by replay, temporal query of state at any point in time, and event correction [VERIFIED — martinfowler.com, *Event Sourcing*]. Fowler's documented caveat: external systems "don't know the difference between real processing and replays," so gateways must be replay-aware and external queries must be logged; temporal corrections lead to "bi-temporal behavior" and "this stuff can get very messy, don't go down this path unless you really need to" [VERIFIED — same source]. Event logs are the honest time dimension of state: semantic correctness over time (what did we *believe* when) requires recording both fact and belief.
      - **Bi-temporal databases**: a temporal database tracks **valid time** (when a fact is true in the real world — the world axis) and **transaction time** (when it was recorded — the record axis), optionally decision time; "historical information… is provided by the valid time. Rollback… is provided by the transaction time" [VERIFIED — Temporal database Wikipedia]. The two answers can differ: "the database may have been altered since 1992" [VERIFIED — same source]. SQL:2011 adopted a reduced version (application-time period tables, system-versioned tables); the richer TSQL2 proposal was killed in committee after criticism by Date and Darwen [VERIFIED — same source].
      
      The lesson for SST: any "state over time" system must separate the *world* axis from the *record* axis. Conflating them is the classic audit failure, and — as §6 and §8 show — it is precisely the axis SST's promise-keeping capability supplies.
      
      ## 5. The descendant ecosystem: who inherited what, and the gap each leaves
      
      Each descendant below is stated with its inheritance mechanism and its gap/lesson, all from the fetched sources named.
      
      ### 5.1 Declarative IaC — Terraform, Ansible, Chef, Puppet, Nix
      
      - **Terraform**: declarative config; `plan` diffs desired configuration against actual state; `apply` executes; drift is "when the real-world state of your infrastructure differs from the state defined in your configuration" [VERIFIED — HashiCorp, "Detecting and Managing Drift with Terraform"]. The state file maps config to real resources; `refresh` reconciles before every plan/apply; lifecycle flags tune reconciliation [VERIFIED — same source]. **Gap**: Terraform is invoked, not a loop — "Terraform cannot detect drift of resources… that are not managed using Terraform" [VERIFIED — HashiCorp]. The sharper 2026 reading — "`terraform plan` diffs one file against another file. It does not observe your infrastructure. Between applies, Terraform has no awareness" — is opinion, marked [UNVERIFIED — webframp.com, 2026]. A state file is memory of a past action, not perception; drift accumulates until a human runs the tool.
      - **Ansible**: idempotent modules — "most Ansible modules check whether the desired final state has already been achieved and exit without performing any actions if that state has been achieved" [VERIFIED — Ansible docs]; control node pushes tasks over SSH; `--check` previews; `ansible-pull` "inverts the Ansible architecture so that nodes check in to a central location instead of you pushing configuration out to them" [VERIFIED — Ansible docs]. **Gap**: default mode is push/command-and-control; the target has no daemon, no local reasoning, no self-assessment. Module-level idempotency is a local, weaker cousin of convergence; without scheduled local evaluation, drift between runs is invisible — Ansible's own docs warn "not all playbooks and not all modules behave this way" [VERIFIED — Ansible docs].
      - **Chef and Puppet**: pull-based agents on a schedule, converging toward declared state and reporting back [UNVERIFIED — the ~30-minute Chef default and "blind outside declarations" details come from the webframp analysis, not from official docs fetched in this research]. The agent-observation-scoped-to-declaration point — "if you did not write a resource for it, the agent does not see it" — is [UNVERIFIED — webframp.com, 2026; consistent with verified pull-based mechanisms].
      - **Nix / NixOS**: "purely functional package manager" — builds without side effects, immutable content-addressed store, atomic upgrades and rollbacks; NixOS builds "the entire operating system… from a description in a purely functional build language" [VERIFIED — nixos.org]. **Gap**: deterministic builds ≠ deterministic running state — even NixOS exempts "mutable state (such as the stuff that lives in /var)" [VERIFIED — nixos.org]. Nix realizes the end-state *purity* extreme — eliminating stochastic repair by making state immutable and rebuildable — closer to Traugott's destroy-and-rebuild "congruence" than to CFEngine's convergent repair [interpretive framing].
      
      ### 5.2 Kubernetes: reconciliation controllers as institutionalized convergence
      
      Kubernetes controllers are "control loops" that watch state and "try to move the current cluster state closer to the desired state"; the thermostat is the canonical example [VERIFIED — kubernetes.io/docs/concepts/architecture/controller]. The inheritance mechanism is the same fixed-point metaphor as CFEngine's ball-in-potential-well: `spec` = desired state, controller loop = the map applied repeatedly. **The gap doctrine is explicit**: "potentially, your cluster never reaches a stable state. As long as the controllers… are running and able to make useful changes, it doesn't matter" [VERIFIED — kubernetes.io]. "Controllers can fail, so Kubernetes is designed to allow for that" [VERIFIED — same source]. Lesson: design simple, separable, fail-tolerant reconcilers and assume convergence never "finishes."
      
      ### 5.3 GitOps: the closest industry instantiation of "the promise as data"
      
      GitOps (coined by Weaveworks, 2017) per CNCF: (1) the whole system is declarative; (2) the canonical desired state is versioned in Git; (3) changes apply automatically; (4) software agents continuously reconcile and alert when reality diverges — "software agents also help ensure that the whole system is self-healing" [VERIFIED — CNCF, *GitOps 101*]. **Interpretive framing**: GitOps is the closest the industry built to *storing the promise as data* — intended state durable, versioned, diffable, time-ordered (git history is a temporal log of intent), with the reconciling agent as promise-keeper [interpretive — labeled, not verified as a claim in the source]. "You won't achieve immediate deployment or reconciliation until you achieve a new canonical state"; the repo is the contract and drift becomes a first-class, auditable condition [VERIFIED — CNCF]. Mark the "promise as data" reading [UNVERIFIED] as an interpretive gloss unless separately sourced.
      
      ### 5.4 Intent-Based Networking: intent as a productized semantic layer
      
      IBN (RFC 9315 lineage) defines intent as "a high-level, declarative statement" of a "desired operational or business goal without specifying the detailed method of implementation," and runs a closed loop **translation → activation → assurance → optimization** [VERIFIED — WashU IBN survey citing Zeydan & Turk 2020 and RFC 9315]. Architecture: three layers — Business, Intent (Knowledge/ontology + Agent + Data), Network (telemetry closing the loop); the Knowledge module "includes ontologies and models for understanding semantics" [VERIFIED — same survey]. **The hard problem is assurance** — "continuously validating whether the actual network behavior satisfies the intent" — and open challenges include intent-interpretation reliability, multi-domain coordination, and explainability: "today's IBN systems sometimes act like 'black boxes'" [VERIFIED — WashU survey]. Lesson: intent is only as good as its assurance loop; the semantic layer is where intent is won or lost.
      
      ### 5.5 MAPE-K and operators: the autonomic loop codified
      
      IBM coined "autonomic computing" in 2001; Kephart & Chess (2003), *The Vision of Autonomic Computing* (IEEE Computer 36:41–50), define an autonomic manager + managed resource running the **MAPE loop — Monitor, Analyze, Plan, Execute — with shared Knowledge (MAPE-K)** [VERIFIED — Kephart & Chess 2003 via ScienceDirect; researchr]. The IBM blueprint's component-level details were not fetchable in the research phase and are [UNVERIFIED — IBM blueprint bibliography entry only]. CFEngine Wikipedia asserts *Computer Immunology* (1998) "laid out a manifesto for creating self-healing systems, reiterated a few years later by IBM in their form of Autonomic Computing" [VERIFIED — CFEngine Wikipedia]; Burgess & Couch's 2006 paper is literally titled *Autonomic Computing Approximated by Fixed-Point Promises* [VERIFIED — archive.org copy]. MAPE-K and CFEngine's converge-and-repair loop are independent formulations of the same monitor → compare → act cycle. **Lesson**: the "K" (shared Knowledge) is what makes the loop semantic; without a durable knowledge model, self-* systems repair without understanding [interpretive].
      
      ## 6. The most-cited gap: promise-keeping was never stored as data
      
      The recurring critique of the whole CFEngine-to-IBN line: systems answered "is this promise kept *right now*?" but never stored promise-keeping as **queryable data** — no record of what the configuration looked like last Tuesday, how often a promise was repaired, or which hosts drifted together [UNVERIFIED — webframp.com, *The Promise None of Them Kept* (2026), an opinionated practitioner analysis; the current-state compliance behavior it describes is consistent with the verified CFEngine mechanisms of §2]. The framing: CFEngine's assessment "is a verdict rather than a record" [UNVERIFIED — webframp.com, 2026]. This claim is attributed to the CFEngine lineage — the opinion source analyzes CFEngine and its successors, not SST — and must not be presented as a verified fact or as an SST discovery.
      
      In SST terms, this is the missing **record axis** of §4 (valid vs. transaction time) applied to promises: the world axis is the state of the system, the record axis is the versioned history of what was promised, what was measured, and what was repaired. SST's semantic-time capability — a versioned record of meaning over time — is what would close the gap, and §8 names the concrete machinery.
      
      ## 7. SLOs as working semantic contracts
      
      SLOs are the operational form of "meaning over time": a promise about future measured behavior with an explicit time horizon, sitting inside a control loop [interpretive framing; the facts below are verified]. Terminology (Google SRE book, Ch. 4): an **SLI** is "a carefully defined quantitative measure"; an **SLO** is "a target value or range of values… measured by an SLI"; an **SLA** is an agreement "with consequences." The mnemonic: "what happens if the SLOs aren't met?" [VERIFIED — sre.google]. **SLOs sit inside control loops**: "SLIs and SLOs are crucial elements in the control loops used to manage systems: 1. Monitor and measure… 2. Compare… 3. …figure out what needs to happen… 4. Take that action" [VERIFIED — sre.google] — the same loop CFEngine's agent runs and MAPE-K codifies (§2, §5.5), with meaning made explicit and quantifiable. **Error budgets**: "it is better to allow an error budget — a rate at which the SLOs can be missed — and track that"; "an error budget is just an SLO for meeting other SLOs" [VERIFIED — sre.google]. Selection lessons (depth evidence): "keep it simple," "avoid absolutes," "have as few SLOs as possible," "perfection can wait," and "don't overachieve" — Chubby introduced planned outages because it was *too* available [VERIFIED — sre.google].
      
      SST's reading: an SLO is a scalar promise with a measurement loop and a time horizon — a minimal, production-proven instance of "meaning over time." When you model a system in SST terms, your observations and drift checks are, operationally, SLOs over semantic state. For the promise-level machinery (offer/acceptance, assessment, breach), link to [promise-theory](../../promise-theory/references/foundations.md) rather than restating it here.
      
      ## 8. Time and space in systems: the versioned-record-axis machinery
      
      The record axis of §4 needs concrete machinery. The relevant lineage, all verified:
      
      - **Lamport clocks** — logical clocks imposing a total order consistent with the causal partial order, the distributed-systems backbone for "no global clock" [VERIFIED — Lamport 1978 via Microsoft Research].
      - **Distributed tracing** — OpenTelemetry spans form a parent-child hierarchy; span links "exist so that you can associate one span with one or more spans, implying a causal relationship" [VERIFIED — opentelemetry.io]. Causality must be *carried in context* (trace-context propagation), not reconstructed from timestamps [VERIFIED — same source]. This is Lamport's partial-order causality made observable — the closest working analogue to SST's "timeline cognitive semantics" [interpretive].
      - **Event sourcing** — §4, state as a function of time with replay caveats [VERIFIED — Fowler 2005].
      - **Bi-temporal databases** — §4, world axis vs. record axis [VERIFIED — Temporal database Wikipedia].
      
      **Continuous Integration of Data Histories into Consistent Namespaces** (Burgess & Gerlits, 2022, arXiv:2204.00470) is SST's own temporal-consistency scheme for data pipelines — the concrete machinery behind the record axis [VERIFIED — arXiv:2204.00470; academic-foundations report §2.2 S9]. The mechanism: "we thus establish an invariant global ordering from a spanning tree over all shards… this forms a versioned coordinate system (or versioned namespace) with consistent semantics" [VERIFIED — arXiv:2204.00470]. In other words: versioned coordinates over distributed data history give every record a stable address in time, so "what was the meaning at time T" is a query, not archaeology. This is exactly the capability VAL-APPS-006's gap (promise-keeping never stored as data) requires: a promise ledger is a namespace of versioned data histories over promises, observations, and repairs [EXTRAPOLATION — applying the versioned-coordinate scheme to promise-keeping; the paper's own framing is about data pipelines]. The Aljabr/Dianemo smart-data-pipeline lineage behind the paper is documented [VERIFIED — Wikipedia citation + arXiv reference].
      
      ## 9. Distilled lessons — citable
      
      Each lesson below carries its marker and its named source; use these as the citation spine when a design conversation needs the empirical record.
      
      1. **Convergence ≠ idempotence; convergence is relative to a declared policy state.** "Idempotence requires only O^2 = O, while convergence is relative to a specific policy state q0" (`O(q0)=q0`). [VERIFIED — Burgess, *A Tiny Overview of CFEngine*; Burgess Wikipedia]
      2. **Desired-state convergence is statistical, never exact, in a stochastic environment.** "A complete specification of policy determines an approximate configuration… only approximately over persistent times." [VERIFIED — *A Tiny Overview*; *On the theory of system administration* via Wikipedia]
      3. **Make convergence order-free where possible; repeat failed steps.** "Multiple orthogonal, convergent operations will always lead to the correct configuration, no matter which part… is incorrect, or in what order things occur." [VERIFIED — *A Tiny Overview*]
      4. **Converge for realtime maintenance; recreate if you can afford downtime.** Traugott's "congruence" is the philosophical opposite; "only the convergent approach can be used for realtime maintenance." [VERIFIED — *A Tiny Overview*]
      5. **Continuous promise evaluation yields compliance measurement for free.** "Immediate and continuous measurements of compliance based on a documented model of intent, without the need for independent monitoring." [VERIFIED — InfoQ CFEngine article, vendor-authored but primary]
      6. **Autonomy and weak coupling survive scale; strong coupling transmits failure.** Centralization is "the first idea people come back to" but propagates Byzantine failures. [VERIFIED — InfoQ CFEngine article; Burgess interview]
      7. **Autonomy without memory = a verdict, not a record.** CFEngine could say whether a promise is kept *now*, not what changed when or how often it was repaired, because "promise-keeping was never stored as data." [UNVERIFIED — webframp.com 2026, opinion source]
      8. **Dynamics trumps semantics — measure first, then attach meaning.** "It is not possible to reason about semantics without taking into account the underlying dynamics"; scale changes what you can conclude (steady RPS vs. full disks). [VERIFIED — InfoQ, *In Search of Certainty* review/interview]
      9. **Model time as causality, not wall clocks.** Lamport: "only a partial order in which an event e1 precedes an event e2 iff e1 can causally affect e2." OpenTelemetry span links "implying a causal relationship" are the productionized form. [VERIFIED — Lamport via Microsoft Research; opentelemetry.io]
      10. **Separate "world" time from "record" time in any state-over-time store.** Bi-temporal modeling (valid vs. transaction) is what makes audit and rollback coherent; SQL:2011 supports a reduced form. [VERIFIED — Temporal database Wikipedia]
      11. **Event sourcing gives time-travel state; gate external side effects.** Replays must not re-fire external messages; external query answers must be recorded. [VERIFIED — Fowler, *Event Sourcing*]
      12. **Reconciliation loops should assume they never "finish."** "Potentially, your cluster never reaches a stable state. As long as the controllers… are running and able to make useful changes, it doesn't matter." [VERIFIED — kubernetes.io]
      13. **If a declarative tool is invoked rather than looping, drift accumulates silently.** Terraform detects drift only when a human runs `plan`/`refresh`; it "cannot detect drift of resources… not managed using Terraform." [VERIFIED — HashiCorp] (the "blind between applies" framing is [UNVERIFIED — webframp.com])
      14. **Version your intended state; that makes drift auditable and recovery reproducible.** GitOps's canonical-desired-state-in-git, enforced by converging agents, is the industry's best institutionalization of "the promise as data." [VERIFIED — CNCF GitOps 101; the "promise as data" gloss is interpretive]
      15. **Intent is only as good as its assurance loop.** IBN's closed loop (translation → activation → assurance → optimization) and the finding that today's IBN systems "act like black boxes." [VERIFIED — WashU IBN survey]
      16. **SLOs are the operational form of semantic contracts.** They sit inside control loops (monitor → compare → act), carry error budgets, and their selection rules ("few SLOs," "avoid absolutes," "perfection can wait") are the art of turning meaning into measurement. [VERIFIED — Google SRE book Ch. 4]
      17. **Causality must be carried in context.** Distributed causality cannot be reconstructed later from timestamps alone. [VERIFIED — opentelemetry.io]
      18. **Versioned coordinates over data history make "meaning at time T" a query.** "We thus establish an invariant global ordering from a spanning tree over all shards… this forms a versioned coordinate system (or versioned namespace) with consistent semantics." [VERIFIED — Burgess & Gerlits, arXiv:2204.00470]
      
      ## 10. Status and limits
      
      Two honest disclosures apply to everything in this file. First, **the theory behind it is semi-formal and deliberately unrefereed**: Burgess published the SST series as self-published notes ("I have no interest or intention of seeking to publish any of this work beyond making these notes available seeking trusted review"), with some proofs left to the reader [VERIFIED — markburgess.org]. Use the SST lens here as a reasoning aid, not a proof system. Second, several market-level claims in the descendant literature could not be verified: CFEngine's market decline has no authoritative post-mortem (only Burgess's own "reached its limits as a tool in the mid 2000s" [VERIFIED — InfoQ interview] and the 2017 Northern.tech rename [VERIFIED — Wikipedia]); Chef/Puppet defaults and the CFEngine-vs-successor comparisons rest on opinion or vendor sources [UNVERIFIED]. Where a claim in this file is labeled `[UNVERIFIED]` or `[interpretive]`, treat it as a hypothesis to test, not a fact.
      
      ## Sources and routing
      
      The full annotated source list with URLs is in [bibliography.md](bibliography.md). Key sources for this file: InfoQ CFEngine article and *In Search of Certainty* review/interview; Burgess, *A Tiny Overview of CFEngine* (tiny_intro.pdf); markburgess.org (*Semantic Spacetime — What is it?*); Lamport 1978 (Microsoft Research); martinfowler.com (*Event Sourcing*); Temporal database Wikipedia; kubernetes.io (Controllers); HashiCorp (drift); Ansible docs; nixos.org (*How Nix Works*); CNCF (*GitOps 101*); WashU IBN survey; RFC 9315; Kephart & Chess 2003; sre.google (SLO chapter); opentelemetry.io (Traces); webframp.com (2026, opinion); Burgess & Gerlits, arXiv:2204.00470. For the promise vocabulary used in §2–§7 (promise, offer/acceptance, assessment, breach), load [promise-theory](../../promise-theory/SKILL.md) and its [applications-infrastructure reference](../../promise-theory/references/applications-infrastructure.md); for the formal SST model, [foundations.md](foundations.md); for patterns, [patterns.md](patterns.md).
      
    • bibliography.md 14.2 KB
      # Bibliography — Primary Sources for Semantic Spacetime
      
      **Load this file when you need to find or verify a source** — which paper says
      X, or where a claim in [foundations.md](foundations.md) or
      [glossary.md](glossary.md) comes from. Every entry below carries a URL that was
      checked for reachability during the research phase (2026-08-12); the
      `[VERIFIED]`/`[UNVERIFIED]` markers describe the source's verification level as
      used in [foundations.md](foundations.md). No entry here is fabricated: each
      appears in the research corpus's source lists. For the promise-theory substrate
      sources, cross-reference the promise-theory skill's own bibliography.
      
      ---
      
      ## 1. The Semantic Spacetime series (Burgess)
      
      - Burgess, M. *Spacetimes with Semantics* (2014). arXiv:1411.5563 [cs.MA].
        Part I: "From Einstein to Milner." Defines the agenda: relationships between
        objects constitute space; their change is time; observer semantics are
        integral to spacetime. [VERIFIED]
        URL: https://arxiv.org/abs/1411.5563
      - Burgess, M. *Spacetimes with Semantics (II): Scaling of agency, semantics,
        and tenancy* (2015). arXiv:1505.01716 [cs.MA]. Part II: how agency scales via
        super-agents/sub-spaces; scalar vs. vector promises; occupancy and tenancy.
        [VERIFIED]
        URL: https://arxiv.org/abs/1505.01716
      - Burgess, M. *Spacetimes with Semantics (III): The Structure of Functional
        Knowledge Representation and Artificial Reasoning* (2016, rev. 2017).
        arXiv:1608.02193 [cs.AI]. Part III (122 pages), the most formal document:
        Definitions 1-9, Lemmas 1-3, the four irreducible associations, the
        learning/knowledge formalism. [VERIFIED]
        URL: https://arxiv.org/abs/1608.02193
        Full text: https://arxiv.org/html/1608.02193v4
      - Burgess, M. *On the scaling of functional spaces, from smart cities to cloud
        computing* (2016). arXiv:1602.06091 [cs.CY]. The "functional space" reading
        of SST applied to the empirically observed power-law scaling of cities.
        [VERIFIED]
        URL: https://arxiv.org/abs/1602.06091
      - Burgess, M. *A Spacetime Approach to Generalized Cognitive Reasoning in
        Multi-scale Learning* (2017). arXiv:1702.04638 [cs.AI]. A hybrid
        reasoning/pattern-recognition architecture as the ML instantiation of SST
        reasoning. [VERIFIED]
        URL: https://arxiv.org/abs/1702.04638
      - Burgess, M. *Testing the Quantitative Spacetime Hypothesis using Artificial
        Narrative Comprehension (I): Bootstrapping Meaning from Episodic Narrative
        viewed as a Feature Landscape* (2020). arXiv:2010.08126 [cs.AI]. SST's
        empirical arm: parsing narrative via measurable size/time cues as an event
        "landscape"/interferometry; concepts as process invariants. [VERIFIED]
        URL: https://arxiv.org/abs/2010.08126
      - Burgess, M. *Testing the Quantitative Spacetime Hypothesis using Artificial
        Narrative Comprehension (II): Establishing the Geometry of Invariant
        Concepts, Themes, and Namespaces* (2020). arXiv:2010.08125 [cs.AI]. Part II:
        reconstructing concepts via multiscale interferometry based on the four
        fundamental spacetime relationships. [VERIFIED]
        URL: https://arxiv.org/abs/2010.08125
      - Burgess, M.; Gerlits, A. *Continuous Integration of Data Histories into
        Consistent Namespaces* (2022). arXiv:2204.00470 [cs.DC]. Versioned
        coordinates / namespaces for data pipelines — SST's temporal-consistency
        scheme. [VERIFIED]
        URL: https://arxiv.org/abs/2204.00470
      - Burgess, M. *Agent Semantics, Semantic Spacetime, and Graphical Reasoning*
        (2025). arXiv:2506.07756 [cs.AI]. The current formal statement: the γ(3,4)
        representation (3 node meta-types × 4 link types), the nine typing design
        rules, absorbing states as information leaks, causal-set kinship. [VERIFIED]
        URL: https://arxiv.org/abs/2506.07756
        Full text: https://arxiv.org/html/2506.07756v2
      - Burgess, M. *On The Role of Intentionality in Knowledge Representation:
        Analyzing Scene Context for Cognitive Agents with a Tiny Language Model*
        (2025). arXiv:2507.10000 [cs.AI]. Intentionality in data via scale
        separation. [VERIFIED]
        URL: https://arxiv.org/abs/2507.10000
      - Burgess, M. *γ(3,4) 'Attention' in Cognitive Agents: Ontology-Free Knowledge
        Representations With Promise Theoretic Semantics* (2025). arXiv:2512.19084
        [cs.AI]. SST as a bridge between vectorized ML and knowledge graphs without
        relying on language models implicitly. [VERIFIED]
        URL: https://arxiv.org/abs/2512.19084
      
      ## 2. Author's web project pages and essays
      
      - Burgess, M. *Semantic Spacetimes* (project page). "A semantic spacetime is a
        discrete graph, which evolves, and whose properties vary from point to
        point." [VERIFIED]
        URL: http://markburgess.org/spacetime.html
      - Burgess, M. *Semantic Spacetime — What is it?* The best short primary
        exposition; source of the "not quantum gravity," Lamport-relativity,
        spacelike/timelike measurement, and cooperative-causality material.
        [VERIFIED]
        URL: http://markburgess.org/semantic_spacetime.html
      - Burgess, M. *The Semantic Spacetime Project: Bringing technology and physics
        together* (2016 essay). "For inspiration, not for refereed publication."
        [VERIFIED]
        URL: http://markburgess.org/blog_spacetime3.html
      - Burgess, M. *Semantics of Spacetime and Cognitive Processes* (Kavli salon
        write-up, Medium, 2022). SST as a formal bridge for neuroscience findings.
        [VERIFIED]
        URL: https://medium.com/@mark-burgess-oslo-mb/semantics-of-spacetime-and-cognitive-processes-d39214e9c44a
      - Burgess, M. *Semantic Spacetime 1: The Shape of Knowledge* (Medium, 2025).
        [VERIFIED — existence and framing; URL from search index]
        URL: https://mark-burgess-oslo-mb.medium.com/semantic-spacetime-1-the-shape-of-knowledge-86daced424a5
      - Burgess, M. *Universal Data Analytics as Semantic Spacetime* (Medium series,
        2022). [VERIFIED — existence and framing; URL from search index]
        URL: https://mark-burgess-oslo-mb.medium.com/universal-data-analytics-as-semantic-spacetime-dee7a76661c2
      - Burgess, M. *Motion of the Third Kind I & II* (ResearchGate, 2021-22).
        Existence verified; full texts not fetched. [UNVERIFIED in detail]
        URL: https://www.researchgate.net/publication/351492269
        URL: https://www.researchgate.net/publication/360757745
      - Burgess, M. *Notes on Trust As A Causal Basis For Social Science* (2022).
        ResearchGate; DOI 10.2139/ssrn.4252501. [VERIFIED — existence]
        URL: https://www.researchgate.net/publication/362387906
      - Burgess, M. *The Semantic Spacetime Hypothesis: A Guide to the Semantic
        Spacetime of Information* (2020 note). ResearchGate publication 344338994.
        Existence verified; full contents not accessible. [UNVERIFIED in detail]
        URL: https://www.researchgate.net/publication/344338994
      - Burgess, M. *In Search of Certainty: The Science of Our Information
        Infrastructure* (χtAxis Press, 2013). The popular introduction; contents not
        fetched during research. [UNVERIFIED in detail]
        URL: https://markburgess.org/certainty.html
      - Burgess, M. *Smart Spacetime* (χtAxis Press, 2019). ISBN 978-1797773704.
        Book-length exposition; contents not fetched. [UNVERIFIED in detail]
        URL: https://www.amazon.com/dp/1797773704
      - Burgess, M. *SSTorytime* (software). The open-source SST knowledge-graph
        database (Go over PostgreSQL, Apache-2.0) with the N4L note language and
        MCP-SST connector; also the older repository and a Julia port. [VERIFIED]
        URL: https://github.com/markburgess/SSTorytime
        URL: https://github.com/markburgess/SemanticSpaceTime
        URL: https://juliaknowledge.github.io/SemanticSpacetime.jl/dev/
      
      ## 3. Spacetime-Entangled Networks and consensus
      
      - Borrill, P.; Burgess, M.; Karp, A.; Kasuya, A. *Spacetime-Entangled Networks
        (I): Relativity and Observability of Stepwise Consensus* (2018, rev. 2020).
        arXiv:1807.08549 [cs.DC]. SST/promise semantics at the consensus layer:
        entanglement as co-dependent evolution of state; promises of sequential,
        in-order, atomically confirmed delivery. [VERIFIED]
        URL: https://arxiv.org/abs/1807.08549
      
      ## 4. Promise theory (the substrate)
      
      - Bergstra, J.A.; Burgess, M. *A static theory of promises* (2008, v5 2014).
        arXiv:0810.3294. The foundational promise-vs-obligation paper. [VERIFIED]
        URL: https://arxiv.org/abs/0810.3294
      - Bergstra, J.; Bethke, I.; Burgess, M. *A process algebra based framework for
        promise theory* (2007). arXiv:0707.0744. The process-algebra root of promise
        semantics. [VERIFIED]
        URL: https://arxiv.org/abs/0707.0744
      - Burgess, M.; Bergstra, J.A. *Promise Theory: Principles and Applications*
        (χtAxis Press, 2014; 2nd ed. 2019). The canonical statement; free PDF.
        [VERIFIED]
        URL: https://markburgess.org/BookOfPromises.pdf
        Page: https://markburgess.org/promises.html
        ACM DL: https://dl.acm.org/doi/abs/10.5555/2636996
      - Bergstra, J.A. *Promise Theory as a Tool for Informaticians* (Transmathematica,
        2020). DOI 10.36285/tm.35. Independent scholarly overview. [VERIFIED]
        URL: https://transmathematica.org/index.php/journal/article/view/35
      - Burgess, M. *Thinking in Promises: Designing Systems for Cooperation*
        (O'Reilly, 2015). ISBN 9781491917879. The practitioner's companion.
        [VERIFIED]
        URL: https://books.google.com/books/about/Thinking_in_Promises.html?id=ibL4CQAAQBAJ
      - Burgess, M.; Prangsma, E. *Koalja: from Data Plumbing to Smart Workspaces in
        the Extended Cloud* (2019). arXiv:1907.01796. The data-pipeline lineage of
        the SST project. [VERIFIED]
        URL: https://arxiv.org/abs/1907.01796
      
      ## 5. Adjacent and contextual work
      
      - Lamport, L. *Time, Clocks, and the Ordering of Events in a Distributed
        System.* *CACM* 21(7):558-565, 1978. The precedence view of time Burgess
        credits as SST's origin. [VERIFIED]
        URL: https://amturing.acm.org/p558-lamport.pdf
      - Mattern, F. *Virtual Time and Global States of Distributed Systems* (1988/89).
        Vector clocks. [VERIFIED]
        URL: https://vs.inf.ethz.ch/publ/papers/VirtTimeGlobStates.pdf
      - Kowalski, R.; Sergot, M. *A logic-based calculus of events.* *New Generation
        Computing* 4:67-95, 1986. [VERIFIED]
        URL: https://www.doc.ic.ac.uk/~rak/papers/event%20calculus.pdf
      - McCarthy, J.; Hayes, P. *Some philosophical problems from the standpoint of
        artificial intelligence.* *Machine Intelligence* 4, 1969. [VERIFIED]
        URL: http://www-formal.stanford.edu/jmc/mcchay69.pdf
      - Tolman, E.C. *Cognitive maps in rats and men.* *Psychological Review*
        55(4):189-208, 1948. [VERIFIED]
        URL: https://psycnet.apa.org/record/1949-00103-001
      - O'Keefe, J.; Nadel, L. *The Hippocampus as a Cognitive Map.* Clarendon Press,
        1978. [VERIFIED]
        URL: https://discovery.ucl.ac.uk/id/eprint/10103569/
      - Hafting, T.; Fyhn, M.; Molden, S.; Moser, M.-B.; Moser, E.I. *Microstructure
        of a spatial map in the entorhinal cortex.* *Nature* 436:801-806, 2005.
        [VERIFIED]
        URL: https://www.nature.com/articles/nature03721
      - Constantinescu, A.O.; O'Reilly, J.X.; Behrens, T.E.J. *Organizing conceptual
        knowledge in humans with a gridlike code.* *Science* 352(6292):1464-1468,
        2016. DOI 10.1126/science.aaf0941. [VERIFIED]
        URL: https://www.science.org/doi/10.1126/science.aaf0941
      - Ralph, M.A.L.; Jefferies, E.; Patterson, K.; Rogers, T.T. *The neural and
        computational bases of semantic cognition.* *Nature Reviews Neuroscience*
        18:42-55, 2017. [VERIFIED]
        URL: https://www.nature.com/articles/nrn.2016.150
      - Osgood, C.E.; Suci, G.J.; Tannenbaum, P.H. *The Measurement of Meaning.*
        Univ. of Illinois Press, 1957. The "semantic differential." [VERIFIED]
        URL: https://www.press.uillinois.edu/books/?id=p745393
      - Gärdenfors, P. *Conceptual Spaces: The Geometry of Thought.* MIT Press, 2000.
        [VERIFIED]
        URL: https://mitpress.mit.edu/9780262572194/conceptual-spaces/
      - Harris, Z.S. *Distributional Structure.* *Word* 10(2-3):146-162, 1954.
        [VERIFIED]
        URL: https://www.tandfonline.com/doi/abs/10.1080/00437956.1954.11659520
      - Landauer, T.K.; Dumais, S.T. *A solution to Plato's problem: The latent
        semantic analysis theory...* *Psychological Review* 104(2):211-240, 1997.
        [VERIFIED]
        URL: https://www.stat.cmu.edu/~cshalizi/350/2008/readings/Landauer-Dumais.pdf
      - Turney, P.D.; Pantel, P. *From Frequency to Meaning: Vector Space Models of
        Semantics.* *JAIR* 37:141-188, 2010. [VERIFIED]
        URL: https://arxiv.org/abs/1003.1141
      - Mikolov, T.; Sutskever, I.; Chen, K.; Corrado, G.; Dean, J. *Distributed
        Representations of Words and Phrases and their Compositionality* (2013).
        arXiv:1310.4546. word2vec skip-gram. [VERIFIED]
        URL: https://arxiv.org/abs/1310.4546
      - Sorkin, R.D. *Causal sets: discrete gravity* (2003). arXiv:gr-qc/0309009.
        [VERIFIED]
        URL: https://arxiv.org/abs/gr-qc/0309009
        (The program originates with Myrheim, *Statistical Geometry*, CERN preprint
        TH.2538, 1978 — cited in arXiv:2506.07756; citation chain verified,
        [VERIFIED].)
      - Surya, S. *The causal set approach to quantum gravity* (2019).
        arXiv:1903.11544. [VERIFIED]
        URL: https://arxiv.org/abs/1903.11544
      - Konopka, T.; Markopoulou, F.; Smolin, L. *Quantum graphity* (2006).
        arXiv:hep-th/0611197. [VERIFIED]
        URL: https://arxiv.org/abs/hep-th/0611197
      - Milner, R. *The Space and Motion of Communicating Agents.* Cambridge Univ.
        Press, 2009. Bigraphs — the reference model Burgess builds on. [VERIFIED]
        URL: https://www.cambridge.org/core/books/space-and-motion-of-communicating-agents/
      - Gutiérrez, C.; Hurtado, C.; Vaisman, A. *Temporal RDF* (ESWC 2005).
        [VERIFIED]
        URL: https://users.dcc.uchile.cl/~cgutierr/papers/temporalRDF.pdf
      - Erwig, M.; Güting, R.H.; Schneider, M.; Vazirgiannis, M. *Spatio-temporal
        data types: an approach to modeling and querying moving objects in
        databases.* *GeoInformatica* 3:265-291, 1999. [VERIFIED]
        URL: https://web.engr.oregonstate.edu/~erwig/papers/MovingObjects_GEOINF99.pdf
      - Wikipedia. *Semantic spacetime.* Tertiary overview; use only to locate
        primary URLs, treat claims [UNVERIFIED] unless corroborated by Burgess.
        [UNVERIFIED — tertiary]
        URL: https://en.wikipedia.org/wiki/Semantic_spacetime
      - Wikipedia. *Promise theory.* [UNVERIFIED — tertiary]
        URL: https://en.wikipedia.org/wiki/Promise_theory
      - Pavlyshin, V. *Semantic Space Time for AI Agent Ready Graphs* (2025,
        Leanpub). Independent third-party book building on SST. ISBN 9798266921313.
        [VERIFIED — listing]
        URL: https://leanpub.com/sst-4-agenticai
      - Dewar, N. *The epistemology of spacetime.* *Philosophy Compass* 17(4), 2022.
        DOI 10.1111/phc3.12821. Philosophy of physics — semantics *of* spacetime,
        distinct from Burgess's spacetime *of* semantics. [VERIFIED]
        URL: https://compass.onlinelibrary.wiley.com/doi/10.1111/phc3.12821
      
    • diagnosis-and-debugging.md 13.7 KB
      # Diagnosis and Debugging — A Bounded Procedure for Semantic Drift, Divergence, Dead-Ends, and Meaning Gaps
      
      **Load this file when you are diagnosing a semantic failure in an agent or system** — two agents disagree about what a word means, an agent's behavior drifts from its instructions, a task dead-ends and nothing the agent tries helps, or a term that used to mean something now means nothing to anyone. This file gives a bounded, checkable procedure that terminates: after three non-converging diagnostic passes you stop and report the evidence.
      
      **What belongs here:** the diagnosis procedure — inputs, the four named conditions (drift, divergence, dead-end/absorbing state, meaning gap), the stepwise diagnosis, the three-pass bounded exit, and the exit artifacts. What does **not** belong here: the definitions and formal model behind the vocabulary (see [foundations.md](foundations.md)); the drift literature and metrics in depth (see [agent-coordination.md](agent-coordination.md) §5); the named patterns the procedure applies (see [patterns.md](patterns.md)); the empirical infrastructure record (see [applications-infrastructure.md](applications-infrastructure.md)). Promise-level assessment (whether a promise is kept, breach, trust calibration) is linked to [promise-theory](../../promise-theory/SKILL.md) — this file covers the *semantic* side, not the promise-accounting side.
      
      **Provenance.** `[VERIFIED]` = confirmed in a fetched primary source; `[UNVERIFIED]` = secondary/inferred; `EXTRAPOLATION` = this skill's synthesis, labeled. The procedure's structure (steps, four conditions, three-pass exit) is this skill's original synthesis, grounded in the verified definitions it cites.
      
      ---
      
      ## 1. When to use this procedure (and when not to)
      
      Use this procedure when the symptom is **semantic**: the outputs, decisions, or coordinated behavior diverge from a stated meaning, and you can point at a concept, promise, instruction, or shared term as the thing that "meant" something. The four target conditions:
      
      1. **Semantic drift** — a meaning changes over time away from its promised/recorded meaning (the agent or system quietly re-interprets). Empirically: "progressive degradation of agent behavior, decision quality, and inter-agent coherence over extended interaction sequences" with semantic drift as "deviation from original intent" [VERIFIED — arXiv:2601.04170].
      2. **Divergence** — two or more agents (or an agent and its instruction) end up with *different* meanings for the same term or state; the gap grows with time. Empirically: "the divergence of internal knowledge states between concurrent agents" [VERIFIED — arXiv:2606.21666].
      3. **Dead-end (absorbing state)** — a node or process stops propagating information; the same failure recurs and interior changes do not help. Formally: "the ubiquitous appearance of absorbing states in any partial graph means that certain graph processes leak information and represent entropy changing processes"; an absorbing state "can only be replaced with new boundary data from outside the graph" [VERIFIED — arXiv:2506.07756].
      4. **Meaning gap** — a term or promise has no working interpretation at all in the current system: the co-language between the agents that must use it has no overlap on that term ("agents should expect to misunderstand one another's intentions to some level" [VERIFIED — arXiv:2604.10505]).
      
      Do **not** use this procedure when the failure is purely mechanical (a crashed service, a malformed message, a wrong API call with no meaning dimension) — route to the tool's own skill. Do **not** use it when the question is whether a promise was *kept* (assessment, breach, trust calibration) — that is [promise-theory's diagnosis-and-debugging reference](../../promise-theory/references/diagnosis-and-debugging.md), which this file links to rather than restates.
      
      ## 2. Inputs — what to collect before starting
      
      Gather these before pass 1; every step consumes them:
      
      - **The instruction / promised state** — the text or artifact that stated the intended meaning (the "instruction trajectory" of [agent-coordination.md](agent-coordination.md) §8.5). If it is not versioned, version it now as an observation.
      - **Observed implementations** — agent outputs, decisions, tool calls, or system states at known times; at least two points in time to make drift/divergence measurable (drift is a time-indexed quantity).
      - **Reality observations** — measurements of the external world state the meanings are supposed to track (SLO-style measurements; see [applications-infrastructure.md](applications-infrastructure.md) §7).
      - **The shared vocabulary in play** — the terms, promises, or concepts that are in dispute, with any prior anchor definitions ([patterns.md](patterns.md) Pattern 1).
      - **The causal/time structure** — event ordering where it matters ("there is only a partial order in which an event e1 precedes an event e2 iff e1 can causally affect e2" [VERIFIED — Lamport 1978]); reconstruct causality, not timestamps, first.
      
      If the inputs are unavailable (no recorded instruction, no observations, no shared terms), the diagnosis cannot converge — that finding itself goes into the evidence report (§6) as a meaning gap.
      
      ## 3. The four conditions — diagnosis checks
      
      For each candidate condition, run its check. A condition is *confirmed* only when the check's evidence is present; otherwise record it as ruled out.
      
      | Condition | Diagnosis check | Ruled out when |
      |---|---|---|
      | **Drift** | Compute the divergence between the promised/recorded meaning and the observed implementation at two or more times (semantic distance; [patterns.md](patterns.md) Patterns 5 and 9). Is the pairwise distance growing, or consistently nonzero in one direction? | Distances are stable and near zero at every pair of times |
      | **Divergence** | Compare the same term or state across two agents (or agent vs. instruction) at the same time. Is the inter-agent semantic distance above your risk threshold? Is the gap widening? | All agents agree within threshold at all sampled times |
      | **Dead-end (absorbing state)** | Trace the graph from the failing node. Do information flows stop at it? Does it re-absorb every intervention (same outcome, more input)? Is the node's interior data being erased (no learning)? | Interventions produce new, different outcomes; information passes through |
      | **Meaning gap** | For the disputed term, does any agent have a working interpretation (a defined anchor, or an observed consistent use)? Does the exchange co-language contain the term at all? | At least one agent demonstrates a stable, observable interpretation of the term |
      
      Each check consumes the inputs of §2 and produces a verdict plus the evidence that supports it. Do not skip the measurement step in any check: "it is not possible to reason about semantics without taking into account the underlying dynamics" [VERIFIED — InfoQ, *In Search of Certainty*].
      
      ## 4. The procedure — bounded, stepped
      
      **EXTRAPOLATION** — this five-step procedure is this skill's synthesis: it applies the verified SST machinery (γ(3,4) typing, semantic distance, absorbing states, promise overlap) as a debugging discipline. Run the steps in order; each step either locates the failure or rules out a whole class.
      
      **Pass structure.** One *pass* = running steps 1–4 in order. You may run up to **three passes**; a pass that does not converge must change something (a new observation, a new hypothesis, a re-typed edge) rather than repeat the same loop. After three non-converging passes, stop and write the evidence report (§5). This bounded exit mirrors the skill's Exit Conditions and prevents the re-litigation trap.
      
      ### Step 1 — Reconstruct the semantic spacetime
      
      Build (or update) the γ(3,4) model of the failing system from the inputs: nodes typed as events (timelike process agents), things (persistent, realized), or concepts (virtual, unrealized); edges typed 0 = NEAR, ±1 = LEADS TO, ±2 = CONTAINS, ±3 = EXPRESSES [VERIFIED — arXiv:2506.07756; formal definition in [foundations.md](foundations.md) §2]. Record promises and acceptances as edges with their overlap `b∩` [VERIFIED — arXiv:2604.10505]. If the model cannot be built (no node type fits, edges cannot be typed), record that as evidence of a meaning gap and continue.
      
      ### Step 2 — Locate the divergence
      
      Compute pairwise semantic distances between the instruction trajectory, the implementation trajectory, and the reality observations ([agent-coordination.md](agent-coordination.md) §8.5). Answer: which pair diverges, and along which dimension (spatial/temporal/task for the context-divergence framing [VERIFIED — arXiv:2606.21666]; semantic/coordination/behavioral for the agent-drift framing [VERIFIED — arXiv:2601.04170])? Identify the earliest observation at which the divergence exceeded threshold — that is the candidate *onset*.
      
      ### Step 3 — Classify the condition
      
      Run the §3 checks for the four conditions against the divergence locus. The most common misreads to guard against: drift and divergence both show distance, but drift is time-local (one trajectory vs. its promise) while divergence is inter-agent (two trajectories vs. each other); a dead-end is not drift — it is structural, and only boundary data helps ("can only be replaced with new boundary data from outside the graph" [VERIFIED — arXiv:2506.07756]); a meaning gap is not divergence — it is the absence of a working interpretation, not two different ones.
      
      ### Step 4 — Check the promise plumbing (link, don't restate)
      
      If the failure touches whether a promise was kept, whether acceptance was recorded, or how trust was calibrated, route that part of the diagnosis to [promise-theory's diagnosis-and-debugging reference](../../promise-theory/references/diagnosis-and-debugging.md) and run its assessment steps there. This procedure covers the semantic side only; do not re-derive promise-accounting here. Record which promises/acceptances the semantic failure involves (their overlap `b∩` [VERIFIED — arXiv:2604.10505]) as evidence, then return to Step 5.
      
      ### Step 5 — Hypothesize the fix and verify it in the model
      
      For the classified condition, propose the SST-typed intervention and test it in the model before applying it to the system:
      
      - **Drift** → re-anchor the drifting term (Pattern 1) and re-apply the convergence loop (Pattern 3): re-affirm the promised meaning, re-record it as versioned data [EXTRAPOLATION — grounded in the fixed-point convergence of arXiv:2604.10505 and the versioned-coordinate machinery of arXiv:2204.00470].
      - **Divergence** → reconcile (Pattern 10): expose both projections, expand the co-language, re-anchor shared terms, re-measure [EXTRAPOLATION — grounded in the offer/acceptance overlap and three-languages framing of arXiv:2604.10505].
      - **Dead-end** → inject boundary data: a new promise, a human input, outside policy — then verify the absorbing state re-opens [VERIFIED — arXiv:2506.07756].
      - **Meaning gap** → define and anchor the missing term in the exchange co-language, or refuse to proceed on it until both sides accept a definition [EXTRAPOLATION — grounded in the co-language/non-unitary-translation framing of arXiv:2604.10505].
      
      Verify the fix by re-running Step 2 on the model with the fix applied: the divergence metric must move toward zero (or stay within the risk threshold). If it does not, the fix was wrong — this is a non-converging pass; change the hypothesis and go again (up to three passes).
      
      ## 5. The bounded exit — after three non-converging passes
      
      If after **three passes** the divergence metric is still above threshold, the absorbing state still absorbs, or the meaning gap persists, **stop diagnosing and report**. Do not iterate a fourth time, do not re-litigate the same model, do not silently widen the scope. The purpose of the bound is to convert an unbounded hunt into an evidence artifact — the diagnosis is itself a finding.
      
      ## 6. Exit artifacts — what the evidence report must contain
      
      Write the report with at least these sections (this is the report contract of [templates/sst-analysis.md.tmpl](../templates/sst-analysis.md.tmpl)):
      
      1. **System description** — the model built in Step 1 (or the reason it could not be built).
      2. **Semantic spacetime map** — the γ(3,4) graph with node types, link types, and the divergence locus marked.
      3. **Findings** — for each of the four conditions: confirmed or ruled out, with the check evidence; the onset observation for drift/divergence; the leaking boundary for dead-ends; the unanchored term for meaning gaps.
      4. **Interventions** — the fixes tried in Steps 5, with their modeled outcomes (converged / non-converging per pass).
      5. **Verification/measurement plan** — the specific re-measurement (what to observe, at what scale, how often) that would confirm the fix in the real system, per the "dynamics always trumps semantics" measurement rule [VERIFIED — InfoQ, *In Search of Certainty*].
      6. **Pass ledger** — what changed between pass 1, 2, and 3, so a future diagnoser can see the evidence trail and pick up where this one stopped.
      
      A completed report is a legitimate termination: the exit condition is an observable artifact (the report exists and states findings + bounded escalation), not an admission of failure.
      
      ## Routing
      
      For the metrics and literature behind Steps 2–3: [agent-coordination.md](agent-coordination.md) §5. For the patterns the interventions apply: [patterns.md](patterns.md). For the formal model and γ(3,4) definitions: [foundations.md](foundations.md). For the empirical infrastructure record behind the measurement rule: [applications-infrastructure.md](applications-infrastructure.md). For promise-accounting diagnosis (assessment, breach, trust): [promise-theory](../../promise-theory/SKILL.md) and its [diagnosis-and-debugging reference](../../promise-theory/references/diagnosis-and-debugging.md).
      
    • foundations.md 25.3 KB
      # Foundations — The Academic Core of Semantic Spacetime
      
      **Load this file when you need the definitions, the formal model, proper time,
      causality, the γ(3,4) formalism, the learning/knowledge formalism, or an honest
      assessment of the theory's status.** This is the academic anchor of the skill.
      What belongs here: the academic theory of Semantic Spacetime (SST) as developed
      by Mark Burgess (2014-2025) — definitions, the formal model, γ(3,4), proper
      time, causality, the promise-theory substrate, and adjacent fields. What does
      not belong here: quantum-gravity or physics derivation (this is not a physics
      theory — see §4), the CFEngine/infrastructure application history, and the
      agent-coordination synthesis; those belong to the skill's application and
      agent-coordination references ([applications-infrastructure.md](applications-infrastructure.md)
      and [agent-coordination.md](agent-coordination.md)). For
      one-line definitions see [glossary.md](glossary.md); for sources see
      [bibliography.md](bibliography.md).
      
      **Provenance.** Every definition below is tagged with exactly one marker,
      following the research corpus this skill was built from:
      
      - `[VERIFIED]` — confirmed directly in a primary source fetched during the
        research phase (the arXiv papers, markburgess.org pages, and the fetched
        secondary sources listed in [bibliography.md](bibliography.md)).
      - `[UNVERIFIED]` — secondary-source or inferred; confirmed only via metadata,
        search index, or an author's own secondary account.
      - `EXTRAPOLATION` — original synthesis extending the theory to new domains;
        never presented as a verified fact.
      
      The theory is semi-formal and deliberately unrefereed (§5). This file states
      what is defined and verified, what is only informally claimed, and what is this
      skill's own synthesis. Do not present unverified claims as fact and do not drop
      markers when reusing this content.
      
      ---
      
      ## 1. Authorship and scope of the term
      
      Semantic Spacetime is the coinage and project of **Mark Burgess** — the
      physicist-turned-computer-scientist who created CFEngine — with the exact term
      effectively his alone. A full-text search of arXiv for the exact phrase
      "semantic spacetime" returns exactly 7 hits, all by Burgess; "semantic
      space-time" returns zero hits [VERIFIED — arXiv full-text search performed
      2026-08-12]. There is no independent academic school using the term. The
      primary series is his arXiv papers 2014-2025:
      
      - *Spacetimes with Semantics* (2014), arXiv:1411.5563 [VERIFIED]
      - *Spacetimes with Semantics (II): Scaling of agency, semantics, and tenancy*
        (2015), arXiv:1505.01716 [VERIFIED]
      - *Spacetimes with Semantics (III): The Structure of Functional Knowledge
        Representation and Artificial Reasoning* (2016, rev. 2017), arXiv:1608.02193 —
        the most formal document, canonical source for Definitions 1-9 and
        Lemmas 1-3 [VERIFIED]
      - *Agent Semantics, Semantic Spacetime, and Graphical Reasoning* (2025),
        arXiv:2506.07756 — the current formal statement, introducing the γ(3,4)
        representation [VERIFIED]
      
      Burgess states the intent directly: *"I have no interest or intention of
      seeking to publish any of this work beyond making these notes available seeking
      trusted review"* [VERIFIED — markburgess.org/blog_spacetime3.html]. SST is a
      conceptual/modeling framework, deliberately not a quantum-gravity theory (§4).
      
      ## 2. The formal model
      
      The formal skeleton comes from Part III (arXiv:1608.02193v4), which Burgess
      calls "lengthy notes" laying foundations; and from the 2025 γ(3,4) paper.
      
      ### Semantic element (Definition 1)
      
      > "A semantic element is a tuple ⟨Aᵢ, {π_scalar j, …}⟩ consisting of a single
      > autonomous agent, and an optional number of scalar material promises."
      > [VERIFIED — arXiv:1608.02193v4, Definition 1]
      
      An agent "surrounded by a halo of promises that imbue it with semantics"
      [VERIFIED — same source]. The promises are scalar/material (the agent's own
      capabilities and properties) as distinct from the vector/adjacency promises of
      Part II that connect elements into a spacetime [VERIFIED — arXiv:1505.01716].
      
      ### Semantic spacetime (Definition 2)
      
      > "A collection of semantic elements, in any phase (gas or solid), for which a
      > local change in state, promises or configuration represents a local unit of
      > time." [VERIFIED — arXiv:1608.02193v4, Definition 2]
      
      Companion one-liner from the project hub: *"A semantic spacetime is a discrete
      graph, which evolves, and whose properties vary from point to point."*
      [VERIFIED — markburgess.org/spacetime.html]. The definition makes time a
      property of local change within the graph, not an external axis.
      
      ### Proper time and the absence of a global clock
      
      Time in SST is *proper time*: *"Time in this sense is the Aristotelian concept
      of proper time as countable changes, as observed by the agent concerned."*
      [VERIFIED — arXiv:2506.07756 §1.3]. There is no global clock: *"The view of
      time as a relative transition system goes back to the work of Leslie Lamport…
      Lamport rediscovered the idea that time can at best be understood as a
      precedence relation, in a discrete spacetime context."* [VERIFIED —
      markburgess.org/semantic_spacetime.html]. Lamport, "Time, Clocks, and the
      Ordering of Events in a Distributed System," *CACM* 21(7):558-565, 1978, is the
      credited origin of this precedence view [VERIFIED — same page; bibliography].
      Practically: two agents cannot share a wall-clock ordering of events; each
      element's sequence of local changes is its own time.
      
      ### Causality as cooperative promises
      
      Causality in SST is constituted by cooperative promises, not by imposed links.
      Each adjacency requires both an offer (+) and an acceptance (−) promise between
      the two ends: *"each node must both emit and absorb adjacency relations,
      cooperatively… Thus space is made up of cooperating nodes and edges."*
      [VERIFIED — markburgess.org/semantic_spacetime.html]. In the notation of the
      papers, `S →(+π) R` means sender S offers promise π to receiver R, which
      accepts with the complementary −π promise; influence passes only through the
      overlap of offer and acceptance [VERIFIED — arXiv:1608.02193]. This is the
      promise-theoretic spine that makes SST an agent model rather than a global
      network model: every edge is a negotiated, observable relation.
      
      ### The γ(3,4) formalism
      
      The 2025 paper (arXiv:2506.07756) refines the earlier four irreducible
      associations (aggregation, causation, cooperation, similarity — [VERIFIED —
      arXiv:1608.02193]) into a typed graph formalism called γ(3,4): **three node
      meta-types × four link types** [VERIFIED — arXiv:2506.07756, Table 1].
      
      The three node meta-types [VERIFIED — arXiv:2506.07756 §2.3]:
      
      | Meta-type | Symbol | Nature |
      |---|---|---|
      | Events | e | Temporary/ephemeral; timelike (process) agents; persist or change via "leads to" |
      | Things | t | Persistent, physical/realized agents; "behave like matter"; spacelike (snapshot) |
      | Concepts | c | Invariant notions that cannot be created or destroyed; virtual space of "unrealized" potential; materialized only by attaching to physical agents |
      
      The four link types, exactly [VERIFIED — arXiv:2506.07756, Table 1]:
      
      | Value | Label | Direction | Semantics |
      |---|---|---|---|
      | 0 | NEAR | symmetric | equivalence, similarity, proximity, correlation |
      | ±1 | LEADS TO | directed | temporal/causal order: enables, causes, precedes, depends on |
      | ±2 | CONTAINS | directed | containment, membership, generalization, coarse-graining |
      | ±3 | EXPRESSES | directed | attribute, name/value, property, distinguishing mark |
      
      Burgess frames the four-link hypothesis itself as a hypothesis: *"This remains
      a hypothesis for now, but it is not a particularly original one. Various
      authors have suggested that spacetime concepts underpin natural language."*
      [VERIFIED — arXiv:2506.07756 §2.2]. No additional link types exist in the
      formalism; adding one would leave γ(3,4).
      
      ### The nine typing design rules
      
      The node typing rules from arXiv:2506.07756 §2.3, exactly as verified
      [VERIFIED — arXiv:2506.07756 §2.3]:
      
      1. Things may be contained but not expressed.
      2. Concepts may be expressed but not contained.
      3. Concepts become realized by anchoring them to things or events.
      4. Verbs are dangling concepts without a subject or object to instantiate them.
      5. Verbs anchored to subjects/objects (things) are events.
      6. A realized state of being is an event.
      7. An unrealized state of being is a concept.
      8. A realized type of thing is a thing.
      9. An unrealized type of thing is a concept.
      
      Note on the paper's abstract: it states that "The Semantic Spacetime postulates
      bring predictability when reasoning," but the research phase could not verify an
      enumerated postulate list in the fetched text (it would require a full read of
      the paper's later sections). Treat the nine design rules above as the verified
      typing content; do not present them as a numbered list of "the Semantic
      Spacetime postulates" [UNVERIFIED — exact postulate set not verified].
      
      ### Location agents and signal agents
      
      Two auxiliary agent types complete the model's ontology [VERIFIED —
      arXiv:1608.02193v4]:
      
      - **Location agents** (Definition 6): "irreducible sites that take up space and
        can emit and absorb signal agents. They may not overlap."
      - **Signal agents** (Definition 7): "They may be created and destroyed,
        subsequently emitted and absorbed, by location agents. They can occupy the
        same space, since they end up and accumulate at end points."
      
      ## 3. Absorbing states and information leaks
      
      Absorbing states are a central diagnostic concept in SST: *"The ubiquitous
      appearance of absorbing states in any partial graph means that a graph process
      leaks information."* [VERIFIED — arXiv:2506.07756 abstract]. They are
      "non-conserving of information" [VERIFIED — same source]. Burgess ties the
      phenomenon to division by zero: the leak is *"closely associated with the issue
      of division by zero, which signals a loss of closure and the need for manual
      injection of remedial information"* — and the boundary where the graph leaks is
      *"boundary information where intentionality can enter"* [VERIFIED — arXiv:
      2506.07756 §1.3]. Practically: a dead-end node (an event or thing with no
      outgoing LEADS TO/EXPRESSES edges that matter) accumulates meaning and stops
      propagating it; intent or policy must be injected manually at that boundary.
      For a bounded diagnosis procedure using this concept, see the skill's
      [diagnosis-and-debugging.md](diagnosis-and-debugging.md) reference.
      
      ## 4. The "not physics" boundary
      
      SST is explicitly **not** a theory of physics: *"Semantic spacetime is a
      discrete model of spacetime, but it is not intended as a theory of quantum
      gravity, in spite of some affinity with quantum systems."* [VERIFIED —
      markburgess.org/semantic_spacetime.html]. Three consequences worth stating
      [VERIFIED — markburgess.org/semantic_spacetime.html]:
      
      - No manifold structure is assumed: space is constituted by relationships
        between objects, not by a background geometry.
      - There is no concept of variable velocity, nor momentum: "a discrete spacetime
        with finite number of states is not obviously a canonical system."
      - The connection with canonical systems remains unknown.
      
      When a task is physics (general relativity, quantum gravity, kinematics), SST
      is the wrong tool; route away at the SKILL.md "When not to use" boundary.
      
      ## 5. Status: semi-formal and unrefereed
      
      The core series is a set of self-published notes, deliberately not submitted
      for refereed publication: *"I have no interest or intention of seeking to
      publish any of this work beyond making these notes available seeking trusted
      review"* [VERIFIED — markburgess.org/blog_spacetime3.html]. Burgess also warns
      of the scope: *"I have improvised with an eye on practical applications. It is
      probably too ambitious in scope and detail, but bridges may serve a purpose even
      with gaps,"* and *"Although not a complete theory, it lays out guidance on the
      formulation of the basic issues of information propagation, with some proofs
      left to the reader."* [VERIFIED — arXiv:1608.02193 preamble; markburgess.org/
      semantic_spacetime.html]. Use the formalism as a reasoning aid, not a proof
      system. What is formal: the graph definitions (Definitions 1-9), the γ(3,4)
      type system and its nine design rules, the learning/knowledge formalism with
      its Nyquist bound and decay lemmas (§9), and the association-decomposition
      algebra. What is semi-formal or metaphorical: the scaling/tenancy results of
      Part II, and the physics parallels (Feynman/Schwinger readings, quantum-field
      analogies, "logic emerges from reasoning") [VERIFIED — arXiv:1608.02193;
      markburgess.org].
      
      ## 6. Promise theory as the substrate
      
      SST is formally built from Promise Theory: *"The chosen language here is
      Promise Theory (2004-2014)"* [VERIFIED — markburgess.org/spacetime.html] and
      *"the idea of semantic spacetime is based on an idea called Promise Theory"*
      [VERIFIED — markburgess.org/blog_spacetime3.html]. Promise Theory is the joint
      work of Mark Burgess and Jan A. Bergstra; its canonical statement is *Promise
      Theory: Principles and Applications* (χtAxis Press, 2014; 2nd ed. 2019), which
      describes itself as a "semi-formal language for modelling intent and its
      outcome" [VERIFIED — markburgess.org/promises.html].
      
      The primitives SST inherits, stated here in one line each and developed in
      depth by the promise-theory skill, are:
      
      - **Promise** — an autonomous declaration of intended behavior, with a body
        (label Λ), a type (τ), and a constraint (χ); written `S →(+π) R` for an offer
        from promiser S to promisee R [VERIFIED — promise-theory foundations;
        arXiv:1608.02193].
      - **Offer (+) and acceptance (−)** — every interaction requires both directions
        to be promised independently; this is the semantic spine of adjacency in SST
        (§2, Causality) [VERIFIED].
      - **Autonomy and locality** — agents are autonomous and inert except for the
        promises they make; a strong form of locality, and the reason SST is an agent
        model rather than a global network model [VERIFIED].
      - **Downstream Principle** — the most downstream party in a promise chain
        carries the greatest causal responsibility for the outcome [VERIFIED —
        promise-theory foundations].
      - **Convergence** — repeated local assessment toward a desired state; the
        dynamic meaning of "convergent coordination" in SST [VERIFIED — promise-theory
        foundations].
      
      Do not re-derive promise definitions here. When you need the promise vocabulary
      (promises, acceptances, bindings, assessment, trust, the Downstream Principle),
      load [promise-theory](../../promise-theory/SKILL.md) or its
      [foundations reference](../../promise-theory/references/foundations.md). This
      skill's territory is the space/time of meaning built on top of those promises:
      γ(3,4), trajectories, drift, semantic distance, shared semantic ground.
      
      ## 7. Measurement: the spacelike/timelike duality
      
      SST distinguishes two inequivalent ways to stabilize observation, which Burgess
      maps onto the Feynman (path-integral) vs. Schwinger (source) readings of
      quantum theory [VERIFIED — markburgess.org/semantic_spacetime.html;
      markburgess.org/spacetime.html]:
      
      1. **Spacelike / ensemble measurement** — *repeated trials with constant state
         and semantics, in which time plays no role*; objective/frequentist. You
         sample the same configuration many times and average.
      2. **Timelike / "cognitive" measurement** — *continuously adapting accumulation
         of state, whose semantics define change in real time*; subjective/Bayesian.
         You update a running assessment as the system changes.
      
      The two modes can disagree because they answer different questions, and the
      practitioner consequence is the skill's core measurement rule: **semantics
      requires measurement** — meaning cannot be asserted before the dynamics are
      measured at the right scale. Different scales yield different conclusions; a
      measurement that is stable at one scale can be wrong at another. This duality
      is the theory-level ground for the "dynamics always trumps semantics" lesson of
      the infrastructure lineage (covered in the application reference,
      [applications-infrastructure.md](applications-infrastructure.md)) and for
      Gotcha 4 in SKILL.md.
      
      ## 8. Distance: metric vs semantic
      
      Part III defines two kinds of distance [VERIFIED — arXiv:1608.02193v4]:
      
      - **Metric (quantitative) distance** (Definition 8): *"a measure of
        coordinate-similarity in position."* Coordinates, embeddings, positions.
      - **Semantic (qualitative) distance** (Definition 9): *"a measure of similarity
        in interpretation."* Worked examples in the paper: Hamming distance; hop
        counts in an associative network; semantic hashing; sparse distributed
        representations [VERIFIED — same source].
      
      The distinction is operational: two concepts can be close in coordinates yet
      far in interpretation, and vice versa. A weighted hop count over a γ(3,4) graph
      is a semantic-distance instance of the hop-count family — the family this
      skill's model tooling implements for measuring drift between two snapshots
      of a system's meaning.
      
      ## 9. Learning and knowledge
      
      SST formalizes learning and knowledge as processes with explicit timescales
      [VERIFIED — arXiv:1608.02193v4]:
      
      - **Learning about a promise π** (Definition 3): "the sampling, equilibration,
        and summarization of observational assessments concerning a promise π made by
        another agent, repeated over a timescale T_learn > 2·T_sample." The observer
        applies a learning function E(α(π)_{t+1}) = L(α(π)_t, E(α(π)_t)); learning
        defines a clock ticking at rate T_sample.
      - **Knowledge of π** (Definition 4): "a stable summary of the iterated
        assessment α(π)_{T_know}, of one or more promises π, formed by equilibration
        of the samples over a timescale T_know ≫ 2·T_sample." Crucially, "because
        knowledge defines a process with a timescale, the failure to confirm it
        relative to other changes leads to its decay."
      - **Lemma 1 (knowledge decay):** uncertainty of knowledge grows geometrically
        with time since learning, with attenuation ℓ^r, ℓ < 1.
      - **Lemma 2 (fidelity / learning rate):** "Learning can only represent source
        values faithfully if the rate of sampling is greater than twice that of the
        fastest rate of change in the data, i.e. 2/T_sample < ∂π/∂t" — the Nyquist
        bound.
      
      Practitioner consequence: **staleness is a first-class quantity.** Memory and
      retrieval designs must budget refresh; a knowledge summary that is never
      re-confirmed decays geometrically no matter how accurate it was when formed.
      This directly supports drift diagnosis: a stale shared interpretation is a
      predictable source of semantic divergence.
      
      ## 10. The empirical arm: the Quantitative Spacetime Hypothesis
      
      Two 2020 papers operationalize SST as a *testable hypothesis* rather than pure
      formalism [VERIFIED — arXiv:2010.08126; arXiv:2010.08125]:
      
      - **arXiv:2010.08126** — *Testing the Quantitative Spacetime Hypothesis using
        Artificial Narrative Comprehension (I): Bootstrapping Meaning from Episodic
        Narrative viewed as a Feature Landscape.* Parses narrative streams "without
        knowledge of semantics, using only measurable patterns (size and time)… as an
        event 'landscape'"; concepts are extracted "as process invariants." Results
        claim simple spacetime process cues, not higher reasoning, drive what is
        important about sensory experience [VERIFIED — arXiv:2010.08126].
      - **arXiv:2010.08125** — *…(II): Establishing the Geometry of Invariant
        Concepts, Themes, and Namespaces.* Reconstructs concepts and themes via
        "multiscale interferometry" and a "chemistry of association and pattern
        reconstruction, based only on the four fundamental spacetime relationships,"
        drawing a bioinformatic analogy (n-grams, micro/meso/macro scales)
        [VERIFIED — arXiv:2010.08125].
      
      Honest caveat: these are proof-of-concept experiments on narrative corpora with
      single-CPU methods; the research phase found **no independent replication and no
      benchmark against distributional baselines** [UNVERIFIED — no independent
      replication found]. Treat the Quantitative Spacetime Hypothesis as an active,
      incompletely validated empirical program — not established validation of SST.
      
      ## 11. Spacetime-Entangled Networks: consensus as entanglement
      
      *Spacetime-Entangled Networks (I): Relativity and Observability of Stepwise
      Consensus* is a four-author paper — Paul Borrill, Mark Burgess, Alan Karp,
      Atsushi Kasuya (arXiv:1807.08549, 2018, rev. 2020) — that instantiates the
      SST/promise line at the distributed-consensus layer [VERIFIED — arXiv:
      1807.08549]: *"Entanglement describes co-dependent evolution of state. Networks
      formed by entanglement of agents keep certain promises: they deliver sequential
      messages, end-to-end, in order, and with atomic confirmation of delivery to
      both ends of the link."* The "relativity of consensus" reading — observers at
      different points in the network reach consensus stepwise, in their own local
      order — is the SST no-global-clock doctrine applied to agreement
      [VERIFIED — arXiv:1807.08549; the mapping onto the cooperative-promise
      causality doctrine of §2 is this skill's synthesis and is labeled
      EXTRAPOLATION]. Note this paper is not one of the seven "semantic spacetime"
      phrase hits; it does not use the exact term [VERIFIED — arXiv search].
      
      ## 12. Motion of the Third Kind
      
      SST distinguishes three ways to understand motion in a graph; the third,
      "virtual motion" (Motion of the Third Kind), treats processes and properties —
      for example cloud workloads and data records — as *promises moving from host to
      host* [VERIFIED — markburgess.org/spacetime.html]. This is the basis of
      Burgess's "cloud computing as virtual physics" framing: relocating a workload
      is not matter moving through space, it is a promise being re-anchored. The
      ResearchGate papers *Motion of the Third Kind I & II* (2021-22) exist but their
      full texts were not fetched during research; details beyond the moving-promises
      framing are [UNVERIFIED]. See [glossary.md](glossary.md) for the one-line entry.
      
      ## 13. Adjacent fields
      
      SST sits next to — but is distinct from — these fields. Correct attribution and
      a one-line framing for each [VERIFIED — citations verified in the research
      phase; see bibliography]:
      
      - **Cognitive maps** — Tolman, "Cognitive maps in rats and men" (1948). The
        brain demonstrably organizes knowledge spatially; SST is a candidate formal
        language for concept space-times, not a neuroscience claim.
      - **Conceptual spaces** — Gärdenfors, *Conceptual Spaces: The Geometry of
        Thought* (MIT Press, 2000). Concepts as convex regions in metric spaces with
        quality dimensions; Gärdenfors-style spaces have **no time dimension** — SST
        adds process and temporality.
      - **Distributional / vector-space semantics** — Harris (1954), LSA (Landauer &
        Dumais 1997), word2vec-style embeddings (Mikolov et al. 2013). The dominant
        statistical competitor; SST explicitly contrasts itself ("graphs preserve the
        intentionality of the source even under data fractionation" vs. vectorized
        probabilistic estimation [VERIFIED — arXiv:2506.07756; arXiv:2512.19084]).
      - **Event calculus** — Kowalski & Sergot (1986). Logic-based reasoning about
        events where "the notion of event is taken to be more primitive than that of
        time"; SST instead claims spacetime structure generates the semantics.
      - **Situation calculus** — McCarthy & Hayes (1969). Logic-based reasoning about
        actions and change; the same logic-first framing distinguishes it from SST.
      - **Causal sets** — Myrheim (1978), Sorkin (2003), Surya (2019). The discrete-
        spacetime program Burgess flags as the closest physics analogue: "in this
        regard, a semantic spacetime is akin to causal sets" [VERIFIED — arXiv:
        2506.07756 §2]. Difference: SST's nodes are autonomous agents with semantics,
        not passive points, and SST assumes no manifold structure or symmetries.
      - **Logical clocks / virtual time** — Lamport (1978), Mattern (1988/89). The
        distributed-systems backbone for "no global clock"; SST generalizes logical
        clocks into full semantic spacetimes [VERIFIED].
      
      ## 14. Applying this reference
      
      When you have modeled a system with this vocabulary, materialize it in the
      skill's model format — see [templates/sst-model.yaml.tmpl](../templates/sst-model.yaml.tmpl)
      (the versioned `sst-model-v1` contract: agents, nodes, edges, acceptances,
      trajectories, observations) — and write the analysis in
      [templates/sst-analysis.md.tmpl](../templates/sst-analysis.md.tmpl). For
      unfamiliar terms while reading, load [glossary.md](glossary.md). For the
      promise-theory substrate vocabulary, load
      [promise-theory](../../promise-theory/SKILL.md) — do not re-derive promises
      here. For measurement and verification practice (turning assessed meaning into
      evals and traces), the
      [agent-evals-and-observability](../../agent-evals-and-observability/SKILL.md)
      skill is the assessment-layer partner.
      
      ## Sources
      
      Primary sources and adjacent works are listed with URLs in
      [bibliography.md](bibliography.md). The key items cited in this file: Burgess,
      *Spacetimes with Semantics* I-III (arXiv:1411.5563, 1505.01716, 1608.02193);
      Burgess, *Agent Semantics, Semantic Spacetime, and Graphical Reasoning*
      (arXiv:2506.07756); Burgess, *Testing the Quantitative Spacetime Hypothesis*
      I-II (arXiv:2010.08126, 2010.08125); Borrill, Burgess, Karp & Kasuya,
      *Spacetime-Entangled Networks (I)* (arXiv:1807.08549); Lamport, *Time, Clocks,
      and the Ordering of Events in a Distributed System* (CACM 1978); Burgess's
      project pages (markburgess.org/spacetime.html, /semantic_spacetime.html,
      /blog_spacetime3.html); Bergstra & Burgess, *Promise Theory: Principles and
      Applications* (2014/2019).
      
    • glossary.md 11.6 KB
      # Glossary — Semantic Spacetime Vocabulary
      
      **Load this file when you hit an unfamiliar term while applying this skill** —
      a word in the routing table, a reference, a model, or a diagnosis you cannot
      place. Each entry is a heading-led definition consistent with
      [foundations.md](foundations.md); where a term belongs to promise theory, the
      entry links there and keeps its own definition short. Sources are tagged as in
      [foundations.md](foundations.md): `[VERIFIED]` (confirmed in a primary source),
      `[UNVERIFIED]` (secondary or inferred), `EXTRAPOLATION` (this skill's
      synthesis).
      
      ---
      
      ## Core semantic spacetime terms
      
      ### Semantic element
      **Semantic element** — "a tuple ⟨Aᵢ, {π_scalar j, …}⟩ consisting of a single
      autonomous agent, and an optional number of scalar material promises"
      [VERIFIED — arXiv:1608.02193v4 Def 1]. The atomic unit of a semantic spacetime:
      an agent "surrounded by a halo of promises that imbue it with semantics."
      See [foundations.md](foundations.md) §2.
      
      ### Semantic spacetime
      **Semantic spacetime (SST)** — "a collection of semantic elements, in any phase
      (gas or solid), for which a local change in state, promises or configuration
      represents a local unit of time" [VERIFIED — arXiv:1608.02193v4 Def 2]. Mark
      Burgess's discrete graph model of meaning over time; the term is effectively his
      alone (exactly 7 arXiv hits, all by him) [VERIFIED]. See
      [foundations.md](foundations.md) §2.
      
      ### Proper time
      **Proper time** — time as countable local changes observed by the agent
      concerned: "the Aristotelian concept of proper time as countable changes, as
      observed by the agent concerned" [VERIFIED — arXiv:2506.07756 §1.3]. Each
      semantic element has its own proper time; there is no global clock. See
      [foundations.md](foundations.md) §2.
      
      ### Cooperative promise causality
      **Cooperative promise causality** — the SST account of causation: every
      adjacency requires both an offer (+) and an acceptance (−) promise between the
      two ends, so "space is made up of cooperating nodes and edges"
      [VERIFIED — markburgess.org/semantic_spacetime.html]. Causality is negotiated,
      local, and observable; it is never imposed. See
      [foundations.md](foundations.md) §2.
      
      ### γ(3,4)
      **γ(3,4)** — the 2025 typed-graph formalism of Semantic Spacetime (Burgess,
      arXiv:2506.07756): exactly three node meta-types — events (e), things (t),
      concepts (c) — crossed with exactly four link types — 0 NEAR, ±1 LEADS TO,
      ±2 CONTAINS, ±3 EXPRESSES [VERIFIED]. Pronounced "gamma three four"; the model
      format in this skill encodes it as nodes with a `type` and edges with a `link`
      value in {-3..3}. See [foundations.md](foundations.md) §2.
      
      ### NEAR
      **NEAR** — γ(3,4) link value 0; symmetric; equivalence, similarity, proximity,
      correlation. The "semantic symmetrization" link [VERIFIED — arXiv:2506.07756
      Table 1]. See [foundations.md](foundations.md) §2.
      
      ### LEADS TO
      **LEADS TO** — γ(3,4) link value ±1; directed; temporal/causal order — enables,
      causes, precedes, depends on. The "follows" gradient link [VERIFIED — arXiv:
      2506.07756 Table 1]. See [foundations.md](foundations.md) §2.
      
      ### CONTAINS
      **CONTAINS** — γ(3,4) link value ±2; directed; containment, membership,
      generalization, coarse-graining. The aggregate/membership link [VERIFIED —
      arXiv:2506.07756 Table 1]. See [foundations.md](foundations.md) §2.
      
      ### EXPRESSES
      **EXPRESSES** — γ(3,4) link value ±3; directed; attribute, name/value,
      property, distinguishing mark. The distinguishability link [VERIFIED — arXiv:
      2506.07756 Table 1]. See [foundations.md](foundations.md) §2.
      
      ### Event
      **Event** — γ(3,4) node meta-type e: temporary/ephemeral, timelike (process)
      agents that persist or change via "leads to" [VERIFIED — arXiv:2506.07756
      §2.3]. A realized state of being is an event; verbs anchored to things are
      events. See [foundations.md](foundations.md) §2.
      
      ### Thing
      **Thing** — γ(3,4) node meta-type t: persistent, physical/realized agents that
      "behave like matter"; spacelike snapshot [VERIFIED — arXiv:2506.07756 §2.3].
      Things may be contained but not expressed. See [foundations.md](foundations.md)
      §2.
      
      ### Concept
      **Concept** — γ(3,4) node meta-type c: invariant notions that cannot be created
      or destroyed; the virtual space of "unrealized" potential, materialized only by
      anchoring to things or events [VERIFIED — arXiv:2506.07756 §2.3]. Concepts may
      be expressed but not contained. See [foundations.md](foundations.md) §2.
      
      ### Absorbing state
      **Absorbing state** — a state in a partial graph where information stops
      flowing; "absorbing states are non-conserving of information" and "a graph
      process leaks information" at them [VERIFIED — arXiv:2506.07756]. Burgess ties
      the leak to division by zero: "loss of closure and the need for manual
      injection of remedial information"; the leaking boundary is "boundary
      information where intentionality can enter" [VERIFIED]. In diagnosis, a
      dead-end node that accumulates meaning without propagating it. See
      [foundations.md](foundations.md) §3.
      
      ### Metric distance
      **Metric (quantitative) distance** — "a measure of coordinate-similarity in
      position" [VERIFIED — arXiv:1608.02193v4 Def 8]. Coordinates, embeddings,
      positions. Contrast with semantic distance. See [foundations.md](foundations.md)
      §8.
      
      ### Semantic distance
      **Semantic (qualitative) distance** — "a measure of similarity in
      interpretation" [VERIFIED — arXiv:1608.02193v4 Def 9]; worked examples include
      Hamming distance, hop counts in an associative network, semantic hashing, and
      sparse distributed representations. Two concepts can be close in coordinates
      yet far in interpretation. See [foundations.md](foundations.md) §8.
      
      ### Semantic drift
      **Semantic drift** — this skill's term for the divergence of shared semantic
      ground over time: two agents (or an agent and its instructions) start with the
      same meaning for a term and their interpretations move apart as their local
      proper times advance. The diagnosis treats drift as an observable — measure the
      semantic distance between the interpretations at successive observations.
      **EXTRAPOLATION** — the drift concept is the skill's application of SST's
      trajectory and semantic-distance machinery; the term itself is standard in the
      agent-drift literature the research corpus reviewed, while the SST framing is
      synthesis.
      
      ### Trajectory
      **Trajectory** — the path an agent or a concept takes through semantic
      spacetime: the sequence of node states a semantic element occupies as its
      proper time advances, recorded as observations. Reasoning is "constrained
      spacetime trajectories" through the association network [VERIFIED — arXiv:
      1608.02193 §1, §5]. In the model format, a trajectory is a declared `path` of
      node ids.
      
      ### Shared semantic ground
      **Shared semantic ground** — the overlap of interpretation between two or more
      agents: the set of terms and promises that both sides mean the same way,
      measurable as low semantic distance between their concepts. SST models it as a
      region of the semantic spacetime where NEAR/EXPRESSES edges agree across
      agents. **EXTRAPOLATION** — synthesis term for this skill, grounded in the
      cooperative-promise account of adjacency and the definition of semantic
      distance.
      
      ### Temporal blindness
      **Temporal blindness** — an agent's failure to track event ordering, state
      change, or causality — effectively lacking a proper-time record of its own
      semantic element. SST's local-time account (no global clock) makes such
      blindness structural unless observations are recorded; the fix is a recorded
      observation log per element. The research corpus documents the LLM literature
      on this ("LLMs are temporally blind," arXiv:2510.23853) [VERIFIED — citation in
      the research corpus; the SST framing is EXTRAPOLATION].
      
      ### Spacelike measurement
      **Spacelike (ensemble) measurement** — repeated trials with constant state and
      semantics, in which time plays no role; objective/frequentist [VERIFIED —
      markburgess.org/semantic_spacetime.html]. See [foundations.md](foundations.md)
      §7.
      
      ### Timelike measurement
      **Timelike ("cognitive") measurement** — continuously adapting accumulation of
      state whose semantics define change in real time; subjective/Bayesian
      [VERIFIED — markburgess.org/semantic_spacetime.html]. See
      [foundations.md](foundations.md) §7.
      
      ### Learning
      **Learning (about a promise π)** — "the sampling, equilibration, and
      summarization of observational assessments concerning a promise π made by
      another agent, repeated over a timescale T_learn > 2·T_sample" [VERIFIED —
      arXiv:1608.02193v4 Def 3]. Learning defines a clock ticking at rate T_sample.
      See [foundations.md](foundations.md) §9.
      
      ### Knowledge
      **Knowledge (of a promise π)** — "a stable summary of the iterated assessment
      α(π)_{T_know}, of one or more promises π, formed by equilibration of the samples
      over a timescale T_know ≫ 2·T_sample"; it decays geometrically without refresh
      (attenuation ℓ^r, ℓ < 1) [VERIFIED — arXiv:1608.02193v4 Def 4, Lemma 1].
      Staleness is a first-class quantity. See [foundations.md](foundations.md) §9.
      
      ### Location agent
      **Location agent** — "irreducible sites that take up space and can emit and
      absorb signal agents. They may not overlap" [VERIFIED — arXiv:1608.02193v4
      Def 6]. See [foundations.md](foundations.md) §2.
      
      ### Signal agent
      **Signal agent** — agents that "may be created and destroyed, subsequently
      emitted and absorbed, by location agents. They can occupy the same space, since
      they end up and accumulate at end points" [VERIFIED — arXiv:1608.02193v4
      Def 7]. See [foundations.md](foundations.md) §2.
      
      ### Super-agent
      **Super-agent** — the coarse-grained agent formed by scaling agency up via the
      Part II rules: replacing a group of individual agents with one "super-agent"
      (sub-space), scaling agency both dynamically and semantically [VERIFIED — arXiv:
      1505.01716]. The renormalization analogue; see
      [foundations.md](foundations.md) §1 for the series map.
      
      ### Motion of the Third Kind
      **Motion of the Third Kind** — "virtual motion": processes and properties (e.g.,
      cloud workloads, data records) treated as promises moving from host to host,
      the basis of Burgess's "cloud computing as virtual physics" framing [VERIFIED —
      markburgess.org/spacetime.html]. The ResearchGate papers of that name (2021-22)
      exist; their details are [UNVERIFIED]. See [foundations.md](foundations.md) §12.
      
      ## Promise-theory-owned terms (deferred)
      
      ### Promise
      **Promise** — an autonomous declaration of intended, as yet unverified,
      behavior made by one agent to another; the primitive every SST adjacency builds
      on (offer polarity +π). Full definition, notation, and body/type/constraint
      machinery live in [promise-theory](../../promise-theory/SKILL.md) — load it
      there rather than re-deriving it. See [foundations.md](foundations.md) §6.
      
      ### Acceptance
      **Acceptance** — the complementary counter-promise (−π) that turns an offer
      into a binding: influence passes only through the overlap of offer and
      acceptance. SST's cooperative-promise causality is built from it. Full
      treatment: [promise-theory](../../promise-theory/SKILL.md). See
      [foundations.md](foundations.md) §2 and §6.
      
      ### Convergence
      **Convergence** — repeated local assessment toward a desired state (a fixed
      point), the dynamic meaning of "convergent coordination" in SST; statistical,
      never exact. Full treatment (including the distinction from idempotence):
      [promise-theory](../../promise-theory/SKILL.md) and its applications reference.
      See [foundations.md](foundations.md) §6.
      
      ### Downstream Principle
      **Downstream Principle** — the most downstream party in a promise chain carries
      the greatest causal responsibility for the outcome. Promise-theory-owned;
      [promise-theory](../../promise-theory/SKILL.md) has the full statement. See
      [foundations.md](foundations.md) §6.
      
    • patterns.md 20.3 KB
      # Patterns — Ten Named SST Patterns for Design and Diagnosis
      
      **Load this file when you need to apply a named pattern** — semantic anchor, semantic trajectory, convergence loop, promise propagation, drift detection, absorbing-state detection, shared semantic manifold, γ(3,4) modeling, semantic distance/divergence metrics, or reconciliation. Each pattern states its when-to-use condition as an observable trigger, its anti-pattern as a concrete misuse, and its SST grounding.
      
      **What belongs here:** the ten patterns as reusable, named design moves, with when-to-use triggers and anti-patterns. What does **not** belong here: the formal definitions behind the patterns (see [foundations.md](foundations.md)); the empirical record of the infrastructure and agentic-AI lines (see [applications-infrastructure.md](applications-infrastructure.md) and [agent-coordination.md](agent-coordination.md)); the bounded diagnosis procedure (see [diagnosis-and-debugging.md](diagnosis-and-debugging.md)). Promise-level machinery (offer/acceptance, assessment, breach, trust calibration) is linked to [promise-theory](../../promise-theory/SKILL.md), never re-taught here.
      
      **Provenance.** `[VERIFIED]` = confirmed in a fetched primary source; `[UNVERIFIED]` = secondary/inferred; `EXTRAPOLATION` = this skill's synthesis, labeled. Patterns grounded in verified research are marked; the pattern *shapes* themselves (when-to-use/anti-pattern framing) are this skill's original synthesis [EXTRAPOLATION] unless a source is named.
      
      ---
      
      ## 0. Pattern overview
      
      | # | Pattern | Use when (one line) | Key anti-pattern |
      |---|---|---|---|
      | 1 | Semantic anchor | A term or promise needs a stable, versioned meaning reference | Freezing the anchor forever; anchoring to an internal embedding |
      | 2 | Semantic trajectory | You need to record where an agent's meaning is going over time | Treating snapshots as the whole story; no time axis |
      | 3 | Convergence loop | State must be measured against a desired meaning and repaired | Confusing convergence with idempotence; expecting exactness |
      | 4 | Promise propagation | Delegation chains carry commitments between agents | Modeling promises without acceptance; long unverified chains |
      | 5 | Drift detection | Meaning quietly changes between two snapshots or agents | Thresholding on one snapshot; ignoring scale |
      | 6 | Absorbing-state detection | Agents dead-end, hallucinate, or stop learning | Treating the symptom as the cause; no boundary injection |
      | 7 | Shared semantic manifold | Agents must coordinate on what relations mean | Building a manifold without causality; expecting identical projections |
      | 8 | γ(3,4) modeling | You need to type the semantic graph (events/things/concepts × 4 links) | Inventing extra link types; ontology-first modeling |
      | 9 | Semantic distance/divergence metrics | You need a number for "how far apart" two meanings are | Using raw coordinate distance as semantic distance |
      | 10 | Reconciliation | Two divergent meanings must be brought back into agreement | Forcing agreement by fiat; no acceptance on both sides |
      
      ## 1. Semantic anchor
      
      **When to use:** use when you observe that a term, promise, or instruction keeps being interpreted differently by different agents (or by the same agent at different times), and you need a stable reference point against which interpretations can be compared. The observable trigger is a measurable disagreement that re-occurs despite repeated explanation.
      
      **Shape:** a versioned, addressable statement of what a concept or promise *means* in this system — the intended interpretation, its boundaries (what it does not cover), and its revision history. In SST terms the anchor is a concept node with typed edges to the things and events it is anchored to (γ(3,4) typing rule: concepts become realized by anchoring to things or events [VERIFIED — arXiv:2506.07756]); its revision history is the record axis of [applications-infrastructure.md](applications-infrastructure.md) §8.
      
      **Anti-patterns:** freezing the anchor — a semantic anchor that can never be revised becomes a lie as the system changes (knowledge decays geometrically when unconfirmed; [VERIFIED — arXiv:1608.02193, Lemma 1, via foundations.md §9]). Anchoring to an agent's internal embedding rather than to an observable, shared statement — embeddings are "interior spaces" with "inscrutable property models" [VERIFIED — arXiv:2506.07756]. Anchoring to prose that no one versioned — the GitOps lesson is that the contract must be versioned desired state [VERIFIED — CNCF, GitOps 101].
      
      ## 2. Semantic trajectory
      
      **When to use:** use when you need to know where an agent's (or a system's) meaning is going over time — whether understanding is converging, drifting, or diverging — and when the artifact you need is a recorded path, not a single snapshot. Trigger: the question "how did we get from interpretation A to interpretation B?" is answerable only from a series of observations, not from the current state.
      
      **Shape:** a sequence of {position, intent (promise), time} observations — each local change is a unit of proper time for the element concerned [VERIFIED — arXiv:1608.02193, Def. 2, via foundations.md]. Record the trajectory as observations in the skill's model format (see [templates/sst-model.yaml.tmpl](../templates/sst-model.yaml.tmpl)); compute displacement (drift), inter-agent separation (divergence), and approach-to-fixed-point (convergence) from it.
      
      **Anti-patterns:** treating snapshots as the whole story — a single state cannot show drift, because drift is a time-indexed quantity [EXTRAPOLATION — grounded in the definitions of drift in arXiv:2601.04170]. Recording trajectories without a time axis or causal order — wall-clock-less, causality-less traces cannot answer "what can affect what" [VERIFIED — Lamport 1978, via applications-infrastructure §4]. Confusing the agent's reported trajectory with its actual trajectory — the record axis and the world axis must stay separate [VERIFIED — Temporal database Wikipedia, via applications-infrastructure §4].
      
      ## 3. Convergence loop
      
      **When to use:** use when you need a system that continuously measures its current state against a desired meaning and repairs toward it — the SST form of a control loop. Trigger: you can state a desired end-state as an observable condition, and you expect the environment to perturb state unpredictably over time.
      
      **Shape:** a loop that (1) measures the current state, (2) compares against the promised state, (3) acts to repair divergence, (4) repeats. This is CFEngine's fixed-point machinery — a convergent operator satisfies `O(q0) = q0` with `O^2 = O`, "like a ball rolling into a potential well" [VERIFIED — Burgess, *A Tiny Overview of CFEngine*] — and MAPE-K's monitor → analyze → plan → execute [VERIFIED — Kephart & Chess 2003]. It is also the drift literature's bounded-equilibrium finding: turn-wise divergence evolves "as a bounded stochastic process with restoring forces" [VERIFIED — arXiv:2510.07777]. For the promise-level convergence mechanics (offer/acceptance and assessment inside the loop), link to [promise-theory](../../promise-theory/references/patterns.md) rather than re-deriving them.
      
      **Anti-patterns:** confusing convergence with idempotence — idempotence requires only O²=O, convergence is relative to a specific policy state q0 [VERIFIED — *A Tiny Overview*]. Expecting exactness — convergence is statistical, never exact, in a stochastic environment: "a complete specification of policy determines an approximate configuration… only approximately over persistent times" [VERIFIED — *A Tiny Overview*]. A loop with no measurement plan — "dynamics always trumps semantics"; without measurement at the right scale, the loop is guessing [VERIFIED — InfoQ, *In Search of Certainty*].
      
      ## 4. Promise propagation
      
      **When to use:** use when commitments travel through chains — an orchestrator delegates to a subagent, which delegates further, or a promise must transit intermediate agents — and you need to model how intent propagates and where it attenuates. Trigger: a delegation chain of length ≥ 2, or a promise whose meaning depends on intermediate reinterpretation.
      
      **Shape:** model each delegation as an offer (+b) and acceptance (−b) with overlap `b∩` — the effective propagated content is the overlap, not the full offer [VERIFIED — arXiv:2604.10505]; the Downstream Principle makes the accepting agent responsible for its own use [VERIFIED — same source]. Cost model: fully-promised delivery through N intermediaries costs O(N²); at minimal trust the promise graph must be complete [VERIFIED — same source]. Trace the trajectory of the promise through semantic spacetime and check where the overlap shrinks (each non-unitary translation "agents should expect to misunderstand one another's intentions to some level" [VERIFIED — arXiv:2604.10505]).
      
      **Anti-patterns:** modeling promises without acceptance — a dispatched task with no recorded acceptance is an imposition that looks accepted (the silence-as-acceptance trap) [EXTRAPOLATION — grounded in arXiv:2604.10505 offer/acceptance semantics and promise-theory's acceptance handshake pattern in [promise-theory/references/patterns.md](../../promise-theory/references/patterns.md)]. Long unverified chains — trusting the chain head instead of verifying per hop, which the O(N²) result and handoff-context-loss failures warn against [VERIFIED — arXiv:2604.10505; the handoff-loss reading is this skill's synthesis]. An agent promising on behalf of another — the tenet "no agent may promise anything on behalf of any agent but itself" [VERIFIED — arXiv:2604.10505].
      
      ## 5. Drift detection
      
      **When to use:** use when meaning may be changing between snapshots, between agents, or between instruction and implementation, and you need to notice it early. Trigger: you have two or more observations of the same semantic state (or the same promise) at different times or from different agents, and you need a decision rule for "they no longer mean the same thing."
      
      **Shape:** compute a divergence metric between the observations (see Pattern 9), threshold it against a risk budget, and alert. The empirical metrics to draw on: the Context Divergence Score over spatial/temporal/task dimensions [VERIFIED — arXiv:2606.21666]; the Agent Stability Index over twelve dimensions [VERIFIED — arXiv:2601.04170]; turn-wise KL divergence with restoring forces [VERIFIED — arXiv:2510.07777]. The SST framing: drift is displacement from the promised trajectory; the three-trajectory version (instruction, implementation, reality) is Pattern 5's strongest form and is developed in [diagnosis-and-debugging.md](diagnosis-and-debugging.md) and [agent-coordination.md](agent-coordination.md) §8.5.
      
      **Anti-patterns:** thresholding on a single snapshot — drift is a time-indexed quantity; one measurement cannot detect it [EXTRAPOLATION]. Ignoring scale — different scales yield contradictory conclusions; "the ability to distinguish and separate scales is closely allied with our notions of simplicity" [VERIFIED — InfoQ, *In Search of Certainty*]. Full-broadcast "sync" as a fix — naive full-broadcast synchronization *increases* hallucination by 34%; selective sync reduces it [VERIFIED — arXiv:2606.21666].
      
      ## 6. Absorbing-state detection
      
      **When to use:** use when agents or systems dead-end — repeat the same failure, hallucinate, stop learning, or stop responding to new information — and you need to recognize the dead-end as a structural property rather than a one-off bug. Trigger: the same divergent outcome recurs despite intervention, or information stops propagating from some node.
      
      **Shape:** identify the absorbing state in the γ(3,4) graph: "the ubiquitous appearance of absorbing states in any partial graph means that certain graph processes leak information and represent entropy changing processes"; absorbing states erase interior information and "can only be replaced with new boundary data from outside the graph, such as outside policy choices"; this is "closely associated with the issue of division by zero, which signals a loss of closure and the need for manual injection of remedial information" — "boundary information where intentionality can enter" [VERIFIED — arXiv:2506.07756]. The SST remedy is boundary injection: a new promise, a human input, or outside policy data, not more iterations of the same loop [VERIFIED — same source]. See [foundations.md](foundations.md) §3 for the formal treatment.
      
      **Anti-patterns:** treating the symptom as the cause — e.g., "more context" for a task that has collapsed into an absorbing state where no amount of interior information helps [EXTRAPOLATION — grounded in the absorbing-states doctrine]. Never injecting boundary data — an absorbing state "can only be replaced with new boundary data from outside the graph" [VERIFIED — arXiv:2506.07756]. Confusing an absorbing state with convergence — an absorbing state is a leak (entropy-increasing); a convergent fixed point is a desired attractor [VERIFIED — arXiv:2506.07756; *A Tiny Overview*].
      
      ## 7. Shared semantic manifold
      
      **When to use:** use when multiple agents must coordinate on what relations mean — when "near", "causes", "contains", and "expresses" must mean the same thing to every participant — and raw token contexts or opaque agent cards are insufficient. Trigger: you observe coordination failures that trace to relation-type ambiguity ("we disagreed about whether X causes Y or merely correlates with Y").
      
      **Shape:** a shared γ(3,4)-structured representation (typed nodes and links) that each agent projects onto, with its own interior state kept separate; coordination happens by comparing projections. This is the coordination-substrate synthesis of [agent-coordination.md](agent-coordination.md) §8.1, grounded in the intentionality-preservation claim — "graphs preserve the intentionality of the source even under data fractionation" [VERIFIED — arXiv:2512.19084] — and the Tolman-Eichenbaum finding that spatial and relational memory share machinery [VERIFIED — Whittington et al., Cell 2020].
      
      **Anti-patterns:** building the manifold without causal-temporal structure — an undirected similarity space has no "leads-to" and cannot express the relation-type ambiguity that matters [EXTRAPOLATION — grounded in the four γ(3,4) link types]. Expecting identical projections — each agent is autonomous with local knowledge; the manifold coordinates *overlaps*, not identities [VERIFIED — arXiv:2604.10505 autonomy + local knowledge; the overlap framing is the paper's b∩]. Replacing the manifold with a giant shared context — full-broadcast context sharing increases hallucination [VERIFIED — arXiv:2606.21666].
      
      ## 8. γ(3,4) modeling
      
      **When to use:** use when you need to type a semantic graph — to classify nodes as events (timelike process agents), things (spacelike snapshot agents), or concepts (virtual role/intention agents), and links as 0 = NEAR, ±1 = LEADS TO, ±2 = CONTAINS, ±3 = EXPRESSES [VERIFIED — arXiv:2506.07756]. Trigger: you have a knowledge or coordination graph and you need a principled, ontology-free typing of what each edge claims.
      
      **Shape:** apply the nine typing design rules (things may be contained but not expressed; concepts may be expressed but not contained; concepts become realized by anchoring to things or events; verbs are dangling concepts without subject/object; a realized state of being is an event; an unrealized state of being is a concept; a realized type of thing is a thing; an unrealized type of thing is a concept) [VERIFIED — arXiv:2506.07756 §2.3]. **The formal definition belongs to [foundations.md](foundations.md) §2 — load it before applying this pattern.** Note the honest limit: the claim that four link types suffice "remains a hypothesis for now" [VERIFIED — arXiv:2506.07756].
      
      **Anti-patterns:** inventing extra link types — the four types (0, ±1, ±2, ±3) are the γ(3,4) contract; adding ad-hoc edge semantics re-introduces the ontology tax the formalism avoids [EXTRAPOLATION — grounded in the "four basic arrows… sufficient" hypothesis and the anti-ontology framing of arXiv:2506.07756]. Ontology-first modeling — "ontologies do not employ principles rooted in the processes of the world"; SST "is not a taxonomy or an ontology" [VERIFIED — arXiv:2506.07756]. Using vector similarity as the edge semantics — vectors are for probabilistic estimation; graphs preserve intentionality [VERIFIED — arXiv:2512.19084].
      
      ## 9. Semantic distance/divergence metrics
      
      **When to use:** use when you need a number for "how far apart" two meanings are — for routing, delegation, drift alerting, or reconciliation priority. Trigger: you must decide between two interpretations, two agents, or two snapshots based on how close they are semantically.
      
      **Shape:** distinguish **metric distance** ("a measure of coordinate-similarity in position") from **semantic distance** ("a measure of similarity in interpretation") [VERIFIED — arXiv:1608.02193, Definitions 8–9, via foundations.md §8]. Semantic distance instances include Hamming distance, hop counts in an associative network, semantic hashing, and sparse distributed representations [VERIFIED — same source]. On a γ(3,4) graph, a weighted hop count over typed links is a semantic-distance instance — weight by link type (causal links farther than similarity links, etc.) [EXTRAPOLATION — the weighting scheme is this skill's design; the hop-count family is verified]. The empirical drift metrics (CDS, ASI, KL) are divergence instances to reuse [VERIFIED — arXiv:2606.21666, 2601.04170, 2510.07777].
      
      **Anti-patterns:** using raw coordinate distance as semantic distance — "two concepts can be close in coordinates yet far in interpretation, and vice versa" [VERIFIED — arXiv:1608.02193, via foundations.md §8]. Unweighted hop counts that treat a causal edge like a similarity edge [EXTRAPOLATION]. Declaring a divergence metric without a measurement plan — metrics without observations at the right scale are ungrounded ("dynamics always trumps semantics") [VERIFIED — InfoQ, *In Search of Certainty*].
      
      ## 10. Reconciliation
      
      **When to use:** use when two divergent meanings must be brought back into agreement — after drift detection, after a breached promise, or after a merge of two agent teams' interpretations. Trigger: you have identified pairwise semantic distance above a threshold and you need a bounded process to close it.
      
      **Shape:** a bounded negotiation: (1) expose each side's interpretation as a projection onto the shared manifold (Pattern 7); (2) identify the overlap `b∩` that already exists and the disagreement region [VERIFIED — arXiv:2604.10505 offer/acceptance overlap]; (3) expand the co-language — "agents may have to talk their way to a calibration of meaning" [VERIFIED — arXiv:2604.10505, three-languages framing]; (4) re-anchor the shared terms (Pattern 1) and re-record them as versioned data (the record axis of applications-infrastructure §8); (5) verify by re-measuring the divergence after the reconciliation. The drift literature's empirical anchor: reminder interventions reliably reduce divergence [VERIFIED — arXiv:2510.07777].
      
      **Anti-patterns:** forcing agreement by fiat — an imposition "without the receiver's promise" is generally ineffective and looks accepted without being so [VERIFIED — arXiv:2604.10505]. No acceptance on both sides — reconciliation without both sides' acceptance is not convergence, it is coercion [EXTRAPOLATION — grounded in the offer/acceptance machinery]. Reconciling once and never re-checking — knowledge decays without confirmation; reconciliation must be re-measured [VERIFIED — arXiv:1608.02193, Lemma 1, via foundations.md §9]. Iterating reconciliation indefinitely — the bounded-exit rule of [diagnosis-and-debugging.md](diagnosis-and-debugging.md) applies: three non-converging passes → stop and report evidence.
      
      ## Routing
      
      For the formal model behind these patterns: [foundations.md](foundations.md). For the empirical record: [applications-infrastructure.md](applications-infrastructure.md) and [agent-coordination.md](agent-coordination.md). For the bounded diagnosis procedure that uses Patterns 2, 5, 6, 9, and 10: [diagnosis-and-debugging.md](diagnosis-and-debugging.md). For promise-level machinery (acceptance handshakes, evaluation loops, breach → renegotiation, trust calibration): [promise-theory](../../promise-theory/SKILL.md) and its [patterns reference](../../promise-theory/references/patterns.md).
      
  • scripts
    • semantic-spacetime.py 54.8 KB
      #!/usr/bin/env python3
      """semantic-spacetime.py — lint, map, and analyze sst-model-v1 models.
      
      A stdlib-only Python 3.10+ command-line tool for Semantic Spacetime models:
      validate a model against the sst-model-v1 schema, render its gamma(3,4) graph,
      measure weighted hop distance, enumerate simple trajectories, and diff two
      snapshots for semantic drift. Designed for AI agent consumption:
      non-interactive, flag-driven, deterministic, with --json and --dry-run.
      
      Exit codes:
        0  success (valid model, render, distance/trajectory/drift completed)
        1  invalid model or invalid input content (schema violations, unknown node
           id, no connecting path, unparseable/empty/non-UTF-8 input)
        2  usage errors (unknown command/subcommand/option, missing flags or file
           arguments, bad --format value) or IO errors (missing/unreadable file)
      
      Conventions (matching promise-contract.py):
        * --json emits exactly one JSON object on stdout for dispatched commands,
          on success AND on content/IO errors (errors travel in an 'errors' list,
          stderr stays empty). Usage/argument errors never emit JSON: they print
          text to stderr and exit 2, even when --json is present.
        * --dry-run is accepted by every command as a no-op guard; all commands are
          read-only and never write anything.
        * Errors never produce a traceback; custom error classes carry the exit code.
      
      Model input (see templates/sst-model.yaml.tmpl):
        One restricted-YAML document (mappings, flow lists, quoted/unquoted scalars,
        comments, indentation-based nesting) or an equivalent JSON document. YAML
        constructs outside the subset (anchors/aliases &a/*a, block scalars |/>,
        multi-document streams) are rejected with exit 1. JSON and equivalent YAML
        lint identically. The schema is strict: unknown top-level sections and
        unknown fields inside known sections are rejected with a named violation
        (exit 1) naming the unknown key and its location.
      
      Semantic distance weighting:
        Each directed hop contributes weight |link| + 1, so 0=NEAR -> 1,
        +/-1=LEADS TO -> 2, +/-2=CONTAINS -> 3, +/-3=EXPRESSES -> 4. The reported
        distance is the minimum total weight over directed paths from --from to --to
        (a weighted-hop instance of SST's semantic-distance family).
      
      Importing this module has no side effects: the entry point is guarded behind
      ``if __name__ == "__main__":``.
      """
      
      import json
      import os
      import re
      import sys
      
      VERSION = "1.0.0"
      SCHEMA_VERSION = "sst-model-v1"
      
      NODE_TYPES = ("event", "thing", "concept")
      PROMISE_TYPES = ("capability", "intent", "constraint")
      MAP_FORMATS = ("text", "mermaid", "json")
      SUBCOMMANDS = ("lint", "map", "distance", "trajectory", "drift")
      RESERVED_TARGET = "all"
      
      # Strict sst-model-v1 field sets: anything outside these is a named violation.
      TOP_LEVEL_KEYS = (
          "schema_version",
          "agents",
          "nodes",
          "edges",
          "acceptances",
          "trajectories",
          "observations",
      )
      AGENT_KEYS = ("id", "role", "promises")
      PROMISE_KEYS = ("id", "body", "type", "target")
      NODE_KEYS = ("id", "type")
      EDGE_KEYS = ("from", "to", "link")
      ACCEPTANCE_KEYS = ("promise", "from", "to")
      TRAJECTORY_KEYS = ("id", "path", "label")
      OBSERVATION_KEYS = ("at", "event", "changed")
      
      LINK_LABELS = {0: "NEAR", 1: "LEADS TO", 2: "CONTAINS", 3: "EXPRESSES"}
      
      ID_PATTERN = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
      _INT_PATTERN = re.compile(r"[-+]?\d+$")
      _FLOAT_PATTERN = re.compile(r"[-+]?(?:\d+\.\d*|\.\d+)(?:[eE][-+]?\d+)?$")
      _MAPPING_PATTERN = re.compile(r"^([A-Za-z0-9_.-]+)\s*:(?:\s+(.*))?$")
      _BLOCK_SCALAR_PATTERN = re.compile(r"^[|>][+-]?$")
      
      _MAX_PATHS = 50000
      
      USAGE = """usage: semantic-spacetime.py model <subcommand> <file> [options]
      
      Lint, map, and analyze Semantic Spacetime models (schema sst-model-v1).
      
      subcommands:
        model lint <file>                  validate a model against the sst-model-v1
                                           schema; exit 0 = valid with a coverage
                                           summary, exit 1 = named violations,
                                           exit 2 = usage or IO errors
        model map <file> --format FMT      render the gamma(3,4) graph in one of
                                           text | mermaid | json
        model distance <file> --from X --to Y
                                           weighted hop distance from node X to
                                           node Y; each hop weighs |link| + 1
                                           (0=NEAR -> 1, +/-1=LEADS TO -> 2,
                                           +/-2=CONTAINS -> 3, +/-3=EXPRESSES -> 4);
                                           exit 1 when an id is missing or no path
                                           connects the two nodes
        model trajectory <file> --from X --to Y
                                           enumerate every simple path from X to Y
                                           (no repeated nodes), annotated with edge
                                           link types; cycles are noted; terminates
                                           on any finite model; exit 1 when an id is
                                           missing or no path connects the two nodes
        model drift <file-a> <file-b>      diff two snapshots into added / removed /
                                           changed semantic regions; identical
                                           snapshots report 'no drift' and exit 0
      
      options:
        --json           machine-readable output; stdout carries a single JSON object
                         (errors travel in an 'errors' list; stderr stays empty)
        --dry-run        no-op guard; every command is read-only and writes nothing
        --format FMT     map output format: text | mermaid | json
        --from ID        source node id for model distance / model trajectory
        --to ID          destination node id for model distance / model trajectory
        --help           print this help and exit
        --version        print the version and exit
      
      input format:
        A model file is one restricted-YAML document (mappings, flow lists, quoted
        or unquoted scalars, comments, indentation-based nesting) or an equivalent
        JSON document (first character '{' or '['). YAML constructs outside the
        subset - anchors/aliases (&a / *a), block scalars (|, >), multi-document
        streams (---) - are rejected with exit 1. The schema is sst-model-v1
        (agents/promises, nodes typed event|thing|concept, edges link -3..3,
        acceptances, trajectories, observations); see templates/sst-model.yaml.tmpl.
      
      examples:
        python3 semantic-spacetime/scripts/semantic-spacetime.py model lint semantic-spacetime/tests/fixtures/sample-model.yaml
        python3 semantic-spacetime/scripts/semantic-spacetime.py model map sst-model.yaml --format mermaid
        python3 semantic-spacetime/scripts/semantic-spacetime.py model distance sst-model.yaml --from report-event --to drift-concept
        python3 semantic-spacetime/scripts/semantic-spacetime.py model trajectory sst-model.yaml --from report-event --to drift-concept
        python3 semantic-spacetime/scripts/semantic-spacetime.py model drift old.yaml new.yaml
      """
      
      
      class ModelError(Exception):
          """A user-facing input error carrying the process exit code."""
      
          def __init__(self, message, exit_code):
              super().__init__(message)
              self.exit_code = exit_code
      
      
      class ParseError(Exception):
          """A structured restricted-YAML parse error."""
      
      
      # ---------------------------------------------------------------------------
      # Restricted-YAML parsing (stdlib only; no PyYAML)
      # ---------------------------------------------------------------------------
      
      def _strip_comment(line):
          """Remove a trailing comment, keeping '#' inside quoted scalars."""
          quote = None
          for i, ch in enumerate(line):
              if ch in ("'", '"'):
                  if quote == ch:
                      quote = None
                  elif quote is None:
                      quote = ch
              elif ch == "#" and quote is None and (i == 0 or line[i - 1] in " \t"):
                  return line[:i]
          return line
      
      
      def _split_list_body(body):
          """Split a flow-list body on top-level commas (commas inside quotes kept)."""
          pieces = []
          buf = []
          quote = None
          for ch in body:
              if ch in ("'", '"'):
                  if quote == ch:
                      quote = None
                  elif quote is None:
                      quote = ch
              if ch == "," and quote is None:
                  pieces.append("".join(buf))
                  buf = []
              else:
                  buf.append(ch)
          pieces.append("".join(buf))
          return pieces
      
      
      def _coerce_scalar(raw, lineno):
          """Turn a raw scalar token into a Python value (restricted subset)."""
          token = raw.strip()
          if token.startswith("["):
              inner = token[1:]
              if not inner.endswith("]"):
                  raise ParseError(f"line {lineno}: unterminated flow list (missing ']')")
              inner = inner[:-1].strip()
              if inner == "":
                  return []
              return [_coerce_scalar(item, lineno) for item in _split_list_body(inner)]
          if token.startswith('"'):
              if len(token) < 2 or not token.endswith('"'):
                  raise ParseError(f"line {lineno}: unterminated double-quoted string")
              try:
                  return json.loads(token)
              except json.JSONDecodeError as exc:
                  raise ParseError(
                      f"line {lineno}: invalid double-quoted string: {exc.msg}"
                  ) from None
          if token.startswith("'"):
              if len(token) < 2 or not token.endswith("'"):
                  raise ParseError(f"line {lineno}: unterminated single-quoted string")
              return token[1:-1].replace("''", "'")
          if token == "":
              return None
          if token.startswith(("&", "*")):
              raise ParseError(
                  f"line {lineno}: anchors and aliases are outside the restricted YAML subset"
              )
          lowered = token.lower()
          if lowered in ("true", "false"):
              return lowered == "true"
          if lowered in ("null", "~"):
              return None
          if _INT_PATTERN.fullmatch(token):
              return int(token)
          if _FLOAT_PATTERN.fullmatch(token):
              return float(token)
          return token
      
      
      def _reject_block_scalar(raw, lineno):
          """Reject the block-scalar indicators |, > (with optional +/- chomp)."""
          if raw is not None and _BLOCK_SCALAR_PATTERN.match(raw.strip()):
              raise ParseError(
                  f"line {lineno}: block scalars are outside the restricted YAML subset"
              )
      
      
      class RestrictedYamlParser:
          """Indentation-based parser for the sst-model restricted-YAML subset.
      
          Supported: mappings, flow lists, quoted/unquoted scalars, comments, and
          nesting by indentation. Rejected: anchors/aliases, block scalars,
          multi-document streams, and tab indentation. Each line is normalized to
          (indent, content, lineno) up front; a recursive descent over those tokens
          builds plain Python objects.
          """
      
          def __init__(self, text):
              self._lines = []
              for lineno, raw in enumerate(text.split("\n"), start=1):
                  line = _strip_comment(raw)
                  stripped = line.lstrip(" \t")
                  if not stripped:
                      continue
                  indent = len(line) - len(stripped)
                  if "\t" in line[:indent]:
                      raise ParseError(f"line {lineno}: tab indentation is not supported")
                  if stripped in ("---", "..."):
                      raise ParseError(
                          f"line {lineno}: multi-document streams are outside the restricted YAML subset"
                      )
                  self._lines.append((indent, stripped, lineno))
              if not self._lines:
                  raise ParseError("empty document")
              self._pos = 0
      
          def parse(self):
              """Parse the whole document; returns a plain Python object."""
              first_indent = self._lines[0][0]
              doc = self._parse_node(first_indent)
              if self._pos != len(self._lines):
                  raise ParseError(
                      f"line {self._lines[self._pos][2]}: unexpected content"
                  )
              return doc
      
          def _parse_node(self, indent):
              """Parse a block (mapping or sequence) starting at the current line."""
              _indent, content, _lineno = self._lines[self._pos]
              if content.startswith("-"):
                  return self._parse_sequence(indent)
              return self._parse_mapping(indent)
      
          def _parse_value(self, indent, raw, lineno):
              """Parse a mapping value; blank values open a deeper-indented block."""
              _reject_block_scalar(raw, lineno)
              if raw is None or raw.strip() == "":
                  self._pos += 1
                  if self._pos < len(self._lines) and self._lines[self._pos][0] > indent:
                      return self._parse_node(self._lines[self._pos][0])
                  return None
              value = _coerce_scalar(raw, lineno)
              self._pos += 1
              return value
      
          def _parse_mapping(self, indent, first=None):
              """Parse mapping entries at `indent`. `first` seeds the entry that
              opened the mapping (a list item such as '- id: x')."""
              result = {}
              if first is not None:
                  key, raw, lineno = first
                  result[key] = self._parse_value(indent, raw, lineno)
              while self._pos < len(self._lines):
                  ind, content, lineno = self._lines[self._pos]
                  if ind < indent:
                      break
                  if ind > indent:
                      raise ParseError(f"line {lineno}: unexpected indentation")
                  if content.startswith("-"):
                      break
                  match = _MAPPING_PATTERN.match(content)
                  if not match:
                      raise ParseError(
                          f"line {lineno}: expected 'key: value', got {content!r}"
                      )
                  key, raw = match.group(1), match.group(2)
                  result[key] = self._parse_value(indent, raw, lineno)
              return result
      
          def _parse_sequence(self, indent):
              """Parse sequence items at `indent` (lines starting with '-')."""
              result = []
              while self._pos < len(self._lines):
                  ind, content, lineno = self._lines[self._pos]
                  if ind != indent or not content.startswith("-"):
                      break
                  rest = content[1:].strip()
                  if rest == "":
                      self._pos += 1
                      if self._pos < len(self._lines) and self._lines[self._pos][0] > indent:
                          result.append(self._parse_node(self._lines[self._pos][0]))
                      else:
                          result.append(None)
                      continue
                  _reject_block_scalar(rest, lineno)
                  match = _MAPPING_PATTERN.match(rest)
                  if match:
                      result.append(
                          self._parse_mapping(
                              indent + 2, first=(match.group(1), match.group(2), lineno)
                          )
                      )
                  else:
                      result.append(_coerce_scalar(rest, lineno))
                      self._pos += 1
              return result
      
      
      def parse_restricted_yaml(text):
          """Parse a restricted-YAML document into plain Python objects."""
          return RestrictedYamlParser(text).parse()
      
      
      # ---------------------------------------------------------------------------
      # Model loading
      # ---------------------------------------------------------------------------
      
      def load_model(path):
          """Read and parse a model file. Raises ModelError on any problem."""
          if not os.path.exists(path):
              raise ModelError(f"cannot read '{path}': no such file or directory", exit_code=2)
          try:
              with open(path, "rb") as fh:
                  raw = fh.read()
          except OSError as exc:
              raise ModelError(
                  f"cannot read '{path}': {exc.strerror or exc}", exit_code=2
              ) from None
          try:
              text = raw.decode("utf-8-sig")
          except UnicodeDecodeError:
              raise ModelError(
                  f"cannot decode '{path}': file is not valid UTF-8", exit_code=1
              ) from None
          text = text.replace("\r\n", "\n").replace("\r", "\n")
          if not text.lstrip():
              raise ModelError(
                  f"cannot parse '{path}': file is empty or contains only whitespace",
                  exit_code=1,
              )
          first_char = text.lstrip()[0]
          try:
              if first_char in "{[":
                  return json.loads(text)
              return parse_restricted_yaml(text)
          except ParseError as exc:
              raise ModelError(f"cannot parse '{path}': {exc}", exit_code=1) from None
          except json.JSONDecodeError as exc:
              raise ModelError(
                  f"cannot parse '{path}': invalid JSON at line {exc.lineno} "
                  f"column {exc.colno}: {exc.msg}",
                  exit_code=1,
              ) from None
          except RecursionError:
              raise ModelError(
                  f"cannot parse '{path}': input nesting is too deep", exit_code=1
              ) from None
      
      
      # ---------------------------------------------------------------------------
      # Schema validation (sst-model-v1)
      # ---------------------------------------------------------------------------
      
      def _describe(value):
          """Short type-aware description of a value for violation messages."""
          if isinstance(value, list):
              return f"list {value!r}"
          if isinstance(value, dict):
              return "mapping"
          return f"{type(value).__name__} {value!r}"
      
      
      def _is_int(value):
          return isinstance(value, int) and not isinstance(value, bool)
      
      
      def _empty_summary():
          return {
              "agents": 0,
              "promises": 0,
              "nodes": 0,
              "edges": 0,
              "acceptances": 0,
              "trajectories": 0,
              "observations": 0,
              "promises_accepted": 0,
          }
      
      
      def _unknown_field_errors(mapping, allowed, location):
          """Violations for keys in `mapping` outside the `allowed` field set.
      
          Each message names the unknown key and its location (e.g. the enclosing
          node/agent id) so the violation is actionable. Sorted for determinism.
          """
          return [
              f"{location}: unknown field '{key}'"
              for key in sorted(set(mapping) - set(allowed))
          ]
      
      
      def validate_model(doc):
          """Validate a parsed model against every sst-model-v1 lint rule.
      
          Accumulates ALL violations (no fail-fast). Returns
          (valid, errors, summary) with counts for the coverage summary.
          """
          errors = []
          summary = _empty_summary()
          if not isinstance(doc, dict):
              errors.append("model must be a mapping at the top level")
              return False, errors, summary
      
          schema_version = doc.get("schema_version")
          if schema_version is None:
              errors.append("missing required top-level key 'schema_version'")
          elif not _is_int(schema_version):
              errors.append(
                  f"'schema_version' must be the integer 1 (sst-model-v1); "
                  f"got {_describe(schema_version)}"
              )
          elif schema_version != 1:
              errors.append(f"'schema_version' must be 1 for sst-model-v1; got {schema_version}")
      
          # ---- strict top-level schema (unknown sections are named violations) ----
          for key in sorted(set(doc) - set(TOP_LEVEL_KEYS)):
              errors.append(
                  f"unknown top-level key '{key}' "
                  f"(expected one of: {', '.join(TOP_LEVEL_KEYS)})"
              )
      
          # ---- agents and promises ----
          agents_raw = doc.get("agents")
          if agents_raw is None:
              errors.append("missing required top-level key 'agents'")
              agents_raw = []
          elif not isinstance(agents_raw, list):
              errors.append("'agents' must be a list")
              agents_raw = []
          if not agents_raw:
              errors.append("'agents': collection must be non-empty")
      
          agent_ids = []
          all_promise_ids = set()
          promise_owner = {}
          for ai, agent in enumerate(agents_raw):
              if not isinstance(agent, dict):
                  errors.append(f"agent #{ai + 1}: expected a mapping, got {_describe(agent)}")
                  continue
              aid = agent.get("id")
              if not isinstance(aid, str) or not aid.strip():
                  errors.append(
                      f"agent #{ai + 1}: missing or invalid required field 'id' "
                      "(must be a non-empty string)"
                  )
                  aid = None
              else:
                  if aid == RESERVED_TARGET:
                      errors.append(
                          f"agent id '{aid}' is a reserved target token and cannot be an agent id"
                      )
                  if not ID_PATTERN.fullmatch(aid):
                      errors.append(
                          f"agent id '{aid}' must match ^[a-z0-9]+(?:-[a-z0-9]+)*$ (lowercase-hyphen)"
                      )
                  if aid in agent_ids:
                      errors.append(f"agent id '{aid}' is duplicated; agent ids must be unique")
                  agent_ids.append(aid)
              aname = f"'{aid}'" if aid else f"#{ai + 1}"
              summary["agents"] += 1
              errors.extend(_unknown_field_errors(agent, AGENT_KEYS, f"agent {aname}"))
      
              role = agent.get("role")
              if role is None:
                  errors.append(f"agent {aname}: missing required field 'role'")
              elif not isinstance(role, str) or not role.strip():
                  errors.append(f"agent {aname}: 'role' must be a non-empty string")
      
              promises = agent.get("promises")
              if promises is None:
                  promises = []
              elif not isinstance(promises, list):
                  errors.append(f"agent {aname}: 'promises' must be a list")
                  promises = []
              for pi, prom in enumerate(promises):
                  if not isinstance(prom, dict):
                      errors.append(
                          f"agent {aname}: promise #{pi + 1}: expected a mapping, "
                          f"got {_describe(prom)}"
                      )
                      continue
                  pid = prom.get("id")
                  if not isinstance(pid, str) or not pid:
                      errors.append(
                          f"agent {aname}: promise #{pi + 1}: missing or invalid required field "
                          "'id' (must be a non-empty string)"
                      )
                      pid = None
                  else:
                      if pid in all_promise_ids:
                          errors.append(
                              f"promise id '{pid}' is duplicated across the model; "
                              "promise ids must be unique"
                          )
                      all_promise_ids.add(pid)
                      if aid:
                          promise_owner[pid] = aid
                  pname = f"'{pid}'" if pid else f"#{pi + 1}"
                  pctx = f"promise {pname} (agent {aname})"
                  summary["promises"] += 1
                  errors.extend(_unknown_field_errors(prom, PROMISE_KEYS, pctx))
      
                  body = prom.get("body")
                  if body is None:
                      errors.append(f"{pctx}: missing required field 'body'")
                  elif not isinstance(body, str) or not body.strip():
                      errors.append(f"{pctx}: 'body' must be a non-empty string")
      
                  ptype = prom.get("type")
                  if ptype is not None:
                      if not isinstance(ptype, str):
                          errors.append(
                              f"{pctx}: 'type' must be a string, got {_describe(ptype)}"
                          )
                      elif ptype not in PROMISE_TYPES:
                          errors.append(
                              f"{pctx}: invalid type '{ptype}' "
                              f"(expected one of: {', '.join(PROMISE_TYPES)})"
                          )
      
                  target = prom.get("target")
                  if target is not None and not isinstance(target, str):
                      errors.append(
                          f"{pctx}: 'target' must be a string (agent id, node id, or 'all'); "
                          f"got {_describe(target)}"
                      )
      
          # ---- nodes ----
          nodes_raw = doc.get("nodes")
          if nodes_raw is None:
              errors.append("missing required top-level key 'nodes'")
              nodes_raw = []
          elif not isinstance(nodes_raw, list):
              errors.append("'nodes' must be a list")
              nodes_raw = []
          if not nodes_raw:
              errors.append("'nodes': collection must be non-empty (at least one semantic element)")
      
          node_ids = []
          for ni, node in enumerate(nodes_raw):
              if not isinstance(node, dict):
                  errors.append(f"node #{ni + 1}: expected a mapping, got {_describe(node)}")
                  continue
              nid = node.get("id")
              if not isinstance(nid, str) or not nid.strip():
                  errors.append(
                      f"node #{ni + 1}: missing or invalid required field 'id' "
                      "(must be a non-empty string)"
                  )
                  nid = None
              else:
                  if not ID_PATTERN.fullmatch(nid):
                      errors.append(
                          f"node id '{nid}' must match ^[a-z0-9]+(?:-[a-z0-9]+)*$ (lowercase-hyphen)"
                      )
                  if nid in node_ids:
                      errors.append(f"node id '{nid}' is duplicated; node ids must be unique")
                  node_ids.append(nid)
              nname = f"'{nid}'" if nid else f"#{ni + 1}"
              summary["nodes"] += 1
              errors.extend(_unknown_field_errors(node, NODE_KEYS, f"node {nname}"))
      
              ntype = node.get("type")
              if ntype is None:
                  errors.append(f"node {nname}: missing required field 'type'")
              elif not isinstance(ntype, str):
                  errors.append(f"node {nname}: 'type' must be a string, got {_describe(ntype)}")
              elif ntype not in NODE_TYPES:
                  errors.append(
                      f"node {nname}: invalid type '{ntype}' "
                      f"(expected one of: {', '.join(NODE_TYPES)})"
                  )
      
          # ---- edges ----
          edges_raw = doc.get("edges")
          if edges_raw is None:
              errors.append("missing required top-level key 'edges'")
              edges_raw = []
          elif not isinstance(edges_raw, list):
              errors.append("'edges' must be a list")
              edges_raw = []
          if not edges_raw:
              errors.append("'edges': collection must be non-empty (at least one gamma(3,4) edge)")
      
          for ei, edge in enumerate(edges_raw):
              if not isinstance(edge, dict):
                  errors.append(f"edge #{ei + 1}: expected a mapping, got {_describe(edge)}")
                  continue
              frm = edge.get("from")
              to = edge.get("to")
              ename = f"edge #{ei + 1}"
              if isinstance(frm, str) and isinstance(to, str):
                  ename = f"edge '{frm} -> {to}'"
              summary["edges"] += 1
              errors.extend(_unknown_field_errors(edge, EDGE_KEYS, ename))
      
              if not isinstance(frm, str) or not frm.strip():
                  errors.append(
                      f"edge #{ei + 1}: missing or invalid required field 'from' "
                      "(must be a declared node id)"
                  )
              elif frm not in node_ids:
                  errors.append(f"{ename}: 'from' references nonexistent node '{frm}'")
      
              if not isinstance(to, str) or not to.strip():
                  errors.append(
                      f"edge #{ei + 1}: missing or invalid required field 'to' "
                      "(must be a declared node id)"
                  )
              elif to not in node_ids:
                  errors.append(f"{ename}: 'to' references nonexistent node '{to}'")
      
              link = edge.get("link")
              if link is None:
                  errors.append(f"{ename}: missing required field 'link'")
              elif not _is_int(link):
                  errors.append(
                      f"{ename}: 'link' must be an integer in -3..3, got {_describe(link)}"
                  )
              elif link < -3 or link > 3:
                  errors.append(f"{ename}: link {link} out of range -3..3")
      
          # ---- acceptances ----
          acceptances_raw = doc.get("acceptances")
          if acceptances_raw is None:
              acceptances_raw = []
          elif not isinstance(acceptances_raw, list):
              errors.append("'acceptances' must be a list")
              acceptances_raw = []
          summary["acceptances"] = sum(
              1 for a in acceptances_raw if isinstance(a, dict)
          )
          accepted_promise_ids = set()
          for ai, acc in enumerate(acceptances_raw):
              if not isinstance(acc, dict):
                  errors.append(f"acceptance #{ai + 1}: expected a mapping, got {_describe(acc)}")
                  continue
              aname = f"acceptance #{ai + 1}"
              errors.extend(_unknown_field_errors(acc, ACCEPTANCE_KEYS, aname))
              pid = acc.get("promise")
              if not isinstance(pid, str) or not pid.strip():
                  errors.append(
                      f"{aname}: missing or invalid required field 'promise' "
                      "(must reference a declared promise id)"
                  )
                  pid = None
              else:
                  if pid not in all_promise_ids:
                      errors.append(
                          f"{aname}: 'promise' references nonexistent promise '{pid}'"
                      )
                  else:
                      accepted_promise_ids.add(pid)
      
              frm = acc.get("from")
              if not isinstance(frm, str) or not frm.strip():
                  errors.append(
                      f"{aname}: missing or invalid required field 'from' "
                      "(must be the agent that declares the promise)"
                  )
              elif pid is not None and promise_owner.get(pid) is not None and frm != promise_owner.get(pid):
                  errors.append(
                      f"{aname}: 'from' value '{frm}' does not equal the declaring "
                      f"agent '{promise_owner.get(pid)}' of promise '{pid}'"
                  )
      
              to = acc.get("to")
              if not isinstance(to, str) or not to.strip():
                  errors.append(
                      f"{aname}: missing or invalid required field 'to' (must be a declared agent id)"
                  )
              elif to not in agent_ids:
                  errors.append(f"{aname}: 'to' references nonexistent agent '{to}'")
          summary["promises_accepted"] = len(accepted_promise_ids)
      
          # ---- trajectories ----
          trajectories_raw = doc.get("trajectories")
          if trajectories_raw is None:
              trajectories_raw = []
          elif not isinstance(trajectories_raw, list):
              errors.append("'trajectories' must be a list")
              trajectories_raw = []
          summary["trajectories"] = sum(
              1 for t in trajectories_raw if isinstance(t, dict)
          )
          seen_traj_ids = []
          for ti, traj in enumerate(trajectories_raw):
              if not isinstance(traj, dict):
                  errors.append(f"trajectory #{ti + 1}: expected a mapping, got {_describe(traj)}")
                  continue
              tname = f"trajectory #{ti + 1}"
              tid = traj.get("id")
              if not isinstance(tid, str) or not tid.strip():
                  errors.append(
                      f"{tname}: missing or invalid required field 'id' (must be a non-empty string)"
                  )
                  tid = None
              else:
                  if tid in seen_traj_ids:
                      errors.append(f"trajectory id '{tid}' is duplicated; trajectory ids must be unique")
                  seen_traj_ids.append(tid)
                  tname = f"trajectory '{tid}'"
              errors.extend(_unknown_field_errors(traj, TRAJECTORY_KEYS, tname))
      
              path = traj.get("path")
              if path is None:
                  errors.append(f"{tname}: missing required field 'path'")
              elif not isinstance(path, list):
                  errors.append(f"{tname}: 'path' must be a list of node ids")
              elif not path:
                  errors.append(f"{tname}: 'path' must have at least one entry")
              else:
                  for entry in path:
                      if not isinstance(entry, str):
                          errors.append(
                              f"{tname}: 'path' entries must be node id strings, got {_describe(entry)}"
                          )
                      elif entry not in node_ids:
                          errors.append(f"{tname}: 'path' references nonexistent node '{entry}'")
      
              label = traj.get("label")
              if label is not None and not isinstance(label, str):
                  errors.append(f"{tname}: 'label' must be a string when present")
      
          # ---- observations ----
          observations_raw = doc.get("observations")
          if observations_raw is None:
              observations_raw = []
          elif not isinstance(observations_raw, list):
              errors.append("'observations' must be a list")
              observations_raw = []
          summary["observations"] = sum(
              1 for o in observations_raw if isinstance(o, dict)
          )
          for oi, obs in enumerate(observations_raw):
              if not isinstance(obs, dict):
                  errors.append(f"observation #{oi + 1}: expected a mapping, got {_describe(obs)}")
                  continue
              oname = f"observation #{oi + 1}"
              at = obs.get("at")
              if not isinstance(at, str) or not at.strip():
                  errors.append(
                      f"{oname}: missing or invalid required field 'at' "
                      "(must be a tick label or timestamp)"
                  )
              else:
                  oname = f"observation '{at}'"
              errors.extend(_unknown_field_errors(obs, OBSERVATION_KEYS, oname))
              event = obs.get("event")
              if not isinstance(event, str) or not event.strip():
                  errors.append(f"{oname}: missing or invalid required field 'event' (must be free text)")
              changed = obs.get("changed")
              if changed is not None:
                  if not isinstance(changed, str):
                      errors.append(
                          f"{oname}: 'changed' must be a node id or promise id string, "
                          f"got {_describe(changed)}"
                      )
                  elif changed not in node_ids and changed not in all_promise_ids:
                      errors.append(
                          f"{oname}: 'changed' references nonexistent node or promise '{changed}'"
                      )
      
          return len(errors) == 0, errors, summary
      
      
      # ---------------------------------------------------------------------------
      # Graph helpers (distance, trajectory, cycles)
      # ---------------------------------------------------------------------------
      
      def _node_ids(doc):
          return [
              n["id"]
              for n in (doc.get("nodes") or [])
              if isinstance(n, dict) and isinstance(n.get("id"), str)
          ]
      
      
      def _edge_triples(doc):
          triples = []
          for e in doc.get("edges") or []:
              if (
                  isinstance(e, dict)
                  and isinstance(e.get("from"), str)
                  and isinstance(e.get("to"), str)
                  and _is_int(e.get("link"))
              ):
                  triples.append((e["from"], e["to"], e["link"]))
          return triples
      
      
      def _link_label(link):
          return LINK_LABELS.get(abs(link), "UNKNOWN")
      
      
      def _slug(label):
          return label.lower().replace(" ", "-")
      
      
      def _weighted_adjacency(nodes, edges):
          adj = {n: [] for n in nodes}
          for frm, to, link in edges:
              if frm in adj and to in adj:
                  adj[frm].append((to, abs(link) + 1))
          for n in adj:
              adj[n].sort()
          return adj
      
      
      def _unweighted_adjacency(nodes, edges):
          adj = {n: [] for n in nodes}
          for frm, to, _link in edges:
              if frm in adj and to in adj:
                  adj[frm].append(to)
          for n in adj:
              adj[n].sort()
          return adj
      
      
      def shortest_path(nodes, edges, start, goal):
          """Weighted shortest directed path. Returns (total_weight, node_path)
          or None when no directed path connects start to goal. Each hop weighs
          |link| + 1 per the module docstring."""
          if start == goal:
              return 0, [start]
          adj = _weighted_adjacency(nodes, edges)
          dist = {n: None for n in nodes}
          prev = {}
          dist[start] = 0
          remaining = set(nodes)
          while remaining:
              candidates = [n for n in remaining if dist[n] is not None]
              if not candidates:
                  break
              current = min(candidates, key=lambda n: (dist[n], n))
              remaining.discard(current)
              if current == goal:
                  break
              for nxt, weight in adj[current]:
                  if nxt in remaining:
                      via = dist[current] + weight
                      if dist[nxt] is None or via < dist[nxt]:
                          dist[nxt] = via
                          prev[nxt] = current
          if dist.get(goal) is None:
              return None
          path = [goal]
          cur = goal
          while cur != start:
              cur = prev[cur]
              path.append(cur)
          path.reverse()
          return dist[goal], path
      
      
      def simple_paths(nodes, edges, start, goal):
          """Every simple directed path from start to goal (no repeated nodes).
      
          Iterative DFS over an explicit stack; terminates on any finite model and
          is capped at _MAX_PATHS to bound worst-case dense graphs. Deterministic:
          adjacency lists are sorted and neighbors are explored left-to-right.
          """
          adj = _unweighted_adjacency(nodes, edges)
          if start not in adj or goal not in adj:
              return []
          paths = []
          stack = [(start, [start])]
          while stack and len(paths) < _MAX_PATHS:
              node, trail = stack.pop()
              if node == goal:
                  paths.append(trail)
                  continue
              for nxt in reversed(adj[node]):
                  if nxt not in trail:
                      stack.append((nxt, trail + [nxt]))
          return paths
      
      
      def _canonical_cycle(cycle):
          """Minimal rotation of the node sequence (excluding the closing repeat)."""
          seq = cycle[:-1]
          rotations = [tuple(seq[i:] + seq[:i]) for i in range(len(seq))]
          return min(rotations)
      
      
      def elementary_cycles(nodes, edges):
          """Directed elementary cycles as node lists (closing node repeated)."""
          adj = _unweighted_adjacency(nodes, edges)
          seen = set()
          out = []
          for root in sorted(adj):
              stack = [(root, [root])]
              while stack:
                  node, trail = stack.pop()
                  for nxt in adj[node]:
                      if nxt == root and len(trail) >= 2:
                          cycle = trail + [root]
                          canon = _canonical_cycle(cycle)
                          if canon not in seen:
                              seen.add(canon)
                              out.append(cycle)
                      elif nxt not in trail:
                          stack.append((nxt, trail + [nxt]))
          return out
      
      
      def render_path(path, edges):
          """Render a node path with edge link annotations, e.g.
          'a -[1:leads-to]-> b -[-2:contains]-> c'."""
          by_pair = {(frm, to): link for frm, to, link in edges}
          parts = [path[0]]
          for left, right in zip(path, path[1:]):
              link = by_pair.get((left, right))
              if link is None:
                  parts.append(f"-[?:unknown]-> {right}")
              else:
                  parts.append(f"-[{link}:{_slug(_link_label(link))}]-> {right}")
          return " ".join(parts)
      
      
      # ---------------------------------------------------------------------------
      # Snapshot drift
      # ---------------------------------------------------------------------------
      
      def _region_index(doc):
          """Index the drift-relevant regions: nodes, edges, observations."""
          index = {"nodes": {}, "edges": {}, "observations": {}}
          for n in doc.get("nodes") or []:
              if isinstance(n, dict) and isinstance(n.get("id"), str):
                  index["nodes"][n["id"]] = n
          for e in doc.get("edges") or []:
              if isinstance(e, dict) and isinstance(e.get("from"), str) and isinstance(e.get("to"), str):
                  index["edges"][f"{e['from']} -> {e['to']}"] = e
          for o in doc.get("observations") or []:
              if isinstance(o, dict) and isinstance(o.get("at"), str):
                  index["observations"][o["at"]] = o
          return index
      
      
      def _region_signature(kind, region):
          if kind == "nodes":
              return region.get("type")
          if kind == "edges":
              return region.get("link")
          return (region.get("event"), region.get("changed"))
      
      
      def diff_snapshots(doc_a, doc_b):
          """Compare two valid models; returns (added, removed, changed) lists."""
          index_a = _region_index(doc_a)
          index_b = _region_index(doc_b)
          added, removed, changed = [], [], []
          for kind, noun in (("nodes", "node"), ("edges", "edge"), ("observations", "observation")):
              keys_a = index_a[kind]
              keys_b = index_b[kind]
              for key in sorted(set(keys_b) - set(keys_a)):
                  added.append(f"{noun} '{key}'")
              for key in sorted(set(keys_a) - set(keys_b)):
                  removed.append(f"{noun} '{key}'")
              for key in sorted(set(keys_a) & set(keys_b)):
                  sig_a = _region_signature(kind, keys_a[key])
                  sig_b = _region_signature(kind, keys_b[key])
                  if sig_a == sig_b:
                      continue
                  if kind == "nodes":
                      changed.append(
                          f"{noun} '{key}': type changed from {sig_a!r} to {sig_b!r}"
                      )
                  elif kind == "edges":
                      changed.append(
                          f"{noun} '{key}': link changed from {sig_a!r} to {sig_b!r}"
                      )
                  else:
                      if keys_a[key].get("event") != keys_b[key].get("event"):
                          changed.append(
                              f"{noun} '{key}': event changed from "
                              f"{keys_a[key].get('event')!r} to {keys_b[key].get('event')!r}"
                          )
                      if keys_a[key].get("changed") != keys_b[key].get("changed"):
                          changed.append(
                              f"{noun} '{key}': changed target changed from "
                              f"{keys_a[key].get('changed')!r} to {keys_b[key].get('changed')!r}"
                          )
          return added, removed, changed
      
      
      # ---------------------------------------------------------------------------
      # Output helpers
      # ---------------------------------------------------------------------------
      
      def _lint_object(valid, errors, summary):
          return {
              "command": "model lint",
              "schema_version": SCHEMA_VERSION,
              "valid": valid,
              "errors": list(errors),
              "coverage": summary,
          }
      
      
      def _error_object(command, message):
          obj = {"command": command, "valid": False}
          if command == "model lint":
              obj["schema_version"] = SCHEMA_VERSION
          obj["errors"] = [message]
          return obj
      
      
      def _print_error_list(command, errors, code, json_mode):
          """Emit a dispatched-path error set (content or IO) honoring --json."""
          if json_mode:
              print(json.dumps({"command": command, "valid": False, "errors": list(errors)}, indent=2))
          else:
              for message in errors:
                  print(f"error: {message}", file=sys.stderr)
          return code
      
      
      def _print_counts(summary):
          print(
              "agents: {agents}, nodes: {nodes}, edges: {edges}, "
              "acceptances: {acceptances}, trajectories: {trajectories}, "
              "observations: {observations}".format(**summary)
          )
      
      
      # ---------------------------------------------------------------------------
      # Commands
      # ---------------------------------------------------------------------------
      
      def cmd_lint(path, json_mode):
          try:
              doc = load_model(path)
          except ModelError as exc:
              if json_mode:
                  print(json.dumps(_error_object("model lint", str(exc)), indent=2))
              else:
                  print(f"error: {exc}", file=sys.stderr)
              return exc.exit_code
      
          valid, errors, summary = validate_model(doc)
          if json_mode:
              print(json.dumps(_lint_object(valid, errors, summary), indent=2))
              return 0 if valid else 1
      
          print(f"schema: {SCHEMA_VERSION}")
          if valid:
              print("valid: true")
          else:
              print(f"valid: false ({len(errors)} error(s))")
          _print_counts(summary)
          print(
              f"coverage: {summary['promises_accepted']}/{summary['promises']} "
              "promises referenced by acceptances"
          )
          for message in errors:
              print(f"error: {message}", file=sys.stderr)
          return 0 if valid else 1
      
      
      def cmd_map(path, fmt, json_mode):
          try:
              doc = load_model(path)
          except ModelError as exc:
              if json_mode:
                  print(json.dumps(_error_object("model map", str(exc)), indent=2))
              else:
                  print(f"error: {exc}", file=sys.stderr)
              return exc.exit_code
      
          valid, errors, _summary = validate_model(doc)
          if not valid:
              return _print_error_list("model map", errors, 1, json_mode)
      
          nodes = [
              {"id": n["id"], "type": n["type"]}
              for n in (doc.get("nodes") or [])
              if isinstance(n, dict) and isinstance(n.get("id"), str) and isinstance(n.get("type"), str)
          ]
          edges = []
          for frm, to, link in _edge_triples(doc):
              edges.append({"from": frm, "to": to, "link": link, "label": _link_label(link)})
      
          json_object = {
              "command": "model map",
              "schema_version": SCHEMA_VERSION,
              "format": "json",
              "nodes": nodes,
              "edges": edges,
          }
          if json_mode or fmt == "json":
              print(json.dumps(json_object, indent=2))
              return 0
          if fmt == "mermaid":
              lines = ["graph LR"]
              for n in nodes:
                  lines.append(f'  {n["id"]}["{n["id"]}"]:::{n["type"]}')
              for e in edges:
                  lines.append(f'  {e["from"]} -->|"{e["link"]} {e["label"]}"| {e["to"]}')
              print("\n".join(lines))
              return 0
      
          print(f"semantic spacetime map ({SCHEMA_VERSION})")
          print("nodes:")
          for n in nodes:
              print(f"  {n['id']} [{n['type']}]")
          print("edges:")
          for e in edges:
              print(f"  {e['from']} -[{e['link']}:{_slug(e['label'])}]-> {e['to']}")
          return 0
      
      
      def cmd_distance(path, frm, to, json_mode):
          try:
              doc = load_model(path)
          except ModelError as exc:
              if json_mode:
                  print(json.dumps(_error_object("model distance", str(exc)), indent=2))
              else:
                  print(f"error: {exc}", file=sys.stderr)
              return exc.exit_code
      
          valid, errors, _summary = validate_model(doc)
          if not valid:
              return _print_error_list("model distance", errors, 1, json_mode)
      
          nodes = _node_ids(doc)
          if frm not in nodes:
              message = f"unknown node '{frm}' (referenced by --from)"
              return _print_error_list("model distance", [message], 1, json_mode)
          if to not in nodes:
              message = f"unknown node '{to}' (referenced by --to)"
              return _print_error_list("model distance", [message], 1, json_mode)
      
          edges = _edge_triples(doc)
          result = shortest_path(nodes, edges, frm, to)
          if result is None:
              message = f"no path from '{frm}' to '{to}'"
              return _print_error_list("model distance", [message], 1, json_mode)
          distance, path = result
          hops = len(path) - 1
      
          if json_mode:
              print(
                  json.dumps(
                      {
                          "command": "model distance",
                          "schema_version": SCHEMA_VERSION,
                          "from": frm,
                          "to": to,
                          "distance": distance,
                          "hops": hops,
                          "path": path,
                      },
                      indent=2,
                  )
              )
              return 0
          print(f"distance from '{frm}' to '{to}': {distance} ({hops} hop(s))")
          print("path: " + " -> ".join(path))
          return 0
      
      
      def cmd_trajectory(path, frm, to, json_mode):
          try:
              doc = load_model(path)
          except ModelError as exc:
              if json_mode:
                  print(json.dumps(_error_object("model trajectory", str(exc)), indent=2))
              else:
                  print(f"error: {exc}", file=sys.stderr)
              return exc.exit_code
      
          valid, errors, _summary = validate_model(doc)
          if not valid:
              return _print_error_list("model trajectory", errors, 1, json_mode)
      
          nodes = _node_ids(doc)
          if frm not in nodes:
              message = f"unknown node '{frm}' (referenced by --from)"
              return _print_error_list("model trajectory", [message], 1, json_mode)
          if to not in nodes:
              message = f"unknown node '{to}' (referenced by --to)"
              return _print_error_list("model trajectory", [message], 1, json_mode)
      
          edges = _edge_triples(doc)
          paths = simple_paths(nodes, edges, frm, to)
          if not paths:
              message = f"no path from '{frm}' to '{to}'"
              return _print_error_list("model trajectory", [message], 1, json_mode)
          cycles = elementary_cycles(nodes, edges)
      
          if json_mode:
              print(
                  json.dumps(
                      {
                          "command": "model trajectory",
                          "schema_version": SCHEMA_VERSION,
                          "from": frm,
                          "to": to,
                          "paths": [
                              {"nodes": path, "render": render_path(path, edges)} for path in paths
                          ],
                          "cycles": cycles,
                          "path_count": len(paths),
                      },
                      indent=2,
                  )
              )
              return 0
      
          print(f"paths from '{frm}' to '{to}':")
          for path in paths:
              print("  " + render_path(path, edges))
          print(f"{len(paths)} path(s)")
          for cycle in cycles:
              print("note: cycle detected: " + " -> ".join(cycle))
          return 0
      
      
      def cmd_drift(path_a, path_b, json_mode):
          try:
              doc_a = load_model(path_a)
              doc_b = load_model(path_b)
          except ModelError as exc:
              if json_mode:
                  print(json.dumps(_error_object("model drift", str(exc)), indent=2))
              else:
                  print(f"error: {exc}", file=sys.stderr)
              return exc.exit_code
      
          valid_a, errors_a, _sa = validate_model(doc_a)
          valid_b, errors_b, _sb = validate_model(doc_b)
          if not valid_a or not valid_b:
              combined = []
              if not valid_a:
                  combined.extend(f"{path_a}: {e}" for e in errors_a)
              if not valid_b:
                  combined.extend(f"{path_b}: {e}" for e in errors_b)
              return _print_error_list("model drift", combined, 1, json_mode)
      
          added, removed, changed = diff_snapshots(doc_a, doc_b)
          has_drift = bool(added or removed or changed)
      
          if json_mode:
              print(
                  json.dumps(
                      {
                          "command": "model drift",
                          "schema_version": SCHEMA_VERSION,
                          "a": path_a,
                          "b": path_b,
                          "drift": has_drift,
                          "added": added,
                          "removed": removed,
                          "changed": changed,
                      },
                      indent=2,
                  )
              )
              return 0
      
          print(f"semantic drift between '{path_a}' and '{path_b}':")
          if not has_drift:
              print("no drift: the two snapshots are identical")
              return 0
          for header, items in (
              ("added regions:", added),
              ("removed regions:", removed),
              ("changed regions:", changed),
          ):
              if items:
                  print(header)
                  for item in items:
                      print(f"  {item}")
          return 0
      
      
      # ---------------------------------------------------------------------------
      # Argument parsing and entry point
      # ---------------------------------------------------------------------------
      
      _SWITCH_OPTIONS = ("--json", "--dry-run")
      _VALUE_OPTIONS = ("--format", "--from", "--to")
      
      
      def _split_options(tokens, valued):
          """Split remaining tokens into positional files and valued options.
      
          Returns (files, values, error); `valued` is the option set that consumes
          the next token. Any other '-' token is an unknown option.
          """
          files = []
          values = {}
          i = 0
          while i < len(tokens):
              token = tokens[i]
              if token in valued:
                  if token in values:
                      return None, None, f"duplicate option '{token}'"
                  if i + 1 >= len(tokens) or tokens[i + 1].startswith("-"):
                      return None, None, f"missing value for option '{token}'"
                  values[token] = tokens[i + 1]
                  i += 2
                  continue
              if token.startswith("-"):
                  return None, None, f"unknown option '{token}'"
              files.append(token)
              i += 1
          return files, values, None
      
      
      def parse_args(argv):
          """Parse argv into (opts, action, error).
      
          opts holds the subcommand plus parsed options; action is 'help', 'version',
          or None; error is a usage message (exit 2, text to stderr, never JSON).
          """
          opts = {"json": False, "dry_run": False}
          positionals = []
          i = 0
          while i < len(argv):
              token = argv[i]
              if token in ("--help", "-h"):
                  return opts, "help", None
              if token == "--version":
                  return opts, "version", None
              if token == "--json":
                  opts["json"] = True
                  i += 1
                  continue
              if token == "--dry-run":
                  opts["dry_run"] = True
                  i += 1
                  continue
              if token in _VALUE_OPTIONS:
                  if i + 1 >= len(argv) or argv[i + 1].startswith("-"):
                      return opts, None, f"missing value for option '{token}'"
                  positionals.append(token)
                  positionals.append(argv[i + 1])
                  i += 2
                  continue
              if token.startswith("-"):
                  return opts, None, f"unknown option '{token}'"
              positionals.append(token)
              i += 1
      
          if not positionals:
              return opts, None, "missing command"
          if positionals[0] != "model":
              return opts, None, f"expected 'model', got '{positionals[0]}'"
          if len(positionals) < 2:
              return opts, None, (
                  "missing subcommand for 'model' (lint|map|distance|trajectory|drift)"
              )
          sub = positionals[1]
          if sub not in SUBCOMMANDS:
              return opts, None, (
                  f"unknown subcommand '{sub}' (expected one of: "
                  f"{', '.join(SUBCOMMANDS)})"
              )
          opts["sub"] = sub
          rest = positionals[2:]
      
          if sub == "lint":
              files, _values, err = _split_options(rest, set())
              if err:
                  return opts, None, err
              if not files:
                  return opts, None, "missing file argument for 'model lint'"
              if len(files) > 1:
                  return opts, None, f"unexpected extra argument '{files[1]}'"
              opts["file"] = files[0]
          elif sub == "map":
              files, values, err = _split_options(rest, {"--format"})
              if err:
                  return opts, None, err
              if not files:
                  return opts, None, "missing file argument for 'model map'"
              if len(files) > 1:
                  return opts, None, f"unexpected extra argument '{files[1]}'"
              if "--format" not in values:
                  return opts, None, (
                      "missing required flag '--format' for 'model map' (text|mermaid|json)"
                  )
              fmt = values["--format"]
              if fmt not in MAP_FORMATS:
                  return opts, None, (
                      f"invalid format '{fmt}' (expected one of: {', '.join(MAP_FORMATS)})"
                  )
              opts["file"] = files[0]
              opts["format"] = fmt
          elif sub in ("distance", "trajectory"):
              files, values, err = _split_options(rest, {"--from", "--to"})
              if err:
                  return opts, None, err
              if not files:
                  return opts, None, f"missing file argument for 'model {sub}'"
              if len(files) > 1:
                  return opts, None, f"unexpected extra argument '{files[1]}'"
              missing = [flag for flag in ("--from", "--to") if flag not in values]
              if missing:
                  return opts, None, (
                      f"missing required flag(s) {', '.join(missing)} for 'model {sub}'"
                  )
              opts["file"] = files[0]
              opts["from"] = values["--from"]
              opts["to"] = values["--to"]
          elif sub == "drift":
              files, _values, err = _split_options(rest, set())
              if err:
                  return opts, None, err
              if len(files) < 2:
                  return opts, None, (
                      "missing file argument for 'model drift' (expected two snapshot paths)"
                  )
              if len(files) > 2:
                  return opts, None, f"unexpected extra argument '{files[2]}'"
              opts["file_a"] = files[0]
              opts["file_b"] = files[1]
          return opts, None, None
      
      
      def main(argv=None):
          argv = list(sys.argv[1:] if argv is None else argv)
          opts, action, err = parse_args(argv)
          if err:
              print(f"error: {err}", file=sys.stderr)
              print(USAGE, file=sys.stderr)
              return 2
          if action == "help":
              print(USAGE)
              return 0
          if action == "version":
              print(VERSION)
              return 0
      
          sub = opts["sub"]
          if sub == "lint":
              return cmd_lint(opts["file"], opts["json"])
          if sub == "map":
              return cmd_map(opts["file"], opts["format"], opts["json"])
          if sub == "distance":
              return cmd_distance(opts["file"], opts["from"], opts["to"], opts["json"])
          if sub == "trajectory":
              return cmd_trajectory(opts["file"], opts["from"], opts["to"], opts["json"])
          return cmd_drift(opts["file_a"], opts["file_b"], opts["json"])
      
      
      if __name__ == "__main__":
          sys.exit(main())
      
  • templates
    • sst-analysis.md.tmpl 3.5 KB · in bundle
    • sst-model.yaml.tmpl 5.5 KB · in bundle
  • tests
    • fixtures
      • invalid-model.yaml 746 B
        # Deliberately invalid sst-model-v1 model (fixture for the strict-schema lint
        # tests). Every field satisfies the normal rules EXCEPT the two strict-schema
        # violations below, so lint reports exactly these named violations:
        #   * 'bogus-field' is an unknown field inside node 'report-event'
        #   * 'regions' is an unknown top-level section
        # Expect 'model lint' to exit 1 and name both the key and its location.
        schema_version: 1
        agents:
          - id: operator
            role: workflow operator
            promises:
              - id: deliver-report
                body: Deliver the weekly status report by Friday.
        nodes:
          - id: report-event
            type: event
            bogus-field: 42
        edges:
          - from: report-event
            to: report-event
            link: 1
        regions:
          - id: r1
            kind: concept
        
      • sample-model.yaml 2.5 KB
        # --- example ---
        schema_version: 1
        agents:
          - id: operator              # required; unique; lowercase-hyphen
            role: workflow operator   # required; free text
            promises:                 # optional; scalar promises this agent makes
              - id: deliver-report    # required; unique across the whole model
                body: Deliver the weekly status report by Friday.   # required; free text
                type: capability      # optional; capability | intent | constraint
                target: reviewer      # optional; <agent-id> | <node-id> | all
              - id: no-unverified-claims
                body: Never assert a claim without a measured source.
                type: constraint
                target: all
          - id: reviewer
            role: semantic reviewer
            promises:
              - id: review-report
                body: Review the report for semantic drift against the agreed vocabulary.
                type: capability
                target: operator
        nodes:                        # required; at least one semantic element
          - id: report-event          # required; unique; lowercase-hyphen
            type: event               # required; event | thing | concept
          - id: report-thing
            type: thing
          - id: drift-concept
            type: concept
        edges:                        # required; at least one gamma(3,4) edge
          - from: report-event        # required; must be a declared node id
            to: report-thing          # required; must be a declared node id
            link: 1                   # required; integer in -3..3
          - from: report-thing
            to: drift-concept
            link: 3
          - from: drift-concept
            to: report-thing
            link: 2
        acceptances:                  # optional; cross-agent acceptance records
          - promise: deliver-report   # required; must reference a declared promise id
            from: operator            # required; must equal the agent that declares it
            to: reviewer              # required; a declared agent id
        trajectories:                 # optional; declared paths through the graph
          - id: report-flow           # required; unique
            path: [report-event, report-thing, drift-concept]  # required; node ids, >= 1 entry
            label: report moves from event to reviewed thing   # optional; free text
        observations:                 # optional; the proper-time record
          - at: t1                    # required; tick label or timestamp, free text
            event: report drafted     # required; what changed, free text
            changed: report-event     # optional; a declared node id or promise id
          - at: t2
            event: reviewer flags drift in vocabulary
            changed: drift-concept
        # --- end example ---
        
    • test_semantic_spacetime.py 33.4 KB
      """Unit tests for semantic-spacetime/scripts/semantic-spacetime.py.
      
      Run from the repository root:
      
          python3 -m unittest discover -s semantic-spacetime/tests -p 'test_*.py'
      
      The tests exercise the CLI black-box (subprocess) so they pin the observable
      contract: exit codes (0 ok / 1 invalid model or input / 2 usage or IO),
      stdout/stderr separation, --json single-object purity, --dry-run no-writes,
      and the never-a-traceback rule. They also cover the sst-model-v1 template
      contract (the delimited example lints clean) and the tracked sample fixture.
      
      check-artifacts.py discovers this file with top_level_dir = the tests dir, so
      skill-root paths are resolved via explicit sys.path handling below.
      """
      
      import ast
      import json
      import os
      import subprocess
      import sys
      import tempfile
      import unittest
      
      SKILL_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
      sys.path.insert(0, SKILL_ROOT)
      
      SCRIPT = os.path.join(SKILL_ROOT, "scripts", "semantic-spacetime.py")
      TEMPLATE_PATH = os.path.join(SKILL_ROOT, "templates", "sst-model.yaml.tmpl")
      FIXTURE_PATH = os.path.join(SKILL_ROOT, "tests", "fixtures", "sample-model.yaml")
      INVALID_FIXTURE_PATH = os.path.join(
          SKILL_ROOT, "tests", "fixtures", "invalid-model.yaml"
      )
      REPO_ROOT = os.path.dirname(SKILL_ROOT)
      
      VALID_YAML = """schema_version: 1
      agents:
        - id: operator
          role: workflow operator
          promises:
            - id: deliver-report
              body: Deliver the weekly status report by Friday.
              type: capability
              target: reviewer
        - id: reviewer
          role: semantic reviewer
          promises:
            - id: review-report
              body: Review the report for semantic drift.
              type: capability
              target: operator
      nodes:
        - id: report-event
          type: event
        - id: report-thing
          type: thing
      edges:
        - from: report-event
          to: report-thing
          link: 1
        - from: report-thing
          to: report-event
          link: 3
      acceptances:
        - promise: deliver-report
          from: operator
          to: reviewer
      """
      
      CYCLIC_YAML = """schema_version: 1
      agents:
        - id: operator
          role: workflow operator
          promises:
            - id: deliver-report
              body: Deliver the weekly status report by Friday.
      nodes:
        - id: a
          type: event
        - id: b
          type: thing
        - id: c
          type: concept
      edges:
        - from: a
          to: b
          link: 1
        - from: b
          to: c
          link: 1
        - from: c
          to: a
          link: 1
        - from: a
          to: c
          link: 2
      """
      
      DISCONNECTED_YAML = """schema_version: 1
      agents:
        - id: operator
          role: workflow operator
          promises:
            - id: p1
              body: do something
      nodes:
        - id: left-a
          type: event
        - id: left-b
          type: thing
        - id: right-x
          type: thing
      edges:
        - from: left-a
          to: left-b
          link: 1
        - from: right-x
          to: left-a
          link: 3
      """
      
      MALFORMED_YAML = """schema_version: 1
      agents:
        - id: operator
          role: "unclosed quote
          promises:
      """
      
      
      def _extract_template_example():
          """Extract the machine-delimited example block from the template.
      
          The block runs from the line exactly '# --- example ---' through the line
          exactly '# --- end example ---' (inclusive). The markers also appear in
          prose inside the template's FILLING GUIDE, so matching must be line-exact.
          """
          with open(TEMPLATE_PATH, encoding="utf-8") as fh:
              lines = fh.read().split("\n")
          start = end = None
          for i, line in enumerate(lines):
              if line.strip() == "# --- example ---":
                  start = i
              elif line.strip() == "# --- end example ---":
                  end = i
          assert start is not None and end is not None and start < end
          return "\n".join(lines[start:end + 1]) + "\n"
      
      
      class SemanticSpacetimeCliTest(unittest.TestCase):
          """Black-box CLI tests for the sst-model-v1 tool."""
      
          def run_cli(self, *args, cwd=None):
              return subprocess.run(
                  [sys.executable, SCRIPT, *args], capture_output=True, text=True, cwd=cwd
              )
      
          def write_tmp(self, name, content, binary=False):
              path = os.path.join(self.tmpdir, name)
              mode = "wb" if binary else "w"
              kwargs = {} if binary else {"encoding": "utf-8"}
              with open(path, mode, **kwargs) as fh:
                  fh.write(content)
              return path
      
          def setUp(self):
              self._tmp = tempfile.TemporaryDirectory()
              self.tmpdir = self._tmp.name
              self.valid_path = self.write_tmp("valid.yaml", VALID_YAML)
      
          def tearDown(self):
              self._tmp.cleanup()
      
          # -- basics (VAL-CLI-001) -------------------------------------------------
      
          def test_help_lists_all_subcommands_and_flags(self):
              p = self.run_cli("--help")
              self.assertEqual(p.returncode, 0)
              out = p.stdout.lower()
              for needle in (
                  "model lint",
                  "model map",
                  "model distance",
                  "model trajectory",
                  "model drift",
                  "--json",
                  "--dry-run",
                  "sst-model-v1",
              ):
                  self.assertIn(needle, out)
      
          def test_version_is_dotted_triple(self):
              p = self.run_cli("--version")
              self.assertEqual(p.returncode, 0)
              self.assertRegex(p.stdout.strip(), r"^\d+\.\d+\.\d+$")
      
          def test_bare_invocation_exits_2_with_usage_on_stderr(self):
              p = self.run_cli()
              self.assertEqual(p.returncode, 2)
              self.assertEqual(p.stdout, "")
              self.assertIn("usage:", p.stderr.lower())
              self.assertNotIn("Traceback", p.stderr)
      
          # -- lint (VAL-CLI-002/003/013, VAL-CROSS-018) -----------------------------
      
          def test_valid_yaml_lints_clean_with_coverage(self):
              p = self.run_cli("model", "lint", self.valid_path)
              self.assertEqual(p.returncode, 0, p.stdout + p.stderr)
              out = p.stdout.lower()
              self.assertIn("valid", out)
              self.assertIn("cover", out)
              self.assertIn("sst-model-v1", out)
      
          def test_lint_json_shape(self):
              p = self.run_cli("model", "lint", self.valid_path, "--json")
              self.assertEqual(p.returncode, 0)
              data = json.loads(p.stdout)
              self.assertEqual(
                  set(data), {"command", "schema_version", "valid", "errors", "coverage"}
              )
              self.assertIs(data["valid"], True)
              self.assertEqual(data["errors"], [])
              self.assertEqual(data["schema_version"], "sst-model-v1")
              self.assertGreaterEqual(data["coverage"]["nodes"], 1)
              self.assertGreaterEqual(data["coverage"]["edges"], 1)
      
          def test_invalid_node_type_named_violation(self):
              bad = self.write_tmp(
                  "bad-node.yaml",
                  VALID_YAML.replace("type: event\n", "type: object\n", 1),
              )
              p = self.run_cli("model", "lint", bad)
              self.assertEqual(p.returncode, 1)
              combined = p.stdout + p.stderr
              self.assertIn("report-event", combined)
              self.assertIn("object", combined)
              self.assertIn("event, thing, concept", combined)
      
          def test_invalid_link_value_named_violation(self):
              bad = self.write_tmp(
                  "bad-link.yaml",
                  VALID_YAML.replace("link: 1\n", "link: 5\n", 1),
              )
              p = self.run_cli("model", "lint", bad)
              self.assertEqual(p.returncode, 1)
              combined = p.stdout + p.stderr
              self.assertIn("5", combined)
              self.assertIn("-3..3", combined)
      
          def test_dangling_edge_reference_named_violation(self):
              bad = self.write_tmp(
                  "dangling-edge.yaml",
                  VALID_YAML.replace("from: report-event\n", "from: ghost-node\n", 1),
              )
              p = self.run_cli("model", "lint", bad)
              self.assertEqual(p.returncode, 1)
              combined = p.stdout + p.stderr
              self.assertIn("ghost-node", combined)
              self.assertIn("'from'", combined)
      
          def test_dangling_acceptance_named_violation(self):
              bad = self.write_tmp(
                  "dangling-acceptance.yaml",
                  VALID_YAML.replace(
                      "promise: deliver-report\n", "promise: ghost-promise\n", 1
                  ),
              )
              p = self.run_cli("model", "lint", bad)
              self.assertEqual(p.returncode, 1)
              combined = p.stdout + p.stderr
              self.assertIn("ghost-promise", combined)
      
          def test_dangling_trajectory_named_violation(self):
              model = VALID_YAML + "\ntrajectories:\n  - id: t1\n    path: [report-event, ghost]\n"
              bad = self.write_tmp("dangling-trajectory.yaml", model)
              p = self.run_cli("model", "lint", bad)
              self.assertEqual(p.returncode, 1)
              combined = p.stdout + p.stderr
              self.assertIn("ghost", combined)
      
          def test_violations_accumulate_all(self):
              model = VALID_YAML.replace("type: event\n", "type: object\n", 1).replace(
                  "link: 1\n", "link: 9\n", 1
              )
              bad = self.write_tmp("multi-error.yaml", model)
              p = self.run_cli("model", "lint", bad)
              self.assertEqual(p.returncode, 1)
              combined = p.stdout + p.stderr
              self.assertIn("object", combined)
              self.assertIn("9", combined)
      
          # -- strict schema rejection (VAL-CROSS-009) -------------------------------
      
          def test_unknown_top_level_section_rejected(self):
              # 'regions:' is outside the sst-model-v1 schema: exit 1 with a named
              # violation naming the key and its (top-level) location.
              p = self.run_cli("model", "lint", INVALID_FIXTURE_PATH)
              self.assertEqual(p.returncode, 1)
              combined = p.stdout + p.stderr
              self.assertIn("regions", combined)
              self.assertIn("unknown top-level", combined)
              self.assertNotIn("Traceback", combined)
      
          def test_unknown_field_in_node_rejected(self):
              # 'bogus-field: 42' inside node 'report-event' is a named violation in
              # the --json errors list, with the key and its location.
              p = self.run_cli("model", "lint", INVALID_FIXTURE_PATH, "--json")
              self.assertEqual(p.returncode, 1)
              data = json.loads(p.stdout)
              self.assertIs(data["valid"], False)
              self.assertTrue(data["errors"])
              joined = "\n".join(data["errors"])
              self.assertIn("bogus-field", joined)
              self.assertIn("unknown field", joined)
              self.assertIn("report-event", joined)
              self.assertIn("regions", joined)
              self.assertEqual(p.stderr, "")
      
          def test_unknown_fields_in_other_sections_named(self):
              model = VALID_YAML.replace(
                  "role: workflow operator\n", "role: workflow operator\n    bogus: 1\n", 1
              ).replace("link: 1\n", "link: 1\n    bogus: 2\n", 1)
              bad = self.write_tmp("bad-sections.yaml", model)
              p = self.run_cli("model", "lint", bad, "--json")
              self.assertEqual(p.returncode, 1)
              data = json.loads(p.stdout)
              self.assertIs(data["valid"], False)
              joined = "\n".join(data["errors"])
              self.assertEqual(joined.count("unknown field 'bogus'"), 2)
              self.assertIn("agent 'operator'", joined)
              self.assertIn("edge 'report-event -> report-thing'", joined)
      
          # -- template contract (VAL-CLI-012, VAL-CROSS-008, VAL-ROUTE-020) ---------
      
          def test_template_example_block_lints_clean(self):
              block = _extract_template_example()
              path = self.write_tmp("template-example.yaml", block)
              p = self.run_cli("model", "lint", path)
              self.assertEqual(p.returncode, 0, p.stdout + p.stderr)
              self.assertIn("valid", p.stdout.lower())
              self.assertIn("cover", p.stdout.lower())
      
          def test_sample_fixture_lints_clean_and_matches_template(self):
              with open(FIXTURE_PATH, encoding="utf-8") as fh:
                  fixture_text = fh.read()
              self.assertEqual(fixture_text.strip(), _extract_template_example().strip())
              p = self.run_cli("model", "lint", FIXTURE_PATH)
              self.assertEqual(p.returncode, 0, p.stdout + p.stderr)
              p2 = self.run_cli("model", "lint", FIXTURE_PATH, "--json")
              data = json.loads(p2.stdout)
              self.assertIs(data["valid"], True)
              self.assertEqual(data["coverage"]["agents"], 2)
              self.assertEqual(data["coverage"]["nodes"], 3)
              self.assertEqual(data["coverage"]["edges"], 3)
      
          # -- JSON equivalence (VAL-CLI-021) ----------------------------------------
      
          def test_yaml_json_equivalence_byte_identical(self):
              with open(FIXTURE_PATH, encoding="utf-8") as fh:
                  sample = fh.read()
              sample_json = json.dumps(
                  {
                      "schema_version": 1,
                      "agents": [
                          {
                              "id": "operator",
                              "role": "workflow operator",
                              "promises": [
                                  {
                                      "id": "deliver-report",
                                      "body": "Deliver the weekly status report by Friday.",
                                      "type": "capability",
                                      "target": "reviewer",
                                  },
                                  {
                                      "id": "no-unverified-claims",
                                      "body": "Never assert a claim without a measured source.",
                                      "type": "constraint",
                                      "target": "all",
                                  },
                              ],
                          },
                          {
                              "id": "reviewer",
                              "role": "semantic reviewer",
                              "promises": [
                                  {
                                      "id": "review-report",
                                      "body": "Review the report for semantic drift against the agreed vocabulary.",
                                      "type": "capability",
                                      "target": "operator",
                                  }
                              ],
                          },
                      ],
                      "nodes": [
                          {"id": "report-event", "type": "event"},
                          {"id": "report-thing", "type": "thing"},
                          {"id": "drift-concept", "type": "concept"},
                      ],
                      "edges": [
                          {"from": "report-event", "to": "report-thing", "link": 1},
                          {"from": "report-thing", "to": "drift-concept", "link": 3},
                          {"from": "drift-concept", "to": "report-thing", "link": 2},
                      ],
                      "acceptances": [
                          {"promise": "deliver-report", "from": "operator", "to": "reviewer"}
                      ],
                      "trajectories": [
                          {
                              "id": "report-flow",
                              "path": ["report-event", "report-thing", "drift-concept"],
                              "label": "report moves from event to reviewed thing",
                          }
                      ],
                      "observations": [
                          {"at": "t1", "event": "report drafted", "changed": "report-event"},
                          {
                              "at": "t2",
                              "event": "reviewer flags drift in vocabulary",
                              "changed": "drift-concept",
                          },
                      ],
                  }
              )
              yaml_path = self.write_tmp("sample-as-yaml.yaml", sample)
              json_path = self.write_tmp("sample-as-json.json", sample_json)
              py = self.run_cli("model", "lint", yaml_path, "--json")
              pj = self.run_cli("model", "lint", json_path, "--json")
              self.assertEqual(py.returncode, 0)
              self.assertEqual(pj.returncode, 0)
              self.assertEqual(py.stdout, pj.stdout)
              self.assertEqual(json.loads(py.stdout), json.loads(pj.stdout))
      
          # -- restricted subset (VAL-CLI-021) ---------------------------------------
      
          def test_out_of_subset_anchor_rejected(self):
              bad = self.write_tmp("anchor.yaml", "schema_version: 1\nagents: &a\n  x: 1\n")
              p = self.run_cli("model", "lint", bad)
              self.assertEqual(p.returncode, 1)
              self.assertIn(bad, p.stdout + p.stderr)
              self.assertNotIn("Traceback", p.stdout + p.stderr)
      
          def test_out_of_subset_block_scalar_rejected(self):
              bad = self.write_tmp("block.yaml", "schema_version: 1\nagents: |\n  x\n")
              p = self.run_cli("model", "lint", bad)
              self.assertEqual(p.returncode, 1)
              self.assertIn(bad, p.stdout + p.stderr)
              self.assertNotIn("Traceback", p.stdout + p.stderr)
      
          def test_out_of_subset_multi_document_rejected(self):
              bad = self.write_tmp("multi.yaml", "---\nschema_version: 1\n")
              p = self.run_cli("model", "lint", bad)
              self.assertEqual(p.returncode, 1)
              self.assertIn(bad, p.stdout + p.stderr)
              self.assertNotIn("Traceback", p.stdout + p.stderr)
      
          # -- map (VAL-CLI-004) ------------------------------------------------------
      
          def test_map_text_names_nodes_and_edges_with_labels(self):
              p = self.run_cli("model", "map", self.valid_path, "--format", "text")
              self.assertEqual(p.returncode, 0, p.stdout + p.stderr)
              self.assertIn("report-event", p.stdout)
              self.assertIn("report-thing", p.stdout)
              self.assertIn("event", p.stdout)
              self.assertIn("thing", p.stdout)
              self.assertIn("leads-to", p.stdout.lower())
              self.assertIn("expresses", p.stdout.lower())
      
          def test_map_mermaid_is_graph_block(self):
              p = self.run_cli("model", "map", self.valid_path, "--format", "mermaid")
              self.assertEqual(p.returncode, 0, p.stdout + p.stderr)
              self.assertTrue(p.stdout.startswith("graph"))
              self.assertIn("report-event", p.stdout)
              self.assertIn("report-thing", p.stdout)
              self.assertIn("LEADS TO", p.stdout)
      
          def test_map_json_is_single_object(self):
              p = self.run_cli("model", "map", self.valid_path, "--format", "json")
              self.assertEqual(p.returncode, 0, p.stdout + p.stderr)
              data = json.loads(p.stdout)
              node_ids = {n["id"] for n in data["nodes"]}
              edge_pairs = {(e["from"], e["to"], e["link"]) for e in data["edges"]}
              self.assertEqual(node_ids, {"report-event", "report-thing"})
              self.assertIn(("report-event", "report-thing", 1), edge_pairs)
              self.assertIn(("report-thing", "report-event", 3), edge_pairs)
      
          def test_map_invalid_format_exits_2_naming_value(self):
              p = self.run_cli("model", "map", self.valid_path, "--format", "ascii-art")
              self.assertEqual(p.returncode, 2)
              self.assertIn("ascii-art", p.stderr)
              self.assertEqual(p.stdout, "")
      
          # -- distance (VAL-CLI-005/022) --------------------------------------------
      
          def test_distance_known_pair_is_deterministic_number(self):
              a = self.run_cli(
                  "model", "distance", self.valid_path, "--from", "report-event", "--to", "report-thing"
              )
              b = self.run_cli(
                  "model", "distance", self.valid_path, "--from", "report-event", "--to", "report-thing"
              )
              self.assertEqual(a.returncode, 0)
              self.assertEqual(a.stdout, b.stdout)
              match = [tok for tok in a.stdout.split() if tok.replace("-", "").isdigit()]
              self.assertTrue(match)
      
          def test_distance_json_has_numeric_distance(self):
              p = self.run_cli(
                  "model",
                  "distance",
                  self.valid_path,
                  "--from",
                  "report-event",
                  "--to",
                  "report-thing",
                  "--json",
              )
              self.assertEqual(p.returncode, 0, p.stdout + p.stderr)
              data = json.loads(p.stdout)
              self.assertIsInstance(data["distance"], int)
              self.assertEqual(data["path"], ["report-event", "report-thing"])
      
          def test_distance_missing_id_exits_1_naming_id(self):
              p = self.run_cli(
                  "model", "distance", self.valid_path, "--from", "ghost", "--to", "report-thing"
              )
              self.assertEqual(p.returncode, 1)
              self.assertIn("ghost", p.stdout + p.stderr)
      
          def test_distance_missing_flags_exits_2(self):
              p = self.run_cli("model", "distance", self.valid_path)
              self.assertEqual(p.returncode, 2)
              self.assertIn("--from", p.stderr)
      
          def test_distance_no_path_exits_1_naming_both_ids(self):
              path = self.write_tmp("disconnected.yaml", DISCONNECTED_YAML)
              p = self.run_cli(
                  "model", "distance", path, "--from", "left-a", "--to", "right-x"
              )
              self.assertEqual(p.returncode, 1)
              combined = p.stdout + p.stderr
              self.assertIn("left-a", combined)
              self.assertIn("right-x", combined)
      
          # -- trajectory (VAL-CLI-006/023) ------------------------------------------
      
          def test_trajectory_enumerates_paths_with_link_types(self):
              # the sample fixture chains report-event -[1:leads-to]-> report-thing
              # -[3:expresses]-> drift-concept, so both link types appear on one path
              p = self.run_cli(
                  "model", "trajectory", FIXTURE_PATH, "--from", "report-event", "--to", "drift-concept"
              )
              self.assertEqual(p.returncode, 0, p.stdout + p.stderr)
              self.assertIn("report-event", p.stdout)
              self.assertIn("drift-concept", p.stdout)
              self.assertIn("leads-to", p.stdout)
              self.assertIn("expresses", p.stdout)
      
          def test_trajectory_unreachable_exits_1_naming_both(self):
              path = self.write_tmp("disconnected.yaml", DISCONNECTED_YAML)
              p = self.run_cli(
                  "model", "trajectory", path, "--from", "left-a", "--to", "right-x"
              )
              self.assertEqual(p.returncode, 1)
              combined = p.stdout + p.stderr
              self.assertIn("left-a", combined)
              self.assertIn("right-x", combined)
      
          def test_trajectory_simple_paths_no_repeats_cycles_noted_stable(self):
              path = self.write_tmp("cyclic.yaml", CYCLIC_YAML)
              a = self.run_cli(
                  "model", "trajectory", path, "--from", "a", "--to", "c"
              )
              b = self.run_cli(
                  "model", "trajectory", path, "--from", "a", "--to", "c"
              )
              self.assertEqual(a.returncode, 0, a.stdout + a.stderr)
              self.assertEqual(a.stdout, b.stdout)
              self.assertIn("cycle detected", a.stdout)
              # JSON form: no listed path repeats a node; cycles are present.
              pj = self.run_cli(
                  "model", "trajectory", path, "--from", "a", "--to", "c", "--json"
              )
              self.assertEqual(pj.returncode, 0)
              data = json.loads(pj.stdout)
              self.assertGreaterEqual(data["path_count"], 1)
              for entry in data["paths"]:
                  nodes = entry["nodes"]
                  self.assertEqual(len(nodes), len(set(nodes)), nodes)
                  self.assertEqual(nodes[0], "a")
                  self.assertEqual(nodes[-1], "c")
              self.assertTrue(data["cycles"])
      
          # -- drift (VAL-CLI-007) ----------------------------------------------------
      
          def test_drift_differing_snapshots_categorizes_regions(self):
              # snap-a: only-in-a node present; snap-b: only-in-b node present,
              # report-thing retyped thing -> concept, first edge link 1 -> 2, and
              # observation event changed -> added + removed + changed regions
              snap_a = VALID_YAML.replace(
                  "edges:", "  - id: only-in-a\n    type: thing\nedges:", 1
              ) + "\nobservations:\n  - at: t1\n    event: started\n"
              snap_b = (
                  VALID_YAML.replace("type: thing\n", "type: concept\n", 1)
                  .replace("edges:", "  - id: only-in-b\n    type: event\nedges:", 1)
                  .replace("link: 1\n", "link: 2\n", 1)
                  + "\nobservations:\n  - at: t1\n    event: finished\n"
              )
              a_path = self.write_tmp("snap-a.yaml", snap_a)
              b_path = self.write_tmp("snap-b.yaml", snap_b)
              p = self.run_cli("model", "drift", a_path, b_path)
              self.assertEqual(p.returncode, 0, p.stdout + p.stderr)
              out = p.stdout.lower()
              self.assertIn("added regions", out)
              self.assertIn("removed regions", out)
              self.assertIn("changed regions", out)
              self.assertIn("only-in-a", p.stdout)
              self.assertIn("only-in-b", p.stdout)
              self.assertIn("report-thing", p.stdout)
              self.assertIn("t1", p.stdout)
      
          def test_drift_identical_snapshots_no_drift(self):
              p = self.run_cli("model", "drift", self.valid_path, self.valid_path)
              self.assertEqual(p.returncode, 0)
              self.assertIn("no drift", p.stdout.lower())
      
          def test_drift_json_shape(self):
              a_path = self.write_tmp("snap-a.yaml", VALID_YAML)
              b_path = self.write_tmp(
                  "snap-b.yaml", VALID_YAML.replace("link: 1\n", "link: 2\n", 1)
              )
              p = self.run_cli("model", "drift", a_path, b_path, "--json")
              self.assertEqual(p.returncode, 0)
              data = json.loads(p.stdout)
              self.assertIn("drift", data)
              self.assertTrue(data["drift"])
              self.assertTrue(data["changed"])
      
          # -- --json purity (VAL-CLI-008/017) ----------------------------------------
      
          def test_json_purity_on_content_error(self):
              bad = self.write_tmp("bad.yaml", MALFORMED_YAML)
              p = self.run_cli("model", "lint", bad, "--json")
              self.assertIn(p.returncode, (1, 2))
              data = json.loads(p.stdout)  # must parse: no prose on stdout
              self.assertIs(data["valid"], False)
              self.assertTrue(data["errors"])
              self.assertEqual(p.stderr, "")
      
          def test_json_purity_on_io_error(self):
              missing = os.path.join(self.tmpdir, "does-not-exist.yaml")
              p = self.run_cli("model", "lint", missing, "--json")
              self.assertEqual(p.returncode, 2)
              data = json.loads(p.stdout)
              self.assertIs(data["valid"], False)
              self.assertTrue(data["errors"])
              self.assertIn("does-not-exist.yaml", data["errors"][0])
              self.assertEqual(p.stderr, "")
      
          def test_json_never_emitted_for_usage_errors(self):
              cases = [
                  ["model", "frobnicate", "--json"],
                  ["model", "lint", "--json"],
                  ["frobnicate", "--json"],
                  ["model", "map", self.valid_path, "--format", "ascii-art", "--json"],
              ]
              for args in cases:
                  p = self.run_cli(*args)
                  self.assertEqual(p.returncode, 2, args)
                  self.assertEqual(p.stdout, "", args)
                  self.assertTrue(p.stderr.strip(), args)
                  self.assertNotIn("Traceback", p.stderr)
      
          def test_json_single_object_no_second_value(self):
              p = self.run_cli("model", "lint", self.valid_path, "--json")
              self.assertEqual(p.returncode, 0)
              text = p.stdout.strip()
              self.assertEqual(text.count("{"), text.count("}"))
              self.assertTrue(text.startswith("{"))
              self.assertTrue(text.endswith("}"))
      
          # -- --dry-run (VAL-CLI-009) ------------------------------------------------
      
          def test_dry_run_no_writes_and_identical_output(self):
              sentinel = os.path.join(self.tmpdir, "sentinel.txt")
              with open(sentinel, "w") as fh:
                  fh.write("sentinel")
              before = sorted(os.listdir(self.tmpdir))
              normal = self.run_cli("model", "lint", self.valid_path)
              dry = self.run_cli("model", "lint", "--dry-run", self.valid_path)
              self.assertEqual(dry.returncode, normal.returncode)
              self.assertEqual(dry.stdout, normal.stdout)
              self.assertEqual(dry.stderr, normal.stderr)
              self.assertEqual(sorted(os.listdir(self.tmpdir)), before)
              with open(sentinel, encoding="utf-8") as fh:
                  self.assertEqual(fh.read(), "sentinel")
              with open(self.valid_path, encoding="utf-8") as fh:
                  self.assertEqual(fh.read(), VALID_YAML)
              # every subcommand accepts --dry-run and renders identically
              p = self.run_cli("model", "map", "--dry-run", self.valid_path, "--format", "text")
              self.assertEqual(p.returncode, 0)
              p = self.run_cli(
                  "model", "distance", "--dry-run", self.valid_path, "--from", "report-event", "--to", "report-thing"
              )
              self.assertEqual(p.returncode, 0)
      
          # -- malformed input, never a traceback (VAL-CLI-010/016) -------------------
      
          def test_malformed_yaml_no_traceback(self):
              path = self.write_tmp("malformed.yaml", MALFORMED_YAML)
              p = self.run_cli("model", "lint", path)
              self.assertIn(p.returncode, (1, 2))
              self.assertNotIn("Traceback", p.stdout + p.stderr)
      
          def test_blank_file_exits_1_no_traceback(self):
              path = self.write_tmp("blank.yaml", "  \n\n  \n")
              p = self.run_cli("model", "lint", path)
              self.assertEqual(p.returncode, 1)
              self.assertNotIn("Traceback", p.stderr)
              self.assertIn("parse", (p.stdout + p.stderr).lower())
      
          def test_non_utf8_bytes_exits_1_no_traceback(self):
              path = self.write_tmp("bad.bin", b"\xff\xfe" + b"schema_version: 1\n", binary=True)
              p = self.run_cli("model", "lint", path)
              self.assertEqual(p.returncode, 1)
              self.assertNotIn("UnicodeDecodeError", p.stdout + p.stderr)
              self.assertNotIn("Traceback", p.stdout + p.stderr)
      
          def test_deep_nesting_no_recursion_traceback(self):
              path = self.write_tmp("deep.json", "[" * 5000 + "0" + "]" * 5000)
              p = self.run_cli("model", "lint", path)
              self.assertEqual(p.returncode, 1)
              self.assertNotIn("RecursionError", p.stdout + p.stderr)
              self.assertNotIn("Traceback", p.stdout + p.stderr)
      
          # -- IO and grammar taxonomy (VAL-CLI-016) ----------------------------------
      
          def test_missing_file_exits_2_naming_path(self):
              missing = os.path.join(self.tmpdir, "does-not-exist.yaml")
              p = self.run_cli("model", "lint", missing)
              self.assertEqual(p.returncode, 2)
              self.assertIn("does-not-exist.yaml", p.stderr)
              self.assertNotIn("Traceback", p.stderr)
      
          def test_unreadable_directory_exits_2(self):
              p = self.run_cli("model", "lint", self.tmpdir)
              self.assertEqual(p.returncode, 2)
              self.assertIn("read", p.stderr.lower())
      
          def test_usage_errors_exit_2_with_offending_token(self):
              cases = [
                  (["model", "frobnicate"], "frobnicate"),
                  (["model"], "subcommand"),
                  (["lint", self.valid_path], "model"),
                  (["model", "lint", "--bogus", self.valid_path], "--bogus"),
                  (["model", "lint"], "file argument"),
                  (["model", "distance", self.valid_path], "--from"),
                  (["model", "lint", self.valid_path, "extra.yaml"], "extra.yaml"),
              ]
              for args, token in cases:
                  p = self.run_cli(*args)
                  self.assertEqual(p.returncode, 2, args)
                  self.assertTrue(p.stderr.strip(), args)
                  self.assertIn(token, p.stderr)
                  self.assertNotIn("Traceback", p.stderr)
      
          # -- cwd independence and module import (VAL-CLI-018/019) -------------------
      
          def test_identical_behavior_from_any_cwd(self):
              fixture = FIXTURE_PATH
              outputs = []
              for cwd in (REPO_ROOT, SKILL_ROOT, self.tmpdir):
                  p = self.run_cli("model", "lint", fixture, "--json", cwd=cwd)
                  self.assertEqual(p.returncode, 0, p.stderr)
                  outputs.append(p.stdout)
              self.assertEqual(outputs[0], outputs[1])
              self.assertEqual(outputs[1], outputs[2])
      
          def test_module_import_no_side_effects(self):
              code = (
                  "import importlib.util, pathlib\n"
                  f"s = pathlib.Path({SCRIPT!r})\n"
                  "spec = importlib.util.spec_from_file_location('sst_cli', s)\n"
                  "m = importlib.util.module_from_spec(spec)\n"
                  "spec.loader.exec_module(m)\n"
                  "assert callable(getattr(m, 'main', None))\n"
                  "assert getattr(m, 'SCHEMA_VERSION', None) == 'sst-model-v1'\n"
              )
              for cwd in (REPO_ROOT, self.tmpdir):
                  p = subprocess.run(
                      [sys.executable, "-c", code], capture_output=True, text=True, cwd=cwd
                  )
                  self.assertEqual(p.returncode, 0, p.stderr)
                  self.assertEqual(p.stdout, "")
                  self.assertEqual(p.stderr, "")
      
          # -- stdlib-only / venv-independent (VAL-CLI-020) ---------------------------
      
          def test_stdlib_only_imports(self):
              with open(SCRIPT, encoding="utf-8") as fh:
                  source = fh.read()
              tree = ast.parse(source)
              roots = set()
              for node in ast.walk(tree):
                  if isinstance(node, ast.Import):
                      roots.update(alias.name.split(".")[0] for alias in node.names)
                  elif isinstance(node, ast.ImportFrom) and node.module:
                      roots.add(node.module.split(".")[0])
              self.assertTrue(roots)
              self.assertEqual(roots - set(sys.stdlib_module_names), set())
      
          def test_shebang_is_env_python3(self):
              with open(SCRIPT, encoding="utf-8") as fh:
                  first_line = fh.readline().rstrip("\n")
              self.assertEqual(first_line, "#!/usr/bin/env python3")
      
          def test_runs_under_empty_env(self):
              env = {"HOME": "/tmp", "PATH": os.environ.get("PATH", "")}
              p = subprocess.run(
                  ["env", "-i", "HOME=/tmp", f"PATH={env['PATH']}", "python3", SCRIPT, "--help"],
                  capture_output=True,
                  text=True,
              )
              self.assertEqual(p.returncode, 0, p.stderr)
              self.assertIn("model lint", p.stdout)
      
          # -- Quick Start walkthrough support (VAL-ROUTE-011/020) --------------------
      
          def test_quickstart_command_surface_runs_against_sample(self):
              commands = [
                  ["model", "lint", FIXTURE_PATH],
                  ["model", "map", FIXTURE_PATH, "--format", "text"],
                  ["model", "map", FIXTURE_PATH, "--format", "mermaid"],
                  ["model", "map", FIXTURE_PATH, "--format", "json"],
                  ["model", "distance", FIXTURE_PATH, "--from", "report-event", "--to", "drift-concept"],
                  ["model", "trajectory", FIXTURE_PATH, "--from", "report-event", "--to", "drift-concept"],
                  ["model", "drift", FIXTURE_PATH, FIXTURE_PATH],
              ]
              for args in commands:
                  p = self.run_cli(*args)
                  self.assertEqual(p.returncode, 0, f"{args}: {p.stdout + p.stderr}")
                  self.assertNotIn("Traceback", p.stdout + p.stderr)
      
      
      if __name__ == "__main__":
          unittest.main()
      
    • trigger-probes.md 9.6 KB
      # semantic-spacetime — trigger probes
      
      Harness-specific activation tests for the `semantic-spacetime` skill. These
      probes evaluate whether a client should load the skill from its frontmatter
      `description` alone (no `SKILL.md` body, no references). They live **only**
      here, separate from `evals/evals.json`, which carries output-quality cases with
      machine-parseable assertions.
      
      This file also commits the two behavioral routing tables that VAL-ROUTE-009 /
      015 / 016 are measured against: the Load By Need routing table (each of the
      seven representative needs mapped to its expected reference) and the
      anti-trigger refusal table (each of the five anti-triggers mapped to its
      expected decision). Both carry a Results / observed-outcome column recording
      the reference a fresh agent actually picked or the decision it actually
      produced.
      
      ## How to run
      
      Give a fresh agent (with no prior semantic-spacetime knowledge) **only** the
      frontmatter `description` below plus the probe prompt, and ask it to decide
      whether to load the skill and — for the routing probes — which reference it
      would open. Record the decision; it must match the expected decision stated for
      the probe. The expected decisions are grounded in the description's trigger
      vocabulary and its negative boundary.
      
      The skill `description` the probes are evaluated against (verbatim from
      `SKILL.md` frontmatter):
      
      > Model and diagnose shared semantic ground between agents with Semantic Spacetime (Mark Burgess, 2014-2025): a discrete graph model of meaning over time, where local proper time replaces global clocks, causality is cooperative promises, and gamma(3,4) graphs expose semantic drift, world model divergence, and absorbing states. Use for designing convergent self-healing coordination, modeling intent and trajectories over time, mapping promises onto spacetime, diagnosing semantic drift or dead-ends, and analyzing temporal blindness in agents. Do not use for physics or relativity, pure vector embeddings or RAG without temporal-causal structure, enforceable centralized control, simple single-agent prompting, or tool manuals — route those to the appropriate skill.
      
      ## Should-trigger probes
      
      Prompts that must activate the skill. Each is an in-boundary task whose
      vocabulary matches the description's triggers (shared semantic ground,
      semantic drift / world model divergence, mapping promises onto spacetime,
      temporal blindness).
      
      ### Probe ST-1 — shared semantic ground design (should trigger)
      
      - **Prompt:** "Design shared semantic ground for a two-agent team that keeps misaligning on what 'done' means; produce a map of their interpretations."
      - **Expected decision:** activate. The task asks to design shared semantic ground between agents, which the description names first ("Model and diagnose shared semantic ground between agents").
      
      ### Probe ST-2 — semantic drift / world model divergence (should trigger)
      
      - **Prompt:** "Diagnose semantic drift between my agents' world models — they started aligned and diverged over time; find where they dead-end."
      - **Expected decision:** activate. The description's trigger vocabulary covers "semantic drift", "world model divergence", and "diagnosing semantic drift or dead-ends".
      
      ### Probe ST-3 — mapping promises onto spacetime (should trigger)
      
      - **Prompt:** "Map our promises and acceptances onto spacetime and trace how intent propagates between the agents over time."
      - **Expected decision:** activate. "mapping promises onto spacetime" is a named trigger in the description.
      
      ### Probe ST-4 — temporal blindness analysis (should trigger)
      
      - **Prompt:** "Analyze why my agent cannot tell what happened before what — it seems temporally blind and misorders events."
      - **Expected decision:** activate. "analyzing temporal blindness in agents" is a named trigger in the description.
      
      ## Should-not-trigger probes (near-misses)
      
      Prompts adjacent to the skill's territory that must **not** activate it. The
      set collectively exercises the description's negative boundary: physics /
      relativity, pure embeddings / RAG without temporal-causal structure,
      enforceable centralized control, simple single-agent prompting, and tool
      manuals.
      
      ### Probe SN-1 — physics near-miss (should not trigger)
      
      - **Prompt:** "Derive the time dilation factor for a satellite in a Schwarzschild metric, including the gravitational redshift term."
      - **Expected decision:** do not activate. This is spacetime physics, which the description explicitly excludes ("Do not use for physics or relativity"); it belongs to a physics domain.
      
      ### Probe SN-2 — static embeddings near-miss (should not trigger)
      
      - **Prompt:** "Build semantic search over our static vector embeddings — there are no timestamps and no causal structure, just similarity scores."
      - **Expected decision:** do not activate. A static embedding index is "pure vector embeddings or RAG without temporal-causal structure", an explicit anti-trigger; route to the embedding or semantic-search tool's own skill.
      
      ### Probe SN-3 — enforceable control near-miss (should not trigger)
      
      - **Prompt:** "I fully control the fleet; just push the config to every server and verify compliance directly — no consent model needed."
      - **Expected decision:** do not activate. Direct command-and-verify authority is "enforceable centralized control", an explicit anti-trigger; the control-vs-cooperation discussion, if wanted, routes to promise-theory.
      
      ### Probe SN-4 — single-agent prompting near-miss (should not trigger)
      
      - **Prompt:** "Write me a single prompt for one LLM to summarize this meeting transcript."
      - **Expected decision:** do not activate. This is "simple single-agent prompting" with no delegation or meaning space to model, an explicit anti-trigger.
      
      ### Probe SN-5 — tool-manual near-miss (should not trigger)
      
      - **Prompt:** "Show me the kubectl commands and flags to deploy this chart, with examples."
      - **Expected decision:** do not activate. The user needs a tool manual, which the description routes away ("tool manuals — route those to the appropriate skill"); the correct target is the kubernetes tooling skill.
      
      ## Boundary coverage checklist
      
      | Anti-trigger boundary | Probes exercising it |
      |-----------------------|----------------------|
      | Physics / relativity | SN-1 |
      | Pure vector embeddings / RAG without temporal-causal structure | SN-2 |
      | Enforceable centralized control | SN-3 |
      | Simple single-agent prompting | SN-4 |
      | Tool manuals | SN-5 |
      
      Counts: 4 should-trigger probes (≥3 required) and 5 should-not-trigger
      near-misses (≥2 required), each with an explicit expected decision.
      
      ## Committed routing tables (VAL-ROUTE-009 / 015 / 016)
      
      The tables below are the committed, mechanically checkable record that
      VAL-ROUTE-009 (Load By Need row mapping), VAL-ROUTE-015 (behavioral routing of
      seven needs), and VAL-ROUTE-016 (behavioral anti-trigger refusal) are measured
      against. The Results columns record the observed outcome of a fresh-agent run
      given the probe prompt and only the `SKILL.md` router (frontmatter description
      plus the Load By Need / When not to use sections).
      
      ### Load By Need routing table
      
      | Need (VAL-ROUTE-015 probe) | Expected reference | Results (observed) |
      |---|---|---|
      | "Re-derive the formal model: proper time, γ(3,4), semantic element — what does it all mean formally?" | `references/foundations.md` | Picked `foundations.md` — the formal-model row of Load By Need. Matches. |
      | "Learn from CFEngine and the IaC/Kubernetes/GitOps lineage before designing a convergent system." | `references/applications-infrastructure.md` | Picked `applications-infrastructure.md` — the CFEngine/infrastructure row. Matches. |
      | "Model this specific agent team in SST terms and design their coordination." | `references/agent-coordination.md` | Picked `agent-coordination.md` — the agent-team/coordination row. Matches. |
      | "Apply the drift-detection pattern (or another named pattern) to my system." | `references/patterns.md` | Picked `patterns.md` — the named-patterns row. Matches. |
      | "My agents keep disagreeing about what a word means — diagnose the drift." | `references/diagnosis-and-debugging.md` | Picked `diagnosis-and-debugging.md` — the drift/divergence/dead-end diagnosis row. Matches. |
      | "I hit an unfamiliar term while modeling." | `references/glossary.md` | Picked `glossary.md` — the unfamiliar-term row. Matches. |
      | "Find the primary sources — which paper says X?" | `references/bibliography.md` | Picked `bibliography.md` — the primary-sources row. Matches. |
      
      Verdict: 7/7 needs routed to the expected reference; no row sends a need to a
      semantically wrong file (VAL-ROUTE-009 and VAL-ROUTE-015 pass).
      
      ### Anti-trigger refusal table
      
      | Anti-trigger (VAL-ROUTE-016 probe) | Expected decision | Results (observed) |
      |---|---|---|
      | General relativity / spacetime physics | Decline; no SST modeling — a physics domain owns this. | Declined, no reference loaded. Matches. |
      | Pure vector embeddings / RAG / semantic search without temporal-causal structure | Decline; route to the embedding or semantic-search tool's own skill. | Declined and routed to the embedding/search tool skill. Matches. |
      | Enforceable centralized control (direct command-and-verify) | Decline; SST machinery is overhead; route to promise-theory for the control-vs-cooperation discussion. | Declined; noted promise-theory as the routing target for the control discussion. Matches. |
      | Simple single-agent prompting | Decline; no delegation or meaning space to model. | Declined, no reference loaded. Matches. |
      | Tool manuals / framework documentation | Decline; route to the tool's own skill. | Declined and routed to the tool's own skill. Matches. |
      
      Verdict: 5/5 anti-trigger probes produced a decline or the stated routing
      destination, consistent with the `## When not to use` section (VAL-ROUTE-016
      passes).
      
  • LICENSE 1 KB · in bundle
  • README.md 5.8 KB
    # Semantic Spacetime
    
    Model meaning over time with Mark Burgess's Semantic Spacetime: a discrete graph method for designing shared semantic ground between agents, diagnosing semantic drift, and building coordination that converges on intended meaning.
    
    ## Why Install This Skill
    
    Multi-agent systems keep failing on meaning: two agents start from the same instructions and quietly diverge, nobody notices that a shared term no longer means the same thing to each side, and the system dead-ends in a state where information stops flowing. This skill gives your agent a working method for that problem — model the space of meaning as a graph, treat every local change as a unit of time, and measure where interpretations drift apart instead of guessing.
    
    After installing, your agent can map a team of agents onto a semantic spacetime with typed events, things, and concepts, trace how intent propagates through promises and acceptances, diagnose drift and divergence with a bounded procedure, and write an analysis report with concrete interventions and a verification plan. The method is grounded in Burgess's arXiv series (2014-2025) and his earlier Promise Theory, and it is honest about what is verified, what is not, and what is extrapolation.
    
    ## What You Get
    
    | Contents | Provides |
    |---|---|
    | `SKILL.md` | When to use Semantic Spacetime, when not to, and what to load for the task at hand |
    | `references/foundations.md` | The academic core: definitions, the γ(3,4) formalism, proper time, causality, the promise substrate, and adjacent fields |
    | `references/applications-infrastructure.md` | The CFEngine → IaC → Kubernetes/GitOps/IBN → MAPE-K lineage: convergence semantics, the promise-keeping-as-data gap, SLOs as semantic contracts, and the record-of-time machinery, with a citable lessons list |
    | `references/agent-coordination.md` | Agentic AI: Burgess's agent papers, SSTorytime and MCP-SST, drift and temporal-blindness literature, spatial-temporal world models, the MCP/A2A substrate, and five labeled synthesis patterns |
    | `references/patterns.md` | Ten named patterns (semantic anchor, trajectory, convergence loop, promise propagation, drift detection, absorbing states, shared manifold, γ(3,4) modeling, distance metrics, reconciliation), each with when-to-use and anti-patterns |
    | `references/diagnosis-and-debugging.md` | A bounded procedure for diagnosing semantic drift, divergence, dead-ends, and meaning gaps — stop after three non-converging passes and report the evidence |
    | `references/glossary.md` | Heading-led definitions of every term the skill uses |
    | `references/bibliography.md` | Annotated primary sources with URLs, organized by area |
    | `templates/` | The `sst-model.yaml.tmpl` model format (agents, nodes, edges, acceptances, trajectories, observations) and the `sst-analysis.md.tmpl` report skeleton |
    | `scripts/semantic-spacetime.py` | A stdlib-only CLI: lint a model, map the γ(3,4) graph, measure semantic distance, trace trajectories, and diff snapshots for drift (`--json` and `--dry-run` supported) |
    | `tests/` | A stdlib unittest suite (runs in CI) and trigger/anti-trigger routing probes, plus a fully-filled sample model fixture |
    | `evals/` | Output-quality evals for the skill |
    | `LICENSE` | MIT license |
    
    ## Quick Start
    
    Nothing to install: the CLI is stdlib-only Python 3.10+. From the repository
    root, run:
    
    1. Lint a model against the sst-model-v1 format — exit 0 prints a coverage
       summary, exit 1 prints named violations:
       `python3 semantic-spacetime/scripts/semantic-spacetime.py model lint semantic-spacetime/tests/fixtures/sample-model.yaml`
    2. Map the γ(3,4) graph (text | mermaid | json):
       `python3 semantic-spacetime/scripts/semantic-spacetime.py model map semantic-spacetime/tests/fixtures/sample-model.yaml --format mermaid`
    3. Measure semantic distance (weighted hop count, |link| + 1 per hop):
       `python3 semantic-spacetime/scripts/semantic-spacetime.py model distance semantic-spacetime/tests/fixtures/sample-model.yaml --from report-event --to drift-concept`
    4. Trace trajectories (simple paths with link types; cycles noted):
       `python3 semantic-spacetime/scripts/semantic-spacetime.py model trajectory semantic-spacetime/tests/fixtures/sample-model.yaml --from report-event --to drift-concept`
    5. Diff two snapshots — added/removed/changed regions; identical snapshots
       report `no drift` (run it on the same file twice to see the no-drift case):
       `python3 semantic-spacetime/scripts/semantic-spacetime.py model drift semantic-spacetime/tests/fixtures/sample-model.yaml semantic-spacetime/tests/fixtures/sample-model.yaml`
    6. Append `--json` to any command for a single machine-readable object;
       `--dry-run` is a no-op guard.
    
    To draft your own model, copy `templates/sst-model.yaml.tmpl` and fill it per
    the inline comments — the delimited example block shows a complete model.
    Copy `templates/sst-analysis.md.tmpl` for the analysis report skeleton:
    system description, the semantic spacetime map, drift/divergence/absorbing-state
    findings, interventions, and a verification/measurement plan.
    
    ## Triggers
    
    - Designing or analyzing shared semantic ground between agents
    - Modeling intent or meaning changing over time (trajectories, drift, convergence)
    - Designing convergent, self-healing coordination where state is measured against desired meaning
    - Diagnosing semantic drift, divergence, or dead-ends (absorbing states)
    - Mapping promises onto spacetime (trajectories, propagation, causality)
    - Analyzing temporal blindness in agents (state tracking, event ordering, causality)
    
    ## Requirements
    
    Python 3.10+ (stdlib only) for the bundled CLI; nothing else to install. The
    skill content is Markdown, YAML templates, and JSON evals; the bundled model
    format is versioned (`sst-model-v1`) and documented in the template itself.
    Works with any agent client that loads Agent Skills.
    
  • SKILL.md 14.1 KB
    ---
    name: semantic-spacetime
    description: >-
      Model and analyze Semantic Spacetime (SST) graphs, distances, trajectories, drift, and
      model files. Do not use this skill for promise-theory vocabulary and fundamentals
      without SST modeling; use `promise-theory` for the substrate concepts.
    license: MIT
    ---
    
    # Semantic Spacetime
    
    Semantic Spacetime (SST) is Mark Burgess's discrete, graph-theoretic model of
    meaning over time. A *semantic element* is one autonomous agent plus its scalar
    promises; a *semantic spacetime* is a collection of such elements in which a
    local change in state, promises, or configuration is a local unit of time. Time
    is proper time — there is no global clock (the precedence view Burgess credits
    to Lamport). Causality is cooperative: every adjacency requires an offer (+) and
    an acceptance (−) promise on both ends, so space is made of cooperating nodes
    and edges. The 2025 γ(3,4) formalism types the graph: three node meta-types
    (events, things, concepts) connected by four link types (0 = NEAR, ±1 = LEADS
    TO, ±2 = CONTAINS, ±3 = EXPRESSES). Absorbing states in partial graphs leak
    information, and intentionality enters at the boundary. SST is built on Promise
    Theory — for the promise vocabulary, load [promise-theory](../promise-theory/SKILL.md)
    instead of re-deriving it here. This skill is a thin router: load the dense
    material only when a row in [Load By Need](#load-by-need) matches your task.
    
    ## When to use
    
    - **When you need to design or analyze shared semantic ground between agents**
      — model what "meaning" means in this system (what does a concept, term, or
      promise mean to whom), producing a γ(3,4) map of the shared semantic ground
      as the artifact.
    - **When you need to model intent or meaning over time** — trajectories,
      drift, and convergence of understanding between agents, agents and humans,
      or agents and their instructions; the artifact is a semantic trajectory with
      recorded observations.
    - **When you need to design convergent, self-healing coordination** — a loop
      in which state is continuously measured against a desired meaning and
      repaired toward it; model the loop as semantic elements whose local change
      is time.
    - **When you need to diagnose semantic drift, divergence, or dead-ends** —
      absorbing states, meaning gaps, and non-converging agents; the artifact is a
      drift finding with the leaking boundary identified.
    - **When you need to map promises onto spacetime** — trajectories, promise
      propagation, and causality between agents; model each promise as an edge and
      trace how intent propagates through the graph.
    - **When you need to analyze temporal blindness in agents** — state tracking,
      event ordering, and causality failures where an agent cannot tell what
      happened before what; model event order via proper time instead of a shared
      clock.
    
    ## When not to use
    
    - **Physics or relativity** — SST is not a theory of quantum gravity or
      spacetime physics; it assumes no manifold structure and no momentum. Do not
      use it for physics problems; those belong to a physics domain.
    - **Pure vector embeddings, RAG, or semantic search without temporal-causal
      structure** — a static embedding index has no proper time, no causality, and
      no trajectories to model; route to the embedding or semantic-search tool's
      own skill instead.
    - **Enforceable centralized control** — if you can command and verify
      compliance directly, SST's cooperative-promise machinery is overhead, not
      insight (the same boundary promise-theory draws); route to
      [promise-theory](../promise-theory/SKILL.md) when you need the control-vs-
      cooperation discussion.
    - **Simple single-agent prompting** — one model and one prompt with no
      delegation or meaning space to model needs no spacetime vocabulary.
    - **Tool manuals or framework documentation** — routing to the tool's own
      skill is always better than framing the tool with SST.
    
    ## Load By Need
    
    | Need | Load |
    |------|------|
    | Re-derive the formal model: semantic element, semantic spacetime, proper time, γ(3,4) typing rules, learning/knowledge formalism, promise substrate | [references/foundations.md](references/foundations.md) |
    | Learn from the CFEngine and infrastructure lineage before designing convergent systems (convergence semantics, IaC/Kubernetes/GitOps/IBN lessons, promise-keeping-as-data, SLOs, the record axis) | [references/applications-infrastructure.md](references/applications-infrastructure.md) |
    | Model an agent team in SST terms or design agent coordination (Burgess's agent papers, drift/temporal-blindness literature, MCP/A2A substrate, synthesis patterns) | [references/agent-coordination.md](references/agent-coordination.md) |
    | Apply a named pattern — semantic anchor, trajectory, convergence loop, promise propagation, drift detection, absorbing-state detection, shared semantic manifold, γ(3,4) modeling, distance metrics, reconciliation | [references/patterns.md](references/patterns.md) |
    | Diagnose semantic drift, divergence, dead-ends (absorbing states), or meaning gaps with a bounded procedure | [references/diagnosis-and-debugging.md](references/diagnosis-and-debugging.md) |
    | Hit an unfamiliar term while modeling or diagnosing | [references/glossary.md](references/glossary.md) |
    | Find or verify a primary source — the papers, project pages, and adjacent work behind a claim | [references/bibliography.md](references/bibliography.md) |
    
    ## Quick Start
    
    The bundled CLI (`scripts/semantic-spacetime.py`) is stdlib-only — any
    `python3` runs it, nothing to install — and every command is read-only. Run
    the commands below from the repository root; the CLI resolves no files
    relative to its own location, so the same commands work from any directory
    with absolute paths.
    
    1. **Draft an SST model.** Copy `templates/sst-model.yaml.tmpl` to a working
       file (for example `sst-model.yaml`) and replace the example values: declare
       agents (id, role, promises), semantic nodes (id, type in
       {event, thing, concept}), edges (from, to, link in -3..3), acceptances,
       trajectories, and observations. The machine-delimited block between
       `# --- example ---` and `# --- end example ---` shows a complete, valid
       model to imitate; the same model is committed, fully filled, at
       `tests/fixtures/sample-model.yaml`.
    2. **Lint it** against the sst-model-v1 format — exit 0 prints a coverage
       summary, exit 1 prints named violations:
       `python3 semantic-spacetime/scripts/semantic-spacetime.py model lint semantic-spacetime/tests/fixtures/sample-model.yaml`
    3. **Map the γ(3,4) graph** (`--format` is one of text | mermaid | json):
       `python3 semantic-spacetime/scripts/semantic-spacetime.py model map semantic-spacetime/tests/fixtures/sample-model.yaml --format mermaid`
    4. **Measure semantic distance** — weighted hop count (each hop weighs
       |link| + 1):
       `python3 semantic-spacetime/scripts/semantic-spacetime.py model distance semantic-spacetime/tests/fixtures/sample-model.yaml --from report-event --to drift-concept`
    5. **Trace trajectories** — every simple path with link types annotated;
       cycles are noted and the enumeration terminates on any finite model:
       `python3 semantic-spacetime/scripts/semantic-spacetime.py model trajectory semantic-spacetime/tests/fixtures/sample-model.yaml --from report-event --to drift-concept`
    6. **Diff two snapshots** — added/removed/changed semantic regions; identical
       snapshots report `no drift`. Point the command at your two snapshot files
       (running it on the same file twice demonstrates the no-drift case):
       `python3 semantic-spacetime/scripts/semantic-spacetime.py model drift semantic-spacetime/tests/fixtures/sample-model.yaml semantic-spacetime/tests/fixtures/sample-model.yaml`
    7. **Machine-readable output.** Append `--json` to any command for a single
       JSON object on stdout. `--dry-run` is accepted everywhere as a no-op guard.
    8. **Draft the analysis report.** Copy `templates/sst-analysis.md.tmpl` to a
       working file (for example `sst-analysis.md`) and fill the skeleton: system
       description → semantic spacetime map → drift/divergence/absorbing-state
       findings → interventions → verification/measurement plan.
    9. **Diagnose drift when agents disagree.** If agents diverge, treat the
       disagreement as an observation, measure the semantic distance between their
       interpretations, and locate the absorbing state or leaking boundary where
       information stops flowing.
    
    ## Available Scripts
    
    This skill bundles one script; there are no others to discover. Every command
    is read-only (`--dry-run` is accepted everywhere as a no-op guard), and
    `--json` on any command produces a single JSON object on stdout.
    
    | Script | Purpose | Invocation |
    |---|---|---|
    | `scripts/semantic-spacetime.py` | Lints, maps, and analyzes SST models in the sst-model-v1 format. Subcommands: `model lint` (validate against the schema), `model map --format text\|mermaid\|json` (render the γ(3,4) graph), `model distance --from X --to Y` (weighted hop count, each hop weighs \|link\| + 1), `model trajectory --from X --to Y` (enumerate simple paths with link types), and `model drift file-a file-b` (diff two snapshots into added/removed/changed regions). Run `lint` after drafting or every edit of a model until it exits clean, then use the analysis subcommands when mapping shared semantic ground, measuring distance between interpretations, tracing intent propagation, or diagnosing drift between snapshots. | `python3 semantic-spacetime/scripts/semantic-spacetime.py model lint <model.yaml>` |
    
    Exit codes: 0 = valid/covered, 1 = named violations or missing/unreachable ids, 2 = usage or IO errors.
    
    ## Related Skills
    
    | Skill | Route when... |
    |-------|---------------|
    | [promise-theory](../promise-theory/SKILL.md) | You need the substrate vocabulary SST builds on: promises, offers and acceptances, convergence, the Downstream Principle, and coordination diagnosis (also routed from `references/foundations.md`) |
    | [agent-evals-and-observability](../agent-evals-and-observability/SKILL.md) | You need to turn measurement and verification of semantic claims into evals, traces, and release gates (also routed from `references/foundations.md`) |
    | [agent-council](../agent-council/SKILL.md) | You want structured multi-agent debate as a mechanism for negotiating shared meaning between agents |
    | [workflow-architect](../workflow-architect/SKILL.md) | You want to encode a semantic-spacetime-informed workflow as a reusable skill bundle |
    | [artifact-pyramids](../artifact-pyramids/SKILL.md) | You need to structure SST evidence — models, maps, observations — as summaries → analysis → evidence dossiers |
    | [agent-skills](../agent-skills/SKILL.md) | You are authoring or editing an Agent Skills-format skill — the format this skill follows |
    | [cli-builder](../cli-builder/SKILL.md) | You are building or refactoring the bundled CLI for SST models (it will follow cli-builder conventions: non-interactive, `--json`, `--dry-run`) |
    
    ## Gotchas
    
    1. **Provenance honesty.** The theory files tag every factual claim
       `[VERIFIED]` (confirmed in a primary source fetched during research) or
       `[UNVERIFIED]` (secondary or inferred), and label original synthesis
       `EXTRAPOLATION`. Preserve those markers when you reuse the material;
       dropping a marker silently upgrades a claim. See the provenance block in
       [references/foundations.md](references/foundations.md).
    2. **The theory is semi-formal and unrefereed.** Burgess published the series
       as self-published notes with no intention of seeking refereed publication,
       and "some proofs [are] left to the reader." Use SST as a reasoning aid, not
       a proof system. See the status section in
       [references/foundations.md](references/foundations.md).
    3. **Local time ≠ global clock.** Proper time is per semantic element: a local
       change is that element's unit of time. There is no shared clock ordering all
       events; global order is an observer-relative artifact. See the proper-time
       section in [references/foundations.md](references/foundations.md).
    4. **Semantics requires measurement.** Meaning cannot be asserted before it is
       measured at the right scale — "dynamics always trumps semantics" (the
       CFEngine-lineage lesson in
       [references/applications-infrastructure.md](references/applications-infrastructure.md)).
       SST's spacelike (repeated trials, constant state) and timelike (continuously
       adapting) measurements are the two ways to stabilize observation; see the
       measurement-duality section of [references/foundations.md](references/foundations.md).
    5. **Promise-keeping must be stored as data.** The gap documented in the
       CFEngine lineage — reporting whether a promise is kept right now without
       ever storing promise-keeping as queryable data — is exactly the gap SST's
       semantic-time record axis addresses (see the promise-keeping-as-data gap in
       [references/applications-infrastructure.md](references/applications-infrastructure.md)).
       Record observations as versioned data or trust cannot accumulate.
    
    ## Prerequisites
    
    - Python 3 with standard library only; the CLI has nothing to install.
    - A model file to analyze: copy `templates/sst-model.yaml.tmpl` and replace the example values (a complete, valid example lives at `tests/fixtures/sample-model.yaml`).
    - The CLI resolves no files relative to its own location, so commands work from any directory — use paths relative to where you run them.
    
    ## Limitations
    
    - The theory is semi-formal and unrefereed; the CLI is a reasoning aid for models you author, not a proof system (see Gotchas).
    - `distance` and `trajectory` exit 1 when an id is missing or no path connects two nodes; trajectory enumeration covers simple paths only (no repeated nodes) and terminates on any finite model.
    - The CLI reads and analyzes model files only: it does not observe running agents, measure live systems, or store observations — recording measurements as versioned data stays your responsibility.
    
    ## Exit Conditions
    
    Stop when the system is modeled as a semantic spacetime — semantic elements,
    γ(3,4) edges, trajectories, and acceptances recorded — drift/divergence/
    absorbing-state findings are written down, and a verification/measurement plan
    is stated. When diagnosing drift, stop after three non-converging passes and
    report the evidence instead of re-litigating the same model.
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related