Skip to content

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]
LayerWhereCatches
Pre-spawn injectionworkflow renders $SCHEMA from output-lint prompt --schema X and pastes it into the agent's spawn inputagents that don't follow prose; sets the contract before write
Post-write hard-gateworkflow runs output-lint check --enforce after the agent's Write returnsdrift that slipped past the prompt; exits 1 on violation
np:doctor drift scanwalks .nubos-pilot/milestones/M*/, lints each VERIFICATION/VALIDATION against the current schemalegacy 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.md

Current schemas

SchemaArtefactProducing workflow
verificationM<NNN>-VERIFICATION.md/np:verify-work <N>
validationM<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

  1. Write the schema as lib/schemas/<name>.cjs exporting an object with the shape above.
  2. Register it in lib/schemas/index.cjs (REGISTRY map + optionally inferSchemaForFile heuristic).
  3. 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.
  4. Add Definition of Done rule citing output-lint + ADR-0017.
  5. Add fixture-based tests under lib/fixtures/<artefact>/ and a regression test in lib/<workflow>-parsers.test.cjs (analog lib/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.