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,119 @@
|
|
|
1
|
+
"""Local Django notes API: real file persistence, no ORM or database connection."""
|
|
2
|
+
from dataclasses import asdict, dataclass
|
|
3
|
+
from datetime import datetime, timezone
|
|
4
|
+
from importlib.metadata import version
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from uuid import UUID, uuid4
|
|
9
|
+
from wsgiref.simple_server import WSGIRequestHandler, make_server
|
|
10
|
+
|
|
11
|
+
import django
|
|
12
|
+
from django.conf import settings
|
|
13
|
+
from django.core.wsgi import get_wsgi_application
|
|
14
|
+
from django.http import HttpResponse, JsonResponse
|
|
15
|
+
from django.urls import re_path
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class Note:
|
|
20
|
+
id: str
|
|
21
|
+
title: str
|
|
22
|
+
body: str
|
|
23
|
+
ownerId: str
|
|
24
|
+
createdAt: str
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def create(cls, payload: object, owner: str) -> "Note":
|
|
28
|
+
if not isinstance(payload, dict) or not all(isinstance(payload.get(key), str) for key in ("title", "body")):
|
|
29
|
+
raise ValueError("invalid_note")
|
|
30
|
+
return cls(str(uuid4()), payload["title"], payload["body"], owner, datetime.now(timezone.utc).isoformat())
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class NoteStore:
|
|
34
|
+
def __init__(self, root: Path):
|
|
35
|
+
self.root = root
|
|
36
|
+
self.root.mkdir(parents=True, exist_ok=True)
|
|
37
|
+
|
|
38
|
+
def path(self, identifier: str) -> Path:
|
|
39
|
+
return self.root / f"{UUID(identifier)}.json"
|
|
40
|
+
|
|
41
|
+
def read(self, identifier: str) -> Note | None:
|
|
42
|
+
try:
|
|
43
|
+
return Note(**json.loads(self.path(identifier).read_text()))
|
|
44
|
+
except (ValueError, FileNotFoundError):
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
def write(self, note: Note) -> None:
|
|
48
|
+
path = self.path(note.id)
|
|
49
|
+
temporary = path.with_suffix(".tmp")
|
|
50
|
+
temporary.write_text(json.dumps(asdict(note)))
|
|
51
|
+
temporary.replace(path)
|
|
52
|
+
|
|
53
|
+
def owned(self, owner: str) -> list[dict]:
|
|
54
|
+
return [asdict(note) for path in sorted(self.root.glob("*.json"))
|
|
55
|
+
if (note := self.read(path.stem)) is not None and note.ownerId == owner]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class NotesAPI:
|
|
59
|
+
def __init__(self, store: NoteStore, tokens: dict[str, str], build: str):
|
|
60
|
+
self.store, self.tokens, self.build = store, tokens, build
|
|
61
|
+
|
|
62
|
+
def health(self, request):
|
|
63
|
+
return JsonResponse({"ok": True, "build": self.build, "notes": len(list(self.store.root.glob("*.json")))})
|
|
64
|
+
|
|
65
|
+
def notes(self, request, identifier: str | None = None):
|
|
66
|
+
authorization = request.headers.get("Authorization", "")
|
|
67
|
+
owner = self.tokens.get(authorization[7:]) if authorization.startswith("Bearer ") else None
|
|
68
|
+
if not owner:
|
|
69
|
+
return JsonResponse({"error": "unauthorized"}, status=401)
|
|
70
|
+
if identifier is None:
|
|
71
|
+
if request.method == "GET":
|
|
72
|
+
return JsonResponse({"notes": self.store.owned(owner)})
|
|
73
|
+
if request.method == "POST":
|
|
74
|
+
try:
|
|
75
|
+
note = Note.create(json.loads(request.body or b"{}"), owner)
|
|
76
|
+
except (ValueError, UnicodeDecodeError):
|
|
77
|
+
return JsonResponse({"error": "invalid_note"}, status=400)
|
|
78
|
+
self.store.write(note)
|
|
79
|
+
return JsonResponse({"note": asdict(note)}, status=201)
|
|
80
|
+
return JsonResponse({"error": "method_not_allowed"}, status=405)
|
|
81
|
+
note = self.store.read(identifier)
|
|
82
|
+
if note is None:
|
|
83
|
+
return JsonResponse({"error": "not_found"}, status=404)
|
|
84
|
+
if note.ownerId != owner:
|
|
85
|
+
return JsonResponse({"error": "forbidden"}, status=403)
|
|
86
|
+
if request.method == "GET":
|
|
87
|
+
return JsonResponse({"note": asdict(note)})
|
|
88
|
+
if request.method == "DELETE":
|
|
89
|
+
self.store.path(identifier).unlink()
|
|
90
|
+
return HttpResponse(status=204)
|
|
91
|
+
return JsonResponse({"error": "method_not_allowed"}, status=405)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
class QuietHandler(WSGIRequestHandler):
|
|
95
|
+
def log_message(self, format, *args):
|
|
96
|
+
pass
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def check_dependencies() -> None:
|
|
100
|
+
# Fail readiness when the installed versions disagree with the pinned input.
|
|
101
|
+
for line in Path(__file__).with_name("requirements.lock").read_text().splitlines():
|
|
102
|
+
if line and not line.startswith("#"):
|
|
103
|
+
name, expected = line.split()[0].split("==")
|
|
104
|
+
if version(name) != expected:
|
|
105
|
+
raise RuntimeError("Install the dependencies from app/requirements.lock")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
check_dependencies()
|
|
109
|
+
settings.configure(DEBUG=False, ROOT_URLCONF=__name__, ALLOWED_HOSTS=["127.0.0.1"],
|
|
110
|
+
INSTALLED_APPS=[], DATABASES={}, MIDDLEWARE=[], DATA_UPLOAD_MAX_MEMORY_SIZE=65536)
|
|
111
|
+
django.setup()
|
|
112
|
+
api = NotesAPI(NoteStore(Path(os.environ["NOTES_DIR"])), json.loads(os.environ["NOTES_TOKENS"]),
|
|
113
|
+
os.environ.get("NOTES_BUILD", "django-sample"))
|
|
114
|
+
urlpatterns = [re_path(r"^health$", api.health), re_path(r"^notes(?:/(?P<identifier>[^/]+))?$", api.notes)]
|
|
115
|
+
|
|
116
|
+
if __name__ == "__main__":
|
|
117
|
+
with make_server("127.0.0.1", int(os.environ.get("PORT", "0")), get_wsgi_application(), handler_class=QuietHandler) as server:
|
|
118
|
+
print(f"listening {server.server_port}", flush=True)
|
|
119
|
+
server.serve_forever()
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
# Pure-Python wheels downloaded from PyPI; installs require these exact hashes.
|
|
2
|
+
asgiref==3.12.1 --hash=sha256:fe386d1c2bff7259ea95929266d12a8cf9a8b5a1c2598402967d8792e7a7c094
|
|
3
|
+
Django==6.1.2 --hash=sha256:141efee6ec64d1db6db90683bf734c550102450f444fb099063b0be1bd27d991
|
|
4
|
+
sqlparse==0.6.0 --hash=sha256:b861c0288ce2fa56209a9a6412d2e066ac664b3873b89c26c9d8415e8e32996f
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Local native application: file-backed notes over MCP and independent HTTP readback.
|
|
2
|
+
// Implements the bounded JSON-response 2025-11-25 transport, not an MCP widget host.
|
|
3
|
+
import { createServer } from 'node:http';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
import { mkdirSync, writeFileSync, readFileSync, readdirSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { send, readBody } from './http.mjs';
|
|
8
|
+
|
|
9
|
+
const directory = process.env.NOTES_DIR;
|
|
10
|
+
if (!directory) throw new Error('NOTES_DIR is required');
|
|
11
|
+
mkdirSync(directory, { recursive: true });
|
|
12
|
+
const tokens = JSON.parse(process.env.NOTES_TOKENS ?? '{}');
|
|
13
|
+
const server = createServer(async (request, response) => {
|
|
14
|
+
try {
|
|
15
|
+
const path = new URL(request.url ?? '/', 'http://127.0.0.1').pathname;
|
|
16
|
+
if (request.method === 'GET' && path === '/health') return send(response, 200, { ok: true, notes: readdirSync(directory).length });
|
|
17
|
+
const token = String(request.headers.authorization ?? '').replace(/^Bearer /, '');
|
|
18
|
+
if (!Object.hasOwn(tokens, token)) return send(response, 401, { error: 'unauthorized' });
|
|
19
|
+
const ownerId = tokens[token];
|
|
20
|
+
const match = path.match(/^\/notes\/([0-9a-f-]{36})$/);
|
|
21
|
+
if (request.method === 'GET' && match) {
|
|
22
|
+
let note;
|
|
23
|
+
try { note = JSON.parse(readFileSync(join(directory, `${match[1]}.json`), 'utf8')); }
|
|
24
|
+
catch { return send(response, 404, { error: 'not_found' }); }
|
|
25
|
+
if (note.ownerId !== ownerId) return send(response, 403, { error: 'forbidden' });
|
|
26
|
+
return send(response, 200, { note });
|
|
27
|
+
}
|
|
28
|
+
if (path !== '/mcp') return send(response, 404, { error: 'not_found' });
|
|
29
|
+
if (request.method !== 'POST') return send(response, 405, { error: 'method_not_allowed' });
|
|
30
|
+
const message = await readBody(request);
|
|
31
|
+
if (message.jsonrpc !== '2.0' || typeof message.method !== 'string') return send(response, 400, { error: 'invalid_request' });
|
|
32
|
+
if (message.id === undefined) { response.writeHead(202); response.end(); return; }
|
|
33
|
+
const reply = result => send(response, 200, { jsonrpc: '2.0', id: message.id, result });
|
|
34
|
+
if (message.method === 'initialize') return reply({ protocolVersion: '2025-11-25', capabilities: { tools: {} }, serverInfo: { name: 'native-notes', version: '1.0.0' } });
|
|
35
|
+
if (message.method === 'tools/list') return reply({ tools: [{ name: 'notes.create', inputSchema: { type: 'object', properties: { title: { type: 'string' }, body: { type: 'string' } }, required: ['title', 'body'], additionalProperties: false } }] });
|
|
36
|
+
if (message.method === 'tools/call' && message.params?.name === 'notes.create') {
|
|
37
|
+
const input = message.params.arguments;
|
|
38
|
+
if (!input || typeof input.title !== 'string' || typeof input.body !== 'string') return reply({ isError: true, content: [{ type: 'text', text: 'Invalid note input' }] });
|
|
39
|
+
const note = { id: randomUUID(), title: input.title, body: input.body, ownerId };
|
|
40
|
+
writeFileSync(join(directory, `${note.id}.json`), JSON.stringify(note));
|
|
41
|
+
return reply({ content: [{ type: 'text', text: JSON.stringify({ note }) }], structuredContent: { note }, isError: false });
|
|
42
|
+
}
|
|
43
|
+
return send(response, 200, { jsonrpc: '2.0', id: message.id, error: { code: -32601, message: 'Unsupported method or tool' } });
|
|
44
|
+
} catch { send(response, 400, { error: 'invalid_request' }); }
|
|
45
|
+
});
|
|
46
|
+
server.listen(Number(process.env.PORT ?? 0), '127.0.0.1');
|
|
47
|
+
process.on('SIGTERM', () => server.close(() => process.exit(0)));
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
title: MCP creation is independently visible through the notes API
|
|
2
|
+
effects:
|
|
3
|
+
maxMcpCalls: 1
|
|
4
|
+
allowedMcpTools: [notes.create]
|
|
5
|
+
steps:
|
|
6
|
+
- name: create
|
|
7
|
+
mcp:
|
|
8
|
+
endpoint: /mcp
|
|
9
|
+
tool: notes.create
|
|
10
|
+
arguments: { title: Protocol proof, body: Saved by the native application }
|
|
11
|
+
expect:
|
|
12
|
+
json: { structuredContent.note.title: Protocol proof }
|
|
13
|
+
save: { noteId: structuredContent.note.id }
|
|
14
|
+
- GET: /notes/{noteId}
|
|
15
|
+
expect:
|
|
16
|
+
status: 200
|
|
17
|
+
json: { note.title: Protocol proof, note.ownerId: alice }
|
|
18
|
+
- GET: /notes/{noteId}
|
|
19
|
+
as: intruder
|
|
20
|
+
expect: 403
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Report jobs
|
|
2
|
+
|
|
3
|
+
This sample runs a local HTTP API and a separate Node worker against one persisted
|
|
4
|
+
SQLite queue. Only the worker publishes report rows. Duplicate owner/request keys
|
|
5
|
+
reuse one job, cancellation waits for a worker acknowledgement, and failed jobs
|
|
6
|
+
leave no report. Fixture cleanup deletes its owned job and report atomically.
|
|
7
|
+
|
|
8
|
+
Generate the sample with `tracegrove sample-config --sample report-jobs --output
|
|
9
|
+
<empty-directory>`. From that directory, run `tracegrove test`. Sample generation
|
|
10
|
+
also compiles the low-level corpus and prints its config path; pass that exact
|
|
11
|
+
path to `workflow-verify plan` or `doctor`. Compilation checks the installed Node
|
|
12
|
+
runtime but does not start the API or worker.
|
|
13
|
+
|
|
14
|
+
All flows use the existing HTTP submit/readback/oracle contracts. A 202 submission
|
|
15
|
+
alone cannot satisfy the completion case: it polls job state, reads the persisted
|
|
16
|
+
report independently, checks one-job idempotency and verifies owner isolation.
|
|
17
|
+
`effects.maxJobs: 1` declares each flow's admitted job ceiling; it does not impose
|
|
18
|
+
a quota on an arbitrary external provider.
|
|
19
|
+
|
|
20
|
+
The `REPORTS_TEST_DROP_REPORT=1` application flag is a controlled defect: the
|
|
21
|
+
worker records success without inserting the report. The completion flow must
|
|
22
|
+
fail on independent report readback, while retaining its sealed failed attempt.
|
|
23
|
+
The flag is part of the frozen app launch configuration. It is not a normal
|
|
24
|
+
successful lane.
|
|
25
|
+
|
|
26
|
+
The package's pinned Node 22 range supplies `node:sqlite`; no database server,
|
|
27
|
+
account, model call or OrgX connection is needed. The core attests the complete
|
|
28
|
+
application source set, including worker and storage code. This is local
|
|
29
|
+
application/job behavior, not a separately issued worker attestation, hosted
|
|
30
|
+
provider qualification, a native `job.submit` adapter, or crash-recovery proof.
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createServer } from 'node:http';
|
|
2
|
+
import { fork } from 'node:child_process';
|
|
3
|
+
import { mkdirSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { JobStore, validJobInput } from './store.mjs';
|
|
6
|
+
import { nativeBridge } from './native-bridge.mjs';
|
|
7
|
+
import { send, readBody } from './http.mjs';
|
|
8
|
+
|
|
9
|
+
const dir = process.env.REPORTS_DIR;
|
|
10
|
+
if (!dir) throw new Error('REPORTS_DIR is required');
|
|
11
|
+
mkdirSync(dir, { recursive: true });
|
|
12
|
+
const store = new JobStore(join(dir, 'reports.sqlite'));
|
|
13
|
+
const tokens = JSON.parse(process.env.REPORTS_TOKENS ?? '{}');
|
|
14
|
+
const worker = fork(new URL('./worker.mjs', import.meta.url), [join(dir, 'reports.sqlite')], {
|
|
15
|
+
stdio: ['ignore', 'ignore', 'ignore', 'ipc'],
|
|
16
|
+
env: { REPORTS_TEST_DROP_REPORT: process.env.REPORTS_TEST_DROP_REPORT ?? '0' },
|
|
17
|
+
});
|
|
18
|
+
let ready = false;
|
|
19
|
+
const nativeReady = nativeBridge(store, tokens, () => ready);
|
|
20
|
+
worker.on('message', message => { if (message?.type === 'ready') { ready = true; nativeReady(); } });
|
|
21
|
+
worker.on('exit', () => { ready = false; });
|
|
22
|
+
const view = job => ({ id: job.id, requestKey: job.request_key, title: job.title, mode: job.mode,
|
|
23
|
+
status: job.status, workerPid: job.worker_pid, apiPid: process.pid });
|
|
24
|
+
const server = createServer(async (req, res) => {
|
|
25
|
+
const url = new URL(req.url ?? '/', 'http://127.0.0.1');
|
|
26
|
+
if (req.method === 'GET' && url.pathname === '/health') return send(res, ready ? 200 : 503, { ok: ready, workerPid: worker.pid, apiPid: process.pid });
|
|
27
|
+
const authorization = req.headers.authorization ?? '';
|
|
28
|
+
const token = authorization.startsWith('Bearer ') ? authorization.slice(7) : '';
|
|
29
|
+
const owner = Object.hasOwn(tokens, token) ? tokens[token] : null;
|
|
30
|
+
if (typeof owner !== 'string' || !owner) return send(res, 401, { error: 'unauthorized' });
|
|
31
|
+
try {
|
|
32
|
+
if (url.pathname === '/jobs' && req.method === 'GET') return send(res, 200, { jobs: store.list(owner).map(view) });
|
|
33
|
+
if (url.pathname === '/jobs' && req.method === 'POST') {
|
|
34
|
+
if (!ready) return send(res, 503, { error: 'worker_unavailable' });
|
|
35
|
+
const body = await readBody(req);
|
|
36
|
+
if (!validJobInput(body)) return send(res, 400, { error: 'invalid_job' });
|
|
37
|
+
const result = store.submit(owner, body);
|
|
38
|
+
if (result.conflict) return send(res, 409, { error: 'idempotency_conflict' });
|
|
39
|
+
return send(res, result.duplicate ? 200 : 202, { job: view(result.job), deduplicated: result.duplicate });
|
|
40
|
+
}
|
|
41
|
+
const match = /^\/(jobs|reports)\/([0-9a-f-]{36})(\/cancel)?$/.exec(url.pathname);
|
|
42
|
+
if (!match) return send(res, 404, { error: 'not_found' });
|
|
43
|
+
const [, resource, id, cancel] = match;
|
|
44
|
+
const job = store.get(id);
|
|
45
|
+
if (!job) return send(res, 404, { error: 'not_found' });
|
|
46
|
+
if (job.owner_id !== owner) return send(res, 403, { error: 'forbidden' });
|
|
47
|
+
if (resource === 'reports' && req.method === 'GET' && !cancel) {
|
|
48
|
+
const report = store.report(id);
|
|
49
|
+
return report ? send(res, 200, { report }) : send(res, 404, { error: 'report_absent' });
|
|
50
|
+
}
|
|
51
|
+
if (resource !== 'jobs') return send(res, 405, { error: 'method_not_allowed' });
|
|
52
|
+
if (cancel && req.method === 'POST') return send(res, store.requestCancel(id) ? 202 : 409, { requested: true });
|
|
53
|
+
if (!cancel && req.method === 'GET') return send(res, 200, { job: view(job) });
|
|
54
|
+
if (!cancel && req.method === 'DELETE') { store.remove(id); return send(res, 204); }
|
|
55
|
+
return send(res, 405, { error: 'method_not_allowed' });
|
|
56
|
+
} catch { return send(res, 400, { error: 'bad_request' }); }
|
|
57
|
+
});
|
|
58
|
+
server.listen(Number(process.env.PORT ?? 0), '127.0.0.1', () => {
|
|
59
|
+
process.send?.({ type: 'listening', port: server.address().port });
|
|
60
|
+
});
|
|
61
|
+
process.on('SIGTERM', () => {
|
|
62
|
+
const close = () => server.close(() => { store.close(); process.exit(0); });
|
|
63
|
+
if (worker.exitCode !== null || worker.signalCode !== null) close();
|
|
64
|
+
else { worker.once('exit', close); worker.kill('SIGTERM'); }
|
|
65
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { validJobInput } from './store.mjs';
|
|
2
|
+
|
|
3
|
+
/** The application owns auth and queue semantics; the worker and SQLite store remain real. */
|
|
4
|
+
export function nativeBridge(store, tokens, workerReady) {
|
|
5
|
+
if (process.env.TRACEGROVE_NATIVE_JOBS !== 'node_ipc/v1') return () => {};
|
|
6
|
+
const empty = status => ({ jobId: null, status, requestKey: null, output: null, workerId: null });
|
|
7
|
+
process.on('message', request => {
|
|
8
|
+
if (request?.kind !== 'tracegrove.job.request') return;
|
|
9
|
+
const { command, credential } = request;
|
|
10
|
+
const owner = Object.hasOwn(tokens, credential) ? tokens[credential] : null;
|
|
11
|
+
let result = empty('forbidden');
|
|
12
|
+
if (typeof owner === 'string' && command.queue === 'reports') {
|
|
13
|
+
let job;
|
|
14
|
+
if (command.action === 'submit') {
|
|
15
|
+
const input = { ...command.payload, requestKey: command.requestKey };
|
|
16
|
+
if (!workerReady() || !validJobInput(input)) result = empty('conflict');
|
|
17
|
+
else { const submitted = store.submit(owner, input); job = submitted.job; if (submitted.conflict) result = empty('conflict'); }
|
|
18
|
+
} else {
|
|
19
|
+
job = store.get(command.jobId);
|
|
20
|
+
if (!job) result = empty('missing');
|
|
21
|
+
else if (job.owner_id !== owner) { job = null; result = empty('forbidden'); }
|
|
22
|
+
else if (command.action === 'cancel') { store.requestCancel(job.id); job = store.get(job.id); }
|
|
23
|
+
}
|
|
24
|
+
if (job) result = { jobId: job.id, status: job.status, requestKey: job.request_key,
|
|
25
|
+
output: { report: store.report(job.id), apiPid: process.pid, totalJobs: store.list(owner).length }, workerId: job.worker_pid ? String(job.worker_pid) : null };
|
|
26
|
+
}
|
|
27
|
+
process.send?.({ kind: 'tracegrove.job.response', id: request.id, result });
|
|
28
|
+
});
|
|
29
|
+
return () => process.send?.({ kind: 'tracegrove.job.ready', version: 1, queues: ['reports'] });
|
|
30
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
|
|
4
|
+
export function validJobInput(input) {
|
|
5
|
+
return typeof input.requestKey === 'string' && !!input.requestKey && input.requestKey.length <= 100 &&
|
|
6
|
+
typeof input.title === 'string' && !!input.title && input.title.length <= 200 && ['succeed', 'fail', 'hold'].includes(input.mode);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Example application storage, separate from Tracegrove's authority and ledger. */
|
|
10
|
+
export class JobStore {
|
|
11
|
+
constructor(path) {
|
|
12
|
+
this.db = new DatabaseSync(path);
|
|
13
|
+
this.db.exec(`PRAGMA busy_timeout=5000; PRAGMA journal_mode=WAL;
|
|
14
|
+
CREATE TABLE IF NOT EXISTS jobs (
|
|
15
|
+
id TEXT PRIMARY KEY, owner_id TEXT NOT NULL, request_key TEXT NOT NULL,
|
|
16
|
+
title TEXT NOT NULL, mode TEXT NOT NULL, status TEXT NOT NULL,
|
|
17
|
+
worker_pid INTEGER, UNIQUE(owner_id,request_key)
|
|
18
|
+
) STRICT;
|
|
19
|
+
CREATE TABLE IF NOT EXISTS reports (
|
|
20
|
+
job_id TEXT PRIMARY KEY, content TEXT NOT NULL, worker_pid INTEGER NOT NULL
|
|
21
|
+
) STRICT;`);
|
|
22
|
+
}
|
|
23
|
+
transaction(action) {
|
|
24
|
+
this.db.exec('BEGIN IMMEDIATE');
|
|
25
|
+
try { const value = action(); this.db.exec('COMMIT'); return value; }
|
|
26
|
+
catch (error) { this.db.exec('ROLLBACK'); throw error; }
|
|
27
|
+
}
|
|
28
|
+
get(id) { return this.db.prepare('SELECT * FROM jobs WHERE id=?').get(id) ?? null; }
|
|
29
|
+
list(owner) { return this.db.prepare('SELECT * FROM jobs WHERE owner_id=? ORDER BY id').all(owner); }
|
|
30
|
+
submit(owner, input) {
|
|
31
|
+
return this.transaction(() => {
|
|
32
|
+
const existing = this.db.prepare('SELECT * FROM jobs WHERE owner_id=? AND request_key=?').get(owner, input.requestKey);
|
|
33
|
+
if (existing) return existing.title === input.title && existing.mode === input.mode
|
|
34
|
+
? { job: existing, duplicate: true } : { conflict: true };
|
|
35
|
+
const id = randomUUID();
|
|
36
|
+
this.db.prepare('INSERT INTO jobs(id,owner_id,request_key,title,mode,status) VALUES(?,?,?,?,?,?)')
|
|
37
|
+
.run(id, owner, input.requestKey, input.title, input.mode, 'queued');
|
|
38
|
+
return { job: this.get(id), duplicate: false };
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
claim() {
|
|
42
|
+
return this.transaction(() => {
|
|
43
|
+
const row = this.db.prepare("SELECT * FROM jobs WHERE status IN ('queued','cancel_requested') ORDER BY rowid LIMIT 1").get();
|
|
44
|
+
if (!row) return null;
|
|
45
|
+
if (row.status === 'cancel_requested') {
|
|
46
|
+
this.db.prepare("UPDATE jobs SET status='cancelled', worker_pid=? WHERE id=?").run(process.pid, row.id);
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
this.db.prepare("UPDATE jobs SET status='running', worker_pid=? WHERE id=?").run(process.pid, row.id);
|
|
50
|
+
return this.get(row.id);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
requestCancel(id) {
|
|
54
|
+
return this.db.prepare("UPDATE jobs SET status='cancel_requested' WHERE id=? AND status IN ('queued','running','cancel_requested')").run(id).changes > 0;
|
|
55
|
+
}
|
|
56
|
+
finish(id, dropReport = false) {
|
|
57
|
+
return this.transaction(() => {
|
|
58
|
+
const job = this.get(id);
|
|
59
|
+
if (!job || !['running', 'cancel_requested'].includes(job.status)) return;
|
|
60
|
+
const status = job.status === 'cancel_requested' ? 'cancelled' : job.mode === 'fail' ? 'failed' : 'succeeded';
|
|
61
|
+
if (status === 'succeeded' && !dropReport) this.db.prepare('INSERT INTO reports(job_id,content,worker_pid) VALUES(?,?,?)')
|
|
62
|
+
.run(id, `Report: ${job.title}`, process.pid);
|
|
63
|
+
this.db.prepare('UPDATE jobs SET status=?, worker_pid=? WHERE id=?').run(status, process.pid, id);
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
report(id) { return this.db.prepare('SELECT * FROM reports WHERE job_id=?').get(id) ?? null; }
|
|
67
|
+
remove(id) {
|
|
68
|
+
this.transaction(() => {
|
|
69
|
+
this.db.prepare('DELETE FROM reports WHERE job_id=?').run(id);
|
|
70
|
+
this.db.prepare('DELETE FROM jobs WHERE id=?').run(id);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
close() { this.db.close(); }
|
|
74
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { setTimeout as pause } from 'node:timers/promises';
|
|
2
|
+
import { JobStore } from './store.mjs';
|
|
3
|
+
|
|
4
|
+
const store = new JobStore(process.argv[2]);
|
|
5
|
+
let busy = false;
|
|
6
|
+
const tick = async () => {
|
|
7
|
+
if (busy) return;
|
|
8
|
+
busy = true;
|
|
9
|
+
try {
|
|
10
|
+
const job = store.claim();
|
|
11
|
+
if (!job) return;
|
|
12
|
+
// Hold mode exercises a genuine worker acknowledgement of cancellation.
|
|
13
|
+
while (job.mode === 'hold' && store.get(job.id)?.status === 'running') await pause(25);
|
|
14
|
+
store.finish(job.id, process.env.REPORTS_TEST_DROP_REPORT === '1');
|
|
15
|
+
} catch { process.exitCode = 1; shutdown(); }
|
|
16
|
+
finally { busy = false; }
|
|
17
|
+
};
|
|
18
|
+
const timer = setInterval(() => { void tick(); }, 25);
|
|
19
|
+
function shutdown() { clearInterval(timer); store.close(); process.exit(process.exitCode ?? 0); }
|
|
20
|
+
process.on('SIGTERM', shutdown);
|
|
21
|
+
process.on('disconnect', shutdown);
|
|
22
|
+
process.send?.({ type: 'ready' });
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
create:
|
|
2
|
+
POST: /jobs
|
|
3
|
+
json: { requestKey: { uuid: true }, title: Failed report, mode: fail }
|
|
4
|
+
expect: 202
|
|
5
|
+
save: { jobId: job.id }
|
|
6
|
+
readback:
|
|
7
|
+
GET: /jobs/{jobId}
|
|
8
|
+
expect: { status: 200, json: { job.title: Failed report } }
|
|
9
|
+
cleanup:
|
|
10
|
+
DELETE: /jobs/{jobId}
|
|
11
|
+
expect: 204
|
|
12
|
+
gone: { GET: "/jobs/{jobId}", status: 404 }
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
create:
|
|
2
|
+
POST: /jobs
|
|
3
|
+
json: { requestKey: { uuid: true }, title: Held report, mode: hold }
|
|
4
|
+
expect: 202
|
|
5
|
+
save: { jobId: job.id }
|
|
6
|
+
readback:
|
|
7
|
+
GET: /jobs/{jobId}
|
|
8
|
+
expect: { status: 200, json: { job.title: Held report } }
|
|
9
|
+
cleanup:
|
|
10
|
+
DELETE: /jobs/{jobId}
|
|
11
|
+
expect: 204
|
|
12
|
+
gone: { GET: "/jobs/{jobId}", status: 404 }
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
create:
|
|
2
|
+
POST: /jobs
|
|
3
|
+
json: { requestKey: { uuid: true }, title: Weekly review, mode: succeed }
|
|
4
|
+
expect: 202
|
|
5
|
+
save: { jobId: job.id, requestKey: job.requestKey }
|
|
6
|
+
readback:
|
|
7
|
+
GET: /jobs/{jobId}
|
|
8
|
+
expect: { status: 200, json: { job.title: Weekly review } }
|
|
9
|
+
cleanup:
|
|
10
|
+
DELETE: /jobs/{jobId}
|
|
11
|
+
expect: 204
|
|
12
|
+
gone: { GET: "/jobs/{jobId}", status: 404 }
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
title: Cancellation is acknowledged by the worker and leaves no report
|
|
2
|
+
tags: [jobs, cancellation]
|
|
3
|
+
effects: { maxJobs: 1 }
|
|
4
|
+
given: { report: held-job }
|
|
5
|
+
steps:
|
|
6
|
+
- name: request cancellation
|
|
7
|
+
POST: /jobs/{report.jobId}/cancel
|
|
8
|
+
expect: 202
|
|
9
|
+
- name: worker acknowledges cancellation
|
|
10
|
+
GET: /jobs/{report.jobId}
|
|
11
|
+
observe: true
|
|
12
|
+
expect: { status: 200, json: { job.status: cancelled }, present: [job.workerPid] }
|
|
13
|
+
- name: no report was published
|
|
14
|
+
GET: /reports/{report.jobId}
|
|
15
|
+
expect: 404
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
title: One queued report produces one persisted output for its owner
|
|
2
|
+
tags: [jobs, smoke]
|
|
3
|
+
effects: { maxJobs: 1 }
|
|
4
|
+
given: { report: success-job }
|
|
5
|
+
steps:
|
|
6
|
+
- name: redeliver the same request
|
|
7
|
+
POST: /jobs
|
|
8
|
+
json: { requestKey: "{report.requestKey}", title: Weekly review, mode: succeed }
|
|
9
|
+
expect: { status: 200, json: { deduplicated: true } }
|
|
10
|
+
- name: only one job exists
|
|
11
|
+
GET: /jobs
|
|
12
|
+
expect: { status: 200, count: { jobs: 1 } }
|
|
13
|
+
- name: worker completes the job
|
|
14
|
+
GET: /jobs/{report.jobId}
|
|
15
|
+
observe: true
|
|
16
|
+
expect: { status: 200, json: { job.status: succeeded }, present: [job.workerPid] }
|
|
17
|
+
- name: read persisted output independently
|
|
18
|
+
GET: /reports/{report.jobId}
|
|
19
|
+
expect: { status: 200, json: { report.content: "Report: Weekly review" } }
|
|
20
|
+
- name: another owner cannot read the job
|
|
21
|
+
GET: /jobs/{report.jobId}
|
|
22
|
+
as: intruder
|
|
23
|
+
expect: 403
|
|
24
|
+
- name: another owner cannot read the report
|
|
25
|
+
GET: /reports/{report.jobId}
|
|
26
|
+
as: intruder
|
|
27
|
+
expect: 403
|
|
28
|
+
- name: an anonymous caller is rejected
|
|
29
|
+
GET: /reports/{report.jobId}
|
|
30
|
+
as: none
|
|
31
|
+
expect: 401
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
title: A failed worker job exposes failure and leaves no report
|
|
2
|
+
tags: [jobs, failure]
|
|
3
|
+
effects: { maxJobs: 1 }
|
|
4
|
+
given: { report: failed-job }
|
|
5
|
+
steps:
|
|
6
|
+
- name: worker records its failure
|
|
7
|
+
GET: /jobs/{report.jobId}
|
|
8
|
+
observe: true
|
|
9
|
+
expect: { status: 200, json: { job.status: failed }, present: [job.workerPid] }
|
|
10
|
+
- name: failure did not publish a report
|
|
11
|
+
GET: /reports/{report.jobId}
|
|
12
|
+
expect: 404
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
project: reports
|
|
2
|
+
app:
|
|
3
|
+
adapter: node
|
|
4
|
+
entry: app/jobs-api.mjs
|
|
5
|
+
sources: [app/**]
|
|
6
|
+
inject: { port: PORT, dataDir: REPORTS_DIR, tokens: REPORTS_TOKENS }
|
|
7
|
+
personas:
|
|
8
|
+
owner: { subject: alice }
|
|
9
|
+
intruder: { subject: bob }
|
|
10
|
+
defaults: { stepTimeoutMs: 3000, caseTimeoutMs: 20000 }
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Native report jobs
|
|
2
|
+
|
|
3
|
+
Generate with `tracegrove sample-config --sample report-native --output <folder>`.
|
|
4
|
+
The generator composes this config and these flows with the shared report-jobs
|
|
5
|
+
application, SQLite store, and independent worker. No application code is copied
|
|
6
|
+
into a second source of truth in this sample directory.
|
|
7
|
+
|
|
8
|
+
Run `tracegrove test --config <folder>/tracegrove.config.yaml`. Submission,
|
|
9
|
+
readback, and cancellation use the native Node IPC bridge. HTTP serves only
|
|
10
|
+
application readiness. See `docs/native-jobs.md` for the typed bridge contract,
|
|
11
|
+
effect limits, scope, and qualification boundaries.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
title: Native cancellation waits for a real worker acknowledgement
|
|
2
|
+
effects: { maxJobs: 1 }
|
|
3
|
+
steps:
|
|
4
|
+
- job: { action: submit, queue: reports, requestKey: cancellation, payload: { title: Held report, mode: hold } }
|
|
5
|
+
save: { jobId: jobId }
|
|
6
|
+
- job: { action: read, queue: reports, jobId: '{jobId}' }
|
|
7
|
+
expect: { json: { status: running } }
|
|
8
|
+
- job: { action: cancel, queue: reports, jobId: '{jobId}' }
|
|
9
|
+
expect: { present: [jobId] }
|
|
10
|
+
- job: { action: read, queue: reports, jobId: '{jobId}' }
|
|
11
|
+
expect: { json: { status: cancelled, output.report: null }, present: [workerId] }
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
title: Native submission deduplicates and an independent native read observes persisted worker output
|
|
2
|
+
effects: { maxJobs: 3 }
|
|
3
|
+
steps:
|
|
4
|
+
- name: submit report
|
|
5
|
+
job: { action: submit, queue: reports, requestKey: completion, payload: { title: Native report, mode: succeed } }
|
|
6
|
+
save: { jobId: jobId }
|
|
7
|
+
expect: { present: [jobId] }
|
|
8
|
+
- name: duplicate delivery
|
|
9
|
+
job: { action: submit, queue: reports, requestKey: completion, payload: { title: Native report, mode: succeed } }
|
|
10
|
+
expect: { json: { output.totalJobs: 1 } }
|
|
11
|
+
- name: conflicting delivery
|
|
12
|
+
job: { action: submit, queue: reports, requestKey: completion, payload: { title: Changed report, mode: succeed } }
|
|
13
|
+
expect: { json: { status: conflict } }
|
|
14
|
+
- name: another persona cannot read the job
|
|
15
|
+
as: intruder
|
|
16
|
+
job: { action: read, queue: reports, jobId: '{jobId}' }
|
|
17
|
+
expect: { json: { status: forbidden } }
|
|
18
|
+
- name: independent persisted output
|
|
19
|
+
job: { action: read, queue: reports, jobId: '{jobId}' }
|
|
20
|
+
expect:
|
|
21
|
+
json: { status: succeeded, output.report.content: 'Report: Native report', output.totalJobs: 1 }
|
|
22
|
+
present: [workerId]
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
title: A native provider failure remains a failed job with no persisted report
|
|
2
|
+
effects: { maxJobs: 1 }
|
|
3
|
+
steps:
|
|
4
|
+
- job: { action: submit, queue: reports, requestKey: failure, payload: { title: Failed report, mode: fail } }
|
|
5
|
+
save: { jobId: jobId }
|
|
6
|
+
- job: { action: read, queue: reports, jobId: '{jobId}' }
|
|
7
|
+
expect: { json: { status: failed, output.report: null }, present: [workerId] }
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
project: native_reports
|
|
2
|
+
app:
|
|
3
|
+
adapter: node
|
|
4
|
+
nativeJobs: node_ipc/v1
|
|
5
|
+
entry: app/jobs-api.mjs
|
|
6
|
+
sources: [app/**]
|
|
7
|
+
inject: { port: PORT, dataDir: REPORTS_DIR, tokens: REPORTS_TOKENS }
|
|
8
|
+
personas:
|
|
9
|
+
owner: { subject: alice }
|
|
10
|
+
intruder: { subject: bob }
|
|
11
|
+
defaults: { stepTimeoutMs: 3000, caseTimeoutMs: 20000 }
|