import fs from 'node:fs' import path from 'node:path' const root = process.cwd() const errors = [] const manifest = JSON.parse(fs.readFileSync(path.join(root, 'generated/screen-manifest.json'), 'utf8')) for (const required of ['COMMON-OPS-001', 'COMMON-REC-001']) { if (!manifest.some(x => x.id === required)) errors.push(`missing required operations screen ${required}`) } const migration = fs.readFileSync(path.join(root, 'backend/Database/Migrations/20260808_005_kbx_operations_reconcile.sql'), 'utf8') for (const token of ['kbx.work_items', 'kbx.work_item_audit', 'kbx.reconcile_items', 'identity_key', 'reference_no', 'source_screen_id', 'source_version']) { if (!migration.includes(token)) errors.push(`operations migration missing ${token}`) } function walk(dir) { if (!fs.existsSync(dir)) return [] return fs.readdirSync(dir, { withFileTypes: true }).flatMap(entry => { const full = path.join(dir, entry.name) return entry.isDirectory() ? walk(full) : [full] }) } const operationalBackend = [ ...walk(path.join(root, 'backend/Modules/Common/Operations')), ...walk(path.join(root, 'backend/Modules/Common/Reconcile')), ].filter(x => x.endsWith('.cs')) const forbiddenDomainWrite = /\b(?:update|insert\s+into|delete\s+from)\s+(?:oms_|wms_|erp_)/i for (const file of operationalBackend) { const text = fs.readFileSync(file, 'utf8') if (forbiddenDomainWrite.test(text)) errors.push(`${path.relative(root, file)} writes a source-module table directly`) } const projection = fs.readFileSync(path.join(root, 'backend/Shared/Operations/OperationsProjectionWriter.cs'), 'utf8') if (!projection.includes('source_version')) errors.push('work-item projection lacks source_version out-of-order protection') const reconcile = fs.readFileSync(path.join(root, 'backend/Shared/Operations/ReconcileProjectionWriter.cs'), 'utf8') if (!reconcile.includes('excluded.occurred_at >= kbx.reconcile_items.occurred_at')) errors.push('reconcile projection lacks stale-observation protection') if (errors.length) { console.error('KBX operations validation failed:') for (const error of errors) console.error(`- ${error}`) process.exit(1) } console.log(`KBX operations validation passed (${operationalBackend.length} operational backend files).`)