| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990 |
- #!/usr/bin/env node
- const fs = require('fs');
- const os = require('os');
- const path = require('path');
- const { spawnSync } = require('child_process');
- const ROOT = path.resolve(__dirname, '..');
- function main() {
- const outputDir = fs.mkdtempSync(path.join(os.tmpdir(), 'tihao-plan-execution-status-'));
- const result = spawnSync(process.execPath, [
- 'scripts/plan-execution-status.js',
- '--output',
- outputDir,
- '--strict'
- ], {
- cwd: ROOT,
- encoding: 'utf8',
- shell: false
- });
- if (result.stdout) process.stdout.write(result.stdout);
- if (result.stderr) process.stderr.write(result.stderr);
- assert(result.status === 0, 'plan execution status should pass');
- const summaryPath = path.join(outputDir, 'plan-execution-status-summary.json');
- const reportPath = path.join(outputDir, 'plan-execution-status.md');
- const csvPath = path.join(outputDir, 'plan-execution-status.csv');
- assert(fs.existsSync(summaryPath), 'summary should exist');
- assert(fs.existsSync(reportPath), 'report should exist');
- assert(fs.existsSync(csvPath), 'csv should exist');
- const summary = JSON.parse(fs.readFileSync(summaryPath, 'utf8').replace(/^\uFEFF/, ''));
- const report = fs.readFileSync(reportPath, 'utf8').replace(/^\uFEFF/, '');
- const csv = fs.readFileSync(csvPath, 'utf8').replace(/^\uFEFF/, '');
- const sections = summary.rows.map(row => row.section);
- const operatorPack = readJson(path.join(ROOT, 'outputs', 'proof-gap-operator-pack-latest', 'proof-gap-operator-pack-summary.json'));
- const businessExecution = readJson(path.join(ROOT, 'outputs', 'business-execution-index-latest', 'business-execution-index-summary.json'));
- const expectedOwnerArtifactCount = Number(operatorPack?.ownerArtifactCount || operatorPack?.ownerArtifacts?.length || summary.operatorPackOwnerArtifactCount || 0);
- const expectedBusinessOwnerArtifactRows = Number(businessExecution?.operatorPackOwnerArtifactRowCount || expectedOwnerArtifactCount);
- assert(summary.passed === true, 'summary should pass structural checks');
- assert(summary.complete === false, 'summary should not claim full completion while proof gaps are open');
- assert(summary.rowCount === 8, 'summary should map the eight plan sections');
- assert(summary.proofOpenCount >= 1, 'summary should expose open proof gaps');
- assert(summary.operatorPackOwnerArtifactCount === expectedOwnerArtifactCount, 'summary should expose proof-gap operator owner attachment count');
- assert(summary.businessExecutionOperatorPackOwnerArtifactRowCount === expectedBusinessOwnerArtifactRows, 'summary should expose business execution operator attachment rows');
- assert(Array.isArray(summary.operatorPackOwnerArtifacts) && summary.operatorPackOwnerArtifacts.length === expectedOwnerArtifactCount, 'summary should carry proof-gap operator owner attachments');
- assert(summary.files.report.includes('plan-execution-status.md'), 'summary should expose report path');
- assert(summary.guardrails.some(item => item.includes('不证明客户效果')), 'guardrails should preserve customer-effect boundary');
- for (const section of ['核心判断', '当前已具备能力', '关键差距', '长跑优化任务', '软件端表格验收', '质量验收标准', '给 AI 的优化任务', '每轮沉淀']) {
- assert(sections.includes(section), `missing plan section row: ${section}`);
- assert(report.includes(section), `report should include section: ${section}`);
- }
- assert(summary.rows.some(row => row.section === '关键差距' && row.status === 'open'), 'key gaps should stay open until real proof closes');
- assert(summary.rows.some(row => row.section === '每轮沉淀' && row.status === 'passed'), 'round deposition row should pass');
- assert(report.includes('# 提号长期优化计划执行状态'), 'report should be Chinese');
- assert(report.includes('proofOpenCount'), 'report should show proof open count');
- assert(report.includes('operatorPackOwnerArtifactCount'), 'report should show operator owner attachment evidence');
- assert(report.includes('by-owner/business.md'), 'report should show business owner operator attachment');
- assert(report.includes('不得宣称命中率提升'), 'report should include no-claim boundary');
- assert(csv.startsWith('顺序,计划章节,状态'), 'csv should use Chinese software-friendly headers');
- assertNoSecretsOrMojibake(report);
- assertNoSecretsOrMojibake(csv);
- console.log(JSON.stringify({
- ok: true,
- outputDir,
- rowCount: summary.rowCount,
- proofOpenCount: summary.proofOpenCount
- }, null, 2));
- }
- function assertNoSecretsOrMojibake(text) {
- assert(!/sk-[A-Za-z0-9_-]{20,}/.test(text), 'output should not contain model token patterns');
- assert(!/r:[A-Za-z0-9]{20,}/.test(text), 'output should not contain Parse sessionToken patterns');
- assert(!/Authorization\s*[:=]/i.test(text), 'output should not expose Authorization header text');
- assert(!/\uFFFD/.test(text), 'output should not contain replacement characters');
- assert(!/[閹荤粵閺嶉崣鐟欑拠閻╂潏闁导缂傞弬閸熸矮娑斾梗]{3,}/.test(text), 'output should not contain common mojibake');
- }
- function readJson(file) {
- if (!fs.existsSync(file)) return null;
- return JSON.parse(fs.readFileSync(file, 'utf8').replace(/^\uFEFF/, ''));
- }
- function assert(condition, message) {
- if (!condition) throw new Error(message);
- }
- if (require.main === module) main();
|