Skip to content

Every failure degrades to silence, and silence is indistinguishable from a decision (8 instances, one defect) #1012

Description

@lilyshen0722

Every failure in this system degrades to silence, and silence is indistinguishable from a decision

Eight defects found in one session. They look unrelated. They are the same defect.

# Defect What a human sees What it actually was
1 Dead-letter cap (#998) agent stops answering events retired to terminal failed after ~45 min
2 POST /api/pg/messages message posted, 200 OK enqueueMentions never called — woke nobody
3 Cascade starvation (#1010) seat stops contributing refused, acked no_action, invisible to peers
4 fable-lead mute (TASK-028) agent has nothing to say wakes, spawns, exits 0, returns NO_REPLY — every health check green
5 Author frame (#1011) agent ignores a human human identity arrived as **unknown**
6 NO_REPLY to a mention agent is broken? deciding? unknowable from outside
7 classifyTriggerunknown silently resets a cascade streak
8 Heartbeats enqueue 0 fleet is quiet boot banner said they run (#986)

In every row, the system did something reasonable-looking and produced nothing. No error, no signal, no trace at the surface a human watches.

Why this is the expensive kind of bug

Silence is the one output that carries no information. A crash names itself. A 500 names itself. A refusal that acks no_action and moves on looks exactly like an agent that considered the message and had nothing to add — which is also a thing we explicitly want agents to do.

So the failure mode and the desired behaviour are the same observable. That is why #4 cost hours: I eliminated message length, session staleness, session bloat, crashes, config, pod routing and CLI version before finding the prompt in argv. Every check came back green because every check measures liveness, and the seat was fully alive.

The principle this should have been designed around

A turn must always produce an observable outcome.

Not "should log" — logs are where silence goes to be invisible. Observable at the surface where the question was asked.

Three consequences:

  1. A refusal aimed at a human is an event, not an absence. If a seat declines, is capped, loses a claim, or returns NO_REPLY to a human mention, that is a fact the human should be able to see. A reaction is the cheap carrier — it is bounded, non-recursive, and commonly_react_to_message already exists on both driver classes.
  2. Outcomes must be queryable per seat. AgentEvent already acks { outcome, reason }. Nothing surfaces it. "Which seats have produced no output in the last hour, and why" should be one query, not an afternoon of ps and log archaeology.
  3. Liveness checks must measure output, not process state. Nine seats were "healthy" — polling, connected, spawning, exiting 0 — while two were starved and one was mute. Counting posts revealed it; nothing else did.

What NOT to do, and this is load-bearing

Do not add a mechanism per row of that table. Cumora's COORDINATION.md names this exactly (anti-pattern 4):

"Don't pile loop-prevention mechanisms when one already exists... Adding a fourth mechanism for a specific observed loop is usually wrong."

We already have three dampeners (self-wake guard, wake-loop dampener, cascade governor) and I was drafting a fourth. The work is consolidation, not accumulation.

Their anti-pattern 8 is this session in one sentence:

"Read the server logs first. If an end-to-end symptom doesn't match any of the in-place defense layers' expected behavior, suspect infra first."

I did the opposite on fable-lead — theorised about session self-imitation, cleared the session, restarted the process, and only then read the prompt. The prompt was in argv the whole time and contained the answer.

Suggested sequence, for challenge rather than adoption

  1. fix(auth): every human message reached agents as 'raised by unknown' #1011 — human identity reaches agents (merged or open; it is the smallest and unblocks the rest)
  2. Surface no_action outcomes per seat — one query, then a UI affordance
  3. Refusal-to-a-human becomes a reaction — needs feat(reactions): notify agents of new acknowledgements #970 resolved first, and feat(reactions): notify agents of new acknowledgements #970 currently types receipts as chat.mention, which is Cumora's anti-pattern 6 (a free bypass that erodes the gate it bypasses)
  4. Consolidate the three dampeners into one room-level control — Cumora's per-room framing, not their specific lapping floor: their empirical baseline is counting games and werewolf, where one contribution per participant is the correct shape, and ours is multi-hour technical review where two seats legitimately exchanged 194 posts

Where I have been wrong tonight, since it bears on trusting this synthesis

Six corrections in one session, most caught by measurement or by an agent rather than by reasoning. That is the argument for making outcomes observable: I was only able to correct any of them where the system produced evidence.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions