Files
lumbridge-code/docs/decisions/0019-attention-carries-provenance.md
T
Metal AgentandClaude Opus 5 3a8a100ea5 Make the attention count real, and make it carry a source
The sidebar had said ATTENTION · 1 since the first commit. It was reading a
fixture's needs_input flag that no live pane ever set, so the number was frozen
at whatever the demo data said.

Attention is now built from observed signals, and every signal carries an
AttentionSource. The rule, enforced by is_countable on the source rather than by
a filter at the call site: a guess may draw a row and sort it, but may not
increment the count. That is decision 0012's provenance rule applied to a
different claim, for the same reason — a wrong count teaches people to ignore
the number, and the number is the whole point of the section.

RuntimeObserved is the only source that produces signals today, from process
exits and runtime faults. The two ACP kinds are declared and never constructed,
so there is a shape for the ACP client to fill and nobody is tempted to
approximate "asked you a question" by watching output for a question mark.

RuntimeEvent::Exited carries a u32 code that was being formatted into a sentence
and discarded; AttentionKind::Finished keeps it, which is why a row can say
"Exited with code 42" instead of "needs attention".

Verified live: exiting a pane with code 42 produces ATTENTION · 1, a row reading
"Exited with code 42 · observed", a dimmed pane banner offering restart, and a
live-PTY count that drops from 3/3 to 2/3.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-31 23:38:55 -07:00

2.8 KiB

0019: Attention carries a source, and a guess may not be counted

Status: accepted; runtime-observed signals are live.

The sidebar said ATTENTION · 1 from the first commit. It was reading a fixture's needs_input flag that no live pane ever set, so the number was frozen at whatever the demo data said and had no relationship to anything running.

Replacing it raises the question the usage ledger already answered once: what is a number in this interface allowed to claim?

The rule

Every attention signal carries an AttentionSource, and a source that is a guess may draw a row and may sort it, but may not increment the count.

  • AcpRequest — the harness asked, over ACP. It said so itself.
  • HarnessReported — a documented status surface reported it.
  • RuntimeObserved — Lumbridge saw it in a process it supervises: an exit, a fault. The only source that produces signals today.
  • TitleHeuristic — inferred from a window title or an output pattern.
  • Fixture — test or demonstration data.

The first three count. The last two do not, and is_countable is a method on the source rather than a filter at the call site, so a new signal cannot be counted by forgetting to exclude it.

This is decision 0012's provenance rule applied to a different claim. The reasoning is the same: a wrong count is worse than no count, because it teaches people to ignore the number, and the number is the whole point of the section.

What is deliberately unreachable

PermissionRequested and QuestionAsked are declared and never constructed. Those arrive over the ACP client, which does not exist yet.

They are written down rather than left out so there is a defined shape for the client to fill — and, more importantly, so nobody is tempted to approximate them with a TitleHeuristic that watches for a question mark in the output. A pane that looks like it is asking something is not a pane that asked. That signal would be a guess, would therefore not be countable, and the honest version of it is simply not to ship it.

What a signal replaces

One pane wants attention for one reason at a time: a new signal replaces the pane's previous one rather than accumulating, or the count would grow every time a pane was restarted. Signals sort by source first and age second, so the most trustworthy and longest-waiting thing is at the top.

Restarting or terminating a pane clears its signal, because the thing that was waiting is no longer waiting.

The exit code survives

RuntimeEvent::Exited carries a u32 code that used to be formatted straight into a sentence and discarded. AttentionKind::Finished { exit_code } keeps it, which is what lets the row say "Exited with code 42" rather than "needs attention", and what will later let a finished-successfully signal be told apart from a failure without re-parsing English.