← back to Dw Daily Catchup 20260909
scripts/audit-channel-state.js
54 lines
#!/usr/bin/env node
'use strict';
// Independent read-only API/storefront verifier. No activation-module imports.
const fs = require('node:fs');
const path = require('node:path');
const os = require('node:os');
const assert = require('node:assert/strict');
const [phase, expectedArg] = process.argv.slice(2);
if (phase!=='channels') throw new Error('Usage: verifier before | after <cumulative new count>');
const evidence = path.resolve('verification/tk11326');
const production = path.join(os.homedir(), 'Projects/dw-rotation-activator');
const day = new Date().toISOString().slice(0, 10);
const auditPath = path.join(production, `out/rotation-activations-${day}.jsonl`);
const ledgerPath = path.join(production, `out/activation-ledger-${day}.json`);
const env = fs.readFileSync(path.join(os.homedir(), 'Projects/secrets-manager/.env'), 'utf8');
const token = (env.match(/^SHOPIFY_ADMIN_TOKEN=(.*)$/m) || [])[1]?.trim().replace(/^['"]|['"]$/g, '');
const policy = JSON.parse(fs.readFileSync('config/gemini-review-policy.json'));
const blocked = new Set(policy.historical_blocked_ids);
const candidates = fs.readFileSync(`out/rotation-activations-${day}.jsonl`, 'utf8').trim().split('\n').map(JSON.parse);
const predicted = candidates.filter(r => r.action === 'dryrun-would-activate').map(r => r.shopify_id);
assert.equal(predicted.length, 462); assert.equal(new Set(predicted).size, 462);
const negativeIds = [...new Set(candidates.filter(r => r.action === 'settlement-block' || r.action === 'skip-gate').map(r => r.shopify_id))].filter(id => !predicted.includes(id));
const controls = [...new Set([...negativeIds.filter(id => blocked.has(id)), ...negativeIds.filter(id => !blocked.has(id)).slice(0, 2)])];
const query = `query($ids:[ID!]!){nodes(ids:$ids){... on Product{
id title vendor status handle createdAt updatedAt tags images(first:1){nodes{url}}
variants(first:100){nodes{id sku title price} pageInfo{hasNextPage}}
review:metafield(namespace:"internal",key:"settlement_hold"){value}
googlePublished:publishedOnPublication(publicationId:"gid://shopify/Publication/29646651457")
resourcePublicationsV2(first:50,onlyPublished:false){nodes{isPublished publishDate publication{id name}} pageInfo{hasNextPage}}
}}}`;
async function readProducts(ids) {
const products = [];
for (let i=0; i<ids.length; i+=40) {
const response = await fetch('https://designer-laboratory-sandbox.myshopify.com/admin/api/2026-07/graphql.json', {
method:'POST', headers:{'Content-Type':'application/json','X-Shopify-Access-Token':token},
body:JSON.stringify({query,variables:{ids:ids.slice(i,i+40)}}), signal:AbortSignal.timeout(30000),
});
const result=await response.json();
assert.equal(response.status,200); assert.equal(result.errors,undefined,JSON.stringify(result.errors));
assert.equal(result.data.nodes.length,ids.slice(i,i+40).length);
assert.ok(result.data.nodes.every(Boolean)); products.push(...result.data.nodes);
}
return products;
}
async function main() {
const products=await readProducts([...predicted,...controls]);
const report={timestamp:new Date().toISOString(),mutations:0,products};
fs.writeFileSync(path.join(evidence,'channel-state.json'),JSON.stringify(report,null,2)+'\n');
const google=products.filter(p=>p.googlePublished).map(p=>({id:p.id,title:p.title,updatedAt:p.updatedAt,google:p.resourcePublicationsV2.nodes.find(r=>r.publication.id.endsWith('/29646651457'))}));
console.log(JSON.stringify({google_published:google.length,first:google.slice(0,4),last:google.slice(-2)},null,2));
}
main().catch(e=>{console.error(e.message);process.exitCode=1;});