check-contract-coverage.test.mjs

Contract coverage tests

Tier 2engineering

Test that each contract check catches its target issue and stays quiet on valid cases.

Add to your project
Add "Contract coverage tests" from ntent to this repo.

Fetch https://ntent.app/r/f/check-contract-coverage-test as plain text. Write it verbatim to scripts/check-contract-coverage.test.mjs.

It needs check-contract-coverage.mjs and contract.json. If this repo does not have them, stop and tell me.

Then check it: node --test passes. Break one reference in the contract by hand and exactly one test goes red, naming the check that caught it.

Reads https://ntent.app/r/f/check-contract-coverage-test

Read the code
check-contract-coverage.test.mjsscripts/check-contract-coverage.test.mjs
545 lines
/**
 * Tests for the reference checks, which are the half of this script that gates
 * a commit. The coverage number is a nag and reads the filesystem, so it is
 * checked by running it; these three are pure functions over a parsed contract
 * and are the part a person cannot verify by reading.
 *
 * What is worth locking down is narrow. Not that a finding is worded well, but
 * that each check FIRES on the shape it exists for and stays quiet on the shape
 * it does not, because a check that never fires and a check that always fires
 * are both deleted within a month.
 *
 * The last two tests are the contract with the pre-commit hook: the gate is the
 * exit code, not the output, and the hook has to work in a repo that has never
 * installed TypeScript.
 *
 * Run, installed:  node --test scripts/check-contract-coverage.test.mjs
 * Run, in this repo: node --test files/scripts/check-contract-coverage.test.mjs
 */

import test from 'node:test'
import assert from 'node:assert/strict'
import { execFileSync } from 'node:child_process'
import { mkdtempSync, mkdirSync, writeFileSync, readFileSync, existsSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join, dirname } from 'node:path'
import { fileURLToPath } from 'node:url'

import { checkReferences, refTarget, fieldName, stripComments, VOCABULARY } from './check-contract-coverage.mjs'

const here = dirname(fileURLToPath(import.meta.url))
const SCRIPT = join(here, 'check-contract-coverage.mjs')

/**
 * The contract these tests read, wherever this file ended up.
 *
 * This file ships to your repo as scripts/check-contract-coverage.test.mjs, and
 * it used to load '../examples/contract.json' — a path that only exists inside
 * the package it was authored in. Installed, it threw ENOENT before the first
 * assertion, which meant the tests passed here and were dead everywhere they
 * were actually needed. Any test that reads a file has to say where that file is
 * in a REPO, not in the source tree it was written in.
 *
 * Installed, the second candidate wins and these run against your own contract,
 * which is the better subject anyway: it is the file your gate is protecting.
 */
const CONTRACT_CANDIDATES = [
  join(here, '..', 'examples', 'contract.json'), // this package's own layout
  join(here, '..', 'contract.json'), // installed: scripts/ sits beside it
  join(process.cwd(), 'contract.json'), // run from anywhere else in the repo
]

const contractPath = CONTRACT_CANDIDATES.find(existsSync)
if (!contractPath) {
  throw new Error(
    `No contract.json found. Looked in:\n  ${CONTRACT_CANDIDATES.join('\n  ')}\n` +
      `These tests check a real contract, so put yours at the repo root.`
  )
}
const EXAMPLE = JSON.parse(readFileSync(contractPath, 'utf8'))

/** The shipped example, with one thing broken. Every case reads the same. */
const withEdit = (edit) => {
  const contract = structuredClone(EXAMPLE)
  edit(contract)
  return checkReferences(contract)
}

const journey = (contract, id) => contract.journeys.find((j) => j.id === id)
const where = (findings) => findings.map((f) => f.where)

/**
 * A complete acceptance record. `built` is refused without one, so every test
 * that builds a journey to probe some OTHER check has to carry it, or the
 * finding it is looking for arrives with a second one about acceptance.
 */
const ACCEPTED = { on: '2026-09-01', by: 'Test', commit: 'abc1234', evidence: ['manual: exercised in the test'] }
const build = (j) => Object.assign(j, { state: 'built', accepted: ACCEPTED })

// ------------------------------------------------------------ the example ---

test('the shipped example resolves, so a fresh install starts green', () => {
  assert.deepEqual(checkReferences(EXAMPLE), [])
})

