Appearance
Output Schemas
Every workflow that writes a structured markdown artefact (M<NNN>-VERIFICATION.md, M<NNN>-VALIDATION.md, soon also S<NNN>-PLAN.md, T<NNNN>-SUMMARY.md, PROJECT-SUMMARY.md) ships against a schema: a JS object in lib/schemas/<name>.cjs that defines required frontmatter keys, type/enum constraints, cross-field invariants, body header patterns, and required block fields.
The contract is enforced at write-time, not at consumer-time. Drift breaks the producing workflow immediately, not three milestones later.
See ADR-0017 for the design rationale.
Three layers
mermaid
flowchart LR
A[Workflow starts] --> B[output-lint prompt --schema X]
B --> C[Inject schema-prompt into agent spawn]
C --> D[Agent writes artefact]
D --> E[output-lint check --enforce]
E -- ok --> F[Workflow declares success]
E -- violation --> G[exit 1, operator re-spawns agent]| Layer | Where | Catches |
|---|---|---|
| Pre-spawn injection | workflow renders $SCHEMA from output-lint prompt --schema X and pastes it into the agent's spawn input | agents that don't follow prose; sets the contract before write |
| Post-write hard-gate | workflow runs output-lint check --enforce after the agent's Write returns | drift that slipped past the prompt; exits 1 on violation |
np:doctor drift scan | walks .nubos-pilot/milestones/M*/, lints each VERIFICATION/VALIDATION against the current schema | legacy artefacts predating the rule, hand-edits, schema evolutions |
Schema shape
A schema is a plain JS object. Example for M<NNN>-VERIFICATION.md:
js
{
name: 'verification',
artifact: 'M<NNN>-VERIFICATION.md',
description: '…',
frontmatter: {
required: ['schema_version', 'milestone', 'milestone_status', 'sc_total', 'passed', 'failed', 'deferred', 'pending'],
properties: {
milestone_status: { type: 'string', enum: ['verified', 'failed', 'deferred'] },
sc_total: { type: 'integer', minimum: 0 },
// …
},
invariants: [
{
lhs: 'frontmatter.sc_total',
op: '===',
rhs: 'frontmatter.passed + frontmatter.failed + frontmatter.deferred + frontmatter.pending',
message: 'sc_total must equal passed + failed + deferred + pending',
},
],
},
body: {
blocks: {
heading_pattern: '^###\\s+(SC-\\d+):\\s+.+$',
min_count: 1,
heading_forbidden_substring: '[object Object]',
required_fields: [
{ name: 'Status', enum: ['Pass', 'Fail', 'Defer', 'Pending'] },
{ name: 'Classified by' },
{ name: 'Evidence' },
],
},
patterns: [
{ pattern: '^\\*\\*Milestone Status:\\*\\*\\s+(verified|failed|deferred)\\b', flags: 'm', min: 1 },
],
},
}CLI
bash
# List registered schemas
node .nubos-pilot/bin/np-tools.cjs output-lint list
# Render schema as markdown prompt (for spawn injection)
node .nubos-pilot/bin/np-tools.cjs output-lint prompt --schema verification
# Lint a file — emits JSON by default
node .nubos-pilot/bin/np-tools.cjs output-lint check --file path/to/M001-VERIFICATION.md --schema verification
# Text format with hard-fail exit code (used by workflows)
node .nubos-pilot/bin/np-tools.cjs output-lint check --file path/to/M001-VERIFICATION.md --schema verification --enforce --text
# Schema is inferred from filename when omitted
node .nubos-pilot/bin/np-tools.cjs output-lint check --file path/to/M001-VALIDATION.mdCurrent schemas
| Schema | Artefact | Producing workflow |
|---|---|---|
verification | M<NNN>-VERIFICATION.md | /np:verify-work <N> |
validation | M<NNN>-VALIDATION.md | /np:validate-phase <N> |
Additional schemas onboard when their producing workflow integrates the rule. See ADR-0017 §Migration plan.
Adding a new schema
- Write the schema as
lib/schemas/<name>.cjsexporting an object with the shape above. - Register it in
lib/schemas/index.cjs(REGISTRYmap + optionallyinferSchemaForFileheuristic). - Wire the producing workflow:
- Pre-spawn:
SCHEMA=$(node .nubos-pilot/bin/np-tools.cjs output-lint prompt --schema <name>)→ inject into agent prompt. - Post-write:
output-lint check --file <path> --schema <name> --enforce --text→ exit 1 on violation.
- Pre-spawn:
- Add
Definition of Donerule citingoutput-lint+ ADR-0017. - Add fixture-based tests under
lib/fixtures/<artefact>/and a regression test inlib/<workflow>-parsers.test.cjs(analoglib/archive-parsers.test.cjs).
Hard-fail contract
A violation makes the workflow exit 1. The operator's response is to re-spawn the producing agent with the violation list as feedback, never to edit the artefact by hand to satisfy the linter. Hand-edits hide the underlying agent-prompt or schema bug.
