Issue 19: The Trap Reads Green

Share
Non Xero Sum logo animation still
nxs blob merge 1

Every vendor in the observability aisle sells the same promise. See everything, the dashboard turns red the instant something breaks, and once you can see the fault it is as good as fixed. The studio bought that promise. We built the seeing part to a standard I was quietly proud of, then spent a season walking into the same wall. Seeing and fixing are two different mechanisms, and the second does not arrive free with the first.

We have logs that catch faults with a sensitivity most teams would envy. We also have faults those logs caught weeks ago, still open, seen by everyone, reading green anyway. The dashboard is not lying. It reports that the fault is visible; it never claimed anything was wired to fixing it, and nobody read the silence where the fix should have been. That is the observation audit trap: the watchers report, the sanction never fires, and a system files itself as monitored while its faults age.

Issue 11 asked whether a rule had a guard at all. That was the right first question. This is the next one, and it is worse. A guard can exist, be named in the registry, show a green checkmark, and still have fired nothing since the fault it guards last broke. Existence is not binding, and binding is not freshness. The gap between those three is where good teams lose whole quarters.

That gap is older than software, and it is the subject this week. Two tools follow The Read: the loop-closure auditor, free, which tells you for each fault whether a repair is bound to the thing that sees it, and Trap Memory, paid, which gives that audit a memory so a trap you have watched for a month stops looking like one you noticed this morning.


The Tape

Six from our own logs this week. Each is an observation that fired and a repair that did not: our records, dated, offered as first-party evidence rather than public citations.

  1. A learnings log every agent appends to on every fault: past fifty entries, and nothing anywhere turns an entry into a changed behaviour (documented internally, confirmed 2026-09-19). Written forever, read by nothing.
  2. A lint that checks path citations runs after the file is already written and only prints advice. The version that would refuse the write before it lands is designed and not armed (2026-09-12).
  3. Three finished pieces of work sat marked open for over two hundred hours each because the closing record carried the wrong arithmetic sign. The tracker had them flagged the whole time (2026-09-07).
  4. A ruling authorised an agent into real backlog work. Nothing armed it, and a day and a half passed before a human finally asked out loud (2026-09-11).
  5. An audit of roughly two thousand internal fix-records found four hundred and fifty-one declaring themselves in force with no mechanism named at all (2026-09-18).
  6. Two rules written into the studio constitution one day were found the next to be live only as words, then wired the day after to a gate that now refuses the offending commit (2026-09-18 to 2026-09-19). The one item here that closed.

Five of these are the trap. The sixth is what leaving it looks like.

Both tools follow The Read: the loop-closure auditor, free and runnable below the argument, and Trap Memory, the paid flagship below the cut that remembers every audit and flags the traps that age.


The Read

The Read: monitoring is not the sanction

Elinor Ostrom's eight design principles, set out in Governing the Commons (Cambridge University Press, 1990), separate the common-pool resources that survived from the ones that collapsed. Two sit next to each other and are constantly collapsed into one. Principle 4 is monitoring: someone watches the resource and the appropriators. Principle 5 is graduated sanctions: violations carry escalating consequences. Her field cases are blunt about the order. Where monitors reported and nothing followed, the monitoring decayed into ritual and the resource failed as if no one had watched. Watching does not sustain cooperation. Watching plus a bound sanction does.

Software rebuilt this architecture and lost this distinction. We renamed monitoring "observability" and sell it as the finished good, because dashboards photograph well. The sanction that consumes a red signal and acts on it has no screen. So the half that photographs ships, and the half that works does not.

loop closure four states

Reduce it to two questions. Can the fault be seen? Is a repair bound to that sight? Four states fall out.

  • CLOSED: seen, and a repair is bound to it and has fired since the last breach.
  • UNOBSERVED: not seen at all, so the repair question is moot.
  • BLIND-ACTUATOR: a repair is bound to no observation, so it fires on nothing.
  • TRAP: seen, and no repair is bound; this is the dangerous one.

TRAP is worse than UNOBSERVED, and that is the whole issue. An unwatched fault still gets attention eventually; a TRAP manufactures the opposite belief. There is a dashboard, it is red, everyone has seen the red, nothing is wired to it, and the fault ages inside a system now reporting it as known. Every Tape item above is this shape: monitored, with no sanction. None is a visibility problem. The studio had more visibility than it could act on, which is not an asset but a liability that reads green.

