← Back

telltale

A terminal instrument for five coding-agent CLIs. It measures every reading, and a reading it cannot measure renders as absent, never as zero.

Case study summary

Frame 01
 fleet usage  quota is a reading against a limit; spend is a count with none  =========================================

 claude  quota relayed by the statusline - 2h ago
        5h             ########------------    42%  ~ 2h13m
        7d             #-------------------     6%  ~ 5d00h

 codex  quota read from its own store, this scan
        models         gpt-5.1-codex
        7d             ###############-----    79%  ~ 22h48m

 gemini  no quota reaches disk anywhere telltale can read
        models         gemini-3-pro
Two vendors expose a quota, so the display renders it against its limit. The third exposes none that reaches disk, so its row states the reason in words and renders no gauge. The header states the distinction the whole view is built on. Lines 3 to 14 of internal/hud/testdata/golden/usage-ascii.txt at 07163dd, in the ASCII render profile, copied byte for byte from the test suite’s own output. The fixture is synthesized, and the repository holds no real session content. The rows below line 14 are not reproduced. The frame is 119 columns and scrolls sideways on a narrow screen.

telltale is a Go terminal program. It reads what five coding-agent CLIs leave on disk. It renders one row per seat: context, spend, quota, liveness, and sandbox posture. The interesting part is not the gauge; it is the refusal. A gauge that fills a gap with a plausible number is worse than no gauge, because a reader trusts a plausible number. Most of the engineering here is the code that refuses to render.

Plate 02 Measured zero draws a full empty track. Absent draws nothing. An invented reading is the failure. 0% MEASURED vendor said zero — — ABSENT no track, no number 47% INVENTED plausible, unmeasured

A measured zero and an absence are different claims, and the render has to keep them different. The third cell is the one the whole program exists to prevent.

Two kinds of number appear below. A script recounts a plain figure from the repository at build time, at the commit printed under Stack. I measured a figure with a source reference beside it once, and I recorded it in the project’s design document. This page cites that record and does not re-measure it. A check in this site’s own test suite fails the build when either kind drifts from the record.

What it is

telltale is one static Go binary with no runtime dependency. It has two faces. The heads-up display renders every live agent session as one row. The dispatch room seats five of those agents side by side and sends them one brief. It renders the five answers as five columns.

The room is the product and the gauges are the infrastructure. The project’s design document records that ranking as a ruling, not as a preference.

Plate 03 Five adapters are seated, two are watched, and the relay sits outside the set. VENDOR ADAPTERS SEATED IN THE ROOM WATCHED, NOT SEATED DROP-FILE RELAY MARKED SELF-REPORTED
telltale ships 7 vendor adapters, and the dispatch room seats five of them. The display watches the other two and does not seat them, on purpose. The drop-file relay is a sixth kind of row and it is not an adapter, so it sits outside the set. The plate names no vendor, and it does not say which two the display only watches. The design document holds that list and the reason for it. The plate also does not show the relay format.

The design document says which two vendors the display only watches, and why. The relay format is documented and any tool can write it. So a program telltale ships no adapter for can still put a row on screen. telltale marks that row self-reported, because telltale did not measure it.

An agent-facing mode serves the same document over stdio. It binds no port and it writes nothing. Install is a direct archive, a scoop manifest exercised once, or a source build. See the display refuse a reading →

What the release does and does not prove. Two of the four archives are built, not run: no recorded run exists for the macOS ARM build or the Linux build. All macOS measurement is one Intel machine. No binary is signed. Checksum verification against the release’s own checksum file is therefore the whole verification the release offers. The Windows reputation prompt is unmeasured. One package-manager manifest set is a draft and I never submitted it.

Contents

Zero and absent are different states

Plate 04 A pointer drops one field. A declaration drops a whole column. TWO INDEPENDENT LAYERS PER SESSION: A POINTER ONE FIELD DROPS PER ADAPTER: A DECLARATION ONE COLUMN DROPS DROPPED, NOT DASHED
An absence is enforced in two independent places, and neither one can cover for the other. A nil pointer on a session drops one field. A capability declaration on an adapter drops a whole column. The plate does not show what a reader sees where the column used to be, and it shows no context percentage. Some vendors expose no per-model window, so that denominator does not exist.

Presence lives in two places, on purpose. Every optional field on a session is a pointer, so an absent reading is a nil and never a sentinel number. A rendered 0 therefore always means the vendor said zero. Above that sits a capability declaration per adapter: a vendor that can never supply a field declares it unsupported. A column of dashes for a vendor that could never fill it is itself a small claim about what was measured.

