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
- Result and evidence: v0.3.0 ships as four archives and a checksum file. Named tests enforce the honesty rules, not intent, so a reader can grep for them. The platform labels say which builds I ran and which I only built.
- Problem: I direct five coding-agent CLIs across several repositories. None of them tells me how much context is left or what a run cost. None tells me whether its sandbox holds, or whether a seat that looks busy died quietly. Any dashboard I built would have to invent at least one of those numbers.
- Ownership: Sole owner and technical lead. 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: The gauge path reads; it never writes back to a vendor. The gauge path and the stdio agent mode bind no port and send nothing off the machine. Two modes are exceptions. The optional event listener binds a loopback port on 127.0.0.1 and refuses any other host. Council mode starts vendor CLIs, which talk to their vendors, and it can write to the workspace. Windows is the primary target. Every vendor changes its on-disk format, and no vendor announces it.
- Actions: One Go binary. I built one adapter per vendor from its real records. I pinned the difference between zero and absent in three golden suites. I moved the README’s pictures out of my hands and into the test suite. I raced one brief across five seats in isolated worktrees. I refused a list of features, and each one would have required a number nobody measured.
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
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.
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.
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.
Zero and absent are different states
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.
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.
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.
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.
- 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.
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.
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.
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.
See it: the recorded frames
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.
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 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 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.
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 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)
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>
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.
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.
- No model judges 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 when 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 state 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. 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 →
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 →
What this does not establish
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.
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.
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.