Files
justvitamin/scripts/refresh-source-acquisition-packet.ts
Omair Saleh 056c47581f feat: editorial review dashboard + elite-grade pilot batch (5 SKUs)
Ships the second dashboard surface — a Pattern Library + Preview Theatre — that
presents the 4-section PDP pilot batch back to Umar, compliance, and the board
in an editorial format. Adds the full data layer that drives it: 5 source-backed
per-SKU drafts at QA 100/100, 15 competitor PDP semantic extracts, PubMed
evidence packs, EFSA claims library extension, JV brand voice guide, hand-curated
product FAQs, and the Matrixify-ready CSV exports for Lewis.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-02 18:50:09 +08:00

120 lines
7.2 KiB
TypeScript

#!/usr/bin/env bun
import { mkdirSync, writeFileSync } from 'fs'
import { join } from 'path'
const root = process.cwd()
const outRoot = join(root, 'content_population_exports')
mkdirSync(outRoot, { recursive: true })
const steps = [
{ name: 'source_acquisition_plan', command: ['bun', 'run', 'source:acquisition'], requiredForPacket: true },
{ name: 'dataforseo_pilot_intake', command: ['bun', 'run', 'dataforseo:pilot'], requiredForPacket: true },
{ name: 'dataforseo_products_live_guard', command: ['bun', 'run', 'dataforseo:products-live'], requiredForPacket: true },
{ name: 'dataforseo_live_connection_evidence', command: ['bun', 'run', 'dataforseo:live-asin-evidence'], requiredForPacket: true },
{ name: 'dataforseo_asin_candidates', command: ['bun', 'run', 'dataforseo:asin-candidates'], requiredForPacket: true },
{ name: 'dataforseo_asin_web_evidence', command: ['bun', 'run', 'dataforseo:asin-web-evidence'], requiredForPacket: true },
{ name: 'dataforseo_unresolved_asin_evidence', command: ['bun', 'run', 'dataforseo:unresolved-asins'], requiredForPacket: true },
{ name: 'dataforseo_asin_confirmations', command: ['bun', 'run', 'dataforseo:asin-confirmations'], requiredForPacket: true },
{ name: 'dataforseo_asin_promotion', command: ['bun', 'run', 'dataforseo:promote-asins'], requiredForPacket: true },
{ name: 'dataforseo_pilot_packet', command: ['bun', 'run', 'dataforseo:pilot'], requiredForPacket: true },
{ name: 'dataforseo_asin_lookup', command: ['bun', 'run', 'dataforseo:asin-lookup'], requiredForPacket: true },
{ name: 'dataforseo_reviews_docs_status', command: ['bun', 'run', 'dataforseo:reviews-docs-status'], requiredForPacket: true },
{ name: 'dataforseo_task_preflight', command: ['bun', 'run', 'dataforseo:task-preflight'], requiredForPacket: true },
{ name: 'dataforseo_reviews_live_guard', command: ['bun', 'run', 'dataforseo:reviews-live'], requiredForPacket: true },
{ name: 'dataforseo_support_escalation', command: ['bun', 'run', 'dataforseo:support-escalation'], requiredForPacket: true },
{ name: 'dataforseo_support_response_gate', command: ['bun', 'run', 'dataforseo:support-response'], requiredForPacket: true },
{ name: 'dataforseo_approval_request', command: ['bun', 'run', 'dataforseo:approval-request'], requiredForPacket: true },
{ name: 'manual_amazon_review_capture', command: ['bun', 'run', 'amazon:manual-capture'], requiredForPacket: true },
{ name: 'manual_amazon_review_capture_validation', command: ['bun', 'run', 'amazon:manual-capture-validate'], requiredForPacket: true },
{ name: 'dataforseo_ingest_waiting_room', command: ['bun', 'run', 'dataforseo:ingest'], requiredForPacket: true },
{ name: 'dataforseo_pilot_review_gate', command: ['bun', 'run', 'dataforseo:review'], requiredForPacket: true },
{ name: 'apify_gapfill_packet', command: ['bun', 'run', 'apify:gapfill'], requiredForPacket: true },
{ name: 'apify_task_preflight', command: ['bun', 'run', 'apify:task-preflight'], requiredForPacket: true },
{ name: 'source_pricing_references', command: ['bun', 'run', 'source:pricing-refs'], requiredForPacket: true },
{ name: 'source_cost_scenarios', command: ['bun', 'run', 'source:cost-scenarios'], requiredForPacket: true },
{ name: 'source_budget_gate', command: ['bun', 'run', 'source:budget'], requiredForPacket: true },
{ name: 'source_cost_plan', command: ['bun', 'run', 'source:cost-plan'], requiredForPacket: true },
{ name: 'source_approval_gate', command: ['bun', 'run', 'source:approvals'], requiredForPacket: true },
{ name: 'source_spend_gate', command: ['bun', 'run', 'source:spend-gate'], requiredForPacket: true },
{ name: 'dataforseo_task_preflight_final', command: ['bun', 'run', 'dataforseo:task-preflight'], requiredForPacket: true },
{ name: 'source_subscription_decision', command: ['bun', 'run', 'source:subscription-decision'], requiredForPacket: true },
{ name: 'source_next_actions', command: ['bun', 'run', 'source:next-actions'], requiredForPacket: true }
]
function csv(rows: any[], headers: string[]) {
const esc = (value: any) => {
if (value === undefined || value === null) value = ''
if (Array.isArray(value)) value = value.join(' | ')
value = String(value)
return /[",\n\r]/.test(value) ? `"${value.replace(/"/g, '""')}"` : value
}
return [headers.join(','), ...rows.map(row => headers.map(header => esc(row[header])).join(','))].join('\n') + '\n'
}
const generatedAt = new Date().toISOString()
const results: any[] = []
for (const step of steps) {
const startedAt = new Date().toISOString()
const proc = Bun.spawnSync({ cmd: step.command, cwd: root, stdout: 'pipe', stderr: 'pipe' })
const stdout = proc.stdout ? new TextDecoder().decode(proc.stdout).trim() : ''
const stderr = proc.stderr ? new TextDecoder().decode(proc.stderr).trim() : ''
const status = proc.exitCode === 0 ? 'pass' : 'blocked'
results.push({
Name: step.name,
Command: step.command.join(' '),
Status: status,
ExitCode: proc.exitCode,
RequiredForPacket: step.requiredForPacket ? 'yes' : 'no',
StartedAt: startedAt,
FinishedAt: new Date().toISOString(),
Stdout: stdout,
Stderr: stderr
})
console.log(`${step.name}: ${status} (${proc.exitCode})`)
}
const packetReady = results.filter(row => row.RequiredForPacket === 'yes').every(row => row.ExitCode === 0)
const manifest = {
generatedAt,
ready: packetReady,
note: 'This proves the staged source-acquisition packet can be regenerated. Run delivery:index after this control run to refresh the overall handoff packet index. It does not mean provider spend or final handoff is allowed.',
counts: {
steps: results.length,
passing: results.filter(row => row.ExitCode === 0).length,
blocked: results.filter(row => row.ExitCode !== 0).length,
required: results.filter(row => row.RequiredForPacket === 'yes').length,
requiredPassing: results.filter(row => row.RequiredForPacket === 'yes' && row.ExitCode === 0).length
},
steps: results,
blockedSteps: results.filter(row => row.ExitCode !== 0).map(row => row.Name)
}
writeFileSync(join(outRoot, 'source_acquisition_control_run.json'), JSON.stringify(manifest, null, 2) + '\n', 'utf8')
writeFileSync(join(outRoot, 'source_acquisition_control_run.csv'), csv(results.map(row => ({
Name: row.Name,
Command: row.Command,
Status: row.Status,
ExitCode: row.ExitCode,
RequiredForPacket: row.RequiredForPacket,
Stdout: row.Stdout.split('\n').slice(0, 4).join(' | '),
Stderr: row.Stderr.split('\n').slice(0, 4).join(' | ')
})), ['Name', 'Command', 'Status', 'ExitCode', 'RequiredForPacket', 'Stdout', 'Stderr']), 'utf8')
writeFileSync(join(outRoot, 'source_acquisition_control_run.md'), [
'# Source acquisition control run',
'',
`Generated: ${generatedAt}`,
'',
`Packet regenerated: ${packetReady ? 'YES' : 'NO'}`,
'',
manifest.note,
'',
`Steps passing: ${manifest.counts.passing}/${manifest.counts.steps}`,
`Required packet steps passing: ${manifest.counts.requiredPassing}/${manifest.counts.required}`,
'',
'## Steps',
...results.map(row => `- ${row.Status === 'pass' ? '[pass]' : '[blocked]'} ${row.Name}: \`${row.Command}\` exit=${row.ExitCode}`),
''
].join('\n'), 'utf8')
console.log(`Source acquisition control ready: ${manifest.ready}`)
console.log(`Steps passing: ${manifest.counts.passing}/${manifest.counts.steps}`)