verify-t3term
Skill11 starsChanged 28 days agoMITView on GitHub
Drive the built t3term TUI or CLI against a fake T3 server and keep what it showed as evidence: start a run, check it is healthy, send keys, clicks and server events, wait on the screen, capture and render frames, check the run's rules, clean up. Use to prove a change works in the binary, to reproduce a bug, to take screenshots from invented data, or as the drive step of verify-pr and verify-this.
- Installs packages
In this repository8 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- benchmark-checklistSkill
- blast-radiusSkill
- maintain-verify-t3termSkill
- tddSkill
- verify-prSkill
- verify-thisSkill
The file
We split this skill into the rules it gives an agent
4 rules. About 0 are common, 1 is distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 14
Read features/README.md and the file for the feature you are verifying before you drive it.
- Very specific: line 56
When doctor passes but the screen looks stuck, t3v restart quits t3term and starts it again with the same HOME.
- Very specific: line 60
Do one action, then wait for what it should change.
- Distinctive: line 87
Every project, thread, URL and line of text the scenarios serve is invented, so captures and PNGs are safe to post on a pull request.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Verify t3term
Unit tests draw frames on ratatui's test backend. This skill runs the real binary in a real terminal, so it catches what those tests can't: how keys and mouse reports decode, resizes, what happens between two frames, the sessions t3term issues and revokes, and the requests it makes while idle.
scripts/t3v does the work. Everything it starts is local and invented: a fake T3 server on 127.0.0.1, a t3 stand-in that issues sessions, a temporary HOME and T3CODE_HOME, and tmux on the run's own socket. t3term runs under env -i with T3TERM_NO_SAVED_LOGIN=1, so it never reads ~/.t3, the Keychain or a real T3 server, and a run is safe on a machine where T3 is running.
It needs macOS, Python 3.9 or newer, tmux (brew install tmux), cargo for builds and, for render only, swiftc from the Xcode command line tools.
Read features/README.md and the file for the feature you are verifying before you drive it.
Launch
From the repository root:
cargo build --locked
T3V=.claude/skills/verify-t3term/scripts/t3v
$T3V startstart runs target/debug/t3term against the links scenario at 200x50 and returns once the scenario's ready text is on screen. It prints the run directory, the evidence directory, the server, the binary and a read-only tmux attach line you can use to watch.
--binary PATHruns another build.--rev REVbuilds any commit first witht3v build REV, which uses a temporary worktree and never touches your checkout, and caches the result in/tmp/t3v-bin/<sha>/t3term.--scenario NAMEpicks the world the server serves. See the table below.--colsand--rowsset the pane, from 80x24 up.t3v resizegoes smaller afterwards.--evidence DIRsays where the proof goes. The default is/tmp/t3v-evidence/<run name>/.--jsonprints one object with the run's path, for scripts.
Two runs work side by side, each with its own port, socket, HOME and evidence. /tmp/t3v-current links only to the newest, so with more than one run pass --run DIR or set T3V_RUN on every command:
BASE=$($T3V start --binary "$($T3V build origin/master)" --evidence /tmp/x/base --json | jq -r .run)
HEAD=$($T3V start --evidence /tmp/x/head --json | jq -r .run)
T3V_RUN=$BASE $T3V keys EnterScenarios
| Scenario | World | Ready text | Feature files |
links | Project "Linked Desk" with 8 threads, one for each state of a thread's linked pull requests: every state, one open link, an emptied list, the old single link, no field, 1,005 links, hostile text and an unreadable list. t3term launch adds threads to it, and t3v launches makes T3 refuse or drop them | Mixed: every state | sidebar, connection, thread-details, linked-pull-requests, pull-request-page, cli, environments, transcript, appearance |
lineage | Project "Lineage Desk" with 10 threads that fork, start agents and transfer context: a parent, forks running and done, archived forks and parents, 134 agents of which 124 are done, and a thread with no relations | Main: open me first | lineage |
terminal | Projects "Terminal Desk" and "Rootless Desk" with 8 threads, one for each state of the terminal drawer: running with history, no terminal yet, no worktree, exited, failed, busy, 8 MiB of history and no workspace root. The fake shell answers what the drawer types. Needs a build with the drawer (#41) | Running: a shell | none until the drawer merges |
t3v expect prints what t3term should show now, worked out from what the server sent. t3v threads lists the thread ids that the world controls take.
Doctor
Run t3v doctor before the first drive and after anything surprising. It only reads, and checks that the run directory is t3v's, that the server and t3term run what the run started, that the port answers with this run's environment, that the pane has the recorded size, that the binary is unchanged and that check's rules hold so far. It exits 0 when everything passes.
When doctor passes but the screen looks stuck, t3v restart quits t3term and starts it again with the same HOME. If that doesn't help, clean up and start a new run rather than drive a broken one.
Drive
Do one action, then wait for what it should change. Never sleep.
t3v keys KEY...sends tmux key names:Enter,Up,Down,Left,Right,Escape,Tab,BTab(Shift+Tab),PageUp,PageDown,Home,End,C-b(Ctrl+B),M-b(Alt+B).--literal TEXTtypes text. Keys go 0.2 s apart unless--gapsays otherwise.t3v click --text "Pull requests"clicks where the text shows now.--nth N,--from-coland--from-rowpick among several places.--at COL ROWcounts from 0.--button wheel-uporwheel-downsends wheel notches, and--repeat Nsends more than one.t3v drag TOdrags the sidebar's edge and releases at column TO.t3v resize COLS ROWS.t3v wait --text T --gone T --idle MS --timeout Spolls the pane every 50 ms until every condition holds at once.--textand--gonerepeat. On a timeout it exits 1 and prints which condition failed and the screen. If t3term exits, the wait fails at once and says how it ended, such ast3term was killed by SIGKILL (9).- World controls change what the server sends, as T3 would:
t3v reconnect --hold 5drops every socket for 5 s,t3v shell failends the sidebar's feed,t3v run thr-single runningstarts a run,t3v links thr-single mergedchanges a thread's links, andt3v frame jsonsends the TUI's sockets a text frame that isn't JSON.t3v frame binarysends binary that isn't UTF-8 instead, and--client clisends either to the CLI's sockets.t3v --helplists them all, and each feature file names the ones it uses.
A click lands where the last frame drew. After a key, a wheel notch, a resize or a server event, wait for the frame that shows the change before you click. t3term ignores clicks on several widgets until that frame, on purpose, and a click sent too early does nothing.
Evidence
The evidence directory outlives the run:
t3v capture NAMEwritesNAME.txt, the screen as plain text you can diff, andNAME.ans, the same with colors.t3v render NAME NAME.pngdraws a capture in Menlo at twice the size. Look at the PNG with the Read tool before you describe it. It refuses to write inside the repository.raw.ansiholds every byte t3term wrote, andsession.castis an asciicast recording of the run.events.jsonllogs each request, answer, key and click the run saw, with tokens, tickets and keys written as<redacted>.summary.jsonis written atstop: sessions issued and revoked, sockets open at stop, requests by method.t3v status --jsongives the same counters while the run is live.rpcRequestsByTagcounts requests by method, which shows a change that makes t3term ask for more while idle.
What counts as proof:
- A claim about the screen cites a
.txtcapture. A claim about colors or layout also cites a rendered PNG you looked at. - A bug shows twice on the build that has it, and is gone twice on the build that fixes it, with the same scenario, size and steps.
t3v checkpasses at the end of the drive, both before and afterstop. A broken rule fails the drive even when the screen looked right.- Every project, thread, URL and line of text the scenarios serve is invented, so captures and PNGs are safe to post on a pull request.
Check
t3v check exits 1 with one line per broken rule. While the run is live: no request without its scope (scopeViolations), no RPC method the server doesn't know (unknownRpcs), no text that must stay hidden on screen (screenLeaks) or in raw.ansi (controlLeaks), no token, ticket or control key the server issued on screen or in any file of the evidence directory (secretLeaks, counts by file, never the values), and no bug in the fake server itself (fixtureErrors; fixture.error in events.jsonl names the exception and its line). A fixture bug fails the run because on screen it would only look like t3term reconnecting. After stop: every session t3term issued was revoked and every socket closed, and the server's last search for its secrets, in summary.json, found none. The server refuses every command that comes through orchestration.dispatchCommand, and status counts them as commandsRefused, which doesn't fail check. In links, a t3term launch payload without a field T3 requires fails check (launchesUnreadable).
Cleanup
t3v stopquits t3term with Ctrl+Q, stops the run's tmux server and the fake server, and keeps everything.t3v checknow holds the run to the after-stop rules. Run it before cleanup, which deletes what it reads.t3v cleanupstops the run if it is still up, deletes the run directory and prints the evidence path. The evidence stays.
Clean up failed attempts too. ls -d /tmp/t3v-run-* lists runs still on disk. Remove each of yours with t3v cleanup --run DIR, never by killing processes by name.
Helpers
t3v envprints theenv -iline that runs t3term against the run. Add a subcommand to drive the CLI:eval "$($T3V env) --json threads". Each CLI command issues and revokes its own session, andcheckcovers them. The line has its ownt3, so the server knows those sessions are the CLI's andt3v expectkeeps describing the TUI.checksearches only the evidence directory for leaked tokens, so redirect CLI output into the evidence directorystartprinted to have it searched. smoke keeps every recipe's output in itssmoke.logthere.t3v build REV [--repo PATH]prints the path of a cached build of any commit. It builds one commit at a time and checks out the commit only once it holds the build lock, because cargo would take a tree checked out during another build for one already built. It stops without caching anything when cargo's JSON messages call the t3term binary fresh, which means cargo didn't build it.- run.json's
binaryrecords the build'scommitand itsuuid, the LC_UUID ld makes from a hash of the linked image. Builds of the same sources in one target directory share a uuid. Two commits with different sources and one uuid are a clue, not proof, that one binary is a copy of the other: a change that doesn't reach the binary, such as one to a test, keeps the uuid. Check whether the changed files are compiled into it. t3v statussummarizes the event log, the live server and the screen.t3v mark NAMEnotes whereraw.ansiends, andstatusthen reportscontrolLeaksSinceMark.t3v restartkeeps the run's HOME, so it tests what t3term saved in~/.config/t3term/settings.json.t3v start --no-tmuxstarts only the server and prints the line that runs t3term in a terminal of your own.
Gotchas
/tmp/t3v-currentnames the newest run on the machine, which may be another agent's. With two runs, pass--runorT3V_RUNevery time.- t3term saves the sidebar's width, whether it is hidden, the shelves you opened or closed and verbose mode in the run's HOME. They last across
restart. A new run starts from defaults. - The sidebar's width moves where the thread details stop being a pane and become a dialog: at 131 columns or more with the default sidebar, 99 or more with it hidden. A drive that dragged the sidebar changes what later steps see.
- tmux sends
M-xas Esc followed by x, which t3term reads as Alt+X. Option on a Mac keyboard does the same only when the terminal is set to send Esc. - The server refuses every command that comes through
orchestration.dispatchCommand, so sending a message, answering an approval or a question, interrupting and changing a mode show t3term's refusal path.features/README.mdlists what that leaves unreachable.linksservesorchestration.launchThread, sot3term launchworks there and adds its thread to the run's world. - t3term can split a string across writes, so
controlLeaksoverraw.ansimay miss text thatscreenLeakscatches on screen. - PNGs differ slightly between machines in how fonts are smoothed. Compare
.txtcaptures, not PNG bytes. - Every account can write to /tmp, so t3v uses a run directory, an evidence directory, /tmp/t3v-bin and /tmp/t3v-target only when this account owns it and no other account can write to it or to a directory on the way. If one belongs to another account, t3v stops and names it. Remove it, or pass
--evidencesomewhere of yours.
Keeping this skill true
When a drive shows the map is wrong, fix the feature file in the same pull request as the change that made it wrong. smoke runs every recipe, so a recipe the app no longer matches fails there. The maintain-verify-t3term skill checks this file and features/ against the app between changes.
Adapted from the create-verification-skill pattern in Cursor's pstack plugin (github.com/cursor/plugins, MIT) for this repo.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

