tracegrove 0.0.0-stage → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +588 -2
- package/conformance/coordination-vectors.json +377 -0
- package/dist/adapters/browser/capabilities.d.ts +45 -0
- package/dist/adapters/browser/capabilities.js +14 -0
- package/dist/adapters/browser/capabilities.js.map +1 -0
- package/dist/adapters/browser/driver.d.ts +45 -0
- package/dist/adapters/browser/driver.js +295 -0
- package/dist/adapters/browser/driver.js.map +1 -0
- package/dist/adapters/browser/session.d.ts +21 -0
- package/dist/adapters/browser/session.js +87 -0
- package/dist/adapters/browser/session.js.map +1 -0
- package/dist/adapters/http/capabilities.d.ts +2 -0
- package/dist/adapters/http/capabilities.js +21 -0
- package/dist/adapters/http/capabilities.js.map +1 -0
- package/dist/adapters/http/driver.d.ts +55 -0
- package/dist/adapters/http/driver.js +125 -0
- package/dist/adapters/http/driver.js.map +1 -0
- package/dist/adapters/job/capabilities.d.ts +45 -0
- package/dist/adapters/job/capabilities.js +8 -0
- package/dist/adapters/job/capabilities.js.map +1 -0
- package/dist/adapters/job/driver.d.ts +26 -0
- package/dist/adapters/job/driver.js +110 -0
- package/dist/adapters/job/driver.js.map +1 -0
- package/dist/adapters/job/types.d.ts +9 -0
- package/dist/adapters/job/types.js +2 -0
- package/dist/adapters/job/types.js.map +1 -0
- package/dist/adapters/mcp/capabilities.d.ts +46 -0
- package/dist/adapters/mcp/capabilities.js +17 -0
- package/dist/adapters/mcp/capabilities.js.map +1 -0
- package/dist/adapters/mcp/driver.d.ts +10 -0
- package/dist/adapters/mcp/driver.js +83 -0
- package/dist/adapters/mcp/driver.js.map +1 -0
- package/dist/adapters/poll.d.ts +12 -0
- package/dist/adapters/poll.js +31 -0
- package/dist/adapters/poll.js.map +1 -0
- package/dist/authoring/activity.d.ts +17 -0
- package/dist/authoring/activity.js +18 -0
- package/dist/authoring/activity.js.map +1 -0
- package/dist/authoring/agent.d.ts +70 -0
- package/dist/authoring/agent.js +268 -0
- package/dist/authoring/agent.js.map +1 -0
- package/dist/authoring/appLaunch.d.ts +7 -0
- package/dist/authoring/appLaunch.js +32 -0
- package/dist/authoring/appLaunch.js.map +1 -0
- package/dist/authoring/appLocation.d.ts +2 -0
- package/dist/authoring/appLocation.js +5 -0
- package/dist/authoring/appLocation.js.map +1 -0
- package/dist/authoring/browser.d.ts +15 -0
- package/dist/authoring/browser.js +20 -0
- package/dist/authoring/browser.js.map +1 -0
- package/dist/authoring/compile.d.ts +47 -0
- package/dist/authoring/compile.js +508 -0
- package/dist/authoring/compile.js.map +1 -0
- package/dist/authoring/discover.d.ts +43 -0
- package/dist/authoring/discover.js +149 -0
- package/dist/authoring/discover.js.map +1 -0
- package/dist/authoring/environmentTemplates.d.ts +3 -0
- package/dist/authoring/environmentTemplates.js +24 -0
- package/dist/authoring/environmentTemplates.js.map +1 -0
- package/dist/authoring/heal.d.ts +108 -0
- package/dist/authoring/heal.js +723 -0
- package/dist/authoring/heal.js.map +1 -0
- package/dist/authoring/index.d.ts +14 -0
- package/dist/authoring/index.js +15 -0
- package/dist/authoring/index.js.map +1 -0
- package/dist/authoring/init.d.ts +21 -0
- package/dist/authoring/init.js +141 -0
- package/dist/authoring/init.js.map +1 -0
- package/dist/authoring/localSettings.d.ts +33 -0
- package/dist/authoring/localSettings.js +31 -0
- package/dist/authoring/localSettings.js.map +1 -0
- package/dist/authoring/media.d.ts +10 -0
- package/dist/authoring/media.js +37 -0
- package/dist/authoring/media.js.map +1 -0
- package/dist/authoring/module.d.ts +2 -0
- package/dist/authoring/module.js +38 -0
- package/dist/authoring/module.js.map +1 -0
- package/dist/authoring/run.d.ts +74 -0
- package/dist/authoring/run.js +242 -0
- package/dist/authoring/run.js.map +1 -0
- package/dist/authoring/runSource.d.ts +6 -0
- package/dist/authoring/runSource.js +24 -0
- package/dist/authoring/runSource.js.map +1 -0
- package/dist/authoring/savedRun.d.ts +13 -0
- package/dist/authoring/savedRun.js +94 -0
- package/dist/authoring/savedRun.js.map +1 -0
- package/dist/authoring/schema.d.ts +574 -0
- package/dist/authoring/schema.js +147 -0
- package/dist/authoring/schema.js.map +1 -0
- package/dist/authoring/snapshotSource.d.ts +13 -0
- package/dist/authoring/snapshotSource.js +41 -0
- package/dist/authoring/snapshotSource.js.map +1 -0
- package/dist/authoring/sourceSelection.d.ts +34 -0
- package/dist/authoring/sourceSelection.js +13 -0
- package/dist/authoring/sourceSelection.js.map +1 -0
- package/dist/authoring/sources.d.ts +5 -0
- package/dist/authoring/sources.js +65 -0
- package/dist/authoring/sources.js.map +1 -0
- package/dist/authoring/status.d.ts +48 -0
- package/dist/authoring/status.js +100 -0
- package/dist/authoring/status.js.map +1 -0
- package/dist/authoring/typed.d.ts +6 -0
- package/dist/authoring/typed.js +5 -0
- package/dist/authoring/typed.js.map +1 -0
- package/dist/authoring/ui.d.ts +10 -0
- package/dist/authoring/ui.js +486 -0
- package/dist/authoring/ui.js.map +1 -0
- package/dist/authoring/uiPage.d.ts +1 -0
- package/dist/authoring/uiPage.js +358 -0
- package/dist/authoring/uiPage.js.map +1 -0
- package/dist/authoring/uiSourceControls.d.ts +2 -0
- package/dist/authoring/uiSourceControls.js +41 -0
- package/dist/authoring/uiSourceControls.js.map +1 -0
- package/dist/authoring/verificationSource.d.ts +41 -0
- package/dist/authoring/verificationSource.js +46 -0
- package/dist/authoring/verificationSource.js.map +1 -0
- package/dist/authoring/workspace.d.ts +33 -0
- package/dist/authoring/workspace.js +58 -0
- package/dist/authoring/workspace.js.map +1 -0
- package/dist/cli/arguments.d.ts +288 -0
- package/dist/cli/arguments.js +121 -0
- package/dist/cli/arguments.js.map +1 -0
- package/dist/cli/cleanup.d.ts +18 -0
- package/dist/cli/cleanup.js +40 -0
- package/dist/cli/cleanup.js.map +1 -0
- package/dist/cli/doctor.d.ts +51 -0
- package/dist/cli/doctor.js +118 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/errors.d.ts +2 -0
- package/dist/cli/errors.js +84 -0
- package/dist/cli/errors.js.map +1 -0
- package/dist/cli/jsonFile.d.ts +2 -0
- package/dist/cli/jsonFile.js +15 -0
- package/dist/cli/jsonFile.js.map +1 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +603 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/cli/render.d.ts +22 -0
- package/dist/cli/render.js +150 -0
- package/dist/cli/render.js.map +1 -0
- package/dist/cli/samples/catalog.d.ts +10 -0
- package/dist/cli/samples/catalog.js +3 -0
- package/dist/cli/samples/catalog.js.map +1 -0
- package/dist/cli/samples/write.d.ts +5 -0
- package/dist/cli/samples/write.js +64 -0
- package/dist/cli/samples/write.js.map +1 -0
- package/dist/cli/source.d.ts +41 -0
- package/dist/cli/source.js +59 -0
- package/dist/cli/source.js.map +1 -0
- package/dist/core/activationDecision.d.ts +153 -0
- package/dist/core/activationDecision.js +34 -0
- package/dist/core/activationDecision.js.map +1 -0
- package/dist/core/artifacts.d.ts +18 -0
- package/dist/core/artifacts.js +79 -0
- package/dist/core/artifacts.js.map +1 -0
- package/dist/core/atomicFiles.d.ts +9 -0
- package/dist/core/atomicFiles.js +76 -0
- package/dist/core/atomicFiles.js.map +1 -0
- package/dist/core/boundedFile.d.ts +2 -0
- package/dist/core/boundedFile.js +28 -0
- package/dist/core/boundedFile.js.map +1 -0
- package/dist/core/bundle.d.ts +77 -0
- package/dist/core/bundle.js +375 -0
- package/dist/core/bundle.js.map +1 -0
- package/dist/core/bundleArchive.d.ts +10 -0
- package/dist/core/bundleArchive.js +47 -0
- package/dist/core/bundleArchive.js.map +1 -0
- package/dist/core/bundleEvidence.d.ts +12 -0
- package/dist/core/bundleEvidence.js +169 -0
- package/dist/core/bundleEvidence.js.map +1 -0
- package/dist/core/bundleExport.d.ts +12 -0
- package/dist/core/bundleExport.js +36 -0
- package/dist/core/bundleExport.js.map +1 -0
- package/dist/core/bundleFiles.d.ts +5 -0
- package/dist/core/bundleFiles.js +47 -0
- package/dist/core/bundleFiles.js.map +1 -0
- package/dist/core/bundlePaths.d.ts +2 -0
- package/dist/core/bundlePaths.js +7 -0
- package/dist/core/bundlePaths.js.map +1 -0
- package/dist/core/bundleSnapshot.d.ts +26 -0
- package/dist/core/bundleSnapshot.js +71 -0
- package/dist/core/bundleSnapshot.js.map +1 -0
- package/dist/core/capabilityBoundary.d.ts +1452 -0
- package/dist/core/capabilityBoundary.js +361 -0
- package/dist/core/capabilityBoundary.js.map +1 -0
- package/dist/core/capabilityLifecycle.d.ts +366 -0
- package/dist/core/capabilityLifecycle.js +79 -0
- package/dist/core/capabilityLifecycle.js.map +1 -0
- package/dist/core/cleanup.d.ts +35 -0
- package/dist/core/cleanup.js +71 -0
- package/dist/core/cleanup.js.map +1 -0
- package/dist/core/cleanupActions.d.ts +5 -0
- package/dist/core/cleanupActions.js +15 -0
- package/dist/core/cleanupActions.js.map +1 -0
- package/dist/core/cleanupAttestation.d.ts +23 -0
- package/dist/core/cleanupAttestation.js +57 -0
- package/dist/core/cleanupAttestation.js.map +1 -0
- package/dist/core/clock.d.ts +4 -0
- package/dist/core/clock.js +14 -0
- package/dist/core/clock.js.map +1 -0
- package/dist/core/compiler.d.ts +19 -0
- package/dist/core/compiler.js +200 -0
- package/dist/core/compiler.js.map +1 -0
- package/dist/core/controller.d.ts +7 -0
- package/dist/core/controller.js +132 -0
- package/dist/core/controller.js.map +1 -0
- package/dist/core/coordination.d.ts +72 -0
- package/dist/core/coordination.js +118 -0
- package/dist/core/coordination.js.map +1 -0
- package/dist/core/corpus.d.ts +60 -0
- package/dist/core/corpus.js +216 -0
- package/dist/core/corpus.js.map +1 -0
- package/dist/core/deadline.d.ts +12 -0
- package/dist/core/deadline.js +38 -0
- package/dist/core/deadline.js.map +1 -0
- package/dist/core/deliveryJournal.d.ts +37 -0
- package/dist/core/deliveryJournal.js +85 -0
- package/dist/core/deliveryJournal.js.map +1 -0
- package/dist/core/environmentPreflight.d.ts +14 -0
- package/dist/core/environmentPreflight.js +179 -0
- package/dist/core/environmentPreflight.js.map +1 -0
- package/dist/core/errors.d.ts +22 -0
- package/dist/core/errors.js +29 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/events.d.ts +14 -0
- package/dist/core/events.js +37 -0
- package/dist/core/events.js.map +1 -0
- package/dist/core/executionInspection.d.ts +90 -0
- package/dist/core/executionInspection.js +31 -0
- package/dist/core/executionInspection.js.map +1 -0
- package/dist/core/executionOwnership.d.ts +72 -0
- package/dist/core/executionOwnership.js +82 -0
- package/dist/core/executionOwnership.js.map +1 -0
- package/dist/core/executionPreparation.d.ts +362 -0
- package/dist/core/executionPreparation.js +93 -0
- package/dist/core/executionPreparation.js.map +1 -0
- package/dist/core/executionReadback.d.ts +15 -0
- package/dist/core/executionReadback.js +21 -0
- package/dist/core/executionReadback.js.map +1 -0
- package/dist/core/executor.d.ts +46 -0
- package/dist/core/executor.js +616 -0
- package/dist/core/executor.js.map +1 -0
- package/dist/core/frozenBundle.d.ts +314 -0
- package/dist/core/frozenBundle.js +24 -0
- package/dist/core/frozenBundle.js.map +1 -0
- package/dist/core/fsDurability.d.ts +2 -0
- package/dist/core/fsDurability.js +12 -0
- package/dist/core/fsDurability.js.map +1 -0
- package/dist/core/generators.d.ts +4 -0
- package/dist/core/generators.js +71 -0
- package/dist/core/generators.js.map +1 -0
- package/dist/core/gitSnapshot.d.ts +63 -0
- package/dist/core/gitSnapshot.js +55 -0
- package/dist/core/gitSnapshot.js.map +1 -0
- package/dist/core/gitSources.d.ts +29 -0
- package/dist/core/gitSources.js +97 -0
- package/dist/core/gitSources.js.map +1 -0
- package/dist/core/ledger.d.ts +38 -0
- package/dist/core/ledger.js +169 -0
- package/dist/core/ledger.js.map +1 -0
- package/dist/core/ledgerInspection.d.ts +18 -0
- package/dist/core/ledgerInspection.js +66 -0
- package/dist/core/ledgerInspection.js.map +1 -0
- package/dist/core/ledgerRecords.d.ts +23 -0
- package/dist/core/ledgerRecords.js +29 -0
- package/dist/core/ledgerRecords.js.map +1 -0
- package/dist/core/localActivationState.d.ts +260 -0
- package/dist/core/localActivationState.js +49 -0
- package/dist/core/localActivationState.js.map +1 -0
- package/dist/core/localPromotionState.d.ts +387 -0
- package/dist/core/localPromotionState.js +22 -0
- package/dist/core/localPromotionState.js.map +1 -0
- package/dist/core/localQualificationResults.d.ts +39 -0
- package/dist/core/localQualificationResults.js +160 -0
- package/dist/core/localQualificationResults.js.map +1 -0
- package/dist/core/localReviewStore.d.ts +28 -0
- package/dist/core/localReviewStore.js +167 -0
- package/dist/core/localReviewStore.js.map +1 -0
- package/dist/core/localScenarioActivation.d.ts +6 -0
- package/dist/core/localScenarioActivation.js +70 -0
- package/dist/core/localScenarioActivation.js.map +1 -0
- package/dist/core/localScenarioOwner.d.ts +20 -0
- package/dist/core/localScenarioOwner.js +2 -0
- package/dist/core/localScenarioOwner.js.map +1 -0
- package/dist/core/localScenarioPromotion.d.ts +9 -0
- package/dist/core/localScenarioPromotion.js +49 -0
- package/dist/core/localScenarioPromotion.js.map +1 -0
- package/dist/core/localScenarioState.d.ts +2297 -0
- package/dist/core/localScenarioState.js +117 -0
- package/dist/core/localScenarioState.js.map +1 -0
- package/dist/core/localScenarioStore.d.ts +838 -0
- package/dist/core/localScenarioStore.js +218 -0
- package/dist/core/localScenarioStore.js.map +1 -0
- package/dist/core/localScenarioStructure.d.ts +4 -0
- package/dist/core/localScenarioStructure.js +52 -0
- package/dist/core/localScenarioStructure.js.map +1 -0
- package/dist/core/localStructureState.d.ts +2 -0
- package/dist/core/localStructureState.js +22 -0
- package/dist/core/localStructureState.js.map +1 -0
- package/dist/core/lookup.d.ts +662 -0
- package/dist/core/lookup.js +55 -0
- package/dist/core/lookup.js.map +1 -0
- package/dist/core/ownership.d.ts +3 -0
- package/dist/core/ownership.js +24 -0
- package/dist/core/ownership.js.map +1 -0
- package/dist/core/planInspection.d.ts +108 -0
- package/dist/core/planInspection.js +44 -0
- package/dist/core/planInspection.js.map +1 -0
- package/dist/core/planner.d.ts +14 -0
- package/dist/core/planner.js +261 -0
- package/dist/core/planner.js.map +1 -0
- package/dist/core/qualification.d.ts +26 -0
- package/dist/core/qualification.js +58 -0
- package/dist/core/qualification.js.map +1 -0
- package/dist/core/qualificationBundle.d.ts +6 -0
- package/dist/core/qualificationBundle.js +51 -0
- package/dist/core/qualificationBundle.js.map +1 -0
- package/dist/core/qualificationExecution.d.ts +879 -0
- package/dist/core/qualificationExecution.js +54 -0
- package/dist/core/qualificationExecution.js.map +1 -0
- package/dist/core/recoveryStatus.d.ts +6 -0
- package/dist/core/recoveryStatus.js +123 -0
- package/dist/core/recoveryStatus.js.map +1 -0
- package/dist/core/reducer.d.ts +56 -0
- package/dist/core/reducer.js +355 -0
- package/dist/core/reducer.js.map +1 -0
- package/dist/core/replayAssessment.d.ts +15 -0
- package/dist/core/replayAssessment.js +78 -0
- package/dist/core/replayAssessment.js.map +1 -0
- package/dist/core/replayEvidence.d.ts +7 -0
- package/dist/core/replayEvidence.js +58 -0
- package/dist/core/replayEvidence.js.map +1 -0
- package/dist/core/repositorySelectionEvidence.d.ts +3 -0
- package/dist/core/repositorySelectionEvidence.js +58 -0
- package/dist/core/repositorySelectionEvidence.js.map +1 -0
- package/dist/core/rerun.d.ts +3 -0
- package/dist/core/rerun.js +24 -0
- package/dist/core/rerun.js.map +1 -0
- package/dist/core/resultSink.d.ts +52 -0
- package/dist/core/resultSink.js +120 -0
- package/dist/core/resultSink.js.map +1 -0
- package/dist/core/review.d.ts +22 -0
- package/dist/core/review.js +65 -0
- package/dist/core/review.js.map +1 -0
- package/dist/core/runnerCapabilities.d.ts +22 -0
- package/dist/core/runnerCapabilities.js +44 -0
- package/dist/core/runnerCapabilities.js.map +1 -0
- package/dist/core/scenario.d.ts +32 -0
- package/dist/core/scenario.js +119 -0
- package/dist/core/scenario.js.map +1 -0
- package/dist/core/scenarioActivation.d.ts +16 -0
- package/dist/core/scenarioActivation.js +47 -0
- package/dist/core/scenarioActivation.js.map +1 -0
- package/dist/core/scenarioStructure.d.ts +400 -0
- package/dist/core/scenarioStructure.js +72 -0
- package/dist/core/scenarioStructure.js.map +1 -0
- package/dist/core/snapshotEvidence.d.ts +3 -0
- package/dist/core/snapshotEvidence.js +37 -0
- package/dist/core/snapshotEvidence.js.map +1 -0
- package/dist/core/snapshotRepository.d.ts +48 -0
- package/dist/core/snapshotRepository.js +47 -0
- package/dist/core/snapshotRepository.js.map +1 -0
- package/dist/core/source-analysis.d.ts +18 -0
- package/dist/core/source-analysis.js +99 -0
- package/dist/core/source-analysis.js.map +1 -0
- package/dist/core/source-citation.d.ts +53 -0
- package/dist/core/source-citation.js +59 -0
- package/dist/core/source-citation.js.map +1 -0
- package/dist/core/sourcePromotion.d.ts +929 -0
- package/dist/core/sourcePromotion.js +92 -0
- package/dist/core/sourcePromotion.js.map +1 -0
- package/dist/core/sourceReviewEvidence.d.ts +271 -0
- package/dist/core/sourceReviewEvidence.js +24 -0
- package/dist/core/sourceReviewEvidence.js.map +1 -0
- package/dist/core/targetObservation.d.ts +3 -0
- package/dist/core/targetObservation.js +26 -0
- package/dist/core/targetObservation.js.map +1 -0
- package/dist/hosts/launchTemplates.d.ts +2 -0
- package/dist/hosts/launchTemplates.js +5 -0
- package/dist/hosts/launchTemplates.js.map +1 -0
- package/dist/hosts/local.d.ts +17 -0
- package/dist/hosts/local.js +311 -0
- package/dist/hosts/local.js.map +1 -0
- package/dist/hosts/localSupervisor.d.ts +1 -0
- package/dist/hosts/localSupervisor.js +115 -0
- package/dist/hosts/localSupervisor.js.map +1 -0
- package/dist/hosts/mock.d.ts +4 -0
- package/dist/hosts/mock.js +82 -0
- package/dist/hosts/mock.js.map +1 -0
- package/dist/hosts/pinnedArguments.d.ts +3 -0
- package/dist/hosts/pinnedArguments.js +39 -0
- package/dist/hosts/pinnedArguments.js.map +1 -0
- package/dist/hosts/runtime.d.ts +5 -0
- package/dist/hosts/runtime.js +60 -0
- package/dist/hosts/runtime.js.map +1 -0
- package/dist/hosts/sourceIdentity.d.ts +54 -0
- package/dist/hosts/sourceIdentity.js +27 -0
- package/dist/hosts/sourceIdentity.js.map +1 -0
- package/dist/hosts/types.d.ts +39 -0
- package/dist/hosts/types.js +2 -0
- package/dist/hosts/types.js.map +1 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.js +52 -0
- package/dist/index.js.map +1 -0
- package/dist/integrations/awr/attachment.d.ts +34 -0
- package/dist/integrations/awr/attachment.js +120 -0
- package/dist/integrations/awr/attachment.js.map +1 -0
- package/dist/integrations/awr/attachmentContracts.d.ts +273 -0
- package/dist/integrations/awr/attachmentContracts.js +39 -0
- package/dist/integrations/awr/attachmentContracts.js.map +1 -0
- package/dist/integrations/awr/attachmentHttp.d.ts +9 -0
- package/dist/integrations/awr/attachmentHttp.js +19 -0
- package/dist/integrations/awr/attachmentHttp.js.map +1 -0
- package/dist/integrations/awr/cli.d.ts +7 -0
- package/dist/integrations/awr/cli.js +43 -0
- package/dist/integrations/awr/cli.js.map +1 -0
- package/dist/integrations/awr/contracts.d.ts +54 -0
- package/dist/integrations/awr/contracts.js +33 -0
- package/dist/integrations/awr/contracts.js.map +1 -0
- package/dist/integrations/awr/http.d.ts +8 -0
- package/dist/integrations/awr/http.js +23 -0
- package/dist/integrations/awr/http.js.map +1 -0
- package/dist/integrations/awr/index.d.ts +7 -0
- package/dist/integrations/awr/index.js +8 -0
- package/dist/integrations/awr/index.js.map +1 -0
- package/dist/integrations/awr/jsonHttp.d.ts +15 -0
- package/dist/integrations/awr/jsonHttp.js +69 -0
- package/dist/integrations/awr/jsonHttp.js.map +1 -0
- package/dist/integrations/awr/outbox.d.ts +28 -0
- package/dist/integrations/awr/outbox.js +88 -0
- package/dist/integrations/awr/outbox.js.map +1 -0
- package/dist/integrations/awr/projection.d.ts +4 -0
- package/dist/integrations/awr/projection.js +54 -0
- package/dist/integrations/awr/projection.js.map +1 -0
- package/dist/protocol/canonical.d.ts +41 -0
- package/dist/protocol/canonical.js +114 -0
- package/dist/protocol/canonical.js.map +1 -0
- package/dist/protocol/index.d.ts +9620 -0
- package/dist/protocol/index.js +185 -0
- package/dist/protocol/index.js.map +1 -0
- package/dist/protocol/primitives.d.ts +63 -0
- package/dist/protocol/primitives.js +46 -0
- package/dist/protocol/primitives.js.map +1 -0
- package/dist/protocol/schemas/browser.d.ts +153 -0
- package/dist/protocol/schemas/browser.js +29 -0
- package/dist/protocol/schemas/browser.js.map +1 -0
- package/dist/protocol/schemas/capability.d.ts +2126 -0
- package/dist/protocol/schemas/capability.js +165 -0
- package/dist/protocol/schemas/capability.js.map +1 -0
- package/dist/protocol/schemas/capabilityLifecycle.d.ts +509 -0
- package/dist/protocol/schemas/capabilityLifecycle.js +47 -0
- package/dist/protocol/schemas/capabilityLifecycle.js.map +1 -0
- package/dist/protocol/schemas/cleanup.d.ts +109 -0
- package/dist/protocol/schemas/cleanup.js +59 -0
- package/dist/protocol/schemas/cleanup.js.map +1 -0
- package/dist/protocol/schemas/cli.d.ts +121 -0
- package/dist/protocol/schemas/cli.js +24 -0
- package/dist/protocol/schemas/cli.js.map +1 -0
- package/dist/protocol/schemas/config.d.ts +59 -0
- package/dist/protocol/schemas/config.js +32 -0
- package/dist/protocol/schemas/config.js.map +1 -0
- package/dist/protocol/schemas/corpus.d.ts +36 -0
- package/dist/protocol/schemas/corpus.js +10 -0
- package/dist/protocol/schemas/corpus.js.map +1 -0
- package/dist/protocol/schemas/definitions.d.ts +3409 -0
- package/dist/protocol/schemas/definitions.js +431 -0
- package/dist/protocol/schemas/definitions.js.map +1 -0
- package/dist/protocol/schemas/evidenceProjection.d.ts +146 -0
- package/dist/protocol/schemas/evidenceProjection.js +18 -0
- package/dist/protocol/schemas/evidenceProjection.js.map +1 -0
- package/dist/protocol/schemas/execution.d.ts +3179 -0
- package/dist/protocol/schemas/execution.js +500 -0
- package/dist/protocol/schemas/execution.js.map +1 -0
- package/dist/protocol/schemas/gitSources.d.ts +330 -0
- package/dist/protocol/schemas/gitSources.js +32 -0
- package/dist/protocol/schemas/gitSources.js.map +1 -0
- package/dist/protocol/schemas/mcp.d.ts +24 -0
- package/dist/protocol/schemas/mcp.js +16 -0
- package/dist/protocol/schemas/mcp.js.map +1 -0
- package/dist/protocol/schemas/nativeJob.d.ts +106 -0
- package/dist/protocol/schemas/nativeJob.js +16 -0
- package/dist/protocol/schemas/nativeJob.js.map +1 -0
- package/dist/protocol/schemas/portableEnvironment.d.ts +1256 -0
- package/dist/protocol/schemas/portableEnvironment.js +108 -0
- package/dist/protocol/schemas/portableEnvironment.js.map +1 -0
- package/dist/protocol/schemas/promotion.d.ts +132 -0
- package/dist/protocol/schemas/promotion.js +9 -0
- package/dist/protocol/schemas/promotion.js.map +1 -0
- package/dist/protocol/schemas/protocol.d.ts +146 -0
- package/dist/protocol/schemas/protocol.js +25 -0
- package/dist/protocol/schemas/protocol.js.map +1 -0
- package/dist/protocol/schemas/recoveryStatus.d.ts +88 -0
- package/dist/protocol/schemas/recoveryStatus.js +20 -0
- package/dist/protocol/schemas/recoveryStatus.js.map +1 -0
- package/dist/protocol/schemas/resultSink.d.ts +80 -0
- package/dist/protocol/schemas/resultSink.js +14 -0
- package/dist/protocol/schemas/resultSink.js.map +1 -0
- package/dist/protocol/schemas/review.d.ts +248 -0
- package/dist/protocol/schemas/review.js +45 -0
- package/dist/protocol/schemas/review.js.map +1 -0
- package/dist/protocol/schemas/scenario.d.ts +810 -0
- package/dist/protocol/schemas/scenario.js +53 -0
- package/dist/protocol/schemas/scenario.js.map +1 -0
- package/dist/protocol/schemas/source.d.ts +300 -0
- package/dist/protocol/schemas/source.js +25 -0
- package/dist/protocol/schemas/source.js.map +1 -0
- package/dist/source/git.d.ts +36 -0
- package/dist/source/git.js +159 -0
- package/dist/source/git.js.map +1 -0
- package/dist/source/gitObjectProof.d.ts +12 -0
- package/dist/source/gitObjectProof.js +115 -0
- package/dist/source/gitObjectProof.js.map +1 -0
- package/dist/source/snapshotPaths.d.ts +2 -0
- package/dist/source/snapshotPaths.js +24 -0
- package/dist/source/snapshotPaths.js.map +1 -0
- package/dist/source/snapshotTree.d.ts +15 -0
- package/dist/source/snapshotTree.js +79 -0
- package/dist/source/snapshotTree.js.map +1 -0
- package/docs/architecture.md +126 -0
- package/docs/awr-delivery.md +102 -0
- package/docs/browser-driver.md +148 -0
- package/docs/cleanup-followups.md +74 -0
- package/docs/developer-ux.md +315 -0
- package/docs/examples/portable-environments/alongside_runner.json +199 -0
- package/docs/examples/portable-environments/external_staging.json +183 -0
- package/docs/git-object-proof.md +100 -0
- package/docs/git-source-resolution.md +133 -0
- package/docs/images/ui-run.png +0 -0
- package/docs/local-execution-recovery.md +55 -0
- package/docs/local-review-store.md +13 -0
- package/docs/local-scenario-store.md +91 -0
- package/docs/mcp-driver.md +87 -0
- package/docs/native-jobs.md +96 -0
- package/docs/portable-environments.md +155 -0
- package/docs/qualification-registration.md +58 -0
- package/docs/qualification-results.md +59 -0
- package/docs/replay-assessment.md +17 -0
- package/docs/result-sink.md +87 -0
- package/docs/scenario-activation.md +54 -0
- package/docs/scenario-structure.md +58 -0
- package/docs/source-promotion.md +104 -0
- package/docs/source-review.md +21 -0
- package/docs/verified-bundles.md +175 -0
- package/docs/work-report-attachment.md +127 -0
- package/package.json +45 -4
- package/samples/django-notes/manage.py +8 -0
- package/samples/django-notes/notes/__init__.py +0 -0
- package/samples/django-notes/notes/auth.py +14 -0
- package/samples/django-notes/notes/migrations/0001_initial.py +19 -0
- package/samples/django-notes/notes/migrations/__init__.py +0 -0
- package/samples/django-notes/notes/models.py +13 -0
- package/samples/django-notes/notes/views.py +33 -0
- package/samples/django-notes/notesite/__init__.py +0 -0
- package/samples/django-notes/notesite/settings.py +17 -0
- package/samples/django-notes/notesite/urls.py +9 -0
- package/samples/django-notes/requirements.txt +1 -0
- package/samples/django-notes/tracegrove/notes/crud.flow.yaml +27 -0
- package/samples/django-notes/tracegrove.config.yaml +7 -0
- package/samples/fastapi-notes/app/__init__.py +0 -0
- package/samples/fastapi-notes/app/main.py +69 -0
- package/samples/fastapi-notes/requirements.txt +2 -0
- package/samples/fastapi-notes/tracegrove/notes/crud.flow.yaml +27 -0
- package/samples/fastapi-notes/tracegrove.config.yaml +8 -0
- package/samples/notes-api/app/app.mjs +67 -0
- package/samples/notes-api/app/http.mjs +16 -0
- package/samples/notes-api/tracegrove/fixtures/existing-note.fixture.yaml +12 -0
- package/samples/notes-api/tracegrove/notes/create-read-delete.flow.yaml +35 -0
- package/samples/notes-api/tracegrove/notes/list.flow.yaml +25 -0
- package/samples/notes-api/tracegrove.config.yaml +15 -0
- package/samples/notes-api/verification/actions/notes.registry.yaml +6 -0
- package/samples/notes-api/verification/auth/host-issued-owner.yaml +6 -0
- package/samples/notes-api/verification/cases/notes.contract.yaml +51 -0
- package/samples/notes-api/verification/cases/notes.create.yaml +123 -0
- package/samples/notes-api/verification/cases/notes.suite.yaml +5 -0
- package/samples/notes-api/verification/components/notes-api-stub.profile.yaml +8 -0
- package/samples/notes-api/verification/components/notes-api.profile.yaml +15 -0
- package/samples/notes-api/verification/environments/local-notes.yaml +11 -0
- package/samples/notes-api/verification/environments/mock-notes.yaml +11 -0
- package/samples/notes-api/verification/fidelity/full-live-sandbox.yaml +7 -0
- package/samples/notes-api/verification/fidelity/mock-contract.yaml +6 -0
- package/samples/notes-api/verification/fixtures/existing-note.payload.yaml +15 -0
- package/samples/notes-api/verification/fixtures/existing-note.plan.yaml +9 -0
- package/samples/notes-api/verification/fixtures/existing-note.recipe.yaml +29 -0
- package/samples/notes-api/verification/hosts/local-process.yaml +27 -0
- package/samples/notes-api/verification/hosts/mock-loopback.yaml +14 -0
- package/samples/notes-api/verification/mocks/notes.responses.yaml +5 -0
- package/samples/notes-api/verification/personas/owner.yaml +16 -0
- package/samples/notes-api/verification/policies/no-authoring-inference.yaml +10 -0
- package/samples/notes-api/verification/runners/http.yaml +5 -0
- package/samples/notes-api/verification/surfaces/http-api.binding.yaml +11 -0
- package/samples/notes-api/verification/surfaces/http-api.pack.yaml +7 -0
- package/samples/notes-api/verification/workflow.yaml +27 -0
- package/samples/notes-browser/tracegrove/notes/browser.flow.yaml +19 -0
- package/samples/notes-browser/verification/cases/notes.browser.yaml +63 -0
- package/samples/notes-browser/verification/cases/notes.suite.yaml +4 -0
- package/samples/notes-browser/verification/runners/http.yaml +5 -0
- package/samples/notes-browser/verification/surfaces/http-api.binding.yaml +11 -0
- package/samples/notes-browser/verification/surfaces/http-api.pack.yaml +7 -0
- package/samples/notes-django/README.md +21 -0
- package/samples/notes-django/app/django_app.py +119 -0
- package/samples/notes-django/app/requirements.lock +4 -0
- package/samples/notes-mcp/app/mcp.mjs +47 -0
- package/samples/notes-mcp/tracegrove/notes.flow.yaml +20 -0
- package/samples/notes-mcp/tracegrove.config.yaml +13 -0
- package/samples/report-jobs/README.md +30 -0
- package/samples/report-jobs/app/jobs-api.mjs +65 -0
- package/samples/report-jobs/app/native-bridge.mjs +30 -0
- package/samples/report-jobs/app/store.mjs +74 -0
- package/samples/report-jobs/app/worker.mjs +22 -0
- package/samples/report-jobs/tracegrove/fixtures/failed-job.fixture.yaml +12 -0
- package/samples/report-jobs/tracegrove/fixtures/held-job.fixture.yaml +12 -0
- package/samples/report-jobs/tracegrove/fixtures/success-job.fixture.yaml +12 -0
- package/samples/report-jobs/tracegrove/reports/cancellation.flow.yaml +15 -0
- package/samples/report-jobs/tracegrove/reports/completion.flow.yaml +31 -0
- package/samples/report-jobs/tracegrove/reports/failure.flow.yaml +12 -0
- package/samples/report-jobs/tracegrove.config.yaml +10 -0
- package/samples/report-native/README.md +11 -0
- package/samples/report-native/tracegrove/cancellation.flow.yaml +11 -0
- package/samples/report-native/tracegrove/completion.flow.yaml +22 -0
- package/samples/report-native/tracegrove/failure.flow.yaml +7 -0
- package/samples/report-native/tracegrove.config.yaml +11 -0
- package/schema/tracegrove_cli-error_v1.json +156 -0
- package/schema/tracegrove_config_v1.json +233 -0
- package/schema/tracegrove_fixture_v1.json +297 -0
- package/schema/tracegrove_flow_v1.json +960 -0
- package/schema/tracegrove_recovery-status_v1.json +234 -0
- package/schema/tracegrove_run-video_v1.json +25 -0
- package/schema/workflow_action-registry_v1.json +303 -0
- package/schema/workflow_auth-recipe_v1.json +146 -0
- package/schema/workflow_boundary-observation_v1.json +515 -0
- package/schema/workflow_browser-command_v1.json +292 -0
- package/schema/workflow_browser-locator_v1.json +74 -0
- package/schema/workflow_browser-network-observation_v1.json +48 -0
- package/schema/workflow_browser-observation_v1.json +53 -0
- package/schema/workflow_budget-reservation_v1.json +150 -0
- package/schema/workflow_campaign-result_v1.json +382 -0
- package/schema/workflow_capability-artifact-manifest_v1.json +529 -0
- package/schema/workflow_capability-descriptor_v1.json +351 -0
- package/schema/workflow_capability-lifecycle-checkpoint_v1.json +510 -0
- package/schema/workflow_capability-observation_v1.json +1707 -0
- package/schema/workflow_capability-operation_v1.json +3212 -0
- package/schema/workflow_capability-replay-plan_v1.json +1247 -0
- package/schema/workflow_capability-retry-policy_v1.json +39 -0
- package/schema/workflow_capability-retry-state_v1.json +242 -0
- package/schema/workflow_case_v1.json +4222 -0
- package/schema/workflow_change-model_v1.json +614 -0
- package/schema/workflow_cleanup-expectation_v1.json +108 -0
- package/schema/workflow_cleanup-followup_v1.json +145 -0
- package/schema/workflow_component-profile_v1.json +219 -0
- package/schema/workflow_config_v1.json +180 -0
- package/schema/workflow_corpus_v1.json +172 -0
- package/schema/workflow_cost-observation_v1.json +366 -0
- package/schema/workflow_credential-lease_v1.json +222 -0
- package/schema/workflow_effective-config_v1.json +214 -0
- package/schema/workflow_environment-plan_v1.json +445 -0
- package/schema/workflow_environment_v1.json +1827 -0
- package/schema/workflow_evidence-projection_v1.json +636 -0
- package/schema/workflow_execution_v1.json +4086 -0
- package/schema/workflow_fidelity-plan_v1.json +512 -0
- package/schema/workflow_fidelity-profile_v1.json +396 -0
- package/schema/workflow_fixture-manifest_v1.json +918 -0
- package/schema/workflow_fixture-plan_v1.json +594 -0
- package/schema/workflow_git-source-request_v1.json +124 -0
- package/schema/workflow_git-source-resolution_v1.json +660 -0
- package/schema/workflow_git-source-snapshot_v1.json +287 -0
- package/schema/workflow_host-lease_v1.json +321 -0
- package/schema/workflow_host-observation_v1.json +282 -0
- package/schema/workflow_host-plan_v1.json +719 -0
- package/schema/workflow_identity-attestation_v1.json +412 -0
- package/schema/workflow_intelligence-policy_v1.json +153 -0
- package/schema/workflow_logical-payload_v1.json +206 -0
- package/schema/workflow_mcp-tool-call_v1.json +70 -0
- package/schema/workflow_mcp-tool-observation_v1.json +125 -0
- package/schema/workflow_mock-responses_v1.json +185 -0
- package/schema/workflow_native-job-action_v1.json +123 -0
- package/schema/workflow_native-job-observation_v1.json +138 -0
- package/schema/workflow_native-job-ready_v1.json +31 -0
- package/schema/workflow_native-job-request_v1.json +155 -0
- package/schema/workflow_native-job-response_v1.json +125 -0
- package/schema/workflow_ownership_v1.json +116 -0
- package/schema/workflow_persona_v1.json +499 -0
- package/schema/workflow_plan-edit-projection_v1.json +152 -0
- package/schema/workflow_plan-review_v1.json +246 -0
- package/schema/workflow_portable-environment-intent_v1.json +499 -0
- package/schema/workflow_portable-environment-profile_v1.json +1429 -0
- package/schema/workflow_portable-preflight_v1.json +2281 -0
- package/schema/workflow_qualification-admission_v1.json +467 -0
- package/schema/workflow_qualification-intent_v1.json +98 -0
- package/schema/workflow_qualification-policy_v1.json +109 -0
- package/schema/workflow_qualification-round_v1.json +226 -0
- package/schema/workflow_replay-assessment_v1.json +197 -0
- package/schema/workflow_replay-finding_v1.json +147 -0
- package/schema/workflow_request_v1.json +1567 -0
- package/schema/workflow_result-ingest_v1.json +343 -0
- package/schema/workflow_revisions_v1.json +334 -0
- package/schema/workflow_routing-decision_v1.json +482 -0
- package/schema/workflow_runner-capabilities_v1.json +316 -0
- package/schema/workflow_runner-profile_v1.json +119 -0
- package/schema/workflow_runner-protocol_v1_host.json +382 -0
- package/schema/workflow_runner-protocol_v1_plugin.json +491 -0
- package/schema/workflow_scenario-activation-intent_v1.json +346 -0
- package/schema/workflow_scenario-activation_v1.json +562 -0
- package/schema/workflow_scenario-projection_v1.json +221 -0
- package/schema/workflow_scenario-promotion_v1.json +455 -0
- package/schema/workflow_scenario-replay-assessment_v1.json +696 -0
- package/schema/workflow_scenario-revision-token_v1.json +115 -0
- package/schema/workflow_scenario-structure_v1.json +368 -0
- package/schema/workflow_scenario_v1.json +401 -0
- package/schema/workflow_scoped-resource_v1.json +550 -0
- package/schema/workflow_seed-recipe_v1.json +724 -0
- package/schema/workflow_selection_v1.json +1108 -0
- package/schema/workflow_signed-cleanup-followup_v1.json +175 -0
- package/schema/workflow_source-plan_v1.json +756 -0
- package/schema/workflow_source-selection_v1.json +456 -0
- package/schema/workflow_source-validation_v1.json +366 -0
- package/schema/workflow_step-event_v1.json +228 -0
- package/schema/workflow_stream-transcript_v1.json +1030 -0
- package/schema/workflow_suite_v1.json +64 -0
- package/schema/workflow_surface-binding_v1.json +411 -0
- package/schema/workflow_surface-pack_v1.json +159 -0
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trusted executor: admits a frozen request, prepares each selected cell on its host, applies
|
|
3
|
+
* fixtures through registered actions, runs compiled steps through the surface driver, collects
|
|
4
|
+
* boundary/host/identity observations and artifacts, cleans owned resources and seals a receipt.
|
|
5
|
+
* The reducer derives every verdict; drivers and hosts only supply observations.
|
|
6
|
+
*/
|
|
7
|
+
import { lstatSync, mkdirSync } from 'node:fs';
|
|
8
|
+
import { join, resolve } from 'node:path';
|
|
9
|
+
import { ActionRegistry, AuthRecipe, BoundaryObservation, BudgetReservation, CampaignFixturePlan, CaseFixturePlan, CaseSpec, CostObservation, CredentialLease, EnvironmentBinding, EnvironmentPlan, ExecutionReceipt, FidelityPlan, FixtureManifest, HostPlan, IntelligencePolicy, LogicalPayload, MockResponses, NativeJobAction, McpToolCall, Persona, RoutingDecision, SeedRecipe, SelectionManifest, SurfaceBinding, digestOfValue, freeze, parseAs, refKey, sameDigest, selfDigest, verifySelfDigest, } from "../protocol/index.js";
|
|
10
|
+
import { HttpDriver, HttpStepError, bearerFor, captureValues, redactObservation } from "../adapters/http/driver.js";
|
|
11
|
+
import { NativeJobError } from "../adapters/job/driver.js";
|
|
12
|
+
import { BrowserStepError } from "../adapters/browser/driver.js";
|
|
13
|
+
import { CaseBrowserSession } from "../adapters/browser/session.js";
|
|
14
|
+
import { LocalProcessHost } from "../hosts/local.js";
|
|
15
|
+
import { MockLoopbackHost } from "../hosts/mock.js";
|
|
16
|
+
import { LocalArtifactStore } from "./artifacts.js";
|
|
17
|
+
import { collectCleanup } from "./cleanup.js";
|
|
18
|
+
import { readVerifiedBundle, writeExecutionBundle } from "./bundle.js";
|
|
19
|
+
import { checkCommittedExecutionBinding } from "./executionReadback.js";
|
|
20
|
+
import { requireRealDirectory, syncDirectory } from "./atomicFiles.js";
|
|
21
|
+
import { isoPlusMs, monotonicMs, newId, nowIso } from "./clock.js";
|
|
22
|
+
import { applyTemplateDeep } from "./compiler.js";
|
|
23
|
+
import { Deadline } from "./deadline.js";
|
|
24
|
+
import { Blocked, Conflict, InfrastructureFailure, isInfrastructureStepFailure } from "./errors.js";
|
|
25
|
+
import { EventLog } from "./events.js";
|
|
26
|
+
import { ExecutionOwnership, assertInterruptedOwnershipResolved } from "./executionOwnership.js";
|
|
27
|
+
import { checkedFrozenRequest, prepareFrozenCells } from "./executionPreparation.js";
|
|
28
|
+
import { REDUCER_REF, compareTargets, evaluateAssertions, evaluatePredicate, reduceCampaign, reduceCell } from "./reducer.js";
|
|
29
|
+
import { targetFromComponentAttestations } from "./targetObservation.js";
|
|
30
|
+
import { authorizeQualificationExecution } from "./qualificationExecution.js";
|
|
31
|
+
export const EXIT_CODES = { passed: 0, assertion_failed: 1, blocked: 2, inconclusive: 2, infrastructure_failed: 3, cancelled: 4 };
|
|
32
|
+
function ownerFor(scope) {
|
|
33
|
+
return scope.ownerId;
|
|
34
|
+
}
|
|
35
|
+
/** Readback reconciles a local commit; it never acquires resources or trusts cached PASS/summary. */
|
|
36
|
+
function committedRunSummary(input) {
|
|
37
|
+
const { executionId, attemptId, generation, attemptNumber, request, selection, scope, ledger } = input;
|
|
38
|
+
if (!/^[A-Za-z0-9_-]+$/.test(executionId) || !Number.isInteger(generation) || generation < 1 || !Number.isInteger(attemptNumber) || attemptNumber < 1)
|
|
39
|
+
throw new Conflict('committed_execution_binding_invalid');
|
|
40
|
+
const executionDir = join(input.stateDir, 'executions', executionId);
|
|
41
|
+
let checked;
|
|
42
|
+
try {
|
|
43
|
+
requireRealDirectory(executionDir);
|
|
44
|
+
checked = readVerifiedBundle(executionDir);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
throw new Conflict('committed_execution_evidence_invalid; preserve the existing execution and reconcile its missing or corrupt evidence');
|
|
48
|
+
}
|
|
49
|
+
checkCommittedExecutionBinding(ledger, checked, { executionId, attemptId, generation, attemptNumber, scope, campaignId: request.campaignId,
|
|
50
|
+
requestDigest: request.ref.digest.hex, requestRef: request.ref, selectionRef: selection.ref });
|
|
51
|
+
return { executionId, attemptId, generation, campaignId: request.campaignId, outcome: checked.campaign.outcome,
|
|
52
|
+
exitCode: EXIT_CODES[checked.campaign.outcome], reasonCodes: [...new Set(checked.receipts.flatMap(receipt => receipt.reasonCodes))],
|
|
53
|
+
receipts: checked.receipts, campaign: checked.campaign, executionDir, redelivered: true };
|
|
54
|
+
}
|
|
55
|
+
function hasCommittedManifest(executionDir) {
|
|
56
|
+
try {
|
|
57
|
+
lstatSync(join(executionDir, 'manifest.json'));
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
if (error.code === 'ENOENT')
|
|
62
|
+
return false;
|
|
63
|
+
throw new Conflict('committed_execution_evidence_unreadable');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export async function executeBundle(options) {
|
|
67
|
+
const { bundle, stateDir, ledger, scope } = options;
|
|
68
|
+
const signal = options.signal ?? new AbortController().signal;
|
|
69
|
+
const request = checkedFrozenRequest(bundle, scope);
|
|
70
|
+
if ((request.purpose === 'qualification' || request.purpose === 'regression' || request.sourcePlanRef || request.reviewRef || request.scenarioRefs.length || request.qualificationRoundRef) &&
|
|
71
|
+
!(request.purpose === 'qualification' && options.qualification)) {
|
|
72
|
+
throw new Blocked([{ code: 'scenario_lifecycle_unavailable', detail: 'qualification requires an authenticated runtime owner; source trials and activated regression admission are unavailable' }]);
|
|
73
|
+
}
|
|
74
|
+
const { docs, selection, fidelityPlan, campaignFixturePlan, intelligencePolicy, prepared } = prepareFrozenCells(bundle, scope, request);
|
|
75
|
+
// Idempotent admission: the same frozen key reconciles the existing execution.
|
|
76
|
+
const qualification = options.qualification && request.purpose === 'qualification'
|
|
77
|
+
? await authorizeQualificationExecution(options.qualification, request, selection, ledger, null) : null;
|
|
78
|
+
if (qualification && resolve(join(stateDir, 'ledger')) !== resolve(ledger.dir))
|
|
79
|
+
throw new Conflict('qualification state directory must belong to the registration owner ledger');
|
|
80
|
+
const idemAggregate = qualification ? `qualification-execution:${qualification.admission.ref.digest.hex}` : `idempotency:${request.idempotencyKey}`;
|
|
81
|
+
const existing = ledger.current(idemAggregate);
|
|
82
|
+
let predecessor = null;
|
|
83
|
+
let attemptNumber = 1;
|
|
84
|
+
if (existing) {
|
|
85
|
+
const admissionHistory = qualification ? ledger.history(idemAggregate) : ledger.history(`execution:${String(existing.data.executionId)}`);
|
|
86
|
+
const admitted = admissionHistory.find((entry) => entry.type === 'admitted');
|
|
87
|
+
const executionId = qualification ? String(admitted?.data.executionId) : String(existing.data.executionId);
|
|
88
|
+
if (admitted?.data.requestDigest !== request.ref.digest.hex)
|
|
89
|
+
throw new Conflict('idempotency key is already bound to a different frozen request');
|
|
90
|
+
if (qualification && admitted?.data.stateDir !== resolve(stateDir))
|
|
91
|
+
throw new Conflict('qualification execution is already bound to another state directory');
|
|
92
|
+
const state = qualification ? existing : ledger.current(`execution:${executionId}`);
|
|
93
|
+
const binding = { executionId, attemptId: String(admitted?.data.attemptId), generation: Number(admitted?.data.generation),
|
|
94
|
+
campaignId: request.campaignId, requestDigest: request.ref.digest.hex, scope };
|
|
95
|
+
const checkedSummary = () => committedRunSummary({ stateDir, ledger, request, selection, scope, executionId,
|
|
96
|
+
attemptId: binding.attemptId, generation: binding.generation, attemptNumber: Number(admitted?.data.attemptNumber) });
|
|
97
|
+
if (qualification) {
|
|
98
|
+
// Qualification rounds bind per-cell allocations on the registration owner's ledger; they never replay or self-recover.
|
|
99
|
+
if (state && state.type === 'finalized')
|
|
100
|
+
return { ...state.data.summary, redelivered: true };
|
|
101
|
+
throw new Conflict('qualification attempt is incomplete; reconcile it instead of replaying allocated effects');
|
|
102
|
+
}
|
|
103
|
+
if (state && state.type === 'finalized') {
|
|
104
|
+
return checkedSummary();
|
|
105
|
+
}
|
|
106
|
+
if (!state || state.type !== 'interrupted')
|
|
107
|
+
throw new Conflict(`request ${request.idempotencyKey} is already in flight as ${executionId} (${state?.type ?? 'missing execution state'}); redelivery reconciles, it does not start another attempt`);
|
|
108
|
+
assertInterruptedOwnershipResolved(ledger, binding);
|
|
109
|
+
if (hasCommittedManifest(join(stateDir, 'executions', executionId))) {
|
|
110
|
+
const recovered = checkedSummary();
|
|
111
|
+
// A crashed publisher may have linked the valid manifest before syncing its directory.
|
|
112
|
+
try {
|
|
113
|
+
syncDirectory(recovered.executionDir);
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
throw new Conflict('committed_execution_durability_unconfirmed; preserve the committed bytes; no acquisition was replayed');
|
|
117
|
+
}
|
|
118
|
+
ledger.append(`execution:${executionId}`, state.version, 'finalized', { outcome: recovered.outcome, campaignResultRef: recovered.campaign.ref,
|
|
119
|
+
receiptRef: recovered.receipts[0]?.ref ?? null, recoveredFromCommittedBundle: true, summary: { ...recovered, receipts: [], redelivered: false } });
|
|
120
|
+
return recovered;
|
|
121
|
+
}
|
|
122
|
+
predecessor = state?.data.receiptRef ?? null;
|
|
123
|
+
attemptNumber = Number(existing.data.attemptNumber ?? 1) + 1;
|
|
124
|
+
}
|
|
125
|
+
if (request.limits.costCeilingCents < bundle.estimatedCostCents)
|
|
126
|
+
throw new Blocked([{ code: 'insufficient_budget_headroom', detail: `estimate ${bundle.estimatedCostCents} > ceiling ${request.limits.costCeilingCents}` }]);
|
|
127
|
+
const executionId = qualification?.admission.attempts[0].executionId ?? newId('exec');
|
|
128
|
+
const attemptId = qualification?.admission.attempts[0].attemptId ?? newId('attempt');
|
|
129
|
+
if (qualification && ledger.current(`execution:${executionId}`))
|
|
130
|
+
throw new Conflict('qualification allocation was already claimed; reconcile its execution');
|
|
131
|
+
const generation = 1;
|
|
132
|
+
const executionDir = join(stateDir, 'executions', executionId);
|
|
133
|
+
mkdirSync(join(stateDir, 'executions'), { recursive: true });
|
|
134
|
+
mkdirSync(executionDir, { recursive: !qualification });
|
|
135
|
+
const executionAggregate = qualification ? idemAggregate : `execution:${executionId}`;
|
|
136
|
+
const reservation = parseAs(BudgetReservation, freeze({
|
|
137
|
+
schemaVersion: 'workflow.budget-reservation/v1', ref: { id: `${executionId}.reservation`, revision: '1' }, scope,
|
|
138
|
+
campaignId: request.campaignId, executionId, attemptId, actionCents: request.limits.costCeilingCents, cleanupCents: 0, uncertainCents: 0, state: 'reserved', reservedAt: nowIso(),
|
|
139
|
+
}), 'budget reservation');
|
|
140
|
+
ledger.append(executionAggregate, 0, 'admitted', { requestDigest: request.ref.digest.hex, idempotencyKey: request.idempotencyKey, executionId, attemptId, generation, attemptNumber, reservation: reservation.ref,
|
|
141
|
+
...(qualification ? { stateDir: resolve(stateDir), qualificationRoundRef: qualification.admission.ref } : {}) });
|
|
142
|
+
if (!qualification)
|
|
143
|
+
ledger.append(idemAggregate, existing ? existing.version : 0, 'bound', { executionId, attemptNumber });
|
|
144
|
+
const ownership = new ExecutionOwnership(ledger, { executionId, attemptId, generation, campaignId: request.campaignId, requestDigest: request.ref.digest.hex, scope });
|
|
145
|
+
ownership.initialize();
|
|
146
|
+
const artifacts = new LocalArtifactStore(join(stateDir, 'artifacts'));
|
|
147
|
+
const campaignDeadline = Deadline.in(request.limits.deadlineMs);
|
|
148
|
+
const receipts = [];
|
|
149
|
+
const hosts = { local: options.hosts?.local ?? new LocalProcessHost(), mock: options.hosts?.mock ?? new MockLoopbackHost() };
|
|
150
|
+
let phaseVersion = ledger.version(executionAggregate);
|
|
151
|
+
for (const item of prepared) {
|
|
152
|
+
const allocation = qualification?.admission.attempts.find(a => a.selectionCellId === item.cell.selectionCellId);
|
|
153
|
+
const beforeEffect = qualification && options.qualification ? async () => {
|
|
154
|
+
try {
|
|
155
|
+
await authorizeQualificationExecution(options.qualification, request, selection, ledger, item.cell.selectionCellId);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
throw new HttpStepError('qualification_authority_unavailable', 'Qualification registration, source, state or effect authority is no longer current');
|
|
159
|
+
}
|
|
160
|
+
} : undefined;
|
|
161
|
+
const receipt = await runCell({ item, request, selection, fidelityPlan, campaignFixturePlan, intelligencePolicy, docs, scope,
|
|
162
|
+
executionId: allocation?.executionId ?? executionId, attemptId: allocation?.attemptId ?? attemptId,
|
|
163
|
+
generation, attemptNumber, predecessor, reservation, campaignDeadline, signal, artifacts, ownership, executionDir, hosts, bundle, onEvent: options.onEvent, beforeEffect });
|
|
164
|
+
receipts.push(receipt);
|
|
165
|
+
phaseVersion = ledger.append(executionAggregate, phaseVersion, 'cell.finalized', { selectionCellId: item.cell.selectionCellId, outcome: receipt.outcome, receiptRef: receipt.ref }).version;
|
|
166
|
+
}
|
|
167
|
+
const campaign = reduceCampaign({ scope, campaignId: request.campaignId, requestRef: request.ref, selection, receipts });
|
|
168
|
+
writeExecutionBundle({ dir: executionDir, bundle, request, selection, receipts, campaign });
|
|
169
|
+
const summary = {
|
|
170
|
+
executionId, attemptId, generation, campaignId: request.campaignId, outcome: campaign.outcome, exitCode: EXIT_CODES[campaign.outcome],
|
|
171
|
+
reasonCodes: [...new Set(receipts.flatMap((r) => r.reasonCodes))], receipts, campaign, executionDir, redelivered: false,
|
|
172
|
+
};
|
|
173
|
+
ledger.append(executionAggregate, phaseVersion, 'finalized', { outcome: campaign.outcome, campaignResultRef: campaign.ref, receiptRef: receipts[0]?.ref ?? null, summary: { ...summary, receipts: [], campaign } });
|
|
174
|
+
return summary;
|
|
175
|
+
}
|
|
176
|
+
async function runCell(ctx) {
|
|
177
|
+
const { item, request, scope, executionId, attemptId, generation, signal } = ctx;
|
|
178
|
+
const { spec, compiled, cell, hostPlan, environmentPlan, authRecipe, fidelityCell } = item;
|
|
179
|
+
const log = new EventLog(executionId, attemptId, generation, (event) => ctx.onEvent?.({ kind: event.kind, stepId: event.stepId, redactedPayload: event.redactedPayload, at: event.at }));
|
|
180
|
+
const startedAt = nowIso();
|
|
181
|
+
const cellDeadline = ctx.campaignDeadline.child(spec.limits.deadlineMs);
|
|
182
|
+
const blockedReasons = [];
|
|
183
|
+
const stepObservations = [];
|
|
184
|
+
let session = null;
|
|
185
|
+
let fixtureManifest = null;
|
|
186
|
+
let fixtureState = item.fixture ? 'unknown' : 'not_required';
|
|
187
|
+
let infrastructureFailure = false;
|
|
188
|
+
let cancelled = false;
|
|
189
|
+
const allocated = [];
|
|
190
|
+
const uncertainAllocations = new Set();
|
|
191
|
+
const fixtureOutputs = {};
|
|
192
|
+
const costs = [];
|
|
193
|
+
const hostStartedMs = monotonicMs();
|
|
194
|
+
log.emit('cell.started', null, { selectionCellId: cell.selectionCellId, caseId: spec.ref.id, caseDigest: spec.ref.digest.hex, lane: fidelityCell.lane });
|
|
195
|
+
const credentialNames = [...new Set([...(authRecipe.credentialName ? [authRecipe.credentialName] : []), ...compiled.credentialNames])];
|
|
196
|
+
const workspaceDir = join(ctx.executionDir, 'host', cell.selectionCellId.replace(/[^A-Za-z0-9._-]/g, '_'));
|
|
197
|
+
const hostOwnershipKey = `host:${cell.selectionCellId}`;
|
|
198
|
+
ctx.ownership.reserve(hostOwnershipKey, 'host', cell.selectionCellId, hostPlan.ref);
|
|
199
|
+
mkdirSync(workspaceDir, { recursive: true });
|
|
200
|
+
const host = hostPlan.adapter === 'mock_loopback' ? ctx.hosts.mock : ctx.hosts.local;
|
|
201
|
+
const mockResponses = hostPlan.mockResponsesRef ? ctx.docs.get(MockResponses, hostPlan.mockResponsesRef, 'mock responses') : null;
|
|
202
|
+
try {
|
|
203
|
+
if (ctx.beforeEffect)
|
|
204
|
+
await ctx.beforeEffect();
|
|
205
|
+
session = await host.start({ hostPlan, projectRoot: ctx.bundle.projectRoot, workspaceDir, scope, campaignId: request.campaignId, executionId, attemptId, generation, actorId: ownerFor(scope), credentialNames, signal, mockResponses });
|
|
206
|
+
ctx.ownership.observed(hostOwnershipKey, session.ownedResources.map(r => r.physicalId));
|
|
207
|
+
log.emit('host.started', null, { adapter: hostPlan.adapter, origin: session.origin, leaseRef: session.lease.ref, observationRef: session.observation.ref, attestations: session.attestations.map((a) => a.ref) });
|
|
208
|
+
}
|
|
209
|
+
catch (error) {
|
|
210
|
+
if (error instanceof Blocked)
|
|
211
|
+
blockedReasons.push(...error.reasons.map((r) => r.code));
|
|
212
|
+
else {
|
|
213
|
+
infrastructureFailure = true;
|
|
214
|
+
log.emit('host.failed', null, { error: String(error.message) });
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
// Host capability check: required capabilities need a probed, supported, current observation.
|
|
218
|
+
let requiredCapabilitiesObserved = false;
|
|
219
|
+
if (session) {
|
|
220
|
+
const supported = new Set(session.observation.capabilities.filter((c) => c.state === 'supported' && c.probe === 'probed').map((c) => c.capability));
|
|
221
|
+
const missing = hostPlan.requiredCapabilities.filter((c) => !supported.has(c));
|
|
222
|
+
requiredCapabilitiesObserved = missing.length === 0;
|
|
223
|
+
if (!requiredCapabilitiesObserved)
|
|
224
|
+
blockedReasons.push(`host_capability_unobserved:${missing.join('/')}`);
|
|
225
|
+
}
|
|
226
|
+
// Identity: compare expected component identities with launcher attestations.
|
|
227
|
+
let observedTarget = null;
|
|
228
|
+
let identityMatched = null;
|
|
229
|
+
if (session) {
|
|
230
|
+
observedTarget = targetFromComponentAttestations(cell.expectedTarget, session.attestations, hostPlan.launch, scope);
|
|
231
|
+
const comparison = compareTargets(cell.expectedTarget, observedTarget);
|
|
232
|
+
identityMatched = comparison.matched;
|
|
233
|
+
log.emit('identity.compared', null, { matched: comparison.matched, mismatches: comparison.mismatches });
|
|
234
|
+
if (comparison.matched === false)
|
|
235
|
+
blockedReasons.push('identity_mismatch');
|
|
236
|
+
if (comparison.matched === null)
|
|
237
|
+
blockedReasons.push('required_identity_unknown');
|
|
238
|
+
}
|
|
239
|
+
const credentialLease = parseAs(CredentialLease, freeze({
|
|
240
|
+
schemaVersion: 'workflow.credential-lease/v1', ref: { id: `${executionId}.${cell.selectionCellId}.credential-lease`, revision: '1' }, scope,
|
|
241
|
+
authRecipeRef: authRecipe.ref, personaRef: item.persona.ref, executionId, attemptId, credentialName: authRecipe.credentialName,
|
|
242
|
+
issuedBy: session ? (hostPlan.adapter === 'mock_loopback' ? 'environment' : 'local_launcher') : 'none', expiresAt: isoPlusMs(hostPlan.sessionLifetimeMs),
|
|
243
|
+
}), 'credential lease');
|
|
244
|
+
const origins = session ? environmentPlan.origins.map((o) => (o.startsWith('launch://') ? session.origin : o)) : environmentPlan.origins;
|
|
245
|
+
const provenance = fidelityCell.lane === 'mock_contract'
|
|
246
|
+
? { runtime: 'mocked', data: 'mocked', provider: 'none', faults: 'none', interceptors: session?.interceptorRefs ?? [], injectedFaults: [] }
|
|
247
|
+
: { runtime: 'live', data: 'seeded_real', provider: 'none', faults: 'none', interceptors: [], injectedFaults: [] };
|
|
248
|
+
const environment = parseAs(EnvironmentBinding, freeze({
|
|
249
|
+
schemaVersion: 'workflow.environment/v1', ref: { id: `${executionId}.${cell.selectionCellId}.environment`, revision: '1' },
|
|
250
|
+
planRef: environmentPlan.ref, campaignId: request.campaignId, executionId, attemptId, generation, scope, class: environmentPlan.class, origins,
|
|
251
|
+
credentialLeaseRef: credentialLease.ref, actionEnvelopeRef: null, leaseExpiresAt: isoPlusMs(hostPlan.sessionLifetimeMs), budgetReservationRef: ctx.reservation.ref,
|
|
252
|
+
admissionEvidenceRefs: session ? [session.lease.ref, session.observation.ref, ...session.attestations.map((a) => a.ref)] : [],
|
|
253
|
+
expected: cell.expectedTarget, observed: observedTarget, provenance, networkProfileRef: null, clockProfileRef: null,
|
|
254
|
+
}), 'environment binding');
|
|
255
|
+
const driver = session ? new HttpDriver({ persona: authRecipe.credentialName ? session.credentials.get(authRecipe.credentialName) ?? null : null, named: Object.fromEntries(session.credentials) }, new Set([session.origin])) : null;
|
|
256
|
+
const origin = session?.origin ?? '';
|
|
257
|
+
const browser = new CaseBrowserSession({ origin, spec, persona: item.persona, bearer: authRecipe.credentialName ? session?.credentials.get(authRecipe.credentialName) ?? null : null,
|
|
258
|
+
onNetwork: observation => { log.emit('browser.network', null, observation); },
|
|
259
|
+
});
|
|
260
|
+
const runtimeVariables = { execution_nonce: attemptId, lease_namespace: cell.selectionCellId, target_origin: origin };
|
|
261
|
+
// ---- preparing: fixtures through registered actions with independent readback ----
|
|
262
|
+
if (item.fixture && session && driver && !infrastructureFailure && blockedReasons.length === 0) {
|
|
263
|
+
const { recipe, payload, registry } = item.fixture;
|
|
264
|
+
const order = topoOrder(recipe);
|
|
265
|
+
const manifestEntities = [];
|
|
266
|
+
const missing = [];
|
|
267
|
+
let failed = false;
|
|
268
|
+
for (const entityKey of order) {
|
|
269
|
+
if (failed) {
|
|
270
|
+
missing.push(entityKey);
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (signal.aborted) {
|
|
274
|
+
cancelled = true;
|
|
275
|
+
missing.push(entityKey);
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
const entity = recipe.entities.find((e) => e.entityKey === entityKey);
|
|
279
|
+
const logical = payload.entities.find((e) => e.entityKey === entityKey)?.values ?? {};
|
|
280
|
+
const variables = {};
|
|
281
|
+
for (const [name, value] of Object.entries(logical)) {
|
|
282
|
+
if (value && typeof value === 'object' && '$entity' in value) {
|
|
283
|
+
const v = value;
|
|
284
|
+
variables[name] = fixtureOutputs[v.$entity]?.[v.output];
|
|
285
|
+
}
|
|
286
|
+
else if (value && typeof value === 'object' && '$runtime' in value)
|
|
287
|
+
variables[name] = runtimeVariables[value.$runtime];
|
|
288
|
+
else
|
|
289
|
+
variables[name] = value;
|
|
290
|
+
}
|
|
291
|
+
const action = registry.actions.find((a) => a.actionId === entity.actionId);
|
|
292
|
+
const allocatingEvent = log.emit('fixture.allocating', null, { entityKey, actionId: action.actionId, effectKey: `${attemptId}:${entityKey}` });
|
|
293
|
+
try {
|
|
294
|
+
if (action.effect !== 'create')
|
|
295
|
+
throw new HttpStepError('fixture_ownership_unavailable', 'only a registered create action can allocate an owned fixture');
|
|
296
|
+
if (ctx.beforeEffect)
|
|
297
|
+
await ctx.beforeEffect();
|
|
298
|
+
ctx.ownership.reserve(`fixture:${cell.selectionCellId}:${entityKey}`, 'fixture', cell.selectionCellId, recipe.ref);
|
|
299
|
+
uncertainAllocations.add(entityKey);
|
|
300
|
+
const observation = await driver.request({ origin, method: action.method, path: action.path, body: action.body, auth: action.auth, variables, deadline: cellDeadline.child(10_000), signal });
|
|
301
|
+
if (action.expectStatus !== null && observation.status !== action.expectStatus)
|
|
302
|
+
throw new HttpStepError('unexpected_status', `${action.actionId} returned ${observation.status}, expected ${action.expectStatus}`);
|
|
303
|
+
const outputs = captureValues(observation, action.captures);
|
|
304
|
+
fixtureOutputs[entityKey] = { ...variables, ...outputs };
|
|
305
|
+
const observedId = outputs[entity.outputNames[0] ?? ''];
|
|
306
|
+
if (typeof observedId !== 'string' && !(typeof observedId === 'number' && Number.isFinite(observedId))) {
|
|
307
|
+
throw new HttpStepError('fixture_identity_unobserved', 'fixture allocation did not return a physical resource ID');
|
|
308
|
+
}
|
|
309
|
+
const parsedId = FixtureManifest.shape.entities.element.shape.physicalId.safeParse(String(observedId));
|
|
310
|
+
if (!parsedId.success)
|
|
311
|
+
throw new HttpStepError('fixture_identity_unobserved', 'captured fixture ID is invalid');
|
|
312
|
+
const physicalId = parsedId.data;
|
|
313
|
+
const cleanupHandle = entity.cleanup ? { actionId: entity.cleanup.actionId, args: { ...variables, ...outputs } } : null;
|
|
314
|
+
// Keep the observed cleanup handle in memory before any fallible acknowledgement/export callback.
|
|
315
|
+
allocated.push({ entityKey, physicalId, cleanup: cleanupHandle, absence: entity.cleanup ? entity.cleanup.absenceReadback : null });
|
|
316
|
+
ctx.ownership.observed(`fixture:${cell.selectionCellId}:${entityKey}`, [physicalId]);
|
|
317
|
+
const allocatedEvent = log.emit('fixture.allocated', null, { entityKey, physicalId, status: observation.status, requestId: observation.requestId, cleanupHandle }, { requestId: observation.requestId });
|
|
318
|
+
uncertainAllocations.delete(entityKey);
|
|
319
|
+
let readbackRefs = [];
|
|
320
|
+
if (entity.readback) {
|
|
321
|
+
const readAction = registry.actions.find((a) => a.actionId === entity.readback.actionId);
|
|
322
|
+
const readback = await driver.request({ origin, method: readAction.method, path: readAction.path, body: readAction.body, auth: readAction.auth, variables: { ...variables, ...outputs }, deadline: cellDeadline.child(10_000), signal });
|
|
323
|
+
const evaluation = evaluatePredicate(entity.readback.predicate, { status: readback.status, body: readback.body });
|
|
324
|
+
const readEvent = log.emit('fixture.readback', null, { entityKey, pass: evaluation.pass, reason: evaluation.reason, observation: redactObservation(readback) }, { requestId: readback.requestId });
|
|
325
|
+
readbackRefs = [EventLog.refOf(readEvent)];
|
|
326
|
+
if (!evaluation.pass)
|
|
327
|
+
throw new HttpStepError('readback_mismatch', `${entityKey}: ${evaluation.reason}`);
|
|
328
|
+
}
|
|
329
|
+
manifestEntities.push({ entityKey, boundaryId: 'application', physicalId, dataOrigin: 'generated_synthetic', cleanupOwnership: 'allocated_by_attempt', startingVersion: null, baselineRef: null, compensationStrategyRef: null, ownershipEvidenceRefs: [EventLog.refOf(allocatingEvent), EventLog.refOf(allocatedEvent)], readbackEvidenceRefs: readbackRefs, cleanupHandle });
|
|
330
|
+
}
|
|
331
|
+
catch (error) {
|
|
332
|
+
failed = true;
|
|
333
|
+
const code = error instanceof HttpStepError ? error.code : 'transport_failure';
|
|
334
|
+
log.emit('fixture.failed', null, { entityKey, code, message: String(error.message) });
|
|
335
|
+
missing.push(entityKey);
|
|
336
|
+
if (code === 'cancelled')
|
|
337
|
+
cancelled = true;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
fixtureState = cancelled ? 'cancelled' : failed ? 'failed' : 'verified';
|
|
341
|
+
fixtureManifest = parseAs(FixtureManifest, freeze({
|
|
342
|
+
schemaVersion: 'workflow.fixture-manifest/v1', ref: { id: `${executionId}.${cell.selectionCellId}.fixtures`, revision: '1' }, scope,
|
|
343
|
+
campaignId: request.campaignId, executionId, attemptId, selectionCellId: cell.selectionCellId,
|
|
344
|
+
campaignFixturePlanRef: ctx.campaignFixturePlan.ref, caseFixturePlanRef: item.fixture.plan.ref, seedRecipeRef: recipe.ref, logicalPayloadRef: payload.ref,
|
|
345
|
+
materializedPayloadDigest: digestOfValue(fixtureOutputs), observedSchemaIdentityRef: null, migrationDigest: null,
|
|
346
|
+
namespace: cell.selectionCellId, executionNonce: attemptId, resourceLeaseRefs: [], entities: manifestEntities,
|
|
347
|
+
outputBindings: fixtureOutputs, lifecycleObservationRefs: [], state: fixtureState,
|
|
348
|
+
missingEntityKeys: missing, unexpectedResourceRefs: [], deadlineAt: new Date(cellDeadline.at).toISOString(), finalizedAt: nowIso(),
|
|
349
|
+
}), 'fixture manifest');
|
|
350
|
+
log.emit('fixture.finalized', null, { state: fixtureState, manifestRef: fixtureManifest.ref, missing });
|
|
351
|
+
}
|
|
352
|
+
// ---- running: compiled steps in frozen order; every step gets one terminal observation ----
|
|
353
|
+
const captured = {};
|
|
354
|
+
let integrationOperations = 0;
|
|
355
|
+
let uncertainOperations = 0;
|
|
356
|
+
let halted = blockedReasons.length > 0 || infrastructureFailure || !session || !driver || (fixtureState !== 'verified' && fixtureState !== 'not_required');
|
|
357
|
+
for (const stepId of compiled.orderedStepIds) {
|
|
358
|
+
const step = compiled.stepsById.get(stepId);
|
|
359
|
+
const inputDigest = digestOfValue(step);
|
|
360
|
+
if (halted || cancelled || signal.aborted) {
|
|
361
|
+
if (signal.aborted && !cancelled)
|
|
362
|
+
cancelled = true;
|
|
363
|
+
stepObservations.push({ stepId, result: cancelled ? 'cancelled' : 'not_run', reasonCode: cancelled ? 'cancelled' : 'halted_by_prior_failure', eventIds: [log.emit('step.skipped', stepId, { reason: cancelled ? 'cancelled' : 'halted' }).eventId], evidenceRefs: [], inputDigest, outputDigest: null, output: null, timing: null });
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
const variables = {};
|
|
367
|
+
try {
|
|
368
|
+
for (const [name, source] of Object.entries(step.inputs)) {
|
|
369
|
+
switch (source.kind) {
|
|
370
|
+
case 'literal':
|
|
371
|
+
variables[name] = source.value;
|
|
372
|
+
break;
|
|
373
|
+
case 'capture':
|
|
374
|
+
variables[name] = captured[source.stepId]?.[source.name];
|
|
375
|
+
break;
|
|
376
|
+
case 'fixture':
|
|
377
|
+
variables[name] = fixtureOutputs[source.entityKey]?.[source.outputName];
|
|
378
|
+
break;
|
|
379
|
+
case 'credential':
|
|
380
|
+
variables[name] = '<credential>';
|
|
381
|
+
break; // never expanded into variables or logs
|
|
382
|
+
case 'runtime':
|
|
383
|
+
variables[name] = runtimeVariables[source.name];
|
|
384
|
+
break;
|
|
385
|
+
}
|
|
386
|
+
if (variables[name] === undefined)
|
|
387
|
+
throw new HttpStepError('unresolved_variable', `${stepId}.${name}`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
catch (error) {
|
|
391
|
+
stepObservations.push({ stepId, result: 'failed', reasonCode: error.code ?? 'unresolved_variable', eventIds: [log.emit('step.failed', stepId, { message: String(error.message) }).eventId], evidenceRefs: [], inputDigest, outputDigest: null, output: null, timing: null });
|
|
392
|
+
halted = true;
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
const stepStart = nowIso();
|
|
396
|
+
const stepStartedMs = monotonicMs();
|
|
397
|
+
const startEvent = log.emit('step.started', stepId, { kind: step.kind, variables: Object.fromEntries(Object.entries(variables).filter(([k]) => step.inputs[k]?.kind !== 'credential')) });
|
|
398
|
+
const deadline = cellDeadline.child(step.timeoutMs);
|
|
399
|
+
try {
|
|
400
|
+
let observation;
|
|
401
|
+
if (ctx.beforeEffect)
|
|
402
|
+
await ctx.beforeEffect();
|
|
403
|
+
if (step.kind === 'http.request') {
|
|
404
|
+
observation = await driver.request({ origin, method: step.method, path: step.path, headers: step.headers, body: step.body, auth: step.auth, variables, deadline, signal });
|
|
405
|
+
if (step.expectStatus !== null && observation.status !== step.expectStatus) {
|
|
406
|
+
const finished = log.emit('step.finished', stepId, { result: 'failed', reason: 'unexpected_status', observation: redactObservation(observation) }, { requestId: observation.requestId });
|
|
407
|
+
stepObservations.push({ stepId, result: 'failed', reasonCode: `unexpected_status:${observation.status}`, eventIds: [startEvent.eventId, finished.eventId], evidenceRefs: [EventLog.refOf(finished)], inputDigest, outputDigest: digestOfValue(redactObservation(observation)), output: redactObservation(observation), timing: { startedAt: stepStart, finishedAt: nowIso(), durationMs: observation.durationMs } });
|
|
408
|
+
halted = true;
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
captured[stepId] = captureValues(observation, step.captures);
|
|
412
|
+
}
|
|
413
|
+
else if (step.kind === 'observe') {
|
|
414
|
+
observation = await driver.observe({ origin, method: step.readback.method, path: step.readback.path, auth: step.readback.auth, variables, deadline, signal }, step.predicate, step.pollIntervalMs);
|
|
415
|
+
captured[stepId] = captureValues(observation, step.captures);
|
|
416
|
+
}
|
|
417
|
+
else if (step.kind === 'mcp.call') {
|
|
418
|
+
if (!spec.effects.allowedHosts.includes(`launch://${hostPlan.launch?.componentId}`))
|
|
419
|
+
throw new HttpStepError('mcp_origin_not_admitted', 'MCP application origin is outside the frozen effect envelope');
|
|
420
|
+
const command = McpToolCall.parse({ ...step.command, arguments: applyTemplateDeep(step.command.arguments, variables) });
|
|
421
|
+
const credential = bearerFor(step, { persona: authRecipe.credentialName ? session?.credentials.get(authRecipe.credentialName) ?? null : null, named: Object.fromEntries(session?.credentials ?? []) });
|
|
422
|
+
let module;
|
|
423
|
+
try {
|
|
424
|
+
module = await import("../adapters/mcp/driver.js");
|
|
425
|
+
}
|
|
426
|
+
catch {
|
|
427
|
+
throw new HttpStepError('mcp_driver_unavailable', 'Install the pinned optional MCP SDK before planning this flow');
|
|
428
|
+
}
|
|
429
|
+
integrationOperations++;
|
|
430
|
+
uncertainOperations++;
|
|
431
|
+
observation = await new module.McpDriver(origin).call(command, credential, deadline, signal);
|
|
432
|
+
if (!observation.isError) {
|
|
433
|
+
uncertainOperations--;
|
|
434
|
+
captured[stepId] = captureValues({ body: observation }, step.captures);
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
else if (step.kind === 'job.submit' || step.kind === 'job.observe' || step.kind === 'job.cancel') {
|
|
438
|
+
if (!session?.nativeJobs)
|
|
439
|
+
throw new NativeJobError('native_job_unavailable', 'The host did not establish a native job channel');
|
|
440
|
+
const command = NativeJobAction.parse(applyTemplateDeep(step.command, variables));
|
|
441
|
+
const credential = bearerFor(step, { persona: authRecipe.credentialName ? session.credentials.get(authRecipe.credentialName) ?? null : null, named: Object.fromEntries(session.credentials) });
|
|
442
|
+
observation = step.kind === 'job.observe' && command.action === 'read'
|
|
443
|
+
? await session.nativeJobs.observe(command, credential, step.predicate, step.pollIntervalMs, deadline, signal)
|
|
444
|
+
: await session.nativeJobs.request(command, credential, deadline, signal);
|
|
445
|
+
captured[stepId] = captureValues({ body: observation }, step.captures);
|
|
446
|
+
}
|
|
447
|
+
else if (step.kind === 'ui.navigate' || step.kind === 'ui.action' || step.kind === 'ui.observe') {
|
|
448
|
+
if (authRecipe.credentialName && !session?.credentials.get(authRecipe.credentialName))
|
|
449
|
+
throw new HttpStepError('missing_persona_credential', 'browser persona credential was not issued');
|
|
450
|
+
observation = await browser.execute(step, variables, deadline, signal);
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
throw new HttpStepError('unsupported_operation', `${step.kind} is not implemented by the HTTP runner`);
|
|
454
|
+
}
|
|
455
|
+
const output = 'requestId' in observation ? redactObservation(observation) : observation;
|
|
456
|
+
const toolError = 'kind' in observation && observation.kind === 'mcp_tool' && observation.isError;
|
|
457
|
+
const result = toolError ? 'failed' : 'completed';
|
|
458
|
+
const finished = log.emit('step.finished', stepId, { result, observation: output }, { requestId: 'requestId' in observation ? observation.requestId : null });
|
|
459
|
+
stepObservations.push({ stepId, result, reasonCode: toolError ? 'mcp_tool_error' : null, eventIds: [startEvent.eventId, finished.eventId], evidenceRefs: [EventLog.refOf(finished)], inputDigest, outputDigest: digestOfValue(output), output, timing: { startedAt: stepStart, finishedAt: nowIso(), durationMs: monotonicMs() - stepStartedMs } });
|
|
460
|
+
if (toolError)
|
|
461
|
+
halted = true;
|
|
462
|
+
}
|
|
463
|
+
catch (error) {
|
|
464
|
+
const rawCode = error instanceof HttpStepError || error instanceof BrowserStepError || error instanceof NativeJobError ? error.code : 'transport_failure';
|
|
465
|
+
const code = rawCode === 'browser_cancelled' ? 'cancelled' : rawCode;
|
|
466
|
+
const finished = log.emit('step.finished', stepId, { result: code === 'cancelled' ? 'cancelled' : 'failed', reason: code, message: String(error.message) });
|
|
467
|
+
if (code === 'cancelled' || code === 'browser_cancelled')
|
|
468
|
+
cancelled = true;
|
|
469
|
+
else if (isInfrastructureStepFailure(code))
|
|
470
|
+
infrastructureFailure = true;
|
|
471
|
+
stepObservations.push({ stepId, result: code === 'cancelled' ? 'cancelled' : 'failed', reasonCode: code, eventIds: [startEvent.eventId, finished.eventId], evidenceRefs: [EventLog.refOf(finished)], inputDigest, outputDigest: null, output: null, timing: { startedAt: stepStart, finishedAt: nowIso(), durationMs: 0 } });
|
|
472
|
+
halted = true;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
// ---- collecting: boundary observations ----
|
|
476
|
+
const boundaryObservations = fidelityCell.boundaries.map((rule) => {
|
|
477
|
+
let observedMode = 'unknown';
|
|
478
|
+
let accountClass = 'unknown';
|
|
479
|
+
if (session) {
|
|
480
|
+
if (rule.boundaryId === 'application') {
|
|
481
|
+
observedMode = session.boundaryMode;
|
|
482
|
+
accountClass = session.boundaryMode === 'real' ? 'local' : 'none';
|
|
483
|
+
}
|
|
484
|
+
else if (rule.boundaryId === 'storage') {
|
|
485
|
+
observedMode = session.dataDir ? 'real' : 'unknown';
|
|
486
|
+
accountClass = session.dataDir ? 'local' : 'unknown';
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
const mismatch = observedMode !== rule.mode ? [`expected ${rule.mode}, observed ${observedMode}`] : [];
|
|
490
|
+
return parseAs(BoundaryObservation, freeze({
|
|
491
|
+
schemaVersion: 'workflow.boundary-observation/v1', ref: { id: `${executionId}.${cell.selectionCellId}.boundary.${rule.boundaryId}`, revision: '1' }, scope,
|
|
492
|
+
campaignId: request.campaignId, executionId, attemptId, selectionCellId: cell.selectionCellId, fidelityPlanRef: ctx.fidelityPlan.ref,
|
|
493
|
+
boundaryId: rule.boundaryId, expectedMode: rule.mode, observedMode, observedAccountClass: accountClass, adapterRef: null,
|
|
494
|
+
identityEvidenceRefs: session?.attestations.map((a) => a.ref) ?? [], interactionEvidenceRefs: stepObservations.flatMap((s) => s.evidenceRefs).slice(0, 64),
|
|
495
|
+
resourceIdsRef: null, interceptorRefs: session?.interceptorRefs ?? [], faultRefs: [], observedAt: nowIso(), mismatchReasons: mismatch,
|
|
496
|
+
}), 'boundary observation');
|
|
497
|
+
});
|
|
498
|
+
const fidelityMatched = boundaryObservations.filter((b) => fidelityCell.claimBoundaryIds.includes(b.boundaryId)).every((b) => b.mismatchReasons.length === 0);
|
|
499
|
+
log.emit('boundaries.observed', null, { observations: boundaryObservations.map((b) => ({ boundaryId: b.boundaryId, expected: b.expectedMode, observed: b.observedMode, mismatches: b.mismatchReasons })) });
|
|
500
|
+
// Flush browser evidence before host cleanup; an unavailable recording cannot satisfy required capture.
|
|
501
|
+
const cleanupDeadline = Deadline.in(hostPlan.cleanupReserveMs);
|
|
502
|
+
const media = [];
|
|
503
|
+
const mediaArtifacts = [];
|
|
504
|
+
try {
|
|
505
|
+
const bytes = await browser.collectVideo(cleanupDeadline);
|
|
506
|
+
if (bytes) {
|
|
507
|
+
const artifact = ctx.artifacts.put({ id: `${executionId}.${cell.selectionCellId}.video`, bytes, mediaType: 'video/webm', producerRef: cell.expectedTarget.runner,
|
|
508
|
+
stepIds: compiled.orderedStepIds.filter(id => compiled.stepsById.get(id).kind.startsWith('ui.')), from: startedAt, to: nowIso() });
|
|
509
|
+
media.push({ digest: artifact.ref.digest.hex, bytes });
|
|
510
|
+
mediaArtifacts.push({ ...artifact, retentionPolicyRef: spec.capture.retentionPolicyRef });
|
|
511
|
+
log.emit('browser.video.collected', null, { ref: artifact.ref, byteCount: artifact.byteCount });
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
catch (error) {
|
|
515
|
+
// Preserve bounded driver facts without serializing arbitrary exception messages.
|
|
516
|
+
const cause = error instanceof BrowserStepError && ['video_final_frame_unavailable', 'video_empty', 'browser_network_metadata_limit'].includes(error.code) ? error.code : null;
|
|
517
|
+
const phase = error instanceof BrowserStepError && ['capture', 'frame', 'flush'].includes(error.phase ?? '') ? error.phase : null;
|
|
518
|
+
infrastructureFailure = true;
|
|
519
|
+
log.emit('browser.failed', null, { code: 'browser_evidence_collection_failed', cause, phase });
|
|
520
|
+
}
|
|
521
|
+
// ---- cleaning: owned fixtures in reverse order, then the host; a cleanup deadline is reserved ----
|
|
522
|
+
// Journal failure must never prevent best-effort cleanup of already acquired resources.
|
|
523
|
+
let ownershipWriteError;
|
|
524
|
+
try {
|
|
525
|
+
if (session)
|
|
526
|
+
ctx.ownership.cleaning(hostOwnershipKey);
|
|
527
|
+
for (const entry of allocated)
|
|
528
|
+
ctx.ownership.cleaning(`fixture:${cell.selectionCellId}:${entry.entityKey}`);
|
|
529
|
+
}
|
|
530
|
+
catch (error) {
|
|
531
|
+
ownershipWriteError = error;
|
|
532
|
+
}
|
|
533
|
+
const { cleanup, criticalCleanupConfirmed, confirmedFixtureKeys, hostCleanupConfirmed } = await collectCleanup({ allocated,
|
|
534
|
+
uncertainAllocations: uncertainAllocations.size, uncertainOperations, registry: item.fixture?.registry ?? null,
|
|
535
|
+
driver, origin, deadline: cleanupDeadline, session, reason: cancelled ? 'cancelled' : 'cell complete', log });
|
|
536
|
+
if (!ownershipWriteError) {
|
|
537
|
+
if (session && hostCleanupConfirmed)
|
|
538
|
+
ctx.ownership.removed(hostOwnershipKey);
|
|
539
|
+
for (const entry of allocated)
|
|
540
|
+
if (confirmedFixtureKeys.includes(entry.entityKey))
|
|
541
|
+
ctx.ownership.removed(`fixture:${cell.selectionCellId}:${entry.entityKey}`);
|
|
542
|
+
}
|
|
543
|
+
if (ownershipWriteError)
|
|
544
|
+
throw ownershipWriteError;
|
|
545
|
+
const hostSeconds = (monotonicMs() - hostStartedMs) / 1000;
|
|
546
|
+
// ---- sealing ----
|
|
547
|
+
const finishedAt = nowIso();
|
|
548
|
+
const sealedEvents = log.toJsonl();
|
|
549
|
+
const eventsArtifact = ctx.artifacts.put({ id: `${executionId}.${cell.selectionCellId}.events`, bytes: Buffer.from(sealedEvents, 'utf8'), mediaType: 'application/x-ndjson', producerRef: REDUCER_REF, stepIds: compiled.orderedStepIds, from: startedAt, to: finishedAt });
|
|
550
|
+
const allArtifacts = [eventsArtifact, ...mediaArtifacts];
|
|
551
|
+
const artifactsVerified = allArtifacts.every(artifact => ctx.artifacts.verify(artifact.ref.digest))
|
|
552
|
+
&& (spec.capture.video !== 'required' || mediaArtifacts.length > 0)
|
|
553
|
+
&& allArtifacts.reduce((sum, artifact) => sum + artifact.byteCount, 0) <= spec.limits.maxArtifactBytes;
|
|
554
|
+
const assertions = evaluateAssertions(spec, environmentPlan.class, stepObservations, REDUCER_REF, startedAt, finishedAt);
|
|
555
|
+
const reduction = reduceCell({ spec, expectedStepIds: compiled.orderedStepIds, steps: stepObservations, assertions, cancelled, fixtureState, identityMatched, fidelityMatched, requiredCapabilitiesObserved, artifactsVerified, unknownMutations: cleanup.remainingUnknownMutations, criticalCleanupConfirmed, infrastructureFailure, blockedReasons });
|
|
556
|
+
const routing = parseAs(RoutingDecision, freeze({
|
|
557
|
+
schemaVersion: 'workflow.routing-decision/v1', ref: { id: `${executionId}.${cell.selectionCellId}.routing`, revision: '1' }, scope,
|
|
558
|
+
owner: { kind: 'attempt', campaignId: request.campaignId, executionId, attemptId }, stage: 'replay', inputDigest: spec.ref.digest,
|
|
559
|
+
intelligencePolicyRef: ctx.intelligencePolicy.ref, catalogRef: null, candidates: [], selected: null,
|
|
560
|
+
estimate: { amountCents: 0, unknownReason: null }, budgetReservationRef: ctx.reservation.ref, escalationReason: 'known case replay performs no authoring inference', decidedAt: nowIso(),
|
|
561
|
+
}), 'routing decision');
|
|
562
|
+
costs.push(parseAs(CostObservation, freeze({
|
|
563
|
+
schemaVersion: 'workflow.cost-observation/v1', ref: { id: `${executionId}.${cell.selectionCellId}.cost.host`, revision: '1' }, scope,
|
|
564
|
+
owner: { kind: 'attempt', campaignId: request.campaignId, executionId, attemptId }, operationId: `${cell.selectionCellId}.host`, stage: 'execution',
|
|
565
|
+
billingRoute: 'host_minutes', providerRequestId: null, usageRef: null, rateSnapshotRef: null,
|
|
566
|
+
quantity: { value: Number(hostSeconds.toFixed(3)), unit: 'seconds' }, amount: { value: 0, currency: 'USD', kind: 'measured' }, reconciliation: 'reconciled', observedAt: nowIso(),
|
|
567
|
+
}), 'cost observation'));
|
|
568
|
+
const accounting = {
|
|
569
|
+
reservations: [{ reservationId: ctx.reservation.ref.id, kind: 'action', cents: ctx.reservation.actionCents }],
|
|
570
|
+
runnerSeconds: Number(hostSeconds.toFixed(3)), browserSeconds: Number(browser.seconds.toFixed(3)), inferenceCalls: 0, inferenceTokens: 0, requestedModel: null, observedModel: null,
|
|
571
|
+
providerRequestIds: stepObservations.flatMap((s) => (s.output && typeof s.output === 'object' && s.output.requestId ? [String(s.output.requestId)] : [])),
|
|
572
|
+
integrationOperations, artifactBytes: allArtifacts.reduce((sum, artifact) => sum + artifact.byteCount, 0), estimatedCents: 0, reconciledCents: 0, rateCardDigest: null, unknownCharges: [],
|
|
573
|
+
routingDecisionRefs: [routing.ref], costObservationRefs: costs.map((c) => c.ref),
|
|
574
|
+
};
|
|
575
|
+
const receipt = parseAs(ExecutionReceipt, freeze({
|
|
576
|
+
schemaVersion: 'workflow.execution/v1', ref: { id: `${executionId}.${cell.selectionCellId}.receipt`, revision: '1' },
|
|
577
|
+
executionId, campaignId: request.campaignId, attemptId, attemptNumber: ctx.attemptNumber, predecessorAttemptRef: ctx.predecessor, retryReason: ctx.predecessor ? 'redelivery after interrupted attempt' : null,
|
|
578
|
+
scope, requesterId: ownerFor(scope), caseRef: spec.ref, personaRef: item.persona.ref, surfaceBindingRef: item.binding.ref, variantRef: null,
|
|
579
|
+
executionHostRef: hostPlan.ref, hostLeaseRef: session?.lease.ref ?? { id: `${executionId}.host-lease.unissued`, revision: '0', digest: digestOfValue('unissued') }, hostObservationRefs: session ? [session.observation.ref] : [],
|
|
580
|
+
environment, idempotencyKey: request.idempotencyKey, requestRef: request.ref, selectionCellId: cell.selectionCellId, qualificationRoundRef: request.qualificationRoundRef,
|
|
581
|
+
fixtureManifestRef: fixtureManifest?.ref ?? null, boundaryObservationRefs: boundaryObservations.map((b) => b.ref), proofClaim: spec.proofClaim,
|
|
582
|
+
coverage: reduction.coverage, eventsDigest: eventsArtifact.ref.digest, assertions, steps: stepObservations, artifacts: allArtifacts, accounting,
|
|
583
|
+
performance: stepObservations.filter((s) => s.timing).map((s) => ({ metric: `step.${s.stepId}.duration`, boundary: compiled.stepsById.get(s.stepId).kind.startsWith('ui.') ? 'browser' : 'http', clock: 'monotonic', cellId: cell.selectionCellId, valueMs: s.timing.durationMs, censored: false, warm: false, strata: { host: hostPlan.adapter } })),
|
|
584
|
+
cleanup, outcome: reduction.outcome, reasonCodes: reduction.reasonCodes, proofEligible: reduction.proofEligible, startedAt, finalizedAt: finishedAt,
|
|
585
|
+
}), 'execution receipt');
|
|
586
|
+
const finalization = log.emit('cell.finalized', null, { outcome: receipt.outcome, reasonCodes: receipt.reasonCodes, receiptRef: receipt.ref });
|
|
587
|
+
// Persist cell-level records beside the receipt for the bundle writer. The events file carries the
|
|
588
|
+
// sealed bytes the receipt hashes; the finalization event is recorded separately.
|
|
589
|
+
writeExecutionBundle({ dir: ctx.executionDir, cell: { id: cell.selectionCellId, receipt, environment, fixtureManifest, boundaryObservations, hostLease: session?.lease ?? null, hostObservation: session?.observation ?? null, attestations: session?.attestations ?? [], credentialLease, routing, costs, eventsJsonl: sealedEvents, finalization, media } });
|
|
590
|
+
return receipt;
|
|
591
|
+
}
|
|
592
|
+
function topoOrder(recipe) {
|
|
593
|
+
const order = [];
|
|
594
|
+
const visiting = new Set();
|
|
595
|
+
const visited = new Set();
|
|
596
|
+
const visit = (key) => {
|
|
597
|
+
if (visited.has(key))
|
|
598
|
+
return;
|
|
599
|
+
if (visiting.has(key))
|
|
600
|
+
throw new Blocked([{ code: 'cyclic_fixture_dependency', detail: key }]);
|
|
601
|
+
visiting.add(key);
|
|
602
|
+
const entity = recipe.entities.find((e) => e.entityKey === key);
|
|
603
|
+
if (!entity)
|
|
604
|
+
throw new Blocked([{ code: 'unknown_fixture_dependency', detail: key }]);
|
|
605
|
+
for (const dep of entity.dependsOn)
|
|
606
|
+
visit(dep);
|
|
607
|
+
visiting.delete(key);
|
|
608
|
+
visited.add(key);
|
|
609
|
+
order.push(key);
|
|
610
|
+
};
|
|
611
|
+
for (const entity of recipe.entities)
|
|
612
|
+
visit(entity.entityKey);
|
|
613
|
+
return order;
|
|
614
|
+
}
|
|
615
|
+
export { selfDigest };
|
|
616
|
+
//# sourceMappingURL=executor.js.map
|