Files
justvitamin/scripts/generate-staging-handoff-packet.ts
T
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

194 lines
8.9 KiB
TypeScript

#!/usr/bin/env bun
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'fs'
import { join } from 'path'
const root = process.cwd()
const outRoot = join(root, 'content_population_exports')
const stagingRoot = join(root, 'data', 'staging')
const pdpQueueRoot = join(stagingRoot, 'pdp-check-queue')
const migrationOutRoot = join(root, '..', 'JV Migration to shopify', 'content_population_exports')
const productsRoot = join(root, 'data', 'content', 'products')
const generatedAt = new Date().toISOString()
function readJson(path: string, fallback: any = null) {
if (!existsSync(path)) return fallback
return JSON.parse(readFileSync(path, 'utf8').replace(/^\uFEFF/, ''))
}
function csv(rows: any[], headers: string[]) {
const esc = (value: any) => {
if (Array.isArray(value)) value = value.join(' | ')
if (value === undefined || value === null) value = ''
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'
}
function fileMeta(label: string, path: string, importOrder: number | '') {
const present = existsSync(path)
const stat = present ? statSync(path) : null
return {
Label: label,
ImportOrder: importOrder,
Path: path,
Present: present ? 'yes' : 'no',
SizeBytes: stat?.size || 0,
LastModified: stat?.mtime?.toISOString() || '',
Use: importOrder === '' ? 'Evidence/reference only' : 'Matrixify staging import candidate'
}
}
mkdirSync(outRoot, { recursive: true })
mkdirSync(pdpQueueRoot, { recursive: true })
const handoff = readJson(join(outRoot, 'handoff_manifest.json'), {})
const approvalGate = readJson(join(outRoot, 'product_approval_decision_gate.json'), {})
const blankGate = readJson(join(outRoot, 'clinical_blank_decision_template.json'), {})
const products = readdirSync(productsRoot)
.filter(file => file.endsWith('.json'))
.sort()
.map(file => readJson(join(productsRoot, file), {}))
const importFiles = [
fileMeta('Metaobjects', join(migrationOutRoot, 'metaobjects_all.csv'), 1),
fileMeta('Smart collection promo cards', join(migrationOutRoot, 'smart_collections_promo_cards.csv'), 2),
fileMeta('Product/variant metafields', join(migrationOutRoot, 'products_content_v5.csv'), 3),
fileMeta('Product blockers', join(migrationOutRoot, 'products_content_v5_blockers.csv'), ''),
fileMeta('Asset validation', join(migrationOutRoot, 'asset_manifest_validation.csv'), ''),
fileMeta('Dashboard handoff manifest', join(outRoot, 'handoff_manifest.json'), ''),
fileMeta('Dashboard export blockers', join(outRoot, 'export_blockers.csv'), '')
]
const prioritySkus = ['JV-D1000', 'JV-D4000', 'JV-TURMERIC500', 'JV-D400', 'JV-5HTP100', 'JV-B12', 'JV-COLLAGEN400', 'JV-OMEGA31000', 'JV-VISISOFT', 'JV-ZINC10']
const productBySku = new Map(products.map(product => [product.sku, product]))
const sampleRows = prioritySkus
.filter(sku => productBySku.has(sku))
.map((sku, index) => {
const product: any = productBySku.get(sku)
const row = {
Sequence: index + 1,
SKU: sku,
Handle: product.handle || '',
ProductName: product.name || '',
Reason: index < 3 ? 'pilot/high-touch content SKU' : product.metafields?.pdpClinicallyShownTo ? 'source-backed clinical claim mapped' : 'representative blank-claim/approval blocker SKU',
ClinicalRef: product.metafields?.pdpClinicallyShownTo || '',
RequiredChecks: 'productMetafields | variantMetafields | metaobjectReferences | fileReferences | faqFields | promoCard | collectionMapping | mobileLayout',
TemplatePath: `data/staging/pdp-check-queue/${sku}.json`
}
const template = {
sku,
handle: row.Handle,
productName: row.ProductName,
reason: row.Reason,
stagingUrl: '',
checkedAt: '',
checkedBy: '',
checks: {
productMetafields: 'pending',
variantMetafields: 'pending',
metaobjectReferences: 'pending',
fileReferences: 'pending',
faqFields: 'pending',
promoCard: 'pending',
collectionMapping: 'pending',
mobileLayout: 'pending'
},
issues: [],
approvedForProduction: false,
note: 'Copy completed passing checks to data/staging/pdp-checks/ after real staging URL review. Do not mark pass without inspecting staging.'
}
writeFileSync(join(pdpQueueRoot, `${sku}.json`), JSON.stringify(template, null, 2) + '\n', 'utf8')
return row
})
const actionRows = [
{
Sequence: 1,
Owner: 'Dashboard owner',
Action: 'Resolve current dashboard blockers before sending import files as ready.',
RequiredEvidence: 'handoff_manifest.readyForLewis=true and export_blockers.csv empty.',
CurrentEvidence: `readyForLewis=${Boolean(handoff.readyForLewis)}; blockers=${handoff.counts?.blockers ?? 'unknown'}`
},
{
Sequence: 2,
Owner: 'Compliance owner',
Action: 'Complete source-backed claims or approved blank decisions.',
RequiredEvidence: 'clinical_blank_decision_template ready OR all needed clinical refs mapped and approved.',
CurrentEvidence: `validBlankApprovals=${blankGate.counts?.validBlankApprovals ?? 0}; pendingBlankDecisions=${blankGate.counts?.pending ?? 'unknown'}`
},
{
Sequence: 3,
Owner: 'Umar / JV reviewer',
Action: 'Complete product approval decision file.',
RequiredEvidence: 'product_approval_decision_gate ready; 190 valid approvals or explicitly rejected rows resolved.',
CurrentEvidence: `validApprovals=${approvalGate.counts?.validApprovals ?? 0}; pending=${approvalGate.counts?.pending ?? 'unknown'}`
},
{
Sequence: 4,
Owner: 'Lewis/Euan',
Action: 'Import files in order on staging and capture Matrixify logs.',
RequiredEvidence: 'Clean import logs saved under data/staging/import-logs/.',
CurrentEvidence: 'No import logs captured yet.'
},
{
Sequence: 5,
Owner: 'Dashboard owner + Lewis/Euan',
Action: 'Complete PDP spot-check queue against real staging URLs.',
RequiredEvidence: 'At least 5 passing JSON checks copied to data/staging/pdp-checks/.',
CurrentEvidence: `${sampleRows.length} PDP check queue templates generated; no completed checks claimed.`
}
]
const manifest = {
generatedAt,
ready: Boolean(handoff.readyForLewis),
purpose: 'Lewis/Euan staging handoff packet with exact import files, sequence, blockers, and PDP spot-check queue. It does not claim staging evidence is complete.',
counts: {
importFiles: importFiles.length,
presentImportFiles: importFiles.filter(row => row.Present === 'yes').length,
pdpSpotCheckQueue: sampleRows.length,
currentExportBlockers: handoff.counts?.blockers ?? null,
approvalPending: approvalGate.counts?.pending ?? null,
blankDecisionPending: blankGate.counts?.pending ?? null
},
importFiles,
sampleRows,
actionRows
}
writeFileSync(join(outRoot, 'staging_handoff_packet.json'), JSON.stringify(manifest, null, 2) + '\n', 'utf8')
writeFileSync(join(outRoot, 'staging_handoff_import_files.csv'), csv(importFiles, ['Label', 'ImportOrder', 'Path', 'Present', 'SizeBytes', 'LastModified', 'Use']), 'utf8')
writeFileSync(join(outRoot, 'staging_pdp_spot_check_queue.csv'), csv(sampleRows, ['Sequence', 'SKU', 'Handle', 'ProductName', 'Reason', 'ClinicalRef', 'RequiredChecks', 'TemplatePath']), 'utf8')
writeFileSync(join(outRoot, 'staging_handoff_actions.csv'), csv(actionRows, ['Sequence', 'Owner', 'Action', 'RequiredEvidence', 'CurrentEvidence']), 'utf8')
writeFileSync(join(outRoot, 'staging_handoff_packet.md'), [
'# JV staging handoff packet',
'',
`Generated: ${generatedAt}`,
'',
'This is the Lewis/Euan staging handoff packet. It gives the import sequence and check queue, but it does not mark staging complete.',
'',
`Ready to hand off: **${manifest.ready ? 'YES' : 'NO'}**`,
'',
'## Current blockers',
`- Export blockers: ${manifest.counts.currentExportBlockers}`,
`- Product approval pending: ${manifest.counts.approvalPending}`,
`- Blank clinical decision pending: ${manifest.counts.blankDecisionPending}`,
'',
'## Import files',
...importFiles.map(row => `- ${row.ImportOrder || 'ref'}: ${row.Label}${row.Present}\`${row.Path}\``),
'',
'## PDP spot-check queue',
...sampleRows.map(row => `- ${row.Sequence}. ${row.SKU}${row.ProductName} (${row.Reason})`),
'',
'## Actions',
...actionRows.map(row => `- ${row.Sequence}. ${row.Owner}: ${row.Action} Current: ${row.CurrentEvidence}`),
'',
'## Rule',
'Do not copy queued PDP templates into `data/staging/pdp-checks/` until the real staging URL has been inspected and every check is actually pass.',
''
].join('\n'), 'utf8')
console.log(`Staging handoff packet ready: ${manifest.ready}`)
console.log(`Import files present: ${manifest.counts.presentImportFiles}/${manifest.counts.importFiles}`)
console.log(`PDP spot-check queue: ${manifest.counts.pdpSpotCheckQueue}`)
if (!manifest.ready) process.exitCode = 1