From rune
Pre-commit quality gate that blocks commits on logic errors, missing error handling, regressions, or incomplete code. Auto-triggers before cook commits, on >100 LOC changes, or via /rune preflight.
npx claudepluginhub rune-kit/rune --plugin @rune/analyticsThis skill uses the workspace's default tool permissions.
<HARD-GATE>
Reviews and verifies code before merge via triage-first checks (up to 16 parallel agents). Pipeline mode verifies vs plans; general mode for PRs/branches/staged changes. Flags findings only.
Runs structured quality review before shipping code at PRs, releases, or milestones. Catches integration boundary failures where AI-generated code breaks between components.
Reviews code for bugs, bad patterns, security issues, performance problems, correctness, and untested code. Reports findings and delegates to fix, test, sentinel, or other skills.
Share bugs, ideas, or general feedback.
Pre-commit quality gate that catches "almost right" code — the kind that compiles and passes linting but has logic errors, missing error handling, or incomplete implementations. Goes beyond static analysis to check data flow, edge cases, async correctness, and regression impact. The last defense before code enters the repository.
cook before commit phasefix after applying fixes (verify fix quality)/rune preflight — manual quality checkscout (L2): find code affected by changes (dependency tracing)sentinel (L2): security sub-check on changed fileshallucination-guard (L3): verify imports and API references existtest (L2): run test suite as pre-commit checkcook (L1): before commit phase — mandatory gateLOGIC — data flow errors, edge case misses, async bugs
ERROR — missing try/catch, bare catches, unhelpful error messages
REGRESSION — untested impact zones, breaking changes to public API
COMPLETE — missing validation, missing loading states, missing tests
SECURITY — delegated to sentinel
IMPORTS — delegated to hallucination-guard
Before checking code quality, verify the code matches what was planned.
Use Bash to get the diff: git diff --cached (staged) or git diff HEAD (all changes).
Use Read to load the approved plan from the calling skill (cook passes plan context).
Check each plan phase against the diff:
| Plan says... | Diff shows... | Verdict |
|---|---|---|
| "Add function X to file Y" | Function X exists in file Y | PASS |
| "Add function X to file Y" | Function X missing | BLOCK — incomplete implementation |
| "Modify function Z" | Function Z untouched | BLOCK — planned change not applied |
| Nothing about file W | File W modified | WARN — out-of-scope change (scope creep) |
Output: List of plan-vs-diff mismatches. Any missing planned change = BLOCK. Any unplanned change = WARN.
If no plan is available (manual preflight invocation), skip Stage A and proceed to Step 1.
Use Read to load each changed file. For every modified function or method:
null, undefined, empty array, or 0 value would cause a runtime error or wrong result.async function that calls an async operation must await it. Identify missing await that would cause race conditions or unhandled promise rejections.== comparisons that could produce wrong results, string-to-number conversions without validation.Common patterns to flag:
// BAD — missing await (race condition)
async function processOrder(orderId: string) {
const order = db.orders.findById(orderId); // order is a Promise, not a value
return calculateTotal(order.items); // crashes: order.items is undefined
}
// GOOD
async function processOrder(orderId: string) {
const order = await db.orders.findById(orderId);
return calculateTotal(order.items);
}
// BAD — sequential independent I/O
const user = await fetchUser(id);
const permissions = await fetchPermissions(id); // waits unnecessarily
// GOOD — parallel
const [user, permissions] = await Promise.all([fetchUser(id), fetchPermissions(id)]);
Flag each issue with: file path, line number, category (null-deref | missing-await | off-by-one | type-coerce), and a one-line description.
For every changed file, verify:
async function has a try/catch block OR the caller explicitly handles the rejected promise.catch(e) {} or except: pass — every catch must log or rethrow with context.fetch / HTTP client call checks the response status before consuming the body.Common patterns to flag:
// BAD — swallowed exception
try {
await saveUser(data);
} catch (e) {} // silent failure, caller never knows
// BAD — leaks internals to client
app.use((err, req, res, next) => {
res.status(500).json({ error: err.stack }); // exposes stack trace
});
// GOOD — log internally, generic message to client
app.use((err, req, res, next) => {
logger.error(err);
res.status(500).json({ error: 'Internal server error' });
});
Flag each violation with: file path, line number, category (bare-catch | missing-status-check | raw-error-exposure), and description.
Use rune:scout to identify all files that import or depend on the changed files/functions.
For each dependent file:
Flag each regression risk with: dependent file path, what changed, whether tests exist, severity (breaking | degraded | untested).
Verify that new code ships complete:
Framework-specific completeness (apply only if detected):
loading state AND error statetry/catch and return typed resultpermission_classesIf any completeness item is missing, flag as WARN with: what is missing, which file needs it.
Verify that new code is consistent with existing project patterns — not just correct, but coherent with the codebase it lives in.
| Check | What To Look For | Severity |
|---|---|---|
| Naming conventions | New functions/variables follow project's existing naming style (camelCase, snake_case, etc.) | WARN |
| File organization | New files placed in correct directory per project structure (e.g., utils/ not lib/, components/ not ui/) | WARN |
| Import patterns | Uses project's established import style (absolute vs relative, barrel exports vs direct) | WARN |
| Error handling style | Matches project's existing pattern (Result type, try/catch, error codes) | WARN |
| State management | Uses same state approach as rest of project (Zustand, context, stores) | BLOCK if different paradigm |
| API patterns | Follows existing response format, middleware chain, auth pattern | BLOCK if diverges |
| Design system usage | Uses existing design tokens/components, not inline overrides | WARN |
Detection: Read 2-3 existing files in the same directory as the change. Compare patterns. Flag divergences.
Skip if: Project has no established patterns (greenfield, <5 files), or CLAUDE.md/conventions.md explicitly says "no conventions yet."
If .rune/evals/ directory exists with eval definition files, verify eval results as part of the quality gate.
| Check | Action | Severity |
|---|---|---|
| Capability eval defined but not run | Feature has .rune/evals/<feature>.md with CAP-* entries but no results | WARN: "Capability evals defined but not executed" |
| Regression eval failing | Any REG-* eval with status=fail | BLOCK: "Regression detected — existing behavior broken" |
| Capability eval below threshold | CAP-* eval pass@k below defined threshold | WARN: "Capability eval below threshold (X% vs Y% required)" |
| No eval file for new feature | New feature added (detected by new test files + new source files) but no .rune/evals/ entry | INFO: "Consider defining capability evals for new feature" |
Skip if: No .rune/evals/ directory exists (project hasn't adopted eval-driven development).
Apply domain-specific quality checks based on detected file types in the diff. These extend the generic completeness checks in Step 4 with deeper domain validation.
Domain hooks are additive — they add checks, never remove generic ones from Steps 1-4. If a domain hook flags BLOCK, the overall preflight verdict is BLOCK regardless of other steps.| Detected Pattern | Domain Hook | Key Checks |
|---|---|---|
migrations/*.sql, *.migration.* | Database | Rollback script present, no bare DROP/DELETE, migration tested |
openapi.*, *.graphql, *.proto | API Contract | Breaking changes flagged, version bumped, deprecated fields documented |
docs/policies/*, PRIVACY*, TERMS* | Legal/Compliance | No placeholder text, review date current, practice matches policy |
**/billing*, **/payment*, **/invoice* | Financial | Decimal precision correct, currency locale-aware, no hardcoded rates |
*.tsx, *.jsx, *.svelte, *.vue, components/* | UI/Frontend | Design token compliance, animation a11y, touch targets, visual hierarchy |
skills/*/SKILL.md, extensions/*/PACK.md | Rune Skill | Frontmatter valid, all required sections present, word count within layer budget |
*.test.*, *.spec.*, __tests__/* | Test Quality | No .skip/.only left in, assertions present (not empty tests), no hardcoded timeouts |
For each detected domain, run its checks on the relevant files in the diff:
### Domain Quality sectionWhen UI/Frontend hook is triggered, run these checks on all .tsx/.jsx/.svelte/.vue files in the diff.
Preamble — load design contract: If .rune/design-system.md exists, read it once. Apply the project's Scale Minimums block over the defaults below (e.g., a project declaring body ≥18px should flag 16px body text). If the file is absent, use defaults and emit a LOW advisory: "No .rune/design-system.md — run rune design to lock visual decisions."
| Check | What to Scan | Severity |
|---|---|---|
| Design token compliance | Hardcoded colors (#fff, rgb(, hsl() instead of CSS variables or Tailwind tokens | WARN: "Hardcoded color at {file}:{line} — use design token" |
| UI-SPEC drift | If .rune/ui-spec.md exists, compare component decisions (card style, form layout, nav type) against spec | BLOCK: "Component at {file} uses bordered cards but UI-SPEC locks elevated cards" |
| Animation accessibility | Animations/transitions without prefers-reduced-motion guard | WARN: "Animation at {file}:{line} missing reduced-motion check" |
| Touch target size | Interactive elements with explicit small sizing (w-5 h-5, p-0.5 on buttons/links) < 44×44px (or project override from design-system.md) | WARN: "Touch target too small at {file}:{line}" |
| Scale Minimum — body text | text-sm / text-xs / explicit font-size: 14px on <p> or primary body regions (not meta/secondary) | WARN: "Body text below 16px at {file}:{line} — reads as AI boilerplate" |
| Scale Minimum — hero display | <h1> with text-3xl or smaller (30px) when the heading is in a hero/landing section | WARN: "Hero heading below 48px at {file}:{line} — insufficient visual hierarchy" |
| Hand-rolled SVG for standard icons | Inline <svg viewBox= in JSX when the surrounding comment/class names indicate standard iconography (dashboard, menu, close, chevron, arrow, search, home, user, settings, bell, trash) | WARN: "Hand-rolled SVG at {file}:{line} — use @phosphor-icons/react or huge-icons, or ship boxed placeholder" |
| Manual hex accent shading | CSS/Tailwind config defining 2+ sibling --accent-hover / --accent-pressed / --accent-active with hex literals (no oklch(from ...) or design-token chain) | WARN: "Manual hex shade at {file}:{line} — derive via oklch(from var(--accent) calc(l - 0.08) c h)" |
| Missing states | Components fetching data without loading/error/empty states | WARN: "Async component at {file} missing [loading |
| Icon accessibility | Decorative icons without aria-hidden="true", functional icons without aria-label | WARN: "Icon at {file}:{line} missing aria attribute" |
| Inline styles | style={{ or style= attribute usage instead of classes/tokens | WARN: "Inline style at {file}:{line} — use CSS class or Tailwind" |
| Font loading | Custom font imports without font-display: swap or Next.js font optimization | WARN: "Font at {file} may cause layout shift — add font-display: swap" |
| Placeholder content | Strings like "Lorem ipsum", "TODO", "placeholder", "test text" in JSX/template | BLOCK: "Placeholder content at {file}:{line} — replace before shipping" |
Skip if: Diff contains only test files, config files, or non-UI code (detected by absence of JSX/template syntax).
Exception for Scale Minimums: Secondary/meta text (<time>, <small>, form hints, table captions) is allowed at 14px. The check only fires on primary body regions — paragraphs inside <main>, <article>, card body, marketing hero/features. Use common sense or an explicit data-scale="meta" attribute to opt out.
Exception for hand-rolled SVG: Project logos, data visualizations (charts/graphs via d3/recharts/visx), and human-designed illustrations are never flagged. The check fires only when class/comment context names a standard icon.
When a domain pack is installed (e.g., @rune-pro/finance, @rune-pro/legal), preflight checks the pack's Hard-Stop Thresholds table and applies matching rules to staged files. This means:
@rune-pro/finance automatically adds financial quality gates to preflight@rune-pro/legal automatically adds compliance checks to preflight### Domain Quality
- **Domains detected**: [Database, Financial]
- `migrations/003-add-billing.sql` — BLOCK: DROP TABLE without rollback script
- `src/billing/invoice.ts:42` — WARN: price calculation uses `toFixed(2)` instead of `Intl.NumberFormat`
If .rune/org/org.md exists, load organization approval workflows and enforce them as additional quality gates.
Read .rune/org/org.md and extract ## Policies, ## Approval Flows, and ## Governance Level| Org Policy | Preflight Check | Severity |
|---|---|---|
minimum_reviewers | Verify PR has required reviewer count before merge | WARN: "Org requires {N} reviewers" |
self-merge_allowed | If "Never" or "No", flag self-merge attempts | BLOCK if org prohibits |
required_checks | Verify all org-required checks (tests, security scan, type check, lint) are passing | BLOCK if missing |
staging_required | If "Yes", verify staging deployment exists before production | WARN if no staging step |
feature_flags | If "Required for user-facing changes", flag new UI without feature flag | WARN |
cross-domain_changes | If changes span multiple team domains, require reviewer from each | WARN |
Load ## Approval Flows > ### Feature Launch and display the required approval chain:
Append org findings under ### Organization Requirements section:
### Organization Requirements
- **Org template**: [startup|mid-size|enterprise]
- **Governance level**: [Minimal|Moderate|Maximum]
- **Minimum reviewers**: 2 (1 must be director+)
- **Required checks**: tests (≥80% coverage), security scan, type check, lint
- **Approval chain**: contributor proposes → lead reviews → vp approves → deploy
- WARN: Self-merge not allowed per org policy
If .rune/org/org.md does not exist, skip and log INFO: "no org config, organization requirements check skipped".
After all domain hooks (Step 4.5) and completeness checks (Step 4) complete, compute a Preflight Health Score to make the verdict numeric and comparable across runs.
Preflight Score = (Logic × 0.30) + (Error Handling × 0.20) + (Completeness × 0.20) + (Coherence × 0.15) + (Regression Risk × 0.15)
5 verification axes (Completeness + Correctness via Logic + Coherence — 3D verification model):
Each dimension is scored per staged files:
| Score | Grade | Verdict |
|---|---|---|
| 90–100 | Excellent | PASS |
| 75–89 | Good | PASS with notes |
| 60–74 | Fair | WARN |
| 40–59 | Poor | WARN (escalate to developer) |
| 0–39 | Critical | BLOCK |
Score is appended to the Preflight Report footer. Useful for tracking quality trend across sprints when cook logs preflight scores to .rune/metrics/.
Invoke rune:sentinel on the changed files. Attach sentinel's output verbatim under the "Security" section of the preflight report. If sentinel returns BLOCK, preflight verdict is also BLOCK.
Aggregate all findings:
Report PASS, WARN, or BLOCK. For WARN, list each item the developer must acknowledge. For BLOCK, list each item that must be fixed before proceeding.
## Preflight Report
- **Status**: PASS | WARN | BLOCK
- **Files Checked**: [count]
- **Changes**: +[added] -[removed] lines across [files] files
### Logic Issues
- `path/to/file.ts:42` — null-deref: `user.name` accessed without null check
- `path/to/api.ts:85` — missing-await: async database call not awaited
### Error Handling
- `path/to/handler.ts:20` — bare-catch: error swallowed silently
### Regression Risk
- `utils/format.ts` — changed function used by 5 modules, 2 have tests, 3 untested (WARN)
### Completeness
- `api/users.ts` — new POST endpoint missing input validation schema
- `components/Form.tsx` — no loading state during submission
### Coherence
- `api/users.ts` — uses `res.json()` but project convention is `sendResponse()` wrapper
- `utils/newHelper.ts` — placed in utils/ but project uses helpers/ directory
### Security (from sentinel)
- [sentinel findings if any]
### Composite Score
- Logic: [score] | Error: [score] | Completeness: [score] | Coherence: [score] | Regression: [score]
- **Preflight Score**: [weighted value] → Grade: [Excellent/Good/Fair/Poor/Critical]
### Verdict
WARN — 3 issues found (0 blocking, 3 must-acknowledge). Resolve before commit or explicitly acknowledge each WARN.
| Artifact | Format | Location |
|---|---|---|
| Preflight report | Markdown | inline (chat output) |
| Issue list (BLOCK/WARN by category) | Markdown list | inline |
| Preflight health score | Markdown table | inline (footer of report) |
| Spec compliance verdict | Markdown table | inline |
| Domain quality findings | Markdown section | inline |
| Failure Mode | Severity | Mitigation |
|---|---|---|
| Stopping at first BLOCK finding without checking remaining files | HIGH | Aggregate all findings first — developer needs the complete list, not just the first blocker |
| "Happy path works" accepted as sufficient | HIGH | CONSTRAINT blocks this — edge case analysis is mandatory on every function |
| Calling verification directly instead of the test skill | MEDIUM | Preflight calls rune:test for test suite execution; rune:verification for lint/type/build checks |
| Skipping sentinel sub-check because "this file doesn't look security-relevant" | HIGH | MUST invoke sentinel — security relevance is sentinel's job to determine, not preflight's |
| Skipping Stage A (spec compliance) when plan is available | HIGH | If cook provides an approved plan, Stage A is mandatory — catches incomplete implementations |
| Agent modified files not in plan without flagging | MEDIUM | Stage A flags unplanned file changes as WARN — scope creep detection |
| Domain hooks not triggered when pack is installed | HIGH | Step 4.5 auto-detects file patterns — if pack is installed but hooks don't fire, check file pattern matching |
| Domain hooks overriding generic checks | HIGH | HARD-GATE: domain hooks are ADDITIVE — they never replace Steps 1-4 |
| Pack Hard-Stop Thresholds ignored in preflight | MEDIUM | Step 4.5 Pack Integration must read installed pack thresholds — test with each new pack |
~2000-4000 tokens input, ~500-1500 tokens output. Sonnet for logic analysis quality.