Skip to content

Authoring checks (rules)

A rule is one checkable requirement — a code clause, a design standard, a firm convention. This page explains how rules are written, compiled, validated, and promoted so they become part of your workspace's canonical ruleset.

You usually don't write rules by hand

The fastest path is to ask the Setup agent ("Author a rule that checks…"). It drafts the rule, compiles it, runs it against your open model, and — once it looks right — promotes it. The mechanics below are what happens under the hood, and what to look at when you want to review or refine a rule yourself.

Two kinds of rule

Kind How it checks Lives as
Deterministic C# code that reads the model and decides pass/fail precisely Test.cs
AI check A prompt that reasons over model data / sheet evidence prompt.md
Hybrid Both — deterministic gate plus AI judgment Test.cs + prompt.md

Every rule also has a spec.yaml — its metadata: id, title, discipline, severity, category, and the kind of test. A README.md captures the rationale.

How a deterministic rule works

A deterministic rule is a small C# class that implements VitruAI's rule contract: it exposes its spec and an Evaluate(...) method that sweeps the model (rooms, elements, parameters, geometry) and returns findings with a status and evidence.

Rules are read-only against your model by design. Before any rule compiles, it passes a safety check that rejects anything that could change your model or reach outside it — file system access, network calls, launching processes, or Revit document mutations (creating, deleting, or editing elements). A rule can read and report; it can't modify.

Sharing code between rules (shared libraries)

Many checks are the same mechanic with a different number. "Bedrooms must be at least 11 m²" and "kitchens must be at least 7 m²" both need the true floor area of a possibly L-shaped room — and that geometry is the hard part, not the threshold.

So the geometry is written once, as a library, and rules link it:

# qaqc/rules/bedroom-min-area/spec.yaml
id: bedroom-min-area
testKind: Deterministic
testClass: Firm.Rules.BedroomMinArea
uses: [room-geometry]          # ← link the shared library
// qaqc/rules/bedroom-min-area/Test.cs
using Firm.Lib.RoomGeometry;   // ← and call it

namespace Firm.Rules
{
    public class BedroomMinArea : IRuleTest { /* … RoomGeometry.AreaM2(room) … */ }
}

A library is a folder under qaqc/lib/<library-id>/ holding a spec.yaml (its id and C# namespace), its .cs source, and a README.md. Your rule and every library it names are compiled together as one unit, which is what makes the using above resolve.

Two names have to be unique, and the reason is the same

Because everything compiles together, two rules cannot declare the same testClass, and two libraries cannot declare the same namespace. If they do, the compiler sees one type defined twice and one of the two checks stops running.

The fix is to qualify names rather than shorten them — Firm.Rules.BedroomMinArea, not BedroomMinArea. Validation catches this before anything runs and tells you which other rule or library already owns the name.

Editing a library re-runs every rule that uses it

Change a library and every rule linking it picks up the new code automatically the next time it runs — there is no version to bump and no stale copy to hunt down.

That cuts both ways — re-validate the dependents

A rule's evidence that it behaves correctly was gathered against the old geometry. Nothing stops a rule from running on new math with old validation, so when you change a library, re-run the rules that use it and confirm they still land the true/false-positive matrix below. This is author discipline, not an automatic gate.

Drafting a library

  • Drafts: qaqc/lib-proposals/<id>/ — anyone with access can draft here.
  • Canonical: qaqc/lib/<id>/ — promoted libraries, maintainer-owned, listed in qaqc/lib/_index.yaml so agents can discover them.

A library can also live at the firm level rather than in one project. If a project defines a library with the same id as a firm one, the project's copy wins for that project — that is how you override firm geometry locally without touching the firm baseline or editing uses: in every rule.

Author → compile → validate → promote

DRAFT          Setup agent (or you) writes spec.yaml + Test.cs into  rules-proposals/<id>/
   │
COMPILE        compile_csharp:  safety pre-pass  →  compile  →  cached, ready to run
   │           (rejections or compile errors come back so the draft can be fixed)
   │
VALIDATE       run it against the open model and inspect findings against the matrix:
   │             • true positive  — a real problem IS flagged, with actionable evidence
   │             • true negative  — a compliant element is NOT flagged
   │             • false positive — a compliant element wrongly flagged  (the trust-killer)
   │             • false negative — a real problem silently passed
   │
PROMOTE        once it behaves, the Setup agent moves it  rules-proposals/ → rules/  (canonical)
               and updates the discipline index so the Coordinator routes to it

Running a rule while authoring vs. a stored rule

  • A freshly compiled draft is run by its compiled handle — used during authoring to iterate quickly in the current session.
  • A stored (promoted) rule is run by its rule id from the workspace. This is what the Coordinator and specialists use during a normal check; it reads the rule's definition from rules/ and runs it.

Either way, findings are captured and written to the run's report (see Concepts → Reports).

Validating a rule ("tests")

There is no separate unit-test framework — validation is empirical. You run the rule against a real model (ideally one with known good and known bad cases) and confirm it lands the true/false-positive/negative matrix above. A rule that produces false positives is the most damaging, because it erodes trust in every other result — so the bar for promotion is "no false positives on the validation model, real problems caught with actionable evidence."

The rule's README.md is where that reasoning is recorded, so a reviewer can see why the rule is trusted, not just that it passed.

Where rules live

  • Drafts: projects/<project>/qaqc/rules-proposals/<id>/ — anyone with access can author here.
  • Canonical: projects/<project>/qaqc/rules/<id>/ — promoted, active rules. The Setup agent owns promotion so the discipline index stays consistent (see Working with workspaces).

See also