agentleFS
gannonhgannonh/kata-code/.agents/skills/verify-katacode/SKILL.md

verify-katacode

Skill1 starsChanged 39 days agoMITView on GitHub

Drive the Kata Code web app on an isolated local stack, pair a browser, click through chat, settings, and usage, and capture screenshots plus accessibility snapshots. Use when proving a user-facing web change, running UAT, or checking that the app still boots and pairs.

  • Reads credentials
  • Installs packages

The file

Show its rules and text

We split this skill into the rules it gives an agent

13 rules. About 0 are common, 4 are distinctive, and 9 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈4

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 9

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    After pairing, a first-run client opens /welcome and the Connect / Agents / Projects wizard.

    line 41
  2. Very specific:

    If node_modules is missing, run vp i and launch again.

    line 45
  3. Distinctive:

    Run this first whenever anything looks off, and before you drive:

    line 49
  4. Distinctive:

    If doctor fails, stop. Do not click around in some other Kata Code tab "to save time." Attaching to the user's session is how you consume …

    line 65
  5. Very specific:

    Use the new Pairing URL: exactly once.

    line 75
  6. Very specific:

    Use one named session for the run.

    line 81
  7. Very specific:

    Do not call internal atoms, test-only endpoints, or t3-sqlite-state.ts exec to claim a user path works.

    line 125
  8. Very specific:

    Write proof under uat-evidence/<RUN_ID>/ in the repo.

    line 131
  9. Very specific:

    Check a second view of any mutation.

    line 150
  10. Very specific:

    Do not put pairing URLs, #token= fragments, or Token: lines in evidence.

    line 151
  11. Distinctive:

    When the proof is captured, or as soon as a launch/drive attempt fails:

    line 158
  12. Very specific:

    Do not remove ~/.katacode or the worktree .katacode.

    line 168
  13. Distinctive:

    All three are executable. Run them from the repository root.

    line 174

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Verify Kata Code (web)

This skill is for a cold agent that has never seen the app. It launches a disposable Kata Code web stack, pairs a browser the way a user does, drives one mapped feature, and leaves proof on disk.

The primary surface is the web client at apps/web, served with the local server. Desktop wraps the same routes in Electron. Mobile is a separate React Native app. Marketing is a separate site. Do not claim those other clients are verified because the web app worked.

Iterative human testing against a kept-alive worktree stack is the sibling test-t3-app skill. This skill is stricter: dedicated --home-dir, never attach to a stack you did not start, always capture evidence, always tear down what you started.

Launch

From the repository root, load the printed values into the current shell:

eval "$(.agents/skills/verify-katacode/bin/launch)"

launch writes progress on stderr and only KEY=value lines on stdout, so eval exports RUN_ID, RUNNER_PID, HOME_DIR, VERIFY_ROOT, WEB_ORIGIN, SERVER_PORT, WEB_PORT, EVIDENCE_DIR, ENV_FILE, and PAIRING_URL. The same exports are also written to $ENV_FILE (${TMPDIR:-/tmp}/katacode-verify-<RUN_ID>/run.env) for a later source "$ENV_FILE".

That starts node scripts/dev-runner.ts dev --home-dir <disposable> with no --browser, no --share, and with VITE_HTTP_URL / VITE_WS_URL unset. Dev is single-origin. Vite proxies /api, /oauth, /.well-known, and /ws. Baking a localhost origin into the bundle breaks every remote browser.

Ready means all of these are true:

  • the shell has RUN_ID, WEB_ORIGIN, HOME_DIR, and PAIRING_URL set (from eval or source "$ENV_FILE")
  • the log contains a [dev-runner] ... serverPort=... webPort=... baseDir=... line whose baseDir is the disposable home
  • the server log contains a /pair#token=... URL (Effect logs it as pairingUrl:; katacode pair prints Pairing URL:)
  • GET $WEB_ORIGIN/.well-known/kata/environment returns JSON with environmentId, label, and serverVersion

Ports hash from the worktree path and move when occupied. A developer vp run dev may already be sitting on this worktree's default ports. Read the values launch printed. Do not assume 5733 / 13773. Vite often binds IPv6-only ([::1]). bin/launch and bin/doctor probe ::1, then 127.0.0.1, then localhost, and print the host that actually returned descriptor JSON as WEB_ORIGIN. Pairing URLs still print localhost. On IPv4-only localhost that hangs; open PAIRING_OPEN_URL="${WEB_ORIGIN}/pair#${PAIRING_URL#*#}", not $PAIRING_URL. Do not assume 5733 / 13773.

