Write Kodus rules that understand TypeScript: plain-language to AST-driven checks
aicode-reviewtypescript

Write Kodus rules that understand TypeScript: plain-language to AST-driven checks

DDaniel Mercer
2026-05-23
24 min read

Learn to write plain-language Kodus rules for TypeScript and map them to AST/ESLint validators for safer code review.

Kodus rules for TypeScript: why plain language is the fastest path to trustworthy review automation

Kodus becomes genuinely useful when it stops feeling like a generic AI and starts behaving like a senior reviewer with opinions you can explain, test, and tune. For TypeScript teams, that means writing rules in plain language first, then mapping them to precise validators that can inspect syntax, types, imports, and control flow. That approach is especially powerful in AI product evaluation conversations, because the first question is rarely “can it use a model?” and much more often “can we trust its output in our code review process?” If you are building with LLM safety guardrails in mind, Kodus is interesting precisely because it can sit between human judgment and automated enforcement. The real advantage is not that it writes clever comments; it is that it can express a team policy in language developers understand, then enforce it consistently across pull requests.

This matters even more in TypeScript projects because many high-value review rules are not about style alone. They concern unsafe database access, error handling in async code, dependency boundaries, and whether a module imports from a forbidden layer. That is the same kind of design discipline you see in serious platform governance work like partner SDK governance, where rules need to be clear enough for humans but strict enough for machines. Kodus fits that pattern well when you pair natural-language policy prompts with AST-level validators or ESLint-style checks. In practice, this gives your team a review layer that is understandable, auditable, and much harder to ignore.

Pro tip: Treat every Kodus rule like a contract: write the human-readable policy first, then define the AST signals that prove or disprove it. If you cannot point to the syntax node or type check that triggers the rule, it is still too vague.

How Kodus fits into a TypeScript code review workflow

From “smart comments” to structured review policy

Kodus is often introduced as an AI code review agent, but the practical way to adopt it is as a policy layer that understands your repo and your engineering standards. The Kody assistant can learn your project context, but for production use you want rules that are stable, repeatable, and explainable. That is where plain-language rule authoring becomes useful: a reviewer can say “all database queries must sanitize inputs” and the implementation team can decide whether that means a SQL tag wrapper, parameterized queries, or a specific ORM pattern. This is much more robust than asking an LLM to improvise a critique from a diff alone. It also aligns with what teams expect from tooling evaluation checklists: clear behavior, controllable scope, and predictable results.

In TypeScript, that predictability matters because the language gives you multiple places to validate intent. You can inspect AST nodes, read type information, or piggyback on an ESLint rule that already knows how to walk the tree. The best Kodus setup uses plain language as the authoring interface, but not as the enforcement engine. The enforcement engine should be deterministic. That way the AI can propose, explain, and prioritize, while the validator provides the final yes/no signal. This pattern is especially attractive for teams practicing systematic review hygiene because it produces stable signals you can trend over time.

Why TypeScript is the right target for AST-driven rules

TypeScript’s syntax tree is rich enough to express most review policies without resorting to brittle text matching. You can inspect function declarations, call expressions, import declarations, await expressions, try/catch blocks, and type annotations with a high degree of confidence. That means a rule like “do not call raw database methods with string concatenation” can be implemented by finding a call expression on a known query API and then checking whether the arguments include an unsafe binary expression or template literal. The same principle applies to imports, where you can prevent cross-layer coupling by checking source paths and import specifiers. If you have ever worked through SQL-as-API design, you already know that the power is not in the syntax alone but in the semantics encoded around it.

There is also a good operational reason to prefer AST or ESLint-style validation: it scales across teams. Humans disagree on wording, but syntax nodes are objective. A reviewer can debate whether a rule is wise, yet the parser will always identify an ImportDeclaration or AwaitExpression the same way. That consistency is how you keep AI-assisted review from drifting into generic advice. In broader engineering terms, it is similar to the difference between narrative governance and measurable controls discussed in identity-signal resilience: the system gets stronger when the control is observable and repeatable.

