/verify-changes
How to test and verify work in the dev-3.0 repo — which vitest config covers what, how to write a test that fits the house style, mocking Electrobun RPC and i18n providers, what coverage is actually expected, and the browser QA hand-off. Use when writing or fixing tests,
$ npx -y skills add h0x91b/dev-3.0 --skill verify-changes --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/verify-changes
Context preview
The summary Claude sees to decide when to auto-load this skill.
How to test and verify work in the dev-3.0 repo — which vitest config covers what, how to write a test that fits the house style, mocking Electrobun RPC and i18n providers, what coverage is actually expected, and the browser QA hand-off. Use when writing or fixing tests,
SKILL.md
verify-changes.SKILL.mdname: verify-changes
description: How to test and verify work in the dev-3.0 repo — which vitest config covers what, how to write a test that fits the house style, mocking Electrobun RPC and i18n providers, what coverage is actually expected, and the browser QA hand-off. Use when writing or fixing tests, deciding what a change needs covered, hitting a failing or flaky suite, or preparing a change for review. Triggers — "write tests for this", "which config runs this", "how do I mock the RPC", "is this covered enough", "the suite is failing".
verify-changes — testing and verification in dev-3.0
The two hard gates (lint + touched tests before push, full suite before a PR) live in `AGENTS.md` and apply whether or not you read this file. Everything here is the detail behind them: which runner covers what, how to write a test that fits, and what "enough" means.
Which config runs what
**Vitest** with `happy-dom` and React Testing Library. Three configs, three independent processes:
| Config | Covers | Script | |---|---|---| | `vitest.config.ts` | renderer (`src/mainview/`) | part of `bun run test` | | `vitest.config.bun.ts` | backend (`src/bun/`) | `bun run test:bun` | | `vitest.config.cli.ts` | CLI (`src/cli/`) | `bun run test:cli` |
bun run test # mainview + bun + cli in parallel, minus 3 slow e2e files (~6s)
bun run test:full # everything incl. slow e2e (~42s) — CI/PR only
bun run test:watch # watch mode
Running vitest directly (outside `bun run`): `bunx vitest run`, never `npx`.
Narrowing while iterating: `bunx vitest run <path>`, `-t "<test name>"`, `--repeat <n>` for suspected flakes. Because the three configs are separate processes, a failure in one says nothing about the others — read which prefix (`[mainview]` / `[bun]` / `[cli]`) failed.
**Local E2E policy:** do not run the full E2E suite locally — `bun run test:full` and any equivalent unfiltered command are reserved for CI/PR validation. Investigating a specific behavior means running that one E2E file or test case.
What to test
- **Unit (mandatory)** — state reducer actions and their edge cases, all pure
functions/utils/parsers, every RPC handler (happy path plus 2-3 error cases), CLI commands (parsing, validation, output), data-layer CRUD plus corrupt-data handling, git operations with mocked spawn, i18n interpolation and pluralization for every locale.
- **Component (mandatory)** — every major interactive component: board views, task cards,
modals, settings panels.
- **E2E (CLI-based)** — full lifecycle through the CLI plus Unix socket against a real app
process in a tmpdir: task lifecycle (create → move statuses → complete), project CRUD, worktree creation and cleanup, notes CRUD, CLI context auto-detection, concurrent writes with no data corruption.
House style
- One logical assertion per test; no dependencies between tests.
- Always `userEvent`, never `fireEvent`. Test behavior, not implementation.
- Mock only external boundaries — git, tmux, fs, Electrobun. Never mock internal modules.
- No `sleep` and no timer juggling; use proper `async`/`await`.
- Tests live in `__tests__/` next to their module, e.g.
`src/mainview/components/__tests__/Dashboard.test.tsx`.
- Avoid the patterns that make a suite flake under CI load: index-based queries
(`getAllBy…[0]`), structural traversal (`.parentElement`, `.closest(…)`), and assertions wedged between two `userEvent` interactions without an intervening `waitFor`.
Mocking Electrobun RPC and providers
Components importing `api` from `rpc.ts` need the Electrobun native module mocked:
vi.mock("../../rpc", () => ({
api: { request: { listDirectory: vi.fn(), addProject: vi.fn() /* … */ } },
}));Components calling `useT()` must render inside `<I18nProvider>` (import from `../../i18n`).
Handler tests mock the `tmux` singleton the same way they mock `rpc.ts`; the tmux client's own tests inject a fake spawn instead.
A test that reads a file as data is an untyped interface
Some tests consume something as **data** rather than importing it as code: workflow YAML parsed line by line, a fixture matched by a regex, a log line asserted on, a JSON shape read by string matching. Nothing type-checks that link, so reshaping the source breaks the test with the compiler silent.
The failure is worse than silent — it is misleading. When the test finally fails it describes the **invariant** ("every Bun-pinning workflow would ship an unproven pin"), not the missing source, so it reads as a real regression and someone spends an hour proving it is not.
Two rules:
- **Before deleting or reshaping anything, grep who reads it** — not just who imports it. A YAML
key, a fixture line shape, a log string.
- **If you write one of these, make its failure message name the cause and the fix** — "the
pinned string moved; update it in this test" — never just restate the invariant.
Bug fixing — reproduce first
Write the failing test that reproduces the bug (red), then fix until it passes (green), and commit test and fix together. The rare exception is a bug that genuinely cannot be reproduced in a test (OS-specific timing, hardware, unmockable third-party behavior) — default to writing the test.
For a suspected flake, reproduce **under load** before theorising: `--repeat`, several concurrent vitest processes, and the file run together with its neighbours rather than alone (cross-test leakage vanishes in isolation). Never "fix" a flake with `retry`, `skip`, or a bumped timeout on its own.
Coverage — expectations, not a gate
Two numbers, no per-metric split: **~70% for normal code, ~85% for critical modules.**
- **Critical modules** (a silent regression here is expensive): `state.ts`,
`src/shared/types.ts` helpers, `src/mainview/i18n/`, `src/cli/`, `src/bun/data.ts`, `src/bun/git.ts`, `src/bun/tmux/`, `src/mainview/utils/`.
- **Not expected to be covered** (bootstrap/wrappers that only make sense in e2e):
Read more
name: verify-changes description: How to test and verify work in the dev-3.0 repo — which vitest config covers what, how to write a test that fits the house style, mocking Electrobun RPC and i18n providers, what coverage is actually expected, and the browser QA hand-off. Use when writing or fixing tests, deciding what a change needs covered, hitting a failing or flaky suite, or preparing a change for review. Triggers — "write tests for this", "which config runs this", "how do I mock the RPC", "is this covered enough", "the suite is failing".
verify-changes — testing and verification in dev-3.0
The two hard gates (lint + touched tests before push, full suite before a PR) live in `AGENTS.md` and apply whether or not you read this file. Everything here is the detail behind them: which runner covers what, how to write a test that fits, and what "enough" means.
Which config runs what
**Vitest** with `happy-dom` and React Testing Library. Three configs, three independent processes:
| Config | Covers | Script | |---|---|---| | `vitest.config.ts` | renderer (`src/mainview/`) | part of `bun run test` | | `vitest.config.bun.ts` | backend (`src/bun/`) | `bun run test:bun` | | `vitest.config.cli.ts` | CLI (`src/cli/`) | `bun run test:cli` |
bun run test # mainview + bun + cli in parallel, minus 3 slow e2e files (~6s) bun run test:full # everything incl. slow e2e (~42s) — CI/PR only bun run test:watch # watch mode
Running vitest directly (outside `bun run`): `bunx vitest run`, never `npx`.
Narrowing while iterating: `bunx vitest run <path>`, `-t "<test name>"`, `--repeat <n>` for suspected flakes. Because the three configs are separate processes, a failure in one says nothing about the others — read which prefix (`[mainview]` / `[bun]` / `[cli]`) failed.
**Local E2E policy:** do not run the full E2E suite locally — `bun run test:full` and any equivalent unfiltered command are reserved for CI/PR validation. Investigating a specific behavior means running that one E2E file or test case.
What to test
- **Unit (mandatory)** — state reducer actions and their edge cases, all pure
functions/utils/parsers, every RPC handler (happy path plus 2-3 error cases), CLI commands (parsing, validation, output), data-layer CRUD plus corrupt-data handling, git operations with mocked spawn, i18n interpolation and pluralization for every locale.
- **Component (mandatory)** — every major interactive component: board views, task cards,
modals, settings panels.
- **E2E (CLI-based)** — full lifecycle through the CLI plus Unix socket against a real app
process in a tmpdir: task lifecycle (create → move statuses → complete), project CRUD, worktree creation and cleanup, notes CRUD, CLI context auto-detection, concurrent writes with no data corruption.
House style
- One logical assertion per test; no dependencies between tests.
- Always `userEvent`, never `fireEvent`. Test behavior, not implementation.
- Mock only external boundaries — git, tmux, fs, Electrobun. Never mock internal modules.
- No `sleep` and no timer juggling; use proper `async`/`await`.
- Tests live in `__tests__/` next to their module, e.g.
`src/mainview/components/__tests__/Dashboard.test.tsx`.
- Avoid the patterns that make a suite flake under CI load: index-based queries
(`getAllBy…[0]`), structural traversal (`.parentElement`, `.closest(…)`), and assertions wedged between two `userEvent` interactions without an intervening `waitFor`.
Mocking Electrobun RPC and providers
Components importing `api` from `rpc.ts` need the Electrobun native module mocked:
vi.mock("../../rpc", () => ({
api: { request: { listDirectory: vi.fn(), addProject: vi.fn() /* … */ } },
}));Components calling `useT()` must render inside `<I18nProvider>` (import from `../../i18n`).
Handler tests mock the `tmux` singleton the same way they mock `rpc.ts`; the tmux client's own tests inject a fake spawn instead.
A test that reads a file as data is an untyped interface
Some tests consume something as **data** rather than importing it as code: workflow YAML parsed line by line, a fixture matched by a regex, a log line asserted on, a JSON shape read by string matching. Nothing type-checks that link, so reshaping the source breaks the test with the compiler silent.
The failure is worse than silent — it is misleading. When the test finally fails it describes the **invariant** ("every Bun-pinning workflow would ship an unproven pin"), not the missing source, so it reads as a real regression and someone spends an hour proving it is not.
Two rules:
- **Before deleting or reshaping anything, grep who reads it** — not just who imports it. A YAML
key, a fixture line shape, a log string.
- **If you write one of these, make its failure message name the cause and the fix** — "the
pinned string moved; update it in this test" — never just restate the invariant.
Bug fixing — reproduce first
Write the failing test that reproduces the bug (red), then fix until it passes (green), and commit test and fix together. The rare exception is a bug that genuinely cannot be reproduced in a test (OS-specific timing, hardware, unmockable third-party behavior) — default to writing the test.
For a suspected flake, reproduce **under load** before theorising: `--repeat`, several concurrent vitest processes, and the file run together with its neighbours rather than alone (cross-test leakage vanishes in isolation). Never "fix" a flake with `retry`, `skip`, or a bumped timeout on its own.
Coverage — expectations, not a gate
Two numbers, no per-metric split: **~70% for normal code, ~85% for critical modules.**
- **Critical modules** (a silent regression here is expensive): `state.ts`,
`src/shared/types.ts` helpers, `src/mainview/i18n/`, `src/cli/`, `src/bun/data.ts`, `src/bun/git.ts`, `src/bun/tmux/`, `src/mainview/utils/`.
- **Not expected to be covered** (bootstrap/wrappers that only make sense in e2e):
Mission control for the One Person Studio — run a fleet of AI coding agents in parallel without losing your mind. Kanban + git worktrees + tmux for Claude Code, Codex, Gemini CLI, OpenCode and any shell agent. Not an IDE.
Repo: h0x91b/dev-3.0
Other skills on dev-30.
- /debug-ui
Drive and visually QA the dev-3.0 UI in a real browser (headless Chromium via agent-browser). Use when verifying a UI/UX change, reproducing a visual bug, taking screenshots of the running app, or self-QA before review. Triggers — "check the UI", "screenshot the app", "does this
Open skill - /ux-create-manifest
Create the initial Product UX Bible for an existing web or full-screen web app by deeply auditing the repository, using sub-agents when available, and generating docs/ux manifests, schemas, budgets, surface maps, action taxonomy, and design-token policies from observed code.
Open skill - /ux-principal
Principal UX architect skill for planning UI features before implementation. Reads and maintains docs/ux manifests, classifies the feature, decides placement, navigation, action hierarchy, design-token roles, accessibility, states, responsive behavior, and produces a precise
Open skill

