verify
Run the appropriate verification chain for recent changes. Detects what changed and runs build, tests, smoke checks, and visual UI verification. Use when the user says "verify", "check this", "does it
SKILL.md.claude/skills/verify/SKILL.md
GitHubVerify
Two modes: change verification (what did I break?) and feature verification (does X work?). Detect which mode from context, run the right checks, visually verify UI when applicable. Report clearly.
- Change verification — "verify", "check this", "does it work" after making changes. Detect what changed (Step 1), run matching chains.
- Feature verification — "verify wikilinks work", "test the editor on iOS". Skip diff detection; pick the suites and UI flows that cover the feature, run those, and do a hands-on UI check (Step 3) if user-facing.
The per-platform UI playbooks live in references/ next to this file — read
the one you need before driving that platform:
| Platform | When | Playbook |
|---|---|---|
| Any running debug app | acting on it and reading its state — use the puppet skill, it is the only sanctioned driver |
.claude/skills/puppet/ |
| Web dev server | pure CSS / markdown / editor decorations, no Tauri APIs | references/desktop.md |
| Tauri desktop | default for desktop features; anything touching Rust/invoke()/platform APIs |
references/desktop.md |
| iOS simulator (native SwiftUI) | apps/ios changes, iOS-specific behavior, "on the simulator" |
references/ios.md |
| Android emulator (native Compose) | apps/android changes, Android-specific behavior (IME, status bar) |
references/android.md |
| Windows qemu VM (WebView2) | #[cfg(windows)], native DnD, NSIS installer, clean-machine launch |
references/windows-vm.md |
Mobile is native, not Tauri: the shipping mobile apps are the SwiftUI and
Compose shells on the shared Rust core. Neither has the MCP bridge — the
bridge is desktop-only. Every platform is driven with just puppet (the puppet skill);
simctl and adb install and launch the phone apps.
Platform matrix (uname -s): macOS = iOS + Android + desktop; Linux =
Android + desktop + the Windows qemu VM (no iOS). The playbooks note the few
OS-specific bits (Wayland env vars, SDK paths).
Session setup, evidence, and teardown
Read references/session.md before app QA. It owns worktree/device isolation,
provisioning, story verdicts, evidence reuse, sync smoke, and cleanup. Read only the
platform playbook needed for the target. Defect capture lives in references/evidence.md.
Step 1: Detect what changed (change verification mode only)
just orient --json
Use changes.all: it includes every commit since the merge base with
origin/main, staged and unstaged changes, deletions, and untracked files.
If the user names another branch, tag or commit, pass it with --base <ref>.
The base is local: fetch it first when freshness matters. A missing base is an
error; do not silently substitute the last commit. JSON preserves filenames
with spaces or newlines. Inspect existing files from that list, retaining
removed paths when selecting the affected owners.
Record head, baseSha, mergeBase and whether local changes are included.
Read each changed owner's nearest AGENTS.md before choosing its chain.
If nothing changed, report the comparison scope and stop. Categorize into ALL matching
categories — a change can match several; run every matching chain:
| Pattern | Category |
|---|---|
src/**/*.svelte, src/**/*.ts (not tests) |
frontend — if files import @tauri-apps/*/invoke/rustCore, also tauri-dependent; if editor-related, the same code ships inside the native apps' embedded editor |
src/**/*.css, src/styles/** |
styles |
src/**/*.test.ts, src/**/*.spec.ts |
unit-tests |
packages/editor/src/{filename,tags,preview,images}.ts |
shared — the conformance-locked TS↔Rust note rules |
packages/editor/** |
editor — feeds desktop AND the native shells' embedded editor.html; toolbar manifest changes also regenerate native toolbar specs |
crates/** |
rust-core — consumed by the Tauri app and (via futo-notes-ffi) both native shells |
apps/tauri/src-tauri/** |
tauri-rust |
apps/ios/** |
ios-native |
apps/android/** |
android-native |
tests/** |
playwright-tests |
docs/spec/** |
spec |
.gitlab-ci.yml, ci/** |
ci |
scripts/**, justfile, agent instructions |
tooling — co-located script tests and the instruction/safety gates |
Step 2: Run verification chains
Use the nearest AGENTS.md and root §7 to select the owning layer's complete chain;
justfile owns the commands. For shared code before merge, run just check;
use just prepush for broad or risky changes. Report any unavailable platform leg.
While iterating, run only the tests you name (just test-unit <file>, cargo test -p <crate> <filter>, just test-ios-native <Suite>, just test-android-native <filter>); run the complete
chain once, before the commit.
For documentation/tooling-only edits, run just check-agent-docs and
just check-qa-input-safety, plus tests for any changed executable tooling.
Spec edits also require the workflow in docs/spec/AGENTS.md. CI changes need a
real pipeline on the changed head with the affected jobs actually executed.
Do not pipe checks through head or tail without pipefail; use the recipes so
failed builds cannot look green. Read each check's result before continuing.
shared — note-rule conformance
Both consumers and the differential lock are required:
just test-unit packages/editor
just test-rust
packages/editor/AGENTS.md owns canonical Rust, the hot-path TS mirror, and reviewed
fixture updates; tests/conformance/README.md explains the differential.
Step 3: UI Verification
When changes affect anything user-facing — or when verifying a user-facing
feature — see it running in the actual app. Pick the platform from the table
at the top and read its playbook in references/ first. Skip only for
test-only or CI-only changes.
"Invisible" behavior still needs verification. Fire-and-forget calls, warmups, prefetches — no visible UI change means you verify via a different signal (console/bridge logs, server logs, files on disk), not that you skip. For server-side changes, launch the app and confirm the client actually hits the new endpoint.
mkdir -p ./test-screenshots
Name screenshots descriptively: web-editor-heading-decoration.png,
android-dark-theme-sidebar.png, ios-swipe-actions.png.
Choosing web vs Tauri (desktop changes)
The web dev server stubs ALL Tauri commands. Check whether changed frontend files depend on Tauri APIs:
grep -rl 'invoke\|@tauri-apps\|rustCore' $(git diff --name-only "$(git merge-base HEAD origin/main)" 2>/dev/null) 2>/dev/null | grep -E '\.(ts|svelte)$' | sort -u
Any match → Tauri. No match and purely CSS/markdown/editor decorations → web is acceptable. Unsure → Tauri.
Editor changes ship to three apps
The embedded editor (editor.html, built from the shared editor code) runs
on desktop AND inside both native shells. For editor-visible changes, verify
on desktop first (fast), then spot-check one native shell — just ios-native
/ just android-native rebuild the editor bundle automatically.
Handling loading/async states in screenshots
A screenshot of a spinner is not verification. If a loading state is visible: wait for the ready condition with a bounded timeout, then capture the final render. If it never becomes ready, report as STUCK and investigate via the platform's log channel. Every verified feature needs at least one screenshot of the actual rendered UI.
Features that require app restart
Crash-dialog-on-relaunch, preference recovery, scan-on-launch behaviors only manifest after a restart: trigger the precondition → kill/terminate the app (platform playbook has the command) → relaunch → verify. Don't skip these; they break most often.
Features that need a sync server
Follow references/session.md for the isolated server and cross-client smoke.
For client sync-stack changes, prefer just test-cross-platform, which starts its
own clients and server. A cold offline package cache can block server provisioning;
report that explicitly. The desktop driver API is in references/desktop.md.
What to look for
- Editor: open a note, type, verify decorations/widgets render
- Theme/styles: toggle light/dark (
just sim-appearance darkon iOS), check contrast, no broken layouts - Navigation: sidebar/list, note switching, back, search
- Settings: toggle options, verify they persist after reload/relaunch
- Hidden affordances: before calling something missing, check context
menus, long-press, swipe actions (
custom_actionsin the iOS a11y tree), overflow menus — seedocs/spec/AGENTS.md - New features: happy path + one edge case
- Invisible behavior: open the surface, then confirm via logs/disk that the call actually fired
Step 4: Report
Record checkout path, host/platform, comparison base, tested SHA and any dirty changes, then summarize commands, exit codes and observed behavior. Link the logs/screenshots/traces and name the verified desktop target or claimed device. For persistence behavior, record a flush/reopen and disk readback as appropriate. If code or installed artifacts changed after a check, rerun the affected check; do not carry its previous PASS forward. Include the next action for any gap. Do not commit private conversation excerpts or real-vault content as evidence.
| Check | Result | Notes |
|------------------|--------|------------------------------------|
| TypeScript | PASS | |
| Build | PASS | |
| Unit tests | PASS | 42/42 |
| Rust core | PASS | conformance green |
| iOS build | PASS | build-ios-native clean |
| Android tests | SKIP | no Android changes |
| UI (desktop) | PASS | 3 screenshots in test-screenshots/ |
| UI (iOS sim) | PASS | content verified on disk |
If anything failed, show the relevant error output and suggest a fix. List screenshots/recordings taken with a one-line description of what each shows. Distinguish Blocked (environment can't exercise it — say why) from FAIL (observed wrong behavior).