Files
msd-core/tests/capability-matrix-sync.test.cjs
Tom Boucher b0bd2f7a48 chore: move committed-generated-artifact freshness checks to lint:ci (#2000)
gsd-test's build leg runs the full 'npm run build' (which regenerates
capability-registry.cjs, loop-host-contract.cjs, package-identity.cjs, etc.),
so committed-freshness guards that lived in the unit suite were masked there:
gsd-test passed a stale-commit that CI's shard-1/3 test then red-flagged
(caught live on PR #1998). The mandated pre-push gate was green on a commit
CI correctly flagged.

Move the committed-state --check guards into a new 'lint:generated-sync'
script wired into lint:ci (the single orchestrated entry point the lint-tests
CI job already runs on a build:lib-only tree, so the committed artifacts are
checked without regeneration). gsd-test no longer contains these guards, so
it can no longer mask them.

- package.json: add lint:generated-sync (7 generators --check); wire into lint:ci.
- generate-package-identity.cjs: add --check mode (was the only generator
  without it); no-arg behaviour unchanged (still writes, as build expects).
- Remove the committed-freshness guards from the unit suite, keeping all
  behavioral/structural tests:
    - capability-registry.test.cjs: drop the --check describe.
    - loop-host-contract.test.cjs: drop the committed-file staleness test
      (keep the normalizeLineEndings unit test).
    - capability-matrix-sync.test.cjs: drop --check + byte-for-byte (keep the
      architectural content invariants: every cap appears, security ship:pre).
    - issue-844-manifest-version-sync.test.cjs: drop describe D (--check).
    - issue-498-package-identity.test.cjs: drop the drift-check test (keep
      behavioral module-export tests); drop the now-unused render import and
      its allow-test-rule exemption (allowlist ratcheted 175 -> 174).
2026-07-03 19:37:14 -04:00

47 lines
2.5 KiB
JavaScript

'use strict';
/**
* capability-matrix-sync.test.cjs — ADR-1244 Phase 6 (D9) content invariants.
*
* The committed docs/reference/capability-matrix.md FRESHNESS guard
* (`gen-capability-matrix.cjs --check` + byte-for-byte) moved to
* `npm run lint:generated-sync`, where it runs against the committed file in
* both local and CI lint lanes — instead of being masked by gsd-test's
* `npm run build` leg regenerating artifacts. What remains here are the
* architectural CONTENT invariants the matrix must satisfy regardless of how
* freshness is enforced: every first-party capability appears as a row, and
* the rendered extension points reflect the registry (not placeholders).
*/
const { describe, test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const ROOT = path.resolve(__dirname, '..');
const MATRIX = path.join(ROOT, 'docs', 'reference', 'capability-matrix.md');
const registry = require('../gsd-core/bin/lib/capability-registry.cjs');
describe('capability-matrix content invariants (ADR-1244 Phase 6)', () => {
test('every first-party capability in the registry appears as a matrix row', () => {
const md = fs.readFileSync(MATRIX, 'utf8');
for (const cap of Object.values(registry.capabilities)) {
if (cap.role !== 'feature' && cap.role !== 'runtime') continue;
assert.ok(md.includes('`' + cap.id + '`'), `capability ${cap.id} (${cap.role}) must appear in the matrix`);
}
});
test('extension points + hook kinds reflect the registry byLoopPoint index (not placeholders)', () => {
const md = fs.readFileSync(MATRIX, 'utf8');
// The stub used "see capability.json" placeholders — the generated matrix must not.
assert.ok(!md.includes('see capability.json'), 'matrix must show real extension points, not placeholders');
// `security` registers a gate at ship:pre — a hard architectural invariant. Assert the precondition
// UNCONDITIONALLY (so this never degrades to a vacuous pass if the registry changes), then assert the
// rendered row reflects it.
const shipPreGates = (registry.byLoopPoint['ship:pre'] && registry.byLoopPoint['ship:pre'].gates) || [];
assert.ok(shipPreGates.some((g) => g.capId === 'security'), 'precondition: security registers a ship:pre gate in the registry');
const securityRow = md.split(/\r?\n/).find((l) => l.includes('`security`') && l.includes('|'));
assert.ok(securityRow && securityRow.includes('`ship:pre`'), 'security row must list its real ship:pre extension point');
});
});