Wire the two-output roll-up register per the ratified D2 contract (~/.claude/coord/register.md; commons c66ccc3 + status vocab fe6b998), resolving the AS#5 gate the coord notice handed catalog. - Output A: rich LOCAL roll-up grepped from ~/repos/*/STATE.md markers. - Output B: public status-token index, a projection of A, opt-in via a committed .rollup-carrier; absent/private modes excluded by design. - V4 public-safety gate: a carrier line must be exactly "topic: <token>" (no prose, no em-dash, known token) or it is blocked from B. - V5 drift-warn: committed-carrier status != STATE status -> warn; STATE wins. Pure-function core + thin ~/repos CLI shell, zero deps, catalog script pattern. 22/22 tests over the ratified adversarial axes. Commits no carrier/output (AS#5 discipline: catalog is public-mirror class -> mode absent). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NeK9hkxrU9wFPBYGYnSV1V
222 lines
8.9 KiB
JavaScript
222 lines
8.9 KiB
JavaScript
// Tests for the state roll-up register builder (build-rollup-register.mjs).
|
|
// Style mirrors check-okf-parity.test.mjs / check-versions.test.mjs: node:test,
|
|
// pure-function core, zero npm deps. The CLI shell (filesystem glob over ~/repos)
|
|
// is NOT unit-tested here — only the pure builder core the contract lives in.
|
|
//
|
|
// Contract source (ratified): ~/.claude/coord/register.md — two outputs, three
|
|
// builder modes, V4 public-safety gate, V5 drift-warn, closed 7-token status vocab.
|
|
// Axes are catalog-owned; this file encodes them as fixtures.
|
|
//
|
|
// The em-dash (U+2014) is written as '—' throughout to keep the source ASCII-clean
|
|
// and to make the V4 "no em-dash in carrier" axis unambiguous.
|
|
import { test } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import {
|
|
STATUS_VOCAB,
|
|
parseMarkerLine,
|
|
parseCarrierLine,
|
|
validateCarrier,
|
|
computeDrift,
|
|
buildRegister,
|
|
} from './build-rollup-register.mjs';
|
|
|
|
const EMDASH = '—';
|
|
|
|
// --- Status vocabulary: the closed 7-token set (register.md "Status-vokabular") ---
|
|
test('STATUS_VOCAB is exactly the closed 7-token set; "active" is dropped', () => {
|
|
assert.deepEqual(
|
|
[...STATUS_VOCAB].sort(),
|
|
['blocked', 'deferred', 'done', 'in-progress', 'not-applicable', 'partial', 'planned'].sort(),
|
|
);
|
|
assert.ok(!STATUS_VOCAB.includes('active'), '"active" was folded into in-progress and is invalid');
|
|
});
|
|
|
|
// --- parseMarkerLine: Output A source (topic: status — prose) ---
|
|
test('parseMarkerLine: parses "topic: status — prose" from a STATE marker', () => {
|
|
const line = `llm-ingestion-okf: planned ${EMDASH} STEG 3 levert, STEG 4 neste`;
|
|
assert.deepEqual(parseMarkerLine(line), {
|
|
topic: 'llm-ingestion-okf',
|
|
status: 'planned',
|
|
prose: 'STEG 3 levert, STEG 4 neste',
|
|
});
|
|
});
|
|
|
|
test('parseMarkerLine: a status-only marker (no prose) parses with empty prose', () => {
|
|
assert.deepEqual(parseMarkerLine('some-topic: done'), {
|
|
topic: 'some-topic',
|
|
status: 'done',
|
|
prose: '',
|
|
});
|
|
});
|
|
|
|
test('parseMarkerLine: unknown status token -> null (not a marker line)', () => {
|
|
assert.equal(parseMarkerLine(`topic: active ${EMDASH} folded away`), null);
|
|
assert.equal(parseMarkerLine(`# STATE ${EMDASH} title heading`), null);
|
|
assert.equal(parseMarkerLine('just some prose without a marker'), null);
|
|
});
|
|
|
|
// --- parseCarrierLine: Output B source (topic: status ALONE) ---
|
|
test('parseCarrierLine: parses a bare "topic: status" carrier line', () => {
|
|
assert.deepEqual(parseCarrierLine('llm-ingestion-guard: planned'), {
|
|
topic: 'llm-ingestion-guard',
|
|
status: 'planned',
|
|
});
|
|
});
|
|
|
|
test('parseCarrierLine: rejects anything beyond the bare token (prose/em-dash/unknown)', () => {
|
|
assert.equal(parseCarrierLine(`topic: planned ${EMDASH} prose`), null, 'em-dash + prose is not a carrier line');
|
|
assert.equal(parseCarrierLine('topic: planned extra words'), null, 'trailing prose is not a carrier line');
|
|
assert.equal(parseCarrierLine('topic: active'), null, 'unknown token is malformed');
|
|
});
|
|
|
|
// --- V4 public-safety gate: validateCarrier rejects prose / em-dash / unknown ---
|
|
test('V4: a clean carrier (bare topic: token lines) validates ok', () => {
|
|
const text = 'llm-ingestion-guard: planned\nllm-ingestion-okf: not-applicable\n';
|
|
const r = validateCarrier(text);
|
|
assert.equal(r.ok, true);
|
|
assert.deepEqual(r.violations, []);
|
|
assert.deepEqual(r.entries, [
|
|
{ topic: 'llm-ingestion-guard', status: 'planned' },
|
|
{ topic: 'llm-ingestion-okf', status: 'not-applicable' },
|
|
]);
|
|
});
|
|
|
|
test('V4: blank lines are tolerated, not violations', () => {
|
|
const r = validateCarrier('\nllm-ingestion-guard: planned\n\n');
|
|
assert.equal(r.ok, true);
|
|
assert.equal(r.entries.length, 1);
|
|
});
|
|
|
|
test('V4: em-dash in a carrier line is a violation (the core public-safety axis)', () => {
|
|
const r = validateCarrier(`topic: planned ${EMDASH} leaked prose`);
|
|
assert.equal(r.ok, false);
|
|
assert.equal(r.violations.length, 1);
|
|
assert.equal(r.violations[0].reason, 'em-dash');
|
|
assert.equal(r.violations[0].lineNo, 1);
|
|
});
|
|
|
|
test('V4: prose after a valid token (no em-dash) is still a violation', () => {
|
|
const r = validateCarrier('topic: planned some trailing prose');
|
|
assert.equal(r.ok, false);
|
|
assert.equal(r.violations[0].reason, 'prose-after-token');
|
|
});
|
|
|
|
test('V4: an unknown status token is a violation', () => {
|
|
const r = validateCarrier('topic: active');
|
|
assert.equal(r.ok, false);
|
|
assert.equal(r.violations[0].reason, 'unknown-status');
|
|
});
|
|
|
|
test('V4: a structurally malformed line (no "topic: status") is a violation', () => {
|
|
const r = validateCarrier('this is not a marker at all');
|
|
assert.equal(r.ok, false);
|
|
assert.equal(r.violations[0].reason, 'malformed');
|
|
});
|
|
|
|
test('V4: reports every offending line, not just the first', () => {
|
|
const text = `a: planned\nb: active\nc: done ${EMDASH} x\n`;
|
|
const r = validateCarrier(text);
|
|
assert.equal(r.ok, false);
|
|
assert.deepEqual(r.violations.map((v) => v.lineNo), [2, 3]);
|
|
assert.deepEqual(r.violations.map((v) => v.reason), ['unknown-status', 'em-dash']);
|
|
});
|
|
|
|
// --- V5 drift-warn: STATE status != carrier status -> warn, STATE wins ---
|
|
test('V5: agreeing topics produce no drift warning', () => {
|
|
const markers = [{ topic: 't', status: 'planned' }];
|
|
const carrier = [{ topic: 't', status: 'planned' }];
|
|
assert.deepEqual(computeDrift(markers, carrier), []);
|
|
});
|
|
|
|
test('V5: disagreeing topic yields a drift warning carrying both sides', () => {
|
|
const markers = [{ topic: 't', status: 'done' }];
|
|
const carrier = [{ topic: 't', status: 'in-progress' }];
|
|
assert.deepEqual(computeDrift(markers, carrier), [
|
|
{ topic: 't', stateStatus: 'done', carrierStatus: 'in-progress' },
|
|
]);
|
|
});
|
|
|
|
test('V5: a carrier topic absent from STATE is not a drift (only shared topics compared)', () => {
|
|
const markers = [{ topic: 'a', status: 'done' }];
|
|
const carrier = [{ topic: 'b', status: 'planned' }];
|
|
assert.deepEqual(computeDrift(markers, carrier), []);
|
|
});
|
|
|
|
// --- buildRegister: three-mode assembly (register.md "Tre builder-moduser") ---
|
|
function repo(name, markers, carrier, carrierMode) {
|
|
return { name, markers, carrier, carrierMode };
|
|
}
|
|
|
|
test('mode "absent": repo has no carrier -> omitted from Output B, present in Output A', () => {
|
|
const repos = [repo('catalog', [{ topic: 'x', status: 'planned', prose: 'p' }], null, 'absent')];
|
|
const { outputB, outputA } = buildRegister({ repos });
|
|
assert.equal(outputB.length, 0, 'absent repo contributes nothing to public index B');
|
|
assert.equal(outputA.length, 1, 'but A (local) still covers it');
|
|
assert.deepEqual(outputA[0], { repo: 'catalog', topic: 'x', status: 'planned', prose: 'p' });
|
|
});
|
|
|
|
test('mode "committed": a valid carrier projects into Output B (STATE-authoritative status)', () => {
|
|
const repos = [
|
|
repo(
|
|
'guard',
|
|
[{ topic: 'llm-ingestion-guard', status: 'planned', prose: 'B6 detail' }],
|
|
'llm-ingestion-guard: planned\n',
|
|
'committed',
|
|
),
|
|
];
|
|
const { outputB } = buildRegister({ repos });
|
|
assert.deepEqual(outputB, ['llm-ingestion-guard: planned']);
|
|
});
|
|
|
|
test('mode "private": private side-channel carrier stays out of Output B', () => {
|
|
const repos = [
|
|
repo(
|
|
'secret',
|
|
[{ topic: 'z', status: 'in-progress', prose: 'p' }],
|
|
'z: in-progress\n',
|
|
'private',
|
|
),
|
|
];
|
|
const { outputB, outputA } = buildRegister({ repos });
|
|
assert.equal(outputB.length, 0, 'private-mode carrier never reaches the public index');
|
|
assert.equal(outputA.length, 1, 'but it is present in the local A index');
|
|
});
|
|
|
|
test('buildRegister: Output B projects STATE status on drift (STATE wins) and records the drift', () => {
|
|
const repos = [
|
|
repo(
|
|
'r',
|
|
[{ topic: 't', status: 'done', prose: 'finished' }],
|
|
't: in-progress\n', // carrier drifted behind STATE
|
|
'committed',
|
|
),
|
|
];
|
|
const { outputB, drift } = buildRegister({ repos });
|
|
assert.deepEqual(outputB, ['t: done'], 'STATE status wins in the projection, not the stale carrier');
|
|
assert.deepEqual(drift, [{ repo: 'r', topic: 't', stateStatus: 'done', carrierStatus: 'in-progress' }]);
|
|
});
|
|
|
|
test('buildRegister: a V4-violating committed carrier is blocked from B and its violation reported', () => {
|
|
const repos = [
|
|
repo(
|
|
'bad',
|
|
[{ topic: 't', status: 'planned', prose: 'p' }],
|
|
`t: planned ${EMDASH} leaked`,
|
|
'committed',
|
|
),
|
|
];
|
|
const { outputB, violations } = buildRegister({ repos });
|
|
assert.equal(outputB.length, 0, 'a repo whose carrier fails V4 must not pollute the public index');
|
|
assert.equal(violations.length, 1);
|
|
assert.equal(violations[0].repo, 'bad');
|
|
assert.equal(violations[0].reason, 'em-dash');
|
|
});
|
|
|
|
test('buildRegister: Output B is sorted and deterministic across repos', () => {
|
|
const repos = [
|
|
repo('b', [{ topic: 'zeta', status: 'done', prose: 'p' }], 'zeta: done\n', 'committed'),
|
|
repo('a', [{ topic: 'alpha', status: 'planned', prose: 'p' }], 'alpha: planned\n', 'committed'),
|
|
];
|
|
const { outputB } = buildRegister({ repos });
|
|
assert.deepEqual(outputB, ['alpha: planned', 'zeta: done'], 'stable sort so the public index has no spurious diffs');
|
|
});
|