Where Kody and RAG help, and where they do not

Kody and RAG-style retrieval are most useful for contextualizing the rule, not replacing the validator. A retrieval layer can pull examples from your repository, past review comments, or internal standards docs so the rule understands how your team actually writes TypeScript. For example, if your codebase uses a custom database wrapper, RAG can surface that wrapper and help Kodus phrase the rule around it. But the validator should still be the source of truth. That separation keeps the system honest and makes the rule easier to maintain when architecture changes. This is the same logic behind AI vendor due diligence: any “smart” layer is only useful if the control plane stays transparent.

RAG also helps reduce false positives by teaching the rule the local vocabulary. A plain-language policy like “avoid direct calls to the payment gateway client outside services/payments” is much easier to calibrate if the model can retrieve the repository’s service layout. Without that context, the rule might overflag legitimate helpers or miss aliasing patterns. Use RAG for examples, exception cases, and explanation generation. Use AST and type checks for enforcement. When those layers work together, Kodus becomes a review system rather than a suggestion engine.

Designing plain-language rules that can be enforced precisely

Write the policy in developer language first

The best rule authoring starts with language your team would use in a code review comment. You want wording like “Database queries must use parameterized inputs; avoid string concatenation in SQL builders,” not “eliminate injection-adjacent lexical ambiguity.” The reason is simple: reviewers need to approve or reject the rule before anyone writes code. Plain language lowers that barrier. It also makes it easier to align with other operational guidance, such as the kind of practical tradeoff analysis found in AI governance trend guides for regulated workflows.

A useful pattern is to include four parts in the human rule: what must happen, what must not happen, where the rule applies, and what exceptions exist. For instance: “In server-side TypeScript files under src/db, all SQL execution must use parameterized placeholders; direct string concatenation is prohibited except for static migration scripts.” That single sentence is already much better than a vague principle. It tells you the scope, the method, and the exception boundary. From there, Kodus or your validator can translate the rule into search criteria and syntax checks.

Map each statement to a machine-checkable signal

Once the policy is written, break it into signals that can be validated. If the policy says “use parameterized inputs,” the signal might be a call to a known query function with an array of parameters, an options object, or a tagged template literal that your ORM guarantees is escaped. If it says “avoid string concatenation,” the signal might be a binary expression containing user-controlled identifiers inside a query argument. If it says “server-side files only,” the signal might be a path match plus a file-level exclusion for tests or migrations. This translation step is what turns Kodus from a conversational tool into a governance system.

The easiest way to think about it is to separate intent from evidence. Intent is the plain-language rule. Evidence is the AST node, type signature, or data-flow pattern that proves the rule was followed. If the evidence is incomplete, the rule should not fire loudly; it should ask for more context or downgrade to a soft recommendation. That makes the system more credible to developers, much like a strong developer checklist for tooling helps teams distinguish flashy demos from production-grade systems.

Define severity and confidence separately

Not all rule violations deserve the same treatment. A rule can be high severity but low confidence, such as when an unsafe pattern might be hidden behind a wrapper. Another rule may be low severity but high confidence, such as a forbidden import from a clearly named internal package. Separating severity from confidence lets Kodus present smarter review output. Developers can see whether the issue is a hard blocker, a nudge, or just context for a human reviewer. This approach mirrors what teams do in risk registers and governance scorecards, such as the structure described in risk register scoring templates.

In practical terms, you might emit four states: pass, warn, fail, and needs-context. Pass means the rule evidence is satisfied. Warn means the rule detected a concern but cannot prove misuse. Fail means the AST or type check confirmed the violation. Needs-context means the file or symbol is outside the validator’s confidence envelope. That taxonomy helps reviewers trust the tool, and trust is the main currency in AI-assisted code review. Teams that adopt this pattern usually spend less time arguing with the tool and more time fixing real problems.

Example 1: database query sanitization rules in TypeScript

Plain-language rule

