import { assert, codedError, hashValue, isFailedTask, sleep, } from '../assertions/core.mjs'; import { actionAuditIdentity, assertNoPersistedImagePayload, auditInputValue, auditPatchsetPathsInclude, auditPathEquals, canonicalAuditInputSummary, collectNativeRuntimeToolPlanProtocolEvidence, countExactSecrets, duplicateCount, emptyToolPlanRepairCountsByProtocolErrorKind, finalMessageId, findSuccessfulToolExecution, isNonEmptyString, receiptAuditIdentity, sumObjectValues, validateNativeRuntimeToolPlanProtocolEvidence, validateToolActionReplays, } from '../assertions/runtime.mjs'; import { fs, path } from '../dependencies.mjs'; import { claimOwnedRunner, ensureOwnedRunnerStableKillSupport, prepareIsolatedSuiteAppData, } from '../harness/app-data.mjs'; import { isLiveTask, listFiles, parseAssignedJson, readJson, readOptionalJsonl, relativeProjectPath, resolveProjectRelative, } from '../harness/io.mjs'; import { prepareCliBinary, runCli, runProcess } from '../harness/process.mjs'; import { assertResultOrientedDisposableTask, seedDisposableProject, } from '../harness/project.mjs'; import { collectPartialRuntimeJsonlSurface, readLatestJsonFile, } from '../harness/reporting.mjs'; import { agentConversationPath, assertStructuredPlanAuditSnapshot, buildTaskSnapshot, confirmPendingActions, countLureLeaks, countMarkerOutsideRuntimeControl, findPendingActions, hasExpectedWorkspaceSandboxMetadata, inspectStructuredPlanSnapshot, killRunnerOnce, mainContextBundlePath, mainRuntimeStatePath, readAllRuntimeEvents, readRunnerStatus, readTaskSnapshot, runnerBootId, validateProjectRootPublicLeakBoundary, waitForCanonicalRuntime, waitForRunnerBootChange, } from '../harness/runtime.mjs'; import { commandDiagnosticLineCount, commandFailureMarker, commandPassedMarker, commandRootErrorLine, commandRootErrorMarker, goalDeliveryPath, goalEditedPayload, goalFailureEvidenceCanary, goalFinalMarker, goalInitialMarker, goalInitialPayload, goalProjectWriteTools, goalRuntimeSuite, goalSessionId, mainAgentId, patchedText, patchsetCreatedContent, patchsetCreatedMarker, patchsetCreatedPath, pollIntervalMs, providerRequestLifecycleSchemaVersion, requestedRunId, runtimeContextBundleSchemaVersion, runTimeoutMs, state, verificationCommand, visibleText, } from '../runtime-state.mjs'; export async function runGoalRuntimeE2e() { await ensureOwnedRunnerStableKillSupport(); await seedDisposableProject(); await assertGoalInitialMarkerAbsent('goal-project-seeded'); state.cliBinary = await prepareCliBinary(); await prepareIsolatedSuiteAppData(); assertGoalPayloadUnscripted(goalInitialPayload, 'goal-initial'); assertGoalPayloadUnscripted(goalEditedPayload, 'goal-edited'); state.initialTask = { chars: [...goalInitialPayload.outcome].length, sha256: hashValue(goalInitialPayload.outcome), }; state.isolatedRunner.launchAttempted = true; const started = parseGoalMutation( await runCli( [ '--agent-goal-start', '--init', state.projectRoot, mainAgentId, goalSessionId, requestedRunId, '--stdin', ], { timeoutMs: 120_000, stdin: `${JSON.stringify(goalInitialPayload)}\n`, }, ), ); assertGoalMutationIdentity(started, 1, 'goal-start'); state.goal.goalId = started.goal.goalId; state.goal.initialRevision = started.goal.revision; state.initialRunId = started.goal.runId; state.initialSessionId = started.goal.sessionId; await claimOwnedRunner(); const canonicalRuntime = await waitForCanonicalRuntime(); assert( canonicalRuntime.agentId === mainAgentId && canonicalRuntime.sessionId === state.initialSessionId && canonicalRuntime.runId === state.initialRunId && canonicalRuntime.goalId === state.goal.goalId && canonicalRuntime.goalRevision === state.goal.initialRevision && canonicalRuntime.goalStatus === 'active', 'goal-start-canonical-runtime-invalid', ); const initialPending = await waitForGoalRevisionPendingAction({ revision: state.goal.initialRevision, marker: goalInitialMarker, codePrefix: 'goal-initial', }); state.goal.initialCompletedStepHashes = [ ...initialPending.plan.completedStepHashes, ]; state.goal.initialPending = summarizeGoalPending(initialPending.pending); await assertGoalInitialMarkerAbsent( 'goal-initial-pending-before-edit', initialPending.pending.actionId, ); const [deliveryBeforeEdit, finalMarkerBeforeEdit] = await Promise.all([ fs.lstat(path.join(state.projectRoot, goalDeliveryPath)).catch((error) => { if (error?.code === 'ENOENT') return null; throw error; }), countMarkerOutsideRuntimeControl(goalFinalMarker), ]); assert( deliveryBeforeEdit === null && finalMarkerBeforeEdit === 0, 'goal-edited-evidence-present-before-edit', ); state.goal.editedEvidenceAbsentBeforeEdit = true; await assertGoalRevisionOneFixtureIsolation(); await injectGoalRevisionTwoFixtureAndProveFailure(); state.goal.revisionTwoEditAgentDbBoundary = ( await readOptionalJsonl(path.join(state.projectRoot, '.agent/agent.db')) ).length; const edited = parseGoalMutation( await runCli( [ '--agent-goal-edit', state.projectRoot, mainAgentId, state.initialSessionId, state.goal.goalId, String(state.goal.initialRevision), '--stdin', ], { timeoutMs: 120_000, stdin: `${JSON.stringify(goalEditedPayload)}\n`, }, ), ); assertGoalMutationIdentity(edited, 2, 'goal-edit'); state.goal.editedRevision = edited.goal.revision; state.goal.editProviderInterrupted = edited.providerInterrupted === true; await waitForGoalOldActionBlocked(initialPending.pending); await waitForGoalRevisionTwoAgentVerificationFailure(); const editedPending = await waitForGoalRevisionPendingAction({ revision: state.goal.editedRevision, codePrefix: 'goal-edited', minimumPlanRevision: initialPending.plan.revision + 1, requiredCompletedStepHashes: state.goal.initialCompletedStepHashes, pendingMatcher: goalPendingMatchesRevisionTwoRepair, }); state.goal.editedPending = summarizeGoalPending(editedPending.pending); const paused = parseGoalMutation( await runCli( [ '--agent-goal-pause', state.projectRoot, mainAgentId, state.initialSessionId, state.goal.goalId, String(state.goal.editedRevision), ], { timeoutMs: 120_000 }, ), ); assertGoalMutationIdentity(paused, state.goal.editedRevision, 'goal-pause'); assert( paused.goal.status === 'paused' && paused.runtime?.state?.status === 'paused' && paused.runtime?.state?.phase === 'paused', 'goal-pause-not-durable', ); state.goal.pauseProviderInterrupted = paused.providerInterrupted === true; const beforeKillRunner = await readRunnerStatus(); state.goal.oldRunnerBootId = runnerBootId(beforeKillRunner); assert( isNonEmptyString(state.goal.oldRunnerBootId), 'goal-runner-boot-before-kill-missing', ); state.goal.pauseSnapshot = await captureGoalPausedSnapshot( editedPending.pending, 'goal-paused-before-kill', ); await killRunnerOnce(); await runCli(['--agent-resume', state.projectRoot], { timeoutMs: 120_000 }); state.resumed = true; const restartedRunner = await waitForRunnerBootChange( state.goal.oldRunnerBootId, ); state.goal.newRunnerBootId = runnerBootId(restartedRunner); assert( isNonEmptyString(state.goal.newRunnerBootId) && state.goal.newRunnerBootId !== state.goal.oldRunnerBootId, 'goal-runner-boot-did-not-change', ); await claimOwnedRunner(restartedRunner); state.goal.executionOwnerRecovered = await waitForGoalExecutionOwnerTakeover(); await assertGoalRemainsPausedAfterRestart( state.goal.pauseSnapshot, editedPending.pending, ); const resumed = parseGoalMutation( await runCli( [ '--agent-goal-resume', state.projectRoot, mainAgentId, state.initialSessionId, state.goal.goalId, String(state.goal.editedRevision), ], { timeoutMs: 120_000 }, ), ); assertGoalMutationIdentity(resumed, state.goal.editedRevision, 'goal-resume'); assert( resumed.goal.status === 'active' && resumed.goal.runId === state.initialRunId && ['pending', 'waiting-for-confirmation', 'running'].includes( resumed.runtime?.state?.status, ), 'goal-explicit-resume-invalid', ); state.identityStable = true; await driveGoalRuntimeToQuiescence(); state.evidence = await validateGoalRuntimeEvidence(); assert(state.evidence.secretLeakCount === 0, 'loaded-key-leak-detected'); } export function goalRevisionOneVerificationFixtureSource() { return `import fs from 'node:fs';\nconst html = fs.readFileSync('game/index.html', 'utf8');\nconst agents = fs.readFileSync('AGENTS.md', 'utf8');\nconst passed = html.includes(${JSON.stringify(visibleText)}) && html.includes(' { if (error?.code === 'ENOENT') return null; throw error; }); assert( gameHtml.includes('REAL_E2E_TARGET:before') && !gameHtml.includes(patchedText) && patchsetMetadata === null, 'goal-revision-two-failure-precondition-missing', ); await assertGoalInitialMarkerAbsent('goal-revision-two-fixture-before-write'); await fs.writeFile( path.join(state.projectRoot, 'verify-e2e.mjs'), goalRevisionTwoVerificationFixtureSource(), ); await assertGoalInitialMarkerAbsent('goal-revision-two-fixture-after-write'); state.goal.revisionTwoFixtureInjected = true; const verification = await runProcess(process.execPath, ['verify-e2e.mjs'], { cwd: state.projectRoot, timeoutMs: 120_000, allowNonZero: true, }); const exactRootCause = `ROOT_CAUSE marker=${commandRootErrorMarker} repairPath=game/index.html old=REAL_E2E_TARGET:before new=${patchedText} createPath=${patchsetCreatedPath} createContentJson=${JSON.stringify(patchsetCreatedContent)}`; assert( verification.code === 1 && verification.signal === null && verification.stderr.includes(commandFailureMarker) && verification.stderr.includes(exactRootCause), 'goal-revision-two-real-failure-not-observed', ); state.goal.revisionTwoHostFailureObserved = true; } export function assertGoalPayloadUnscripted(payload, codePrefix) { assert( payload && isNonEmptyString(payload.outcome) && Array.isArray(payload.constraints) && payload.constraints.length > 0 && Array.isArray(payload.verification) && payload.verification.length > 0, `${codePrefix}-payload-invalid`, ); assertResultOrientedDisposableTask( [payload.outcome, ...payload.constraints, ...payload.verification].join( '\n', ), codePrefix, ); } export function goalPrivateBodyValues() { return [ goalInitialPayload.outcome, ...goalInitialPayload.constraints, ...goalInitialPayload.verification, goalEditedPayload.outcome, ...goalEditedPayload.constraints, ...goalEditedPayload.verification, goalInitialMarker, goalFinalMarker, goalFailureEvidenceCanary, ]; } export function goalPublicBodyValues() { return [ goalInitialPayload.outcome, ...goalInitialPayload.constraints, ...goalInitialPayload.verification, goalEditedPayload.outcome, ...goalEditedPayload.constraints, ...goalEditedPayload.verification, goalInitialMarker, goalFinalMarker, goalFailureEvidenceCanary, ]; } export function parseGoalMutation(result) { const mutation = parseAssignedJson(result.stdout, ['goalMutationJson']); assert( mutation?.goal && mutation?.runtime?.state && typeof mutation.providerInterrupted === 'boolean', 'goal-mutation-json-invalid', ); return mutation; } export function assertGoalMutationIdentity( mutation, expectedRevision, codePrefix, ) { const goal = mutation.goal; const runtime = mutation.runtime.state; const payload = expectedRevision === state.goal.initialRevision || expectedRevision === 1 ? goalInitialPayload : goalEditedPayload; const runtimeIdentityMatches = runtime.runId === goal.runId && runtime.agentId === goal.agentId && runtime.sessionId === goal.sessionId && runtime.goalId === goal.goalId && runtime.goalRevision === goal.revision && runtime.goalStatus === goal.status; const queuedStartMatches = codePrefix === 'goal-start' && mutation.runtime.recentTasks?.some( (task) => task.agentId === goal.agentId && task.sessionId === goal.sessionId && task.runId === goal.runId && task.goalId === goal.goalId && task.goalRevision === goal.revision && task.goalStatus === goal.status && task.status === 'pending', ); assert( goal.schemaVersion === 'game-creator-agent-goal.v1' && isNonEmptyString(goal.projectId) && goal.agentId === mainAgentId && goal.sessionId === goalSessionId && goal.runId === requestedRunId && goal.revision === expectedRevision && goal.outcome === payload.outcome && JSON.stringify(goal.constraints) === JSON.stringify(payload.constraints) && JSON.stringify(goal.verification) === JSON.stringify(payload.verification) && (runtimeIdentityMatches || queuedStartMatches), `${codePrefix}-identity-invalid`, ); } export function goalSnapshotFingerprint(goal) { // serde_json::Map uses lexicographically sorted keys without preserve_order. return hashValue( JSON.stringify({ agentId: goal.agentId, constraints: goal.constraints, goalId: goal.goalId, outcome: goal.outcome, projectId: goal.projectId, revision: goal.revision, runId: goal.runId, sessionId: goal.sessionId, verification: goal.verification, }), ); } export async function readGoalStatus() { const result = await runCli( [ '--agent-goal-status', state.projectRoot, mainAgentId, state.initialSessionId, ], { timeoutMs: 60_000 }, ); const goal = parseAssignedJson(result.stdout, ['goalJson']); assert(goal && typeof goal === 'object', 'goal-status-json-invalid'); return goal; } export async function assertGoalRuntimeCanProgress(codePrefix) { const [taskSnapshot, runtime] = await Promise.all([ readTaskSnapshot(), readJson(mainRuntimeStatePath()).catch(() => null), ]); const current = taskSnapshot.latest.find( (task) => task.agentId === mainAgentId && task.runId === state.initialRunId, ); if (current && isFailedTask(current)) { throw codedError(`${codePrefix}-runtime-failed`); } if ( runtime?.status === 'needs-reconciliation' || runtime?.phase === 'needs-reconciliation' ) { throw codedError(`${codePrefix}-runtime-needs-reconciliation`); } } export function assertGoalContextSnapshot( runtime, contextBundle, goal, codePrefix, ) { const expectedGoalSnapshotFingerprint = goalSnapshotFingerprint(goal); assert( contextBundle.schemaVersion === runtimeContextBundleSchemaVersion && contextBundle.projectId === goal.projectId && contextBundle.agentId === runtime.agentId && contextBundle.taskId === runtime.taskId && contextBundle.sessionId === runtime.sessionId && contextBundle.runId === runtime.runId && contextBundle.goalId === goal.goalId && contextBundle.goalRevision === goal.revision && contextBundle.goalStatus === 'active' && contextBundle.goalSnapshotFingerprint === expectedGoalSnapshotFingerprint && runtime.goalId === goal.goalId && runtime.goalRevision === goal.revision && runtime.goalStatus === goal.status && runtime.goalOutcome === goal.outcome && JSON.stringify(runtime.goalConstraints) === JSON.stringify(goal.constraints) && JSON.stringify(runtime.goalVerification) === JSON.stringify(goal.verification) && contextBundle.planRevision === runtime.planRevision && contextBundle.planExplanation === runtime.planExplanation && JSON.stringify(contextBundle.planSteps) === JSON.stringify(runtime.planSteps) && contextBundle.activePlanStepIndex === runtime.activePlanStepIndex, `${codePrefix}-context-snapshot-mismatch`, ); } export async function readVerifiedGoalPlanSnapshot(codePrefix) { const [runtime, contextBundle, records, goal] = await Promise.all([ readJson(mainRuntimeStatePath()), readJson(mainContextBundlePath()), readOptionalJsonl(path.join(state.projectRoot, '.agent/agent.db')), readGoalStatus(), ]); assert(goal.status === 'active', `${codePrefix}-goal-not-active`); const snapshot = inspectStructuredPlanSnapshot(runtime, codePrefix); assertGoalContextSnapshot(runtime, contextBundle, goal, codePrefix); assertStructuredPlanAuditSnapshot(runtime, records, `${codePrefix}-audit`); return { ...snapshot, runtime, contextBundle, goal, records }; } export function goalPendingDeliveryMutation(pending) { const action = pending?.record?.action ?? pending?.action; const input = action?.input; if (!action || !input || typeof input !== 'object') return null; if (action.tool === 'file.write') { return { tool: action.tool, path: input.path, content: input.content, }; } if (action.tool !== 'project.patchset' || !Array.isArray(input.changes)) { return null; } const matchingChanges = input.changes.filter( (change) => change?.operation === 'create' && change?.path === goalDeliveryPath, ); if (matchingChanges.length !== 1) return null; return { tool: action.tool, path: matchingChanges[0].path, content: matchingChanges[0].content, }; } export function goalPendingMatchesDelivery(pending, marker) { const mutation = goalPendingDeliveryMutation(pending); return ( mutation?.path === goalDeliveryPath && mutation.content === `${marker}\n` ); } export function goalPendingIsStandaloneDeliveryMutation(pending, marker) { if (!goalPendingMatchesDelivery(pending, marker)) return false; const action = pending?.record?.action ?? pending?.action; return ( action?.tool === 'file.write' || (action?.tool === 'project.patchset' && action.input?.changes?.length === 1) ); } export function goalPendingMatchesRevisionTwoRepair(pending) { const action = pending?.record?.action ?? pending?.action; const input = action?.input; if (!action || !input || typeof input !== 'object') return false; if (action.tool === 'file.patch') { return input.path === 'game/index.html'; } if (action.tool === 'file.write') { return ( input.path === 'game/index.html' || input.path === patchsetCreatedPath ); } return ( action.tool === 'project.patchset' && Array.isArray(input.changes) && input.changes.some( (change) => change?.path === 'game/index.html' || change?.path === patchsetCreatedPath, ) ); } export function validateGoalPendingAction(pending, plan, revision, codePrefix) { const record = pending.record; const expectedGoalSnapshotFingerprint = goalSnapshotFingerprint(plan.goal); assert( record?.schemaVersion === 'game-creator-pending-action.v5' && record.agentId === mainAgentId && record.taskId === plan.runtime.taskId && record.sessionId === state.initialSessionId && record.runId === state.initialRunId && record.goalId === state.goal.goalId && record.goalRevision === revision && record.goalSnapshotFingerprint === expectedGoalSnapshotFingerprint && plan.contextBundle.goalSnapshotFingerprint === expectedGoalSnapshotFingerprint && isNonEmptyString(record.actionId) && isNonEmptyString(record.actionFingerprint) && record.actionId === pending.actionId && record.action?.tool === pending.tool && ['pending', 'pending-confirmation'].includes(record.status) && Number.isSafeInteger(record.plannedSteerCursor), `${codePrefix}-pending-action-invalid`, ); if (revision === state.goal.initialRevision) { assert( state.goal.initialGoalSnapshotFingerprint == null || state.goal.initialGoalSnapshotFingerprint === expectedGoalSnapshotFingerprint, `${codePrefix}-initial-goal-fingerprint-changed`, ); state.goal.initialGoalSnapshotFingerprint = expectedGoalSnapshotFingerprint; } else if (revision === state.goal.editedRevision) { assert( isNonEmptyString(state.goal.initialGoalSnapshotFingerprint) && expectedGoalSnapshotFingerprint !== state.goal.initialGoalSnapshotFingerprint, `${codePrefix}-goal-fingerprint-did-not-change`, ); state.goal.editedGoalSnapshotFingerprint = expectedGoalSnapshotFingerprint; } } export async function waitForGoalRevisionPendingAction({ revision, codePrefix, minimumPlanRevision = 1, requiredCompletedStepHashes = [], marker = null, pendingMatcher = null, }) { const matchesPending = pendingMatcher ?? ((pending) => goalPendingMatchesDelivery(pending, marker)); assert( typeof matchesPending === 'function' && (pendingMatcher || isNonEmptyString(marker)), `${codePrefix}-pending-matcher-invalid`, ); const deadline = Date.now() + runTimeoutMs; let lastError = null; while (Date.now() < deadline) { await assertGoalInitialMarkerAbsent(`${codePrefix}-poll`); const taskSnapshot = await readTaskSnapshot(); const initial = taskSnapshot.latest.find( (task) => task.agentId === mainAgentId && task.runId === state.initialRunId, ); if (initial && isFailedTask(initial)) { throw codedError(`${codePrefix}-runtime-failed-before-pending`); } if (initial && !isLiveTask(initial)) { throw codedError(`${codePrefix}-runtime-terminal-before-pending`); } let plan = null; let targetPending = null; try { const pendingActions = (await findPendingActions()).filter( (pending) => pending.agentId === mainAgentId && pending.runId === state.initialRunId, ); targetPending = pendingActions.find(matchesPending); plan = await readVerifiedGoalPlanSnapshot(`${codePrefix}-plan`); } catch (error) { if (targetPending) { throw codedError(`${codePrefix}-pending-contract-invalid`, error); } lastError = error; } if (targetPending) { validateGoalPendingAction(targetPending, plan, revision, codePrefix); assert( plan.revision >= minimumPlanRevision && plan.completedStepHashes.length > 0 && plan.incompleteStepCount > 0 && requiredCompletedStepHashes.every((stepHash) => plan.completedStepHashes.includes(stepHash), ), `${codePrefix}-partial-plan-missing`, ); const messages = await readOptionalJsonl( agentConversationPath(mainAgentId, state.initialSessionId), ); assert( messages.filter((message) => message.role === 'assistant').length === 0, `${codePrefix}-assistant-before-control-point`, ); return { pending: targetPending, plan }; } await confirmPendingActions(null, (pending) => !matchesPending(pending)); await sleep(pollIntervalMs); } throw codedError(`${codePrefix}-pending-action-timeout`, lastError); } export function summarizeGoalPending(pending) { return { actionId: pending.actionId, actionFingerprint: pending.record.actionFingerprint, goalRevision: pending.record.goalRevision, schemaVersion: pending.record.schemaVersion, tool: pending.tool, }; } export function goalOldActionExecutionEvidence(records, actionId) { const executing = records.filter( (record) => record.actionId === actionId && [ 'agent.runtime.tool_action.executing', 'agent.runtime.tool_confirmation.approved', ].includes(record.recordType), ); const successfulReceipts = records.filter( (record) => record.recordType === 'agent.runtime.action_receipt' && record.actionId === actionId && ['ok', 'command-failed'].includes(record.status), ); return { executionCount: executing.length, successfulReceiptCount: successfulReceipts.length, }; } export async function waitForGoalOldActionBlocked(initialPending) { const deadline = Date.now() + 120_000; let lastError = null; while (Date.now() < deadline) { await assertGoalRuntimeCanProgress('goal-old-action-block'); await assertGoalInitialMarkerAbsent( 'goal-old-action-block-poll', initialPending.actionId, ); try { const [runtime, contextBundle, records, goal, oldMarkerCount] = await Promise.all([ readJson(mainRuntimeStatePath()), readJson(mainContextBundlePath()), readOptionalJsonl(path.join(state.projectRoot, '.agent/agent.db')), readGoalStatus(), countMarkerOutsideRuntimeControl(goalInitialMarker), ]); const execution = goalOldActionExecutionEvidence( records, initialPending.actionId, ); assert( execution.executionCount === 0 && execution.successfulReceiptCount === 0 && oldMarkerCount === 0, 'goal-old-action-executed', ); const blocked = (contextBundle.observations ?? []).filter( (observation) => observation?.tool === 'runtime.goal' && observation?.status === 'blocked', ); const blockedReceipts = records.filter( (record) => record.recordType === 'agent.runtime.action_receipt' && record.agentId === mainAgentId && record.runId === state.initialRunId && record.actionId === initialPending.actionId && record.tool === 'runtime.goal' && record.status === 'blocked', ); if ( goal.revision === state.goal.editedRevision && goal.status === 'active' && runtime.runId === state.initialRunId && runtime.sessionId === state.initialSessionId && runtime.goalId === state.goal.goalId && runtime.goalRevision === state.goal.editedRevision && contextBundle.schemaVersion === runtimeContextBundleSchemaVersion && contextBundle.goalId === state.goal.goalId && contextBundle.goalRevision === state.goal.editedRevision && contextBundle.goalSnapshotFingerprint === goalSnapshotFingerprint(goal) && contextBundle.goalSnapshotFingerprint !== state.goal.initialGoalSnapshotFingerprint && blocked.length >= 1 && blockedReceipts.length === 1 ) { state.goal.initialPending.blockedObservationCount = blocked.length; state.goal.initialPending.blockedReceiptCount = blockedReceipts.length; return; } lastError = codedError('goal-old-action-block-not-yet-observed'); } catch (error) { lastError = error; } await sleep(pollIntervalMs); } throw codedError('goal-old-action-block-timeout', lastError); } export async function waitForGoalRevisionTwoAgentVerificationFailure() { assert( state.goal.revisionTwoFixtureInjected === true && state.goal.revisionTwoHostFailureObserved === true && Number.isSafeInteger(state.goal.revisionTwoEditAgentDbBoundary), 'goal-revision-two-agent-failure-precondition-invalid', ); const deadline = Date.now() + runTimeoutMs; let lastError = null; while (Date.now() < deadline) { await assertGoalRuntimeCanProgress('goal-revision-two-agent-failure'); await assertGoalInitialMarkerAbsent('goal-revision-two-verify-poll'); const records = await readOptionalJsonl( path.join(state.projectRoot, '.agent/agent.db'), ); const failure = findGoalRevisionTwoAgentVerificationFailure(records); if (failure) { const contextBundle = await readJson(mainContextBundlePath()); const failedObservations = (contextBundle.observations ?? []).filter( (observation) => observation?.tool === 'project.verify' && observation?.status === 'failed', ); if (failedObservations.length === 0) { lastError = codedError( 'goal-revision-two-failed-observation-not-checkpointed', ); await sleep(pollIntervalMs); continue; } const logPath = resolveProjectRelative(failure.audit.logPath); assert( relativeProjectPath(logPath).startsWith('.agent/') && failure.audit.exitCode === 1 && failure.audit.timedOut === false, 'goal-revision-two-failed-verification-audit-invalid', ); const log = await fs.readFile(logPath); assert( countExactSecrets(log, [commandRootErrorMarker]) === 1 && log.includes(Buffer.from(commandFailureMarker)), 'goal-revision-two-failed-verification-log-invalid', ); state.goal.revisionTwoFailureObserved = true; state.goal.revisionTwoFailureExitCode = failure.audit.exitCode; state.goal.revisionTwoFailureFingerprint = hashValue( Buffer.concat([ Buffer.from( `${failure.audit.actionId}\0${failure.audit.actionFingerprint}\0`, ), log, ]), ); state.goal.revisionTwoAgentDbBoundary = failure.observationIndex + 1; return; } const pendingActions = (await findPendingActions()).filter( (pending) => pending.agentId === mainAgentId && pending.runId === state.initialRunId, ); const unsupportedEarlyWrite = pendingActions.find( (pending) => goalProjectWriteTools.has(pending.tool) && !goalPendingIsStandaloneDeliveryMutation(pending, goalFinalMarker), ); assert( !unsupportedEarlyWrite, 'goal-revision-two-repair-before-failed-verification', ); await confirmPendingActions( new Set([ 'project.checkpoint', 'project.verify', 'file.write', 'project.patchset', ]), (pending) => pending.tool === 'project.checkpoint' || pending.tool === 'project.verify' || goalPendingIsStandaloneDeliveryMutation(pending, goalFinalMarker), ); lastError = codedError('goal-revision-two-agent-failure-not-yet-observed'); await sleep(pollIntervalMs); } throw codedError('goal-revision-two-agent-failure-timeout', lastError); } export function findGoalRevisionTwoAgentVerificationFailure(records) { for ( let auditIndex = state.goal.revisionTwoEditAgentDbBoundary; auditIndex < records.length; auditIndex += 1 ) { const audit = records[auditIndex]; if ( audit.recordType !== 'agent.runtime.project.verify' || audit.agentId !== mainAgentId || audit.runId !== state.initialRunId || audit.status !== 'failed' || audit.exitCode !== 1 || audit.timedOut !== false || !['test', 'check:e2e'].includes(audit.script) || audit.expectedCommand !== verificationCommand || !isNonEmptyString(audit.actionId) || !isNonEmptyString(audit.actionFingerprint) || !isNonEmptyString(audit.logPath) || !hasExpectedWorkspaceSandboxMetadata(audit) ) { continue; } let startIndex = -1; for (let index = auditIndex - 1; index >= 0; index -= 1) { const record = records[index]; if ( record.agentId === mainAgentId && record.runId === state.initialRunId && record.tool === 'project.verify' && record.actionId === audit.actionId && record.actionFingerprint === audit.actionFingerprint && [ 'agent.runtime.tool_action.executing', 'agent.runtime.tool_confirmation.approved', ].includes(record.recordType) ) { startIndex = index; break; } } const observationIndex = records.findIndex( (record, index) => index > auditIndex && record.recordType === 'agent.runtime.tool_observation' && record.agentId === mainAgentId && record.runId === state.initialRunId && record.tool === 'project.verify' && record.actionId === audit.actionId && record.status === 'failed', ); if ( startIndex < state.goal.revisionTwoEditAgentDbBoundary || observationIndex < 0 ) { continue; } const unsupportedEarlyWrite = records .slice(state.goal.revisionTwoEditAgentDbBoundary, observationIndex + 1) .some( (record) => goalProjectWriteTools.has(record.tool) && !state.goal.preFailureDeliveryActionIds.has(record.actionId) && [ 'agent.runtime.tool_action.executing', 'agent.runtime.tool_confirmation.approved', ].includes(record.recordType), ); assert( !unsupportedEarlyWrite, 'goal-revision-two-repair-executed-before-failure', ); return { audit, auditIndex, observationIndex, startIndex }; } return null; } export async function readGoalRuntimePersistence() { const taskSnapshot = await readTaskSnapshot(); const events = await readAllRuntimeEvents(); const [ agentDb, conversations, activity, output, runtimeState, contextBundle, goal, ] = await Promise.all([ readOptionalJsonl(path.join(state.projectRoot, '.agent/agent.db')), readOptionalJsonl( agentConversationPath(mainAgentId, state.initialSessionId), ), readOptionalJsonl(path.join(state.projectRoot, '.agent/activity.jsonl')), readOptionalJsonl(path.join(state.projectRoot, '.agent/output.jsonl')), readJson(mainRuntimeStatePath()), readJson(mainContextBundlePath()), readGoalStatus(), ]); const pendingActions = (await findPendingActions()).filter( (pending) => pending.agentId === mainAgentId && pending.runId === state.initialRunId, ); return { taskSnapshot, events, agentDb, conversations, activity, output, runtimeState, contextBundle, goal, pendingActions, }; } export async function captureGoalPausedSnapshot(expectedPending, codePrefix) { await assertGoalInitialMarkerAbsent(`${codePrefix}-marker-check`); const persistence = await readGoalRuntimePersistence(); const { taskSnapshot, events, agentDb, conversations, runtimeState, contextBundle, goal, pendingActions, } = persistence; const latest = taskSnapshot.latest.find( (task) => task.agentId === mainAgentId && task.runId === state.initialRunId, ); const targetRuns = [ ...new Set( taskSnapshot.all .filter((task) => task.agentId === mainAgentId) .map((task) => task.runId), ), ].sort(); const pending = pendingActions.find( (candidate) => candidate.actionId === expectedPending.actionId, ); assert( goal.goalId === state.goal.goalId && goal.revision === state.goal.editedRevision && goal.status === 'paused' && runtimeState.agentId === mainAgentId && runtimeState.sessionId === state.initialSessionId && runtimeState.runId === state.initialRunId && runtimeState.goalId === state.goal.goalId && runtimeState.goalRevision === state.goal.editedRevision && runtimeState.goalStatus === 'paused' && runtimeState.status === 'paused' && runtimeState.phase === 'paused' && latest?.status === 'paused' && latest?.phase === 'paused' && JSON.stringify(targetRuns) === JSON.stringify([state.initialRunId]) && contextBundle.schemaVersion === runtimeContextBundleSchemaVersion && contextBundle.goalId === state.goal.goalId && contextBundle.goalRevision === state.goal.editedRevision && contextBundle.goalStatus === 'active' && pending?.record?.schemaVersion === 'game-creator-pending-action.v5' && pending.record.goalId === state.goal.goalId && pending.record.goalRevision === state.goal.editedRevision && pending.record.goalSnapshotFingerprint === contextBundle.goalSnapshotFingerprint && contextBundle.goalSnapshotFingerprint === goalSnapshotFingerprint(goal) && contextBundle.goalSnapshotFingerprint === state.goal.editedGoalSnapshotFingerprint && contextBundle.goalSnapshotFingerprint !== state.goal.initialGoalSnapshotFingerprint && conversations.filter((message) => message.role === 'assistant').length === 0, `${codePrefix}-state-invalid`, ); const planProtocolCount = agentDb.filter( (record) => record.recordType === 'agent.runtime.tool_plan.protocol' && record.agentId === mainAgentId && record.runId === state.initialRunId, ).length; const actionProgressCount = agentDb.filter( (record) => record.agentId === mainAgentId && record.runId === state.initialRunId && [ 'agent.runtime.tool_action.executing', 'agent.runtime.tool_action.observed', 'agent.runtime.tool_confirmation.approved', 'agent.runtime.action_receipt', ].includes(record.recordType), ).length; const providerRequestStartedCount = agentDb.filter( (record) => record.recordType === 'agent.runtime.provider_request.lifecycle' && record.agentId === mainAgentId && record.runId === state.initialRunId && record.status === 'started', ).length; const signaturePayload = { goalSha256: hashValue(JSON.stringify(goal)), runtimeSha256: hashValue(JSON.stringify(runtimeState)), contextSha256: hashValue(JSON.stringify(contextBundle)), pendingSha256: hashValue(JSON.stringify(pending.record)), taskCount: taskSnapshot.all.filter( (task) => task.agentId === mainAgentId && task.runId === state.initialRunId, ).length, eventCount: events.filter( (event) => event.agentId === mainAgentId && event.runId === state.initialRunId, ).length, agentDbCount: agentDb.filter( (record) => record.agentId === mainAgentId && record.runId === state.initialRunId, ).length, conversationCount: conversations.length, assistantCount: conversations.filter( (message) => message.role === 'assistant', ).length, planProtocolCount, providerRequestStartedCount, actionProgressCount, planRevision: runtimeState.planRevision, pendingActionIdHash: hashValue(pending.actionId), }; return { ...signaturePayload, signature: hashValue(JSON.stringify(signaturePayload)), }; } export async function assertGoalRemainsPausedAfterRestart( baseline, expectedPending, ) { let latest = null; for (let poll = 0; poll < 4; poll += 1) { latest = await captureGoalPausedSnapshot( expectedPending, 'goal-paused-after-restart', ); assert( latest.signature === baseline.signature, 'goal-paused-evidence-progressed-after-restart', ); await sleep(1_000); } state.goal.pausedAfterRestart = latest; } export async function waitForGoalExecutionOwnerTakeover() { const ownerPath = path.join( state.projectRoot, '.agent/runtime/execution-owner.json', ); const deadline = Date.now() + 30_000; while (Date.now() < deadline) { const owner = await readJson(ownerPath).catch(() => null); if ( Number.isSafeInteger(owner?.protocolVersion) && owner.protocolVersion > 0 && Number.isSafeInteger(owner.pid) && owner.pid > 1 && owner.bootId === state.goal.newRunnerBootId && owner.recoveredFromBootId === state.goal.oldRunnerBootId ) { return true; } await sleep(100); } throw codedError('goal-execution-owner-takeover-timeout'); } export async function driveGoalRuntimeToQuiescence() { const deadline = Date.now() + runTimeoutMs; let quietPolls = 0; while (Date.now() < deadline) { await assertGoalInitialMarkerAbsent('goal-quiescence-poll'); await confirmPendingActions(); const [snapshot, goal] = await Promise.all([ readTaskSnapshot(), readGoalStatus(), ]); const initial = snapshot.latest.find( (task) => task.agentId === mainAgentId && task.runId === state.initialRunId, ); if (initial && isFailedTask(initial)) { throw codedError('goal-runtime-failed'); } if (goal.status === 'needs-reconciliation') { throw codedError('goal-runtime-needs-reconciliation'); } const pending = await findPendingActions(); const completed = initial?.status === 'completed' && initial?.phase === 'completed' && goal.status === 'completed'; const hasLive = snapshot.latest.some(isLiveTask); if (completed && !hasLive && pending.length === 0) { quietPolls += 1; if (quietPolls >= 3) return; } else { quietPolls = 0; } await sleep(pollIntervalMs); } throw codedError('goal-runtime-e2e-timeout'); } export async function monitorGoalWriteActionUntilSettled(pending) { state.goal.monitoredWriteActionIds.add(pending.actionId); const deadline = Date.now() + 120_000; while (Date.now() < deadline) { await assertGoalRuntimeCanProgress('goal-write-action-settle'); await assertGoalInitialMarkerAbsent( 'goal-write-after-confirm', pending.actionId, ); const records = await readOptionalJsonl( path.join(state.projectRoot, '.agent/agent.db'), ); const settled = records.some( (record) => record.agentId === pending.agentId && record.runId === pending.runId && record.actionId === pending.actionId && record.tool === pending.tool && [ 'agent.runtime.action_receipt', 'agent.runtime.tool_action.observed', 'agent.runtime.tool_observation', ].includes(record.recordType), ); if (settled) { await assertGoalInitialMarkerAbsent( 'goal-write-settled', pending.actionId, ); return; } await sleep(100); } throw codedError('goal-write-action-settle-timeout'); } export async function validateGoalRuntimeEvidence() { const persistence = await readGoalRuntimePersistence(); const { taskSnapshot, events, agentDb, conversations, activity, output, runtimeState, contextBundle, goal, pendingActions, } = persistence; assert(taskSnapshot.all.length > 0, 'goal-task-evidence-missing'); assert(events.length > 0, 'goal-event-evidence-missing'); assert(agentDb.length > 0, 'goal-agent-db-evidence-missing'); assertNoPersistedImagePayload('goal-task', taskSnapshot.all); assertNoPersistedImagePayload('goal-event', events); assertNoPersistedImagePayload('goal-agent-db', agentDb); const plan = inspectStructuredPlanSnapshot(runtimeState, 'goal-final-plan'); const verificationExecution = findFinalSuccessfulGoalVerificationExecution(agentDb); const projectRevision = await readJson( path.join(state.projectRoot, '.agent/runtime/project-revision.json'), ); const verificationGate = await readGoalVerificationGate( projectRevision, verificationExecution, goal, ); const expectedCompletionEvidence = [ `goalRevision=${state.goal.editedRevision}`, `planRevision=${runtimeState.planRevision} completedSteps=${plan.completedStepHashes.length}`, `verificationRequired=true verifiedRevision=${projectRevision.revision}`, `runId=${state.initialRunId} sessionId=${state.initialSessionId}`, ]; assert( plan.incompleteStepCount === 0 && plan.completedStepHashes.length === runtimeState.planSteps.length && state.goal.initialCompletedStepHashes.every((stepHash) => plan.completedStepHashes.includes(stepHash), ), 'goal-final-plan-incomplete', ); assertGoalContextSnapshot(runtimeState, contextBundle, goal, 'goal-final'); assertStructuredPlanAuditSnapshot( runtimeState, agentDb, 'goal-final-plan-audit', ); assert( goal.schemaVersion === 'game-creator-agent-goal.v1' && goal.goalId === state.goal.goalId && goal.agentId === mainAgentId && goal.sessionId === state.initialSessionId && goal.runId === state.initialRunId && goal.revision === state.goal.editedRevision && goalSnapshotFingerprint(goal) === state.goal.editedGoalSnapshotFingerprint && state.goal.editedGoalSnapshotFingerprint !== state.goal.initialGoalSnapshotFingerprint && goal.status === 'completed' && Number.isSafeInteger(goal.completedAt) && goal.completedAt > 0 && projectRevision.updatedAt <= goal.completedAt && verificationGate.updatedAt <= goal.completedAt && /^[0-9a-f]{64}$/u.test(goal.responseFingerprint) && Array.isArray(goal.completionEvidence) && JSON.stringify(goal.completionEvidence) === JSON.stringify(expectedCompletionEvidence) && runtimeState.status === 'idle' && runtimeState.phase === 'completed' && runtimeState.goalId === goal.goalId && runtimeState.goalRevision === goal.revision && runtimeState.goalStatus === 'completed', 'goal-final-state-invalid', ); const targetTasks = taskSnapshot.all.filter( (task) => task.agentId === mainAgentId && task.runId === state.initialRunId, ); const targetRunIds = [ ...new Set( taskSnapshot.all .filter((task) => task.agentId === mainAgentId) .map((task) => task.runId), ), ].sort(); const completedTasks = targetTasks.filter( (task) => task.status === 'completed' && task.phase === 'completed', ); const latest = taskSnapshot.latest.find( (task) => task.agentId === mainAgentId && task.runId === state.initialRunId, ); assert( JSON.stringify(targetRunIds) === JSON.stringify([state.initialRunId]) && completedTasks.length === 2 && completedTasks.filter((task) => task.goalStatus === 'active').length === 1 && completedTasks.filter((task) => task.goalStatus === 'completed') .length === 1 && latest?.goalStatus === 'completed', 'goal-finalization-projection-invalid', ); const finalMessage = finalMessageId( mainAgentId, state.initialSessionId, state.initialRunId, ); const userMessages = conversations.filter( (message) => message.role === 'user', ); const assistantMessages = conversations.filter( (message) => message.role === 'assistant', ); const finalAssistant = assistantMessages.find( (message) => message.messageId === finalMessage, ); const assistantAudits = agentDb.filter( (record) => record.recordType === 'conversation.message' && record.role === 'assistant' && record.agentId === mainAgentId && record.sessionId === state.initialSessionId && record.messageId === finalMessage, ); const responseEvents = events.filter( (event) => event.agentId === mainAgentId && event.runId === state.initialRunId && event.eventType === 'response' && event.phase === 'completed', ); const completedEvents = events.filter( (event) => event.agentId === mainAgentId && event.runId === state.initialRunId && event.eventType === 'turn.completed' && event.phase === 'completed', ); assert( userMessages.length === 2 && assistantMessages.length === 1 && finalAssistant?.agentId === mainAgentId && hashValue(finalAssistant.content.trim()) === goal.responseFingerprint && assistantAudits.length === 1 && responseEvents.length === 1 && completedEvents.length === 1, 'goal-final-assistant-invalid', ); const providerLifecycle = validateGoalProviderRequestLifecycle(agentDb); const finalizationLifecycle = validateGoalFinalizationLifecycle( agentDb, goal, runtimeState, ); assert( verificationExecution.completionIndex < finalizationLifecycle.firstStageIndex, 'goal-completed-before-verification-gate-passed', ); const oldExecution = goalOldActionExecutionEvidence( agentDb, state.goal.initialPending.actionId, ); const editedActionRecords = agentDb.filter( (record) => record.actionId === state.goal.editedPending.actionId, ); const editedExecutionCount = editedActionRecords.filter((record) => [ 'agent.runtime.tool_action.executing', 'agent.runtime.tool_confirmation.approved', ].includes(record.recordType), ).length; const editedReceipts = editedActionRecords.filter( (record) => record.recordType === 'agent.runtime.action_receipt' && record.status === 'ok', ); assert( state.goal.initialPending.blockedObservationCount >= 1 && state.goal.initialPending.blockedReceiptCount === 1 && oldExecution.executionCount === 0 && oldExecution.successfulReceiptCount === 0 && editedExecutionCount >= 1 && editedReceipts.length === 1 && pendingActions.length === 0, 'goal-action-transition-invalid', ); const executedGoalWriteActionIds = new Set( agentDb .filter( (record) => record.agentId === mainAgentId && record.runId === state.initialRunId && goalProjectWriteTools.has(record.tool) && [ 'agent.runtime.tool_action.executing', 'agent.runtime.tool_confirmation.approved', ].includes(record.recordType), ) .map((record) => record.actionId) .filter(isNonEmptyString), ); assert( [...executedGoalWriteActionIds].every((actionId) => state.goal.monitoredWriteActionIds.has(actionId), ) && state.goal.initialMarkerAbsenceCheckCount > 0, 'goal-write-action-marker-monitoring-incomplete', ); assert( state.goal.runner.pidfdClaimCount >= 2 && state.goal.runner.pidfdSignalCount >= 1, 'goal-runner-pidfd-evidence-invalid', ); const repairEvidence = validateGoalRevisionTwoRepairEvidence( agentDb, verificationExecution, ); const verification = await runProcess(process.execPath, ['verify-e2e.mjs'], { cwd: state.projectRoot, timeoutMs: 120_000, }); assert( verification.stdout.includes(commandPassedMarker), 'goal-final-project-verification-failed', ); const goalDelivery = await fs.readFile( path.join(state.projectRoot, goalDeliveryPath), 'utf8', ); const oldMarkerProjectCount = await countMarkerOutsideRuntimeControl(goalInitialMarker); assert( goalDelivery === `${goalFinalMarker}\n` && oldMarkerProjectCount === 0, 'goal-final-delivery-invalid', ); const finalizationFiles = await listFiles( path.join(state.projectRoot, '.agent/runtime/finalizations'), ); const finalizationJournalCount = finalizationFiles.filter((file) => file.endsWith('.json'), ).length; assert(finalizationJournalCount === 0, 'goal-finalization-journal-present'); const replayEvidence = validateToolActionReplays(agentDb); const duplicateActionCount = duplicateCount( agentDb.filter((record) => record.actionId).map(actionAuditIdentity), ); const duplicateMessageCount = duplicateCount( conversations.map((message) => message.messageId).filter(Boolean), ); const receipts = agentDb.filter( (record) => record.recordType === 'agent.runtime.action_receipt', ); const duplicateReceiptCount = duplicateCount( receipts.map(receiptAuditIdentity), ); assert( duplicateActionCount === 0 && duplicateMessageCount === 0 && duplicateReceiptCount === 0 && replayEvidence.sideEffectReplayCount === 0, 'goal-duplicate-or-replay-detected', ); const publicSurfaces = { event: events, agentDb, receipt: receipts, activity, output, }; const goalPublicBodyLeakCounts = {}; for (const [surface, records] of Object.entries(publicSurfaces)) { goalPublicBodyLeakCounts[surface] = countExactSecrets( Buffer.from(JSON.stringify(records)), goalPublicBodyValues(), ); assert( goalPublicBodyLeakCounts[surface] === 0, `goal-body-public-${surface}-leak-detected`, ); } const goalPublicBodyLeakCount = sumObjectValues(goalPublicBodyLeakCounts); const projectPathPublicLeakCounts = validateProjectRootPublicLeakBoundary( publicSurfaces, 'goal-public', ); const projectPathPublicLeakCount = sumObjectValues( projectPathPublicLeakCounts, ); state.lureLeakCount = await countLureLeaks(); assert(state.lureLeakCount === 0, 'sensitive-lure-leak-detected'); const nativeToolPlanProtocolEvidence = validateNativeRuntimeToolPlanProtocolEvidence(agentDb); const successfulToolExecutionCount = receipts.filter( (record) => record.agentId === mainAgentId && record.runId === state.initialRunId && record.status === 'ok', ).length; return { scenario: 'goal-edit-pause-runner-restart-resume', taskCount: taskSnapshot.all.length, eventCount: events.length, agentDbRecordCount: agentDb.length, conversationMessageCount: conversations.length, successfulToolExecutionCount, ...nativeToolPlanProtocolEvidence, structuredPlanRevision: plan.revision, structuredPlanCompletedStepCount: plan.completedStepHashes.length, goalInitialCompletedStepCount: state.goal.initialCompletedStepHashes.length, goalRetainedInitialCompletedStepCount: state.goal.initialCompletedStepHashes.filter((stepHash) => plan.completedStepHashes.includes(stepHash), ).length, goalEditedEvidenceAbsentBeforeEdit: state.goal.editedEvidenceAbsentBeforeEdit, goalRevisionOneFixtureIsolated: state.goal.revisionOneFixtureIsolated, goalRevisionTwoFixtureInjected: state.goal.revisionTwoFixtureInjected, goalRevisionTwoHostFailureObserved: state.goal.revisionTwoHostFailureObserved, goalRevisionTwoFailureObserved: state.goal.revisionTwoFailureObserved, goalRevisionTwoFailureExitCode: state.goal.revisionTwoFailureExitCode, goalRevisionTwoFailureFingerprint: state.goal.revisionTwoFailureFingerprint, goalRevisionTwoRepairActionCount: repairEvidence.actionCount, goalRevisionTwoRepairFileWriteCount: repairEvidence.fileWriteCount, goalRevisionTwoRepairPatchsetCount: repairEvidence.patchsetCount, goalIdHash: hashValue(goal.goalId), goalInitialRevision: state.goal.initialRevision, goalEditedRevision: state.goal.editedRevision, goalSnapshotFingerprintChanged: state.goal.initialGoalSnapshotFingerprint !== state.goal.editedGoalSnapshotFingerprint, goalInitialMarkerAbsenceCheckCount: state.goal.initialMarkerAbsenceCheckCount, goalMonitoredWriteActionCount: state.goal.monitoredWriteActionIds.size, goalPreFailureDeliveryActionCount: state.goal.preFailureDeliveryActionIds.size, goalFinalStatus: goal.status, goalEditProviderInterrupted: state.goal.editProviderInterrupted, goalPauseProviderInterrupted: state.goal.pauseProviderInterrupted, goalPausedBeforeKill: true, goalPausedAfterRestart: true, goalRunnerBootChanged: state.goal.oldRunnerBootId !== state.goal.newRunnerBootId, goalRunnerKillMethod: 'linux-pidfd', goalRunnerPidfdClaimCount: state.goal.runner.pidfdClaimCount, goalRunnerPidfdSignalCount: state.goal.runner.pidfdSignalCount, goalExecutionOwnerRecovered: state.goal.executionOwnerRecovered === true, goalExplicitResumeSameRun: true, goalTargetRunCount: targetRunIds.length, goalUnexpectedRunCount: targetRunIds.length - 1, goalOldActionCount: 1, goalOldActionBlockedReceiptCount: state.goal.initialPending.blockedReceiptCount, goalOldActionExecutionCount: oldExecution.executionCount, goalOldActionReplayCount: oldExecution.successfulReceiptCount, goalEditedActionExecutionCount: editedExecutionCount, goalPausedTaskDelta: state.goal.pausedAfterRestart.taskCount - state.goal.pauseSnapshot.taskCount, goalPausedPlanDelta: state.goal.pausedAfterRestart.planRevision - state.goal.pauseSnapshot.planRevision, goalPausedConversationDelta: state.goal.pausedAfterRestart.conversationCount - state.goal.pauseSnapshot.conversationCount, goalPausedProviderPlanDelta: state.goal.pausedAfterRestart.planProtocolCount - state.goal.pauseSnapshot.planProtocolCount, goalPausedProviderRequestStartedDelta: state.goal.pausedAfterRestart.providerRequestStartedCount - state.goal.pauseSnapshot.providerRequestStartedCount, goalPausedActionProgressDelta: state.goal.pausedAfterRestart.actionProgressCount - state.goal.pauseSnapshot.actionProgressCount, goalContextSchemaVersion: contextBundle.schemaVersion, goalPendingSchemaVersion: state.goal.editedPending.schemaVersion, projectRevision: projectRevision.revision, verificationPassed: true, verificationActionIdentityBound: true, verificationActionIdHash: hashValue(verificationExecution.actionId), goalProviderRequestStartedCount: providerLifecycle.startedCount, goalProviderRequestTerminalCount: providerLifecycle.terminalCount, goalFinalizationSchemaVersion: finalizationLifecycle.schemaVersion, goalFinalizationObserved: true, goalFinalizationStageCount: finalizationLifecycle.stageCount, goalFinalizationIdHash: finalizationLifecycle.finalizationIdHash, goalFinalizationJournalCount: finalizationJournalCount, goalCompletedProjectionCount: completedTasks.length, goalAssistantCount: assistantMessages.length, goalPublicBodyLeakCount, goalBodyReportLeakCount: state.goalBodyReportLeakCount, sideEffectActionCount: replayEvidence.sideEffectActionCount, sideEffectReplayCount: replayEvidence.sideEffectReplayCount, idempotentReplayActionCount: replayEvidence.idempotentReplayActionCount, actionReceiptReplayRecordCount: replayEvidence.actionReceiptReplayRecordCount, finalAssistantCount: assistantMessages.length, finalAssistantAuditCount: assistantAudits.length, duplicateActionCount, duplicateMessageCount, duplicateReceiptCount, confirmedActionCount: state.confirmedActionIds.size, projectPathPublicLeakCount, projectPathPublicSurfaceCount: Object.keys(projectPathPublicLeakCounts) .length, secretLeakCount: state.transcriptLeakCount + state.projectLeakCount, lureLeakCount: state.lureLeakCount, paths: [ '.agent/runtime/tasks', '.agent/runtime/events', '.agent/agent.db', relativeProjectPath(mainContextBundlePath()), relativeProjectPath(mainRuntimeStatePath()), '.agent/runtime/goals/current', '.agent/conversations', goalDeliveryPath, ], }; } export async function readGoalVerificationGate( projectRevision, verificationExecution, goal, ) { const manifest = await readJson( path.join(state.projectRoot, '.agent/manifest.json'), ); assert( projectRevision?.schemaVersion === 'game-creator-project-revision.v1' && isNonEmptyString(projectRevision.projectId) && manifest?.projectId === projectRevision.projectId && goal.projectId === manifest.projectId && Number.isSafeInteger(projectRevision.revision) && projectRevision.revision > 0 && Number.isSafeInteger(projectRevision.updatedAt) && projectRevision.updatedAt > 0, 'goal-project-revision-invalid', ); const verificationFiles = await listFiles( path.join(state.projectRoot, '.agent/runtime/verification'), ); const matching = []; for (const file of verificationFiles.filter((entry) => entry.endsWith('.json'), )) { const value = await readJson(file); if (value.agentId === mainAgentId && value.runId === state.initialRunId) { matching.push(value); } } assert(matching.length === 1, 'goal-verification-gate-count-invalid'); const gate = matching[0]; assert( gate.schemaVersion === 'game-creator-verification-gate.v1' && gate.projectId === projectRevision.projectId && gate.agentId === mainAgentId && gate.runId === state.initialRunId && gate.requiresVerification === true && gate.mutationRevision === projectRevision.revision && gate.verifiedRevision === projectRevision.revision && ['file.write', 'project.patchset'].includes(gate.lastMutationTool) && gate.lastVerificationTool === verificationExecution.tool && gate.lastVerificationStatus === 'passed' && Number.isSafeInteger(gate.updatedAt) && gate.updatedAt >= projectRevision.updatedAt && gate.updatedAt >= verificationExecution.verificationAudit.updatedAt && verificationExecution.verificationAudit.actionId === verificationExecution.actionId && verificationExecution.verificationAudit.actionFingerprint === verificationExecution.actionFingerprint, 'goal-verification-credential-invalid', ); return gate; } export function findFinalSuccessfulGoalVerificationExecution(agentDb) { let auditIndex = -1; for (let index = agentDb.length - 1; index >= 0; index -= 1) { const record = agentDb[index]; if ( record.recordType === 'agent.runtime.project.verify' && record.agentId === mainAgentId && record.runId === state.initialRunId ) { auditIndex = index; break; } } assert(auditIndex >= 0, 'goal-project-verification-missing'); const verificationAudit = agentDb[auditIndex]; assert( verificationAudit.status === 'completed' && verificationAudit.exitCode === 0 && verificationAudit.timedOut === false && ['test', 'check:e2e'].includes(verificationAudit.script) && verificationAudit.expectedCommand === verificationCommand && isNonEmptyString(verificationAudit.actionId) && isNonEmptyString(verificationAudit.actionFingerprint) && Number.isSafeInteger(verificationAudit.updatedAt) && hasExpectedWorkspaceSandboxMetadata(verificationAudit), 'goal-final-project-verification-audit-invalid', ); const execution = findSuccessfulToolExecution( agentDb, 'project.verify', state.initialRunId, (candidate) => candidate.actionId === verificationAudit.actionId && candidate.actionFingerprint === verificationAudit.actionFingerprint && ['test', 'check:e2e'].includes( auditInputValue(candidate.inputSummary, 'script'), ), ); assert( execution && execution.startIndex < auditIndex && auditIndex < execution.resultIndex, 'goal-final-project-verification-action-identity-invalid', ); return { ...execution, auditIndex, verificationAudit }; } export function validateGoalRevisionTwoRepairEvidence( agentDb, verificationExecution, ) { assert( state.goal.revisionTwoFixtureInjected === true && state.goal.revisionTwoHostFailureObserved === true && state.goal.revisionTwoFailureObserved === true && Number.isSafeInteger(state.goal.revisionTwoAgentDbBoundary) && state.goal.revisionTwoAgentDbBoundary > 0, 'goal-revision-two-failure-boundary-invalid', ); const afterFailureBoundary = (execution) => execution.startIndex >= state.goal.revisionTwoAgentDbBoundary && execution.completionIndex < verificationExecution.startIndex; const gamePatchset = findSuccessfulToolExecution( agentDb, 'project.patchset', state.initialRunId, (execution) => afterFailureBoundary(execution) && auditPatchsetPathsInclude(execution.inputSummary, [ 'update:game/index.html', ]), ); const createdPatchset = findSuccessfulToolExecution( agentDb, 'project.patchset', state.initialRunId, (execution) => afterFailureBoundary(execution) && auditPatchsetPathsInclude(execution.inputSummary, [ `create:${patchsetCreatedPath}`, ]), ); const gameWrite = findSuccessfulToolExecution( agentDb, 'file.write', state.initialRunId, (execution) => afterFailureBoundary(execution) && auditPathEquals(execution.inputSummary, 'game/index.html'), ); const createdWrite = findSuccessfulToolExecution( agentDb, 'file.write', state.initialRunId, (execution) => afterFailureBoundary(execution) && auditPathEquals(execution.inputSummary, patchsetCreatedPath), ); assert( Boolean(gamePatchset || gameWrite) && Boolean(createdPatchset || createdWrite), 'goal-revision-two-agent-repair-evidence-missing', ); const executions = [ gamePatchset ?? gameWrite, createdPatchset ?? createdWrite, ]; const byAction = new Map( executions.map((execution) => [execution.actionId, execution]), ); return { actionCount: byAction.size, fileWriteCount: [...byAction.values()].filter( (execution) => execution.tool === 'file.write', ).length, patchsetCount: [...byAction.values()].filter( (execution) => execution.tool === 'project.patchset', ).length, }; } export function validateGoalProviderRequestLifecycle(agentDb) { const records = agentDb.filter( (record) => record.recordType === 'agent.runtime.provider_request.lifecycle' && record.agentId === mainAgentId && record.runId === state.initialRunId, ); const byRequest = new Map(); for (const record of records) { assert( record.auditSchemaVersion === providerRequestLifecycleSchemaVersion && record.taskId && record.sessionId === state.initialSessionId && ['tool-plan', 'final-reply'].includes(record.requestKind) && typeof record.requestSlot === 'string' && typeof record.webSearchEnabled === 'boolean' && (record.requestKind !== 'final-reply' || record.webSearchEnabled === false) && !['prompt', 'response', 'error', 'baseUrl', 'model'].some((key) => Object.hasOwn(record, key), ), 'goal-provider-request-lifecycle-invalid', ); const group = byRequest.get(record.requestId) ?? []; group.push(record); byRequest.set(record.requestId, group); } assert(byRequest.size > 0, 'goal-provider-request-lifecycle-missing'); for (const group of byRequest.values()) { assert( group.length === 2 && group[0].status === 'started' && ['completed', 'failed', 'interrupted'].includes(group[1].status) && group[0].requestKind === group[1].requestKind && group[0].requestSlot === group[1].requestSlot, 'goal-provider-request-lifecycle-incomplete', ); } return { startedCount: byRequest.size, terminalCount: byRequest.size, }; } export function validateGoalFinalizationLifecycle(agentDb, goal, runtimeState) { const records = agentDb.filter( (record) => record.recordType === 'agent.runtime.finalization.lifecycle' && record.agentId === mainAgentId && record.runId === state.initialRunId, ); const expectedStages = [ 'prepared', 'assistant-persisted', 'runtime-completed', 'goal-completed', ]; assert( records.length === expectedStages.length, 'goal-finalization-audit-count-invalid', ); const finalizationId = records[0]?.finalizationId; const messageId = records[0]?.messageId; const expectedGoalSnapshotFingerprint = goalSnapshotFingerprint(goal); for (const [index, record] of records.entries()) { assert( record.auditSchemaVersion === 'game-creator-finalization-lifecycle.v1' && record.journalSchemaVersion === 'game-creator-runtime-finalization.v4' && record.finalizationId === finalizationId && record.messageId === messageId && record.taskId === runtimeState.taskId && record.sessionId === state.initialSessionId && record.stage === expectedStages[index] && record.stageOrdinal === index + 1 && record.previousStage === (index === 0 ? null : expectedStages[index - 1]) && record.goalId === goal.goalId && record.goalRevision === goal.revision && record.goalSnapshotFingerprint === expectedGoalSnapshotFingerprint && record.planRevision === runtimeState.planRevision && record.responseFingerprint === goal.responseFingerprint && Number.isSafeInteger(record.stageAt) && record.stageAt > 0 && !['task', 'response', 'prompt', 'observation'].some((key) => Object.hasOwn(record, key), ), 'goal-finalization-audit-invalid', ); if (index > 0) { assert( record.stageAt >= records[index - 1].stageAt, 'goal-finalization-audit-time-regressed', ); } } const assistantAuditIndex = agentDb.findIndex( (record) => record.recordType === 'conversation.message' && record.messageId === messageId && record.role === 'assistant', ); const assistantStageIndex = agentDb.findIndex( (record) => record.recordType === 'agent.runtime.finalization.lifecycle' && record.finalizationId === finalizationId && record.stage === 'assistant-persisted', ); assert( assistantAuditIndex >= 0 && assistantAuditIndex < assistantStageIndex, 'goal-finalization-assistant-order-invalid', ); return { schemaVersion: records[0].journalSchemaVersion, stageCount: records.length, finalizationIdHash: hashValue(finalizationId), firstStageIndex: agentDb.findIndex((record) => record === records[0]), }; } export async function assertGoalInitialMarkerAbsent(code, actionId = null) { if (!isGoalRuntimeSuite() || !state.projectRoot) return; const count = await countMarkerOutsideRuntimeControl(goalInitialMarker); state.goal.initialMarkerAbsenceCheckCount += 1; if (isNonEmptyString(actionId)) { state.goal.monitoredWriteActionIds.add(actionId); } assert(count === 0, `${code}-revision-one-marker-landed`); } export function emptyGoalEvidence() { return { scenario: 'goal-edit-pause-runner-restart-resume', taskCount: 0, eventCount: 0, agentDbRecordCount: 0, conversationMessageCount: 0, successfulToolExecutionCount: 0, toolPlanProtocolCount: 0, nativeRuntimeToolPlanCount: 0, toolPlanRepairCount: 0, nativeRuntimeToolPlanRepairCount: 0, toolPlanRepairedLoopCount: 0, toolPlanSecondRepairCount: 0, toolPlanRepairCountsByProtocolErrorKind: emptyToolPlanRepairCountsByProtocolErrorKind(), wrapperToolPlanFallbackCount: 0, textJsonToolPlanFallbackCount: 0, toolPlanAuditPayloadLeakCount: 0, structuredPlanRevision: 0, structuredPlanCompletedStepCount: 0, goalInitialCompletedStepCount: 0, goalRetainedInitialCompletedStepCount: 0, goalEditedEvidenceAbsentBeforeEdit: false, goalRevisionOneFixtureIsolated: false, goalRevisionTwoFixtureInjected: false, goalRevisionTwoHostFailureObserved: false, goalRevisionTwoFailureObserved: false, goalRevisionTwoFailureExitCode: null, goalRevisionTwoFailureFingerprint: null, goalRevisionTwoRepairActionCount: 0, goalRevisionTwoRepairFileWriteCount: 0, goalRevisionTwoRepairPatchsetCount: 0, goalIdHash: null, goalInitialRevision: 0, goalEditedRevision: 0, goalSnapshotFingerprintChanged: false, goalInitialMarkerAbsenceCheckCount: 0, goalMonitoredWriteActionCount: 0, goalPreFailureDeliveryActionCount: 0, goalFinalStatus: null, goalEditProviderInterrupted: false, goalPauseProviderInterrupted: false, goalPausedBeforeKill: false, goalPausedAfterRestart: false, goalRunnerBootChanged: false, goalRunnerKillMethod: null, goalRunnerPidfdClaimCount: 0, goalRunnerPidfdSignalCount: 0, goalRunnerStopped: false, goalAppDataCleanupPerformed: false, goalExecutionOwnerRecovered: false, goalExplicitResumeSameRun: false, goalTargetRunCount: 0, goalUnexpectedRunCount: 0, goalOldActionCount: 0, goalOldActionBlockedReceiptCount: 0, goalOldActionExecutionCount: 0, goalOldActionReplayCount: 0, goalEditedActionExecutionCount: 0, goalPausedTaskDelta: 0, goalPausedPlanDelta: 0, goalPausedConversationDelta: 0, goalPausedProviderPlanDelta: 0, goalPausedProviderRequestStartedDelta: 0, goalPausedActionProgressDelta: 0, goalContextSchemaVersion: null, goalPendingSchemaVersion: null, projectRevision: 0, verificationPassed: false, verificationActionIdentityBound: false, verificationActionIdHash: null, goalProviderRequestStartedCount: 0, goalProviderRequestTerminalCount: 0, goalFinalizationSchemaVersion: null, goalFinalizationObserved: false, goalFinalizationStageCount: 0, goalFinalizationIdHash: null, goalFinalizationJournalCount: 0, goalCompletedProjectionCount: 0, goalAssistantCount: 0, goalPublicBodyLeakCount: 0, goalBodyReportLeakCount: 0, sideEffectActionCount: 0, sideEffectReplayCount: 0, idempotentReplayActionCount: 0, actionReceiptReplayRecordCount: 0, finalAssistantCount: 0, finalAssistantAuditCount: 0, duplicateActionCount: 0, duplicateMessageCount: 0, duplicateReceiptCount: 0, confirmedActionCount: 0, projectPathPublicLeakCount: 0, projectPathPublicSurfaceCount: 0, projectPathTranscriptLeakCount: 0, projectPathReportLeakCount: 0, secretLeakCount: 0, lureLeakCount: 0, failureEvidenceErrors: { task: [], event: [], agentDb: [], conversation: [], }, paths: [], }; } export async function collectPartialGoalEvidence() { const [taskSurface, eventSurface, agentDbSurface, conversationSurface] = await Promise.all([ collectPartialRuntimeJsonlSurface('goal', 'task', async () => ( await listFiles(path.join(state.projectRoot, '.agent/runtime/tasks')) ).filter((file) => file.endsWith('.jsonl')), ), collectPartialRuntimeJsonlSurface('goal', 'event', async () => ( await listFiles(path.join(state.projectRoot, '.agent/runtime/events')) ).filter((file) => file.endsWith('.jsonl')), ), collectPartialRuntimeJsonlSurface('goal', 'agent-db', async () => [ path.join(state.projectRoot, '.agent/agent.db'), ]), collectPartialRuntimeJsonlSurface('goal', 'conversation', async () => ( await listFiles(path.join(state.projectRoot, '.agent/conversations')) ).filter((file) => file.endsWith('.jsonl')), ), ]); const taskSnapshot = buildTaskSnapshot(taskSurface.records); const events = eventSurface.records; const agentDb = agentDbSurface.records; const conversations = conversationSurface.records; const runtime = await readJson(mainRuntimeStatePath()).catch(() => null); const contextFiles = await listFiles( path.join(state.projectRoot, '.agent/runtime/context-bundles', mainAgentId), ).catch(() => []); const context = await readLatestJsonFile(contextFiles); const goalFiles = await listFiles( path.join(state.projectRoot, '.agent/runtime/goals/current'), ).catch(() => []); const goals = await Promise.all( goalFiles .filter((file) => file.endsWith('.json')) .map((file) => readJson(file).catch(() => null)), ); const goal = goals.find( (candidate) => candidate?.agentId === mainAgentId && (!state.initialRunId || candidate.runId === state.initialRunId), ); const targetRunIds = [ ...new Set( taskSnapshot.all .filter((task) => task.agentId === mainAgentId) .map((task) => task.runId), ), ]; const receipts = agentDb.filter( (record) => record.recordType === 'agent.runtime.action_receipt', ); const providerLifecycle = agentDb.filter( (record) => record.recordType === 'agent.runtime.provider_request.lifecycle' && record.agentId === mainAgentId, ); const finalizationLifecycle = agentDb.filter( (record) => record.recordType === 'agent.runtime.finalization.lifecycle' && record.agentId === mainAgentId, ); const completedStepCount = Array.isArray(runtime?.planSteps) ? runtime.planSteps.filter((step) => step.status === 'completed').length : 0; const nativeToolPlanProtocolEvidence = collectNativeRuntimeToolPlanProtocolEvidence(agentDb); return { taskCount: taskSnapshot.all.length, eventCount: events.length, agentDbRecordCount: agentDb.length, conversationMessageCount: conversations.length, successfulToolExecutionCount: receipts.filter( (record) => record.status === 'ok', ).length, ...nativeToolPlanProtocolEvidence, structuredPlanRevision: runtime?.planRevision ?? 0, structuredPlanCompletedStepCount: completedStepCount, goalEditedEvidenceAbsentBeforeEdit: state.goal.editedEvidenceAbsentBeforeEdit, goalRevisionOneFixtureIsolated: state.goal.revisionOneFixtureIsolated, goalRevisionTwoFixtureInjected: state.goal.revisionTwoFixtureInjected, goalRevisionTwoHostFailureObserved: state.goal.revisionTwoHostFailureObserved, goalRevisionTwoFailureObserved: state.goal.revisionTwoFailureObserved, goalRevisionTwoFailureExitCode: state.goal.revisionTwoFailureExitCode, goalRevisionTwoFailureFingerprint: state.goal.revisionTwoFailureFingerprint, goalIdHash: isNonEmptyString(goal?.goalId) ? hashValue(goal.goalId) : null, goalInitialRevision: state.goal.initialRevision || goal?.revision || 0, goalEditedRevision: state.goal.editedRevision, goalSnapshotFingerprintChanged: isNonEmptyString(state.goal.initialGoalSnapshotFingerprint) && isNonEmptyString(state.goal.editedGoalSnapshotFingerprint) && state.goal.initialGoalSnapshotFingerprint !== state.goal.editedGoalSnapshotFingerprint, goalInitialMarkerAbsenceCheckCount: state.goal.initialMarkerAbsenceCheckCount, goalMonitoredWriteActionCount: state.goal.monitoredWriteActionIds.size, goalPreFailureDeliveryActionCount: state.goal.preFailureDeliveryActionIds.size, goalFinalStatus: goal?.status ?? runtime?.goalStatus ?? null, goalRunnerKillMethod: state.goal.runner.pidfdClaimCount > 0 ? 'linux-pidfd' : null, goalRunnerPidfdClaimCount: state.goal.runner.pidfdClaimCount, goalRunnerPidfdSignalCount: state.goal.runner.pidfdSignalCount, goalTargetRunCount: targetRunIds.length, goalUnexpectedRunCount: Math.max(0, targetRunIds.length - 1), goalContextSchemaVersion: context?.schemaVersion ?? null, goalProviderRequestStartedCount: providerLifecycle.filter( (record) => record.status === 'started', ).length, goalProviderRequestTerminalCount: providerLifecycle.filter((record) => ['completed', 'failed', 'interrupted'].includes(record.status), ).length, goalFinalizationSchemaVersion: finalizationLifecycle.at(-1)?.journalSchemaVersion ?? null, goalFinalizationObserved: finalizationLifecycle.length > 0, goalFinalizationStageCount: finalizationLifecycle.length, goalFinalizationIdHash: isNonEmptyString( finalizationLifecycle.at(-1)?.finalizationId, ) ? hashValue(finalizationLifecycle.at(-1).finalizationId) : null, goalAssistantCount: conversations.filter( (message) => message.role === 'assistant', ).length, finalAssistantCount: goal?.status === 'completed' ? conversations.filter((message) => message.role === 'assistant').length : 0, finalAssistantAuditCount: goal?.status === 'completed' ? agentDb.filter( (record) => record.recordType === 'conversation.message' && record.role === 'assistant', ).length : 0, duplicateMessageCount: duplicateCount( conversations.map((message) => message.messageId).filter(Boolean), ), duplicateReceiptCount: duplicateCount(receipts.map(receiptAuditIdentity)), failureEvidenceErrors: { task: taskSurface.errors, event: eventSurface.errors, agentDb: agentDbSurface.errors, conversation: conversationSurface.errors, }, paths: [ '.agent/runtime/tasks', '.agent/runtime/events', '.agent/agent.db', '.agent/runtime/context-bundles', '.agent/runtime/goals/current', '.agent/conversations', ], }; } export function isGoalRuntimeSuite() { return state.suite === goalRuntimeSuite; } export function goalStaleActionReceiptMatchesOriginalAction(original, receipt) { if ( !isGoalRuntimeSuite() || receipt.recordType !== 'agent.runtime.action_receipt' || receipt.tool !== 'runtime.goal' || receipt.status !== 'blocked' || !goalProjectWriteTools.has(original.tool) || !isNonEmptyString(original.rawInputSummary) || original.agentId !== receipt.agentId || original.runId !== receipt.runId || original.actionFingerprint !== receipt.actionFingerprint ) { return false; } const expectedReceiptSummary = canonicalAuditInputSummary( `inputSummarySha256=${hashValue(original.rawInputSummary)} ยท inputSummaryChars=${[...original.rawInputSummary].length}`, ); return receipt.inputSummary === expectedReceiptSummary; }