The disposable home is ${TMPDIR:-/tmp}/katacode-verify-<RUN_ID>/home. Runtime state is <home>/userdata. Never launch against ~/.katacode or the worktree .katacode. Those are the user's live (or worktree) databases.

bin/launch starts the runner in a new process session and then exits. That is intentional: the stack must outlive the helper. bin/cleanup is what stops it, using the pid in run.json.

bin/launch starts the runner with --no-auto-bootstrap-project-from-cwd and KATACODE_AUTO_BOOTSTRAP_PROJECT_FROM_CWD=0. Web mode otherwise creates a project from the worktree cwd (often titled server). A fresh launched home must have no auto-created project after onboarding.

After pairing, a first-run client opens /welcome and the Connect / Agents / Projects wizard. Finish that wizard without importing projects, then you should see the empty landing, not a thread in this repo.

Teardown is bin/cleanup. It is required after a failed launch too, so a half-started runner is not left holding ports.

If node_modules is missing, run vp i and launch again. bin/launch puts $REPO_ROOT/node_modules/.bin on PATH so vp is found without a global install. Do not invent a different start command.

Doctor

Run this first whenever anything looks off, and before you drive:

.agents/skills/verify-katacode/bin/doctor

Pass the run id if .last-run is stale: bin/doctor web-20260826-163000-a1b2c3d4.

Doctor is read-only. It answers "is this instance worth driving?" by checking:

  • the runner pid from run.json is alive and its start identity matches runnerStart
  • server-runtime.json pid is alive and in that runner's process tree
  • the recorded web and server ports are listened to by processes under that runner
  • the home dir is the disposable one, not ~/.katacode or the worktree .katacode
  • the web origin serves the environment descriptor and the app shell

If doctor fails, stop. Do not click around in some other Kata Code tab "to save time." Attaching to the user's session is how you consume their pairing token, write into their database, or kill the wrong pid.

On Linux, Vite often binds IPv6-only ([::1]). localhost then times out on IPv4. bin/doctor probes ::1 first. If the descriptor probe fails in the first second after launch, run doctor once more against the same run id before treating the instance as dead.

A consumed pairing token is not a doctor failure. Mint a replacement against the same home:

node apps/server/src/bin.ts pair --base-dir "$HOME_DIR"

Use the new Pairing URL: exactly once. Tokens from pair have standard client scopes. The startup URL has admin scopes, which you need for Settings → Connections. For usage, settings general/appearance, the empty landing, and the command palette, a replacement token is enough.

Drive

Install the browser CLI if needed (npm i -g agent-browser && agent-browser install), then load its current command list with agent-browser skills get core so you match the installed version. Before the first open, run agent-browser --version and require 0.37.1 or later if you will record video. Recording was tested on 0.37.1 and 0.38.1; 0.37.0 was not tested. Otherwise run agent-browser upgrade (listed in --help, not exercised in KAT-3652) and check the version again. Do it before you pair: when the CLI version changes, the next command restarts the browser and the paired tab is gone. Also require the Recording checks in agent-browser doctor to pass (ffmpeg with libvpx and libx264). If --version errors, the active node may have no agent-browser install, as happens under mise. Run npm i -g agent-browser, or put the node version that has it first on PATH (mise ls node lists them), and check again. If the version still stays below 0.37.1, plan on the screenshot fallback under Evidence.

Use one named session for the run. Do not pass --session-name (that persists cookies under ~/.agent-browser).

PAIRING_OPEN_URL="${WEB_ORIGIN}/pair#${PAIRING_URL#*#}"
agent-browser --session katacode-verify open "$PAIRING_OPEN_URL"
agent-browser --session katacode-verify set viewport 1400 900
agent-browser --session katacode-verify wait --text "Connect your computers"
agent-browser --session katacode-verify snapshot -i
# If `/pair` was already mounted (token-free before-shot), fill Pairing token and Continue instead.

$PAIRING_URL is the value from eval "$(bin/launch)" or source "$ENV_FILE". It ends in /pair#token=... and still uses localhost. $PAIRING_OPEN_URL keeps that fragment on $WEB_ORIGIN (so [::1] when Vite is IPv6-only). Open $PAIRING_OPEN_URL exactly once as the first navigation that consumes the token (after any token-free $WEB_ORIGIN/ before-shot in the pairing recipe). Opening it twice, or opening it in a second browser, burns the token. If the before-shot already mounted /pair, hash auto-submit will not run; use the form or reload.

