refresh-round-artifacts.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. #!/usr/bin/env node
  2. const fs = require('fs');
  3. const path = require('path');
  4. const { spawnSync } = require('child_process');
  5. const ROOT = path.resolve(__dirname, '..');
  6. function main() {
  7. const args = parseArgs(process.argv.slice(2));
  8. const outputRoot = path.resolve(args.outputRoot || path.join(ROOT, 'outputs'));
  9. const strict = args.strict !== false;
  10. fs.mkdirSync(outputRoot, { recursive: true });
  11. const steps = [
  12. {
  13. id: 'proof-gap-software-form',
  14. command: ['node', ['scripts/export-proof-gap-software-form.js', '--output', path.join(outputRoot, 'tihao-proof-gap-software-form-latest'), '--strict']]
  15. },
  16. {
  17. id: 'proof-gap-request',
  18. command: ['node', ['scripts/generate-proof-gap-request.js', '--output', path.join(outputRoot, 'proof-gap-request-latest')]]
  19. },
  20. {
  21. id: 'proof-gap-closure',
  22. command: ['node', ['scripts/proof-gap-closure-audit.js', '--output', path.join(outputRoot, 'proof-gap-closure-latest')]]
  23. },
  24. {
  25. id: 'feedback-loop-closure',
  26. command: ['node', ['scripts/feedback-loop-closure-audit.js', '--output', path.join(outputRoot, 'feedback-loop-closure-latest')]]
  27. },
  28. {
  29. id: 'homepage-evidence-readiness',
  30. command: ['node', ['scripts/homepage-evidence-readiness-audit.js', '--output', path.join(outputRoot, 'homepage-evidence-readiness-latest')]]
  31. },
  32. {
  33. id: 'intake-readiness',
  34. command: ['node', [
  35. 'scripts/intake-readiness-audit.js',
  36. '--data-pack',
  37. path.join(outputRoot, 'data-intake-pack-latest'),
  38. '--video-pack',
  39. path.join(outputRoot, 'video-intake-pack-latest'),
  40. '--output',
  41. path.join(outputRoot, 'intake-readiness-latest')
  42. ]]
  43. },
  44. {
  45. id: 'video-resource-readiness',
  46. command: ['node', [
  47. 'scripts/video-resource-readiness-audit.js',
  48. '--input',
  49. path.join(outputRoot, 'video-intake-pack-latest', 'video-resource-template.csv'),
  50. '--output',
  51. path.join(outputRoot, 'video-resource-readiness-latest')
  52. ]]
  53. },
  54. {
  55. id: 'live-preflight',
  56. command: ['node', [
  57. 'scripts/live-preflight.js',
  58. '--output',
  59. path.join(outputRoot, 'live-preflight-latest'),
  60. '--strict',
  61. '--allow-failure'
  62. ]]
  63. },
  64. {
  65. id: 'longrun-readiness',
  66. command: ['node', [
  67. 'scripts/long-run-readiness-audit.js',
  68. '--mode',
  69. 'full-matrix',
  70. '--intake-readiness',
  71. path.join(outputRoot, 'intake-readiness-latest', 'intake-readiness-summary.json'),
  72. '--proof-gap',
  73. path.join(outputRoot, 'proof-gap-request-latest', 'proof-gap-request-summary.json'),
  74. '--proof-gap-closure',
  75. path.join(outputRoot, 'proof-gap-closure-latest', 'proof-gap-closure-summary.json'),
  76. '--output',
  77. path.join(outputRoot, 'long-run-readiness-latest')
  78. ]]
  79. },
  80. {
  81. id: 'intake-field-checklist',
  82. command: ['node', [
  83. 'scripts/intake-field-checklist.js',
  84. '--data-pack',
  85. path.join(outputRoot, 'data-intake-pack-latest'),
  86. '--video-pack',
  87. path.join(outputRoot, 'video-intake-pack-latest'),
  88. '--output',
  89. path.join(outputRoot, 'intake-field-checklist-latest')
  90. ]]
  91. },
  92. {
  93. id: 'manual-review-label-guide',
  94. command: ['node', ['scripts/manual-review-label-guide.js', '--output', path.join(outputRoot, 'manual-review-label-guide-latest'), '--strict']]
  95. },
  96. {
  97. id: 'proof-gap-operator-pack',
  98. command: ['node', ['scripts/proof-gap-operator-pack.js', '--outputs', outputRoot, '--output', path.join(outputRoot, 'proof-gap-operator-pack-latest'), '--strict']]
  99. },
  100. {
  101. id: 'business-proof-progress',
  102. command: ['node', ['scripts/business-proof-progress.js', '--outputs', outputRoot, '--output', path.join(outputRoot, 'business-proof-progress-latest')]]
  103. },
  104. {
  105. id: 'business-proof-next-actions',
  106. command: ['node', ['scripts/business-proof-next-actions.js', '--outputs', outputRoot, '--output', path.join(outputRoot, 'business-proof-next-actions-latest')]]
  107. },
  108. {
  109. id: 'optimization-completion',
  110. command: ['node', ['scripts/optimization-completion-audit.js', '--outputs', outputRoot, '--output', path.join(outputRoot, 'optimization-completion-latest')]]
  111. },
  112. {
  113. id: 'business-execution-index',
  114. command: ['node', ['scripts/business-execution-index.js', '--outputs', outputRoot, '--output', path.join(outputRoot, 'business-execution-index-latest'), '--strict']]
  115. },
  116. {
  117. id: 'optimization-status',
  118. command: ['node', ['scripts/optimization-status-audit.js', '--output', path.join(outputRoot, 'optimization-status-latest')]]
  119. },
  120. {
  121. id: 'plan-execution-status',
  122. command: ['node', ['scripts/plan-execution-status.js', '--outputs', outputRoot, '--output', path.join(outputRoot, 'plan-execution-status-latest'), '--strict']]
  123. },
  124. {
  125. id: 'real-proof-intake-bundle',
  126. command: ['node', ['scripts/real-proof-intake-bundle.js', '--outputs', outputRoot, '--output', path.join(outputRoot, 'real-proof-intake-bundle-latest'), '--strict']]
  127. },
  128. {
  129. id: 'software-client-list',
  130. command: ['node', ['scripts/refresh-software-client-list.js', '--outputs', outputRoot, '--output', path.join(outputRoot, 'software-client-latest')]]
  131. },
  132. {
  133. id: 'local-seed-material-index',
  134. command: ['node', ['scripts/local-seed-material-index.js', '--outputs', outputRoot, '--output', path.join(outputRoot, 'local-seed-material-index-latest')]]
  135. },
  136. {
  137. id: 'local-seed-to-intake-worklist',
  138. command: ['node', ['scripts/local-seed-to-intake-worklist.js', '--outputs', outputRoot, '--output', path.join(outputRoot, 'local-seed-to-intake-worklist-latest')]]
  139. },
  140. {
  141. id: 'real-gap-material-audit',
  142. command: ['node', ['scripts/real-gap-material-audit.js', '--outputs', outputRoot, '--output', path.join(outputRoot, 'real-gap-material-audit-latest'), '--strict']]
  143. },
  144. {
  145. id: 'real-proof-closure-work-order',
  146. command: ['node', ['scripts/real-proof-closure-work-order.js', '--outputs', outputRoot, '--output', path.join(outputRoot, 'real-proof-closure-work-order-latest'), '--strict']]
  147. },
  148. {
  149. id: 'latest-form-index',
  150. command: ['node', ['scripts/latest-form-index.js', '--outputs', outputRoot, '--output', path.join(outputRoot, 'latest-form-index-latest'), '--strict']]
  151. },
  152. {
  153. id: 'optimization-handoff',
  154. command: ['node', ['scripts/generate-optimization-handoff.js', '--outputs', outputRoot, '--output', path.join(outputRoot, 'optimization-handoff-latest')]]
  155. },
  156. {
  157. id: 'experience-transcript-index',
  158. command: ['node', ['scripts/experience-transcript-index.js', '--output', path.join(outputRoot, 'experience-transcript-index-latest')]]
  159. },
  160. {
  161. id: 'evidence-index',
  162. command: ['node', ['scripts/evidence-index.js', '--outputs', outputRoot, '--output', path.join(outputRoot, 'evidence-index-latest')]]
  163. },
  164. {
  165. id: 'round-deposition',
  166. command: ['node', ['scripts/round-deposition-audit.js', '--outputs', outputRoot, '--output', path.join(outputRoot, 'round-deposition-latest'), '--strict']]
  167. }
  168. ];
  169. const results = steps.map(step => runStep(step, strict));
  170. const summary = {
  171. generatedAt: new Date().toISOString(),
  172. outputRoot,
  173. strict,
  174. passed: results.every(item => item.status === 'pass'),
  175. steps: results,
  176. artifacts: {
  177. proofGapSoftwareForm: rel(path.join(outputRoot, 'tihao-proof-gap-software-form-latest', 'tihao-proof-gap-software-form-summary.json')),
  178. proofGapRequest: rel(path.join(outputRoot, 'proof-gap-request-latest', 'proof-gap-request-summary.json')),
  179. proofGapClosure: rel(path.join(outputRoot, 'proof-gap-closure-latest', 'proof-gap-closure-summary.json')),
  180. feedbackLoopClosure: rel(path.join(outputRoot, 'feedback-loop-closure-latest', 'feedback-loop-closure-summary.json')),
  181. homepageEvidenceReadiness: rel(path.join(outputRoot, 'homepage-evidence-readiness-latest', 'homepage-evidence-readiness-summary.json')),
  182. intakeReadiness: rel(path.join(outputRoot, 'intake-readiness-latest', 'intake-readiness-summary.json')),
  183. videoResourceReadiness: rel(path.join(outputRoot, 'video-resource-readiness-latest', 'video-resource-readiness-summary.json')),
  184. livePreflight: rel(path.join(outputRoot, 'live-preflight-latest', 'live-preflight-summary.json')),
  185. longRunReadiness: rel(path.join(outputRoot, 'long-run-readiness-latest', 'long-run-readiness-summary.json')),
  186. intakeFieldChecklist: rel(path.join(outputRoot, 'intake-field-checklist-latest', 'intake-field-checklist-summary.json')),
  187. manualReviewLabelGuide: rel(path.join(outputRoot, 'manual-review-label-guide-latest', 'manual-review-label-guide-summary.json')),
  188. businessProofProgress: rel(path.join(outputRoot, 'business-proof-progress-latest', 'business-proof-progress-summary.json')),
  189. businessProofNextActions: rel(path.join(outputRoot, 'business-proof-next-actions-latest', 'business-proof-next-actions-summary.json')),
  190. businessExecutionIndex: rel(path.join(outputRoot, 'business-execution-index-latest', 'business-execution-index-summary.json')),
  191. optimizationStatus: rel(path.join(outputRoot, 'optimization-status-latest', 'optimization-status-summary.json')),
  192. evidenceIndex: rel(path.join(outputRoot, 'evidence-index-latest', 'evidence-index-summary.json')),
  193. handoffSummary: rel(path.join(outputRoot, 'optimization-handoff-latest', 'optimization-handoff-summary.json')),
  194. planExecutionStatus: rel(path.join(outputRoot, 'plan-execution-status-latest', 'plan-execution-status-summary.json')),
  195. proofGapOperatorPack: rel(path.join(outputRoot, 'proof-gap-operator-pack-latest', 'proof-gap-operator-pack-summary.json')),
  196. realProofIntakeBundle: rel(path.join(outputRoot, 'real-proof-intake-bundle-latest', 'real-proof-intake-bundle-summary.json')),
  197. softwareClientList: rel(path.join(outputRoot, 'software-client-latest', 'software-client-refresh-summary.json')),
  198. latestFormIndex: rel(path.join(outputRoot, 'latest-form-index-latest', 'latest-form-index-summary.json')),
  199. optimizationCompletion: rel(path.join(outputRoot, 'optimization-completion-latest', 'optimization-completion-summary.json')),
  200. localSeedMaterialIndex: rel(path.join(outputRoot, 'local-seed-material-index-latest', 'local-seed-material-index-summary.json')),
  201. localSeedToIntakeWorklist: rel(path.join(outputRoot, 'local-seed-to-intake-worklist-latest', 'local-seed-to-intake-worklist-summary.json')),
  202. realGapMaterialAudit: rel(path.join(outputRoot, 'real-gap-material-audit-latest', 'real-gap-material-audit-summary.json')),
  203. realProofClosureWorkOrder: rel(path.join(outputRoot, 'real-proof-closure-work-order-latest', 'real-proof-closure-work-order-summary.json')),
  204. experienceTranscriptIndex: rel(path.join(outputRoot, 'experience-transcript-index-latest', 'experience-transcript-index-summary.json')),
  205. roundDeposition: rel(path.join(outputRoot, 'round-deposition-latest', 'round-deposition-summary.json'))
  206. }
  207. };
  208. const summaryPath = path.join(outputRoot, 'round-refresh-summary.json');
  209. fs.writeFileSync(summaryPath, JSON.stringify(summary, null, 2), 'utf8');
  210. console.log(JSON.stringify({
  211. outputRoot,
  212. summary: summaryPath,
  213. passed: summary.passed,
  214. steps: summary.steps.map(item => `${item.id}:${item.status}`)
  215. }, null, 2));
  216. if (!summary.passed && strict) process.exitCode = 1;
  217. }
  218. function runStep(step, strict) {
  219. const [command, args] = step.command;
  220. const resolvedCommand = command === 'node' ? process.execPath : command;
  221. const startedAt = new Date().toISOString();
  222. const result = spawnSync(resolvedCommand, args, {
  223. cwd: ROOT,
  224. encoding: 'utf8',
  225. shell: false
  226. });
  227. const status = result.status === 0 ? 'pass' : 'fail';
  228. if (result.stdout) process.stdout.write(result.stdout);
  229. if (result.stderr) process.stderr.write(result.stderr);
  230. if (status === 'fail' && strict) {
  231. return {
  232. id: step.id,
  233. status,
  234. startedAt,
  235. finishedAt: new Date().toISOString(),
  236. exitCode: result.status,
  237. error: `${command} ${args.join(' ')} failed`
  238. };
  239. }
  240. return {
  241. id: step.id,
  242. status,
  243. startedAt,
  244. finishedAt: new Date().toISOString(),
  245. exitCode: result.status
  246. };
  247. }
  248. function parseArgs(argv) {
  249. const result = {};
  250. for (let index = 0; index < argv.length; index += 1) {
  251. const arg = argv[index];
  252. if (!arg.startsWith('--')) continue;
  253. const key = arg.slice(2).replace(/-([a-z])/g, (_, char) => char.toUpperCase());
  254. const next = argv[index + 1];
  255. if (!next || next.startsWith('--')) result[key] = true;
  256. else {
  257. result[key] = next;
  258. index += 1;
  259. }
  260. }
  261. return result;
  262. }
  263. function rel(file) {
  264. return path.relative(ROOT, file).replace(/\\/g, '/');
  265. }
  266. if (require.main === module) main();