test('every finding names a fix, because a gate without a remedy gets bypassed', () => {
  const findings = withEdit((c) => {
    c.rules[0].applies_to = ['credit_note']
    c.journeys[0].actor = 'ops_admin'
    c.entities[0].fields.push({ type: 'money' })
    build(journey(c, 'refund-payment'))
  })
  assert.equal(findings.length, 4, 'four unrelated breakages report separately, not first-one-wins')
  assert.equal(new Set(where(findings)).size, 4)
  for (const f of findings) {
    assert.ok(f.fix && f.fix.length > 20, `${f.where} has no usable fix line`)
    assert.ok(f.message, `${f.where} has no message`)
  }
})

// ------------------------------------------ 0. identity and vocabulary ---

test('two entities with one id is a finding, because every reference to it resolves to whichever one you picked', () => {
  const findings = withEdit((c) => c.entities.push({ id: 'invoice', fields: ['nonce'] }))
  assert.deepEqual(where(findings), ['entities.invoice'])
  assert.match(findings[0].message, /declared twice/)
})

test('a duplicate is caught in every pillar, not only entities', () => {
  const findings = withEdit((c) => {
    c.journeys.push({ id: 'pay-invoice', actor: 'customer', entities: ['invoice'], state: 'unbuilt' })
    c.rules.push({ ...c.rules[0] })
    c.open_questions.push({ ...c.open_questions[0] })
  })
  assert.deepEqual(where(findings), ['journeys.pay-invoice', 'rules.BR-014', 'open_questions.OQ-009'])
})

test('a state or status outside the vocabulary is a finding, since every check downstream would read it leniently', () => {
  const findings = withEdit((c) => {
    journey(c, 'pay-invoice').state = 'done'
    c.open_questions[0].status = 'closed'
  })
  assert.deepEqual(where(findings), ['journeys.pay-invoice.state', 'open_questions.OQ-009.status'])
  assert.match(findings[0].fix, new RegExp(VOCABULARY['journeys[].state'].join(', ')))
})

test('an item with no id at all is a finding rather than a crash', () => {
  const findings = withEdit((c) => c.actors.push({ description: 'anonymous' }))
  assert.deepEqual(where(findings), ['actors[4]'])
})

// ------------------------------------------ 1. cross-references resolve ---

test('a rule binding an entity nobody declared is a finding', () => {
  const findings = withEdit((c) => c.rules[0].applies_to.push('credit_note'))
  assert.deepEqual(where(findings), ['rules.BR-014.applies_to'])
  assert.match(findings[0].message, /does not resolve to an entity/)
  assert.match(findings[0].fix, /Known: invoice, customer, subscription, payment/)
})

test('a journey naming an actor nobody declared is a finding', () => {
  const findings = withEdit((c) => (c.journeys[0].actor = 'ops_admin'))
  assert.deepEqual(where(findings), ['journeys.create-invoice.actor'])
  assert.match(findings[0].message, /does not resolve to an actor/)
})

test('an access row is checked on both axes, so a typo in either is caught', () => {
  const findings = withEdit((c) => {
    c.access.matrix[0].actor = 'finance-admin'
    c.access.matrix[1].entity = 'invoices'
  })
  assert.deepEqual(where(findings), ['access.matrix[0].actor', 'access.matrix[1].entity'])
})

test('a constraint or entitlement pointing at a journey that no longer exists is a finding', () => {
  const findings = withEdit((c) => {
    c.constraints[1].affects = ['journey:pay-invoices']
    c.entitlements.gates[0].gates = ['journey:brand-invoice']
  })
  assert.deepEqual(where(findings), ['entitlements.ENT-001.gates', 'constraints.C-002.affects'])
})

test('a prefix decides which pillar a bare id is looked up in', () => {
  assert.deepEqual(refTarget('BR-014'), ['rule', 'BR-014'])
  assert.deepEqual(refTarget('OQ-009'), ['question', 'OQ-009'])
  assert.deepEqual(refTarget('ENT-002'), ['entitlement', 'ENT-002'])
  assert.deepEqual(refTarget('C-001'), ['constraint', 'C-001'])
  assert.deepEqual(refTarget('invoice'), ['entity', 'invoice'], 'unprefixed means entity')
  assert.deepEqual(refTarget('journey:pay-invoice'), ['journey', 'pay-invoice'])
})

test('a missing pillar says to add the pillar, not to check the spelling', () => {
  const findings = withEdit((c) => delete c.actors)
  assert.ok(findings.length > 0)
  assert.match(findings[0].fix, /There is no actors array/)
})