After pairing, the app strips the token from the URL and redirects to /. FirstRunGate then replace-navigates to /welcome. Wait until you see the setup dialog Set up Kata Code and heading Connect your computers. Do not treat What should we work on? as pairing proof yet. /welcome paints that empty hero behind the wizard.

Finish the wizard without importing:

  1. Connect. The local computer row shows Connected. Click Continue. Do not open Add a computer.
  2. Agents. Heading Connect your agents. Click Continue. Do not require an agent to be Ready.
  3. Projects. Heading Your projects or Choose your projects. Click Do not import projects.

Then wait until the setup dialog is gone and the URL is /. A correct launch then shows:

  • the text What should we work on? (a styled div, not a heading role) and a button Add project

A draft at /draft/... after that skip means a project already exists (cwd auto-bootstrap or an import). That is a launch failure for this skill, not a pairing failure.

Then follow the matching file under features/. Prefer ARIA names, data-testid, and route paths over coordinates. wait --text matches visible text only: icon buttons such as Usage and Refresh usage are named by aria-label, so wait on a heading or use find role button --name.

Stable handles in this app:

WhatHandle
Pairing formheading Pair with this environment, textbox Pairing token, buttons Continue and Reload app
Welcome wizarddialog Set up Kata Code, progress Setup progress, steps Connect / Agents / Projects, headings Connect your computers / Connect your agents / Your projects or Choose your projects, skip-import button Do not import projects
Empty landingtext What should we work on? (no heading role), button Add project
Add projectdialog Command palette, group Sources listing New project (preselected), Local folder, Git URL, then provider repositories
Draft composertextbox Message
Sidebar settingsbutton Settings
Sidebar usagebutton Usage
Command palettedata-testid="command-palette" with data-palette-mode="command", name Command palette, shortcut mod+k (⌘K on macOS, Ctrl+K elsewhere). The same testid serves File picker (files) and Search project contents (content)
Usage pageheading Usage, nav Usage breadcrumb, menu All environments, groups Usage metric (Cost / Tokens / Limits) / Usage period / Usage breakdown, button Refresh usage or Refresh limits
Settingsbreadcrumb Settings breadcrumb, combobox Search settings, environment-scope nav labels General, Appearance, Keybindings, SnapShots, Providers, Integrations, Source Control, Connections, Archive; project or checkout scope also shows Project

Do not call internal atoms, test-only endpoints, or t3-sqlite-state.ts exec to claim a user path works. SQLite inspection is a side-effect check after a real UI action, and only against the disposable home.

Provider CLIs (Codex, Claude, Cursor, Grok, OpenCode) live on the server machine. The server detects them from its user's environment, so they can show Ready even in an isolated home. You can prove pairing, navigation, settings, usage chrome, and adding a project without a provider. Do not send a message unless your brief allows a live provider call, because each reply costs money. Record that skip.

Evidence

Write proof under uat-evidence/<RUN_ID>/ in the repo. Cleanup must not delete that directory.

Minimum for a pass:

  • logs/launch.txt from bin/launch (already redacted)
  • one screenshot before the action and one after, or a snapshot pair that shows the same delta
  • an accessibility snapshot of the resulting screen (agent-browser --session katacode-verify snapshot) saved under snapshots/
  • evidence.json naming the feature id, entry point used, WEB_ORIGIN (no token), and the observable end state

To record video, use agent-browser 0.37.1 or later (see Drive). record start then attaches to the active tab. It opens no new tab and does not navigate, so the pairing, the viewport, and an unsent composer draft all survive. It writes 30 fps and holds the latest frame between repaints, so idle time is kept and the video length is close to wall-clock time. In KAT-3652, a flow of 10 idle seconds, a page navigation, and 3 more seconds recorded as 13.4 s on 0.37.1 and 13.1 s on 0.38.1, and both kept the frames from before the navigation. Pair first, then run agent-browser --session katacode-verify record start <path.webm> on the paired tab, so the token never enters the video. Run agent-browser --session katacode-verify record stop --json right after the captured action and before you upload the file. Stop finalizes the WebM, and an active recording is not a complete file. Require capturedFrames above 1 in its output; 1 means Chrome sent a single frame and the video is a still.

