docs/open-decisions.md
The two open decisions — evidence and options
Prepared after AMD-0003 was vetoed and the gate reopened. Both were recorded as governance decisions; measurement changed the shape of the first one, so it is presented as re-framed rather than as previously stated.
Nothing here decides anything. Each option is stated with its consequence so the decision can be made against evidence rather than against my preference.
Decision 1 — Floor and tier units (blocks II.2, II.3, tripwire W5)
How it was recorded
docs/tripwires.md W5: "II.2 asks which metric feeds height, II.3 asks in
what units. Both wait on one decision."
What measurement shows
That framing is incomplete in a way that matters. Four measurements:
Height currently derives from
code-size.src/lib/world/structure.ts:266—heightRule: { kind: "measure", metric: "code-size", base: 5, scale: 3.6 }. Article II.2: "It is forbidden to derive height from code size, file counts, or any measure of how much has been written rather than done." This is a direct textual violation, in source, today.Height is continuous. The civic hall stands at
11.399999999999999floors. W5 measured 2001 distinct heights from 2001 inputs.No cumulative-output metric exists in the fact vocabulary.
WorkItemFact.stateis"open" | "blocked"— there is no terminal state, so completion is never expressed as a fact.But the evidence exists.
db/schema.sqlhq_taskcarriesstate IN ('open','blocked','done','cancelled')andcompleted_at TIMESTAMPTZ. Cumulative output is recorded; it is discarded at the fact boundary.
The consequence for the decision
The decision cannot be taken onto anything yet. A unit quantises a metric, and the metric II.2 requires does not currently reach the world. Choosing units today would quantise a forbidden metric — making the violation discrete rather than removing it, and W5 would close while II.2 stayed violated.
So this is not one decision. It is one decision plus one prerequisite, and the prerequisite is implementation that needs no governance at all.
Options
| Action | Consequence | |
|---|---|---|
| A (recommended) | Surface completions as facts first — extend WorkItemFact with a terminal state, emit a cumulative completed measure per subject — then decide units against the observed distribution |
The units decision becomes evidence-based: band edges chosen from real data rather than invented. II.2 and II.3 close together. Requires no amendment; facts.ts is not constitutional |
| B | Decide units now, in the abstract, against code-size |
W5 closes and II.2 remains violated, now permanently encoded in a declared unit. The Legend would assert a source the Constitution forbids |
| C | Amend II.2 to permit code size as a height source | Honest if the intent has changed, but it inverts the article's stated purpose — height means done, not written. Requires an Article X amendment |
Recommendation: A. It is the only option under which the units are measured rather than chosen, and it is unblocked today.
MEASURED 2026-08-03 — step 2 done, step 3 returns a degenerate distribution
Preservation is fixed: substrate/runtime.ts now emits a per-subject
completed measure. It had been running the query, typing the result and
discarding it.
The distribution was then measured against the live database, read-only and in aggregate:
| Source | Result |
|---|---|
hq_task WHERE state='done' |
0 rows. Totals: 3 open, 3 cancelled, 0 done |
hq_agent_run WHERE state='succeeded' |
9 rows, all for one agent (z) — n=1 subject, min=max=9, 1 distinct value |
hq_handoff, hq_meeting, hq_memory |
0 rows each |
The floor-unit decision is still blocked, and the reason has changed. It was blocked because the evidence was discarded at the fact boundary. That is fixed. It is now blocked because the evidence has not accumulated: no task has ever completed, and the only cumulative output that exists is one subject with one value.
Band edges cannot be derived from a single point. Choosing a unit now would fit it to one observation — the same defect that withdrew X.3, where "a governance rule that swings that far on one data point is not ready".
A second candidate source surfaced and is recorded rather than adopted.
hq_agent_run.state='succeeded' is also cumulative operational output, is
per-agent, and unlike hq_task it has actual rows. It is arguably the better
II.2 source — it records work executed rather than work marked done by an
operator. Whether height should represent completed tasks or successful runs is
a governance judgement about what "operational output" means, not something
measurement settles. It is not wired to anything.
What unblocks step 3: completed work accumulating across several subjects. Until then any proposed unit is speculative, and the honest state is waiting for data, not waiting for analysis.
Note on scope: the db→fact boundary is outside every enumerator in the
system. The enumerators cover src/lib/world; the connector that drops
state='done' is not in the object space. That is a coverage boundary this
decision surfaced, and it is not yet recorded as one.
Decision 2 — Gap surfacing design (blocks F6b)
What measurement shows
core/ingestproduces Gaps.IngestResult = { facts, gaps }; every observation a connector cannot map becomes aGapwith a reason and detail.- Nothing consumes them. No component, page, or lib module reads
gaps. They are produced and discarded — the channel exists and terminates. repoTodosis deliberately unmapped, keyedrepo-todos-<name>, with no migration by design: a TODO marker is untriaged text with no correct condition, so it has no correct mapping either.
The consequence for the decision
Gaps are the system's own record of what it observed and could not represent —
which is the same category as UNWITNESSED and the coverage boundary, and the
one the architecture treats as load-bearing. Producing them and discarding them
is the failure mode the ledger exists to prevent, occurring in the runtime
rather than in the documentation.
The decision is where a Gap becomes visible, and it is genuinely a design choice with no measured answer.
Options
| Surface | Consequence | |
|---|---|---|
| A (recommended) | A count plus reasons in the existing runtime panel — the same place source failures already appear | Cheapest, consistent with how sources[].ok already reports substrate failure. Makes "observed but unrepresentable" visible without inventing a new surface |
| B | Gaps become incidents, appearing in the world as markers | Highest visibility, but conflates the system failed with the system could not interpret. Article I.3 territory: an incident is an operational fact, a Gap is a representational one |
| C | A dedicated Gap view | Most faithful, most work, and unjustified until the volume is known — nothing currently measures how many Gaps a real ingest produces |
Recommendation: A, with a measurement first: instrument the count before choosing a surface, because C cannot be justified without knowing the volume and A may be sufficient permanently.
repoTodos needs no decision — it stays unmapped, which is the correct
treatment of untriaged text, and F6b retires only its legacy identity, not its
unmapped status.
Decision 3 — a DOM environment for scene-graph observation (new, 2026-08-03)
What was authorised, and what measurement then showed
@react-three/test-renderer was authorised and installed on the reasoning that
it unlocks the largest remaining unobservable object. It is necessary but not
sufficient, and that was measured rather than assumed:
| Step | Result |
|---|---|
Import renderer and WorldScene |
works |
Render with a hand-built World |
DOMRect is not defined |
+ DOMRect, ResizeObserver stubs |
document is not defined |
+ minimal document stub |
canvas.getContext is not a function |
+ custom gl mock via RenderProps |
fails with an empty error |
The renderer calls document.createElement('canvas') and expects a real canvas.
It ships no mock, declares no DOM dependency, and its only peers are react,
@react-three/fiber and three.
composeWorld also cannot be used to build the fixture — build.ts is
server-only, and here that marker is correct: the module holds
deriveWorld, which does database IO. Genuine privilege, so the restriction
belongs. The fixture must be hand-built, which is a real cost: the test then
observes WorldScene given a World, not the pipeline that produced it.
Options
| Action | Consequence | |
|---|---|---|
| A (recommended) | Add jsdom (or happy-dom) as a second dev dependency |
Completes the capability. Scene structure — positions, materials, hierarchy — is trustworthy under jsdom, because three.js builds the scene graph independently of the GL context. Anything requiring actual rasterisation stays unobservable, and that limit should be recorded rather than glossed |
| B | Hand-stub the DOM and WebGL in test setup | No new dependency, but the instrument would observe a scene built under a heavily faked environment. Divergence from the real one would be undetectable — an instrument whose fidelity cannot itself be measured |
| C | Revert @react-three/test-renderer; keep scene-graph on the boundary |
Honest and cheap. Leaves six of eight known rendering violations permanently unreachable |
Recommendation: A. Every criterion given for the first dependency applies unchanged — dev-only, unlocks the largest unobservable object, no measured zero-dependency path. B is the one to avoid: it produces an instrument that looks like scene-graph observation while measuring something else, which is the substitution defect the observable-object rule exists to prevent.
The installed dependency is retained rather than reverted: it is a genuine prerequisite for A, and unused only until that decision is taken.
Sequencing
Both recommendations are measure-then-decide, which is why neither asks for a decision today:
- Instrument the Gap count (unblocked, no governance).
- Surface completions as facts (unblocked, no governance).
- Decide units against the observed completion distribution.
- Decide the Gap surface against the observed Gap volume.
- Only then P4 — the Legend amendment — with both units and surfacing settled, so the first visible constitutional artifact after the gate reopened carries measured values rather than chosen ones.