telltale
A terminal instrument for five coding-agent CLIs. Every reading is measured, and a reading it cannot measure is drawn absent, not zero.
Case study summary
- Problem: I direct five coding-agent CLIs across several repositories. None of them tells me how much context is left, what a run cost, whether its sandbox holds, or whether a seat that looks busy has quietly died. Any dashboard I could have built would have had to invent at least one of those numbers.
- Ownership: Sole author. I set the honesty contract, the read-only boundary, and the release bar, and I made every ship-or-refuse call recorded on this page. Claude did most of the typing. The golden files and the postmortems are the proof.
- Constraints: It reads; it never writes back to a vendor. It binds no port and sends nothing off the machine. Windows is the primary target. Every vendor changes its on-disk format without telling anyone.
- Actions: One Go binary. I built one adapter per vendor from its real records, pinned the difference between zero and absent in three golden suites, moved the README’s pictures out of my hands and into the test suite, raced one brief across five seats in isolated worktrees, and refused a list of features that would each have required a number nobody measured.
- Result and evidence: v0.2.0 ships as four archives and a checksum file. The honesty rules are enforced by named tests rather than by intent, so a reader can grep for them. The platform labels say which builds were run and which were only built.
telltale is a Go terminal program. It reads what five coding-agent CLIs leave on disk and draws one row per seat: context, spend, quota, liveness, 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 plausible number gets trusted. Most of the engineering here is the code that declines to render.
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 plain figure was recounted from the repository by a script at build time, at the commit printed under Stack. A figure with a source reference beside it was measured once and recorded in the project’s design document, and this page cites that record rather than re-measuring it. A check in this site’s own test suite fails the build if either kind drifts from the file it was pulled into.
What it is
One static Go binary, no runtime dependency. It has two faces. A heads-up display draws every live agent session as one row. A dispatch room seats five of those agents side by side, sends them one brief, and shows the five answers as five columns. The room is the product and the gauges are the infrastructure, and that ranking is a recorded ruling in the project’s design document rather than a preference of mine.
Underneath, there are 7 vendor adapters. That is a larger number than the five seats on purpose: two of the vendors are watched by the display but are deliberately not seated in the room, and the design document says which and why. A sixth kind of row comes from a drop-file relay — a documented format any tool can write, so a program telltale ships no adapter for can still put a row on screen. That row is marked 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.
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 internal/hud/testdata/golden/usage-ascii.txt
at 38f262a, copied byte for byte from the test suite’s
own output. The fixture is synthesized; the repository holds no real
session content. Scrolls sideways on a narrow screen.
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, so checksum verification against the release’s own checksum file is the whole verification the release offers. The Windows reputation prompt is unmeasured. One package-manager manifest set is a draft and was never submitted.
Zero and absent are different states
Presence lives in two places, on purpose. Every optional field on a
session is a pointer, so a missing reading is a nil rather than a
sentinel number, and 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, and the
column is dropped entirely rather than filled with dashes. 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 how one of those declarations is documented. It is not asserted. The package documentation names the fields the survey looked for in a live corpus and 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, and where the vendor does not expose one, any percentage would be arithmetic performed on a number I made up.
3 golden files, in three different packages, fail the build if a measured zero and an absence ever render the same. The rule is enforced by a stored render rather than by 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.
The badges claimed more than I had measured
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 describing its sandbox posture. The first version of that row read as enforcement across four seats when exactly one of them had a measured mechanism behind it. Nobody wrote a false sentence. The badge simply did not say what kind of thing it was.
The fix was not a better badge. Every badge now names its evidence class, and there are three: enforced, where a live run measured the restriction holding; refuted, where a live run measured it failing; and unverified, where it was looked for and no result was obtained. A fourth class was retired, because “claimed” read like “enforced” and was not. The original error stays in the design document as the reason the rule exists.
One seat has moved in both directions, which 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, and it earned the stronger badge back only on a later re-probe that 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, and a negative finding measured once by one operator is not a fact about somebody’s product.
The same vocabulary covers the seats that were considered and not taken, so that “we looked and said no” never reads like “nobody looked”. Non-seated candidates are recorded with their evidence class, and one is recorded 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.
Five seats, one brief, and no winner
One brief is raced across every seat at once, each attempt in its own git
worktree, and the attempts are compared by diff instead of by prose. Two
mechanics exist only to stop a lying diff. The diff anchors on a recorded
base commit and never on HEAD, and untracked files are staged
first — so an attempt whose entire answer is a new file cannot read
as “no changes”.
Each finisher carries a rank, a phase word, its own clock, and a commit receipt. A seat cut by hand keeps its receipt too. In the first race that seated all five, the first finish came in at 50s, two more finished within about twenty seconds of it, and two seats were given up by hand after roughly 11 minutes — both keeping their commits exactly as the finish-line design promised. Those two figures are cited from the design document’s section on the race, not re-measured here.
I withhold the seat names, and the reason is the point. One race is not a benchmark. Publishing a ranking off a single brief on a single machine would be a claim about five products that the measurement cannot support, and the standings inside the program refuse the same thing for the same reason.
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”, which is not a FAIL. The standings say adopted, never won, because the git refs can record that a branch merged and cannot record a rank, a phase, or that a seat was cut. An undecided race is reported beside the rate and never inside it, and the rate never prints without its count. Hand-cut receipts are refused outright, which makes the figure an undercount — and an undercount is the honest direction to be wrong in.
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.
internal/council/testdata/golden/arena-record-ascii.txt at
38f262a, copied byte for byte. The per-seat rows below
this header are deliberately not reproduced here. Scrolls sideways on a
narrow screen.
What has no live half yet. No live race has run under a check, and no real hybrid adopt has been armed. Both are built and both are unexercised.
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, and it failed by reporting nothing found.
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. The pattern was anchored
on the substring cost and it was lowercase, so a key
spelled with a capital letter in the middle matched neither half. It 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, where the sweep could match one key and reported one key. A session captured weeks earlier carried the identical key set, which settles it: this was never vendor drift. The counts were on disk before the adapter was written. Both figures are cited from the design document’s own correction block.
The same class of mistake landed a second time, when a pattern that swept file contents could never have matched a directory name.
The rule this bought: before a vendor is built 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, which 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
One seat’s command-line flag had been separated from its value, and 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. Every later probe is brief-fenced 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, so a racer that had already exited left its column reading 21 minutes after the fact: no diff, no commit, no rank, no receipt. Two earlier drives had written it off as the vendor quitting. It was not the vendor. The fix was attribution, and it was verified the same day — the column retired in 18 seconds. Both durations are cited 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
The frames in the README were hand-drawn. They had drifted several ways at once, and one of the drifts was a cost line showing a derived figure that the codebase deliberately does not compute. A picture of a program that the program did not draw is a claim with nothing checking it.
They are now emitted by the test suite 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. Every count on this page was re-read from the repository at build time, and the commit it was read at is printed under Stack.
What it refuses to render
Each of these was considered, argued, and declined, and each refusal is a file in the repository with the argument still attached.
- No model judging a check. An estimate marker means a figure the program computed, and an opinion is not a computation.
- No rating or ranking on the standings. A rating is a number with no measurement under it.
- No fleet total, anywhere. Each run reports one vendor, so the arithmetic is not available to make. A test fails the build if a total appears.
- No “percent of plan” spend meter. The plan budget is a guess.
- No cost estimated from a price table. That invents money out of token counts.
- No cross-device sync. Network egress breaks the no-write contract.
- No plugin runtime for third-party adapters. It would run a stranger’s code inside the process whose entire contract is “reads, never writes”. A drop-file relay is the middle path I took instead.
- No pre-seeded rows in a table. A rectangular table is a table of claims, and every zero cell would assert a request nobody sent.
- No star count, download count, install count, or “used by” badge. The program measures none of them, and a third-party render of an unmeasured number is the badge form of a rendered guess.
The review keys are the same rule in a smaller place. Pressing one 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 finished by the time you are reading it. A person pressing a key stays the only thing that spends a quota, and a test asserts it by counting processes spawned and turns started rather than by checking the wording.
The gate that measured the capacity
The same habit that gates a model choice on this site was pointed at the build capacity itself. On a fixed date I wrote down six evidence axes and a close date, froze every change until the close, and let the axes decide. The month returned a step down, and 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 →
What this does not establish
- 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.
- The check mode and the hybrid adopt have no live half yet.
- 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 soak arm found slow fires and refuses to name a cause, because two pollers shared the machine.
- One adapter is researched from source and not live-verified, and its heading says so.
- Two token-total displays are wired end to end and render nowhere today, by ruling.
- 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.
- One seat’s guard wiring is an open obligation, not a finished control.
- 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.
Stack
Go, one static binary, no runtime dependency. No network listener is bound 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 three
zero-vs-absentgoldens, in three packages, consumed byTestGoldenZeroVsAbsent,TestGoldenRenders, andTestGoldenDocuments; TestNoTotalIsRenderedAnywhere, which fails the build if a total is rendered — and asserts it on the rendered string rather than on the code;TestNoGaugeReadsTheEventStore, which walks the transitive import graph and fails if any gauge package can reach the verbatim event store;TestDQuotesIntoTheDraftAndSpawnsNothing, which asserts a review key seeds the draft and spends no quota, by counting processes and turns;- the council package’s
TestMain, which panics if a test would reach a real vendor binary. The default used to be the opposite, and it was measured spending 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 have caught that, because CI has no vendors installed; - a CI step that pipes a fixture through the built binary and greps its own standard output, failing if a quota window renders without its rate-limit source.
I do not print the permission-bypass flag name, the per-vendor sandbox settings, or a containment table. Naming them publishes a bypass instruction.
How it was built: I set the direction, the contracts, and the bar. 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.
Counts and frames on this page were re-read from
sanlee-ys/telltale at 38f262a on
2026-08-30. The suite holds
132 golden text renders and
1780 test functions at that commit.
Each figure names the command that produced it, and a check in this
site’s test suite fails the build if a rendered figure drifts from
that record.
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 — and it is enforced by golden files rather than by intent, because intent does not fail a build. A list of features was refused outright, each because it would have required an invented number, and 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, and both times the wrong answer arrived as an absence rather than as an error, which is the failure mode this product is least able to see in itself. Every sentence above resolves to a file you can open.