The strongest detail in the repository is the way the code documents one of those declarations. The code does not merely state it. The package documentation names the fields the survey looked for in a live corpus, and it records that the search returned no matches. An absence documented with the search that found nothing is a different claim from an absence nobody looked for.

The same rule is why there is no context percentage for some vendors. A percentage needs a per-model window as its denominator. Where the vendor does not expose one, any percentage would be arithmetic on a number I made up.

3 golden files, in three different packages, fail the build when a measured zero and an absence ever render the same. A stored render enforces the rule, not intent, because intent does not fail a build.

The rule: a gauge may state a number, or it may state nothing. It may not state a number it did not measure. An absence documented with the search that found nothing is a different claim from an absence nobody looked for. That difference is the whole product.

Contents

The badges claimed more than I measured

A doubled rule marks a decision that was reversed.

Reversed. A badge with no evidence class

The row read like enforcement, and one seat had a mechanism

Each seat in the room carries a badge that describes its sandbox posture. The first version of that row read as enforcement across four seats. Exactly one of the four had a measured mechanism behind it. Nobody wrote a false sentence. The badge simply did not say what kind of thing it was.

Plate 05 Three evidence classes are live, and the fourth is retired. EVERY BADGE NAMES ITS EVIDENCE CLASS ENFORCED A LIVE RUN MEASURED IT HOLDING REFUTED A LIVE RUN MEASURED IT FAILING UNVERIFIED LOOKED FOR, NO RESULT CLAIMED RETIRED IT READ LIKE ENFORCEMENT
A badge names its evidence class, so the reader is not left to supply the strongest reading. Three classes are live: enforced, refuted, and unverified. The fourth class, claimed, is retired. The plate names no vendor and no seat. It shows no per-vendor sandbox posture and no verdict table, because which settings do not contain writes is a bypass instruction.

The fix was not a better badge. Every badge now names its evidence class, and there are three: enforced, refuted, and unverified. I retired a fourth class, because “claimed” read like “enforced” and was not. The original error stays in the design document as the reason the rule exists.

One seat moved in both directions, and that is the only way to show the classes are real. It carried a weaker badge, because at one tool version a read-only flag failed every process spawn. It earned the stronger badge back on a later re-probe. That re-probe measured a live write denied, with no file left on disk.

I do not name the seat, and I do not publish a per-vendor verdict table. Which settings do not contain writes is a bypass instruction. A negative finding measured once by one operator is not a fact about somebody’s product.

The same vocabulary covers the seats I considered and did not take, so “I looked and said no” never reads like “nobody looked”. The record holds every non-seated candidate with its evidence class. It records one as installed, waitlisted, and explicitly not a rejection.

The lesson: a badge that does not name its evidence class is a rumour with a border. A reader supplies the strongest reading available. So the claim has to carry its own evidence class, or it lies by default.

Contents

Five seats, one brief, and no winner

telltale races one brief across every seat at once. Each attempt runs in its own git worktree. A diff compares the attempts, and prose does not. Two of the mechanics below exist only to stop a lying diff.

Strip 06
The mechanic The failure it blocks
  • The diff anchors on a recorded base commit and never on HEAD. A lying diff.
  • Untracked files are staged first. An attempt whose entire answer is a new file reads as “no changes”.
  • A check grades an attempt from the real exit code and nothing else. Not from output, not from the diff, not from duration, and not from a model’s opinion.
  • A command that could not run reads “check unavailable”. It is not a FAIL.
  • The standings say adopted, never won. The git refs can record that a branch merged. They cannot record a rank, a phase, or that a seat was cut.
  • Hand-cut receipts are refused outright. The figure is an undercount.
Six race mechanics, each beside the failure the page says it blocks. Every cell keeps the words the prose used, and the two column names are the only new words. The strip names no seat and reports no result. One race is not a benchmark, so there is no ranking to show. It also leaves out the undecided-rate rule, because the prose never says what that rule blocks.

The standings report an undecided race beside the rate and never inside it, and the rate never prints without its count. An undercount is the honest direction to be wrong in.

Each finisher carries a rank, a phase word, its own clock, and a commit receipt. A seat that I cut by hand keeps its receipt too. The first race seated all five seats. The first finish came in at 50s, and two more finished within about twenty seconds of it. I gave up two seats by hand after roughly 11 minutes. Both seats kept their commits, exactly as the finish-line design promised.

This page cites those two figures from the design document’s section on the race. It does not re-measure them. See the record state its reach →

