verification-gates
The verification tiers and gate commands for Struq Voice. Use whenever finishing a slice, before a commit, or when asked to 'verify', 'run the gates', 'typecheck', 'lint', 'run tests', 'which tier is
Files1
View on GitHubSKILL.md.claude/skills/verification-gates/SKILL.md
GitHubVerification gates for Struq Voice
Pick the lowest tier that fully covers the change, then run it. The tier also decides who may merge, which is why guessing low is not a shortcut.
The tiers
| Tier | Scope | Proof | Merges |
|---|---|---|---|
| T1 | docs, copy, screenshots, comments | pnpm typecheck && pnpm lint |
agent, once CI is green |
| T2 | renderer views, state machines, IPC, engines, post-processing | T1 + pnpm test |
Roy |
| T3 | native modules, updater, paste, meeting worker, release scripts | T2 + pnpm smoke:boot, and say what was observed |
Roy |
Four rules that fall out of the table:
- A branch takes the highest tier it touches. One line in
paste.tsmakes the whole branch T3. - A change to a skill, to
AGENTS.md, or to this table is never T1. It changes how everything after it runs. - Tier up when unsure. The cost of a tier too high is two minutes.
- Report what was run AND what was deliberately not run. A gate nobody can see the output of is a claim, not a check.
The gates
pnpm typecheck # tsc --noEmit -p tsconfig.node.json && web && e2e
pnpm lint # eslint . (typescript-eslint strictTypeChecked)
pnpm test # vitest run (risk-weighted unit suite)
Zero errors. There is no softer bar within a tier.
Boot smoke (T3)
pnpm smoke:boot
The script expects the production bundle to stay alive for ten seconds. It
uses isolated user data, hides the window, kills only its own process tree, and
removes its temporary files. Electron 39 for Windows does not accept a
--headless command-line switch.
This is the gate that catches a broken quit or a native module that fails to load, neither of which any unit test sees.
e2e: a deliberate probe, never automatic
pnpm test:e2e builds and runs Playwright. It is headless but slow, and
hook.spec.ts needs a real microphone and real OS focus, so it fails in
isolation on machines without a mic or with hook contention.
Do not run it unprompted, at any tier. Roy runs it himself. Do not "fix" the e2e specs without being asked. A gate that flakes is a gate that gets switched off, which is why this one stays opt-in rather than being wired into T3.
Cleanup
Always kill stray processes after any smoke or manual launch:
taskkill //F //IM electron.exe
taskkill //F //IM "Struq Voice.exe"
Lint pitfalls that come up often
- No async functions without an await (add
await Promise.resolve()in fakes). - No returning a void expression from an arrow shorthand; wrap in braces.
- Template literals with numbers need
String(n). - No unnecessary type assertions or conditionals the checker can prove.
- No unused imports or variables (names must match
/^_/to be exempt). noUncheckedIndexedAccess: index arrays only after a bounds check.exactOptionalPropertyTypes: do not set optional fields toundefined.