Session 5 of voyage-rebrand (V6). Operator-authorized cross-plugin scope. - git mv plugins/ultraplan-local plugins/voyage (rename detected, history preserved) - .claude-plugin/marketplace.json: voyage entry replaces ultraplan-local - CLAUDE.md: voyage row in plugin list, voyage in design-system consumer list - README.md: bulk rename ultra*-local commands -> trek* commands; ultraplan-local refs -> voyage; type discriminators (type: trekbrief/trekreview); session-title pattern (voyage:<command>:<slug>); v4.0.0 release-note paragraph - plugins/voyage/.claude-plugin/plugin.json: homepage/repository URLs point to monorepo voyage path - plugins/voyage/verify.sh: drop URL whitelist exception (no longer needed) Closes voyage-rebrand. bash plugins/voyage/verify.sh PASS 7/7. npm test 361/361.
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'));
|
|
});
|