I withhold the seat names, and the reason is the point. One race is not a benchmark. A ranking published off a single brief on a single machine would be a claim about five products. The measurement cannot support that claim. The standings inside the program refuse the same thing for the same reason.

What has no live half yet. No live race ever ran under a check, and I never armed a real hybrid adopt. Both are built and both are unexercised.

Contents

Twice the instrument was the bug

Four corrections, and two of them are the same shape. The thing that was wrong was my own measuring equipment. It failed in one direction: it reported nothing found.

Plate 07 An old session with the identical key set rules out vendor drift. WHAT THE SWEEP COULD SEE RECORD ON DISK SWEEP A PATTERN REPORTED NOTHING FOUND OLD SESSION WEEKS EARLIER IDENTICAL KEY SET INSTRUMENT, NOT DRIFT
The sweep sat between the record and the answer, and it reported nothing found. A session recorded weeks earlier carried the identical key set, so the verdict is the instrument and not vendor drift. The plate carries no count; both counts are cited figures, so they stay in the prose below with their design-document reference.

Reversed. The regex answered the question it was asked

A sweep reported one key, and the record carried eleven

A pattern sweep behind one vendor’s cost row concluded that the vendor exposed a cost total and nothing else. I anchored the pattern on the substring cost, and I wrote it in lowercase. A key spelled with a capital letter in the middle matched neither half. The pattern was structurally incapable of the question a reader takes that row to answer.

A live re-measure read eleven keys off the same turn record. The sweep could match one key and reported one key. A session recorded weeks earlier carried the identical key set, and that settles it: this was never vendor drift. The counts were on disk before I wrote the adapter. This page cites both figures from the design document’s own correction block.

The same class of mistake landed a second time. A pattern swept file contents, and it could never match a directory name.

The rule this bought: before I build a vendor here, re-read its records, not its struct. A record struct is an allowlist, so what it omits is a decision and not an absence. Both instrument bugs failed in the same direction: they reported nothing found. That is the one failure mode this product is least able to see in itself.

A green test over the wrong prompt

A live test stayed green while one seat was dead for a day

I separated one seat’s command-line flag from its value. That vendor’s parser refuses a brief that opens with three dashes. Every briefed turn died with an empty column. The live test stayed green the whole time, because it probed with a prompt that began with a letter.

The lesson: probe a vendor with a prompt shaped like a brief, not like a greeting. I brief-fence every later probe for that reason.

A guard that read a live process as health

A finished racer read as streaming for twenty-one minutes

A stale-exit guard attributed a live process to the wrong seat. A racer that already exited therefore left its column live for 21 minutes after the fact: no diff, no commit, no rank, no receipt. Two earlier drives wrote it off as a vendor that quit. It was not the vendor.

The fix was attribution, and I checked it the same day. The column retired in 18 seconds. This page cites both durations from the design document’s measurement block.

The lesson: a debt that will not pay may be a broken feature rather than an unfinished errand.

Reversed. Pictures a human maintained

The README’s frames drifted, and one of them showed a number the code refuses to compute

I drew the frames in the README by hand. They drifted several ways at once. One drift was a cost line that showed a derived figure the codebase deliberately does not compute. A picture of a program that the program did not render is a claim with no check on it.

The test suite now emits them from the real render, and a test fails the build when they stop matching.

Why it governs this page too: a picture a human maintains is a claim with no gate on it. A script re-read every count on this page from the repository at build time. The page prints the commit it read them at under Stack.

Contents

See it: the recorded frames

Frame 08

Six more recorded states of the instrument follow the reading at the top of this page. Each one is a byte-exact, contiguous line range of one committed golden file, and each caption names the file, the range, and the commit. A golden file is what the upstream test suite asserts against, so a drifted frame turns that build red. This is a state stepper and not a time stepper.

  1. a stale reading
  2. an absence
  3. the record
  4. the gate
  5. the refusal
  6. on a phone
 fleet usage  quota is a reading against a limit; spend is a count with none  =========================================

 claude  quota relayed by the statusline - ! 19h ago - older than the fleet's shortest quota window
        7d             ###-----------------    15%  ~ 4d07h

 codex  quota read from its own store, this scan
        models         gpt-5.1-codex
        7d             ###############-----    79%  ~ 22h48m
