340 lines
10 KiB
JavaScript
340 lines
10 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const repoRoot = path.resolve(__dirname, '..');
|
|
const finalRoot = path.join(
|
|
repoRoot,
|
|
'examples',
|
|
'scene_skill_102_runtime_semantics_rematerialization_2026-04-21',
|
|
);
|
|
const skillRoot = path.join(finalRoot, 'skills');
|
|
const indexPath = path.join(finalRoot, 'scene_skill_102_index.json');
|
|
const outputPath = path.join(
|
|
repoRoot,
|
|
'tests',
|
|
'fixtures',
|
|
'generated_scene',
|
|
'scene_skill_102_runtime_semantics_full_direct_mock_execution_2026-04-21.json',
|
|
);
|
|
const reportPath = path.join(
|
|
repoRoot,
|
|
'docs',
|
|
'superpowers',
|
|
'reports',
|
|
'2026-04-21-generated-scene-runtime-semantics-full-direct-mock-execution-report.md',
|
|
);
|
|
|
|
function readJson(file) {
|
|
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
}
|
|
|
|
function writeJson(file, value) {
|
|
fs.writeFileSync(file, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
|
}
|
|
|
|
function findScript(sceneId) {
|
|
const scriptsDir = path.join(skillRoot, sceneId, 'scripts');
|
|
const scripts = fs
|
|
.readdirSync(scriptsDir)
|
|
.filter((name) => name.endsWith('.js') && !name.endsWith('.test.js'))
|
|
.sort();
|
|
if (scripts.length === 0) throw new Error(`no runtime script found for ${sceneId}`);
|
|
return path.join(scriptsDir, scripts[0]);
|
|
}
|
|
|
|
function createResponse(payload) {
|
|
return {
|
|
ok: true,
|
|
status: 200,
|
|
async json() {
|
|
return payload;
|
|
},
|
|
};
|
|
}
|
|
|
|
function parseBody(body) {
|
|
if (!body || typeof body !== 'string') return {};
|
|
try {
|
|
return JSON.parse(body);
|
|
} catch (_) {
|
|
return Object.fromEntries(
|
|
body
|
|
.split('&')
|
|
.filter(Boolean)
|
|
.map((part) => {
|
|
const [key, value = ''] = part.split('=');
|
|
return [decodeURIComponent(key), decodeURIComponent(value)];
|
|
}),
|
|
);
|
|
}
|
|
}
|
|
|
|
function makeRichRow() {
|
|
return {
|
|
id: 'row-1',
|
|
wkOrderNo: 'WK-1',
|
|
appNo: 'WK-1',
|
|
countyCodeName: 'COUNTY-1',
|
|
countyName: 'COUNTY-1',
|
|
orgNo: 'ORG-1',
|
|
orgName: 'ORG-NAME',
|
|
ORG_NAME: 'ORG-NAME',
|
|
YGDL: '100',
|
|
LINE_LOSS_RATE: '1.23',
|
|
PPQ: '10',
|
|
UPQ: '9',
|
|
LOSS_PQ: '1',
|
|
dyfjmZs: '1',
|
|
dyfjmHgZs: '1',
|
|
dyfjmHgl: '100%',
|
|
gyxzzrZs: '1',
|
|
gyxzzrHgZs: '1',
|
|
gyxzzrHgl: '100%',
|
|
dyjmZs: '1',
|
|
dyjmHgZs: '1',
|
|
dyjmHgl: '100%',
|
|
assetId: 'ASSET-1',
|
|
devId: 'DEV-1',
|
|
deviceName: 'DEVICE-1',
|
|
dataStatus: 'ok',
|
|
status: 5,
|
|
value: 'ok',
|
|
};
|
|
}
|
|
|
|
function installGlobals(archetype, expectedDomain, requestLog) {
|
|
delete global.$;
|
|
global.location = { hostname: expectedDomain || '' };
|
|
global.window = global;
|
|
global.document = {
|
|
title: 'mock page',
|
|
body: {},
|
|
querySelector() {
|
|
return null;
|
|
},
|
|
querySelectorAll() {
|
|
return [];
|
|
},
|
|
};
|
|
global.fetch = async (url, options = {}) => {
|
|
requestLog.push({
|
|
via: 'fetch',
|
|
url: String(url),
|
|
method: options.method || 'GET',
|
|
});
|
|
const body = parseBody(options.body);
|
|
const row = makeRichRow();
|
|
if (archetype === 'paginated_enrichment') {
|
|
if (body.page && Number(body.page) > 1) return createResponse({ data: [] });
|
|
if (String(url).includes('yx.gs.sgcc.com.cn')) {
|
|
return createResponse({ data: { enrichmentField: 'detail', ...row } });
|
|
}
|
|
return createResponse({ data: [row], rows: [row], content: [row] });
|
|
}
|
|
if (archetype === 'multi_mode_request') {
|
|
return createResponse({ content: [row], data: [row], rows: [row] });
|
|
}
|
|
if (archetype === 'single_request_enrichment') {
|
|
return createResponse({ data: [row], rows: [row], content: [row] });
|
|
}
|
|
if (archetype === 'multi_endpoint_inventory') {
|
|
return createResponse({ rows: [row], data: [row], content: [row] });
|
|
}
|
|
if (archetype === 'local_doc_pipeline') {
|
|
return createResponse({ ok: true, data: [row], rows: [row] });
|
|
}
|
|
if (archetype === 'page_state_eval') {
|
|
return createResponse({ data: [row], rows: [row] });
|
|
}
|
|
return createResponse({ data: [row], rows: [row], content: [row] });
|
|
};
|
|
}
|
|
|
|
function makeDeps(archetype, requestLog) {
|
|
const validatePageContext = () => ({ ok: true });
|
|
if (archetype === 'host_bridge_workflow') {
|
|
return {
|
|
validatePageContext,
|
|
async invokeHostBridge(action, args) {
|
|
requestLog.push({ via: 'host-bridge', action });
|
|
return { ok: true, action, callbackId: 'mock-callback' };
|
|
},
|
|
async queryCallbackEndpoint(endpoint, args) {
|
|
requestLog.push({ via: 'callback', endpoint: endpoint.name });
|
|
return { data: [makeRichRow()], rows: [makeRichRow()] };
|
|
},
|
|
};
|
|
}
|
|
if (archetype === 'page_state_eval') {
|
|
return {
|
|
validatePageContext,
|
|
async queryState(args) {
|
|
requestLog.push({ via: 'page-state' });
|
|
return { data: [makeRichRow()] };
|
|
},
|
|
};
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
async function runScene(row) {
|
|
const started = Date.now();
|
|
const { sceneId, sceneName, archetype } = row;
|
|
let scriptPath = '';
|
|
const requestLog = [];
|
|
try {
|
|
scriptPath = findScript(sceneId);
|
|
const report = readJson(
|
|
path.join(skillRoot, sceneId, 'references', 'generation-report.json'),
|
|
);
|
|
const expectedDomain = report.bootstrap?.expectedDomain || '';
|
|
installGlobals(archetype, expectedDomain, requestLog);
|
|
delete require.cache[require.resolve(scriptPath)];
|
|
const mod = require(scriptPath);
|
|
if (typeof mod.buildBrowserEntrypointResult !== 'function') {
|
|
return {
|
|
sceneId,
|
|
sceneName,
|
|
archetype,
|
|
directMockStatus: 'direct-mock-fail',
|
|
scriptLoadStatus: 'script-load-pass',
|
|
failureReason: 'missing_buildBrowserEntrypointResult_export',
|
|
durationMs: Date.now() - started,
|
|
requestCount: requestLog.length,
|
|
};
|
|
}
|
|
|
|
const args = {
|
|
expected_domain: expectedDomain,
|
|
target_url: report.bootstrap?.targetUrl || '',
|
|
org_label: 'MOCK_ORG',
|
|
org_code: 'MOCK_ORG_CODE',
|
|
period_mode: report.defaultMode || 'month',
|
|
period_mode_code: report.defaultMode || 'month',
|
|
period_value: '2026-04',
|
|
period_payload: {},
|
|
};
|
|
const deps = makeDeps(archetype, requestLog);
|
|
const artifact = deps
|
|
? await mod.buildBrowserEntrypointResult(args, deps)
|
|
: await mod.buildBrowserEntrypointResult(args);
|
|
const artifactStatus = artifact?.status || 'missing';
|
|
const passStatuses = new Set(['ok', 'empty']);
|
|
const partialStatuses = new Set(['partial']);
|
|
const directMockStatus = passStatuses.has(artifactStatus)
|
|
? 'direct-mock-pass'
|
|
: partialStatuses.has(artifactStatus)
|
|
? 'direct-mock-partial'
|
|
: 'direct-mock-fail';
|
|
return {
|
|
sceneId,
|
|
sceneName,
|
|
archetype,
|
|
directMockStatus,
|
|
scriptLoadStatus: 'script-load-pass',
|
|
mockDependencyStatus: 'mock-dependency-ready',
|
|
artifactStatus,
|
|
artifactType: artifact?.type || null,
|
|
rowCount: Array.isArray(artifact?.rows) ? artifact.rows.length : null,
|
|
counts: artifact?.counts || {},
|
|
failureReason:
|
|
directMockStatus === 'direct-mock-fail' ? `artifact_status_${artifactStatus}` : null,
|
|
durationMs: Date.now() - started,
|
|
requestCount: requestLog.length,
|
|
scriptPath: path.relative(repoRoot, scriptPath).replace(/\\/g, '/'),
|
|
};
|
|
} catch (error) {
|
|
return {
|
|
sceneId,
|
|
sceneName,
|
|
archetype,
|
|
directMockStatus: 'direct-mock-fail',
|
|
scriptLoadStatus: scriptPath ? 'script-load-pass' : 'script-load-fail',
|
|
failureReason: error.message,
|
|
durationMs: Date.now() - started,
|
|
requestCount: requestLog.length,
|
|
scriptPath: scriptPath ? path.relative(repoRoot, scriptPath).replace(/\\/g, '/') : null,
|
|
};
|
|
}
|
|
}
|
|
|
|
async function main() {
|
|
const index = readJson(indexPath);
|
|
const scenes = Array.isArray(index) ? index : index.scenes;
|
|
const results = [];
|
|
for (const row of scenes) {
|
|
results.push(await runScene(row));
|
|
}
|
|
|
|
const byStatus = {};
|
|
const byArchetype = {};
|
|
for (const result of results) {
|
|
byStatus[result.directMockStatus] = (byStatus[result.directMockStatus] || 0) + 1;
|
|
byArchetype[result.archetype] = byArchetype[result.archetype] || {};
|
|
byArchetype[result.archetype][result.directMockStatus] =
|
|
(byArchetype[result.archetype][result.directMockStatus] || 0) + 1;
|
|
}
|
|
|
|
const asset = {
|
|
runDate: '2026-04-21',
|
|
plan: '2026-04-21-generated-scene-runtime-semantics-validation-refresh-execution-plan.md',
|
|
execution: 'full-direct-mock-only-no-browser-no-network',
|
|
summary: {
|
|
totalScenes: results.length,
|
|
byStatus,
|
|
byArchetype,
|
|
productionNetworkUsed: false,
|
|
realBrowserUsed: false,
|
|
generatedSkillsModified: false,
|
|
},
|
|
results,
|
|
};
|
|
writeJson(outputPath, asset);
|
|
|
|
const lines = [];
|
|
lines.push('# Scene Skill 102 Full Direct Mock Execution Report');
|
|
lines.push('');
|
|
lines.push('> Date: 2026-04-21');
|
|
lines.push('> Plan: `2026-04-21-generated-scene-runtime-semantics-validation-refresh-execution-plan.md`');
|
|
lines.push('');
|
|
lines.push('## Scope');
|
|
lines.push('');
|
|
lines.push('This run executed all 102 generated scene skill scripts in a local mock runtime. It did not use a real browser, real network, production credentials, or business systems. It did not modify generated skill packages.');
|
|
lines.push('');
|
|
lines.push('## Summary');
|
|
lines.push('');
|
|
lines.push('| Status | Count |');
|
|
lines.push('| --- | ---: |');
|
|
for (const [status, count] of Object.entries(byStatus).sort()) {
|
|
lines.push(`| \`${status}\` | ${count} |`);
|
|
}
|
|
lines.push('');
|
|
lines.push('## By Archetype');
|
|
lines.push('');
|
|
lines.push('| Archetype | Result |');
|
|
lines.push('| --- | --- |');
|
|
for (const [archetype, statuses] of Object.entries(byArchetype).sort()) {
|
|
const summary = Object.entries(statuses)
|
|
.map(([status, count]) => `${status}: ${count}`)
|
|
.join(', ');
|
|
lines.push(`| \`${archetype}\` | ${summary} |`);
|
|
}
|
|
lines.push('');
|
|
lines.push('## Interpretation');
|
|
lines.push('');
|
|
lines.push('Direct mock execution passing means every generated skill entrypoint can load and complete against controlled fake dependencies. It still does not mean production execution passed.');
|
|
lines.push('');
|
|
lines.push('## Next Step');
|
|
lines.push('');
|
|
lines.push('If this full direct mock run is acceptable, the next bounded stage is pseudo-production batch selection. That stage should choose a small, archetype-balanced batch for real or quasi-real environment execution planning.');
|
|
lines.push('');
|
|
fs.writeFileSync(reportPath, `${lines.join('\n')}\n`, 'utf8');
|
|
|
|
console.log(JSON.stringify(asset.summary, null, 2));
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error(error);
|
|
process.exit(1);
|
|
});
|