test('a field object with no name is a finding, since everything downstream reads the name', () => {
  const findings = withEdit((c) => c.entities[0].fields.push({ type: 'money' }))
  assert.equal(findings.length, 1)
  assert.match(findings[0].where, /entities\.invoice\.fields\[\d+\]/)
})

test('a field is a bare string or an object, and both give up the same name', () => {
  assert.equal(fieldName('tax_rate'), 'tax_rate')
  assert.equal(fieldName({ name: 'tax_rate', type: 'decimal' }), 'tax_rate')
  assert.equal(fieldName({ type: 'decimal' }), undefined, 'so the check above can catch it')
})

// -------------------------------------------- 2. rule ids cited in prose ---

test('renaming a rule that a journey gap still cites is a finding', () => {
  const findings = withEdit((c) => (c.rules[0].id = 'BR-114'))
  assert.ok(
    findings.some((f) => f.where === 'cited in prose' && f.message.startsWith('BR-014')),
    'the citation lives in a sentence, which is where renames get missed'
  )
})

test('a retired id still resolves once the rule that absorbed it says so', () => {
  const findings = withEdit((c) => (c.rules[1].supersedes = []))
  assert.deepEqual(
    findings.map((f) => f.message),
    ['BR-007 is referred to but is not in rules', 'BR-018 is referred to but is not in rules'],
    'without supersedes, a consolidated id dies silently'
  )
  assert.deepEqual(checkReferences(EXAMPLE), [], 'with it, both resolve')
})

// ------------------------------------------------------ 3. open questions ---

test('citing a question that is not in the file is a finding', () => {
  const findings = withEdit((c) => (c.open_questions = c.open_questions.filter((q) => q.id !== 'OQ-011')))
  assert.ok(findings.some((f) => f.message.startsWith('OQ-011')))
})

test('a journey cannot be built while a blocking question it stands on is open', () => {
  const findings = withEdit((c) => (build(journey(c, 'refund-payment'))))
  assert.deepEqual(where(findings), ['journeys.refund-payment'])
  assert.match(findings[0].message, /marked built while OQ-011 is open and blocking/)
})

test('answering the question is what unblocks it, and so is dropping the blocking flag', () => {
  const answered = withEdit((c) => {
    build(journey(c, 'refund-payment'))
    c.open_questions.find((q) => q.id === 'OQ-011').status = 'resolved'
  })
  assert.deepEqual(answered, [], 'resolved is the honest way out')

  const unflagged = withEdit((c) => {
    build(journey(c, 'refund-payment'))
    c.open_questions.find((q) => q.id === 'OQ-011').blocking = false
  })
  assert.deepEqual(unflagged, [], 'so is deciding it does not block, which is a visible edit')
})

test('a question is only blocking where it was declared blocking', () => {
  const findings = withEdit((c) => {
    build(journey(c, 'refund-payment'))
    journey(c, 'refund-payment').gap = undefined
    const q = c.open_questions.find((q) => q.id === 'OQ-011')
    q.blocking = false
    q.status = 'open'
  })
  assert.deepEqual(findings, [], 'an open question that nobody called blocking does not stop a build')
})

test('an unbuilt journey may stand on as many open questions as it likes', () => {
  assert.deepEqual(checkReferences(EXAMPLE), [], 'refund-payment is unbuilt and cites OQ-011')
})

test('a blocking question on an entitlement gate blocks the journeys that gate names', () => {
  // OQ-009 blocks ENT-002, and ENT-002 gates create-invoice. The gate's
  // at-limit state is undecided, so the screen that shows it cannot be done.
  const findings = withEdit((c) => build(journey(c, 'create-invoice')))
  assert.deepEqual(where(findings), ['journeys.create-invoice'])
  assert.match(findings[0].message, /OQ-009.*blocks ENT-002, which the journey stands on/)
})

test('a blocking question on a rule blocks a journey only where the journey says it depends on that rule', () => {
  // The example's gap line cites OQ-009 in prose, which is its own reason to
  // block. Cleared here so the only path left is the dependency.
  const built = (c) => Object.assign(journey(c, 'create-invoice'), { state: 'built', gap: undefined, accepted: ACCEPTED })
  const dependent = withEdit((c) => {
    built(c)
    c.open_questions.find((q) => q.id === 'OQ-009').blocks = ['BR-014']
  })
  assert.deepEqual(where(dependent), ['journeys.create-invoice'], 'create-invoice lists BR-014 in depends_on')
  assert.match(dependent[0].message, /blocks BR-014, which the journey stands on/)

  const unrelated = withEdit((c) => {
    built(c)
    c.open_questions.find((q) => q.id === 'OQ-009').blocks = ['BR-030']
  })
  assert.deepEqual(unrelated, [], 'BR-030 applies to invoice too, and that is inference, not a dependency')
})

