056c47581f
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>
152 lines
5.9 KiB
TypeScript
152 lines
5.9 KiB
TypeScript
#!/usr/bin/env bun
|
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs'
|
|
import { dirname, join } from 'path'
|
|
|
|
const root = process.cwd()
|
|
const outRoot = join(root, 'content_population_exports')
|
|
const stagingRoot = join(root, 'data', 'staging')
|
|
const importLogRoot = join(stagingRoot, 'import-logs')
|
|
const pdpCheckRoot = join(stagingRoot, 'pdp-checks')
|
|
const cutoverPath = join(stagingRoot, 'cutover-approval.json')
|
|
mkdirSync(outRoot, { recursive: true })
|
|
mkdirSync(importLogRoot, { recursive: true })
|
|
mkdirSync(pdpCheckRoot, { recursive: true })
|
|
|
|
function readJson(path: string, fallback: any = null) {
|
|
if (!existsSync(path)) return fallback
|
|
return JSON.parse(readFileSync(path, 'utf8').replace(/^\uFEFF/, ''))
|
|
}
|
|
function writeJson(path: string, data: any) {
|
|
mkdirSync(dirname(path), { recursive: true })
|
|
writeFileSync(path, JSON.stringify(data, null, 2) + '\n', 'utf8')
|
|
}
|
|
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 cutoverTemplate = readJson(join(stagingRoot, '_templates', 'cutover-approval-template.json'), {
|
|
approved: false,
|
|
approvedBy: '',
|
|
approvedAt: '',
|
|
stagingImportEvidence: 'data/staging/import-logs/',
|
|
pdpSpotCheckEvidence: 'data/staging/pdp-checks/',
|
|
knownRisksAccepted: [],
|
|
notes: ''
|
|
})
|
|
|
|
let cutoverCreated = false
|
|
if (!existsSync(cutoverPath)) {
|
|
writeJson(cutoverPath, {
|
|
...cutoverTemplate,
|
|
approved: false,
|
|
approvedBy: '',
|
|
approvedAt: '',
|
|
knownRisksAccepted: [],
|
|
notes: 'Pending. This file is an editable cutover approval tracker, not approval evidence until approved=true with approver/date after staging checks pass.'
|
|
})
|
|
cutoverCreated = true
|
|
}
|
|
|
|
const importReadmePath = join(importLogRoot, 'README.md')
|
|
const pdpReadmePath = join(pdpCheckRoot, 'README.md')
|
|
if (!existsSync(importReadmePath)) {
|
|
writeFileSync(importReadmePath, [
|
|
'# Staging import logs',
|
|
'',
|
|
'Save real Matrixify staging import log JSON files here after Lewis/Euan run imports.',
|
|
'',
|
|
'Required files/evidence are not generated by this dashboard. Use `data/staging/_templates/import-log-template.json` as the schema, then run `bun run staging:evidence`.',
|
|
'',
|
|
'Do not place placeholder JSON files here; any JSON in this folder is treated as submitted evidence by the validator.',
|
|
''
|
|
].join('\n'), 'utf8')
|
|
}
|
|
if (!existsSync(pdpReadmePath)) {
|
|
writeFileSync(pdpReadmePath, [
|
|
'# Staging PDP spot checks',
|
|
'',
|
|
'Save real staging PDP spot-check JSON files here after checking live staging URLs.',
|
|
'',
|
|
'At least 5 passing checks are required. Use `data/staging/_templates/pdp-spot-check-template.json` as the schema, then run `bun run staging:evidence`.',
|
|
'',
|
|
'Do not place placeholder JSON files here; any JSON in this folder is treated as submitted evidence by the validator.',
|
|
''
|
|
].join('\n'), 'utf8')
|
|
}
|
|
|
|
const rows = [
|
|
{
|
|
Item: 'cutover_approval_tracker',
|
|
Path: 'data/staging/cutover-approval.json',
|
|
Status: cutoverCreated ? 'created_pending_tracker' : 'already_present',
|
|
EvidenceValue: 'Not approval until approved=true with approver and approvedAt after staging evidence passes.',
|
|
NextAction: 'Lewis/Euan/project owner fills after import logs and PDP checks pass.'
|
|
},
|
|
{
|
|
Item: 'import_log_folder_instructions',
|
|
Path: 'data/staging/import-logs/README.md',
|
|
Status: 'present',
|
|
EvidenceValue: 'Instructions only; no import evidence generated.',
|
|
NextAction: 'Save real Matrixify import log JSON files after staging imports.'
|
|
},
|
|
{
|
|
Item: 'pdp_check_folder_instructions',
|
|
Path: 'data/staging/pdp-checks/README.md',
|
|
Status: 'present',
|
|
EvidenceValue: 'Instructions only; no PDP evidence generated.',
|
|
NextAction: 'Save at least 5 real PDP spot-check JSON files after staging URL checks.'
|
|
}
|
|
]
|
|
const manifest = {
|
|
generatedAt,
|
|
ready: false,
|
|
purpose: 'Initializes editable staging evidence intake locations without fabricating import logs, PDP checks, or cutover approval.',
|
|
cutoverCreated,
|
|
counts: {
|
|
intakeItems: rows.length,
|
|
fakeEvidenceCreated: 0
|
|
},
|
|
rows,
|
|
files: {
|
|
markdown: 'content_population_exports/staging_evidence_intake.md',
|
|
json: 'content_population_exports/staging_evidence_intake.json',
|
|
csv: 'content_population_exports/staging_evidence_intake.csv',
|
|
cutoverApproval: 'data/staging/cutover-approval.json',
|
|
importLogReadme: 'data/staging/import-logs/README.md',
|
|
pdpCheckReadme: 'data/staging/pdp-checks/README.md'
|
|
}
|
|
}
|
|
|
|
writeJson(join(outRoot, 'staging_evidence_intake.json'), manifest)
|
|
writeFileSync(join(outRoot, 'staging_evidence_intake.csv'), csv(rows, ['Item', 'Path', 'Status', 'EvidenceValue', 'NextAction']), 'utf8')
|
|
writeFileSync(join(outRoot, 'staging_evidence_intake.md'), [
|
|
'# Staging evidence intake',
|
|
'',
|
|
`Generated: ${generatedAt}`,
|
|
'',
|
|
'Ready: **NO**',
|
|
'',
|
|
'This initializes the editable staging evidence intake paths. It deliberately does not create fake Matrixify logs, fake PDP checks, or a signed cutover approval.',
|
|
'',
|
|
`Fake evidence created: ${manifest.counts.fakeEvidenceCreated}`,
|
|
'',
|
|
'## Intake rows',
|
|
'| Item | Path | Status | Next action |',
|
|
'|---|---|---|---|',
|
|
...rows.map(row => `| ${row.Item} | ${row.Path} | ${row.Status} | ${row.NextAction} |`),
|
|
'',
|
|
'## Guardrail',
|
|
'Only JSON files created from real staging activity should be placed in `data/staging/import-logs/` and `data/staging/pdp-checks/`. The cutover tracker remains blocked until real approval is recorded.',
|
|
''
|
|
].join('\n'), 'utf8')
|
|
|
|
console.log(`Staging evidence intake initialized; cutoverCreated=${cutoverCreated}`)
|
|
console.log('Fake evidence created: 0')
|