A strong database rule should be explicit and narrow. For example: “All dynamic SQL executed from TypeScript server code must use parameterized queries or approved escaping helpers. Reject string concatenation, inline interpolation, and ad hoc query assembly in execution calls.” This wording works because it names the risky behavior and the acceptable alternatives. It also gives you enough room to adapt to PostgreSQL, MySQL, or an ORM wrapper without rewriting the policy. This is the kind of actionable control that helps teams avoid the hidden costs of sloppy automation, a concern that often appears in discussions about incident response for AI systems.

AST-style validator strategy

At the AST level, look for CallExpression nodes whose callee resolves to a known database function, such as query, execute, or an ORM method. Then inspect the argument list for unsafe construction patterns: BinaryExpression concatenation, TemplateLiteral expressions with embedded identifiers, or a variable sourced from request input without sanitization. If you have type information available, add a second pass that traces the origin of the value to Request, Body, Params, or user-defined DTOs. That combination is much stronger than regex matching and far less noisy than naive AI commentary.

You can also create whitelist conditions. For example, a tagged template from a trusted SQL library can pass, even though it is syntactically a template literal. That means the validator must be aware of the import source, not just the node kind. This is where import constraints and database rules overlap: the safety of the query often depends on the safety of the helper you imported. That is why a single policy can benefit from both AST and module-resolution logic, similar in spirit to how partner SDK governance separates allowed capabilities from forbidden ones.

Example check logic

A practical implementation might read like this in pseudocode: “If a call to db.query contains a binary expression with a request-derived variable, flag it. If the call uses a prepared-statement wrapper from @acme/sql, allow it. If the value is concatenated inside a template literal and the function returns a raw string, fail unless the file matches a migration allowlist.” This logic is understandable to developers and easy to encode as an ESLint rule or Codus validator plug-in. The critical part is that the plain-language rule and the AST logic remain aligned. When they drift apart, trust erodes quickly, which is why many teams compare these systems as carefully as they would compare a vendor’s AI claims.

Example 2: async error handling rules that prevent silent failures

Plain-language rule

Async bugs are one of the highest-value places to apply Kodus because they are easy to miss during manual review and painful in production. A useful rule is: “Any awaited operation that can reject must be handled with try/catch, or its rejection must be intentionally propagated via a top-level error boundary. Do not swallow errors in async callbacks.” This is clear, specific, and testable. It also fits how teams reason about reliability when they are designing code paths that should fail loudly rather than silently, a concern closely related to operational calm after automation changes.

AST and control-flow signals

For this rule, the basic AST target is AwaitExpression. The validator should check whether the enclosing block is wrapped in a TryStatement or whether the parent function is known to propagate errors to a framework boundary. For promise chains, look for CallExpression nodes with .catch() and ensure the catch handler either logs and rethrows or converts the error into a controlled response. The harder case is detached async work, such as fire-and-forget tasks in event handlers. In those cases, the validator should flag the pattern unless there is explicit justification, because silent rejections are often operational bugs disguised as convenience.

If you can incorporate type information, you can improve precision further by identifying functions that return Promise<T> and calls that are likely to reject, such as network I/O, file access, and database mutations. The rule should not punish safe synchronous code just because it appears inside an async function. That distinction is exactly where AST plus types beat plain text analysis. Teams who care about reliability often formalize these patterns the same way they formalize recovery playbooks and resilience checks, like the operational thinking in system recovery education.

Practical review outcomes

In a pull request, a good Kodus rule would not just say “handle errors.” It would identify the line where an awaited call lacks a surrounding error boundary, explain why the path is risky, and suggest the right remediation. For example, it could recommend adding a try/catch with a structured log or returning the promise to the caller. That makes the review actionable rather than accusatory. Over time, the rule reduces repeated bugs, and because it is policy-based, it also teaches new contributors the team’s expectation. That learning effect is one reason AI review tools are being evaluated more seriously across the ecosystem, much like teams study guardrail design before they deploy LLMs in sensitive workflows.

Example 3: import constraints for layered TypeScript architectures

Plain-language rule