test('a blocking question on an entity blocks the built journeys that list that entity', () => {
  const findings = withEdit((c) => {
    c.open_questions.find((q) => q.id === 'OQ-009').blocks = ['customer']
  })
  assert.deepEqual(where(findings), [], 'no built journey lists customer')
  const hit = withEdit((c) => {
    c.open_questions.find((q) => q.id === 'OQ-009').blocks = ['payment']
  })
  assert.deepEqual(where(hit).sort(), ['journeys.pay-invoice', 'journeys.settle-charge'])
})

test('depends_on is resolved like any other reference', () => {
  const findings = withEdit((c) => (journey(c, 'create-invoice').depends_on = ['C-999']))
  assert.deepEqual(where(findings), ['journeys.create-invoice.depends_on'])
  assert.match(findings[0].message, /does not resolve to a constraint/)
})

// --------------------------------------------------------- 4. acceptance ---

test('the shipped example says who found each built journey working', () => {
  const built = EXAMPLE.journeys.filter((j) => j.state === 'built')
  assert.ok(built.length > 0, 'the example has something built, or none of this is exercised')
  for (const j of built) assert.ok(j.accepted, `${j.id} is built and carries its record`)
})

test('a journey marked built with no acceptance record is a finding', () => {
  const findings = withEdit((c) => (journey(c, 'pay-invoice').accepted = undefined))
  assert.deepEqual(where(findings), ['journeys.pay-invoice'])
  assert.match(findings[0].message, /no acceptance record/)
  assert.match(findings[0].fix, /"accepted"/, 'the fix shows the shape rather than describing it')
})

test('an incomplete record names the fields it is missing', () => {
  const findings = withEdit((c) => (journey(c, 'pay-invoice').accepted = { on: '2026-09-04', evidence: [] }))
  assert.deepEqual(where(findings), ['journeys.pay-invoice.accepted'])
  assert.match(findings[0].message, /missing by, commit, evidence/)
})

test('a piece of evidence has to say something', () => {
  const findings = withEdit((c) => (journey(c, 'pay-invoice').accepted.evidence = ['']))
  assert.match(findings[0].message, /missing evidence/)
})

test('the date has to be a date, so the record can be read against the log', () => {
  const findings = withEdit((c) => (journey(c, 'pay-invoice').accepted.on = 'last Thursday'))
  assert.deepEqual(where(findings), ['journeys.pay-invoice.accepted.on'])
})

test('a partial journey needs no record, and an unfinished one may keep an old one', () => {
  const partial = withEdit((c) =>
    Object.assign(journey(c, 'pay-invoice'), { state: 'partial', gap: 'the receipt download is a 404', accepted: undefined })
  )
  assert.deepEqual(partial, [], 'moving a journey back is the honest way out and costs nothing')
  const kept = withEdit((c) => (journey(c, 'send-invoice').accepted = ACCEPTED))
  assert.deepEqual(kept, [], 'a record on an unfinished journey is history, not a claim')
})

test('a record from before the model moved on is reported as stale, not failed', (t) => {
  // settle-charge in the example was accepted against 0.3.0 and the contract
  // is at 0.4.0. That is worth knowing and is not a contradiction: the journey
  // may still be fine. So it is a note in the report and never a finding.
  assert.deepEqual(checkReferences(EXAMPLE), [])
  const json = runRefs(EXAMPLE, ['--json'], { refs: false })
  if (json.code === 2) return t.skip('TypeScript is not installed here, so the full report cannot run')
  const byId = Object.fromEntries(JSON.parse(json.stdout).acceptance.map((a) => [a.id, a]))
  assert.equal(byId['settle-charge'].contractMoved, true)
  assert.equal(byId['pay-invoice'].contractMoved, false)
  assert.equal(byId['pay-invoice'].commitKnown, null, 'a scratch directory has no git to ask, and that is not a no')
})

// ------------------------------------------------------- the UI pointer ---

test('a trailing comment does not count as a mention', () => {
  // `return null } // total_amount` used to score the field as on screen,
  // because only comments that started a line were stripped.
  const src = 'export default function Page() { return null } // total_amount\n'
  assert.doesNotMatch(stripComments(src), /total_amount/)
})