There is a sharper failure inside CLOSED, the one that fooled us longest. A control can show both fields filled and still be open in fact, because the named repair has not fired since the fault last broke. A falsifier whose verifier never runs is not a guard; it is a monument to the idea of one. The tool below calls that STALE and treats it as a live trap, because on paper it is the healthiest row in the registry.

The twenty-minute version

Write your controls down as a small registry, one object each. For every control, name the thing that observes the fault and the thing that repairs it, and where you have them, the timestamps of the last breach and the last repair. Be honest when the repair field is empty, because the blank is the finding. Then run the tool below. The number it hands back, the loops that do not actually close, is the one you cannot fake in a status meeting.

Retrieval is procurement. Closure is the work.


The Tool

The Tool: loop-closure-auditor.py

The free, from-scratch tool this week. It reads a control registry and tells you, for each fault, whether a repair is bound to the observation, and whether that repair has fired since the last breach. Standard library only, complete and runnable below.

Give it a JSON array of controls, each naming observed_by, repaired_by, and where you have them the last_breach_observed, last_repair_fired, and last_verdict timestamps. It returns each control's state and fails the run if any live trap exists. It does not judge whether your repair is any good. It judges the one thing teams skip: is a repair bound to the observation at all, and has it fired since the fault last broke.

#!/usr/bin/env python3
"""loop-closure-auditor.py - audit a control registry for the observation audit trap.

For every control it asks two questions, in order:
  1. Can the fault be SEEN?      (is an observation mechanism bound to it)
  2. Is a repair BOUND to that observation? (does a named actuator consume the
     observation and act, closing the loop)

The four states that fall out, and why the order matters:
  CLOSED          observed AND a repair is bound      the loop closes
  TRAP            observed AND no repair is bound      you can see it, nothing fixes it
  UNOBSERVED      not observed (repair moot)           a blind spot, honestly a blind spot
  BLIND-ACTUATOR  a repair bound to no observation     an actuator that fires on nothing

The whole point is the difference between TRAP and UNOBSERVED. A blind spot at
least advertises itself as a blind spot. A TRAP reads GREEN on the org chart:
there is a dashboard, it is RED, everyone has seen it, and nothing is wired to
the RED. Observability without a bound actuator is not a lesser good than
observability with one. It can be worse, because it manufactures the belief that
the fault is handled while the fault ages.

A stricter check runs when the registry carries timestamps: a control that is
CLOSED on paper but whose last repair predates its last observed breach has a
STALE binding, and is reported as a live trap even though a repair mechanism is
named. A named actuator that has not fired since the breach is not closing the
loop today.

Exit 0 if no live trap. Exit 1 if any TRAP or STALE binding exists. A read or
parse failure returns exit code two. Standard library only.
"""
from __future__ import annotations

import argparse
import json
import sys
from datetime import datetime, timezone

def parse_ts(value):
    if value is None:
        return None
    text = str(value).strip().replace("Z", "+00:00")
    try:
        dt = datetime.fromisoformat(text)
    except ValueError:
        return None
    return dt if dt.tzinfo else dt.replace(tzinfo=timezone.utc)

def classify(control):
    """Return (status, why) for one control record."""
    observed = bool((control.get("observed_by") or "").strip())
    repaired = bool((control.get("repaired_by") or "").strip())

    if observed and repaired:
        # The loop closes on paper. Now test whether it closes today.
        last_obs = parse_ts(control.get("last_breach_observed"))
        last_fix = parse_ts(control.get("last_repair_fired"))
        breach = str(control.get("last_verdict", "")).strip().upper() in ("RED", "FAIL", "BREACH")
        if breach and last_obs is not None and (last_fix is None or last_fix < last_obs):
            return ("STALE", "a repair is named but has not fired since the last observed breach")
        return ("CLOSED", "observation bound to a repair that has fired since the last breach")
    if observed and not repaired:
        return ("TRAP", "the fault is observable and no repair is bound to the observation")
    if repaired and not observed:
        return ("BLIND-ACTUATOR", "a repair is bound to no observation, so it fires on nothing")
    return ("UNOBSERVED", "no observation mechanism, so the fault cannot be seen at all")

