marque-rules 0.2.1

Rule trait definitions for marque — the contract every rule crate implements
Documentation

marque-rules

Trait definitions for the Marque rule system — the contract every rule crate implements.

marque-rules defines the types and traits that bind the engine to its rules. It contains no rule implementations; those live in downstream crates such as marque-capco. Keeping the trait crate small and dependency-light is what allows rule crates to be swapped without touching the engine.

Role in Marque

marque-rules (traits)
   ↑                ↑
marque-capco    marque-engine
(implements)    (orchestrates)

The engine depends on marque-rules and on whatever rule crates the binary chooses to register. Rule crates depend only on marque-rules and marque-ism.

Public API

Type Role
Rule The trait every rule implements. Stateless; given parsed attributes plus a RuleContext, returns Vec<Diagnostic>.
RuleSet A bundle of rules exposed by a rule crate, with a schema version.
RuleId Stable rule identifier (e.g., "E001").
Severity Off / Warn / Error / Fix. Configurable per rule.
Diagnostic A violation: rule, severity, span, message, citation, optional fix.
FixProposal A proposed edit with confidence: f32 and FixSource provenance.
AppliedFix A FixProposal promoted by the engine, with timestamp + classifier id. The audit record.
RuleContext Position context (Zone, DocumentPosition, PageContext) and corrections map handed to Rule::check.

Usage

A minimal rule:

use marque_ism::{IsmAttributes, Span};
use marque_rules::{Diagnostic, Rule, RuleContext, RuleId, Severity};

struct AlwaysFire;

impl Rule for AlwaysFire {
    fn id(&self) -> RuleId { RuleId::new("X001") }
    fn name(&self) -> &'static str { "always-fire" }
    fn default_severity(&self) -> Severity { Severity::Warn }

    fn check(&self, _attrs: &IsmAttributes, _ctx: &RuleContext) -> Vec<Diagnostic> {
        vec![Diagnostic::new(
            self.id(),
            Severity::Warn,
            Span::new(0, 0),
            "example diagnostic",
            "EXAMPLE-§1",
            None,
        )]
    }
}

Architectural invariants

Two contracts in this crate are enforced by convention, not the type system — violating them is a compliance bug, not just a style issue:

  1. AppliedFix::__engine_promote is engine-only in production code. Rule crates and CLI code must never construct AppliedFix directly in production paths. They produce FixProposal values; only marque_engine::Engine::fix may promote them. Bypassing this skips the confidence-threshold gate, the fix-ordering sort, and the overlap guard, and corrupts the audit log.

    Test code (#[cfg(test)] modules, tests/ integration files, dev-dependencies-gated test-utility crates) MAY call __engine_promote to fabricate synthetic AppliedFix fixtures for testing audit emitters, sentinel checks, and renderers without a full Engine. The carve-out is scoped per Constitution V Principle V — see the doc comment on __engine_promote for the three constraints.

  2. FixProposal is pure data. No timestamps, no classifier identity, no runtime context. That purity is what makes rule output snapshot- testable without a clock or user identity.

See the "Architectural Invariants" section of the workspace CLAUDE.md for the full list.

Features

Feature Default Effect
serde off Serialize / Deserialize for diagnostic and fix types.

WASM compatibility

WASM-safe. Pure types, no I/O, no threads.

License

Marque License 1.0 (LicenseRef-MarqueLicense-1.0). See LICENSE.md.