test('stripping comments leaves strings, urls and regex literals alone', () => {
  const src = [
    "const u = 'https://example.com/total_amount' // not this one",
    'const r = /\\/\\/(total)/ /* nor this */',
    'const t = `a // ${b}` ',
    'const d = a / b // ratio',
  ].join('\n')
  const out = stripComments(src)
  assert.match(out, /https:\/\/example\.com\/total_amount/)
  assert.match(out, /\/\\\/\\\/\(total\)\//, 'the regex literal survives')
  assert.match(out, /`a \/\/ \$\{b\}`/, 'the template literal survives')
  assert.doesNotMatch(out, /not this one|nor this|ratio/)
})

// ------------------------------------------------- the gate, end to end ---

const runRefs = (contract, extra = [], { refs = true } = {}) => {
  const dir = mkdtempSync(join(tmpdir(), 'contract-'))
  writeFileSync(join(dir, 'contract.json'), JSON.stringify(contract))
  const args = [SCRIPT, ...(refs ? ['--refs'] : []), ...extra]
  try {
    const stdout = execFileSync(process.execPath, args, { cwd: dir, encoding: 'utf8' })
    return { code: 0, stdout }
  } catch (e) {
    return { code: e.status, stdout: e.stdout }
  }
}

test('a broken reference fails in --json too', () => {
  const broken = structuredClone(EXAMPLE)
  build(journey(broken, 'refund-payment'))

  const json = runRefs(broken, ['--json'])
  assert.equal(json.code, 1, 'the exit code cannot depend on the output format')
  assert.equal(JSON.parse(json.stdout).broken.length, 1)
})

test('the coverage floor is enforced in --json as well as in the table', (t) => {
  // The bug: the JSON branch returned before the threshold comparison, so a CI
  // job asking for a machine-readable report AND enforcement got exit 0 below
  // its own floor. Asking for a different output format is not asking for a
  // different answer, and the format the robots read decides the build.
  //
  // The temp dir has a contract and no source, so type coverage is 0% and any
  // floor above zero has to fail.
  const text = runRefs(EXAMPLE, ['--check', '--min', '1'], { refs: false })
  if (text.code === 2) {
    // The full run reads types, so it needs a compiler. --refs, which is the
    // half that gates a commit, does not, and it is covered below either way.
    return t.skip('TypeScript is not installed here, so the type layer cannot run')
  }
  const json = runRefs(EXAMPLE, ['--check', '--min', '1', '--json'], { refs: false })
  assert.equal(text.code, 1, '0% is below a 100% floor')
  assert.equal(json.code, 1, 'and it is still below it when you ask for JSON')

  const report = JSON.parse(json.stdout)
  assert.equal(report.belowFloor, true)
  assert.equal(report.ok, false, 'the report says so as well as the exit code')

  // The example maps three of its four entities. Under --check the fourth is
  // a failure until somebody maps it or excuses it with a reason, because the
  // number is not about the whole contract until then.
  const unmapped = runRefs(EXAMPLE, ['--check', '--min', '0', '--json'], { refs: false })
  assert.equal(unmapped.code, 1)
  assert.equal(JSON.parse(unmapped.stdout).unmappedScope, true, 'payment is in the contract and in no map')

  // Only the mapped entities, and nothing pointing at the fourth.
  const mapped = { entities: EXAMPLE.entities.filter((e) => e.id !== 'payment') }
  const green = runRefs(mapped, ['--check', '--min', '0', '--json'], { refs: false })
  assert.equal(green.code, 0, 'and a floor it clears passes, so this is not just always-fail')
  assert.equal(JSON.parse(green.stdout).unmappedScope, false)
})

test('a name in the right place is not coverage: the counterexample from the methodology review', (t) => {
  // A money field declared boolean, a fixture carrying the key under a
  // different object with a null value, and a page that returns null with the
  // field name in a trailing comment. This scored 100% at every layer.
  const dir = mkdtempSync(join(tmpdir(), 'contract-'))
  const write = (rel, text) => {
    mkdirSync(join(dir, dirname(rel)), { recursive: true })
    writeFileSync(join(dir, rel), text)
  }
  const contract = {
    entities: [
      { id: 'invoice', fields: [{ name: 'total_amount', type: 'money', required: true }, 'due_date'] },
      { id: 'audit_event', fields: ['actor', 'at'] },
    ],
    journeys: [],
  }
  write('contract.json', JSON.stringify(contract))
  write('src/types/invoice.ts', 'export interface Invoice { total_amount: boolean; due_date: string }')
  write('fixtures/invoice.json', JSON.stringify({ customer: { total_amount: null }, due_date: '2026-01-01' }))
  write('src/app/invoices/page.tsx', 'export default function Page() { return null } // total_amount due_date')

  const run = (extra = []) => {
    try {
      return { code: 0, stdout: execFileSync(process.execPath, [SCRIPT, '--json', ...extra], { cwd: dir, encoding: 'utf8' }) }
    } catch (e) {
      return { code: e.status, stdout: e.stdout }
    }
  }
  const first = run(['--check', '--min', '1'])
  if (first.code === 2) return t.skip('TypeScript is not installed here, so the type layer cannot run')

  const report = JSON.parse(first.stdout)
  const invoice = report.rows.find((r) => r.id === 'invoice')
  assert.equal(invoice.type, 50, 'total_amount is declared boolean, which a money field cannot be')
  assert.deepEqual(invoice.mismatched, [{ field: 'total_amount', contract: 'money', declared: 'boolean' }])
  assert.equal(invoice.fixture, 50, 'a null under another object is not the field seen with data')
  assert.equal(invoice.ui, 0, 'a trailing comment is not a mention')
  assert.equal(report.ok, false)

  // The unmapped entity is outside the number and, under --check, a failure.
  assert.deepEqual(report.unmeasured, ['audit_event'])
  assert.deepEqual(report.measured, { fields: 2, of: 4 })
  assert.equal(report.unmappedScope, true)
  assert.equal(first.code, 1)
})

test('a field on a base interface counts, and a field the contract keeps off screen leaves the UI column', (t) => {
  const dir = mkdtempSync(join(tmpdir(), 'contract-'))
  const write = (rel, text) => {
    mkdirSync(join(dir, dirname(rel)), { recursive: true })
    writeFileSync(join(dir, rel), text)
  }
  write(
    'contract.json',
    JSON.stringify({
      entities: [
        {
          id: 'invoice',
          fields: ['invoice_number', { name: 'total_amount', type: 'money' }, { name: 'created_at', ui: false, why: 'audit only' }],
        },
      ],
    })
  )
  write(
    'src/types/invoice.ts',
    [
      'interface Timestamped { created_at: string }',
      'type Numbered = { invoice_number: string }',
      'export interface Invoice extends Timestamped { total_amount: number }',
      'export type InvoiceDetail = Numbered & Invoice',
    ].join('\n')
  )
  write('src/app/invoices/page.tsx', 'export const Page = (i: Invoice) => <b>{i.invoice_number} {i.total_amount}</b>')

  let stdout
  try {
    stdout = execFileSync(process.execPath, [SCRIPT, '--json'], { cwd: dir, encoding: 'utf8' })
  } catch (e) {
    if (e.status === 2) return t.skip('TypeScript is not installed here')
    stdout = e.stdout
  }
  const invoice = JSON.parse(stdout).rows[0]
  assert.equal(invoice.type, 100, 'inherited and intersected members are declared members')
  assert.equal(invoice.ui, 100, 'created_at is off screen by decision, so it is not a UI gap')
  assert.deepEqual(invoice.offScreen, [{ field: 'created_at', why: 'audit only' }])
})

test('--refs exits 0 on a contract that resolves and 1 on one that does not', () => {
  assert.equal(runRefs(EXAMPLE).code, 0, 'the exit code IS the gate; the output is for the human')

  const broken = structuredClone(EXAMPLE)
  journey(broken, 'refund-payment').state = 'built'
  const run = runRefs(broken)
  assert.equal(run.code, 1)
  assert.match(run.stdout, /BROKEN REFERENCES/)
  assert.match(run.stdout, /fix: Answer OQ-011/)
})

test('--refs never loads TypeScript, so the hook works before the first install', () => {
  const source = readFileSync(SCRIPT, 'utf8')
  assert.doesNotMatch(
    source,
    /^const ts = require\('typescript'\)/m,
    'a top-level require would crash --refs in a repo that has not installed it'
  )
  assert.doesNotMatch(
    source,
    /^import .*['"]typescript['"]/m,
    'and neither would a top-level import'
  )
  assert.match(
    source,
    /const typescript = \(\) => \{[\s\S]*require\('typescript'\)/,
    'it is loaded inside the accessor, on the first parse that needs it'
  )
})

Success check: node --test passes. Break one reference in the contract by hand and exactly one test goes red, naming the check that caught it.

Included in