Import boundaries are one of the most effective ways to preserve architecture at scale. A clear rule might read: “UI components may not import directly from data-access modules. Shared utilities are allowed only if they contain no persistence code. Feature folders may import from their own layer and from approved public APIs.” That is understandable to humans and maps cleanly onto code structure. It also helps reviewers spot creeping coupling before it becomes a refactor emergency. When teams ignore these boundaries, they often end up with the architectural equivalent of the hidden costs described in cost breakdown guides: the small compromises compound.

AST and module-resolution checks

At the syntax level, inspect ImportDeclaration nodes and the literal path string. Then resolve the import to the actual file and compare it against approved layer rules. If a component under src/ui imports from src/db, flag it unless the import targets an explicitly exported interface layer. If your project uses path aliases, the validator must resolve aliases before evaluating the boundary. This is exactly the kind of problem where an ESLint-style approach shines, because the rule can leverage existing parser and resolver infrastructure while Kodus handles the explanation and review context.

There is a deeper benefit here: once import constraints are codified, you can create rule families instead of one-off warnings. One family can govern layer boundaries. Another can enforce that test utilities never leak into production bundles. A third can ensure that server-only modules are not imported into client code. These policies are especially useful in mixed frontend/backend TypeScript monorepos, where accidental coupling is easy to introduce. For organizations investing in shared platform standards, the discipline resembles the governance controls used in OEM SDK ecosystems.

How to avoid brittle false positives

Import rules often fail when teams overfit them to directory names instead of architectural intent. If you only match folders, refactors will break the policy. A better approach is to annotate boundary modules, public APIs, and allowed exception files. For example, you might mark src/data/public.ts as the only approved import surface from the data layer. Then your rule checks whether the import path resolves to that public surface. This is more maintainable and easier to document. It also means your Codus rule remains accurate even as files move, which is essential in active codebases that practice disciplined platform evolution.

Implementation blueprint: turning a natural-language rule into validator code

Step 1: define the human policy and examples

Start with a short policy statement plus at least two positive and two negative examples. The positive examples show what should pass. The negative examples show what should fail. In TypeScript, examples are often more useful than abstract definitions because syntax nuances matter. For instance, a tagged SQL helper may look like unsafe interpolation to a novice, but it can be perfectly safe if the library escapes values. The examples serve as training data for Kody or any retrieval layer, and they also keep the rule documentation honest. Think of this as the equivalent of a high-quality tool evaluation rubric: concrete, testable, and repeatable.

Step 2: extract the node patterns

List the AST nodes involved and the surrounding context required. For database rules, that might be call expressions, template literals, variable identifiers, and import resolution. For async rules, it might be await expressions, try statements, catch clauses, and promise-returning calls. For import constraints, it is import declarations and path resolution. Once you know the nodes, you can decide whether to implement a custom ESLint rule, a standalone TypeScript compiler API check, or a Kodus plug-in that wraps one of those engines. The key is choosing a deterministic parser, not leaving rule interpretation to model guesswork.

Step 3: define exceptions and allowlists

No real codebase has zero exceptions. Migration scripts may intentionally use raw SQL. Framework error boundaries may intentionally rethrow. Platform public APIs may intentionally cross architectural layers. Encode those exceptions up front, not after the first flood of false positives. This is where a self-hosted deployment becomes especially attractive, because the team can tune rules without exposing code or policy outside the organization. If you are weighing control, privacy, and operational ownership, the same logic appears in self-hosted AI procurement checklists and is one reason developers prefer open systems over closed black boxes.

Step 4: test with fixtures and PR samples

Each rule should have a test harness with passing and failing snippets. Ideally, you test both isolated files and realistic pull request diffs. The goal is to catch parser edge cases, alias resolution issues, and false positives before the rule reaches production. If your team uses monorepos, test across packages, because import and path rules often behave differently when workspace aliases are involved. A good testing loop will save more time than any amount of prompt tuning. In practice, the most reliable teams treat rule development like infrastructure work, not content generation.

