2026-05-15 17:55:43 -04:00
/**
* Tests for scripts/operator-readiness-dashboard.js
*/
const assert = require ( 'assert' );
const fs = require ( 'fs' );
const os = require ( 'os' );
const path = require ( 'path' );
const { execFileSync , spawnSync } = require ( 'child_process' );
const SCRIPT = path . join ( __dirname , '..' , '..' , 'scripts' , 'operator-readiness-dashboard.js' );
2026-05-15 22:18:48 -04:00
const { buildReport , parseArgs , renderMarkdown , renderText } = require ( SCRIPT );
2026-05-15 17:55:43 -04:00
function createTempDir ( prefix ) {
return fs . mkdtempSync ( path . join ( os . tmpdir (), prefix ));
}
function cleanup ( dirPath ) {
fs . rmSync ( dirPath , { recursive : true , force : true });
}
function writeFile ( rootDir , relativePath , content ) {
const targetPath = path . join ( rootDir , relativePath );
fs . mkdirSync ( path . dirname ( targetPath ), { recursive : true });
fs . writeFileSync ( targetPath , content );
}
function seedRepo ( rootDir , overrides = {}) {
const files = {
'package.json' : JSON . stringify ({
name : 'everything-claude-code' ,
files : [
'scripts/observability-readiness.js' ,
'scripts/operator-readiness-dashboard.js' ,
2026-05-17 15:35:23 -04:00
'scripts/platform-audit.js' ,
2026-05-19 07:58:20 -04:00
'scripts/preview-pack-smoke.js' ,
'scripts/release-video-suite.js'
2026-05-15 17:55:43 -04:00
],
scripts : {
'discussion:audit' : 'node scripts/discussion-audit.js' ,
'observability:ready' : 'node scripts/observability-readiness.js' ,
'operator:dashboard' : 'node scripts/operator-readiness-dashboard.js' ,
'platform:audit' : 'node scripts/platform-audit.js' ,
2026-05-17 15:35:23 -04:00
'preview-pack:smoke' : 'node scripts/preview-pack-smoke.js' ,
2026-05-19 07:58:20 -04:00
'release:video-suite' : 'node scripts/release-video-suite.js' ,
2026-05-15 22:39:35 -04:00
'security:ioc-scan' : 'node scripts/ci/scan-supply-chain-iocs.js' ,
'security:advisory-sources' : 'node scripts/ci/supply-chain-advisory-sources.js'
2026-05-15 17:55:43 -04:00
}
}, null , 2 ),
'scripts/operator-readiness-dashboard.js' : 'operator dashboard generator' ,
2026-05-17 15:35:23 -04:00
'scripts/preview-pack-smoke.js' : [
'ecc.preview-pack-smoke.v1' ,
'preview-pack-artifacts-present' ,
'hermes-boundary-sanitized' ,
'publication-blockers-preserved'
]. join ( '\n' ),
2026-05-19 07:58:20 -04:00
'scripts/release-video-suite.js' : [
'ecc.release-video-suite.v1' ,
'video-source-assets-present' ,
'video-release-artifacts-present'
]. join ( '\n' ),
2026-05-15 17:55:43 -04:00
'docs/ECC-2.0-GA-ROADMAP.md' : [
'https://linear.app/itomarkets/project/ecc-platform-roadmap-52b328ee03e1' ,
'Linear ITO-44 ITO-59' ,
2026-05-17 01:10:12 -04:00
'AgentShield PR #92 #78-#92 checksum-backed policy export policy promote checksum-verified policy promotion' ,
2026-05-15 17:55:43 -04:00
'AgentShield Enterprise Iteration' ,
2026-05-16 02:02:17 -04:00
'ECC-Tools PR #78' ,
2026-05-15 17:55:43 -04:00
'hosted promotion' ,
2026-05-17 14:26:16 -04:00
'operator-visible promotion output values' ,
'hosted promotion judge audit traces' ,
'package-manager hardening Action outputs' ,
'production Marketplace readback state' ,
'eb69412' ,
2026-05-18 00:36:01 -04:00
'Marketplace webhook provenance' ,
'2859678' ,
2026-05-18 02:03:37 -04:00
'Wrangler OAuth readback' ,
'42653f9' ,
2026-05-18 03:27:42 -04:00
'target account billing readback' ,
'632e059' ,
2026-05-19 21:39:03 -04:00
'select-ready-target' ,
2026-05-19 22:09:45 -04:00
'selected-target official announcement gate' ,
2026-05-19 22:43:19 -04:00
'billing gate env-file operator path' ,
2026-05-19 23:09:39 -04:00
'non-breaking operator bearer path' ,
'announcementGateReady` is `true' ,
'd3d62df83fa075660fa4530c3e0edc311a4355fe' ,
2026-05-19 22:43:19 -04:00
'72119a1' ,
2026-05-19 22:09:45 -04:00
'16a5bb3' ,
2026-05-19 21:39:03 -04:00
'f14ed2fe-a219-470c-8119-63429e197027' ,
'old "no Marketplace-managed Pro target billing-state" blocker is cleared' ,
2026-05-19 22:09:45 -04:00
'30f60710' ,
'26135974576' ,
'467d148a-712a-4777-aad9-95593e9f1739' ,
'7642ee9c-3107-400c-a229-53e2895a8914' ,
2026-05-18 15:23:48 -04:00
'69ca535' ,
'team feedback controls' ,
'e56fc1a' ,
'1Password CLI authorization timed out' ,
'Cloudflare API auth returned `Authentication error [code: 10000]`' ,
2026-05-15 17:55:43 -04:00
'announcementGate' ,
2026-05-17 15:13:42 -04:00
'ITO-55' ,
'Linear live sync is current for the May 17 merge batch' ,
'operator progress snapshot'
2026-05-15 17:55:43 -04:00
]. join ( '\n' ),
2026-05-18 09:10:51 -04:00
'docs/releases/2.0.0-rc.1/publication-readiness.md' : 'Claude plugin Codex plugin release-name-plugin-publication-checklist-2026-05-18.md' ,
2026-05-15 17:55:43 -04:00
'docs/releases/2.0.0-rc.1/naming-and-publication-matrix.md' : 'Claude plugin Codex plugin npm package Publication Paths' ,
2026-05-18 09:10:51 -04:00
'docs/releases/2.0.0-rc.1/release-name-plugin-publication-checklist-2026-05-18.md' : [
2026-05-19 06:42:17 -04:00
'Ship `v2.0.0-rc.1` as **ECC**' ,
'affaan-m/ECC' ,
2026-05-18 09:10:51 -04:00
'ecc-universal' ,
'claude plugin tag .claude-plugin --dry-run' ,
'codex plugin marketplace add' ,
2026-05-19 06:42:17 -04:00
'Do not rename the npm package until rc.1 is published'
2026-05-18 09:10:51 -04:00
]. join ( '\n' ),
2026-05-17 15:35:23 -04:00
'docs/releases/2.0.0-rc.1/preview-pack-manifest.md' : [
'publication-readiness.md release-notes.md quickstart.md' ,
2026-05-18 09:10:51 -04:00
'release-name-plugin-publication-checklist-2026-05-18.md' ,
2026-05-19 10:23:34 -04:00
'owner-approval-packet-2026-05-19.md' ,
2026-05-17 15:35:23 -04:00
'`scripts/preview-pack-smoke.js`' ,
'npm run preview-pack:smoke'
]. join ( '\n' ),
2026-05-19 10:23:34 -04:00
'docs/releases/2.0.0-rc.1/owner-approval-packet-2026-05-19.md' : [
'Owner Approval Packet' ,
'Decision Register' ,
'GitHub prerelease' ,
'npm `next` publish' ,
'Claude plugin tag' ,
'Video upload' ,
'Final URL Fill-In' ,
'Do Not Approve If' ,
'No outbound email, personal-account post, package publish, plugin tag, or billing announcement is authorized by this packet alone.'
]. join ( '\n' ),
2026-05-15 17:55:43 -04:00
'docs/releases/2.0.0-rc.1/release-notes.md' : 'release notes' ,
'docs/releases/2.0.0-rc.1/x-thread.md' : 'x thread' ,
'docs/releases/2.0.0-rc.1/linkedin-post.md' : 'linkedin post' ,
2026-05-17 22:29:06 -04:00
'docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-18.md' : [
2026-05-15 17:58:54 -04:00
'This dashboard is generated by `npm run operator:dashboard`' ,
2026-05-15 17:55:43 -04:00
'operator:dashboard' ,
'Prompt-To-Artifact Checklist' ,
'Next Work Order' ,
'ITO-44' ,
'ITO-59' ,
'PR queue' ,
'Not complete'
]. join ( '\n' ),
2026-05-19 07:58:20 -04:00
'docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-19.md' : [
'This dashboard is generated by `npm run operator:dashboard`' ,
'operator:dashboard' ,
'Growth Baseline' ,
'hypergrowth release command center' ,
'Prompt-To-Artifact Checklist' ,
'Next Work Order' ,
'ITO-44' ,
'ITO-59' ,
'PR queue' ,
'Not complete'
]. join ( '\n' ),
2026-05-19 22:09:45 -04:00
'docs/releases/2.0.0-rc.1/operator-readiness-dashboard-2026-05-20.md' : [
'This dashboard is generated by `npm run operator:dashboard`' ,
'operator:dashboard' ,
'Growth Baseline' ,
'hypergrowth release command center' ,
'Prompt-To-Artifact Checklist' ,
'Next Work Order' ,
'ITO-44' ,
'ITO-59' ,
'PR queue' ,
'Not complete'
]. join ( '\n' ),
2026-05-18 06:16:45 -04:00
'docs/releases/2.0.0-rc.1/owner-queue-cleanup-2026-05-18.md' : [
2026-05-18 06:35:44 -04:00
'Owner-wide open PRs after cleanup: 0.' ,
'Owner-wide open issues after cleanup: 0.' ,
2026-05-18 06:16:45 -04:00
'Stale dependency-bot PRs closed: 24.' ,
'Stale legacy payments/0EM roadmap issues closed: 72.'
]. join ( '\n' ),
2026-05-17 15:35:23 -04:00
'docs/HERMES-SETUP.md' : 'Hermes setup Public Release Candidate Scope' ,
'skills/hermes-imports/SKILL.md' : 'Hermes imports Sanitization Checklist Do not ship raw workspace exports Output Contract' ,
2026-05-17 14:45:41 -04:00
'docs/stale-pr-salvage-ledger.md' : [
'Remaining Manual-Review Backlog' ,
'Linear ITO-55' ,
'#1687 zh-CN localization tail' ,
'#1609 Persian README translation' ,
'#1563 zh-TW README sync' ,
'#1564 Turkish README sync' ,
'#1565 pt-BR README sync' ,
'not a release-blocking salvage task'
]. join ( '\n' ),
'docs/legacy-artifact-inventory.md' : [
'Translator/manual review' ,
'ITO-55' ,
'#1687 zh-CN localization tail' ,
'#1609 Persian README translation' ,
'#1563 zh-TW README sync' ,
'#1564 Turkish README sync' ,
'#1565 pt-BR README sync' ,
'no automatic import remains release-blocking'
]. join ( '\n' ),
2026-05-17 15:13:42 -04:00
'docs/architecture/progress-sync-contract.md' : [
'GitHub PRs/issues/discussions Linear project local handoff repo roadmap scripts/work-items.js' ,
'node scripts/work-items.js sync-github --repo <owner/repo>' ,
'node scripts/status.js --json' ,
'Linear remains the external status surface'
]. join ( '\n' ),
2026-05-15 17:55:43 -04:00
'docs/architecture/observability-readiness.md' : 'observability-readiness.js' ,
2026-05-15 22:39:35 -04:00
'docs/security/supply-chain-incident-response.md' : 'TanStack Mini Shai-Hulud node-ipc scan-supply-chain-iocs.js supply-chain-advisory-sources.js' ,
2026-05-17 22:29:06 -04:00
'docs/releases/2.0.0-rc.1/publication-evidence-2026-05-18.md' : [
'TanStack' ,
'Mini Shai-Hulud' ,
'Home persistence IOC scan' ,
'Supply-Chain Watch' ,
'npm signatures' ,
'Node IPC follow-up node-ipc IOC scan'
]. join ( '\n' ),
2026-05-19 07:42:22 -04:00
'docs/releases/2.0.0-rc.1/publication-evidence-2026-05-19.md' : [
'Release video suite' ,
'growth outreach' ,
2026-05-19 07:58:20 -04:00
'Operator dashboard' ,
2026-05-19 07:42:22 -04:00
'GitGuardian' ,
'macOS/Ubuntu/Windows test matrix' ,
2026-05-19 18:51:25 -04:00
'2568 passed' ,
2026-05-19 07:58:20 -04:00
'Business baseline' ,
'$1,728/mo' ,
'$8,272/mo'
]. join ( '\n' ),
'docs/releases/2.0.0/ecc-2-hypergrowth-release-command-center.md' : [
'harness-native operator system' ,
'| MRR | `$1,728/mo` | `$10,000/mo` | `$8,272/mo` |' ,
'Video Suite' ,
'Distribution Plan' ,
'Owner Approvals'
]. join ( '\n' ),
'docs/releases/2.0.0-rc.1/video-suite-production.md' : [
'ECC 2.0 Video Suite Production Manifest' ,
'Primary launch video' ,
'Self-Eval Gate' ,
'timeline'
]. join ( '\n' ),
'docs/releases/2.0.0-rc.1/partner-sponsor-talks-pack.md' : [
'Sponsor Outbound' ,
'Platform Partner DM' ,
'Consulting Intro' ,
'Talk And Podcast Pitch' ,
'GitHub Discussion Announcement' ,
'Do Not Send Or Publish If'
2026-05-19 07:42:22 -04:00
]. join ( '\n' ),
2026-05-15 22:39:35 -04:00
'.github/workflows/supply-chain-watch.yml' : 'name: Supply-Chain Watch supply-chain-advisory-sources.js supply-chain-advisory-sources.json'
2026-05-15 17:55:43 -04:00
};
for ( const [ relativePath , content ] of Object . entries ({ ... files , ... overrides })) {
if ( content === null ) {
continue ;
}
writeFile ( rootDir , relativePath , content );
}
}
function run ( args = [], options = {}) {
return execFileSync ( 'node' , [ SCRIPT , ... args ], {
cwd : options . cwd || path . join ( __dirname , '..' , '..' ),
encoding : 'utf8' ,
stdio : [ 'pipe' , 'pipe' , 'pipe' ],
timeout : 10000
});
}
function runProcess ( args = [], options = {}) {
return spawnSync ( 'node' , [ SCRIPT , ... args ], {
cwd : options . cwd || path . join ( __dirname , '..' , '..' ),
encoding : 'utf8' ,
stdio : [ 'pipe' , 'pipe' , 'pipe' ],
timeout : 10000
});
}
2026-05-17 01:20:13 -04:00
function buildSeededReport ( rootDir ) {
return buildReport ({
allowUntracked : [],
exitCode : false ,
format : 'json' ,
generatedAt : '2026-05-15T00:00:00.000Z' ,
help : false ,
repos : [],
root : rootDir ,
skipGithub : true ,
thresholds : { maxOpenPrs : 20 , maxOpenIssues : 20 , maxDirtyFiles : 0 },
useEnvGithubToken : false ,
writePath : null
});
}
2026-05-15 17:55:43 -04:00
function test ( name , fn ) {
try {
fn ();
console . log ( ` PASS ${ name } ` );
return true ;
} catch ( error ) {
console . log ( ` FAIL ${ name } ` );
console . log ( ` Error: ${ error . message } ` );
return false ;
}
}
function runTests () {
console . log ( '\n=== Testing operator-readiness-dashboard.js ===\n' );
let passed = 0 ;
let failed = 0 ;
if ( test ( 'parseArgs accepts dashboard flags and rejects invalid values' , () => {
const rootDir = createTempDir ( 'operator-dashboard-args-' );
try {
const parsed = parseArgs ([
'node' ,
'script' ,
'--format=json' ,
`--root= ${ rootDir } ` ,
'--skip-github' ,
'--allow-untracked' ,
'docs/drafts/' ,
'--repo' ,
2026-05-19 06:42:17 -04:00
'affaan-m/ECC' ,
2026-05-15 17:55:43 -04:00
'--generated-at' ,
'2026-05-15T00:00:00.000Z'
]);
assert . strictEqual ( parsed . format , 'json' );
assert . strictEqual ( parsed . root , path . resolve ( rootDir ));
assert . strictEqual ( parsed . skipGithub , true );
assert . deepStrictEqual ( parsed . allowUntracked , [ 'docs/drafts/' ]);
2026-05-19 06:42:17 -04:00
assert . deepStrictEqual ( parsed . repos , [ 'affaan-m/ECC' ]);
2026-05-15 17:55:43 -04:00
assert . strictEqual ( parsed . generatedAt , '2026-05-15T00:00:00.000Z' );
assert . throws (() => parseArgs ([ 'node' , 'script' , '--format' , 'xml' ]), /Invalid format/ );
assert . throws (() => parseArgs ([ 'node' , 'script' , '--write' , 'dashboard.md' , '--format' , 'text' ]), /--write requires/ );
assert . throws (() => parseArgs ([ 'node' , 'script' , '--max-open-prs' , 'x' ]), /Invalid --max-open-prs/ );
assert . throws (() => parseArgs ([ 'node' , 'script' , '--unknown' ]), /Unknown argument/ );
} finally {
cleanup ( rootDir );
}
})) passed ++ ; else failed ++ ;
if ( test ( 'seeded repo emits an objective audit with remaining work' , () => {
const rootDir = createTempDir ( 'operator-dashboard-report-' );
try {
seedRepo ( rootDir );
2026-05-17 01:20:13 -04:00
const report = buildSeededReport ( rootDir );
2026-05-15 17:55:43 -04:00
assert . strictEqual ( report . schema_version , 'ecc.operator-readiness-dashboard.v1' );
assert . strictEqual ( report . generatedAt , '2026-05-15T00:00:00.000Z' );
assert . strictEqual ( report . dashboardReady , true );
assert . strictEqual ( report . ready , false );
assert . strictEqual ( report . publicationReady , false );
assert . ok ( report . requirements . some ( item => item . id === 'completion-dashboard' && item . status === 'complete' ));
2026-05-17 15:35:23 -04:00
assert . ok ( report . requirements . some ( item => (
item . id === 'ecc-preview-pack'
&& item . status === 'current'
&& item . evidence . includes ( 'deterministic smoke gate' )
&& item . gap === 'repeat clean-checkout preview-pack smoke before publication'
)));
assert . ok ( report . requirements . some ( item => (
item . id === 'hermes-specialized-skills'
&& item . status === 'current'
&& item . evidence . includes ( 'covered by preview-pack smoke' )
&& item . gap === 'repeat preview-pack smoke before release review'
)));
2026-05-15 17:55:43 -04:00
assert . ok ( report . requirements . some ( item => item . id === 'ecc-tools-next-level' && item . status === 'in_progress' ));
2026-05-16 12:26:39 -04:00
assert . ok ( report . requirements . some ( item => (
item . id === 'agentshield-enterprise-iteration'
2026-05-17 14:26:16 -04:00
&& item . gap === 'deepen live operator approval/readback after Marketplace/payment gates'
&& item . evidence . includes ( 'policy-promotion Action outputs' )
&& item . evidence . includes ( 'hosted promotion judge audit traces' )
)));
assert . ok ( report . requirements . some ( item => (
item . id === 'ecc-tools-next-level'
2026-05-19 23:09:39 -04:00
&& item . gap === 'repeat KV readback and selected-target announcement gate immediately before launch; keep native-payments copy behind the final release, plugin, URL, and owner-approval gates'
2026-05-17 14:26:16 -04:00
&& item . evidence . includes ( 'operator-visible promotion output details' )
&& item . evidence . includes ( 'hosted promotion judge audit traces' )
2026-05-19 22:09:45 -04:00
&& item . evidence . includes ( 'selected-target announcement gate' )
2026-05-19 22:43:19 -04:00
&& item . evidence . includes ( 'billing gate env-file operator path' )
2026-05-19 23:09:39 -04:00
&& item . evidence . includes ( 'non-breaking operator bearer path' )
2026-05-17 14:26:16 -04:00
&& item . evidence . includes ( 'billing announcement preflight' )
2026-05-18 00:36:01 -04:00
&& item . evidence . includes ( 'aggregate production billing KV readback' )
2026-05-19 23:09:39 -04:00
&& item . evidence . includes ( 'Wrangler selected-target readback' )
2026-05-18 03:27:42 -04:00
&& item . evidence . includes ( 'target-account billing readback' )
2026-05-18 00:36:01 -04:00
&& item . evidence . includes ( 'provenance-aware Marketplace billing-state gates' )
2026-05-19 21:39:03 -04:00
&& item . evidence . includes ( 'ready Marketplace Pro target selection' )
2026-05-18 15:23:48 -04:00
&& item . evidence . includes ( 'hosted team-learning feedback controls' )
&& item . evidence . includes ( 'ECC-Tools Dependabot alert remediation' )
2026-05-17 14:26:16 -04:00
)));
2026-05-18 09:10:51 -04:00
assert . ok ( report . requirements . some ( item => (
item . id === 'naming-and-plugin-publication'
&& item . artifact . includes ( 'release-name-plugin-publication checklist' )
&& item . evidence . includes ( 'release publication checklist' )
&& item . gap === 'real tag/push, marketplace submission, and final channel choice remain approval-gated'
)));
2026-05-19 07:58:20 -04:00
assert . deepStrictEqual ( report . growth , {
currentMrr : '$1,728/mo' ,
targetMrr : '$10,000/mo' ,
gapMrr : '$8,272/mo' ,
lanes : [
'GitHub Sponsors and OSS partner sponsors' ,
'ECC Tools Pro subscriptions' ,
'consulting and implementation contracts' ,
'talks, podcasts, conference demos, and partner webinars' ,
],
});
assert . ok ( report . requirements . some ( item => (
item . id === 'hypergrowth-command-center'
&& item . status === 'current'
&& item . evidence . includes ( 'current MRR' )
&& item . gap === 'refresh after every MRR, channel, or approval-state change before public launch'
)));
assert . ok ( report . requirements . some ( item => (
item . id === 'release-video-suite'
&& item . status === 'in_progress'
&& item . evidence . includes ( 'deterministic video-suite gate' )
&& item . gap === 'render final owner-approved MP4s, captions, platform reframes, and editable timeline before posting'
)));
assert . ok ( report . requirements . some ( item => (
item . id === 'partner-sponsor-talks-pack'
&& item . status === 'in_progress'
&& item . evidence . includes ( 'sponsor outbound' )
&& item . gap === 'replace final URLs after publication gates, then get explicit approval before outbound or personal-account posts'
)));
2026-05-19 10:23:34 -04:00
assert . ok ( report . requirements . some ( item => (
item . id === 'owner-approval-packet'
&& item . status === 'current'
&& item . evidence . includes ( 'release, package, plugin, video, billing, social, and outbound decisions' )
&& item . gap === 'review owner approvals from the final release commit before any publication or outbound action'
)));
2026-05-17 14:26:16 -04:00
assert . ok ( report . requirements . some ( item => (
item . id === 'supply-chain-local-protection'
&& item . artifact . includes ( 'AgentShield package-manager hardening' )
&& item . evidence . includes ( 'known AI-tool persistence IOCs' )
&& item . evidence . includes ( 'unsupported npm age-key drift' )
&& item . gap === 'repeat advisory/source refresh and Linear sync after each significant supply-chain batch'
2026-05-16 12:26:39 -04:00
)));
2026-05-17 14:45:41 -04:00
assert . ok ( report . requirements . some ( item => (
item . id === 'legacy-salvage'
&& item . status === 'current'
&& item . evidence . includes ( 'all localization tails are attached to Linear ITO-55' )
&& item . gap === 'repeat legacy scan before release'
)));
2026-05-17 15:13:42 -04:00
assert . ok ( report . requirements . some ( item => (
item . id === 'linear-roadmap-and-progress'
&& item . status === 'current'
2026-05-19 22:09:45 -04:00
&& item . evidence . includes ( 'May 20 Marketplace Pro release-gate comments' )
2026-05-17 15:13:42 -04:00
&& item . gap === 'repeat Linear/project status update and local work-items sync after each significant merge batch'
)));
2026-05-15 17:55:43 -04:00
assert . ok ( report . top_actions . some ( item => item . id === 'naming-and-plugin-publication' ));
2026-05-19 07:58:20 -04:00
assert . ok ( report . top_actions . some ( item => item . id === 'release-video-suite' ));
assert . ok ( report . top_actions . some ( item => item . id === 'partner-sponsor-talks-pack' ));
2026-05-19 10:23:34 -04:00
assert . ok ( ! report . top_actions . some ( item => item . id === 'owner-approval-packet' ));
2026-05-17 15:35:23 -04:00
assert . ok ( ! report . top_actions . some ( item => item . id === 'ecc-preview-pack' ));
assert . ok ( ! report . top_actions . some ( item => item . id === 'hermes-specialized-skills' ));
2026-05-19 07:58:20 -04:00
assert . ok ( ! report . top_actions . some ( item => item . id === 'hypergrowth-command-center' ));
2026-05-17 14:45:41 -04:00
assert . ok ( ! report . top_actions . some ( item => item . id === 'legacy-salvage' ));
2026-05-17 15:13:42 -04:00
assert . ok ( ! report . top_actions . some ( item => item . id === 'linear-roadmap-and-progress' ));
} finally {
cleanup ( rootDir );
}
})) passed ++ ; else failed ++ ;
2026-05-19 09:39:10 -04:00
if ( test ( 'release video suite moves current when publish-candidate evidence is recorded' , () => {
const rootDir = createTempDir ( 'operator-dashboard-video-current-' );
try {
seedRepo ( rootDir , {
'docs/releases/2.0.0-rc.1/publication-evidence-2026-05-19.md' : [
'Release video suite' ,
'growth outreach' ,
'Operator dashboard' ,
'GitGuardian' ,
'macOS/Ubuntu/Windows test matrix' ,
2026-05-19 18:51:25 -04:00
'2568 passed' ,
2026-05-19 09:39:10 -04:00
'Business baseline' ,
'$1,728/mo' ,
'$8,272/mo' ,
'Ready true' ,
'15/15 source assets present' ,
'13/13 render, timeline, caption, EDL, and segment artifacts present' ,
'12/12 publish-candidate outputs present with zero detected black-frame segments' ,
'primary rough render self-eval passed'
]. join ( '\n' )
});
const report = buildSeededReport ( rootDir );
const releaseVideo = report . requirements . find ( item => item . id === 'release-video-suite' );
assert . strictEqual ( releaseVideo . status , 'current' );
assert . ok ( releaseVideo . evidence . includes ( '15/15 source assets' ));
assert . ok ( releaseVideo . evidence . includes ( '12/12 publish candidates' ));
assert . ok ( releaseVideo . evidence . includes ( 'zero detected black-frame segments' ));
assert . strictEqual ( releaseVideo . gap , 'final owner approval, upload, and public video URLs remain approval-gated' );
assert . ok ( ! report . top_actions . some ( item => item . id === 'release-video-suite' ));
assert . ok ( report . next_work_order . some ( item => item . includes ( 'Review the owner-approved primary launch video candidates' )));
} finally {
cleanup ( rootDir );
}
})) passed ++ ; else failed ++ ;
2026-05-17 15:13:42 -04:00
if ( test ( 'Linear progress stays in progress until live sync evidence is mirrored' , () => {
const rootDir = createTempDir ( 'operator-dashboard-linear-progress-' );
try {
seedRepo ( rootDir , {
'docs/ECC-2.0-GA-ROADMAP.md' : [
'https://linear.app/itomarkets/project/ecc-platform-roadmap-52b328ee03e1' ,
'Linear ITO-44 ITO-59' ,
'AgentShield Enterprise Iteration' ,
'ECC-Tools PR #78' ,
'hosted promotion' ,
'announcementGate' ,
'ITO-55'
]. join ( '\n' )
});
const report = buildSeededReport ( rootDir );
const linearProgress = report . requirements . find ( item => item . id === 'linear-roadmap-and-progress' );
assert . strictEqual ( linearProgress . status , 'in_progress' );
assert . strictEqual ( linearProgress . evidence , 'repo mirror and progress-sync contract are present' );
assert . strictEqual ( linearProgress . gap , 'recurring Linear status sync and productized realtime sync remain pending' );
assert . ok ( report . top_actions . some ( item => item . id === 'linear-roadmap-and-progress' ));
2026-05-15 17:55:43 -04:00
} finally {
cleanup ( rootDir );
}
})) passed ++ ; else failed ++ ;
2026-05-17 15:35:23 -04:00
if ( test ( 'preview pack and Hermes gates stay in progress until smoke gate is wired' , () => {
const rootDir = createTempDir ( 'operator-dashboard-preview-smoke-' );
try {
seedRepo ( rootDir , {
'package.json' : JSON . stringify ({
files : [
'scripts/observability-readiness.js' ,
'scripts/operator-readiness-dashboard.js' ,
'scripts/platform-audit.js'
],
scripts : {
'discussion:audit' : 'node scripts/discussion-audit.js' ,
'observability:ready' : 'node scripts/observability-readiness.js' ,
'operator:dashboard' : 'node scripts/operator-readiness-dashboard.js' ,
'platform:audit' : 'node scripts/platform-audit.js' ,
'security:ioc-scan' : 'node scripts/ci/scan-supply-chain-iocs.js' ,
'security:advisory-sources' : 'node scripts/ci/supply-chain-advisory-sources.js'
}
}, null , 2 ),
'scripts/preview-pack-smoke.js' : null ,
'docs/releases/2.0.0-rc.1/preview-pack-manifest.md' : 'publication-readiness.md release-notes.md quickstart.md'
});
const report = buildSeededReport ( rootDir );
const previewPack = report . requirements . find ( item => item . id === 'ecc-preview-pack' );
const hermes = report . requirements . find ( item => item . id === 'hermes-specialized-skills' );
assert . strictEqual ( previewPack . status , 'in_progress' );
assert . strictEqual ( previewPack . gap , 'final clean-checkout release approval and publish evidence still pending' );
assert . strictEqual ( hermes . status , 'in_progress' );
assert . strictEqual ( hermes . gap , 'final preview-pack smoke and release review pending' );
assert . ok ( report . top_actions . some ( item => item . id === 'ecc-preview-pack' ));
assert . ok ( report . top_actions . some ( item => item . id === 'hermes-specialized-skills' ));
} finally {
cleanup ( rootDir );
}
})) passed ++ ; else failed ++ ;
2026-05-19 10:23:34 -04:00
if ( test ( 'owner approval packet fails closed when it is missing from the release pack' , () => {
const rootDir = createTempDir ( 'operator-dashboard-owner-packet-' );
try {
seedRepo ( rootDir , {
'docs/releases/2.0.0-rc.1/owner-approval-packet-2026-05-19.md' : null ,
'docs/releases/2.0.0-rc.1/preview-pack-manifest.md' : [
'publication-readiness.md release-notes.md quickstart.md' ,
'release-name-plugin-publication-checklist-2026-05-18.md' ,
'`scripts/preview-pack-smoke.js`' ,
'npm run preview-pack:smoke'
]. join ( '\n' )
});
const report = buildSeededReport ( rootDir );
const ownerPacket = report . requirements . find ( item => item . id === 'owner-approval-packet' );
assert . strictEqual ( ownerPacket . status , 'not_complete' );
assert . strictEqual ( ownerPacket . evidence , 'owner approval packet is missing or incomplete' );
assert . strictEqual ( ownerPacket . gap , 'add the owner decision sheet before publication review' );
assert . ok ( report . top_actions . some ( item => item . id === 'owner-approval-packet' ));
} finally {
cleanup ( rootDir );
}
})) passed ++ ; else failed ++ ;
2026-05-17 01:20:13 -04:00
if ( test ( 'AgentShield enterprise evidence covers export and policy promotion markers' , () => {
const cases = [
{
marker : 'AgentShield PR #92' ,
gap : 'workflow automation around protected rollout and richer runtime review UX pending after policy promotion shipped'
},
{
marker : 'AgentShield #92' ,
gap : 'workflow automation around protected rollout and richer runtime review UX pending after policy promotion shipped'
},
{
marker : 'policy promote' ,
gap : 'workflow automation around protected rollout and richer runtime review UX pending after policy promotion shipped'
},
{
marker : 'checksum-verified policy promotion' ,
gap : 'workflow automation around protected rollout and richer runtime review UX pending after policy promotion shipped'
},
2026-05-17 14:26:16 -04:00
{
marker : 'hosted promotion judge audit traces' ,
gap : 'deepen live operator approval/readback after Marketplace/payment gates'
},
2026-05-17 01:20:13 -04:00
{
marker : '#78-#91' ,
gap : 'workflow automation plus policy promotion/review UX pending after policy export shipped'
},
{
marker : 'AgentShield PR #91' ,
gap : 'workflow automation plus policy promotion/review UX pending after policy export shipped'
},
{
marker : 'AgentShield #91' ,
gap : 'workflow automation plus policy promotion/review UX pending after policy export shipped'
},
{
marker : 'checksum-backed policy export' ,
gap : 'workflow automation plus policy promotion/review UX pending after policy export shipped'
},
{
marker : '#78-#90' ,
gap : 'durable policy export and fleet-review workflow automation remain pending after reviewItems shipped'
}
];
for ( const { marker , gap } of cases ) {
const rootDir = createTempDir ( 'operator-dashboard-agentshield-' );
try {
seedRepo ( rootDir , {
'docs/ECC-2.0-GA-ROADMAP.md' : [
'https://linear.app/itomarkets/project/ecc-platform-roadmap-52b328ee03e1' ,
'Linear ITO-44 ITO-59' ,
'AgentShield Enterprise Iteration' ,
marker ,
'ECC-Tools PR #78' ,
'hosted promotion' ,
'announcementGate' ,
'ITO-55'
]. join ( '\n' )
});
const report = buildSeededReport ( rootDir );
const item = report . requirements . find ( requirement => requirement . id === 'agentshield-enterprise-iteration' );
assert . strictEqual ( item . status , 'in_progress' , marker );
assert . strictEqual ( item . gap , gap , marker );
} finally {
cleanup ( rootDir );
}
}
})) passed ++ ; else failed ++ ;
2026-05-16 03:37:10 -04:00
if ( test ( 'legacy salvage recognizes the real manual-review backlog heading' , () => {
const rootDir = createTempDir ( 'operator-dashboard-legacy-salvage-' );
try {
seedRepo ( rootDir , {
'docs/ECC-2.0-GA-ROADMAP.md' : [
'https://linear.app/itomarkets/project/ecc-platform-roadmap-52b328ee03e1' ,
'Linear ITO-44 ITO-59' ,
2026-05-17 01:10:12 -04:00
'AgentShield PR #92 #78-#92 checksum-backed policy export policy promote checksum-verified policy promotion' ,
2026-05-16 03:37:10 -04:00
'AgentShield Enterprise Iteration' ,
'ECC-Tools PR #78' ,
'hosted promotion' ,
'announcementGate'
]. join ( '\n' ),
'docs/stale-pr-salvage-ledger.md' : [
'# Stale PR Salvage Ledger' ,
'' ,
'## Remaining Manual-Review Backlog' ,
'' ,
'- #1609 Persian README translation' ,
'- #1563 zh-TW README sync'
]. join ( '\n' )
});
2026-05-17 01:20:13 -04:00
const report = buildSeededReport ( rootDir );
2026-05-16 03:37:10 -04:00
const legacySalvage = report . requirements . find ( item => item . id === 'legacy-salvage' );
assert . strictEqual ( legacySalvage . status , 'in_progress' );
} finally {
cleanup ( rootDir );
}
})) passed ++ ; else failed ++ ;
2026-05-15 17:55:43 -04:00
if ( test ( 'markdown output can be written as the dashboard artifact' , () => {
const rootDir = createTempDir ( 'operator-dashboard-markdown-' );
const outputPath = path . join ( rootDir , 'artifacts' , 'dashboard.md' );
try {
seedRepo ( rootDir );
const stdout = run ([
'--markdown' ,
'--skip-github' ,
`--root= ${ rootDir } ` ,
'--generated-at=2026-05-15T00:00:00.000Z' ,
'--write' ,
outputPath
], { cwd : rootDir });
const written = fs . readFileSync ( outputPath , 'utf8' );
assert . strictEqual ( stdout , written );
assert . ok ( written . includes ( '# ECC Operator Readiness Dashboard' ));
assert . ok ( written . includes ( 'Generated: 2026-05-15T00:00:00.000Z' ));
2026-05-19 07:58:20 -04:00
assert . ok ( written . includes ( '## Growth Baseline' ));
assert . ok ( written . includes ( '| MRR | $1,728/mo | $10,000/mo | $8,272/mo |' ));
2026-05-15 17:55:43 -04:00
assert . ok ( written . includes ( '## Prompt-To-Artifact Checklist' ));
assert . ok ( written . includes ( 'Build ITO-44 completion dashboard into a repeatable command' ));
assert . ok ( written . includes ( '## Next Work Order' ));
} finally {
cleanup ( rootDir );
}
})) passed ++ ; else failed ++ ;
2026-05-15 22:18:48 -04:00
if ( test ( 'text output renders compact status and top actions' , () => {
const rootDir = createTempDir ( 'operator-dashboard-text-' );
try {
seedRepo ( rootDir );
const stdout = run ([
'--format=text' ,
'--skip-github' ,
`--root= ${ rootDir } ` ,
'--generated-at=2026-05-15T00:00:00.000Z'
], { cwd : rootDir });
assert . ok ( stdout . includes ( 'ECC Operator Readiness Dashboard' ));
assert . ok ( stdout . includes ( 'work remaining' ));
assert . ok ( stdout . includes ( 'Dashboard ready: true' ));
assert . ok ( stdout . includes ( 'Publication ready: false' ));
2026-05-19 07:58:20 -04:00
assert . ok ( stdout . includes ( 'MRR: $1,728/mo -> $10,000/mo (gap $8,272/mo)' ));
2026-05-15 22:18:48 -04:00
assert . ok ( stdout . includes ( 'Top actions:' ));
assert . ok ( stdout . includes ( 'naming-and-plugin-publication' ));
} finally {
cleanup ( rootDir );
}
})) passed ++ ; else failed ++ ;
if ( test ( 'renderers handle a ready report with no top actions' , () => {
const report = {
dashboardReady : true ,
generatedAt : '2026-05-15T00:00:00.000Z' ,
head : 'abc123' ,
next_work_order : [ 'Ship release evidence' ],
platform : {
blockingDirtyCount : 0 ,
discussionsMissingAcceptedAnswer : 0 ,
discussionsNeedingMaintainerTouch : 0 ,
githubSkipped : false ,
ignoredDirtyCount : 0 ,
openIssues : 1 ,
openPrs : 1 ,
ready : true
},
publicationReady : true ,
ready : true ,
requirements : [
{
artifact : 'artifact.md' ,
evidence : 'verified' ,
gap : '' ,
id : 'release' ,
requirement : 'Release is approved' ,
status : 'complete'
}
],
top_actions : []
};
const text = renderText ( report );
assert . ok ( text . includes ( 'objective ready' ));
assert . ok ( text . includes ( 'Commit: abc123' ));
assert . ok ( text . includes ( ' none' ));
const markdown = renderMarkdown ( report );
assert . ok ( markdown . includes ( 'Status: objective ready' ));
assert . ok ( markdown . includes ( '| PR queue | Current | 1 open PRs across tracked repos |' ));
assert . ok ( markdown . includes ( '| Publication | Ready |' ));
assert . ok ( markdown . includes ( '- none' ));
})) passed ++ ; else failed ++ ;
2026-05-15 17:55:43 -04:00
if ( test ( 'exit-code mode fails closed while macro objective has gaps' , () => {
const rootDir = createTempDir ( 'operator-dashboard-exit-' );
try {
seedRepo ( rootDir );
const result = runProcess ([
'--json' ,
'--skip-github' ,
`--root= ${ rootDir } ` ,
'--generated-at=2026-05-15T00:00:00.000Z' ,
'--exit-code'
], { cwd : rootDir });
assert . strictEqual ( result . status , 2 );
assert . strictEqual ( result . stderr , '' );
assert . ok ( result . stdout . includes ( '"ready": false' ));
assert . ok ( result . stdout . includes ( '"publicationReady": false' ));
} finally {
cleanup ( rootDir );
}
})) passed ++ ; else failed ++ ;
2026-05-15 22:18:48 -04:00
if ( test ( 'cli help exits successfully and invalid cli flags fail before reporting' , () => {
const help = runProcess ([ '--help' ]);
assert . strictEqual ( help . status , 0 );
assert . strictEqual ( help . stderr , '' );
assert . ok ( help . stdout . includes ( 'Usage: node scripts/operator-readiness-dashboard.js' ));
assert . ok ( help . stdout . includes ( '--write <path>' ));
const invalid = runProcess ([ '--format=xml' ]);
assert . strictEqual ( invalid . status , 1 );
assert . strictEqual ( invalid . stdout , '' );
assert . match ( invalid . stderr , /Error: Invalid format/ );
})) passed ++ ; else failed ++ ;
2026-05-15 17:55:43 -04:00
console . log ( `\nPassed: ${ passed } ` );
console . log ( `Failed: ${ failed } ` );
if ( failed > 0 ) {
process . exit ( 1 );
}
}
if ( require . main === module ) {
runTests ();
}