From 1bb901accdeb2bcbff44c45ecb70d1585f2b9aeb Mon Sep 17 00:00:00 2001 From: Kevin Longe <273747162+kvlonge@users.noreply.github.com> Date: Wed, 9 Sep 2026 22:18:38 +0100 Subject: [PATCH] docs: deepen rules overview --- concepts/rules.mdx | 188 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 156 insertions(+), 32 deletions(-) diff --git a/concepts/rules.mdx b/concepts/rules.mdx index 2552d6e..a13a2d0 100644 --- a/concepts/rules.mdx +++ b/concepts/rules.mdx @@ -1,52 +1,176 @@ --- title: "Rules" -description: "Enforce repeatable SQL and project requirements during compilation." +description: "Turn repeated SQL and project review decisions into compiler-enforced requirements." --- -Rules are configurable compile-time requirements evaluated against SQLBuild's compiled project. -They turn decisions that would otherwise be repeated in code review into deterministic compiler -findings. +Code review is an expensive place to keep enforcing a decision the team has already made. -SQLBuild evaluates checks in this order: +Comments such as these are useful once, but repetitive after that: -1. mandatory compiler correctness -2. selected native built-in Rules -3. selected custom Python Rules -4. artifact completion +- “This query needs deterministic ordering before `LIMIT`." +- “Move this model into the agreed area and layer." +- “Final models cannot depend on another area's private intermediate models." +- “This incremental output needs a declared grain, an enforced contract, and a focused test." +- “External sources must enter through staging." -Rules report findings and never rewrite source. [`sqb format`](/cli/format) remains the separate -source-rewriting command. +Rules turn those decisions into configurable compile-time requirements. They run over the same +project representation SQLBuild uses to produce executable artifacts, so a failed Rule is a failed +compile rather than an optional report somebody must remember to run. + +## What Rules are for + +A Rule answers a static question about authored SQL or the compiled project: + +- Is this SQL construct safe and intentional? +- Does this model live in the correct architectural boundary? +- Is the dependency graph using public interfaces? +- Do inferred output columns agree with a contract or declared grain? +- Does a model have the tests and audits required by its role? +- Does the project contain or omit a required path or resource? + +SQLBuild already knows the SQL, models, paths, configuration, declarations, columns, contracts, +tests, audits, and resolved graph. Rules expose those compiler-owned facts instead of asking each +check to reconstruct an incomplete view from files or generated artifacts. + +This enables checks that combine facts. For example, a project can allow `SELECT *` in staging while +requiring final models to have explicit contracted outputs, or allow cross-area dependencies only +when the referenced model lives under an `interface` directory. + +## Where SQL linting went + +SQL linting is part of the Rules system rather than a separate command. + +The `SQBRSQL` built-in family covers deterministic SQL diagnostics such as: + +- unsafe `NULL` comparisons; +- implicit cartesian joins; +- unordered `LIMIT` and `OFFSET`; +- unused CTEs; +- duplicate aliases; +- unstable window ordering; +- risky set-operation shapes; +- selected SQL structure and convention requirements. + +```toml +[rules] +select = ["SQBRSQL"] +``` + +These checks run through ordinary compilation: + +```bash +sqb compile +``` + +There is intentionally no separate `sqb lint` lifecycle. If an SQL requirement affects whether the +project is acceptable, the compiler enforces it with the rest of the configured Rules. + +Not every traditional lint concern should become a diagnostic. SQLBuild separates three kinds of +SQL responsibility: + +| Responsibility | Owner | Example | +|---|---|---| +| Correctness required to understand the project | Compiler | Invalid syntax, unresolved references, incompatible output shape | +| Configurable, deterministic requirements | `SQBRSQL` Rules | Unordered `LIMIT`, unused CTE, implicit cartesian join | +| Canonical presentation | [`sqb format`](/cli/format) | Capitalization, indentation, spacing, comma and clause layout | + +Rules never rewrite source. `sqb format` rewrites source and does not decide whether a project +passes its configured Rules. This keeps diagnostics and source mutation separate. ## Built-in and custom Rules -Native built-in Rules use `SQBR...` codes and cover reusable requirements maintained by SQLBuild. -Projects can define custom Python Rules with project-owned `XSQBR...` codes. Both use the same -selection, findings, exceptions, ordering, and cache system. +SQLBuild maintains native built-in Rules for reusable requirements. Their codes begin with `SQBR`, +for example `SQBRSQL004` and `SQBRGRAPH101`. + +Projects can define custom Python Rules under `rules/**/*.py`. Their codes begin with `XSQBR`, for +example `XSQBRARCH001`. Custom Rules use the same selection, findings, exceptions, execution order, +and cache system as built-ins. -You can adopt and configure custom Rules without maintaining their implementation. Python authoring -details live in [Custom Rules](/concepts/rules/custom-rules). +```toml +[rules] +select = [ + "SQBRSQL", + "SQBRGRAPH", + "XSQBRARCH", +] +``` + +Exact codes select one Rule. A code without its final three digits selects the family. An empty +`select` disables configurable Rules, but never disables mandatory compiler correctness. + + + Define typed model or project checks over compiler-owned facts with `@rule`, `RuleContext`, and + `Finding`. + ## One definition of project validity -`sqb compile` is authoritative. Build and execution commands use the same compiler path and reject -configured findings before opening a warehouse connection. A successful compile means mandatory -compiler correctness and every selected Rule passed. +`sqb compile` is authoritative. SQLBuild evaluates a project in this order: + +```text +mandatory compiler correctness +→ selected native built-in Rules +→ selected custom Python Rules +→ artifact completion +``` + +Build and planning commands use the same compiler path. Configured findings stop execution before a +warehouse connection is opened. Compile artifacts are not completed from a project that failed its +Rules. -Use `sqb rules` for focused catalogue inspection and development. Passing a focused Rule selection -does not claim that the complete configured Rules set passed. +Use a focused command while developing or adopting one Rule: -## Rules and other checks +```bash +sqb rules list +sqb rules show SQBRSQL004 +sqb rules run SQBRSQL004 +sqb rules run XSQBRARCH --select customer_orders +``` -| Capability | Purpose | +A focused run proves only that requested Rule and model selection passed. Run normal compilation +before treating the complete configured project as valid. + +## Rules compared with tests, audits, and contracts + +Rules do not replace every form of validation: + +| Capability | Question it answers | |---|---| -| Compiler correctness | Mandatory requirements needed to construct a trustworthy project | -| Rules | Configurable static requirements over compiled project facts | -| Formatting | Canonical source rewriting through `sqb format` | -| Tests | Expected behavior of SQL logic | -| Audits | Warehouse-data quality requirements | -| Runtime checks | Validation of external resources and execution state | +| Mandatory compiler correctness | Can SQLBuild construct a trustworthy project and executable representation? | +| Rules | Does the authored and compiled project satisfy selected static requirements? | +| Contracts | Does a model declare and preserve the required output shape? | +| SQL tests | Does SQL logic produce an expected result for controlled inputs? | +| Audits | Does warehouse data satisfy a runtime quality requirement? | +| Runtime checks | Are external resources and execution state valid now? | + +Use a Rule when the answer can be determined from source and compiler facts. Use a test when you +need to execute SQL against controlled data. Use an audit when the answer depends on warehouse data. + +## Findings and intentional exceptions + +A finding contains a stable code, project-relative path, line, column, explanation, and remediation. +This makes the same requirement usable in a terminal, JSON output, CI annotation, or agent workflow. + +Intentional departures remain explicit. Exact exceptions and path-scoped ignores require a reason; +exact exceptions are stale-checked so obsolete suppressions do not silently accumulate. Mandatory +compiler correctness cannot be suppressed. + +See [Findings and exceptions](/concepts/rules/findings-and-exceptions) for configuration examples. + +## Performance is part of the design + +Model Rules are evaluated and cached per model. Editing one model invalidates affected subjects +rather than every invocation. Project Rules run once over the complete project view and are the +explicit choice for genuinely project-wide invariants, with broader invalidation. + +Cache identity includes the Rule implementation and referenced module bindings, imported helpers, +options, subject, accessed compiler facts, tracked project-tree observations, dialect, and +compatibility versions. Negative observations are dependencies too: if a Rule observes that a glob +has no matches, adding a matching file invalidates the result. + +See [Execution and caching](/concepts/rules/execution-and-caching) for the complete lifecycle. -## Explore Rules +## Continue @@ -58,7 +182,7 @@ does not claim that the complete configured Rules set passed. Learn evaluation order, focused execution, and dependency-aware reuse. - - Author typed Python Rules over compiler-owned facts. + + List, inspect, run, and generate guidance for configured Rules.