'use strict'; const assert = require('node:assert/strict'); const test = require('node:test'); const { createPs5FpkgRouteSelectionTrace, parseKstuffRouteSelectionSnapshot, } = require('../../src/services/ps5-fpkg-route-selection-trace'); const OPERATION_ID = '123e4567-e89b-42d3-a456-426614174000'; const FINGERPRINT = 'a'.repeat(64); function runtime(overrides = {}) { return { product: 'EchoStretch/kstuff-lite', version: '1.10', revision: '3b2ab4a16ba43c8ab38daf7ec83cc297014e0e47', payloadSha256: '1'.repeat(64), loaderSha256: '2'.repeat(64), readerSha256: '3'.repeat(64), observabilityProfile: 'chimera-p15-a7-route-selection-v1', identityStatus: 'exact', traceEligible: true, firmware: '9.60', fingerprint: FINGERPRINT, ...overrides, }; } function metrics(overrides = {}) { return { nmountDispatch: 0, nmountTrap: 0, nmountEmulated: 0, debugRegisterArmAttempts: 0, debugRegisterReadFailures: 0, debugRegisterPcbPointerFailures: 0, debugRegisterPcbStateFailures: 0, debugRegisterStackPushFailures: 0, debugRegisterPcbSetFailures: 0, debugRegisterWriteFailures: 0, debugRegisterArmSuccesses: 0, debugRegisterRollbackRestoreFailures: 0, debugRegisterFinishSuccesses: 0, debugRegisterFinishRestoreFailures: 0, mailboxTotal: 0, mailboxFself: 0, mailboxFpkg: 0, mailboxNpdrm: 0, mailboxUnhandled: 0, verifySuperBlockMailbox: 0, verifySuperBlockEmulated: 0, ...overrides, }; } function snapshot(sequence, values) { return { sequence, sampledAt: 1000 + sequence, runtimeFingerprint: FINGERPRINT, metrics: metrics(values), }; } function trace(postMetrics, overrides = {}) { return createPs5FpkgRouteSelectionTrace({ operationId: OPERATION_ID, titleId: 'FAKE00000', runtime: runtime(), pre: snapshot(10, metrics()), post: snapshot(11, postMetrics), launchRequests: 1, retryPerformed: false, ...overrides, }); } test('A7 parser accepts only the route-selection semantic schema', () => { const parsed = parseKstuffRouteSelectionSnapshot([ 'schema=chimera.p15-a7-route-selection-metrics.v1', 'status=attached', 'sample=8', 'route nmount_dispatch=7 nmount_trap=3 nmount_emu=2', 'dbgarm attempt=7 read_fail=1 pcb_ptr_fail=0 pcb_state_fail=0 stack_fail=0 pcb_set_fail=0 write_fail=0 armed=6 rollback_restore_fail=0 finish=6 finish_restore_fail=0', 'mailbox total=5 fself=1 fpkg=2 npdrm=1 unhandled=1', 'fpkg verify=2 verify_emu=2', ].join('\n'), { sequence: 8, sampledAt: 1008, runtimeFingerprint: FINGERPRINT }); assert.equal(parsed.metrics.nmountDispatch, 7); assert.equal(parsed.metrics.debugRegisterReadFailures, 1); assert.equal(parsed.metrics.debugRegisterArmSuccesses, 6); assert.equal(parsed.metrics.mailboxUnhandled, 1); assert.equal(parsed.metrics.verifySuperBlockMailbox, 2); }); test('A7 parser rejects raw values, unknown fields, duplicates and sample drift', () => { const base = [ 'schema=chimera.p15-a7-route-selection-metrics.v1', 'status=attached', 'sample=8', 'route nmount_dispatch=1 nmount_trap=0 nmount_emu=0', 'dbgarm attempt=1 read_fail=0 pcb_ptr_fail=0 pcb_state_fail=0 stack_fail=0 pcb_set_fail=0 write_fail=0 armed=1 rollback_restore_fail=0 finish=1 finish_restore_fail=0', 'mailbox total=0 fself=0 fpkg=0 npdrm=0 unhandled=0', 'fpkg verify=0 verify_emu=0', ]; for (const unsafe of [ [...base, 'request_ptr=0xffff800012345678'], base.map(line => line.startsWith('dbgarm ') ? `${line} dr0=1234` : line), [...base, base[3]], base.map(line => line === 'sample=8' ? 'sample=9' : line), ]) { assert.throws( () => parseKstuffRouteSelectionSnapshot(unsafe.join('\n'), { sequence: 8, sampledAt: 1008, runtimeFingerprint: FINGERPRINT }), error => error.code === 'PS5_FPKG_ROUTE_SELECTION_TRACE_UNSAFE_LINE', ); } }); test('A7 identifies an exact debug-register arming preflight failure', () => { const result = trace(metrics({ nmountDispatch: 1, debugRegisterArmAttempts: 1, debugRegisterReadFailures: 1, })); assert.equal(result.status, 'observed'); assert.equal(result.firstFailedStage, 'debug_register_read_failed'); assert.equal(result.normalizedFailure, 'PS5_FPKG_DEBUG_REGISTER_ARM_FAILED'); assert.equal(result.route.debugRegisterArmed, false); assert.equal(result.route.mailboxTargetReached, false); }); test('A7 distinguishes successful arming from a mailbox target never invoked', () => { const result = trace(metrics({ nmountDispatch: 1, debugRegisterArmAttempts: 1, debugRegisterArmSuccesses: 1, debugRegisterFinishSuccesses: 1, })); assert.equal(result.firstFailedStage, 'mailbox_target_not_invoked_during_nmount'); assert.equal(result.normalizedFailure, 'PS5_FPKG_MAILBOX_TARGET_NOT_REACHED'); assert.equal(result.route.debugRegisterArmed, true); assert.equal(result.route.mailboxTargetReached, false); assert.equal(result.recovery.debugRegistersRestored, true); }); test('A7 distinguishes an unrecognized mailbox route from no mailbox call', () => { const result = trace(metrics({ nmountDispatch: 1, nmountTrap: 1, debugRegisterArmAttempts: 1, debugRegisterArmSuccesses: 1, debugRegisterFinishSuccesses: 1, mailboxTotal: 1, mailboxUnhandled: 1, })); assert.equal(result.firstFailedStage, 'mailbox_route_unrecognized_for_fpkg'); assert.equal(result.normalizedFailure, 'PS5_FPKG_MAILBOX_ROUTE_UNRECOGNIZED'); assert.equal(result.route.mailboxTargetReached, true); assert.equal(result.route.fpkgMailboxRecognized, false); }); test('A7 records when the route reaches verifySuperBlock without claiming a fix', () => { const result = trace(metrics({ nmountDispatch: 1, nmountTrap: 1, nmountEmulated: 1, debugRegisterArmAttempts: 1, debugRegisterArmSuccesses: 1, debugRegisterFinishSuccesses: 1, mailboxTotal: 1, mailboxFpkg: 1, verifySuperBlockMailbox: 1, verifySuperBlockEmulated: 1, })); assert.equal(result.firstFailedStage, null); assert.equal(result.normalizedFailure, null); assert.equal(result.route.verifySuperBlockReached, true); assert.equal(result.route.verifySuperBlockEmulated, true); assert.equal(result.productionRestorable, false); }); test('A7 fails closed on ambiguous dispatch count, counter reset and interruption', () => { const ambiguous = trace(metrics({ nmountDispatch: 2, debugRegisterArmAttempts: 2 })); assert.equal(ambiguous.status, 'outcome_unknown'); assert.equal(ambiguous.outcomeReason, 'launch_scope_ambiguous'); const reset = createPs5FpkgRouteSelectionTrace({ operationId: OPERATION_ID, titleId: 'FAKE00000', runtime: runtime(), pre: snapshot(10, metrics({ nmountDispatch: 4 })), post: snapshot(11, metrics({ nmountDispatch: 3 })), launchRequests: 1, }); assert.equal(reset.status, 'outcome_unknown'); assert.equal(reset.metricContinuity, 'counter-reset-or-drift'); const interrupted = createPs5FpkgRouteSelectionTrace({ operationId: OPERATION_ID, titleId: 'FAKE00000', runtime: runtime(), pre: snapshot(10, metrics()), post: null, launchRequests: 1, interrupted: true, }); assert.equal(interrupted.status, 'outcome_unknown'); assert.equal(interrupted.outcomeReason, 'interrupted'); }); test('A7 fails closed when debug-register completion is missing or contradictory', () => { const unfinished = trace(metrics({ nmountDispatch: 1, debugRegisterArmAttempts: 1, debugRegisterArmSuccesses: 1, })); assert.equal(unfinished.status, 'outcome_unknown'); assert.equal(unfinished.outcomeReason, 'debug_register_finalization_missing'); const conflicting = trace(metrics({ nmountDispatch: 1, debugRegisterArmAttempts: 1, debugRegisterReadFailures: 1, debugRegisterArmSuccesses: 1, debugRegisterFinishSuccesses: 1, })); assert.equal(conflicting.status, 'outcome_unknown'); assert.equal(conflicting.outcomeReason, 'arming_outcome_ambiguous'); const rollbackRestoreFailure = trace(metrics({ nmountDispatch: 1, debugRegisterArmAttempts: 1, debugRegisterWriteFailures: 1, debugRegisterRollbackRestoreFailures: 1, })); assert.equal(rollbackRestoreFailure.status, 'outcome_unknown'); assert.equal(rollbackRestoreFailure.outcomeReason, 'debug_register_restore_failed'); }); test('A7 evidence never serializes register, pointer, mailbox-buffer or key material', () => { const result = trace(metrics({ nmountDispatch: 1, debugRegisterArmAttempts: 1, debugRegisterWriteFailures: 1, })); const serialized = JSON.stringify(result); for (const forbidden of ['dr0', 'requestPointer', 'mailboxBuffer', 'registerValues', 'keyBytes', 'dmem', 'eekpfs']) { assert.doesNotMatch(serialized, new RegExp(forbidden, 'i')); } });