OpenCanon

Agent-ready. Human-readable. Runtime-enforced.

Proof

Proof is how Project Canon becomes more than prose. Validators, gates, fixtures, Doctor, generated-doc drift checks, and declared task checks all verify that implementation still matches the canon.

Convention with runtime validator

A convention can own the prose, scope, generated docs, and runtime enforcement in one definition. Runtime checks that read project facts and return findings.

service-db-boundary.ts
ts
import { defineConvention } from "@opencanon/core";
 
export default defineConvention({
  id: "service-db-boundary",
  title: "Services compose DAL instead of direct DB clients",
  topics: ["service", "dal"],
  why: "DAL modules keep table/query details in one layer.",
  rule: "Services own workflow composition but should use DAL modules for persistence details.",
  applies: { kind: "files", globs: ["src/services/**/*.ts"] },
  render: {
    kind: "generated",
    docs: "docs/opencanon/canon/service-db-boundary.md",
    style: "reference"
  },
  runtime: {
    kind: "validator",
    severity: "error",
    scope: "import-edge",
    facts: ["imports"],
    validate({ ctx }) {
      return ctx.facts.imports()
        .filter((edge) => edge.source.endsWith("/db/client"))
        .map((edge) => edge.from.report({
          line: edge.line,
          message: "Services must not import DB clients directly."
        }));
    }
  }
});

Facts, not string scans

Validators read structured facts from the project runtime. Facts include imports, exports, calls, comments, literals, dependency metadata, graph edges, and file content when a file-scoped check genuinely needs text.

Graph-aware checks

graph validator
ts
runtime: {
  kind: "validator",
  severity: "warning",
  scope: "project",
  facts: ["graph"],
  validate({ ctx }) {
    return ctx.graph.callers("loadCompany").map((edge) =>
      edge.source.file.report({
        line: edge.source.line,
        message: "Inspect loadCompany side effects before changing this call.",
        fix: {
          safety: "manual",
          command: "opencanon graph callees loadCompany",
          description: "Inspect downstream side effects before changing this call."
        }
      })
    );
  }
}

Graph-backed checks can inspect symbols, references, callers, callees, and impact edges before a risky edit spreads.

Fixtures

Fixtures are tiny virtual projects that pin expected behavior. They live under opencanon/fixtures/ and run with the same validator engine as real project files.

opencanon/fixtures/service-db-boundary/invalid.ts
ts
import { defineFixture } from "@opencanon/core/testing";
 
export default defineFixture({
  files: ({ file }) => [
    file.ts("src/services/company.service.ts", `
      import { db } from "../db/client";
 
      export function loadCompany(id: string) {
        return db.company.findUnique({ where: { id } });
      }
    `)
  ]
});

Commit gates

Gates are for ambiguous changes that require user intent before commit. They produce pending approvals rather than normal findings.

gate runtime
ts
runtime: {
  kind: "gate",
  severity: "error",
  scope: "file",
  facts: ["literals"],
  validate({ ctx }) {
    for (const file of ctx.targetFiles) {
      if (!file.text.includes("sessionTtlDays")) continue;
 
      ctx.commitGate({
        id: "auth-session-ttl-change",
        title: "Auth session TTL changed",
        reason: "Session duration affects security and product behavior.",
        question: "Did the user approve this auth session TTL change?",
        file: file.path,
        evidence: [{ file: file.path }],
        approvalScope: "staged-diff"
      });
    }
 
    return [];
  }
}
approval flow
sh
opencanon validate --check-fixtures
opencanon validate --changed
opencanon gate pending --format json
opencanon gate approve auth-session-ttl-change \
  --summary "User approved changing sessionTtlDays from 365 to 730." \
  --via agent

Generated docs stay derived

OpenCanon-owned Markdown is rendered from definitions. Doctor rerenders and diffs generated docs so humans get clean prose without making Markdown a second source of truth.