On 0.36.0, the one older release tested, record start opened a second tab and recorded that one. The new tab reloaded the current URL, and Kata Code opened a new draft there with an empty composer. The first tab kept the unsent draft. Use the screenshot fallback below the floor, when agent-browser doctor fails its Recording checks, or when record stop --json reports capturedFrames of 1 for a flow that changed the screen. Pair first, then capture one agent-browser screenshot per second while you drive the flow. Start the loop only after pairing succeeds, and never capture the pairing form or any token entry, because every frame goes into the uploaded video. Then assemble the frames with ffmpeg -framerate 1 -i frames/%03d.png -c:v libx264 -pix_fmt yuv420p -r 25 out.mp4. Say in the PR comment that the video is a 1 fps capture. Check the frames before you upload.

For a change with an open pull request, attach at least one representative UI screenshot or video from the run to the PR conversation before calling the change verified. First require gh pr comment --help to list --attach; this binary upload requires GitHub CLI 2.101.0 or later. When available, run gh pr comment <number> --body "UI evidence for <feature>." --attach "$EVIDENCE_DIR/screenshots/<file>.png#<observable state>". Post one attachment per comment. In KAT-3532, a comment with several --attach files posted after the first upload, and each later upload then failed with a TLS error. Otherwise use an authenticated GitHub integration or the authenticated PR page that accepts binary uploads. If none is available, report the PR evidence upload as blocked and do not call the change verified. A local filesystem path, an inaccessible file:// link, or a text-only claim does not count. Fetch the PR comment after uploading and require a rendered attachment URL, then record the PR URL, comment URL, and attached evidence filenames in evidence.json. Keep the original files under uat-evidence/<RUN_ID>/ after upload. If the verification has no PR, keep the evidence locally and record pullRequest: null; do not create an empty PR only to host evidence.

Proof standards:

  • Exercise the real user path. Pairing in the browser counts. Posting to /api/auth/browser-session yourself does not.
  • Capture the action and the resulting state, not only the final screen.
  • Check a second view of any mutation. Adding a project means the landing heading is gone and the sidebar or composer shows that project. Toggling usage to Tokens means the big number is a token count, not $0.00.
  • Do not put pairing URLs, #token= fragments, or Token: lines in evidence. bin/launch already redacts logs/launch.txt. If you copy more log, pipe it through the same sed in bin/lib.sh (redact_secrets).
  • Mocks are not allowed for pairing, routing, or settings persistence. The only acceptable gap is an external provider CLI that is not installed; say so and stop at the UI that names the missing provider.

uat-evidence/ is gitignored. Leave it on disk so a human can open the screenshots. Do not commit it.

Cleanup

When the proof is captured, or as soon as a launch/drive attempt fails:

.agents/skills/verify-katacode/bin/cleanup

That verifies the runner pid in run.json still has the recorded runnerStart identity, then kills that pid and its descendants, then deletes ${TMPDIR:-/tmp}/katacode-verify-<RUN_ID>/. If the pid is gone or the identity no longer matches, cleanup skips signals and only removes this run's state. It does not delete uat-evidence/<RUN_ID>/.

Never pkill -f, never pgrep | kill, never kill a pid you found by matching katacode or the worktree path. This machine runs other Kata Code servers. The worktree you are in may already have a vp run dev on different ports. Kill only the pid launch printed, and only after the start identity matches.

Do not remove ~/.katacode or the worktree .katacode.

Close the katacode-verify agent-browser session after cleanup so the next run does not reuse cookies against a dead origin.

Helpers

All three are executable. Run them from the repository root.

eval "$(.agents/skills/verify-katacode/bin/launch)"
.agents/skills/verify-katacode/bin/doctor
.agents/skills/verify-katacode/bin/doctor web-20260826-163000-a1b2c3d4
.agents/skills/verify-katacode/bin/cleanup
# later shell: source "$ENV_FILE"

launch writes uat-evidence/<RUN_ID>/, ${TMPDIR:-/tmp}/katacode-verify-<RUN_ID>/run.json, and run.env. doctor and cleanup read .agents/skills/verify-katacode/.last-run when you omit the id. Run ids are web-<UTC>-<8 hex> and must stay filename-safe (no / or ..).

Feature recipes live in features/. Drive from that map. A proof that uses one convenient entry point is incomplete when the map lists others; report the ones you did not reach rather than silently skipping them.

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.