MAJOR
- 9e97cd5 40-validate-standalone.sh: route a target's sc2_gate to its dedicated
gate (config-audit → 50-config-audit-sc2.sh), mirroring 99-dryrun.sh, so --all
no longer falsely FAILs config-audit on the machine-locked v5.0.0 tests.
- 1708e90 99-dryrun.sh: assert EXACTLY one tag survives (F5); a partial tag-strip
no longer silently reports the wrong tag via head -1.
- 4e494c8 99-dryrun.sh: capture the SC2 standalone failing set from the dry-run's
own prepped extract ($dest), not the 40-validate side-effect clean room.
- aeb6292 00-preflight.sh: assert every map path is whitespace/glob-free, making
the word-split path handling in 99-dryrun.sh sound.
- 5d112cb extract the SC6 DROP + SC2 regression detectors into sc6-check.sh /
sc2-regression.sh and add sc-checks.test.mjs — a negative test proving each
detector FIRES (force-fresh re-extraction would undo a planted file-drop).
- 9e588ca 10-extract.sh re-asserts git filter-repo before use (self-heal runs
preflight only on a missing mirror); RUNBOOK lists git-filter-repo + python3>=3.6.
MINOR
- bc0f8a7 plugin-map.json: reset ms-ai-architect blob_strip_safe to null
(00-preflight.sh populates it per run).
- 8d649e9 99-dryrun.sh: gate SC6 behind extract success; a failed extract is
labelled (extract failed), not a content DROP.
- 4044c49 99-dryrun.sh: guard mktemp — an empty capture is an error, not a
false zero-regression PASS.
Also: 00-preflight.test.mjs asserted all 3 'renamed' plugins carry >=2 paths, but
llm-security became single-path in 836b8e9 (copilot was a coexisting plugin, not a
rename) — a stale pre-existing failure. Aligned the test to the ratified map and
added a positive single-path lock against re-introducing the 87-file-drop defect.
Verified: full dry-run 11/11, 0 pushes; sc-checks/99-dryrun/40-validate/00-preflight/
60-rewrite suites green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
69 lines
3.1 KiB
JavaScript
69 lines
3.1 KiB
JavaScript
// Step 1 test — validates plugin-map.json shape (rename-awareness, HTTPS URLs, blob/vendor flags).
|
|
// Pattern: plugins/voyage/tests/validators/*.test.mjs (node:test style, zero deps).
|
|
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { readFileSync } from 'node:fs';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { dirname, join } from 'node:path';
|
|
|
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
const map = JSON.parse(readFileSync(join(here, 'plugin-map.json'), 'utf8'));
|
|
const targets = map.targets;
|
|
const keys = Object.keys(targets);
|
|
|
|
// voyage ← ultraplan-local and linkedin-studio ← linkedin-thought-leadership are TRUE renames, so each
|
|
// carries >=2 --path entries (F1). llm-security is NOT a rename: llm-security-copilot was a SEPARATE,
|
|
// COEXISTING plugin, so llm-security is SINGLE-path (corrected 2026-06-17, commit 836b8e9) — a dual --path
|
|
// there collided at the coexistence commits and dropped 87 files. The map is authoritative; this suite
|
|
// tracks it.
|
|
const RENAMED = ['voyage', 'linkedin-studio'];
|
|
|
|
test('plugin-map.json parses and has exactly 11 targets (10 plugins + DS)', () => {
|
|
assert.equal(keys.length, 11, `expected 11 targets, found ${keys.length}: ${keys.join(', ')}`);
|
|
});
|
|
|
|
test('the renamed plugins (voyage, linkedin-studio) each carry >=2 --path entries (F1)', () => {
|
|
for (const k of RENAMED) {
|
|
assert.ok(targets[k], `missing renamed target ${k}`);
|
|
assert.ok(
|
|
Array.isArray(targets[k].paths) && targets[k].paths.length >= 2,
|
|
`${k} must carry >=2 paths (both names), has ${targets[k].paths?.length}`
|
|
);
|
|
}
|
|
});
|
|
|
|
test('llm-security is SINGLE-path — NOT a rename (copilot was a coexisting plugin, 836b8e9)', () => {
|
|
// Locks in the 87-file-drop correction: re-introducing a 2nd --path here would re-collide at the
|
|
// coexistence commits and silently drop files again.
|
|
assert.equal(
|
|
targets['llm-security'].paths.length, 1,
|
|
`llm-security must stay single-path (dual-path dropped 87 files), has ${targets['llm-security'].paths.length}`
|
|
);
|
|
});
|
|
|
|
test('every target repo_url is https:// (not ssh://) — F2 / #9740', () => {
|
|
for (const k of keys) {
|
|
assert.match(targets[k].repo_url, /^https:\/\/git\.fromaitochitta\.com\/open\//, `${k} repo_url not the expected HTTPS form`);
|
|
assert.doesNotMatch(targets[k].repo_url, /^ssh:\/\//, `${k} repo_url must not be ssh://`);
|
|
}
|
|
});
|
|
|
|
test('ms-ai-architect has blob_strip: true (F3)', () => {
|
|
assert.equal(targets['ms-ai-architect'].blob_strip, true);
|
|
});
|
|
|
|
test('only llm-security + ms-ai-architect have has_vendor_ds: true', () => {
|
|
const vendor = keys.filter((k) => targets[k].has_vendor_ds === true).sort();
|
|
assert.deepEqual(vendor, ['llm-security', 'ms-ai-architect']);
|
|
});
|
|
|
|
test('every target tag is v<semver> (F5 single clean re-tag per repo)', () => {
|
|
for (const k of keys) {
|
|
assert.match(targets[k].tag, /^v\d+\.\d+\.\d+$/, `${k} tag '${targets[k].tag}' is not v<semver>`);
|
|
}
|
|
});
|
|
|
|
test('the removed plugin is dropped, not extracted', () => {
|
|
assert.ok(Array.isArray(map.drop) && map.drop.includes('plugins/ultra-cc-architect/'));
|
|
assert.ok(!keys.includes('ultra-cc-architect'));
|
|
});
|