Deterministic linter for OpenProse programs — a Markdown-based language for multi-agent orchestration. A fast, compiled counterweight to the agent-native execution model: it checks the static, spec-driven parts of the language that should stay deterministic even when execution is delegated to an LLM.
git submodule update --init --recursive # fetch the vendored spec (required)
cargo run -- lint path/to/program.md # lint one program (or pass a directory)Each file reports ok or its diagnostics, then a profile summary. Diagnostics
print as file:line:col severity CODE message:
my-program.md:11:1 warning MDW011 Ensures clause uses hedging language (should/might/may); ensures are obligations, not suggestions
profile: compat
0 error(s), 1 warning(s) across 1 file(s)
Errors fail the run (exit 1); warnings do not. Build details are below; see Lint Rules and Exit Codes for the code families.
The OpenProse spec is vendored as a git submodule, so initialize submodules first:
git submodule update --init --recursive
cargo buildThe crate is not published to crates.io (publish = false). After cargo build, the
binary is at ${CARGO_TARGET_DIR:-target}/debug/openprose-lint; the examples below
write openprose-lint as shorthand. To run a subcommand without installing it on
your PATH, prefer cargo run -- <subcommand> (e.g. cargo run -- lint path/to/program.md).
# Lint current OpenProse programs (.md format)
openprose-lint lint path/to/program.md
openprose-lint lint path/to/programs/
openprose-lint lint --program-dir path/to/program-directory/
# Preflight briefing for VM agents (~100-200 token structured analysis, scales with program size)
openprose-lint briefing path/to/program.md
# Spec gap discovery (undocumented vocabulary in a corpus)
openprose-lint discover path/to/programs/
# Runtime capability requirements for a program or program directory
openprose-lint capabilities path/to/program.md
openprose-lint capabilities path/to/program-directory/
# Compare program requirements against a runtime declaration
openprose-lint capabilities --runtime-manifest specs/runtime-subjects/pi-no-extensions-self-declared.json path/to/program.md
# Validate a deterministic OpenProse adapter manifest
openprose-lint adapter validate specs/adapters/pi-v1-md.json
# Dogfood an adapter-initialized Claude Code run against a stable local program fixture
openprose-lint adapter dogfood specs/adapters/claude-code-v1-md.json \
fixtures/adapter/parallel-reviews \
--input-file code=tests/fixtures/get_user_records.py \
--expect-binding synthesizer/report
# The proof report keeps separate wire/execute system-append artifacts so
# phase-specific adapter prompts stay auditable.
# Conformance: run the vendored conformance suite (specs/conformance/)
openprose-lint conformance
# List available spec sources
openprose-lint specs
# Verify a spec identity manifest against hashes, a git pin, and package provenance
openprose-lint specs verify --manifest path/to/spec-version.json \
--root path/to/skill/open-prose \
--git-repo path/to/prose-checkout \
--expect-repo openprose/prose \
--expect-commit <git-sha> \
--package-json path/to/node_modules/@openprose/reactor/package.jsonThe public linting surface is intentionally small: lint for current Markdown programs and
lint-legacy for archived imperative programs. Version- or generation-suffixed command names are
not part of the public interface.
The briefing command outputs a versioned, structured markdown block designed to be read by a Prose-Complete VM agent before execution. It contains pre-parsed contract, service resolution, feature flags, and diagnostic summary — putting the agent in compiler headspace with deterministic structural analysis.
<!-- openprose-lint briefing v1 -->
## example-job-daily
kind: program | services: 4 | imports: 3
### contract
requires:
- company_name: the fleet operator to research
- gate_level: (optional, default "external") review level
ensures:
- brief, delivered, dashboard_updated
errors: (none)
environment:
- SLACK_WEBHOOK_URL
- SLACK_BOT_TOKEN
- REVIEW_CHANNEL
### services
example-discovery → inline
human-gate → use: std/delivery/human-gate
slack-notifier → use: std/delivery/slack-notifier
dashboard-builder → use: std/delivery/dashboard-builder
### features
environment: yes | use-imports: yes | run-inputs: no | execution-block: yes
### diagnostics
0 errors, 0 warnings
See docs/specs/2026-04-08-preflight-briefing-design.md for the full design spec.
docs/README.md— map of maintainer docs and retention rulesdocs/specs/2026-04-08-preflight-briefing-design.md— deterministic VM preflight schemadocs/specs/2026-04-15-runtime-conformance-model.md— terminology, capability profiles, and certification model for OpenProse runtimesdocs/specs/2026-04-16-adapter-manifest-model.md— deterministic coding-agent initialization model for OpenProse adaptersdocs/adapting-and-self-verifying-a-runtime.md— practical workflow for adapting a new CLI, dogfooding it in tmux, and externally verifying runtime support claims without overclaiming completenessspecs/conformance-capability-schema.json— machine-readable capability vocabulary, profile definitions, and dependency graph (vocab v0.1.0)specs/adapter-manifest-schema.json— machine-readable adapter manifest schema for deterministic initializationspecs/runtime-subjects/— example self-declared runtime capability manifestsspecs/adapters/— example deterministic adapter manifests for Pi, Codex CLI, and Claude Code
The linter vendors openprose/prose as a git submodule:
- submodule:
reference/openprose-prose - VM spec:
reference/openprose-prose/skills/open-prose/prose.md - Forme spec:
reference/openprose-prose/skills/open-prose/forme.md - Deps spec:
reference/openprose-prose/skills/open-prose/deps.md - pin: see
specs/openprose.json
Build-time vocabulary extraction reads the compiler spec at reference/openprose-prose/skills/open-prose/compiler/index.prose.md during cargo build and generates spec_vocab.rs. Bump the submodule to update: cd reference/openprose-prose && git fetch origin && git checkout origin/main && cd ../..
openprose-lint specs verify checks a spec identity manifest without relying on
package version alone. The manifest records:
- the OpenProse spec id and source repo,
- the OpenProse skill version and
runtime_contract, - optional package provenance such as
@openprose/reactorversions, - SHA-256 digests for the load-bearing skill/spec files.
Package versions are labels; artifact hashes and the pinned git checkout are the contract. Direct manifest mode works for package bundles, scratch checkouts, and release candidates:
openprose-lint specs verify --manifest skill/open-prose/spec-version.json \
--root skill/open-prose \
--expect-repo openprose/prose \
--package-json package.jsonIf a manifest declares package versions, pass matching --package-json paths for
each declared package. Otherwise verification fails rather than treating package
provenance as implicitly checked.
For git-pinned source checks, pass the checkout, expected repo, and expected commit explicitly:
openprose-lint specs verify --manifest skills/open-prose/spec-version.json \
--root skills/open-prose \
--git-repo . \
--expect-repo openprose/prose \
--expect-commit "$(git rev-parse HEAD)"--spec mode uses the registry repo and pin as the expected source identity.
When the registry entry declares paths.version_manifest, the verifier reads
that manifest. Otherwise it synthesizes a source-identity check from the
registry-declared load-bearing paths and verifies those artifact blobs against
the pinned submodule commit. The fallback also emits a non-failing
source_capabilities section so source-layout changes show up as discovered
feature docs instead of as implicit parser assumptions:
openprose-lint specs verify --spec openproseUse direct manifest mode for package bundles and release candidates that need to
verify package provenance such as @openprose/reactor versions.
The manifest may omit source.commit when it is committed inside the same git
tree; the external pin from --expect-commit or specs/openprose.json avoids a
self-referential commit hash. Packaged bundles may include source.commit
because the package is assembled after the source commit exists.
When --git-repo is supplied, --root must live inside that checkout. The
verifier also reads SKILL.md frontmatter and checks that its version and
runtime_contract match the manifest. Git-pinned checks compare each artifact
digest to the blob stored at the expected commit, so uncommitted worktree bytes
cannot masquerade as pinned source. Unknown future runtime_contract values
fail closed until this linter is updated, and artifact paths must resolve to
regular files without symlinked components under the declared root.
compat(default): accepts current syntax plus known historical corpus patternsstrict: also flags spec deviations thatcompattolerates, such as missing frontmatterversionfields. Warnings do not fail the run.
openprose-lint lint --profile strict path/to/program.mdOpenProse previously used an imperative .prose format. Most new users should ignore it, but archived programs can still be checked explicitly. Do not document or depend on private generation-suffixed aliases; use the public commands below.
openprose-lint lint-legacy path/to/file.prose0: no lint errors1: one or more lint errors2: CLI usage or filesystem error
Warnings do not fail the run.
Errors:
MDE001–MDE009: structural (frontmatter delimiters)MDE010–MDE019: required frontmatter fieldsMDE020–MDE029: body structureMDE030–MDE039: component validationMDE040–MDE049: cross-validation (single-file)MDE050–MDE059: cross-validation (multi-file)
Warnings:
MDW001–MDW009: frontmatter vocabularyMDW010–MDW019: contract qualityMDW020–MDW029: component qualityMDW030–MDW039: cross-validation warnings
The public lint command targets the current declarative Markdown language. A legacy path remains for archived imperative .prose programs:
- Current Markdown linting: parses
.mdprograms, including heading classification, contract parsing (## Contractand bare labels), service resolution, anduse:import awareness. This is exposed asopenprose-lint lint. - Legacy compatibility linting: parses archived
.prosefiles. Vocabulary is extracted from the pinned compiler spec at build time viabuild.rs->spec_vocab.rs, with fallback support for older v0 compiler layouts. This is exposed asopenprose-lint lint-legacy. - Briefing generation: reuses the current Markdown parser to produce structured preflight analysis for VM agents.
capabilities.rs: infers runtime capability requirements from a program, emits implied substrate dependencies, and can compare them against a runtime manifest.adapter.rs: validates deterministic adapter manifests that pin exact OpenProse files, channels, and phase attachments.adapter_dogfood.rs: turns the pinned Claude Code adapter into a repeatable live proof run by rendering the exact prompts/files, staging a temp program copy, validating the root VM's final JSON proof payloads, checking reported published outputs on disk, surfacing observed hook/tool-use evidence, and verifying the expected published binding is actually present and non-empty.
- LSP:
cargo buildalso produces anopenprose-lsplanguage-server binary for editor diagnostics (live lint + hover). - WASM: the linter library compiles to WebAssembly (
cargo build --target wasm32-unknown-unknown --lib);src/wasm.rsexportslint/hoverfor embedding in external editors and tools. It has no in-repo consumer andscripts/ci.shonly compiles it, so treat it as a stable but lightly maintained integration surface.
Verification runs as a local gate, not GitHub Actions: bash scripts/ci.sh runs fmt, installs the pinned Bun tooling, runs true-up gate and a spec-hygiene check, then runs clippy, tests, build, and the conformance + lint smoke checks. Activate the pre-push hook with git config core.hooksPath .githooks. See CONTRIBUTING.md.
This is an official OpenProse project at github.com/openprose/prose-lint. It lints the OpenProse language defined at github.com/openprose/prose.
MIT — see LICENSE.