Skip to content
← Documentation

docs/tripwires.md


Tripwires

Open questions where the honest answer today is "not enough evidence". Each records the tension, why it was not resolved, and the concrete, checkable condition that would supply the missing evidence.

A tripwire is not a to-do. It is a decision deferred with a trigger, so it cannot quietly become permanent by being forgotten.


W1 — Article II.5 enumerates movement causes as a closed list

Tension. "An agent moves only for: a task, a delegation, an investigation, a meeting, a transport, an observation, an incident response, or a maintenance action." A rostered shift handover appears to fit none of the eight.

Why unresolved. No rostering connector exists, so there is no runtime evidence. Article X.1.4 admits runtime evidence only.

Trigger. A registered connector emits a placement whose declared cause maps to none of the eight, and the gap is recorded. One real gap report suffices.

Layer if triggered. Constitutional — the list is in the Constitution.


W2 — the legend has a row for agent position, not subject position

Tension. placement is subject-at-subject since the shipping proof: a container has a position and is not an agent. The legend has no row for a non-actor subject's position, which reads strictly as a tension with IV.5.

Why unresolved. No connector emits a placement for a non-actor subject. Derived from specification, not observed.

Trigger. A connector emits a placement whose subject kind is not actor, and the projection has no legend row to render it.

Layer if triggered. Constitutional — the legend is Article II.

Expected to fire first. The first non-software connector will move something that is not a person.


W3 — is an execution log part of the World?

Tension. The runtime panel shows recent agent runs. Those are stored facts (hq_agent_run rows), and Article I says every visible object traces to a stored operational fact — which it does. But the World models state at an instant, and a log is history. Routing the log through World would require a fact family that fits a completed run, and none of the four does without stretching.

What was resolved instead. The genuine violation was duplication: the panel read in-flight handoffs and a stored-task count straight from Postgres while the World already carried both. That is an implementation defect and it is fixed — the panel now reads those from the World, and the query that produced the second source is deleted at its origin rather than merely unused.

Why the rest is unresolved. Whether a log belongs in the World is a specification question, and forcing it into a fact family that does not fit would be worse than leaving it visibly separate. readExecutionLog is now named and documented as what it is: an execution record plus a control surface, neither of which is world state.

Trigger. Either of:

  1. A second consumer needs run history, making the log a de facto second source of truth rather than one panel's record; or
  2. The core acquires a fact family that a completed run fits without stretching.

Layer if triggered. Specification, not constitutional — the four fact families are a specification artefact, and extending them is an Article X matter only if it changes what the Constitution means rather than what the specification contains.


W4 — is a time series part of the World?

Tension. The engineering-activity sparkline shows commits per day over 30 days. Each day's count is a real measurement, but the World models state at an instant: worldAt(facts, lens) filters to one moment. A 30-day series is not one moment.

Why unresolved. Routing it through the World would require either a time-series concept — a new primitive minted to satisfy a panel, which is exactly what the ontology must not accumulate — or the World carrying 30 days of facts, which contradicts what a World is.

In the core the honest answer already exists: a series is many facts at different validAt, and the series is produced by querying facts across a window rather than by the World holding a series. The legacy stack has no such query, so the sparkline still reads telemetry directly.

What was resolved instead. Every other field of headline and self now derives from the World. Two reads remain in the page: this one, and readAt, which is provenance of a source rather than world state.

Trigger. Either of:

  1. The legacy stack gains a facts-over-a-window query (it arrives naturally with the core migration, since validAt already supports it); or
  2. A second consumer needs the series, making the direct telemetry read a de facto second source rather than one panel's convenience.

Layer if triggered. Implementation, not specification — the core already expresses series correctly. Only the legacy stack lacks the query.

Explicitly NOT a reason to add a time-series fact family. W3 and W4 are the same shape: history is not state, and the answer is a different query, not a new concept.


W5 — Article II.3 is violated, and the constitutional legend asserts otherwise

Measured, not inferred. Sweeping a repository's code-size metric from 1000 to 3000 lines produces 2001 distinct building heights from 2001 inputs — a perfectly continuous function. Adding a single line of code moves a building:

height at 2000 lines : 16.88448951909036
height at 2001 lines : 16.88527066331588

Article II.3: "Every geometric property is discrete. Continuous metrics are quantised into units before they reach geometry." Its stated consequence — "geometry cannot change minute to minute" — is false today. resolveHeight is continuous in all four branches: load is linear, measure is logarithmic, orchestration is proportional, and only fixed is discrete by accident of being constant.

The sharper finding is in the legend. src/lib/world/constitution.ts is a constitutional file under Article VIII. Its LEGEND declares:

{ visual: "height", represents: "cumulative operational output",
  source: "output", quantised: true, changeRate: "rare" }

quantised: true. The field is declared on all nine rows and read by nothing — not verifyWorld, not any test. So a constitutional file asserts a property of the system that is both false and unchecked. Article VII.3 is directly on point: "Where impossibility is not achievable, the probe is the enforcement, and it must fail loudly." There is no probe.

Why unresolved — two independent blocks, neither of which I may resolve.

  1. The unit is not declared. II.3 requires "1 floor = one unit of cumulative output (unit defined per building kind, declared in the legend, never inferred)." No unit exists in the legend. Choosing one would be inferring it, which the article forbids in those words. This is the standing II.2 debt, blocked on the ratifying authority's decision about floor and tier units.

  2. Hysteresis requires prior state. II.3 mandates that a building "gains a floor at threshold T and loses it only below T − h." resolveHeight is a pure function of the current measure and cannot express hysteresis at all — the same shape as Article III, where seniority was not recoverable from the present entity set and had to become a recorded fact. Where the prior height is recorded is a design decision, not an implementation detail.

Why no failing test was landed. A red test blocks prebuild, and Article VIII.1.6 forbids landing one that skips ("a test that silently skips … is treated as failing, never as passing"). Landing enforcement before the units exist would therefore halt all work on a defect that cannot be repaired until a decision is made. The measurement above is the evidence instead.

Trigger. Ratification of floor and tier units. At that point II.3 becomes mechanizable and the probe should assert two properties: that a continuous sweep yields a small finite set of heights, and that the transition exhibits hysteresis — a value crossing T upward and then returning to just below T does not lose the floor.

Layer if triggered. Specification then implementation. The units are a constitutional question (they are legend content, and VIII.1.2 makes the legend exhaustive); quantising and recording prior height are implementation.

Related debt. II.2 (height derives from code size, not cumulative output) is the same blockage seen from the other side: II.2 asks which metric feeds height, II.3 asks in what units.

CORRECTED 2026-08-03 — "both wait on one decision" was wrong. They wait on one decision and one prerequisite. Measured: WorkItemFact.state is "open" | "blocked" with no terminal state, so no cumulative-output metric exists in the fact vocabulary for a unit to quantise. The evidence exists — hq_task.state='done', completed_at — and is discarded at the fact boundary. Deciding units first would quantise code-size, closing this tripwire while II.2 stayed violated. See open-decisions.md.