Skip to content

feat: introduce deterministic canon linter for static canon validation #36

Open

Description

@jimbojw

Problem & Motivation

Canon Clerk is designed to enforce semantic, natural language architectural invariants via LLM evaluation cascades. However, foundational syntactic, structural, schema, and naming conventions do not require LLMs—they can and should be validated deterministically using standard static analysis (regex, YAML parsers, and file system checks).

Currently, authors writing canons can easily introduce silent defects without immediate feedback:

  • Typos in YAML frontmatter keys or incorrect value types.
  • Broken references to local documentation or specification files.
  • Inconsistent naming conventions (e.g., camelCase filenames, duplicate numeric prefixes).
  • Non-canon files inadvertently committed inside .canons/ directories.

Running a fast, offline, deterministic static linter in pre-commit hooks and CI prior to LLM evaluation saves tokens, reduces latency, and provides instantaneous feedback to canon authors.

Proposed Solution

Introduce a deterministic Canon Linter subproject / package that statically scans repository .canons/ directories without requiring LLM connections or external API credentials.

1. ESLint-Aligned Architecture & Sensitivity Controls

Following industry-standard linter conventions established by ESLint:

  • Severity Levels: Every rule supports three standard levels:
    • "off" (or 0): Rule is disabled.
    • "warn" (or 1): Emits a warning; does not fail the process by default.
    • "error" (or 2): Emits an error; triggers non-zero exit code (1).
  • Default Exit Code Behavior:
    • Clean or warnings only $\rightarrow$ Exit 0 (Success).
    • One or more errors $\rightarrow$ Exit 1 (Failure).
  • Sensitivity Control (--max-warnings <n>):
    • Adopts ESLint's ratchet model: if emitted warnings exceed <n>, the linter exits with code 1.
    • Enables strict zero-tolerance CI gates via canon-clerk lint --max-warnings 0.
  • Quiet Mode (--quiet):
    • Suppresses warning output entirely, reporting only errors.

2. Deep Monorepo Support: Cascading & "root" Breakout

To support complex monorepos, the linter's configuration discovery must mirror the hierarchical inheritance of tsconfig.json and ESLint:

  • Hierarchical Cascade & Inheritance:
    • The linter traverses upward from each discovered .canons/ directory to locate configuration files (e.g., .canonlintrc.json).
    • Subdirectory configurations automatically inherit and merge rules from parent configurations.
    • Specific rule severities can be overridden locally without redefining the entire rule catalog.
    • Optional explicit "extends": "<path>" support for sharing base configurations across monorepo packages.
  • Breakout "root": true Boundary:
    • Setting "root": true in a package-level configuration stops the upward directory traversal.
    • Enables isolated monorepo packages, submodules, or microservices to enforce their own canon standards without inheriting rules from the monorepo root.

3. Default Rule Catalog

Scope Rule Default Severity Description
Directory no-non-canon-files warn Flags non-canon or non-markdown files placed inside .canons/ directories.
File Naming kebab-case-filenames warn Requires canon filenames to adhere to lower kebab-case.
File Naming unique-numeric-prefixes warn Detects duplicate ordering prefixes (e.g. two 0001-* files in the same directory).
Frontmatter valid-yaml-frontmatter error Ensures opening/closing --- delimiters and valid YAML mapping syntax.
Frontmatter no-unrecognized-keys warn Flags unrecognized frontmatter keys not defined in SPEC.md.
Frontmatter valid-frontmatter-types error Verifies reserved keys match schema types (id: string, paths: string[], inspect: string[], etc.).
Frontmatter id-matches-filename warn Flags when frontmatter id diverges from the file stem slug.
References valid-reference-targets error Verifies files referenced in frontmatter references: exist on disk.
References links-declared-in-references warn Flags relative Markdown body links that are not declared in references: frontmatter.

4. CLI Interface

# Standard local run (warns on advisories, fails on errors)
canon-clerk lint

# Strict CI mode (ratchet zero warnings)
canon-clerk lint --max-warnings 0

# Quiet CI run (errors only)
canon-clerk lint --quiet

Acceptance Criteria

  • Core deterministic scanning engine without LLM dependencies.
  • ESLint-aligned severity matrix (off, warn, error) and exit code handling.
  • Sensitivity controls implemented via --max-warnings <n> and --quiet.
  • Hierarchical configuration resolution with inheritance (extends) and breakout ("root": true) boundary support.
  • Initial rule catalog covering directory hygiene, file naming, frontmatter schema, and reference integrity.
  • Comprehensive unit test suite with positive and negative canon fixtures across nested monorepo structures.

Activity

  1. added
    type: featNew user-facing functionality (triggers SemVer minor release).
    scope: canonsCanon specification (SPEC.md) and dogfood canons (.canons/).
    and removed on Sep 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    scope: canonsCanon specification (SPEC.md) and dogfood canons (.canons/).type: featNew user-facing functionality (triggers SemVer minor release).

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      ⌂ Proxy