Content Contract Engine is a local-first TypeScript CLI and library for applying deterministic release contracts to Markdown and MDX.
Version 0.1.0 is the first public preview. The package is tested on Node.js 22 and 24 across Windows and Ubuntu. Its configuration, findings, rule IDs, exit codes, and published JSON schemas are documented compatibility surfaces.
| Contract | Built-in behavior |
|---|---|
| Frontmatter | Validate YAML frontmatter against JSON Schema 2020-12 |
| Local links | Check exact file casing, images, extensionless paths, and heading anchors |
| Canonical facts | Resolve explicit references from one reviewable YAML catalog |
| Freshness | Enforce review and expiry dates using UTC calendar days |
| Existing debt | Baseline current findings while failing new regressions |
| Automation | Return sorted terminal or versioned JSON findings with stable rule IDs |
| Rendering | Substitute facts into a separate output tree without editing source files |
The engine complements prose linters and external URL checkers. It does not replace markdownlint, Vale, lychee, a static-site generator, or an HTML sanitizer.
npm install --save-dev content-contract-engineNode.js 22 or newer is required. The package is ESM-only.
Create a configuration for the files you already have:
npx content-contract init . --source "docs/**/*.{md,mdx}"
npx content-contract verifyThis mode creates:
content-contract.config.json;- an empty, reviewable
.content-contract-baseline.json; .content-contract/.gitignorefor generated output.
It does not create or edit files under your source glob.
If verification reports existing debt that cannot be fixed in the same change, review the findings and capture that exact set:
npx content-contract baseline
npx content-contract verifyThe baseline matches rule ID, file, source location, and message. A new or
changed finding still fails the gate. Baselines affect verify; render always
blocks on the complete unsuppressed error set.
npx content-contract init ./content-contract-demo
npx content-contract verify --config ./content-contract-demo/content-contract.config.json
npx content-contract render --config ./content-contract-demo/content-contract.config.jsonThe generated review dates are 180 days from initialization. They are intended to expire and should be replaced with dates that match the repository's review policy.
A successful verification ends with output like:
PASS: 2 file(s), 1 fact(s), 0 error(s), 0 warning(s), 0 info message(s), 0 baselined finding(s).
{
"$schema": "https://raw.githubusercontent.com/Jason-Doyle/ContentContractEngine/v0.1.0/schemas/content-contract.config.schema.json",
"version": 1,
"sources": [
{
"id": "docs",
"include": ["docs/**/*.{md,mdx}"],
"frontmatterSchema": "docs/schema.json"
}
],
"baseline": {
"file": ".content-contract-baseline.json"
},
"facts": {
"file": "docs/facts.yaml"
},
"freshness": {
"warningDays": 30
},
"render": {
"outputDirectory": ".content-contract/rendered"
},
"gate": {
"failOn": "error"
}
}Canonical facts remain ordinary reviewable files:
version: 1
facts:
api_version:
value: v2
owner: platform
source: docs/architecture.md
reviewBy: 2027-03-20Content references facts explicitly:
The current API version is {{fact:api_version}}.verify checks that the reference exists. render substitutes the configured
value into the separate output tree. The optional fact source is descriptive
metadata; the engine does not fetch or independently verify it.
| Command | Behavior |
|---|---|
content-contract init [directory] |
Create a complete example project |
content-contract init --source <glob> |
Configure an existing content tree |
content-contract verify |
Run deterministic validators and the release gate |
content-contract baseline |
Replace the configured baseline with current findings |
content-contract render |
Verify without baseline suppression, then render facts |
content-contract explain <rule> |
Explain a stable rule ID or name |
Exit code 0 means success, 1 means the release gate failed, and 2 means
the command or project could not be evaluated.
Both verify and render support --format json. See the
CLI reference and the
verification result schema.
Install the package in the repository and add:
name: Content contract
on:
pull_request:
push:
branches:
- main
permissions:
contents: read
jobs:
verify:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: 22
cache: npm
- run: npm ci
- run: npx --no-install content-contract verify --format jsonimport { verify, type ContentValidator } from 'content-contract-engine';
const validator: ContentValidator = {
name: 'required-owner',
validate: ({ project }) =>
project.documents
.filter((document) => !document.frontmatter.owner)
.map((document) => ({
ruleId: 'ORG001',
ruleName: 'required-owner',
severity: 'error',
file: document.relativePath,
message: 'Content must have an owner.',
help: 'Add owner to the document frontmatter.',
})),
};
const result = await verify('./content-contract.config.json', {
validators: [validator],
});
if (!result.passed) {
process.exitCode = 1;
}The supported exports and schema subpaths are listed in the library API reference.
- Deterministic validators decide pass or fail.
- The built-in engine does not use accounts, telemetry, hosted services, or model providers.
- Rendering never overwrites source documents.
- Configuration, facts, and baselines are reviewable files that can weaken a release gate and should receive the same review as code.
- Findings are sorted and contain a stable rule ID, severity, help text, and a source location when one is meaningful.
- Setup
- CLI reference
- Configuration
- Contract behavior
- Library API
- Architecture
- Validation rules
- Agent workflow
- Security model
- Maintainer releases
- Roadmap and scope
- External URLs are not fetched.
- Referenced assets are validated but not copied during rendering.
- Fact values are inserted verbatim; downstream rendering must escape them for its output context.
- Rendering is atomic per file, not across the complete output tree.
- Source-size limits are not enforced.
- macOS is intended to work but is not in the automated CI matrix.
npm ci
npm run qaSee CONTRIBUTING.md before submitting a change.
Apache License 2.0.