Concept Rules
kern review doesn't just lint syntax. It builds a concept graph of your code — entrypoints, effects, guards, state mutations, boundaries — and checks the relationships between them.
How concept rules work
Traditional linters check patterns line by line. Concept rules first infer what your code does (is this an effect? a guard? a state mutation?) then check if the relationships are correct (is the effect guarded? is the mutation within its boundary?).
This catches bugs that no regex-based linter can find — the kind of bugs that take senior engineers hours to debug.
The 5 concept rules
unguarded-effect
A side effect (network, database, filesystem) without try/catch or error handling.
Why it matters: Unhandled network calls crash at runtime. A fetch() without try/catch is a ticking bomb in production.
// kern review flags this:
async function loadUser(id: string) {
const res = await fetch(`/api/users/${id}`); // ← unguarded
return res.json();
}
// Fix: wrap the effect
async function loadUser(id: string) {
try {
const res = await fetch(`/api/users/${id}`);
return res.json();
} catch (err) {
return null;
}
}boundary-mutation
State mutated across a module boundary — one module directly changes another module's state.
Why it matters: Cross-boundary mutations create invisible dependencies. When module A silently changes module B's state, debugging becomes impossible.
// kern review flags this:
import { cartState } from '../stores/cart';
function addDiscount() {
cartState.items[0].price *= 0.8; // ← mutating across boundary
}
// Fix: expose a mutation function from the owning module
import { applyDiscount } from '../stores/cart';
applyDiscount(0, 0.8);ignored-error
An error is caught but not handled — empty catch block or catch with no action.
Why it matters: Swallowed errors hide bugs. The application continues in a broken state without anyone knowing.
// kern review flags this:
try {
await saveOrder(order);
} catch (err) {
// empty — error silently swallowed
}
// Fix: at minimum, log it
try {
await saveOrder(order);
} catch (err) {
console.error('Failed to save order:', err);
throw err;
}illegal-dependency
A cross-module dependency that bypasses the intended architecture — importing internals instead of public API.
Why it matters: Internal imports break when the dependency refactors. They create tight coupling that prevents independent evolution.
// kern review flags this:
import { _internalParser } from '../core/src/parser'; // ← internal
// Fix: use the public export
import { parse } from '../core';unrecovered-effect
An effect (database write, API call) can fail but has no recovery path — no retry, no fallback, no error propagation.
Why it matters: Network calls fail. Disk writes fail. If there's no recovery path, the failure cascades silently.
// kern review flags this:
async function syncData() {
await db.write(data); // ← can fail, no recovery
await api.notify(webhook); // ← can fail, no recovery
}
// Fix: add recovery
async function syncData() {
try {
await db.write(data);
} catch (err) {
await db.write(data); // retry once
}
await api.notify(webhook).catch(() => {
queue.push({ type: 'retry-notify', webhook });
});
}Concept node types
kern review classifies code into 6 concept types:
- entrypoint — Route handler, main function, event listener, exported function
- effect — Network call, database query, filesystem operation, process spawn
- state_mutation — Direct state change (local, module, or global scope)
- error_raise — throw, reject, error return
- error_handle — catch, .catch(), error boundary
- guard — Auth check, validation, rate limit, policy check
The concept rules check relationships between these: "Is every effect guarded? Is every error handled? Do mutations stay within their boundary?"