5 nye validator-moduler (alle m/ CLI-shim for invokering fra commands): - brief-validator.mjs — frontmatter (type, brief_version, task, slug, research_topics, research_status), state machine (research_topics > 0 + skipped requires brief_quality: partial), body sections (Intent/Goal/Success Criteria) - research-validator.mjs — type=ultraresearch-brief, confidence ∈ [0,1], dimensions ≥ 1, body sections, --dir mode for batch validering - plan-validator.mjs — wrapper over plan-schema + manifest-yaml; håndhever step-count == manifest-count, plan_version=1.7 - progress-validator.mjs — schema_version, status enum, current_step in range, step shape, checkResumeReadiness - architecture-discovery.mjs — EKSTERN KONTRAKT: drift-WARN ikke drift-FAIL; tolererer non-canonical filnavn, surfacer loose files som warnings Doc-consistency-test pinning prose vs source-of-truth: - agents/*.md count == CLAUDE.md agent-tabell rader - commands/*.md mentioned i CLAUDE.md - command frontmatter.name == filnavn - templates/plan-template.md plan_version 1.7 invariant - settings.json kun kjente scopes (ultraplan, ultraresearch) - settings.json ingen exploration eller agentTeam (vestigial guard etter Spor 0) - CLAUDE.md refererer alle 4 pipeline-commands Wave 1 + Wave 2 = 108 tester grønn. [skip-docs]: Test-infrastrukturen er ikke user-facing før Spor 1 wiring lander; README/CLAUDE.md oppdateres når commands faktisk endrer atferd (neste commit). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
99 lines
2.4 KiB
JavaScript
99 lines
2.4 KiB
JavaScript
import { test } from 'node:test';
|
|
import { strict as assert } from 'node:assert';
|
|
import { validatePlanContent } from '../../lib/validators/plan-validator.mjs';
|
|
|
|
const VALID_PLAN = `---
|
|
plan_version: "1.7"
|
|
---
|
|
|
|
# Plan
|
|
|
|
## Implementation Plan
|
|
|
|
### Step 1: Add foo
|
|
|
|
- Files: a.ts
|
|
- Manifest:
|
|
\`\`\`yaml
|
|
manifest:
|
|
expected_paths:
|
|
- a.ts
|
|
min_file_count: 1
|
|
commit_message_pattern: "^feat:"
|
|
bash_syntax_check: []
|
|
forbidden_paths: []
|
|
must_contain: []
|
|
\`\`\`
|
|
|
|
### Step 2: Add bar
|
|
|
|
- Files: b.ts
|
|
- Manifest:
|
|
\`\`\`yaml
|
|
manifest:
|
|
expected_paths:
|
|
- b.ts
|
|
min_file_count: 1
|
|
commit_message_pattern: "^feat:"
|
|
bash_syntax_check: []
|
|
forbidden_paths: []
|
|
must_contain: []
|
|
\`\`\`
|
|
`;
|
|
|
|
const FORBIDDEN_PLAN = `---
|
|
plan_version: "1.7"
|
|
---
|
|
|
|
## Fase 1: Drift form
|
|
|
|
content
|
|
`;
|
|
|
|
const STEP_WITHOUT_MANIFEST = `### Step 1: oops
|
|
no manifest
|
|
|
|
### Step 2: ok
|
|
|
|
- Manifest:
|
|
\`\`\`yaml
|
|
manifest:
|
|
expected_paths: [foo]
|
|
min_file_count: 1
|
|
commit_message_pattern: "^x:"
|
|
bash_syntax_check: []
|
|
forbidden_paths: []
|
|
must_contain: []
|
|
\`\`\`
|
|
`;
|
|
|
|
test('validatePlan — strict accepts canonical v1.7 plan', () => {
|
|
const r = validatePlanContent(VALID_PLAN, { strict: true });
|
|
assert.equal(r.valid, true, JSON.stringify(r.errors));
|
|
assert.equal(r.parsed.steps.length, 2);
|
|
assert.equal(r.parsed.planVersion, '1.7');
|
|
});
|
|
|
|
test('validatePlan — forbidden Fase form blocks in strict mode', () => {
|
|
const r = validatePlanContent(FORBIDDEN_PLAN, { strict: true });
|
|
assert.equal(r.valid, false);
|
|
assert.ok(r.errors.find(e => e.code === 'PLAN_FORBIDDEN_HEADING'));
|
|
});
|
|
|
|
test('validatePlan — manifest count mismatch caught', () => {
|
|
const r = validatePlanContent(STEP_WITHOUT_MANIFEST, { strict: true });
|
|
assert.equal(r.valid, false);
|
|
assert.ok(r.errors.find(e => /Step 1/.test(e.message) && /MANIFEST_MISSING/.test(e.code)));
|
|
});
|
|
|
|
test('validatePlan — version warning when missing', () => {
|
|
const noVersion = VALID_PLAN.replace(/plan_version: "1\.7"\n/, '');
|
|
const r = validatePlanContent(noVersion, { strict: true });
|
|
assert.ok(r.warnings.find(w => w.code === 'PLAN_NO_VERSION'));
|
|
});
|
|
|
|
test('validatePlan — older version triggers warning', () => {
|
|
const old = VALID_PLAN.replace('plan_version: "1.7"', 'plan_version: "1.5"');
|
|
const r = validatePlanContent(old, { strict: true });
|
|
assert.ok(r.warnings.find(w => w.code === 'PLAN_VERSION_MISMATCH'));
|
|
});
|