'use strict'; const assert = require('node:assert/strict'); const fs = require('node:fs'); const os = require('node:os'); const path = require('node:path'); const test = require('node:test'); const { BASE_REVISION, IDENTITY, SAMPLE_OFFSETS_MS, SCHEMA, createPs5ShellUiTransitionHostPlan, normalizeShellUiTransitionObservation, } = require('../../src/services/ps5-shellui-transition-observation'); const { P15_SHELLUI_TRANSITION_DIAGNOSTIC, createPs5UploadPackageInstallerProvider, } = require('../../src/services/ps5upload-package-installer-provider'); const OPERATION_ID = '123e4567-e89b-42d3-a456-426614174000'; const TITLE_ID = 'CHIM00003'; function attempt() { return { index: 1, path: 'shellui_rpc', call_entered: true, dispatched: true, sony_return_observed: true, raw_rax_u64_hex: '0x0000000000000000', raw_low32_hex: '0x00000000', raw_signed32: 0, timed_out: false, tracee_lost: false, wrapper_rc: 0, wrapper_hex: '0x00000000', branch_taken: 'accepted', }; } function observation({ preAppId = 0, appIds = [0, 0, 0, 0, 0, 0, 0], shellUiPids = [58, 58, 58, 58, 58, 58, 58], complete = true } = {}) { return { schema: SCHEMA, diagnostic_identity: IDENTITY, base_revision: BASE_REVISION, operation_id: OPERATION_ID, title_id: TITLE_ID, shellui: { pid_before: 58, pid_after: 58, same_process: true, init_rc: 0, ready: true }, foreground_user_present: true, attempts: [attempt()], internal_retry_occurred: false, retry_delay_ms: 0, terminal_attempt_index: 1, terminal_wrapper_rc: 0, terminal_wrapper_hex: '0x00000000', provider_normalized_result: 'accepted', provider_accepted: true, application_transition: { pre_running_big_app_id: preAppId, samples: SAMPLE_OFFSETS_MS.map((offset, index) => ({ scheduled_offset_ms: offset, observed_after_ms: offset, running_big_app_id: appIds[index], shellui_pid: shellUiPids[index], process_inventory_complete: complete, target_pid: null, })), }, }; } test('accepted LNC without big-app allocation is normalized as the first downstream boundary', () => { const result = normalizeShellUiTransitionObservation(observation(), { operationId: OPERATION_ID, titleId: TITLE_ID }); assert.equal(result.providerAccepted, true); assert.equal(result.applicationTransition.classification, 'accepted_lnc_without_big_app_transition'); assert.equal(result.applicationTransition.bigAppTransitionObserved, false); assert.equal(result.applicationTransition.exactTargetProcessObserved, false); assert.equal(result.applicationTransition.outcomeQualified, true); assert.equal(result.applicationTransition.timingQualified, true); assert.deepEqual(result.applicationTransition.sampleOffsetsMs, SAMPLE_OFFSETS_MS); }); test('Sony no-big-app sentinel -1 is idle while other negative values stay fail-closed', () => { const idle = normalizeShellUiTransitionObservation(observation({ preAppId: -1, appIds: [-1, -1, -1, -1, -1, -1, -1] }), { operationId: OPERATION_ID, titleId: TITLE_ID }); assert.equal(idle.applicationTransition.cleanBaseline, true); assert.equal(idle.applicationTransition.bigAppSampleDomainQualified, true); assert.equal(idle.applicationTransition.classification, 'accepted_lnc_without_big_app_transition'); assert.equal(idle.applicationTransition.outcomeQualified, true); assert.equal(idle.applicationTransition.baselineIdleSentinel, -1); const ambiguous = normalizeShellUiTransitionObservation(observation({ preAppId: -2, appIds: [-2, -2, -2, -2, -2, -2, -2] }), { operationId: OPERATION_ID, titleId: TITLE_ID }); assert.equal(ambiguous.applicationTransition.cleanBaseline, false); assert.equal(ambiguous.applicationTransition.bigAppSampleDomainQualified, false); assert.equal(ambiguous.applicationTransition.classification, 'outcome_unknown'); }); test('new positive running-big-app id is retained but never promoted to exact executable proof', () => { const result = normalizeShellUiTransitionObservation(observation({ appIds: [0, 0, 0, 41, 41, 41, 41] }), { operationId: OPERATION_ID, titleId: TITLE_ID }); assert.equal(result.applicationTransition.classification, 'big_app_transition_observed_unattributed'); assert.equal(result.applicationTransition.bigAppTransitionObserved, true); assert.equal(result.applicationTransition.firstPositiveOffsetMs, 250); assert.equal(result.applicationTransition.exactTargetProcessObserved, false); assert.equal(result.applicationTransition.launchVerified, false); assert.equal(result.applicationTransition.productionRestorable, false); }); test('pre-existing big app, incomplete inventory, ShellUI drift and hostile sample shapes fail closed', () => { for (const input of [ observation({ preAppId: 7, appIds: [7, 7, 7, 7, 7, 7, 7] }), observation({ complete: false }), observation({ shellUiPids: [58, 58, 58, 59, 59, 59, 59] }), ]) { const result = normalizeShellUiTransitionObservation(input, { operationId: OPERATION_ID, titleId: TITLE_ID }); assert.equal(result.applicationTransition.classification, 'outcome_unknown'); assert.equal(result.applicationTransition.outcomeQualified, false); } const missing = observation(); missing.application_transition.samples.pop(); assert.throws(() => normalizeShellUiTransitionObservation(missing, { operationId: OPERATION_ID, titleId: TITLE_ID }), error => error.code === 'PS5_FPKG_TRANSITION_OBSERVATION_INVALID'); const extra = observation(); extra.application_transition.samples[0].raw_pointer = '0xdeadbeef'; assert.throws(() => normalizeShellUiTransitionObservation(extra, { operationId: OPERATION_ID, titleId: TITLE_ID }), error => error.code === 'PS5_FPKG_TRANSITION_OBSERVATION_INVALID'); const slow = observation(); slow.application_transition.samples.at(-1).observed_after_ms = 5001; const slowResult = normalizeShellUiTransitionObservation(slow, { operationId: OPERATION_ID, titleId: TITLE_ID }); assert.equal(slowResult.applicationTransition.classification, 'outcome_unknown'); assert.equal(slowResult.applicationTransition.timingQualified, false); }); test('host plan is digest-bound, observation-only and forbids every consumed title', () => { const plan = createPs5ShellUiTransitionHostPlan({ engine: { path: 'build/diagnostics/ps5upload-p15-a10/ps5upload-engine', size: 123, sha256: 'a'.repeat(64) }, payload: { path: 'build/diagnostics/ps5upload-p15-a10/ps5upload.elf', size: 456, sha256: 'b'.repeat(64) }, source: { revision: BASE_REVISION, sha256: 'c'.repeat(64), diffSha256: 'd'.repeat(64) }, previousResult: { titleId: 'CHIM00002', installedObserved: true, launchRequests: 1, clientRetries: 0, terminalCode: 'PS5_FPKG_SHELLUI_LAUNCH_FLOW_FAILED' }, }); assert.match(plan.planDigest, /^[a-f0-9]{64}$/); assert.equal(plan.liveActionAuthorized, false); assert.equal(plan.requiresFreshDistinctTitle, true); assert.deepEqual(plan.consumedTitleIds, ['FAKE00000', 'CHIM00001', 'CHIM00002']); assert.equal(plan.runtime.identity, IDENTITY); assert.deepEqual(plan.runtime.sampleOffsetsMs, SAMPLE_OFFSETS_MS); assert.equal(Object.isFrozen(plan), true); assert.throws(() => createPs5ShellUiTransitionHostPlan({ engine: { path: 'x', size: 1, sha256: 'a'.repeat(64) }, payload: { path: 'y', size: 1, sha256: 'b'.repeat(64) }, source: { revision: BASE_REVISION, sha256: 'c'.repeat(64), diffSha256: 'd'.repeat(64) }, previousResult: { titleId: 'CHIM00002', installedObserved: false, launchRequests: 1, clientRetries: 0, terminalCode: 'PS5_FPKG_SHELLUI_LAUNCH_FLOW_FAILED' }, }), error => error.code === 'PS5_FPKG_TRANSITION_PLAN_PRECONDITION'); }); function response(body, status = 200) { const serialized = JSON.stringify(body); return { ok: status >= 200 && status < 300, status, headers: { get: name => name === 'content-length' ? String(Buffer.byteLength(serialized)) : null }, json: async () => body, text: async () => serialized }; } test('provider accepts only the exact A10 diagnostic identity and returns correlated transition evidence', async t => { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'chimera-a10-provider-')); t.after(() => fs.rmSync(root, { recursive: true, force: true })); const pkg = path.join(root, 'control.pkg'); const bytes = Buffer.concat([Buffer.from([0x7f, 0x46, 0x49, 0x48]), Buffer.alloc(64, 0x41)]); fs.writeFileSync(pkg, bytes); const component = { type: 'base', componentId: 'base', size: bytes.length, sha256: require('node:crypto').createHash('sha256').update(bytes).digest('hex'), contentId: `UP9000-${TITLE_ID}_00-A10CONTROL000003`, metadata: { absolutePath: pkg, titleId: { value: TITLE_ID } } }; const provider = createPs5UploadPackageInstallerProvider({ engineOrigin: 'http://127.0.0.1:19113', consoleHost: 'ps5.local', packageRoot: root, launchDiagnostic: P15_SHELLUI_TRANSITION_DIAGNOSTIC, getRuntimeIdentity: async () => ({ schema: 'chimera.ps5upload-runtime-identity.v1', expected: { version: '5.4.19', revision: BASE_REVISION, engineSha256: 'e'.repeat(64), payloadSha256: 'f'.repeat(64), dpiSha256: null, diagnostic: { identity: IDENTITY, baseRevision: BASE_REVISION, sourceSha256: '1'.repeat(64), diffSha256: '2'.repeat(64) } }, engine: { version: '5.4.19', revision: BASE_REVISION, observedSha256: 'e'.repeat(64), evidence: 'test', diagnosticIdentity: IDENTITY }, payload: { version: '5.4.19', revision: BASE_REVISION, observedSha256: 'f'.repeat(64), evidence: 'test', diagnosticIdentity: IDENTITY }, dpi: { active: false }, }), fetch: async (url, options = {}) => { const value = String(url); if (value.endsWith('/api/version')) return response({ version: '5.4.19', diagnostic_identity: IDENTITY, base_revision: BASE_REVISION, observation_schema: SCHEMA }); if (value.includes('/api/ps5/status')) return response({ version: '5.4.19', ucred_elevated: true }); if (value.includes('/api/ps5/readiness')) return response({ ready: true }); if (value.includes('/api/ps5/process/list')) return response({ processes: [{ pid: 7, name: 'kstuff.elf' }, { pid: 8, name: 'etaHEN' }], truncated: false }); if (value.includes('/api/ps5/syslog/tail')) return response({ text: '' }); if (value.includes('/api/ps5/app/launch')) { const body = JSON.parse(options.body); assert.equal(body.operation_id, OPERATION_ID); return response({ ok: true, launch_observation: observation() }); } return response({}); }, }); const preflight = await provider.preflightLaunch({ titleId: TITLE_ID, components: [component] }); assert.equal(preflight.ready, true); assert.equal(preflight.runtime.diagnostic.identity, IDENTITY); const launched = await provider.launch({ titleId: TITLE_ID, operationId: OPERATION_ID }); assert.equal(launched.accepted, true); assert.equal(launched.diagnostic.applicationTransition.classification, 'accepted_lnc_without_big_app_transition'); assert.equal(launched.launchVerified, false); });