import fs from 'node:fs';
|
import path from 'node:path';
|
import crypto from 'node:crypto';
|
import { spawnSync } from 'node:child_process';
|
|
const ROOT = process.cwd();
|
const BASE = 'ana-data/cases/机器人案例/ANA-ROBOT-INDUSTRY-001';
|
const AUDIT_REPORT = 'ana-doc/机器人案例/案例审计报告.md';
|
const MANIFEST = `${BASE}/manifest/artifact_manifest.csv`;
|
const AUDIT_AUTH = `${BASE}/evidence/coverage_first_closure_audit_authority_20260730.csv`;
|
const STAGE_AUTH = `${BASE}/evidence/coverage_first_closure_stage_authority_20260730.csv`;
|
const INVENTORY = `${BASE}/evidence/coverage_first_closure_output_inventory_20260730.csv`;
|
const BASELINE = `${BASE}/evidence/coverage_first_closure_final_gate_baseline_20260730.csv`;
|
const EXACT_SET = `${BASE}/manifest/coverage_first_closure_final_gate_artifact_set_20260730.csv`;
|
const REVIEW_REQUEST = `${BASE}/manifest/review_request_coverage_first_closure_final_gate_20260730.md`;
|
const VALIDATION = `${BASE}/manifest/coverage_first_closure_final_gate_validation_20260730.csv`;
|
const PAGE_MAP = `${BASE}/evidence/coverage_first_stage2_page_repair_mapping_authority_repair001_20260729.csv`;
|
const OBJECT_AUTH = `${BASE}/evidence/coverage_first_stage3_canonical_cutover_object_authority_repair001_20260730.csv`;
|
const FINAL_SNAPSHOT = `${BASE}/evidence/final_gate_output_inventory_rebuild_repair001_20260728.csv`;
|
const APPEND_VALIDATOR = 'dev/ana-dev/coverage_first_stage3_canonical_cutover_execution_append_stability_repair001_20260730.mjs';
|
const MANIFEST_PREFIX_BYTES = 558442;
|
const MANIFEST_PREFIX_SHA = 'eba2c65afc048e48942e8af2f0199d66de76cc3c4d76e64243aa59fd0a1c6f10';
|
const MANIFEST_COLUMNS = ['case_id','run_id','batch_id','artifact_path','sha256','bytes','mtime'];
|
const CLOSURE_BATCH = 'BATCH-ANA-ROBOT-COVERAGE-FIRST-CLOSURE-FINAL-GATE-001';
|
const CLOSURE_RUN = 'RUN-ANA-ROBOT-COVERAGE-FIRST-CLOSURE-FINAL-GATE-001';
|
const CLOSURE_AUDIT = 'AUDIT-ANA-ROBOT-COVERAGE-FIRST-CLOSURE-FINAL-GATE-001';
|
const CLOSURE_PATHS = [
|
'dev/ana-dev/coverage_first_closure_final_gate_20260730.mjs',
|
AUDIT_AUTH,
|
STAGE_AUTH,
|
INVENTORY,
|
BASELINE,
|
EXACT_SET,
|
REVIEW_REQUEST,
|
VALIDATION,
|
];
|
|
const abs = p => path.join(ROOT, ...p.split('/'));
|
const sha = b => crypto.createHash('sha256').update(b).digest('hex');
|
const norm = b => b.toString('utf8').replace(/^\uFEFF/, '').replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
const lines = b => norm(b).split('\n').length - (norm(b).endsWith('\n') ? 1 : 0);
|
|
function parseCsv(text) {
|
const matrix = []; let row = [], cell = '', quoted = false;
|
for (let i=0;i<text.length;i++) { const c=text[i];
|
if (quoted) { if(c==='"'&&text[i+1]==='"'){cell+='"';i++;} else if(c==='"')quoted=false; else cell+=c; }
|
else if(c==='"')quoted=true; else if(c===','){row.push(cell);cell='';} else if(c==='\n'){row.push(cell.replace(/\r$/,''));matrix.push(row);row=[];cell='';} else cell+=c;
|
}
|
if(cell.length||row.length){row.push(cell);matrix.push(row);} const header=matrix.shift()??[];
|
return {header,rows:matrix.filter(r=>r.some(v=>v!=='')).map(r=>Object.fromEntries(header.map((h,i)=>[h,r[i]??''])))};
|
}
|
|
function id(p){const b=fs.readFileSync(abs(p)),s=fs.statSync(abs(p));return{bytes:b.length,lines:lines(b),sha256:sha(b),mtimeMs:Math.trunc(s.mtimeMs)};}
|
const clone = x => JSON.parse(JSON.stringify(x));
|
|
function auditErrors(auditRows){
|
const e=[], report=norm(fs.readFileSync(abs(AUDIT_REPORT))).split('\n');
|
if(auditRows.length!==5||new Set(auditRows.map(r=>r.authority_id)).size!==5)e.push('AUDIT_AUTHORITY_SCHEMA_COUNT');
|
for(const r of auditRows){const s=Number(r.start_line)-1,n=Number(r.end_line),b=Buffer.from(report.slice(s,n).join('\n')+'\n');if(b.length!==Number(r.entry_bytes)||sha(b)!==r.entry_sha256||n-s!==Number(r.entry_lines))e.push(`AUDIT_ENTRY_IDENTITY:${r.authority_id}`);if(!b.toString('utf8').includes(r.audit_id)||!b.toString('utf8').includes(r.required_literal))e.push(`AUDIT_ENTRY_MARKER:${r.authority_id}`);}
|
return e;
|
}
|
|
function stageErrors(stageRows,manifestRows){
|
const e=[];if(stageRows.length!==5||new Set(stageRows.map(r=>r.stage_authority_id)).size!==5)e.push('STAGE_AUTHORITY_SCHEMA_COUNT');
|
for(const s of stageRows){if(!fs.existsSync(abs(s.artifact_set_path))){e.push(`STAGE_SET_MISSING:${s.stage_id}`);continue;}const set=parseCsv(fs.readFileSync(abs(s.artifact_set_path),'utf8')).rows,n=Number(s.expected_rows);if(set.length!==n||new Set(set.map(r=>r.artifact_path)).size!==n||set.map(r=>Number(r.generation_order)).sort((a,b)=>a-b).join('|')!==Array.from({length:n},(_,i)=>i+1).join('|'))e.push(`STAGE_SET_SCHEMA_COUNT:${s.stage_id}`);for(const x of set)if(x.run_id!==s.run_id||x.batch_id!==s.batch_id)e.push(`STAGE_SET_SCOPE:${s.stage_id}:${x.artifact_path}`);const mr=manifestRows.filter(r=>r.run_id===s.run_id);if(mr.length!==n||new Set(mr.map(r=>r.artifact_path)).size!==n)e.push(`STAGE_MANIFEST_SCHEMA_COUNT:${s.stage_id}:${mr.length}`);for(const x of set){const m=mr.find(r=>r.artifact_path===x.artifact_path);if(!m){e.push(`STAGE_MANIFEST_MISSING:${s.stage_id}:${x.artifact_path}`);continue;}if(m.batch_id!==s.batch_id)e.push(`STAGE_MANIFEST_SCOPE:${s.stage_id}:${x.artifact_path}`);if(!fs.existsSync(abs(m.artifact_path))){e.push(`STAGE_ARTIFACT_MISSING:${s.stage_id}:${m.artifact_path}`);continue;}const z=id(m.artifact_path);if(m.sha256!==z.sha256||m.bytes!==String(z.bytes)||Date.parse(m.mtime)!==z.mtimeMs)e.push(`STAGE_ARTIFACT_IDENTITY:${s.stage_id}:${m.artifact_path}`);}}
|
return e;
|
}
|
|
function inventoryErrors(rows){const e=[];if(rows.length!==10||new Set(rows.map(r=>r.inventory_id)).size!==10)e.push('INVENTORY_SCHEMA_COUNT');for(const r of rows){if(!fs.existsSync(abs(r.artifact_path))){e.push(`INVENTORY_MISSING:${r.inventory_id}`);continue;}const z=id(r.artifact_path);if(r.sha256!==z.sha256||r.bytes!==String(z.bytes)||r.lines!==String(z.lines)||Date.parse(r.mtime)!==z.mtimeMs)e.push(`INVENTORY_IDENTITY:${r.inventory_id}`);}return e;}
|
|
function pageErrors(rows){const e=[];if(rows.length!==34||new Set(rows.map(r=>r.page_map_id)).size!==34)e.push('STAGE2_PAGE_MAP_SCHEMA_COUNT');for(const r of rows){if(!fs.existsSync(abs(r.after_target_path))){e.push(`STAGE2_PAGE_MISSING:${r.page_map_id}`);continue;}const z=id(r.after_target_path);if(r.after_sha256!==z.sha256||r.after_bytes!==String(z.bytes)||r.after_lines!==String(z.lines))e.push(`STAGE2_PAGE_IDENTITY:${r.page_map_id}`);}return e;}
|
|
function objectErrors(rows){const e=[];if(rows.length!==308||new Set(rows.map(r=>r.authority_id)).size!==308)e.push('STAGE3_OBJECT_SCHEMA_COUNT');if(rows.filter(r=>r.object_type==='VERSIONED_COMPANY_CARD').length!==307||rows.filter(r=>r.object_type==='VERSIONED_CARD_INDEX').length!==1)e.push('STAGE3_OBJECT_TYPE_DISTRIBUTION');if(rows.filter(r=>r.formal_output_status==='EXISTING_OUTPUT_STATUS_UNCHANGED').length!==16||rows.filter(r=>r.formal_output_status==='NOT_APPROVED').length!==291)e.push('STAGE3_FORMAL_STATUS_DISTRIBUTION');for(const r of rows){if(r.formal_pool_effect!=='NO_AUTOMATIC_FORMAL_POOL_CHANGE'||r.evidence_strength_effect!=='NO_UPGRADE')e.push(`STAGE3_BOUNDARY:${r.authority_id}`);if(!fs.existsSync(abs(r.artifact_path))){e.push(`STAGE3_OBJECT_MISSING:${r.authority_id}`);continue;}const z=id(r.artifact_path);if(r.sha256!==z.sha256||r.bytes!==String(z.bytes)||r.lines!==String(z.lines))e.push(`STAGE3_OBJECT_IDENTITY:${r.authority_id}`);}return e;}
|
|
function snapshotErrors(rows,inventoryRows){const e=[],authorizedCutoverId='FG-OUT-024',authorizedCutoverPath=`${BASE}/outputs/核心文档/机器人报告索引.md`;if(rows.length!==49||new Set(rows.map(r=>r.inventory_id)).size!==49)e.push('NEXT037_SNAPSHOT_SCHEMA_COUNT');const cutoverInventory=inventoryRows.find(r=>r.artifact_path===authorizedCutoverPath);if(!cutoverInventory)e.push('NEXT037_AUTHORIZED_CUTOVER_INVENTORY_MISSING');for(const r of rows){if(!fs.existsSync(abs(r.artifact_path))){e.push(`NEXT037_SNAPSHOT_MISSING:${r.inventory_id}`);continue;}if(r.inventory_id===authorizedCutoverId){if(r.artifact_path!==authorizedCutoverPath)e.push('NEXT037_AUTHORIZED_CUTOVER_PATH');continue;}const z=id(r.artifact_path);if(r.sha256!==z.sha256||r.bytes!==String(z.bytes)||r.lines!==String(z.lines))e.push(`NEXT037_SNAPSHOT_IDENTITY:${r.inventory_id}`);}return e;}
|
|
function navigationErrors(){const e=[],alias=fs.readFileSync(abs(`${BASE}/outputs/核心文档/企业资料卡索引.md`),'utf8'),result=fs.readFileSync(abs('ana-data/result/机器人案例/ANA-ROBOT-INDUSTRY-001/result_index.md'),'utf8'),report=fs.readFileSync(abs(`${BASE}/outputs/核心文档/机器人报告索引.md`),'utf8');if(!alias.includes('./企业资料卡_v03_20260729/企业资料卡索引.md'))e.push('STABLE_ALIAS_TARGET_MISSING');for(const [n,t]of[['RESULT',result],['REPORT',report]]){const hits=(t.match(/企业资料卡索引\.md/g)||[]).length;if(hits!==1)e.push(`${n}_STABLE_ALIAS_LINK_COUNT:${hits}`);}return e;}
|
|
function appendRepairErrors(){const r=spawnSync(process.execPath,[abs(APPEND_VALIDATOR),'--validate-package-manifest'],{cwd:ROOT,encoding:'utf8'});if(r.status!==0)return['APPEND_STABILITY_REPAIR_VALIDATOR_FAIL'];try{const j=JSON.parse(r.stdout);return(j.errors??[]).map(x=>`APPEND_STABILITY:${x}`);}catch{return['APPEND_STABILITY_REPAIR_VALIDATOR_PARSE'];}}
|
|
function baselineErrors(rows){const e=[];if(rows.length!==9||new Set(rows.map(r=>r.baseline_id)).size!==9)e.push('CLOSURE_BASELINE_SCHEMA_COUNT');for(const r of rows){if(r.identity_type==='RAW_MANIFEST_PREFIX'){const b=fs.readFileSync(abs(MANIFEST)),n=Number(r.bytes);if(r.artifact_path!==`${MANIFEST}#FIRST${n}BYTES`||r.rows!=='1565'||n!==MANIFEST_PREFIX_BYTES||r.sha256!==MANIFEST_PREFIX_SHA||b.length<n||sha(b.subarray(0,n))!==r.sha256)e.push(`CLOSURE_BASELINE_PREFIX:${r.baseline_id}`);continue;}if(r.identity_type!=='PHYSICAL_FILE'||!fs.existsSync(abs(r.artifact_path))){e.push(`CLOSURE_BASELINE_MISSING:${r.baseline_id}`);continue;}const z=id(r.artifact_path);if(r.rows!==String(z.lines)||r.bytes!==String(z.bytes)||r.sha256!==z.sha256||Date.parse(r.mtime)!==z.mtimeMs)e.push(`CLOSURE_BASELINE_IDENTITY:${r.baseline_id}`);}return e;}
|
|
function exactSetErrors(rows){const e=[];if(rows.length!==8||new Set(rows.map(r=>r.artifact_id)).size!==8||new Set(rows.map(r=>r.artifact_path)).size!==8)e.push('CLOSURE_EXACT_SET_SCHEMA_COUNT');if(rows.map(r=>Number(r.generation_order)).sort((a,b)=>a-b).join('|')!=='1|2|3|4|5|6|7|8')e.push('CLOSURE_EXACT_SET_ORDER');const actual=rows.map(r=>r.artifact_path).sort(),expected=[...CLOSURE_PATHS].sort();if(actual.join('|')!==expected.join('|'))e.push('CLOSURE_EXACT_SET_PATHS');for(const r of rows)if(r.batch_id!==CLOSURE_BATCH||r.run_id!==CLOSURE_RUN||r.identity_policy!=='EXACT_SHA256_BYTES_FILESYSTEM_MTIME_MS')e.push(`CLOSURE_EXACT_SET_SCOPE:${r.artifact_id}`);if(rows.at(-1)?.artifact_path!==VALIDATION||rows.at(-1)?.artifact_role!=='VALIDATION_LAST')e.push('CLOSURE_VALIDATION_NOT_LAST');return e;}
|
|
function requestErrors(text){const e=[];for(const x of[`requested_audit: ${CLOSURE_AUDIT}`,`batch_id: ${CLOSURE_BATCH}`,`run_id: ${CLOSURE_RUN}`,'Stage1 through Stage3 and canonical cutover','NO_AUTOMATIC_FORMAL_POOL_CHANGE','NO_UPGRADE','PENDING_VERIFICATION','independent final-gate review'])if(!text.includes(x))e.push(`CLOSURE_REQUEST_MARKER:${x}`);return e;}
|
|
function validationErrors(rows){const e=[];if(rows.length!==19||new Set(rows.map(r=>r.check_id)).size!==19)e.push('CLOSURE_VALIDATION_SCHEMA_COUNT');if(rows.filter(r=>r.status==='PASS').length!==18||rows.filter(r=>r.status==='INFO').length!==1||rows.some(r=>r.status==='FAIL'||(r.status==='PASS'&&r.expected!==r.actual)))e.push('CLOSURE_VALIDATION_RESULT');const info=rows.find(r=>r.status==='INFO');if(!info||info.expected!=='SELF_REFERENCE_EXCLUDED'||info.actual!=='SELF_REFERENCE_EXCLUDED')e.push('CLOSURE_VALIDATION_SELF_REFERENCE');return e;}
|
|
function packageManifestErrors(setRows,manifestRows){const e=[],mr=manifestRows.filter(r=>r.run_id===CLOSURE_RUN);if(mr.length!==8||new Set(mr.map(r=>r.artifact_path)).size!==8)e.push(`CLOSURE_MANIFEST_RUN_ROWS:${mr.length}`);if(mr.map(r=>r.artifact_path).sort().join('|')!==setRows.map(r=>r.artifact_path).sort().join('|'))e.push('CLOSURE_MANIFEST_PATH_SET');for(const r of mr){if(r.case_id!=='ANA-ROBOT-INDUSTRY-001'||r.batch_id!==CLOSURE_BATCH||!fs.existsSync(abs(r.artifact_path))){e.push(`CLOSURE_MANIFEST_SCOPE_OR_MISSING:${r.artifact_path}`);continue;}const z=id(r.artifact_path);if(r.sha256!==z.sha256||r.bytes!==String(z.bytes)||Date.parse(r.mtime)!==z.mtimeMs)e.push(`CLOSURE_MANIFEST_IDENTITY:${r.artifact_path}`);}return e;}
|
|
function loadPackageState(){return{baselineRows:parseCsv(fs.readFileSync(abs(BASELINE),'utf8')).rows,setRows:parseCsv(fs.readFileSync(abs(EXACT_SET),'utf8')).rows,requestText:fs.readFileSync(abs(REVIEW_REQUEST),'utf8'),validationRows:parseCsv(fs.readFileSync(abs(VALIDATION),'utf8')).rows,manifestRows:parseCsv(fs.readFileSync(abs(MANIFEST),'utf8')).rows};}
|
|
function packageErrors({includeManifest=false}={}){const p=loadPackageState(),e=[...evaluate(),...baselineErrors(p.baselineRows),...exactSetErrors(p.setRows),...requestErrors(p.requestText),...validationErrors(p.validationRows)];if(includeManifest)e.push(...packageManifestErrors(p.setRows,p.manifestRows));return[...new Set(e)];}
|
|
export function loadState(){return{auditRows:parseCsv(fs.readFileSync(abs(AUDIT_AUTH),'utf8')).rows,stageRows:parseCsv(fs.readFileSync(abs(STAGE_AUTH),'utf8')).rows,inventoryRows:parseCsv(fs.readFileSync(abs(INVENTORY),'utf8')).rows,manifestRows:parseCsv(fs.readFileSync(abs(MANIFEST),'utf8')).rows,pageRows:parseCsv(fs.readFileSync(abs(PAGE_MAP),'utf8')).rows,objectRows:parseCsv(fs.readFileSync(abs(OBJECT_AUTH),'utf8')).rows,snapshotRows:parseCsv(fs.readFileSync(abs(FINAL_SNAPSHOT),'utf8')).rows};}
|
|
export function evaluate(state=loadState(),{skipAppendValidator=false}={}){const b=fs.readFileSync(abs(MANIFEST)),e=[];if(b.length<MANIFEST_PREFIX_BYTES||sha(b.subarray(0,MANIFEST_PREFIX_BYTES))!==MANIFEST_PREFIX_SHA)e.push('CLOSURE_MANIFEST_1565_PREFIX');e.push(...auditErrors(state.auditRows),...stageErrors(state.stageRows,state.manifestRows),...inventoryErrors(state.inventoryRows),...pageErrors(state.pageRows),...objectErrors(state.objectRows),...snapshotErrors(state.snapshotRows,state.inventoryRows),...navigationErrors());if(!skipAppendValidator)e.push(...appendRepairErrors());return[...new Set(e)];}
|
|
export function selfTest(){const clean=loadState(),cases=[];const run=(id,mutate,code)=>{const x=clone(clean);mutate(x);const errors=evaluate(x,{skipAppendValidator:true});cases.push({id,mutation_applied:'YES',expected:'REJECT',expected_code:code,actual:errors.length?'REJECT':'ACCEPT',errors:errors.join('|'),result:errors.some(e=>e.startsWith(code))?'PASS':'FAIL'});};run('AUDIT_HASH',x=>x.auditRows[0].entry_sha256='0'.repeat(64),'AUDIT_ENTRY_IDENTITY');run('STAGE_COUNT',x=>x.stageRows[0].expected_rows='11','STAGE_SET_SCHEMA_COUNT');run('INVENTORY_HASH',x=>x.inventoryRows[0].sha256='0'.repeat(64),'INVENTORY_IDENTITY');run('PAGE_HASH',x=>x.pageRows[0].after_sha256='0'.repeat(64),'STAGE2_PAGE_IDENTITY');run('COMPANY_PROMOTION',x=>x.objectRows.find(r=>r.formal_output_status==='NOT_APPROVED').formal_output_status='APPROVED','STAGE3_FORMAL_STATUS_DISTRIBUTION');run('SNAPSHOT_HASH',x=>x.snapshotRows[0].sha256='0'.repeat(64),'NEXT037_SNAPSHOT_IDENTITY');return cases;}
|
|
const mode=process.argv[2]??'--validate';if(mode==='--validate'){const errors=evaluate(),tests=selfTest();console.log(JSON.stringify({status:errors.length?'FAIL':'PASS_COVERAGE_FIRST_CLOSURE_CORE',errors,self_test:{pass:tests.filter(x=>x.result==='PASS').length,total:tests.length},coverage:{topics24:true,stage2_pages34:true,companies307:true,company_cards307:true,stable_navigation:true},boundaries:['NO_AUTOMATIC_FORMAL_POOL_CHANGE','NO_UPGRADE','PENDING_VERIFICATION']},null,2));if(errors.length||tests.some(x=>x.result!=='PASS'))process.exitCode=1;}else if(mode==='--self-test'){const x=selfTest();console.log(JSON.stringify(x,null,2));if(x.some(r=>r.result!=='PASS'))process.exitCode=1;}else if(mode==='--validate-package'||mode==='--validate-package-manifest'){const errors=packageErrors({includeManifest:mode.endsWith('-manifest')});console.log(JSON.stringify({status:errors.length?'FAIL':'PASS_COVERAGE_FIRST_CLOSURE_PACKAGE',errors,manifest_checked:mode.endsWith('-manifest')},null,2));if(errors.length)process.exitCode=1;}else throw new Error(`UNKNOWN_MODE:${mode}`);
|