Rule typeBest signalCommon false positiveHow to reduce noiseBest tool shape
Database sanitizationCallExpression + data-flowSafe tagged templateWhitelist approved SQL helpersESLint rule with type info
Async error handlingAwaitExpression + TryStatementFramework-level boundaryRecognize handler wrappersAST checker with function context
Import constraintsImportDeclaration + resolverAlias or barrel fileResolve public APIs and aliasesESLint import rule
Layer isolationPath + package boundaryTest utilities in prod codeEnvironment-specific allowlistMonorepo-aware validator
Logging policyCallExpression + severity mappingStructured logger wrappersAllow approved wrapper modulesRule pack with config

Operationalizing Kodus in a self-hosted TypeScript environment

Why self-hosted matters for code review policy

If your code review policy contains architectural secrets, security expectations, or proprietary naming conventions, self-hosting is often the safest route. Kodus’s model-agnostic design makes that practical because you can connect the models you already trust while keeping rule logic and repository context under your control. That is especially important for teams with compliance constraints or sensitive IP. The self-hosted model also makes it easier to keep an audit trail of rule changes, reviewer feedback, and enforcement outcomes. For organizations that care about governance, this is the difference between experimentation and infrastructure.

Self-hosting also improves rule iteration speed. You can update a rule, run it against recent PRs, and see the error profile immediately. That feedback loop encourages teams to refine language, narrow scope, and improve documentation. It also supports the kind of real-world tuning that AI tools need to earn trust, much like the operational rigor in post-rollout stability playbooks.

Governance, ownership, and change management

Every good review rule should have an owner, a rationale, and a review cadence. Otherwise, it becomes stale or politicized. In a self-hosted Kodus setup, you can assign each rule to a platform engineer, a security lead, or a domain owner. You should also version your rules alongside code, so rule behavior changes are reviewed just like application changes. This keeps the policy transparent and makes it easier to explain why a rule exists. Teams that formalize ownership tend to get better long-term adoption because developers know where to ask questions when a rule feels wrong.

Change management is also where RAG can be useful. If a rule changes because the codebase changed, retrieval can surface the relevant migration docs or ADRs directly in the review output. That reduces confusion and keeps commentary grounded in the team’s actual decisions. In other words, Kodus should not just say “this is a violation”; it should help explain the organizational reason the rule exists. That is how AI review becomes a knowledge system rather than a complaint generator.

Measuring impact on code quality

The best way to prove value is to measure fewer escaped bugs, faster reviews, and less time spent on repetitive comments. Track rule hit rates, false positive rates, and time-to-fix for each policy family. You can also measure whether certain bug classes disappear after a rule is introduced, such as raw SQL injection patterns or uncaught promise rejections. These metrics give you a grounded way to judge whether the system is improving code quality or just adding noise. That is the same analytical discipline behind turning raw metrics into decisions: the numbers matter only if they change behavior.

When you present results to leadership, focus on avoided rework and faster reviewer throughput, not AI novelty. Developers care that the tool catches things earlier. Managers care that it lowers incident risk and review latency. Security cares that it enforces boundaries consistently. A good Kodus setup can serve all three audiences if the rules are precise and the outputs are easy to audit.

Best practices for writing high-signal plain-language rules

Keep one rule per risk

Do not overload a single rule with unrelated concerns. A database rule should not also police logging format, and an import rule should not also enforce naming conventions. When one rule tries to do too much, it becomes impossible to map to a single AST or ESLint validator, and false positives rise fast. Splitting policy by risk keeps your implementation clear and your review output readable. That discipline is similar to the way strong engineering teams separate procurement, security, and implementation concerns in broader platform decisions.

Prefer “must,” “must not,” and “except” language

Soft wording creates ambiguity. Words like “consider” and “generally” are useful in human discussions but weak in machine-enforced policy. Use strong verbs in the rule text, then list exceptions explicitly. For example: “API routes must catch and normalize async errors; exception: top-level framework error middleware that rethrows after logging.” The clearer the human language, the easier the validator implementation becomes. Clarity here is a feature, not a style preference.