The display marks a relayed reading that went stale, and the mark carries the reason: the reading is older than the fleet’s shortest quota window. The display does not drop it and does not refresh it by guesswork. Lines 3 to 10 of internal/hud/testdata/golden/usage-stale-ascii.txt at 07163dd, in the ASCII render profile. The fixture is synthesized. The frame does not show how the display decides that a reading is stale, only that it says so. The frame is 119 columns and scrolls sideways on a narrow screen.
 cursor  no quota anywhere - its store holds experiment values, not usage
        models         composer-2.5

 grok  no quota anywhere - no window, no ordinal, no reset time on its disk
        models         grok-4.5
An absence names its own search. Two vendors have no quota that reaches disk, and each row states a different reason in words. One store holds experiment values and not usage. The other has no window, no ordinal, and no reset time at all. Lines 20 to 24 of internal/hud/testdata/golden/usage-stale-ascii.txt at 07163dd, in the ASCII render profile. A third vendor states a third reason, and this window does not reach it. The first frame above carries that row. The window stops below the relayed block, because one line there names a vendor family beside a rate, and this site never publishes that pairing. The frame is 75 columns and scrolls sideways on a narrow screen.
  arena record  ===================================================================================  telltale  5 races  
  read from the arena/ and adopt/ branches this repository still holds, t2 through t9: 3 decided by you, 2 nobody was   
  adopted from. A race whose branches were dropped is no longer in the record.                                          
The record states its own reach before it states a single result. It names what it read, how many races that was, and how many a human decided. A race whose branches were dropped is gone from the record, and it is not counted as a loss. Lines 8 to 10 of internal/council/testdata/golden/arena-record-ascii.txt at 07163dd, in the ASCII render profile. The per-seat rows below this header are not reproduced, at any line range and under any caption. They pair a named seat with an adoption rate and a verdict, and one race is not a benchmark. The frame is 120 columns and scrolls sideways on a narrow screen.
  +------------------------------------------------------------------------------------------------------------------+  
  |  :                                                                                                               |  
  +---------------------------------------------------------------------------------------------------------  GATE  -+  
                  Write: internal/council/clock.go  |  y approve  |  n deny  |  a stop asking  |  ctrl+c cancel claude  
A person who presses a key is the only thing that spends a quota. The composer box sits above the gate bar, and the bar offers approve, deny, stop asking, and cancel. Lines 21 to 24 of internal/council/testdata/golden/gated-vs-streaming-ascii.txt at 07163dd, in the ASCII render profile. The seat rows above this window are not reproduced. The row directly under them pairs each named seat with its sandbox setting, and naming which settings do not contain writes is a bypass instruction. The frame is 120 columns and scrolls sideways on a narrow screen.
 telltale needs 60 columns (have 52)
Below 60 columns the display renders nothing but the reason. This is the product’s own refusal, in one line, and it is the same rule the gauges follow: state the reason, never a plausible substitute. Line 1 of internal/hud/testdata/golden/floor-width.txt at 07163dd, in the ASCII render profile. The frame does not show what the display would have rendered at 60 columns, because it renders nothing. It is 36 columns, and it is the only frame here that a 390px phone reads whole with no sideways scroll.
  +------------------------------------------------------+  
  |  ^ 2 more above   third line_                        |  
  +---------------------------------------------  GATE  -+  
   Write: internal/council/gate.go  |  y approve  |  n de>  
The 60-column twin of the gate frame, and the narrowest council window in the corpus, at half the width of every frame above it. The composer clips to one row and says how many rows it hid. Lines 7 to 10 of internal/council/testdata/golden/composer-clipped-to-one-row-ascii.txt at 07163dd, in the ASCII render profile. Lines 1 to 6 are cut. Line 5 is a council seat row, and lines 3, 4, and 6 each name a vendor beside a state. This site publishes neither. The frame is 60 columns, and it still scrolls sideways on a phone, though far less than the frames above it.

The frame that matters most is the one that cannot be shown. The flagship golden is the one that renders a measured zero and an absence side by side. It carries four distinct non-ASCII characters with no twin in the shipped subset, and one of its lines carries a dollar figure. So the page argues that claim with a plate and states here that it cannot render it. That sentence is not a fallback. It is this page’s own rule applied to itself.

Contents

What it refuses to render

I considered each of these, I argued it, and I declined it. Each refusal is a file in the repository with the argument still attached.

The review keys are the same rule in a smaller place. A press lands a quoted hunk in the live composer draft, and nothing else happens. The surveyed alternatives route a comment straight back to the agent. This room cannot do that honestly, because a race attempt is one-shot and it is finished before you read it. A person who presses a key stays the only thing that spends a quota. A test asserts that: it counts the processes spawned and the turns started, and it does not check the wording. See the gate bar →