ORDER = ("STALE", "TRAP", "BLIND-ACTUATOR", "UNOBSERVED", "CLOSED")

def main():
    ap = argparse.ArgumentParser(
        description="Audit a control registry for observed-but-unrepaired faults.")
    ap.add_argument("registry", help="JSON array of control records")
    args = ap.parse_args()

    try:
        with open(args.registry, encoding="utf-8") as fh:
            controls = json.load(fh)
    except OSError as exc:
        print("cannot read %s: %s" % (args.registry, exc), file=sys.stderr)
        return 2
    except json.JSONDecodeError as exc:
        print("cannot parse %s: %s" % (args.registry, exc), file=sys.stderr)
        return 2
    if not isinstance(controls, list) or not controls:
        print("registry must be a non-empty JSON array of controls", file=sys.stderr)
        return 2

    rows = []
    for c in controls:
        status, why = classify(c)
        rows.append((c.get("id", "unnamed"), status, why))

    tally = {s: 0 for s in ORDER}
    for _, status, _ in rows:
        tally[status] += 1

    print("LOOP CLOSURE AUDIT")
    print("  registry        : %s" % args.registry)
    print("  controls        : %d" % len(rows))
    for s in ORDER:
        print("  %-14s: %d" % (s.lower(), tally[s]))
    print()

    rows.sort(key=lambda r: ORDER.index(r[1]))
    for cid, status, why in rows:
        print("  %-14s %-32s %s" % (status, cid, why))

    live_traps = tally["TRAP"] + tally["STALE"]
    print()
    if live_traps:
        print("VERDICT: TRAP. %d control(s) are observed and not repaired." % live_traps)
        print("         An observed fault nobody repairs reads GREEN on the org chart.")
        return 1
    print("VERDICT: CLOSED. Every observation is bound to a repair that has fired.")
    return 0

if __name__ == "__main__":
    sys.exit(main())

Point it at a registry of eight controls, each with its observer and repair named where one exists. This is the run, from the terminal:

LOOP CLOSURE AUDIT
  registry        : samples/loop-closure-registry.json
  controls        : 8
  stale         : 1
  trap          : 3
  blind-actuator: 1
  unobserved    : 1
  closed        : 2

  STALE          fire-completion-debt             a repair is named but has not fired since the last observed breach
  TRAP           studio-now-RED-rows              the fault is observable and no repair is bound to the observation
  TRAP           AGENT_LEARNINGS-log              the fault is observable and no repair is bound to the observation
  TRAP           citation-range-lint              the fault is observable and no repair is bound to the observation
  BLIND-ACTUATOR silent-scheduler-death           a repair is bound to no observation, so it fires on nothing
  UNOBSERVED     vault-idle-timeout               no observation mechanism, so the fault cannot be seen at all
  CLOSED         canonical-store-truncation       observation bound to a repair that has fired since the last breach
  CLOSED         commit-provenance-RULE-125       observation bound to a repair that has fired since the last breach

VERDICT: TRAP. 4 control(s) are observed and not repaired.
         An observed fault nobody repairs reads GREEN on the org chart.

Exit code one. Four of the eight are live: three observed with nothing bound to the observation, and one STALE, naming a repair that has not fired since the last breach, closed on paper and open in fact. Point the same tool at a registry where every loop genuinely closes and it says so, exiting zero:

LOOP CLOSURE AUDIT
  registry        : samples/loop-closure-registry-closed.json
  controls        : 2
  stale         : 0
  trap          : 0
  blind-actuator: 0
  unobserved    : 0
  closed        : 2

  CLOSED         studio-now-RED-rows              observation bound to a repair that has fired since the last breach
  CLOSED         canonical-store-truncation       observation bound to a repair that has fired since the last breach

VERDICT: CLOSED. Every observation is bound to a repair that has fired.

Founder offer

If you build autonomous systems, the kind where no human reads the dashboard in time, both tools are yours to run. The paid one compounds: every audit you record teaches it which of your traps are aging, the reading a single audit can never give you.

Pro is $15 a month or $250 a year, which covers the Brief, the paid flagship tool, the paid Feed payload, and the archive. Founder is $300 a year, capped at 100 seats, and includes a founders-only MCP server that is built once the hundredth seat sells.

No pitch beyond that. The closed loop is the argument.

founder bar issue 19