Document the why, not just the what

Developers accept rules more readily when the rationale is visible. If a rule exists to prevent SQL injection, say so. If it exists to keep boundary layers stable, say so. If it exists to ensure recoverable async behavior, say so. The “why” helps teams maintain the rule when architecture changes, and it supports education for new engineers. It also makes Kodus output feel less arbitrary, which is essential for adoption in a busy review culture.

Pro tip: If a rule cannot be explained in one sentence and enforced with a measurable syntax or type signal, it is probably too broad to ship.

FAQ

Can Kodus replace ESLint for TypeScript code quality checks?

No. The strongest setup is usually Kodus plus ESLint-style validators, not Kodus instead of them. ESLint is excellent at deterministic syntax and import rules, while Kodus adds natural-language policy authoring, explanation, prioritization, and review context. If you want a rule to be trustworthy in production, the validator should still be deterministic and testable. Kodus is the layer that makes those rules easier to create, understand, and maintain.

What is the best way to write a rule for SQL sanitization?

Start with a plain-language statement that names the forbidden pattern and the approved alternatives. Then map that policy to query call sites, template literals, concatenation nodes, and trusted helper imports. Test the rule against safe tagged templates, parameterized queries, and known-dangerous examples. If you can, add type and data-flow checks so the validator can trace request-derived input into query construction.

How does RAG improve Kodus rules?

RAG helps with context, examples, and exception handling. It can pull your team’s docs, previous PR comments, and repository conventions so the rule sounds like it was written by someone who knows your codebase. But RAG should not be the enforcement layer. The final decision should still come from an AST, type, or ESLint-style check. That separation keeps the system accurate and auditable.

Do I need to self-host Kodus for serious TypeScript repositories?

Not always, but self-hosting is often a better fit when rules involve proprietary architecture, security-sensitive code, or strict compliance needs. It also gives you tighter control over model choice, data handling, and audit trails. For teams processing many pull requests, self-hosting can simplify governance and reduce reliance on external platforms. If your code review policy is an asset, keeping it under your own control is usually wise.

What is the biggest mistake teams make when authoring plain-language rules?

The biggest mistake is writing a policy that sounds sensible to humans but cannot be mapped to a concrete code signal. If the rule cannot point to specific nodes, imports, or type facts, it will become noisy or inconsistent. Another common issue is trying to combine too many concerns into one rule. Keep the policy narrow, define exceptions, and test it with real code examples before rolling it out broadly.

How do I reduce false positives in import boundary rules?

Resolve aliases, recognize public API barrels, and allow explicit exceptions for tests or migrations. Avoid folder-name-only rules because they break during refactors. Instead, connect the rule to architecture intent: which modules are public, which are private, and what layers are allowed to depend on each other. That makes the policy more durable and much easier to maintain.

Conclusion: the winning pattern is human-readable policy plus deterministic validation

The best Kodus rules for TypeScript are not clever prompts, and they are not hard-coded one-offs either. They are a bridge between plain-language engineering policy and deterministic checks that understand AST structure, imports, control flow, and types. That bridge is what lets you scale code review without losing trust. When you combine Kody-style context, RAG-assisted examples, and ESLint-like validators, you get review automation that actually improves code quality instead of adding background noise. For teams trying to modernize code review while staying self-hosted and in control, that is the sweet spot.

Start with the three rule families that pay off fastest: database query sanitization, async error handling, and import constraints. Write them in the language your engineers use every day. Then map each one to a concrete syntax or type signal and test it against real repository examples. If you keep the policy narrow, the implementation deterministic, and the explanation clear, Kodus becomes more than an AI assistant. It becomes a repeatable system for enforcing TypeScript standards at the speed of modern delivery.

Related Topics

#ai#code-review#typescript
D

Daniel Mercer

Senior TypeScript Editor

Senior editor and content strategist. Writing about technology, design, and the future of digital media. Follow along for deep dives into the industry's moving parts.

2026-05-23T14:25:03.881Z