Contents

The gate that measured the capacity

I pointed the same habit that gates a model choice on this site at the build capacity itself. On a fixed date I wrote down six evidence axes and a close date. I froze every change until the close, and I let the axes decide. The month returned a step down. It also returned a process finding I did not want: nothing ever wrote to the axes. I tell that one on the program page, because it is a program artifact and not a product feature. The capacity gate →

Contents

What this does not establish

Strip 09

Release and platform

  • Two of the four release archives are built, not run. Nothing here proves they start.
  • All macOS measurement is one Intel machine. The ARM build was not walked at all.
  • No binary is signed, and the Windows reputation prompt is unmeasured.
  • One package manager is a draft manifest that was never submitted; the other route was exercised once.

Built, with no live half

  • The check mode and the hybrid adopt have no live half yet.
  • Two token-total displays are wired end to end and render nowhere today, by ruling.
  • One seat’s guard wiring is an open obligation, not a finished control.

How far the measurement reaches

  • The spend-history mode reads one vendor. The others are surveyed and named on every run with the reason each is not read — and that survey is a source read, not a fresh live measurement, which has already been caught out once.
  • The soak is 35 minutes on an idle machine, on one operating system, and it is not a CI gate. Two of its cost readings belong to the machine, not to the vendor.
  • One adapter is researched from source and not live-verified, and its heading says so.
  • The health command never probes authentication or network. Those rows read “not checked”, which is neither a failure nor a soft pass, and it exits 0 even when every seat is missing.

What this page refuses to publish

  • One soak arm found slow fires and refuses to name a cause, because two pollers shared the machine.
  • There is no recorded demo, capture, or video in the repository. A committed capture is a publication and would need a per-frame review for workspace paths and seat identity first.
  • I do not publish a count of live vendor drives. The project carried one twice and it was wrong both times.
Fourteen recorded limits, in four kinds. Every line keeps its exact text, and the four group names are the only new words. The grouping ranks nothing and drops nothing. A short group is not a small problem, and a limit that is not on this list is a limit I have not found yet.

Contents

Stack

Go, one static binary, no runtime dependency. The program binds no network listener by default. The agent-facing mode serves the same document over stdio and binds no port. No telemetry, no price table, and no model call anywhere in the gauge path. Public repository: github.com/sanlee-ys/telltale.

The honesty rules are named tests, so a skeptic can grep rather than trust:

The honesty rule The test that fails the build
A measured zero and an absence never render the same. the three zero-vs-absent goldens, in three packages, read by TestGoldenZeroVsAbsent, TestGoldenRenders, and TestGoldenDocuments
No total is rendered anywhere. The assertion reads the rendered string, not the code. TestNoTotalIsRenderedAnywhere
No gauge package can reach the verbatim event store. TestNoGaugeReadsTheEventStore, which walks the transitive import graph
A review key seeds the draft and spends no quota. TestDQuotesIntoTheDraftAndSpawnsNothing, which counts processes and turns
No test reaches a real vendor binary. the council package’s TestMain, which panics
A quota window never renders without its rate-limit source. a CI step that pipes a fixture through the built binary and greps its own standard output

TestMain’s default used to be the opposite. I measured that default, and it spent real money. A plain suite run on a machine with a vendor installed opened a live, write-enabled turn on my own account. CI could never find that, because CI has no vendors installed.

I do not print the permission-bypass flag name, the per-vendor sandbox settings, or a containment table. Any of those names publishes a bypass instruction.

How it was built: Claude does most of the typing. The proof is the suite and the goldens, and a test entry point that panics rather than let a test spend money. How I built this site →

A script re-read the counts and frames on this page from sanlee-ys/telltale at 07163dd on 2026-09-14. The suite holds 171 golden text renders and 2191 test functions at that commit. Each figure names the command that produced it. A check in this site’s test suite fails the build when a rendered figure drifts from that record.

Contents

What it demonstrates

The rest of this record is a system I built with agents. This is the instrument I built to watch them. The honesty rule is one sentence: a gauge may not render a number nobody measured. Golden files enforce that rule, not intent, because intent does not fail a build.

I refused a list of features outright, and each one would have required an invented number. Every refusal is a file you can read, with the argument still attached.

When my own instrument was the thing that was wrong, the correction went into the record next to the claim it broke. Both times the wrong answer arrived as an absence and not as an error. That is the failure mode this product is least able to see in itself. Every sentence above resolves to a file you can open.

Read the code →

The program that built it →