$curl -o .claude/agents/preflight.md https://raw.githubusercontent.com/Rune-kit/rune/HEAD/agents/preflight.mdPre-commit quality gate — catches 'almost right' code. Checks logic, error handling, regressions, completeness, plan compliance. BLOCK verdict stops commit.
| 1 | # preflight |
| 2 | |
| 3 | ## Purpose |
| 4 | |
| 5 | <HARD-GATE> |
| 6 | Preflight verdict of BLOCK stops the pipeline. The calling skill (cook, deploy, launch) MUST halt until all BLOCK findings are resolved and preflight re-runs clean. |
| 7 | </HARD-GATE> |
| 8 | |
| 9 | 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. |
| 10 | |
| 11 | ## Triggers |
| 12 | |
| 13 | - Called automatically by `cook` before commit phase |
| 14 | - Called by `fix` after applying fixes (verify fix quality) |
| 15 | - `/rune preflight` — manual quality check |
| 16 | - Auto-trigger: when staged changes exceed 100 LOC |
| 17 | |
| 18 | ## Calls (outbound) |
| 19 | |
| 20 | - `scout` (L2): find code affected by changes (dependency tracing) |
| 21 | - `sentinel` (L2): security sub-check on changed files |
| 22 | - `hallucination-guard` (L3): verify imports and API references exist |
| 23 | - `test` (L2): run test suite as pre-commit check |
| 24 | |
| 25 | ## Called By (inbound) |
| 26 | |
| 27 | - `cook` (L1): before commit phase — mandatory gate |
| 28 | |
| 29 | ## Check Categories |
| 30 | |
| 31 | ``` |
| 32 | LOGIC — data flow errors, edge case misses, async bugs |
| 33 | ERROR — missing try/catch, bare catches, unhelpful error messages |
| 34 | REGRESSION — untested impact zones, breaking changes to public API |
| 35 | COMPLETE — missing validation, missing loading states, missing tests |
| 36 | SECURITY — delegated to sentinel |
| 37 | IMPORTS — delegated to hallucination-guard |
| 38 | ``` |
| 39 | |
| 40 | ## Executable Steps |
| 41 | |
| 42 | ### Stage A — Spec Compliance (Plan vs Diff) |
| 43 | |
| 44 | Before checking code quality, verify the code matches what was planned. |
| 45 | |
| 46 | Use `Bash` to get the diff: `git diff --cached` (staged) or `git diff HEAD` (all changes). |
| 47 | Use `Read` to load the approved plan from the calling skill (cook passes plan context). |
| 48 | |
| 49 | **Check each plan phase against the diff:** |
| 50 | |
| 51 | | Plan says... | Diff shows... | Verdict | |
| 52 | |---|---|---| |
| 53 | | "Add function X to file Y" | Function X exists in file Y | PASS | |
| 54 | | "Add function X to file Y" | Function X missing | BLOCK — incomplete implementation | |
| 55 | | "Modify function Z" | Function Z untouched | BLOCK — planned change not applied | |
| 56 | | Nothing about file W | File W modified | WARN — out-of-scope change (scope creep) | |
| 57 | |
| 58 | **Output**: List of plan-vs-diff mismatches. Any missing planned change = BLOCK. Any unplanned change = WARN. |
| 59 | |
| 60 | If no plan is available (manual preflight invocation), skip Stage A and proceed to Step 1. |
| 61 | |
| 62 | ### Step 1 — Logic Review |
| 63 | Use `Read` to load each changed file. For every modified function or method: |
| 64 | - Trace the data flow from input to output. Identify where a `null`, `undefined`, empty array, or 0 value would cause a runtime error or wrong result. |
| 65 | - Check async/await: every `async` function that calls an async operation must `await` it. Identify missing `await` that would cause race conditions or unhandled promise rejections. |
| 66 | - Check boundary conditions: off-by-one in loops, array index out of bounds, division by zero. |
| 67 | - Check type coercions: implicit `==` comparisons that could produce wrong results, string-to-number conversions without validation. |
| 68 | |
| 69 | **Common patterns to flag:** |
| 70 | |
| 71 | ```typescript |
| 72 | // BAD — missing await (race condition) |
| 73 | async function processOrder(orderId: string) { |
| 74 | const order = db.orders.findById(orderId); // order is a Promise, not a value |
| 75 | return calculateTotal(order.items); // crashes: order.items is undefined |
| 76 | } |
| 77 | // GOOD |
| 78 | async function processOrder(orderId: string) { |
| 79 | const order = await db.orders.findById(orderId); |
| 80 | return calculateTotal(order.items); |
| 81 | } |
| 82 | ``` |
| 83 | |
| 84 | ```typescript |
| 85 | // BAD — sequential independent I/O |
| 86 | const user = await fetchUser(id); |
| 87 | const permissions = await fetchPermissions(id); // waits unnecessarily |
| 88 | // GOOD — parallel |
| 89 | const [user, permissions] = await Promise.all([fetchUser(id), fetchPermissions(id)]); |
| 90 | ``` |
| 91 | |
| 92 | Flag each issue with: file path, line number, category (null-deref | missing-await | off-by-one | type-coerce), and a one-line description. |
| 93 | |
| 94 | ### Step 2 — Error Handling |
| 95 | For every changed file, verify: |
| 96 | - Every `async` function has a `try/catch` block OR the caller explicitly handles the rejected promise. |
| 97 | - No bare `catch(e) {}` or `except: pass` — every catch must log or rethrow with context. |
| 98 | - Every `fetch` / HTTP client call checks the response status before consuming the body. |
| 99 | - Error messages are user-friendly: no raw stack traces, no internal variable names exposed to the client. |
| 100 | - API route handlers return appropriate HTTP status codes (4xx for client errors, 5xx for server errors). |
| 101 | |
| 102 | **Common patterns to flag:** |
| 103 | |
| 104 | ```typescrip |