import fs from 'node:fs/promises'; import path from 'node:path'; const FIXTURE_ID_PATTERN = /^PRC-[A-Z]+-\d{3}$/; const SHA256_PATTERN = /^[0-9a-f]{64}$/i; const FORBIDDEN_KEYS = new Set([ 'raw_chat', 'raw_message', 'raw_memory', 'attachment_content', 'email', 'phone', 'openid', 'cookie', 'token', 'secret', 'user_id', 'workspace_path', 'production_url', ]); function collectForbiddenKeys(value, prefix = '') { if (!value || typeof value !== 'object') return []; const found = []; for (const [key, child] of Object.entries(value)) { const field = prefix ? `${prefix}.${key}` : key; if (FORBIDDEN_KEYS.has(key.toLowerCase())) found.push(field); found.push(...collectForbiddenKeys(child, field)); } return found; } export function validateRegressionFixture(fixture, { catalogIds, } = {}) { const errors = []; if (!FIXTURE_ID_PATTERN.test(fixture?.id ?? '')) errors.push('id must match PRC--NNN'); if (!Array.isArray(fixture?.scenario_ids) || fixture.scenario_ids.length === 0) { errors.push('scenario_ids must be a non-empty array'); } else if (catalogIds) { for (const scenarioId of fixture.scenario_ids) { if (!catalogIds.has(scenarioId)) errors.push(`unknown scenario_id ${scenarioId}`); } } for (const field of ['failure_signature', 'synthetic_intent', 'source_batch_id']) { if (!String(fixture?.[field] ?? '').trim()) errors.push(`${field} is required`); } if (!SHA256_PATTERN.test(fixture?.evidence_digest ?? '')) { errors.push('evidence_digest must be an irreversible SHA256'); } if (fixture?.contains_personal_data !== false) { errors.push('contains_personal_data must be false'); } if (!fixture?.privacy_review || !String(fixture.privacy_review.reviewed_by ?? '').trim() || Number.isNaN(Date.parse(fixture.privacy_review.reviewed_at ?? ''))) { errors.push('privacy_review with reviewer and timestamp is required'); } if (!fixture?.automation || !Array.isArray(fixture.automation.command) || fixture.automation.command.length < 2) { errors.push('automation.command must be executable'); } const forbidden = collectForbiddenKeys(fixture); if (forbidden.length) errors.push(`forbidden personal-data fields: ${forbidden.join(',')}`); return { valid: errors.length === 0, errors }; } export async function loadActiveRegressionCorpus({ root, catalog, } = {}) { const corpusRoot = path.join(root, 'scenarios', 'production-regressions'); const manifestPath = path.join(corpusRoot, 'manifests', 'active.json'); let manifest; try { manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8')); } catch (error) { if (error.code === 'ENOENT') { return { status: 'missing', manifestPath, fixtures: [], errors: ['active production regression manifest is missing'], }; } return { status: 'invalid', manifestPath, fixtures: [], errors: [`active production regression manifest is invalid: ${error.message}`], }; } const fixtureIds = manifest?.fixture_ids; if (!Array.isArray(fixtureIds) || fixtureIds.length === 0) { return { status: 'invalid', manifestPath, fixtures: [], errors: ['active manifest must contain at least one fixture_id'], }; } if (new Set(fixtureIds).size !== fixtureIds.length) { return { status: 'invalid', manifestPath, fixtures: [], errors: ['active manifest contains duplicate fixture_ids'], }; } const fixtures = []; const errors = []; const catalogIds = new Set(catalog.map((scenario) => scenario.id)); for (const fixtureId of fixtureIds) { if (!FIXTURE_ID_PATTERN.test(fixtureId)) { errors.push(`invalid fixture id ${fixtureId}`); continue; } const matches = []; for (const bucket of ['agent-run', 'page-data', 'memory', 'wechat', 'files']) { const candidate = path.join(corpusRoot, bucket, `${fixtureId}.json`); try { await fs.access(candidate); matches.push(candidate); } catch { // The fixture belongs to exactly one approved regression bucket. } } if (matches.length !== 1) { errors.push(`${fixtureId} must exist in exactly one regression bucket`); continue; } try { const fixture = JSON.parse(await fs.readFile(matches[0], 'utf8')); const validation = validateRegressionFixture(fixture, { catalogIds }); if (fixture.id !== fixtureId) validation.errors.push(`fixture id does not match ${fixtureId}`); if (validation.errors.length) { errors.push(`${fixtureId}: ${validation.errors.join('; ')}`); } else { fixtures.push({ ...fixture, file: path.relative(root, matches[0]) }); } } catch (error) { errors.push(`${fixtureId}: invalid JSON (${error.message})`); } } return { status: errors.length ? 'invalid' : 'ready', manifestPath, fixtures, errors, }; }