diff --git a/.bitcode/v42-readfitsfinding-preview-quote.json b/.bitcode/v42-readfitsfinding-preview-quote.json new file mode 100644 index 00000000..0c555920 --- /dev/null +++ b/.bitcode/v42-readfitsfinding-preview-quote.json @@ -0,0 +1,676 @@ +{ + "artifactId": "v42-readfitsfinding-preview-quote", + "schemaId": "bitcode.v42.readFitsFindingPreviewQuote.v1", + "version": "V42", + "currentTarget": "V41", + "sourceSafetyVerdict": "source-safe-readfitsfinding-preview-quote-metadata", + "generatedAt": "deterministic", + "artifactRoot": "v42-readfitsfinding-preview-quote:69267c22ee5a9efe19024b6d", + "passed": true, + "rows": [ + { + "rowId": "admission:accepted-need-required", + "purpose": "Require an accepted ReadNeedComprehensionSynthesis Need before ReadFitsFindingSynthesis can search or preview an AssetPack.", + "sourceRoots": [ + "packages/pipelines/asset-pack/src/read-need.ts", + "packages/pipelines/asset-pack/src/read-fits-finding-runtime.ts", + "uapi/app/terminal/TerminalDepositReadWorkbench.tsx" + ], + "emittedTypes": [ + "ReadFitsFindingAdmission", + "accepted_need_admission" + ], + "requiredEvidence": [ + "accepted_read_need_missing", + "findingFitsAdmission", + "acceptedNeed" + ], + "rowRoot": "v42-readfitsfinding-preview-quote-row:fbe895bf78a96e815c9be5d6", + "sourceSafetyClass": "source_safe_readfitsfinding_preview_quote_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "rawProtectedPromptVisible": false, + "rawProviderResponseVisible": false, + "unpaidAssetPackSourceVisible": false, + "walletPrivateMaterialVisible": false, + "settlementPrivatePayloadVisible": false, + "credentialsSerialized": false, + "forbiddenPayloadClasses": [ + "protected-source-payloads", + "raw-protected-prompts", + "raw-provider-responses", + "unpaid-assetpack-source", + "wallet-private-material", + "settlement-private-payloads", + "secret-values" + ] + }, + { + "rowId": "search:many-candidate-depository-discovery", + "purpose": "Search Depository AssetPack supply for many candidates above threshold before selecting the fit set.", + "sourceRoots": [ + "packages/pipelines/asset-pack/src/depository-search.ts", + "packages/pipelines/asset-pack/src/__tests__/depository-search.test.ts" + ], + "emittedTypes": [ + "DepositorySearchResult", + "DepositoryCandidate" + ], + "requiredEvidence": [ + "searchedAssetCount", + "candidateRanking", + "maxSelectedCandidates" + ], + "rowRoot": "v42-readfitsfinding-preview-quote-row:a7549cf3806d92ce20f13fff", + "sourceSafetyClass": "source_safe_readfitsfinding_preview_quote_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "rawProtectedPromptVisible": false, + "rawProviderResponseVisible": false, + "unpaidAssetPackSourceVisible": false, + "walletPrivateMaterialVisible": false, + "settlementPrivatePayloadVisible": false, + "credentialsSerialized": false, + "forbiddenPayloadClasses": [ + "protected-source-payloads", + "raw-protected-prompts", + "raw-provider-responses", + "unpaid-assetpack-source", + "wallet-private-material", + "settlement-private-payloads", + "secret-values" + ] + }, + { + "rowId": "search:multi-channel-vector-provider-tooling", + "purpose": "Bind lexical, symbolic, path, metadata, measurement, embedding-vector, and provider-specific search channels to source-safe tool receipts.", + "sourceRoots": [ + "packages/pipelines/asset-pack/src/depository-search.ts", + "packages/pipelines/asset-pack/src/read-fits-finding-runtime.ts" + ], + "emittedTypes": [ + "ReadFitsFindingSynthesisSearchReceipt", + "DepositorySearchQueryPlan" + ], + "requiredEvidence": [ + "lexical", + "symbolic", + "path", + "metadata", + "measurement", + "embedding-vector", + "provider-specific", + "ReadFitsFindingSynthesis.tool.vector-depository-search" + ], + "rowRoot": "v42-readfitsfinding-preview-quote-row:26ef6786dfdec3d0571c9d19", + "sourceSafetyClass": "source_safe_readfitsfinding_preview_quote_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "rawProtectedPromptVisible": false, + "rawProviderResponseVisible": false, + "unpaidAssetPackSourceVisible": false, + "walletPrivateMaterialVisible": false, + "settlementPrivatePayloadVisible": false, + "credentialsSerialized": false, + "forbiddenPayloadClasses": [ + "protected-source-payloads", + "raw-protected-prompts", + "raw-provider-responses", + "unpaid-assetpack-source", + "wallet-private-material", + "settlement-private-payloads", + "secret-values" + ] + }, + { + "rowId": "ranking:candidate-ranking-and-thresholds", + "purpose": "Rank candidates with deterministic scores, thresholds, blockers, rejected counts, and proof/measurement requirements.", + "sourceRoots": [ + "packages/pipelines/asset-pack/src/depository-search.ts", + "packages/pipelines/asset-pack/src/__tests__/depository-search.test.ts" + ], + "emittedTypes": [ + "DepositoryCandidateRanking", + "DepositoryCandidateFitEvidence" + ], + "requiredEvidence": [ + "finalScore", + "semanticScore", + "proofScore", + "measurementScore", + "rejectedCandidateCount" + ], + "rowRoot": "v42-readfitsfinding-preview-quote-row:aff87ef4ef37eaf19c22272c", + "sourceSafetyClass": "source_safe_readfitsfinding_preview_quote_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "rawProtectedPromptVisible": false, + "rawProviderResponseVisible": false, + "unpaidAssetPackSourceVisible": false, + "walletPrivateMaterialVisible": false, + "settlementPrivatePayloadVisible": false, + "credentialsSerialized": false, + "forbiddenPayloadClasses": [ + "protected-source-payloads", + "raw-protected-prompts", + "raw-provider-responses", + "unpaid-assetpack-source", + "wallet-private-material", + "settlement-private-payloads", + "secret-values" + ] + }, + { + "rowId": "provenance:selected-fit-provenance", + "purpose": "Persist selected fit provenance with selected candidate ids, fit deposit ids, proof roots, measurement roots, and reconciliation roots.", + "sourceRoots": [ + "packages/pipelines/asset-pack/src/read-fits-finding-runtime.ts", + "packages/pipelines/asset-pack/src/asset-pack-preview-boundary.ts", + "packages/pipelines/asset-pack/src/__tests__/read-fits-finding-runtime.test.ts" + ], + "emittedTypes": [ + "selected_fit_provenance", + "AssetPackPreviewSelectedFitProvenance" + ], + "requiredEvidence": [ + "selectedFitProvenanceRoot", + "fitDepositAssetIds", + "reconciliationReadbackRoot" + ], + "rowRoot": "v42-readfitsfinding-preview-quote-row:799590ec67d4c203c0d80ca9", + "sourceSafetyClass": "source_safe_readfitsfinding_preview_quote_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "rawProtectedPromptVisible": false, + "rawProviderResponseVisible": false, + "unpaidAssetPackSourceVisible": false, + "walletPrivateMaterialVisible": false, + "settlementPrivatePayloadVisible": false, + "credentialsSerialized": false, + "forbiddenPayloadClasses": [ + "protected-source-payloads", + "raw-protected-prompts", + "raw-provider-responses", + "unpaid-assetpack-source", + "wallet-private-material", + "settlement-private-payloads", + "secret-values" + ] + }, + { + "rowId": "preview:source-safe-assetpack-preview", + "purpose": "Create the source-safe AssetPack preview that exposes measurements, fit posture, roots, and delivery posture while source stays locked.", + "sourceRoots": [ + "packages/pipelines/asset-pack/src/asset-pack-preview-boundary.ts", + "packages/pipelines/asset-pack/src/read-need.ts", + "packages/pipelines/asset-pack/src/__tests__/asset-pack-preview-boundary.test.ts" + ], + "emittedTypes": [ + "AssetPackSourceSafePreview", + "AssetPackPreviewBoundary" + ], + "requiredEvidence": [ + "sourceSafePreview", + "AssetPackPreviewBoundary", + "sourceBearingDeliveryVisible" + ], + "rowRoot": "v42-readfitsfinding-preview-quote-row:6c78f5b53697900316d08828", + "sourceSafetyClass": "source_safe_readfitsfinding_preview_quote_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "rawProtectedPromptVisible": false, + "rawProviderResponseVisible": false, + "unpaidAssetPackSourceVisible": false, + "walletPrivateMaterialVisible": false, + "settlementPrivatePayloadVisible": false, + "credentialsSerialized": false, + "forbiddenPayloadClasses": [ + "protected-source-payloads", + "raw-protected-prompts", + "raw-provider-responses", + "unpaid-assetpack-source", + "wallet-private-material", + "settlement-private-payloads", + "secret-values" + ] + }, + { + "rowId": "quote:deterministic-share-to-fee-btc-quote", + "purpose": "Read back deterministic BTD/BTC quote calculation from measurement weight, measurement volume, admitted fit quality, dust floor, and minimum sats.", + "sourceRoots": [ + "packages/pipelines/asset-pack/src/read-need.ts", + "packages/pipelines/asset-pack/src/asset-pack-preview-boundary.ts", + "packages/pipelines/asset-pack/src/__tests__/asset-pack-preview-boundary.test.ts" + ], + "emittedTypes": [ + "ShareToFeeQuote", + "AssetPackPreviewQuoteReceipt" + ], + "requiredEvidence": [ + "sum(measurement.weight * measurement.volume * admitted_fit_quality)", + "deterministic", + "quoteRoot" + ], + "rowRoot": "v42-readfitsfinding-preview-quote-row:2cd3ae0f6b53015ff194183b", + "sourceSafetyClass": "source_safe_readfitsfinding_preview_quote_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "rawProtectedPromptVisible": false, + "rawProviderResponseVisible": false, + "unpaidAssetPackSourceVisible": false, + "walletPrivateMaterialVisible": false, + "settlementPrivatePayloadVisible": false, + "credentialsSerialized": false, + "forbiddenPayloadClasses": [ + "protected-source-payloads", + "raw-protected-prompts", + "raw-provider-responses", + "unpaid-assetpack-source", + "wallet-private-material", + "settlement-private-payloads", + "secret-values" + ] + }, + { + "rowId": "disclosure:no-pre-settlement-source", + "purpose": "Fail closed when preview, telemetry, route summary, or UI readback would expose protected source or unpaid AssetPack source before settlement.", + "sourceRoots": [ + "packages/pipelines/asset-pack/src/asset-pack-preview-boundary.ts", + "uapi/app/api/pipeline-harness/asset-pack/runner.ts", + "uapi/app/terminal/TerminalDepositReadWorkbench.tsx" + ], + "emittedTypes": [ + "AssetPackDisclosureReview", + "AssetPackPreviewBoundarySourceSafety" + ], + "requiredEvidence": [ + "protectedSourceVisible: false", + "unpaidAssetPackSourceVisible: false", + "credentialsSerialized: false" + ], + "rowRoot": "v42-readfitsfinding-preview-quote-row:6c6f576e66de92dfbfcb4b73", + "sourceSafetyClass": "source_safe_readfitsfinding_preview_quote_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "rawProtectedPromptVisible": false, + "rawProviderResponseVisible": false, + "unpaidAssetPackSourceVisible": false, + "walletPrivateMaterialVisible": false, + "settlementPrivatePayloadVisible": false, + "credentialsSerialized": false, + "forbiddenPayloadClasses": [ + "protected-source-payloads", + "raw-protected-prompts", + "raw-provider-responses", + "unpaid-assetpack-source", + "wallet-private-material", + "settlement-private-payloads", + "secret-values" + ] + }, + { + "rowId": "settlement:instructions-before-rights-transfer", + "purpose": "Expose source-safe settlement instructions and delivery lock before Gate 6 rights-transfer and repository delivery unlock.", + "sourceRoots": [ + "packages/pipelines/asset-pack/src/asset-pack-preview-boundary.ts", + "packages/pipelines/asset-pack/src/__tests__/asset-pack-preview-boundary.test.ts" + ], + "emittedTypes": [ + "AssetPackPreviewSettlementInstructions", + "AssetPackPreviewDeliveryPosture" + ], + "requiredEvidence": [ + "quote_ready_settlement_required", + "reader_wallet_authorized_before_broadcast", + "withheld_until_settlement" + ], + "rowRoot": "v42-readfitsfinding-preview-quote-row:3072bb82f264a8795871106d", + "sourceSafetyClass": "source_safe_readfitsfinding_preview_quote_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "rawProtectedPromptVisible": false, + "rawProviderResponseVisible": false, + "unpaidAssetPackSourceVisible": false, + "walletPrivateMaterialVisible": false, + "settlementPrivatePayloadVisible": false, + "credentialsSerialized": false, + "forbiddenPayloadClasses": [ + "protected-source-payloads", + "raw-protected-prompts", + "raw-provider-responses", + "unpaid-assetpack-source", + "wallet-private-material", + "settlement-private-payloads", + "secret-values" + ] + }, + { + "rowId": "route:harness-preview-quote-summary", + "purpose": "Summarize preview boundary, quote receipt, selected-fit provenance, settlement instructions, delivery posture, and disclosure review through the pipeline harness route without source payloads.", + "sourceRoots": [ + "uapi/app/api/pipeline-harness/asset-pack/runner.ts", + "uapi/tests/api/pipelineHarnessRoute.test.ts" + ], + "emittedTypes": [ + "assetPackPreviewBoundary", + "assetPackQuoteReceipt", + "assetPackSettlementInstructions", + "assetPackDeliveryPosture" + ], + "requiredEvidence": [ + "summarizeAssetPackPreviewBoundary", + "assetPackPreviewBoundary", + "storageRecordCount" + ], + "rowRoot": "v42-readfitsfinding-preview-quote-row:0ffb7be9b16813de0ef71096", + "sourceSafetyClass": "source_safe_readfitsfinding_preview_quote_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "rawProtectedPromptVisible": false, + "rawProviderResponseVisible": false, + "unpaidAssetPackSourceVisible": false, + "walletPrivateMaterialVisible": false, + "settlementPrivatePayloadVisible": false, + "credentialsSerialized": false, + "forbiddenPayloadClasses": [ + "protected-source-payloads", + "raw-protected-prompts", + "raw-provider-responses", + "unpaid-assetpack-source", + "wallet-private-material", + "settlement-private-payloads", + "secret-values" + ] + }, + { + "rowId": "ui:terminal-preview-quote-provenance-readback", + "purpose": "Render Terminal Finding Fits preview, quote, provenance, settlement, delivery, and replay roots as expandable source-safe metadata.", + "sourceRoots": [ + "uapi/app/terminal/TerminalDepositReadWorkbench.tsx", + "uapi/app/terminal/terminal-pipeline-harness-client.ts", + "uapi/tests/terminalPipelineHarnessClient.test.ts" + ], + "emittedTypes": [ + "assetPackPreviewBoundaryRows", + "TerminalReadFitsFindingSynthesisHarnessStreamSnapshot" + ], + "requiredEvidence": [ + "Finding Fits preview, quote, and provenance", + "quote_ready_settlement_required", + "withheld_until_settlement" + ], + "rowRoot": "v42-readfitsfinding-preview-quote-row:0291291e16e03e2e74707398", + "sourceSafetyClass": "source_safe_readfitsfinding_preview_quote_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "rawProtectedPromptVisible": false, + "rawProviderResponseVisible": false, + "unpaidAssetPackSourceVisible": false, + "walletPrivateMaterialVisible": false, + "settlementPrivatePayloadVisible": false, + "credentialsSerialized": false, + "forbiddenPayloadClasses": [ + "protected-source-payloads", + "raw-protected-prompts", + "raw-provider-responses", + "unpaid-assetpack-source", + "wallet-private-material", + "settlement-private-payloads", + "secret-values" + ] + }, + { + "rowId": "proof:tests-artifact-workflow", + "purpose": "Bind V42 Gate 5 closure to package tests, UAPI tests, protocol artifact tests, docs, scripts, workflows, and generated source-safe proof.", + "sourceRoots": [ + "packages/pipelines/asset-pack/src/__tests__/depository-search.test.ts", + "packages/pipelines/asset-pack/src/__tests__/read-fits-finding-runtime.test.ts", + "packages/pipelines/asset-pack/src/__tests__/asset-pack-preview-boundary.test.ts", + "uapi/tests/api/pipelineHarnessRoute.test.ts", + ".github/workflows/bitcode-gate-quality.yml", + ".github/workflows/bitcode-canon-quality.yml", + "BITCODE_SPEC_V42.md", + "BITCODE_SPEC_V42_PARITY_MATRIX.md", + "SPECIFICATIONS_ROADMAP.md" + ], + "emittedTypes": [ + "V42ReadFitsFindingPreviewQuote" + ], + "requiredEvidence": [ + "check-v42-gate5-readfitsfinding-preview-quote.mjs", + "v42-readfitsfinding-preview-quote" + ], + "rowRoot": "v42-readfitsfinding-preview-quote-row:b09ae61cf4664f8a3914be8e", + "sourceSafetyClass": "source_safe_readfitsfinding_preview_quote_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "rawProtectedPromptVisible": false, + "rawProviderResponseVisible": false, + "unpaidAssetPackSourceVisible": false, + "walletPrivateMaterialVisible": false, + "settlementPrivatePayloadVisible": false, + "credentialsSerialized": false, + "forbiddenPayloadClasses": [ + "protected-source-payloads", + "raw-protected-prompts", + "raw-provider-responses", + "unpaid-assetpack-source", + "wallet-private-material", + "settlement-private-payloads", + "secret-values" + ] + } + ], + "rowIds": [ + "admission:accepted-need-required", + "search:many-candidate-depository-discovery", + "search:multi-channel-vector-provider-tooling", + "ranking:candidate-ranking-and-thresholds", + "provenance:selected-fit-provenance", + "preview:source-safe-assetpack-preview", + "quote:deterministic-share-to-fee-btc-quote", + "disclosure:no-pre-settlement-source", + "settlement:instructions-before-rights-transfer", + "route:harness-preview-quote-summary", + "ui:terminal-preview-quote-provenance-readback", + "proof:tests-artifact-workflow" + ], + "predicateResults": [ + { + "id": "accepted-need-gates-finding-fits", + "sourcePath": "packages/pipelines/asset-pack/src/read-fits-finding-runtime.ts", + "passed": true + }, + { + "id": "search-defines-many-channel-ids", + "sourcePath": "packages/pipelines/asset-pack/src/depository-search.ts", + "passed": true + }, + { + "id": "search-defines-tool-ids", + "sourcePath": "packages/pipelines/asset-pack/src/depository-search.ts", + "passed": true + }, + { + "id": "search-covers-provider-embedding-policy", + "sourcePath": "packages/pipelines/asset-pack/src/depository-search.ts", + "passed": true + }, + { + "id": "search-tests-many-candidates", + "sourcePath": "packages/pipelines/asset-pack/src/__tests__/depository-search.test.ts", + "passed": true + }, + { + "id": "runtime-persists-selected-fit-provenance", + "sourcePath": "packages/pipelines/asset-pack/src/read-fits-finding-runtime.ts", + "passed": true + }, + { + "id": "runtime-telemetry-counts", + "sourcePath": "packages/pipelines/asset-pack/src/read-fits-finding-runtime.ts", + "passed": true + }, + { + "id": "runtime-tests-source-safe-counts", + "sourcePath": "packages/pipelines/asset-pack/src/__tests__/read-fits-finding-runtime.test.ts", + "passed": true + }, + { + "id": "preview-boundary-defines-quote-provenance-settlement-delivery", + "sourcePath": "packages/pipelines/asset-pack/src/asset-pack-preview-boundary.ts", + "passed": true + }, + { + "id": "preview-boundary-source-safety", + "sourcePath": "packages/pipelines/asset-pack/src/asset-pack-preview-boundary.ts", + "passed": true + }, + { + "id": "quote-formula-implemented", + "sourcePath": "packages/pipelines/asset-pack/src/read-need.ts", + "passed": true + }, + { + "id": "preview-tests-cover-determinism-and-storage-safety", + "sourcePath": "packages/pipelines/asset-pack/src/__tests__/asset-pack-preview-boundary.test.ts", + "passed": true + }, + { + "id": "package-postprocess-surfaces-boundary", + "sourcePath": "packages/pipelines/asset-pack/src/postprocess.ts", + "passed": true + }, + { + "id": "contract-keeps-readfitsfinding-topology", + "sourcePath": "packages/pipelines/asset-pack/src/reading-pipeline-contract.ts", + "passed": true + }, + { + "id": "harness-route-summarizes-preview-boundary", + "sourcePath": "uapi/app/api/pipeline-harness/asset-pack/runner.ts", + "passed": true + }, + { + "id": "harness-route-tests-preview-boundary-summary", + "sourcePath": "uapi/tests/api/pipelineHarnessRoute.test.ts", + "passed": true + }, + { + "id": "terminal-ui-renders-preview-boundary-rows", + "sourcePath": "uapi/app/terminal/TerminalDepositReadWorkbench.tsx", + "passed": true + }, + { + "id": "terminal-client-summarizes-boundary", + "sourcePath": "uapi/app/terminal/terminal-pipeline-harness-client.ts", + "passed": true + }, + { + "id": "terminal-client-tests-boundary-summary", + "sourcePath": "uapi/tests/terminalPipelineHarnessClient.test.ts", + "passed": true + }, + { + "id": "spec-gate5-expanded", + "sourcePath": "BITCODE_SPEC_V42.md", + "passed": true + }, + { + "id": "delta-gate5-expanded", + "sourcePath": "BITCODE_SPEC_V42_DELTA.md", + "passed": true + }, + { + "id": "notes-gate5-expanded", + "sourcePath": "BITCODE_SPEC_V42_NOTES.md", + "passed": true + }, + { + "id": "parity-gate5-implemented", + "sourcePath": "BITCODE_SPEC_V42_PARITY_MATRIX.md", + "passed": true + }, + { + "id": "roadmap-advanced-to-gate5", + "sourcePath": "SPECIFICATIONS_ROADMAP.md", + "passed": true + }, + { + "id": "readmes-document-gate5", + "sourcePath": "README.md", + "passed": true + }, + { + "id": "workflows-run-gate5-check", + "sourcePath": ".github/workflows/bitcode-gate-quality.yml", + "passed": true + } + ], + "coverage": { + "rowCount": 12, + "requiredPredicateCount": 26, + "passedPredicateCount": 26, + "failedPredicateIds": [], + "pipelineName": "ReadFitsFindingSynthesis", + "requiredPriorPipelineName": "ReadNeedComprehensionSynthesis", + "phaseCount": 7, + "agentCount": 8, + "ptrrStepCount": 32, + "failsafeSequenceCount": 96, + "thricifiedGenerationCount": 96, + "searchChannelIds": [ + "lexical", + "symbolic", + "path", + "metadata", + "measurement", + "embedding-vector", + "provider-specific" + ], + "selectedFitProvenanceRequired": true, + "sourceSafePreviewRequired": true, + "deterministicQuoteRequired": true, + "noProtectedSourceBeforeSettlement": true, + "settlementInstructionsRequired": true, + "terminalPreviewQuoteReadbackCovered": true, + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "rawProtectedPromptVisible": false, + "rawProviderResponseVisible": false, + "unpaidAssetPackSourceVisible": false, + "walletPrivateMaterialVisible": false, + "settlementPrivatePayloadVisible": false, + "credentialsSerialized": false, + "legacySourceRoots": false + }, + "sourceRoots": { + "depositorySearch": "packages/pipelines/asset-pack/src/depository-search.ts", + "readFitsFindingRuntime": "packages/pipelines/asset-pack/src/read-fits-finding-runtime.ts", + "assetPackPreviewBoundary": "packages/pipelines/asset-pack/src/asset-pack-preview-boundary.ts", + "readNeed": "packages/pipelines/asset-pack/src/read-need.ts", + "packageIndex": "packages/pipelines/asset-pack/src/index.ts", + "postprocess": "packages/pipelines/asset-pack/src/postprocess.ts", + "readingPipelineContract": "packages/pipelines/asset-pack/src/reading-pipeline-contract.ts", + "depositorySearchTest": "packages/pipelines/asset-pack/src/__tests__/depository-search.test.ts", + "runtimeTest": "packages/pipelines/asset-pack/src/__tests__/read-fits-finding-runtime.test.ts", + "previewBoundaryTest": "packages/pipelines/asset-pack/src/__tests__/asset-pack-preview-boundary.test.ts", + "postprocessTest": "packages/pipelines/asset-pack/src/__tests__/postprocess.test.ts", + "harnessRunner": "uapi/app/api/pipeline-harness/asset-pack/runner.ts", + "harnessRouteTest": "uapi/tests/api/pipelineHarnessRoute.test.ts", + "terminalWorkbench": "uapi/app/terminal/TerminalDepositReadWorkbench.tsx", + "terminalHarnessClient": "uapi/app/terminal/terminal-pipeline-harness-client.ts", + "terminalHarnessClientTest": "uapi/tests/terminalPipelineHarnessClient.test.ts", + "gateWorkflow": ".github/workflows/bitcode-gate-quality.yml", + "canonWorkflow": ".github/workflows/bitcode-canon-quality.yml", + "v42Spec": "BITCODE_SPEC_V42.md", + "v42Delta": "BITCODE_SPEC_V42_DELTA.md", + "v42Notes": "BITCODE_SPEC_V42_NOTES.md", + "v42Parity": "BITCODE_SPEC_V42_PARITY_MATRIX.md", + "roadmap": "SPECIFICATIONS_ROADMAP.md", + "rootReadme": "README.md", + "terminalReadme": "uapi/app/terminal/README.md", + "assetPackReadme": "packages/pipelines/asset-pack/README.md", + "protocolReadme": "packages/protocol/README.md" + } +} diff --git a/.github/workflows/bitcode-canon-quality.yml b/.github/workflows/bitcode-canon-quality.yml index 13e5d697..23a88608 100644 --- a/.github/workflows/bitcode-canon-quality.yml +++ b/.github/workflows/bitcode-canon-quality.yml @@ -304,6 +304,9 @@ jobs: if [ -f scripts/check-v42-gate4-readneed-review-resynthesis-product-closure.mjs ]; then node scripts/check-v42-gate4-readneed-review-resynthesis-product-closure.mjs --skip-branch-check --skip-package-tests --skip-uapi-tests fi + if [ -f scripts/check-v42-gate5-readfitsfinding-preview-quote.mjs ]; then + node scripts/check-v42-gate5-readfitsfinding-preview-quote.mjs --skip-branch-check --skip-package-tests --skip-uapi-tests + fi fi else echo "Unexpected BITCODE_SPEC.txt pointer: $POINTER" >&2 diff --git a/.github/workflows/bitcode-gate-quality.yml b/.github/workflows/bitcode-gate-quality.yml index 82dc5ab8..9c2a28c0 100644 --- a/.github/workflows/bitcode-gate-quality.yml +++ b/.github/workflows/bitcode-gate-quality.yml @@ -433,6 +433,9 @@ jobs: if [ -f scripts/check-v42-gate4-readneed-review-resynthesis-product-closure.mjs ]; then node scripts/check-v42-gate4-readneed-review-resynthesis-product-closure.mjs --skip-branch-check --skip-package-tests --skip-uapi-tests fi + if [ -f scripts/check-v42-gate5-readfitsfinding-preview-quote.mjs ]; then + node scripts/check-v42-gate5-readfitsfinding-preview-quote.mjs --skip-branch-check --skip-package-tests --skip-uapi-tests + fi fi else echo "Unexpected BITCODE_SPEC.txt pointer: $POINTER" >&2 diff --git a/BITCODE_SPEC_V42.md b/BITCODE_SPEC_V42.md index 8c1ce389..316527d3 100644 --- a/BITCODE_SPEC_V42.md +++ b/BITCODE_SPEC_V42.md @@ -140,6 +140,8 @@ The artifact binds `ReadNeedReviewResynthesisRuntime`, all four review actions, Gate 5 must make `ReadFitsFindingSynthesis` product-ready in the MVP flow. The pipeline must search the Depository for many candidates above threshold, rank candidates, select fits, synthesize source-safe AssetPack measurements and preview metadata, keep source-bearing content withheld, calculate deterministic BTD/BTC quote posture, and expose a reviewable preview. It must cover vector search and provider search tooling, selected-fit provenance, prompt/tool return types, telemetry, quote formula readback, source-safe UI, and failure/repair states. +Gate 5 is implemented by `.bitcode/v42-readfitsfinding-preview-quote.json`. +The artifact binds the accepted-Need gate, many-channel Depository search, candidate ranking, selected-fit provenance, `AssetPackPreviewBoundary`, deterministic share-to-fee quote receipt, disclosure review, settlement instructions, delivery lock, harness route summary, Terminal preview/quote/provenance readback, and the package/API/protocol tests that prove no protected source or unpaid AssetPack source crosses the pre-settlement boundary. ## V42 Gate 6 Settlement Rights Transfer And Repository Delivery Closure diff --git a/BITCODE_SPEC_V42_DELTA.md b/BITCODE_SPEC_V42_DELTA.md index 36e45942..177b9a08 100644 --- a/BITCODE_SPEC_V42_DELTA.md +++ b/BITCODE_SPEC_V42_DELTA.md @@ -52,6 +52,7 @@ Gate 4 now binds `ReadNeedReviewResynthesisRuntime` (`readNeedReviewRuntime` rou ### Gate 5: ReadFitsFinding AssetPack Preview And Quote Closure Implement and prove many-candidate Depository search, selected-fit provenance, source-safe AssetPack preview, deterministic BTD/BTC quote, and no pre-settlement source leakage. +Gate 5 now binds `ReadFitsFindingRuntime`, many-channel Depository search, `AssetPackPreviewBoundary`, quote receipt readback, selected-fit provenance, settlement instructions, delivery posture, harness evidence summaries, Terminal preview/quote/provenance rows, `.bitcode/v42-readfitsfinding-preview-quote.json`, and `check:v42-gate5`. ### Gate 6: Settlement Rights Transfer And Repository Delivery Closure diff --git a/BITCODE_SPEC_V42_NOTES.md b/BITCODE_SPEC_V42_NOTES.md index 098818f0..139f5316 100644 --- a/BITCODE_SPEC_V42_NOTES.md +++ b/BITCODE_SPEC_V42_NOTES.md @@ -61,6 +61,14 @@ V42 must make the preview valuable without leaking the source-bearing AssetPack Readers may see measurements, fit confidence, quote posture, selected-fit provenance summaries, proof roots, and source-safe explanations. They may not see protected source, raw provider responses, protected prompts, private settlement payloads, wallet private material, or unpaid AssetPack source before BTC settlement and BTD rights transfer. +## Gate 5 implementation notes + +Gate 5 closes the source-safe Finding Fits preview boundary. +`ReadFitsFindingSynthesis` must already have an accepted Need, then search many Depository candidates across lexical, symbolic, path, metadata, measurement, embedding-vector, and provider-specific channels. +Selected-fit provenance carries selected candidate ids, fit deposit ids, query and ranking roots, proof roots, measurement roots, and reconciliation readback roots. +`AssetPackPreviewBoundary` is the review object before payment: it contains the source-safe preview, deterministic quote receipt, disclosure review, settlement instructions, delivery lock, storage projection, replay receipt, and repair posture. +The route and Terminal readback expose those fields as metadata only; protected source and unpaid AssetPack source remain absent until Gate 6 settlement, rights transfer, and delivery unlock. + ## AI-reading demonstration note The standalone demonstration should prove why Bitcode matters for AI-dominant Reading. diff --git a/BITCODE_SPEC_V42_PARITY_MATRIX.md b/BITCODE_SPEC_V42_PARITY_MATRIX.md index b87b9c7c..65c010b7 100644 --- a/BITCODE_SPEC_V42_PARITY_MATRIX.md +++ b/BITCODE_SPEC_V42_PARITY_MATRIX.md @@ -36,7 +36,7 @@ This matrix records the reliable MVP product surfaces that must become promotion | Depositing shortest path | Source material can be admitted with Depository proof and compensation visibility | `.bitcode/v42-depositing-shortest-path.json`, `DepositorySupplyCompensationPreview`, `/api/deposits`, Terminal deposit readback | implemented | | Reading state machine | Five-step Reading UX is route-owned, persistent, and source-safe | `.bitcode/v42-reading-shortest-path-state-machine.json`, `TerminalEnterpriseReadingRouteState`, `readingStage`, route/retry/failure tests | implemented | | ReadNeed product closure | Need synthesis, review, feedback, resynthesis, accepted-Need admission, rejected Need blockers, source-safe telemetry, and Terminal runtime readback are product-ready | `.bitcode/v42-readneed-review-resynthesis-product-closure.json`, `ReadNeedReviewResynthesisRuntime`, `/api/read-review`, Terminal Need runtime readback | implemented | -| Finding Fits preview and quote | Many-candidate search, selected-fit provenance, source-safe preview, and quote are product-ready | later V42 Gate 5 artifact | draft-required | +| Finding Fits preview and quote | Many-candidate search, selected-fit provenance, source-safe preview, and quote are product-ready | `.bitcode/v42-readfitsfinding-preview-quote.json`, `ReadFitsFindingRuntime`, `AssetPackPreviewBoundary`, harness preview summary, Terminal preview/quote/provenance readback | implemented | | Settlement and delivery | BTC/BTD settlement, rights transfer, compensation, and repository PR delivery are synchronized | later V42 Gate 6 artifact | draft-required | | AI-reading demonstration | Standalone demonstration proves AssetPack improves AI beyond public-data-only baseline | later V42 Gate 7 artifact | draft-required | | Local/staging rehearsal | Full MVP path rehearsed locally and in staging-testnet with mainnet blocked | later V42 Gate 8 artifact | draft-required | @@ -50,7 +50,7 @@ This matrix records the reliable MVP product surfaces that must become promotion | Gate 2 | Depositing shortest path and compensation visibility artifact | implemented | | Gate 3 | Reading shortest path state machine artifact | implemented | | Gate 4 | ReadNeed review and resynthesis product closure artifact | implemented | -| Gate 5 | ReadFitsFinding AssetPack preview and quote closure artifact | draft-required | +| Gate 5 | ReadFitsFinding AssetPack preview and quote closure artifact | implemented | | Gate 6 | Settlement rights transfer and repository delivery closure artifact | draft-required | | Gate 7 | AI-reading dominant demonstration MVP artifact | draft-required | | Gate 8 | Local and staging-testnet full MVP rehearsal artifact | draft-required | diff --git a/README.md b/README.md index 48298c20..dbc40710 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,16 @@ rejected Need blockers, PTRR/Failsafe/Thricified telemetry receipts, Terminal Need runtime readback, `.bitcode/v42-readneed-review-resynthesis-product-closure.json`, and `check:v42-gate4`. +V42 Gate 5 adds Finding Fits preview and quote closure with +accepted-Need-gated `ReadFitsFindingRuntime`, many-channel Depository search, +selected-fit provenance, `AssetPackPreviewBoundary`, deterministic +share-to-fee quote receipts, disclosure review, settlement instructions, +delivery lock, harness evidence summaries, Terminal preview/quote/provenance +readback, `.bitcode/v42-readfitsfinding-preview-quote.json`, and +`check:v42-gate5`. The preview remains metadata-only before settlement: +protected source, unpaid AssetPack source, wallet private material, private +settlement payloads, credentials, raw protected prompts, and raw provider +responses stay out of route and UI readback. V43+ is roadmapped as the later agentic depositing evolution: repository agents synthesize deposit AssetPack options from connected enterprise code, Depository state, and Reading demand; enterprises approve or reject diff --git a/SPECIFICATIONS_ROADMAP.md b/SPECIFICATIONS_ROADMAP.md index 7bd3c34c..2d2fe120 100644 --- a/SPECIFICATIONS_ROADMAP.md +++ b/SPECIFICATIONS_ROADMAP.md @@ -5,13 +5,14 @@ - Current active canonical pointer: `BITCODE_SPEC.txt` -> `V41` - Current active canon: `BITCODE_SPEC_V41.md` - Current draft target: `BITCODE_SPEC_V42.md`. -- Current working gate: V42 Gate 4 ReadNeed Review And Resynthesis Product Closure. -- Next queued gate after V42 Gate 4: V42 Gate 5 ReadFitsFinding AssetPack Preview And Quote Closure. +- Current working gate: V42 Gate 5 ReadFitsFinding AssetPack Preview And Quote Closure. +- Next queued gate after V42 Gate 5: V42 Gate 6 Settlement Rights Transfer And Repository Delivery Closure. - Latest closed version: V41 Prompt And PromptPart Excellence, which promoted PromptPart and Prompt inventory, registry interpolation contracts, Reading prompt baselines, ReadNeedComprehensionSynthesis prompt hardening, ReadFitsFindingSynthesis prompt hardening, Conversation/tool/interface prompt rewrite, prompt benchmark telemetry, and V41 promotion readiness. - Recent V42 opening anchor: reliable MVP experience opens over promoted V41 with V42 SPEC, DELTA, NOTES, and PARITY files, `check:v42-gate1`, active V41 / draft V42 posture, and a nine-gate plan for shortest-path Depositing, five-step Reading, ReadNeed product closure, ReadFitsFinding preview and quote closure, settlement and repository delivery, AI-reading demonstration, local/staging rehearsal, and promotion readiness. - V42 Gate 2 closure anchor: reliable MVP experience now owns source-safe Depositing compensation visibility through `DepositorySupplyCompensationPreview`, deposit route `depositoryEvidence.compensationPreview`, deterministic `.bitcode/v42-depositing-shortest-path.json`, route/API readiness checks, source validation, Depository search/vector/storage projection, source-to-shares compensation readback keys, Terminal compensation roots, focused package/protocol tests, workflow wiring, and `check:v42-gate2`. - V42 Gate 3 closure anchor: reliable MVP experience now owns the five-step Reading shortest path state machine through `TerminalEnterpriseReadingUxState`, `TerminalEnterpriseReadingRouteState`, recoverable transaction ids, `readingStage` route hydration, retry/restart posture, source-safe failure repair actions, accepted-Need blockers, source-safe preview blockers, rich Reading pipeline telemetry readback, deterministic `.bitcode/v42-reading-shortest-path-state-machine.json`, focused route/component/protocol tests, workflow wiring, and `check:v42-gate3`. - V42 Gate 4 closure anchor: reliable MVP experience now owns ReadNeed product closure through `ReadNeedReviewResynthesisRuntime`, source-safe storage projection, reviewed Need feedback/resynthesis lineage, accepted-Need Finding Fits admission, rejected Need blockers, PTRR/Failsafe/Thricified telemetry receipts, Terminal Need runtime/storage/telemetry readback, deterministic `.bitcode/v42-readneed-review-resynthesis-product-closure.json`, focused package/route/protocol tests, workflow wiring, and `check:v42-gate4`. +- V42 Gate 5 closure anchor: reliable MVP experience now owns ReadFitsFinding preview and quote closure through accepted-Need-gated `ReadFitsFindingRuntime`, many-channel Depository search, selected-fit provenance, `AssetPackPreviewBoundary`, deterministic share-to-fee quote receipts, disclosure review, settlement instructions, delivery lock, harness evidence summaries, Terminal preview/quote/provenance readback, deterministic `.bitcode/v42-readfitsfinding-preview-quote.json`, focused package/route/protocol tests, workflow wiring, and `check:v42-gate5`. - Recent V41 closure anchor: V41 canonical promotion updated `BITCODE_SPEC.txt` to `V41`, generated `BITCODE_SPEC_V41_PROVEN.md`, preserved active V41 / draft V42 runtime posture, and closed prompt-program excellence canon. - Recent V40 closure anchor: V40 canonical promotion updated `BITCODE_SPEC.txt` to `V40`, generated `BITCODE_SPEC_V40_PROVEN.md`, preserved active V40 / draft V41 runtime posture, and closed exhaustive commercial application testing canon. - Recent V39 closure anchor: V39 canonical promotion updated `BITCODE_SPEC.txt` to `V39`, generated `BITCODE_SPEC_V39_PROVEN.md`, preserved active V39 / draft V40 runtime posture, and closed commercial Reading readiness canon. diff --git a/package.json b/package.json index 4cfaeee3..6593a435 100644 --- a/package.json +++ b/package.json @@ -313,6 +313,9 @@ "generate:v42-readneed-review-resynthesis-product-closure": "node scripts/generate-v42-readneed-review-resynthesis-product-closure.mjs", "check:v42-readneed-review-resynthesis-product-closure": "node scripts/generate-v42-readneed-review-resynthesis-product-closure.mjs --check", "check:v42-gate4": "node scripts/check-v42-gate4-readneed-review-resynthesis-product-closure.mjs", + "generate:v42-readfitsfinding-preview-quote": "node scripts/generate-v42-readfitsfinding-preview-quote.mjs", + "check:v42-readfitsfinding-preview-quote": "node scripts/generate-v42-readfitsfinding-preview-quote.mjs --check", + "check:v42-gate5": "node scripts/check-v42-gate5-readfitsfinding-preview-quote.mjs", "generate:v38-inference-surface-inventory": "node scripts/generate-v38-inference-surface-inventory.mjs", "check:v38-inference-surface-inventory": "node scripts/generate-v38-inference-surface-inventory.mjs --check", "check:v38-gate2": "node scripts/check-v38-gate2-inference-surface-inventory.mjs", diff --git a/packages/pipelines/asset-pack/README.md b/packages/pipelines/asset-pack/README.md index 051e9e77..9d328152 100644 --- a/packages/pipelines/asset-pack/README.md +++ b/packages/pipelines/asset-pack/README.md @@ -185,6 +185,14 @@ wallet private material, private settlement payloads, credentials, or unpaid source-bearing AssetPack content. Pull-request delivery remains withheld until BTC settlement, BTD rights transfer, and ledger/database/storage readback agree. +V42 Gate 5 binds this boundary into product closure through +`.bitcode/v42-readfitsfinding-preview-quote.json` and `check:v42-gate5`. +That proof requires accepted-Need admission, many-channel Depository search, +candidate ranking, selected-fit provenance, deterministic quote receipts, +source-safe disclosure review, settlement instructions, delivery lock, harness +route summaries, Terminal preview/quote/provenance readback, and focused +package/API/protocol tests before the paid boundary can proceed to Gate 6. + ## Settlement Rights Delivery `AssetPackSettlementRightsDeliveryBoundary` is the paid-boundary package diff --git a/packages/pipelines/asset-pack/src/__tests__/asset-pack-preview-boundary.test.ts b/packages/pipelines/asset-pack/src/__tests__/asset-pack-preview-boundary.test.ts index 03287d3c..a0a641e5 100644 --- a/packages/pipelines/asset-pack/src/__tests__/asset-pack-preview-boundary.test.ts +++ b/packages/pipelines/asset-pack/src/__tests__/asset-pack-preview-boundary.test.ts @@ -191,9 +191,15 @@ describe('AssetPack preview quote boundary', () => { expect(boundary.quoteReceipt.quoteRoot).toBe(boundary.sourceSafePreview.feeQuote.quoteRoot); expect(boundary.quoteReceipt.sats).toBeGreaterThanOrEqual(546); expect(boundary.quoteReceipt.quoteRoot).toBe(repeated.quoteReceipt.quoteRoot); + expect(boundary.quoteReceipt.deterministic).toBe(true); + expect(boundary.quoteReceipt.weightedAdmittedVolume).toBe(boundary.sourceSafePreview.feeQuote.weightedAdmittedVolume); + expect(boundary.settlementInstructions.sats).toBe(boundary.quoteReceipt.sats); + expect(boundary.settlementInstructions.quoteRoot).toBe(boundary.quoteReceipt.quoteRoot); expect(boundary.proofRoots.boundaryRoot).toBe(repeated.proofRoots.boundaryRoot); expect(boundary.selectedFitProvenance.selectedCandidates).toHaveLength(2); expect(boundary.selectedFitProvenance.selectedCandidates[0].proofRoot).toBe('sha256:proof-1'); + expect(boundary.storageProjection.every((record) => record.sourceSafety.sourceSafeMetadataOnly)).toBe(true); + expect(boundary.storageProjection.every((record) => record.sourceSafety.unpaidAssetPackSourceVisible === false)).toBe(true); expect(boundary.replayReceipt.verified).toEqual({ quoteRootMatchesPreview: true, disclosureReviewRootMatchesReview: true, diff --git a/packages/pipelines/asset-pack/src/__tests__/read-fits-finding-runtime.test.ts b/packages/pipelines/asset-pack/src/__tests__/read-fits-finding-runtime.test.ts index 9a2bcfcf..d68fbfce 100644 --- a/packages/pipelines/asset-pack/src/__tests__/read-fits-finding-runtime.test.ts +++ b/packages/pipelines/asset-pack/src/__tests__/read-fits-finding-runtime.test.ts @@ -181,6 +181,10 @@ describe('ReadFitsFinding runtime, ranking, and replay', () => { 'telemetry_receipt', ]), ); + expect(runtime.storageProjection.every((record) => record.sourceSafety.protectedSourceVisible === false)).toBe(true); + expect(runtime.storageProjection.every((record) => record.sourceSafety.rawProviderResponseVisible === false)).toBe(true); + expect(runtime.searchSummary.selectedCandidateCount).toBe(2); + expect(runtime.searchSummary.fitDepositAssetIds).toEqual(['fit-deposit-runtime-1', 'fit-deposit-runtime-2']); expect(runtime.sourceSafety).toMatchObject({ sourceSafetyClass: 'source_safe_read_fits_finding_runtime_metadata', protectedSourceVisible: false, diff --git a/packages/protocol/README.md b/packages/protocol/README.md index a32d34a9..1fa40812 100644 --- a/packages/protocol/README.md +++ b/packages/protocol/README.md @@ -210,6 +210,16 @@ source-safe Read Request and Need storage, feedback lineage, Need measurement, accepted-Need admission, rejected Need blockers, PTRR/Failsafe/Thricified telemetry receipts, `/api/read-review` action coverage, Terminal runtime readback, and source-safe disclosure boundaries. +V42 Gate 5 adds `V42ReadFitsFindingPreviewQuote` through +`packages/protocol/src/canonical/v42-readfitsfinding-preview-quote.js`, +`packages/protocol/test/v42-readfitsfinding-preview-quote.test.js`, +`.bitcode/v42-readfitsfinding-preview-quote.json`, and `check:v42-gate5`. +It proves accepted-Need-gated Finding Fits, many-channel Depository search, +candidate ranking, selected-fit provenance, source-safe AssetPack preview, +deterministic share-to-fee quote receipts, disclosure review, settlement +instructions, delivery lock, harness route summaries, Terminal +preview/quote/provenance readback, and no pre-settlement protected source or +unpaid AssetPack source exposure. V40 Gate 2 adds `V40TestInventoryCoverageMatrix` through `packages/protocol/src/canonical/v40-test-inventory-coverage-matrix.js`, `packages/protocol/test/v40-test-inventory-coverage-matrix.test.js`, diff --git a/packages/protocol/src/canonical/v42-readfitsfinding-preview-quote.js b/packages/protocol/src/canonical/v42-readfitsfinding-preview-quote.js new file mode 100644 index 00000000..577db1f0 --- /dev/null +++ b/packages/protocol/src/canonical/v42-readfitsfinding-preview-quote.js @@ -0,0 +1,343 @@ +// @ts-check + +import crypto from 'node:crypto'; +import { existsSync, readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const DEFAULT_REPO_ROOT = path.resolve(__dirname, '..', '..', '..', '..'); + +export const V42_READFITSFINDING_PREVIEW_QUOTE_ARTIFACT_PATH = + '.bitcode/v42-readfitsfinding-preview-quote.json'; +export const V42_READFITSFINDING_PREVIEW_QUOTE_SCHEMA_ID = + 'bitcode.v42.readFitsFindingPreviewQuote.v1'; +export const V42_READFITSFINDING_PREVIEW_QUOTE_VERSION = 'V42'; +export const V42_READFITSFINDING_PREVIEW_QUOTE_CURRENT_TARGET = 'V41'; +export const V42_READFITSFINDING_PREVIEW_QUOTE_SOURCE_SAFETY_VERDICT = + 'source-safe-readfitsfinding-preview-quote-metadata'; + +export const V42_READFITSFINDING_PREVIEW_QUOTE_ROW_IDS = Object.freeze([ + 'admission:accepted-need-required', + 'search:many-candidate-depository-discovery', + 'search:multi-channel-vector-provider-tooling', + 'ranking:candidate-ranking-and-thresholds', + 'provenance:selected-fit-provenance', + 'preview:source-safe-assetpack-preview', + 'quote:deterministic-share-to-fee-btc-quote', + 'disclosure:no-pre-settlement-source', + 'settlement:instructions-before-rights-transfer', + 'route:harness-preview-quote-summary', + 'ui:terminal-preview-quote-provenance-readback', + 'proof:tests-artifact-workflow', +]); + +const SOURCE_ROOTS = Object.freeze({ + depositorySearch: 'packages/pipelines/asset-pack/src/depository-search.ts', + readFitsFindingRuntime: 'packages/pipelines/asset-pack/src/read-fits-finding-runtime.ts', + assetPackPreviewBoundary: 'packages/pipelines/asset-pack/src/asset-pack-preview-boundary.ts', + readNeed: 'packages/pipelines/asset-pack/src/read-need.ts', + packageIndex: 'packages/pipelines/asset-pack/src/index.ts', + postprocess: 'packages/pipelines/asset-pack/src/postprocess.ts', + readingPipelineContract: 'packages/pipelines/asset-pack/src/reading-pipeline-contract.ts', + depositorySearchTest: 'packages/pipelines/asset-pack/src/__tests__/depository-search.test.ts', + runtimeTest: 'packages/pipelines/asset-pack/src/__tests__/read-fits-finding-runtime.test.ts', + previewBoundaryTest: 'packages/pipelines/asset-pack/src/__tests__/asset-pack-preview-boundary.test.ts', + postprocessTest: 'packages/pipelines/asset-pack/src/__tests__/postprocess.test.ts', + harnessRunner: 'uapi/app/api/pipeline-harness/asset-pack/runner.ts', + harnessRouteTest: 'uapi/tests/api/pipelineHarnessRoute.test.ts', + terminalWorkbench: 'uapi/app/terminal/TerminalDepositReadWorkbench.tsx', + terminalHarnessClient: 'uapi/app/terminal/terminal-pipeline-harness-client.ts', + terminalHarnessClientTest: 'uapi/tests/terminalPipelineHarnessClient.test.ts', + gateWorkflow: '.github/workflows/bitcode-gate-quality.yml', + canonWorkflow: '.github/workflows/bitcode-canon-quality.yml', + v42Spec: 'BITCODE_SPEC_V42.md', + v42Delta: 'BITCODE_SPEC_V42_DELTA.md', + v42Notes: 'BITCODE_SPEC_V42_NOTES.md', + v42Parity: 'BITCODE_SPEC_V42_PARITY_MATRIX.md', + roadmap: 'SPECIFICATIONS_ROADMAP.md', + rootReadme: 'README.md', + terminalReadme: 'uapi/app/terminal/README.md', + assetPackReadme: 'packages/pipelines/asset-pack/README.md', + protocolReadme: 'packages/protocol/README.md', +}); + +const SEARCH_CHANNEL_IDS = Object.freeze([ + 'lexical', + 'symbolic', + 'path', + 'metadata', + 'measurement', + 'embedding-vector', + 'provider-specific', +]); + +const FORBIDDEN_PAYLOAD_CLASSES = Object.freeze([ + 'protected-source-payloads', + 'raw-protected-prompts', + 'raw-provider-responses', + 'unpaid-assetpack-source', + 'wallet-private-material', + 'settlement-private-payloads', + 'secret-values', +]); + +function digest(value) { + return crypto.createHash('sha256').update(value).digest('hex').slice(0, 24); +} + +function rowRoot(id) { + return `v42-readfitsfinding-preview-quote-row:${digest(id)}`; +} + +function readSource(repoRoot, sourcePath) { + const absolutePath = path.join(repoRoot, sourcePath); + return existsSync(absolutePath) ? readFileSync(absolutePath, 'utf8') : ''; +} + +function predicateResult(id, sourcePath, passed) { + return { id, sourcePath, passed: Boolean(passed) }; +} + +function row(input) { + return { + ...input, + rowRoot: rowRoot(input.rowId), + sourceSafetyClass: 'source_safe_readfitsfinding_preview_quote_metadata', + sourceSafeMetadataOnly: true, + protectedSourceVisible: false, + rawProtectedPromptVisible: false, + rawProviderResponseVisible: false, + unpaidAssetPackSourceVisible: false, + walletPrivateMaterialVisible: false, + settlementPrivatePayloadVisible: false, + credentialsSerialized: false, + forbiddenPayloadClasses: [...FORBIDDEN_PAYLOAD_CLASSES], + }; +} + +export const V42_READFITSFINDING_PREVIEW_QUOTE_ROWS = Object.freeze([ + row({ + rowId: 'admission:accepted-need-required', + purpose: + 'Require an accepted ReadNeedComprehensionSynthesis Need before ReadFitsFindingSynthesis can search or preview an AssetPack.', + sourceRoots: [SOURCE_ROOTS.readNeed, SOURCE_ROOTS.readFitsFindingRuntime, SOURCE_ROOTS.terminalWorkbench], + emittedTypes: ['ReadFitsFindingAdmission', 'accepted_need_admission'], + requiredEvidence: ['accepted_read_need_missing', 'findingFitsAdmission', 'acceptedNeed'], + }), + row({ + rowId: 'search:many-candidate-depository-discovery', + purpose: + 'Search Depository AssetPack supply for many candidates above threshold before selecting the fit set.', + sourceRoots: [SOURCE_ROOTS.depositorySearch, SOURCE_ROOTS.depositorySearchTest], + emittedTypes: ['DepositorySearchResult', 'DepositoryCandidate'], + requiredEvidence: ['searchedAssetCount', 'candidateRanking', 'maxSelectedCandidates'], + }), + row({ + rowId: 'search:multi-channel-vector-provider-tooling', + purpose: + 'Bind lexical, symbolic, path, metadata, measurement, embedding-vector, and provider-specific search channels to source-safe tool receipts.', + sourceRoots: [SOURCE_ROOTS.depositorySearch, SOURCE_ROOTS.readFitsFindingRuntime], + emittedTypes: ['ReadFitsFindingSynthesisSearchReceipt', 'DepositorySearchQueryPlan'], + requiredEvidence: [...SEARCH_CHANNEL_IDS, 'ReadFitsFindingSynthesis.tool.vector-depository-search'], + }), + row({ + rowId: 'ranking:candidate-ranking-and-thresholds', + purpose: + 'Rank candidates with deterministic scores, thresholds, blockers, rejected counts, and proof/measurement requirements.', + sourceRoots: [SOURCE_ROOTS.depositorySearch, SOURCE_ROOTS.depositorySearchTest], + emittedTypes: ['DepositoryCandidateRanking', 'DepositoryCandidateFitEvidence'], + requiredEvidence: ['finalScore', 'semanticScore', 'proofScore', 'measurementScore', 'rejectedCandidateCount'], + }), + row({ + rowId: 'provenance:selected-fit-provenance', + purpose: + 'Persist selected fit provenance with selected candidate ids, fit deposit ids, proof roots, measurement roots, and reconciliation roots.', + sourceRoots: [SOURCE_ROOTS.readFitsFindingRuntime, SOURCE_ROOTS.assetPackPreviewBoundary, SOURCE_ROOTS.runtimeTest], + emittedTypes: ['selected_fit_provenance', 'AssetPackPreviewSelectedFitProvenance'], + requiredEvidence: ['selectedFitProvenanceRoot', 'fitDepositAssetIds', 'reconciliationReadbackRoot'], + }), + row({ + rowId: 'preview:source-safe-assetpack-preview', + purpose: + 'Create the source-safe AssetPack preview that exposes measurements, fit posture, roots, and delivery posture while source stays locked.', + sourceRoots: [SOURCE_ROOTS.assetPackPreviewBoundary, SOURCE_ROOTS.readNeed, SOURCE_ROOTS.previewBoundaryTest], + emittedTypes: ['AssetPackSourceSafePreview', 'AssetPackPreviewBoundary'], + requiredEvidence: ['sourceSafePreview', 'AssetPackPreviewBoundary', 'sourceBearingDeliveryVisible'], + }), + row({ + rowId: 'quote:deterministic-share-to-fee-btc-quote', + purpose: + 'Read back deterministic BTD/BTC quote calculation from measurement weight, measurement volume, admitted fit quality, dust floor, and minimum sats.', + sourceRoots: [SOURCE_ROOTS.readNeed, SOURCE_ROOTS.assetPackPreviewBoundary, SOURCE_ROOTS.previewBoundaryTest], + emittedTypes: ['ShareToFeeQuote', 'AssetPackPreviewQuoteReceipt'], + requiredEvidence: ['sum(measurement.weight * measurement.volume * admitted_fit_quality)', 'deterministic', 'quoteRoot'], + }), + row({ + rowId: 'disclosure:no-pre-settlement-source', + purpose: + 'Fail closed when preview, telemetry, route summary, or UI readback would expose protected source or unpaid AssetPack source before settlement.', + sourceRoots: [SOURCE_ROOTS.assetPackPreviewBoundary, SOURCE_ROOTS.harnessRunner, SOURCE_ROOTS.terminalWorkbench], + emittedTypes: ['AssetPackDisclosureReview', 'AssetPackPreviewBoundarySourceSafety'], + requiredEvidence: ['protectedSourceVisible: false', 'unpaidAssetPackSourceVisible: false', 'credentialsSerialized: false'], + }), + row({ + rowId: 'settlement:instructions-before-rights-transfer', + purpose: + 'Expose source-safe settlement instructions and delivery lock before Gate 6 rights-transfer and repository delivery unlock.', + sourceRoots: [SOURCE_ROOTS.assetPackPreviewBoundary, SOURCE_ROOTS.previewBoundaryTest], + emittedTypes: ['AssetPackPreviewSettlementInstructions', 'AssetPackPreviewDeliveryPosture'], + requiredEvidence: ['quote_ready_settlement_required', 'reader_wallet_authorized_before_broadcast', 'withheld_until_settlement'], + }), + row({ + rowId: 'route:harness-preview-quote-summary', + purpose: + 'Summarize preview boundary, quote receipt, selected-fit provenance, settlement instructions, delivery posture, and disclosure review through the pipeline harness route without source payloads.', + sourceRoots: [SOURCE_ROOTS.harnessRunner, SOURCE_ROOTS.harnessRouteTest], + emittedTypes: ['assetPackPreviewBoundary', 'assetPackQuoteReceipt', 'assetPackSettlementInstructions', 'assetPackDeliveryPosture'], + requiredEvidence: ['summarizeAssetPackPreviewBoundary', 'assetPackPreviewBoundary', 'storageRecordCount'], + }), + row({ + rowId: 'ui:terminal-preview-quote-provenance-readback', + purpose: + 'Render Terminal Finding Fits preview, quote, provenance, settlement, delivery, and replay roots as expandable source-safe metadata.', + sourceRoots: [SOURCE_ROOTS.terminalWorkbench, SOURCE_ROOTS.terminalHarnessClient, SOURCE_ROOTS.terminalHarnessClientTest], + emittedTypes: ['assetPackPreviewBoundaryRows', 'TerminalReadFitsFindingSynthesisHarnessStreamSnapshot'], + requiredEvidence: ['Finding Fits preview, quote, and provenance', 'quote_ready_settlement_required', 'withheld_until_settlement'], + }), + row({ + rowId: 'proof:tests-artifact-workflow', + purpose: + 'Bind V42 Gate 5 closure to package tests, UAPI tests, protocol artifact tests, docs, scripts, workflows, and generated source-safe proof.', + sourceRoots: [ + SOURCE_ROOTS.depositorySearchTest, + SOURCE_ROOTS.runtimeTest, + SOURCE_ROOTS.previewBoundaryTest, + SOURCE_ROOTS.harnessRouteTest, + SOURCE_ROOTS.gateWorkflow, + SOURCE_ROOTS.canonWorkflow, + SOURCE_ROOTS.v42Spec, + SOURCE_ROOTS.v42Parity, + SOURCE_ROOTS.roadmap, + ], + emittedTypes: ['V42ReadFitsFindingPreviewQuote'], + requiredEvidence: ['check-v42-gate5-readfitsfinding-preview-quote.mjs', 'v42-readfitsfinding-preview-quote'], + }), +]); + +function buildPredicateResults(repoRoot) { + const depositorySearch = readSource(repoRoot, SOURCE_ROOTS.depositorySearch); + const runtime = readSource(repoRoot, SOURCE_ROOTS.readFitsFindingRuntime); + const previewBoundary = readSource(repoRoot, SOURCE_ROOTS.assetPackPreviewBoundary); + const readNeed = readSource(repoRoot, SOURCE_ROOTS.readNeed); + const packageIndex = readSource(repoRoot, SOURCE_ROOTS.packageIndex); + const postprocess = readSource(repoRoot, SOURCE_ROOTS.postprocess); + const contract = readSource(repoRoot, SOURCE_ROOTS.readingPipelineContract); + const depositorySearchTest = readSource(repoRoot, SOURCE_ROOTS.depositorySearchTest); + const runtimeTest = readSource(repoRoot, SOURCE_ROOTS.runtimeTest); + const previewBoundaryTest = readSource(repoRoot, SOURCE_ROOTS.previewBoundaryTest); + const postprocessTest = readSource(repoRoot, SOURCE_ROOTS.postprocessTest); + const harnessRunner = readSource(repoRoot, SOURCE_ROOTS.harnessRunner); + const harnessRouteTest = readSource(repoRoot, SOURCE_ROOTS.harnessRouteTest); + const terminalWorkbench = readSource(repoRoot, SOURCE_ROOTS.terminalWorkbench); + const terminalHarnessClient = readSource(repoRoot, SOURCE_ROOTS.terminalHarnessClient); + const terminalHarnessClientTest = readSource(repoRoot, SOURCE_ROOTS.terminalHarnessClientTest); + const gateWorkflow = readSource(repoRoot, SOURCE_ROOTS.gateWorkflow); + const canonWorkflow = readSource(repoRoot, SOURCE_ROOTS.canonWorkflow); + const spec = readSource(repoRoot, SOURCE_ROOTS.v42Spec); + const delta = readSource(repoRoot, SOURCE_ROOTS.v42Delta); + const notes = readSource(repoRoot, SOURCE_ROOTS.v42Notes); + const parity = readSource(repoRoot, SOURCE_ROOTS.v42Parity); + const roadmap = readSource(repoRoot, SOURCE_ROOTS.roadmap); + const rootReadme = readSource(repoRoot, SOURCE_ROOTS.rootReadme); + const terminalReadme = readSource(repoRoot, SOURCE_ROOTS.terminalReadme); + const assetPackReadme = readSource(repoRoot, SOURCE_ROOTS.assetPackReadme); + const protocolReadme = readSource(repoRoot, SOURCE_ROOTS.protocolReadme); + + return [ + predicateResult('accepted-need-gates-finding-fits', SOURCE_ROOTS.readFitsFindingRuntime, runtime.includes('admitReadFitsFinding') && runtime.includes('accepted_need_admission') && runtime.includes('accept_read_need')), + predicateResult('search-defines-many-channel-ids', SOURCE_ROOTS.depositorySearch, SEARCH_CHANNEL_IDS.every((id) => depositorySearch.includes(`'${id}'`))), + predicateResult('search-defines-tool-ids', SOURCE_ROOTS.depositorySearch, depositorySearch.includes('ReadFitsFindingSynthesis.tool.lexical-depository-search') && depositorySearch.includes('ReadFitsFindingSynthesis.tool.vector-depository-search')), + predicateResult('search-covers-provider-embedding-policy', SOURCE_ROOTS.depositorySearch, depositorySearch.includes('embeddingPolicy') && depositorySearch.includes('providerIds') && depositorySearch.includes('selectedFitProvenanceRoot')), + predicateResult('search-tests-many-candidates', SOURCE_ROOTS.depositorySearchTest, depositorySearchTest.includes('discovers every qualifying fit deposit') && depositorySearchTest.includes('selectedFitProvenanceRoot')), + predicateResult('runtime-persists-selected-fit-provenance', SOURCE_ROOTS.readFitsFindingRuntime, runtime.includes('selected_fit_provenance') && runtime.includes('ReadFitsFindingReplayReceipt') && runtime.includes('persistReadFitsFindingRuntime')), + predicateResult('runtime-telemetry-counts', SOURCE_ROOTS.readFitsFindingRuntime, runtime.includes('phaseIds') && runtime.includes('failsafeSequenceIds') && runtime.includes('thricifiedGenerationIds')), + predicateResult('runtime-tests-source-safe-counts', SOURCE_ROOTS.runtimeTest, runtimeTest.includes('source_safe_read_fits_finding_runtime_metadata') && runtimeTest.includes('thricifiedGenerationIds') && runtimeTest.includes('fitDepositAssetIds')), + predicateResult('preview-boundary-defines-quote-provenance-settlement-delivery', SOURCE_ROOTS.assetPackPreviewBoundary, previewBoundary.includes('AssetPackPreviewQuoteReceipt') && previewBoundary.includes('AssetPackPreviewSelectedFitProvenance') && previewBoundary.includes('AssetPackPreviewSettlementInstructions') && previewBoundary.includes('AssetPackPreviewDeliveryPosture')), + predicateResult('preview-boundary-source-safety', SOURCE_ROOTS.assetPackPreviewBoundary, previewBoundary.includes('source_safe_assetpack_preview_quote_boundary') && previewBoundary.includes('unpaidAssetPackSourceVisible: false') && previewBoundary.includes('walletPrivateMaterialVisible: false')), + predicateResult('quote-formula-implemented', SOURCE_ROOTS.readNeed, readNeed.includes('sum(measurement.weight * measurement.volume * admitted_fit_quality)') && readNeed.includes('weightedAdmittedVolume') && readNeed.includes('minimumSats')), + predicateResult('preview-tests-cover-determinism-and-storage-safety', SOURCE_ROOTS.previewBoundaryTest, previewBoundaryTest.includes('deterministic') && previewBoundaryTest.includes('quoteRoot') && previewBoundaryTest.includes('unpaidAssetPackSourceVisible')), + predicateResult('package-postprocess-surfaces-boundary', SOURCE_ROOTS.postprocess, packageIndex.includes('assetPackPreviewBoundary') && postprocess.includes('assetPackQuoteReceipt') && postprocessTest.includes('assetPackPreviewBoundary')), + predicateResult('contract-keeps-readfitsfinding-topology', SOURCE_ROOTS.readingPipelineContract, contract.includes('ReadFitsFindingSynthesis.discovery') && contract.includes('ptrrStepCount') && contract.includes('thricifiedGenerationCount')), + predicateResult('harness-route-summarizes-preview-boundary', SOURCE_ROOTS.harnessRunner, harnessRunner.includes('summarizeAssetPackPreviewBoundary') && harnessRunner.includes('assetPackQuoteReceipt') && harnessRunner.includes('assetPackSettlementInstructions') && harnessRunner.includes('assetPackDeliveryPosture')), + predicateResult('harness-route-tests-preview-boundary-summary', SOURCE_ROOTS.harnessRouteTest, harnessRouteTest.includes('asset-pack-preview-boundary-route-test') && harnessRouteTest.includes('quote_ready_settlement_required') && harnessRouteTest.includes('storageRecordCount')), + predicateResult('terminal-ui-renders-preview-boundary-rows', SOURCE_ROOTS.terminalWorkbench, terminalWorkbench.includes('assetPackPreviewBoundaryRows') && terminalWorkbench.includes('Finding Fits preview, quote, and provenance') && terminalWorkbench.includes('assetPackSettlementInstructions')), + predicateResult('terminal-client-summarizes-boundary', SOURCE_ROOTS.terminalHarnessClient, terminalHarnessClient.includes('boundaryQuoteReceipt') && terminalHarnessClient.includes('boundarySelectedFitProvenance') && terminalHarnessClient.includes('boundaryDeliveryPosture')), + predicateResult('terminal-client-tests-boundary-summary', SOURCE_ROOTS.terminalHarnessClientTest, terminalHarnessClientTest.includes('assetPackPreviewBoundary') && terminalHarnessClientTest.includes('delivery withheld_until_settlement')), + predicateResult('spec-gate5-expanded', SOURCE_ROOTS.v42Spec, spec.includes('V42 Gate 5') && spec.includes('v42-readfitsfinding-preview-quote')), + predicateResult('delta-gate5-expanded', SOURCE_ROOTS.v42Delta, delta.includes('Gate 5') && delta.includes('source-safe AssetPack preview')), + predicateResult('notes-gate5-expanded', SOURCE_ROOTS.v42Notes, notes.includes('Gate 5 implementation notes') && notes.includes('selected-fit provenance')), + predicateResult('parity-gate5-implemented', SOURCE_ROOTS.v42Parity, parity.includes('Finding Fits preview and quote') && parity.includes('implemented')), + predicateResult('roadmap-advanced-to-gate5', SOURCE_ROOTS.roadmap, roadmap.includes('V42 Gate 5 closure anchor') && roadmap.includes('AssetPackPreviewBoundary')), + predicateResult('readmes-document-gate5', SOURCE_ROOTS.rootReadme, rootReadme.includes('V42 Gate 5') && terminalReadme.includes('Finding Fits preview') && assetPackReadme.includes('V42 Gate 5') && protocolReadme.includes('V42ReadFitsFindingPreviewQuote')), + predicateResult('workflows-run-gate5-check', SOURCE_ROOTS.gateWorkflow, gateWorkflow.includes('check-v42-gate5-readfitsfinding-preview-quote.mjs') && canonWorkflow.includes('check-v42-gate5-readfitsfinding-preview-quote.mjs')), + ]; +} + +export function buildV42ReadFitsFindingPreviewQuote(options = {}) { + const repoRoot = options.repoRoot || DEFAULT_REPO_ROOT; + const predicateResults = buildPredicateResults(repoRoot); + const failedPredicateIds = predicateResults + .filter((predicate) => !predicate.passed) + .map((predicate) => predicate.id); + const rowRoots = V42_READFITSFINDING_PREVIEW_QUOTE_ROWS.map((item) => item.rowRoot); + const artifactRoot = `v42-readfitsfinding-preview-quote:${digest(JSON.stringify({ + rowRoots, + failedPredicateIds, + }))}`; + + return { + artifactId: 'v42-readfitsfinding-preview-quote', + schemaId: V42_READFITSFINDING_PREVIEW_QUOTE_SCHEMA_ID, + version: V42_READFITSFINDING_PREVIEW_QUOTE_VERSION, + currentTarget: V42_READFITSFINDING_PREVIEW_QUOTE_CURRENT_TARGET, + sourceSafetyVerdict: V42_READFITSFINDING_PREVIEW_QUOTE_SOURCE_SAFETY_VERDICT, + generatedAt: 'deterministic', + artifactRoot, + passed: failedPredicateIds.length === 0, + rows: V42_READFITSFINDING_PREVIEW_QUOTE_ROWS, + rowIds: [...V42_READFITSFINDING_PREVIEW_QUOTE_ROW_IDS], + predicateResults, + coverage: { + rowCount: V42_READFITSFINDING_PREVIEW_QUOTE_ROWS.length, + requiredPredicateCount: predicateResults.length, + passedPredicateCount: predicateResults.length - failedPredicateIds.length, + failedPredicateIds, + pipelineName: 'ReadFitsFindingSynthesis', + requiredPriorPipelineName: 'ReadNeedComprehensionSynthesis', + phaseCount: 7, + agentCount: 8, + ptrrStepCount: 32, + failsafeSequenceCount: 96, + thricifiedGenerationCount: 96, + searchChannelIds: [...SEARCH_CHANNEL_IDS], + selectedFitProvenanceRequired: true, + sourceSafePreviewRequired: true, + deterministicQuoteRequired: true, + noProtectedSourceBeforeSettlement: true, + settlementInstructionsRequired: true, + terminalPreviewQuoteReadbackCovered: true, + sourceSafeMetadataOnly: true, + protectedSourceVisible: false, + rawProtectedPromptVisible: false, + rawProviderResponseVisible: false, + unpaidAssetPackSourceVisible: false, + walletPrivateMaterialVisible: false, + settlementPrivatePayloadVisible: false, + credentialsSerialized: false, + legacySourceRoots: Object.values(SOURCE_ROOTS).some((sourcePath) => sourcePath.includes('_legacy/')), + }, + sourceRoots: SOURCE_ROOTS, + }; +} diff --git a/packages/protocol/src/index.d.ts b/packages/protocol/src/index.d.ts index eee3699b..3efaa2a1 100644 --- a/packages/protocol/src/index.d.ts +++ b/packages/protocol/src/index.d.ts @@ -538,6 +538,14 @@ export const V42_READNEED_REVIEW_RESYNTHESIS_PRODUCT_CLOSURE_SOURCE_SAFETY_VERDI export const V42_READNEED_REVIEW_RESYNTHESIS_PRODUCT_CLOSURE_ROW_IDS: readonly string[]; export const V42_READNEED_REVIEW_RESYNTHESIS_PRODUCT_CLOSURE_ROWS: readonly Record[]; export function buildV42ReadNeedReviewResynthesisProductClosure(input?: Record): BitcodeProtocolReport; +export const V42_READFITSFINDING_PREVIEW_QUOTE_ARTIFACT_PATH: string; +export const V42_READFITSFINDING_PREVIEW_QUOTE_CURRENT_TARGET: string; +export const V42_READFITSFINDING_PREVIEW_QUOTE_SCHEMA_ID: string; +export const V42_READFITSFINDING_PREVIEW_QUOTE_VERSION: string; +export const V42_READFITSFINDING_PREVIEW_QUOTE_SOURCE_SAFETY_VERDICT: string; +export const V42_READFITSFINDING_PREVIEW_QUOTE_ROW_IDS: readonly string[]; +export const V42_READFITSFINDING_PREVIEW_QUOTE_ROWS: readonly Record[]; +export function buildV42ReadFitsFindingPreviewQuote(input?: Record): BitcodeProtocolReport; export const EXCHANGE_INTENT_ORDER_CONTRACTS_ARTIFACT_PATH: string; export const EXCHANGE_INTENT_ORDER_CONTRACTS_CURRENT_TARGET: string; export const EXCHANGE_INTENT_ORDER_CONTRACTS_SCHEMA_ID: string; diff --git a/packages/protocol/src/index.js b/packages/protocol/src/index.js index 358610d2..0d56bca1 100644 --- a/packages/protocol/src/index.js +++ b/packages/protocol/src/index.js @@ -602,6 +602,16 @@ export { V42_READNEED_REVIEW_RESYNTHESIS_SCHEMA_ID, buildV42ReadNeedReviewResynthesisProductClosure } from './canonical/v42-readneed-review-resynthesis-product-closure.js'; +export { + V42_READFITSFINDING_PREVIEW_QUOTE_ARTIFACT_PATH, + V42_READFITSFINDING_PREVIEW_QUOTE_CURRENT_TARGET, + V42_READFITSFINDING_PREVIEW_QUOTE_ROW_IDS, + V42_READFITSFINDING_PREVIEW_QUOTE_ROWS, + V42_READFITSFINDING_PREVIEW_QUOTE_SCHEMA_ID, + V42_READFITSFINDING_PREVIEW_QUOTE_SOURCE_SAFETY_VERDICT, + V42_READFITSFINDING_PREVIEW_QUOTE_VERSION, + buildV42ReadFitsFindingPreviewQuote +} from './canonical/v42-readfitsfinding-preview-quote.js'; export { EXCHANGE_INTENT_ACTION_KINDS, EXCHANGE_INTENT_ORDER_CONTRACTS_ARTIFACT_PATH, diff --git a/packages/protocol/test/v42-readfitsfinding-preview-quote.test.js b/packages/protocol/test/v42-readfitsfinding-preview-quote.test.js new file mode 100644 index 00000000..169a499c --- /dev/null +++ b/packages/protocol/test/v42-readfitsfinding-preview-quote.test.js @@ -0,0 +1,80 @@ +import assert from 'node:assert/strict'; +import test from 'node:test'; + +import { + V42_READFITSFINDING_PREVIEW_QUOTE_ARTIFACT_PATH, + V42_READFITSFINDING_PREVIEW_QUOTE_ROWS, + V42_READFITSFINDING_PREVIEW_QUOTE_ROW_IDS, + V42_READFITSFINDING_PREVIEW_QUOTE_SCHEMA_ID, + V42_READFITSFINDING_PREVIEW_QUOTE_SOURCE_SAFETY_VERDICT, + buildV42ReadFitsFindingPreviewQuote, +} from '../src/canonical/v42-readfitsfinding-preview-quote.js'; + +test('V42 ReadFitsFinding preview and quote closure binds search, provenance, preview, and quote', () => { + const report = buildV42ReadFitsFindingPreviewQuote(); + + assert.equal( + V42_READFITSFINDING_PREVIEW_QUOTE_ARTIFACT_PATH, + '.bitcode/v42-readfitsfinding-preview-quote.json', + ); + assert.equal(report.artifactId, 'v42-readfitsfinding-preview-quote'); + assert.equal(report.schemaId, V42_READFITSFINDING_PREVIEW_QUOTE_SCHEMA_ID); + assert.equal(report.version, 'V42'); + assert.equal(report.currentTarget, 'V41'); + assert.equal(report.sourceSafetyVerdict, V42_READFITSFINDING_PREVIEW_QUOTE_SOURCE_SAFETY_VERDICT); + assert.equal(report.passed, true); + assert.deepEqual(report.rowIds, [...V42_READFITSFINDING_PREVIEW_QUOTE_ROW_IDS]); + assert.equal(report.rows.length, V42_READFITSFINDING_PREVIEW_QUOTE_ROWS.length); + assert.equal(report.coverage.rowCount, 12); + assert.equal(report.coverage.pipelineName, 'ReadFitsFindingSynthesis'); + assert.equal(report.coverage.requiredPriorPipelineName, 'ReadNeedComprehensionSynthesis'); + assert.equal(report.coverage.phaseCount, 7); + assert.equal(report.coverage.agentCount, 8); + assert.equal(report.coverage.ptrrStepCount, 32); + assert.equal(report.coverage.failsafeSequenceCount, 96); + assert.equal(report.coverage.thricifiedGenerationCount, 96); + assert.deepEqual(report.coverage.searchChannelIds, [ + 'lexical', + 'symbolic', + 'path', + 'metadata', + 'measurement', + 'embedding-vector', + 'provider-specific', + ]); + assert.equal(report.coverage.selectedFitProvenanceRequired, true); + assert.equal(report.coverage.sourceSafePreviewRequired, true); + assert.equal(report.coverage.deterministicQuoteRequired, true); + assert.equal(report.coverage.noProtectedSourceBeforeSettlement, true); + assert.equal(report.coverage.settlementInstructionsRequired, true); + assert.equal(report.coverage.terminalPreviewQuoteReadbackCovered, true); + assert.equal(report.coverage.sourceSafeMetadataOnly, true); + assert.equal(report.coverage.protectedSourceVisible, false); + assert.equal(report.coverage.rawProtectedPromptVisible, false); + assert.equal(report.coverage.rawProviderResponseVisible, false); + assert.equal(report.coverage.unpaidAssetPackSourceVisible, false); + assert.equal(report.coverage.walletPrivateMaterialVisible, false); + assert.equal(report.coverage.settlementPrivatePayloadVisible, false); + assert.equal(report.coverage.credentialsSerialized, false); + assert.equal(report.coverage.legacySourceRoots, false); + assert.deepEqual(report.coverage.failedPredicateIds, []); + assert.ok(report.artifactRoot.startsWith('v42-readfitsfinding-preview-quote:')); +}); + +test('V42 ReadFitsFinding preview and quote rows remain source-safe metadata', () => { + for (const row of V42_READFITSFINDING_PREVIEW_QUOTE_ROWS) { + assert.ok(row.rowRoot.startsWith('v42-readfitsfinding-preview-quote-row:')); + assert.equal(row.sourceSafetyClass, 'source_safe_readfitsfinding_preview_quote_metadata'); + assert.equal(row.sourceSafeMetadataOnly, true); + assert.equal(row.protectedSourceVisible, false); + assert.equal(row.rawProtectedPromptVisible, false); + assert.equal(row.rawProviderResponseVisible, false); + assert.equal(row.unpaidAssetPackSourceVisible, false); + assert.equal(row.walletPrivateMaterialVisible, false); + assert.equal(row.settlementPrivatePayloadVisible, false); + assert.equal(row.credentialsSerialized, false); + assert.ok(row.forbiddenPayloadClasses.includes('protected-source-payloads')); + assert.ok(row.forbiddenPayloadClasses.includes('raw-provider-responses')); + assert.ok(row.forbiddenPayloadClasses.includes('unpaid-assetpack-source')); + } +}); diff --git a/scripts/check-v42-gate5-readfitsfinding-preview-quote.mjs b/scripts/check-v42-gate5-readfitsfinding-preview-quote.mjs new file mode 100644 index 00000000..86408c24 --- /dev/null +++ b/scripts/check-v42-gate5-readfitsfinding-preview-quote.mjs @@ -0,0 +1,268 @@ +#!/usr/bin/env node + +import { execFileSync } from 'node:child_process'; +import { existsSync, readFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const defaultRepoRoot = path.resolve(__dirname, '..'); +const ARTIFACT_PATH = '.bitcode/v42-readfitsfinding-preview-quote.json'; + +const SECRET_MARKERS = [ + `${['sk', 'proj'].join('-')}-`, + `${['sb', 'secret'].join('_')}__`, + ['service', 'role'].join('_'), + Buffer.from('{"alg":"HS256","typ":"JWT"}').toString('base64url').slice(0, 18), + ['OPENAI', 'API', 'KEY'].join('_'), + ['SUPABASE', 'SERVICE', 'ROLE'].join('_'), + ['VERCEL', 'TOKEN'].join('_'), + ['VERCEL', 'OIDC', 'TOKEN'].join('_'), + ['PRIVATE', 'KEY'].join('_'), +]; + +function read(root, relativePath) { + return readFileSync(path.join(root, relativePath), 'utf8'); +} + +function fileExists(root, relativePath) { + return existsSync(path.join(root, relativePath)); +} + +function git(root, args) { + return execFileSync('git', args, { cwd: root, encoding: 'utf8' }).trim(); +} + +function run(root, command, args) { + return execFileSync(command, args, { + cwd: root, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + }).trim(); +} + +function assertCheck(failures, condition, message) { + if (!condition) failures.push(message); +} + +function parseArgs(argv) { + const args = { + skipBranchCheck: false, + skipPackageTests: false, + skipUapiTests: false, + repoRoot: defaultRepoRoot, + help: false, + }; + + for (let index = 0; index < argv.length; index += 1) { + const arg = argv[index]; + if (arg === '--skip-branch-check') args.skipBranchCheck = true; + else if (arg === '--skip-package-tests') args.skipPackageTests = true; + else if (arg === '--skip-uapi-tests') args.skipUapiTests = true; + else if (arg === '--repo-root') args.repoRoot = path.resolve(argv[++index]); + else if (arg === '--help' || arg === '-h') args.help = true; + else throw new Error(`Unknown argument ${arg}`); + } + + return args; +} + +function printHelp() { + process.stdout.write( + [ + 'Usage: node scripts/check-v42-gate5-readfitsfinding-preview-quote.mjs [--skip-branch-check] [--skip-package-tests] [--skip-uapi-tests] [--repo-root ]', + '', + 'Checks V42 Gate 5 ReadFitsFinding preview and quote closure: many-candidate search, selected-fit provenance, source-safe AssetPack preview, deterministic quote, no pre-settlement source exposure, Terminal readback, tests, docs, workflows, and proof artifact.', + ].join('\n'), + ); + process.stdout.write('\n'); +} + +function main() { + const args = parseArgs(process.argv.slice(2)); + if (args.help) { + printHelp(); + return; + } + + const root = args.repoRoot; + const failures = []; + const pointer = read(root, 'BITCODE_SPEC.txt').trim(); + + assertCheck( + failures, + pointer === 'V41', + `BITCODE_SPEC.txt must remain V41 during V42 gate work. Observed ${pointer || 'empty'}.`, + ); + + if (!args.skipBranchCheck) { + const branch = git(root, ['branch', '--show-current']); + assertCheck( + failures, + branch === 'version/v42' || /^v42\/gate-(?:5|[6-9]|10)-[a-z0-9][a-z0-9-]*$/u.test(branch), + `V42 Gate 5+ work must occur on version/v42 or v42/gate-5..10-* branches. Observed ${branch || 'detached HEAD'}.`, + ); + } + + const requiredFiles = [ + ARTIFACT_PATH, + 'packages/pipelines/asset-pack/src/depository-search.ts', + 'packages/pipelines/asset-pack/src/read-fits-finding-runtime.ts', + 'packages/pipelines/asset-pack/src/asset-pack-preview-boundary.ts', + 'packages/pipelines/asset-pack/src/read-need.ts', + 'packages/pipelines/asset-pack/src/postprocess.ts', + 'packages/pipelines/asset-pack/src/__tests__/depository-search.test.ts', + 'packages/pipelines/asset-pack/src/__tests__/read-fits-finding-runtime.test.ts', + 'packages/pipelines/asset-pack/src/__tests__/asset-pack-preview-boundary.test.ts', + 'uapi/app/api/pipeline-harness/asset-pack/runner.ts', + 'uapi/app/terminal/TerminalDepositReadWorkbench.tsx', + 'uapi/app/terminal/terminal-pipeline-harness-client.ts', + 'uapi/tests/api/pipelineHarnessRoute.test.ts', + 'uapi/tests/terminalPipelineHarnessClient.test.ts', + 'packages/protocol/src/canonical/v42-readfitsfinding-preview-quote.js', + 'packages/protocol/test/v42-readfitsfinding-preview-quote.test.js', + 'scripts/generate-v42-readfitsfinding-preview-quote.mjs', + 'scripts/check-v42-gate5-readfitsfinding-preview-quote.mjs', + 'BITCODE_SPEC_V42.md', + 'BITCODE_SPEC_V42_DELTA.md', + 'BITCODE_SPEC_V42_NOTES.md', + 'BITCODE_SPEC_V42_PARITY_MATRIX.md', + 'SPECIFICATIONS_ROADMAP.md', + 'README.md', + 'uapi/app/terminal/README.md', + 'packages/pipelines/asset-pack/README.md', + 'packages/protocol/README.md', + 'package.json', + '.github/workflows/bitcode-gate-quality.yml', + '.github/workflows/bitcode-canon-quality.yml', + ]; + + for (const relativePath of requiredFiles) { + assertCheck(failures, fileExists(root, relativePath), `Missing V42 Gate 5 file: ${relativePath}`); + } + + if (failures.length === 0) { + try { + run(root, 'node', ['scripts/generate-v42-readfitsfinding-preview-quote.mjs', '--check']); + } catch (error) { + failures.push(`V42 ReadFitsFinding preview/quote artifact check failed: ${error.stderr || error.message}`); + } + } + + if (failures.length === 0) { + try { + run(root, 'node', [ + '--test', + '--test-force-exit', + 'packages/protocol/test/v42-readfitsfinding-preview-quote.test.js', + ]); + } catch (error) { + failures.push(`V42 ReadFitsFinding preview/quote protocol test failed: ${error.stderr || error.message}`); + } + } + + if (failures.length === 0 && !args.skipPackageTests) { + try { + run(root, 'pnpm', [ + '--filter', + '@bitcode/pipeline-asset-pack', + 'exec', + 'jest', + '--config', + 'jest.config.cjs', + '--runTestsByPath', + 'src/__tests__/depository-search.test.ts', + 'src/__tests__/read-fits-finding-runtime.test.ts', + 'src/__tests__/asset-pack-preview-boundary.test.ts', + 'src/__tests__/postprocess.test.ts', + '--runInBand', + '--forceExit', + ]); + } catch (error) { + failures.push(`V42 ReadFitsFinding preview/quote package tests failed: ${error.stderr || error.message}`); + } + } + + if (failures.length === 0 && !args.skipUapiTests) { + try { + run(root, 'pnpm', [ + '--dir', + 'uapi', + 'exec', + 'jest', + '--runTestsByPath', + 'tests/api/pipelineHarnessRoute.test.ts', + 'tests/terminalPipelineHarnessClient.test.ts', + 'tests/terminalDepositReadWorkbench.test.ts', + '--runInBand', + ]); + } catch (error) { + failures.push(`V42 ReadFitsFinding preview/quote UAPI tests failed: ${error.stderr || error.message}`); + } + } + + const serializedArtifact = fileExists(root, ARTIFACT_PATH) ? read(root, ARTIFACT_PATH) : ''; + for (const marker of SECRET_MARKERS) { + assertCheck(failures, !serializedArtifact.includes(marker), `V42 Gate 5 artifact must not contain secret marker ${marker}.`); + } + + const artifact = serializedArtifact ? JSON.parse(serializedArtifact) : null; + if (artifact) { + assertCheck(failures, artifact.artifactId === 'v42-readfitsfinding-preview-quote', 'Gate 5 artifactId must match.'); + assertCheck( + failures, + artifact.schemaId === 'bitcode.v42.readFitsFindingPreviewQuote.v1', + 'Gate 5 schemaId must match.', + ); + assertCheck(failures, artifact.version === 'V42' && artifact.currentTarget === 'V41', 'Gate 5 artifact must bind V42 over active V41.'); + assertCheck(failures, artifact.passed === true, 'Gate 5 artifact must pass.'); + assertCheck( + failures, + artifact.sourceSafetyVerdict === 'source-safe-readfitsfinding-preview-quote-metadata', + 'Gate 5 artifact must declare source-safe ReadFitsFinding preview/quote metadata.', + ); + assertCheck(failures, artifact.coverage.rowCount === 12, 'Gate 5 must cover twelve ReadFitsFinding preview/quote rows.'); + assertCheck(failures, artifact.coverage.pipelineName === 'ReadFitsFindingSynthesis', 'Gate 5 must bind ReadFitsFindingSynthesis.'); + assertCheck(failures, artifact.coverage.requiredPriorPipelineName === 'ReadNeedComprehensionSynthesis', 'Gate 5 must bind accepted-Need prior pipeline.'); + assertCheck(failures, artifact.coverage.phaseCount === 7, 'Gate 5 must cover seven ReadFitsFindingSynthesis phases.'); + assertCheck(failures, artifact.coverage.agentCount === 8, 'Gate 5 must cover eight ReadFitsFindingSynthesis PTRR agents.'); + assertCheck(failures, artifact.coverage.ptrrStepCount === 32, 'Gate 5 must cover thirty-two PTRR steps.'); + assertCheck(failures, artifact.coverage.failsafeSequenceCount === 96, 'Gate 5 must cover ninety-six Failsafe sequences.'); + assertCheck(failures, artifact.coverage.thricifiedGenerationCount === 96, 'Gate 5 must cover ninety-six ThricifiedGeneration receipts.'); + assertCheck(failures, Array.isArray(artifact.coverage.searchChannelIds) && artifact.coverage.searchChannelIds.length === 7, 'Gate 5 must cover seven search channels.'); + assertCheck(failures, artifact.coverage.selectedFitProvenanceRequired === true, 'Gate 5 must require selected-fit provenance.'); + assertCheck(failures, artifact.coverage.sourceSafePreviewRequired === true, 'Gate 5 must require source-safe AssetPack preview.'); + assertCheck(failures, artifact.coverage.deterministicQuoteRequired === true, 'Gate 5 must require deterministic quote.'); + assertCheck(failures, artifact.coverage.noProtectedSourceBeforeSettlement === true, 'Gate 5 must block protected source before settlement.'); + assertCheck(failures, artifact.coverage.settlementInstructionsRequired === true, 'Gate 5 must require settlement instructions.'); + assertCheck(failures, artifact.coverage.terminalPreviewQuoteReadbackCovered === true, 'Gate 5 must cover Terminal preview/quote readback.'); + assertCheck(failures, artifact.coverage.sourceSafeMetadataOnly === true, 'Gate 5 must remain source-safe metadata only.'); + assertCheck(failures, artifact.coverage.protectedSourceVisible === false, 'Gate 5 artifact must not expose protected source.'); + assertCheck(failures, artifact.coverage.rawProtectedPromptVisible === false, 'Gate 5 artifact must not expose protected prompts.'); + assertCheck(failures, artifact.coverage.rawProviderResponseVisible === false, 'Gate 5 artifact must not expose raw provider responses.'); + assertCheck(failures, artifact.coverage.unpaidAssetPackSourceVisible === false, 'Gate 5 artifact must not expose unpaid AssetPack source.'); + assertCheck(failures, artifact.coverage.walletPrivateMaterialVisible === false, 'Gate 5 artifact must not expose wallet private material.'); + assertCheck(failures, artifact.coverage.settlementPrivatePayloadVisible === false, 'Gate 5 artifact must not expose settlement private payloads.'); + assertCheck(failures, artifact.coverage.credentialsSerialized === false, 'Gate 5 artifact must not serialize credentials.'); + assertCheck(failures, artifact.coverage.legacySourceRoots === false, 'Gate 5 must not rely on legacy source roots.'); + assertCheck(failures, Array.isArray(artifact.coverage.failedPredicateIds) && artifact.coverage.failedPredicateIds.length === 0, 'Gate 5 predicates must all pass.'); + } + + const spec = read(root, 'BITCODE_SPEC_V42.md'); + const parity = read(root, 'BITCODE_SPEC_V42_PARITY_MATRIX.md'); + const terminalReadme = read(root, 'uapi/app/terminal/README.md'); + assertCheck(failures, spec.includes('V42 Gate 5') && spec.includes('v42-readfitsfinding-preview-quote'), 'V42 spec must expand Gate 5 ReadFitsFinding preview/quote closure.'); + assertCheck(failures, parity.includes('Finding Fits preview and quote') && parity.includes('implemented'), 'V42 parity matrix must mark Finding Fits preview and quote implemented.'); + assertCheck(failures, terminalReadme.includes('V42 Gate 5') && terminalReadme.includes('Finding Fits preview'), 'Terminal README must document Gate 5 preview/quote readback.'); + + if (failures.length > 0) { + process.stderr.write(`V42 Gate 5 ReadFitsFinding preview/quote check failed:\n- ${failures.join('\n- ')}\n`); + process.exitCode = 1; + return; + } + + process.stdout.write(`V42 Gate 5 ReadFitsFinding preview/quote ok artifact=${artifact.artifactRoot}\n`); +} + +main(); diff --git a/scripts/generate-v42-readfitsfinding-preview-quote.mjs b/scripts/generate-v42-readfitsfinding-preview-quote.mjs new file mode 100644 index 00000000..62e6ffbd --- /dev/null +++ b/scripts/generate-v42-readfitsfinding-preview-quote.mjs @@ -0,0 +1,31 @@ +#!/usr/bin/env node + +import { readFileSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { + V42_READFITSFINDING_PREVIEW_QUOTE_ARTIFACT_PATH, + buildV42ReadFitsFindingPreviewQuote, +} from '../packages/protocol/src/canonical/v42-readfitsfinding-preview-quote.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const repoRoot = path.resolve(__dirname, '..'); + +const check = process.argv.includes('--check'); +const artifact = buildV42ReadFitsFindingPreviewQuote({ repoRoot }); +const serialized = `${JSON.stringify(artifact, null, 2)}\n`; +const artifactPath = path.join(repoRoot, V42_READFITSFINDING_PREVIEW_QUOTE_ARTIFACT_PATH); + +if (check) { + const current = readFileSync(artifactPath, 'utf8'); + if (current !== serialized) { + process.stderr.write( + `${V42_READFITSFINDING_PREVIEW_QUOTE_ARTIFACT_PATH} is stale. Run pnpm run generate:v42-readfitsfinding-preview-quote.\n`, + ); + process.exitCode = 1; + } +} else { + writeFileSync(artifactPath, serialized); + process.stdout.write(`wrote ${V42_READFITSFINDING_PREVIEW_QUOTE_ARTIFACT_PATH}\n`); +} diff --git a/uapi/app/api/pipeline-harness/asset-pack/runner.ts b/uapi/app/api/pipeline-harness/asset-pack/runner.ts index 3ec0705a..8cf8fd26 100644 --- a/uapi/app/api/pipeline-harness/asset-pack/runner.ts +++ b/uapi/app/api/pipeline-harness/asset-pack/runner.ts @@ -419,6 +419,31 @@ function summarizeEvidence(evidence: unknown): Record | null { : record.sourceSafePreview && typeof record.sourceSafePreview === 'object' ? (record.sourceSafePreview as Record) : null; + const assetPackPreviewBoundary = output?.assetPackPreviewBoundary && typeof output.assetPackPreviewBoundary === 'object' + ? (output.assetPackPreviewBoundary as Record) + : record.assetPackPreviewBoundary && typeof record.assetPackPreviewBoundary === 'object' + ? (record.assetPackPreviewBoundary as Record) + : null; + const assetPackQuoteReceipt = output?.assetPackQuoteReceipt && typeof output.assetPackQuoteReceipt === 'object' + ? (output.assetPackQuoteReceipt as Record) + : assetPackPreviewBoundary?.quoteReceipt && typeof assetPackPreviewBoundary.quoteReceipt === 'object' + ? (assetPackPreviewBoundary.quoteReceipt as Record) + : null; + const assetPackSettlementInstructions = output?.assetPackSettlementInstructions && typeof output.assetPackSettlementInstructions === 'object' + ? (output.assetPackSettlementInstructions as Record) + : assetPackPreviewBoundary?.settlementInstructions && typeof assetPackPreviewBoundary.settlementInstructions === 'object' + ? (assetPackPreviewBoundary.settlementInstructions as Record) + : null; + const assetPackDeliveryPosture = output?.assetPackDeliveryPosture && typeof output.assetPackDeliveryPosture === 'object' + ? (output.assetPackDeliveryPosture as Record) + : assetPackPreviewBoundary?.deliveryPosture && typeof assetPackPreviewBoundary.deliveryPosture === 'object' + ? (assetPackPreviewBoundary.deliveryPosture as Record) + : null; + const assetPackDisclosureReview = output?.assetPackDisclosureReview && typeof output.assetPackDisclosureReview === 'object' + ? (output.assetPackDisclosureReview as Record) + : assetPackPreviewBoundary?.disclosureReview && typeof assetPackPreviewBoundary.disclosureReview === 'object' + ? (assetPackPreviewBoundary.disclosureReview as Record) + : null; const summarizeCandidate = (candidate: unknown) => { if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) return null; const record = candidate as Record; @@ -522,6 +547,65 @@ function summarizeEvidence(evidence: unknown): Record | null { delivery: sourceSafePreview.delivery, } : null, + assetPackPreviewBoundary: summarizeAssetPackPreviewBoundary(assetPackPreviewBoundary), + assetPackQuoteReceipt: assetPackQuoteReceipt + ? { + schema: assetPackQuoteReceipt.schema, + quoteId: assetPackQuoteReceipt.quoteId, + deterministic: assetPackQuoteReceipt.deterministic, + formula: assetPackQuoteReceipt.formula, + needId: assetPackQuoteReceipt.needId, + needMeasurementRoot: assetPackQuoteReceipt.needMeasurementRoot, + admittedFitQuality: assetPackQuoteReceipt.admittedFitQuality, + weightedRequestedVolume: assetPackQuoteReceipt.weightedRequestedVolume, + weightedAdmittedVolume: assetPackQuoteReceipt.weightedAdmittedVolume, + sats: assetPackQuoteReceipt.sats, + feeSchedule: assetPackQuoteReceipt.feeSchedule, + finalityState: assetPackQuoteReceipt.finalityState, + payer: assetPackQuoteReceipt.payer, + quoteRoot: assetPackQuoteReceipt.quoteRoot, + receiptRoot: assetPackQuoteReceipt.receiptRoot, + } + : null, + assetPackSettlementInstructions: assetPackSettlementInstructions + ? { + schema: assetPackSettlementInstructions.schema, + state: assetPackSettlementInstructions.state, + payer: assetPackSettlementInstructions.payer, + payee: assetPackSettlementInstructions.payee, + btcNetwork: assetPackSettlementInstructions.btcNetwork, + sats: assetPackSettlementInstructions.sats, + quoteRoot: assetPackSettlementInstructions.quoteRoot, + serverCustody: assetPackSettlementInstructions.serverCustody, + btcFeeOperation: assetPackSettlementInstructions.btcFeeOperation, + settlementRequiredBeforeUnlock: assetPackSettlementInstructions.settlementRequiredBeforeUnlock, + requiredReadbacksBeforeUnlock: assetPackSettlementInstructions.requiredReadbacksBeforeUnlock, + forbiddenBeforeSettlement: assetPackSettlementInstructions.forbiddenBeforeSettlement, + instructionsRoot: assetPackSettlementInstructions.instructionsRoot, + } + : null, + assetPackDeliveryPosture: assetPackDeliveryPosture + ? { + schema: assetPackDeliveryPosture.schema, + state: assetPackDeliveryPosture.state, + deliveryMechanism: assetPackDeliveryPosture.deliveryMechanism, + pullRequestTarget: assetPackDeliveryPosture.pullRequestTarget, + sourceBearingDeliveryVisible: assetPackDeliveryPosture.sourceBearingDeliveryVisible, + availableAfterSettlement: assetPackDeliveryPosture.availableAfterSettlement, + blockerCodes: assetPackDeliveryPosture.blockerCodes, + deliveryRoot: assetPackDeliveryPosture.deliveryRoot, + } + : null, + assetPackDisclosureReview: assetPackDisclosureReview + ? { + schema: assetPackDisclosureReview.schema, + assetPackId: assetPackDisclosureReview.assetPackId, + access: assetPackDisclosureReview.access, + policy: assetPackDisclosureReview.policy, + sourceLeakage: assetPackDisclosureReview.sourceLeakage, + roots: assetPackDisclosureReview.roots, + } + : null, depositorySearch: depositorySearch ? { ...summarizeFitLike(depositorySearch), @@ -570,6 +654,139 @@ function summarizeEvidence(evidence: unknown): Record | null { }; } +function summarizeAssetPackPreviewBoundary( + boundary: Record | null, +): Record | null { + if (!boundary) return null; + const quoteReceipt = boundary.quoteReceipt && typeof boundary.quoteReceipt === 'object' + ? (boundary.quoteReceipt as Record) + : null; + const selectedFitProvenance = boundary.selectedFitProvenance && typeof boundary.selectedFitProvenance === 'object' + ? (boundary.selectedFitProvenance as Record) + : null; + const settlementInstructions = boundary.settlementInstructions && typeof boundary.settlementInstructions === 'object' + ? (boundary.settlementInstructions as Record) + : null; + const deliveryPosture = boundary.deliveryPosture && typeof boundary.deliveryPosture === 'object' + ? (boundary.deliveryPosture as Record) + : null; + const replayReceipt = boundary.replayReceipt && typeof boundary.replayReceipt === 'object' + ? (boundary.replayReceipt as Record) + : null; + const repairPosture = boundary.repairPosture && typeof boundary.repairPosture === 'object' + ? (boundary.repairPosture as Record) + : null; + const proofRoots = boundary.proofRoots && typeof boundary.proofRoots === 'object' + ? (boundary.proofRoots as Record) + : null; + const sourceSafety = boundary.sourceSafety && typeof boundary.sourceSafety === 'object' + ? (boundary.sourceSafety as Record) + : null; + const storageProjection = Array.isArray(boundary.storageProjection) + ? boundary.storageProjection + .map((record) => record && typeof record === 'object' && !Array.isArray(record) + ? record as Record + : null) + .filter((record): record is Record => Boolean(record)) + : []; + + return { + schema: boundary.schema, + boundaryId: boundary.boundaryId, + assetPackId: boundary.assetPackId, + sourceSafety, + proofRoots, + quoteReceipt: quoteReceipt + ? { + quoteId: quoteReceipt.quoteId, + deterministic: quoteReceipt.deterministic, + formula: quoteReceipt.formula, + needId: quoteReceipt.needId, + admittedFitQuality: quoteReceipt.admittedFitQuality, + weightedRequestedVolume: quoteReceipt.weightedRequestedVolume, + weightedAdmittedVolume: quoteReceipt.weightedAdmittedVolume, + sats: quoteReceipt.sats, + finalityState: quoteReceipt.finalityState, + payer: quoteReceipt.payer, + quoteRoot: quoteReceipt.quoteRoot, + receiptRoot: quoteReceipt.receiptRoot, + } + : null, + selectedFitProvenance: selectedFitProvenance + ? { + schema: selectedFitProvenance.schema, + resultState: selectedFitProvenance.resultState, + selectedCandidateAssetIds: selectedFitProvenance.selectedCandidateAssetIds, + fitDepositAssetIds: selectedFitProvenance.fitDepositAssetIds, + queryRoot: selectedFitProvenance.queryRoot, + rankingRoot: selectedFitProvenance.rankingRoot, + selectedCandidates: Array.isArray(selectedFitProvenance.selectedCandidates) + ? selectedFitProvenance.selectedCandidates.map((candidate) => + candidate && typeof candidate === 'object' + ? { + assetId: (candidate as Record).assetId, + finalScore: (candidate as Record).finalScore, + semanticScore: (candidate as Record).semanticScore, + proofRoot: (candidate as Record).proofRoot, + measurementRoot: (candidate as Record).measurementRoot, + reconciliationReadbackRoot: (candidate as Record).reconciliationReadbackRoot, + } + : null, + ).filter(Boolean) + : [], + provenanceRoot: selectedFitProvenance.provenanceRoot, + } + : null, + settlementInstructions: settlementInstructions + ? { + state: settlementInstructions.state, + payer: settlementInstructions.payer, + payee: settlementInstructions.payee, + btcNetwork: settlementInstructions.btcNetwork, + sats: settlementInstructions.sats, + quoteRoot: settlementInstructions.quoteRoot, + serverCustody: settlementInstructions.serverCustody, + settlementRequiredBeforeUnlock: settlementInstructions.settlementRequiredBeforeUnlock, + instructionsRoot: settlementInstructions.instructionsRoot, + } + : null, + deliveryPosture: deliveryPosture + ? { + state: deliveryPosture.state, + deliveryMechanism: deliveryPosture.deliveryMechanism, + pullRequestTarget: deliveryPosture.pullRequestTarget, + sourceBearingDeliveryVisible: deliveryPosture.sourceBearingDeliveryVisible, + availableAfterSettlement: deliveryPosture.availableAfterSettlement, + blockerCodes: deliveryPosture.blockerCodes, + deliveryRoot: deliveryPosture.deliveryRoot, + } + : null, + replayReceipt: replayReceipt + ? { + replayMode: replayReceipt.replayMode, + previewRoot: replayReceipt.previewRoot, + quoteRoot: replayReceipt.quoteRoot, + selectedFitProvenanceRoot: replayReceipt.selectedFitProvenanceRoot, + settlementInstructionsRoot: replayReceipt.settlementInstructionsRoot, + deliveryPostureRoot: replayReceipt.deliveryPostureRoot, + storageRoot: replayReceipt.storageRoot, + replayRoot: replayReceipt.replayRoot, + verified: replayReceipt.verified, + } + : null, + repairPosture, + storageProjection: storageProjection.map((record) => ({ + recordId: record.recordId, + recordKind: record.recordKind, + namespace: record.namespace, + key: record.key, + root: record.root, + sourceSafety: record.sourceSafety, + })), + storageRecordCount: storageProjection.length, + }; +} + function safeHostEvent(event: PipelineHarnessHostEvent): PipelineHarnessHostEvent { return event; } diff --git a/uapi/app/terminal/README.md b/uapi/app/terminal/README.md index a5803f85..6ec89b32 100644 --- a/uapi/app/terminal/README.md +++ b/uapi/app/terminal/README.md @@ -125,6 +125,16 @@ Finding Fits; accepting the Need is the only admission handoff to `.bitcode/v42-readneed-review-resynthesis-product-closure.json`, checked by `pnpm run check:v42-gate4`. +V42 Gate 5 closes the Terminal Finding Fits preview and quote readback. The +source-safe preview panel now reads `AssetPackPreviewBoundary` summaries from +pipeline harness evidence, including selected-fit provenance, deterministic +quote receipt, settlement instructions, delivery posture, replay root, and +storage record count. Terminal keeps those rows expandable and metadata-only: +protected source and unpaid AssetPack source stay withheld until settlement +and rights-transfer readback. The source-safe proof artifact is +`.bitcode/v42-readfitsfinding-preview-quote.json`, checked by +`pnpm run check:v42-gate5`. + ## Live staging-testnet QA Terminal Deposit/Read QA starts only after Wallet and Externals prerequisites are diff --git a/uapi/app/terminal/TerminalDepositReadWorkbench.tsx b/uapi/app/terminal/TerminalDepositReadWorkbench.tsx index 599e8117..870f2bad 100644 --- a/uapi/app/terminal/TerminalDepositReadWorkbench.tsx +++ b/uapi/app/terminal/TerminalDepositReadWorkbench.tsx @@ -311,9 +311,27 @@ export default function TerminalDepositReadWorkbench({ } return null; }, [harnessEvents]); - const sourceSafePreview = objectValue(completedHarnessEvidence?.sourceSafePreview); + const assetPackPreviewBoundary = objectValue(completedHarnessEvidence?.assetPackPreviewBoundary); + const boundarySourceSafePreview = objectValue(assetPackPreviewBoundary?.sourceSafePreview); + const sourceSafePreview = + objectValue(completedHarnessEvidence?.sourceSafePreview) || + boundarySourceSafePreview; + const assetPackSelectedFitProvenance = + objectValue(assetPackPreviewBoundary?.selectedFitProvenance); + const assetPackQuoteReceipt = + objectValue(assetPackPreviewBoundary?.quoteReceipt) || + objectValue(completedHarnessEvidence?.assetPackQuoteReceipt); + const assetPackSettlementInstructions = + objectValue(assetPackPreviewBoundary?.settlementInstructions) || + objectValue(completedHarnessEvidence?.assetPackSettlementInstructions); + const assetPackDeliveryPosture = + objectValue(assetPackPreviewBoundary?.deliveryPosture) || + objectValue(completedHarnessEvidence?.assetPackDeliveryPosture); + const assetPackPreviewProofRoots = objectValue(assetPackPreviewBoundary?.proofRoots); + const assetPackPreviewReplayReceipt = objectValue(assetPackPreviewBoundary?.replayReceipt); const assetPackDisclosureReview = objectValue(completedHarnessEvidence?.assetPackDisclosureReview) || + objectValue(assetPackPreviewBoundary?.disclosureReview) || objectValue(sourceSafePreview?.disclosureReview); const disclosureAccess = objectValue(assetPackDisclosureReview?.access); const disclosurePolicy = objectValue(assetPackDisclosureReview?.policy); @@ -321,12 +339,18 @@ export default function TerminalDepositReadWorkbench({ const disclosureRoots = objectValue(assetPackDisclosureReview?.roots); const disclosureSourceSafe = disclosureLeakage?.protectedSourceDetected !== true; const ledgerSettlement = objectValue(completedHarnessEvidence?.ledgerSettlement); - const previewFeeQuote = objectValue(sourceSafePreview?.feeQuote); + const previewFeeQuote = + assetPackQuoteReceipt || + objectValue(sourceSafePreview?.feeQuote); const protectedSourceUnlock = objectValue(sourceSafePreview?.unlock) || objectValue(ledgerSettlement?.protectedSourceUnlock); const settledReadback = ledgerSettlement?.status === 'settled'; - const pullRequestDelivered = settledReadback && Boolean(textValue(objectValue(sourceSafePreview?.delivery)?.pullRequestTarget)); + const previewDelivery = objectValue(sourceSafePreview?.delivery); + const pullRequestDelivered = settledReadback && Boolean( + textValue(previewDelivery?.pullRequestTarget) || + textValue(assetPackDeliveryPosture?.pullRequestTarget), + ); const enterpriseReadingState = useMemo( () => buildTerminalEnterpriseReadingUxState({ @@ -417,6 +441,99 @@ export default function TerminalDepositReadWorkbench({ sourceSafePreview?.accessPolicy, ], ); + const assetPackPreviewBoundaryRows = useMemo( + () => [ + { + label: 'Boundary', + value: shortIdentifier(assetPackPreviewBoundary?.boundaryId) || 'pending', + }, + { + label: 'Preview root', + value: + shortIdentifier(assetPackPreviewProofRoots?.previewRoot) || + shortIdentifier(objectValue(sourceSafePreview?.roots)?.previewRoot) || + 'pending', + }, + { + label: 'Quote', + value: numericValue(previewFeeQuote?.sats) ? `${String(previewFeeQuote?.sats)} sats` : 'pending', + }, + { + label: 'Quote root', + value: + shortIdentifier(assetPackQuoteReceipt?.quoteRoot) || + shortIdentifier(previewFeeQuote?.quoteRoot) || + 'pending', + }, + { + label: 'Formula', + value: textValue(assetPackQuoteReceipt?.formula) || textValue(previewFeeQuote?.formula) || 'pending', + }, + { + label: 'Deterministic', + value: assetPackQuoteReceipt?.deterministic === true ? 'yes' : 'pending', + }, + { + label: 'Selected fits', + value: stringList(assetPackSelectedFitProvenance?.selectedCandidateAssetIds).join(', ') || 'pending', + }, + { + label: 'Fit deposits', + value: stringList(assetPackSelectedFitProvenance?.fitDepositAssetIds).join(', ') || 'pending', + }, + { + label: 'Provenance root', + value: + shortIdentifier(assetPackSelectedFitProvenance?.provenanceRoot) || + shortIdentifier(assetPackPreviewProofRoots?.selectedFitProvenanceRoot) || + 'pending', + }, + { + label: 'Settlement', + value: textValue(assetPackSettlementInstructions?.state) || 'pending', + }, + { + label: 'Network', + value: textValue(assetPackSettlementInstructions?.btcNetwork) || 'pending', + }, + { + label: 'Instructions root', + value: shortIdentifier(assetPackSettlementInstructions?.instructionsRoot) || 'pending', + }, + { + label: 'Delivery', + value: textValue(assetPackDeliveryPosture?.state) || 'pending', + }, + { + label: 'Delivery root', + value: shortIdentifier(assetPackDeliveryPosture?.deliveryRoot) || 'pending', + }, + { + label: 'Replay root', + value: + shortIdentifier(assetPackPreviewReplayReceipt?.replayRoot) || + shortIdentifier(assetPackPreviewProofRoots?.replayRoot) || + 'pending', + }, + { + label: 'Storage records', + value: numericValue(assetPackPreviewBoundary?.storageRecordCount) + ? String(assetPackPreviewBoundary?.storageRecordCount) + : String(countList(assetPackPreviewBoundary?.storageProjection) || 0), + }, + ], + [ + assetPackDeliveryPosture, + assetPackPreviewBoundary, + assetPackPreviewProofRoots, + assetPackPreviewReplayReceipt, + assetPackQuoteReceipt, + assetPackSelectedFitProvenance, + assetPackSettlementInstructions, + previewFeeQuote, + sourceSafePreview?.roots, + ], + ); const readNeedRows = useMemo(() => { if (!currentReadNeed) return []; return [ @@ -974,7 +1091,7 @@ export default function TerminalDepositReadWorkbench({ { label: 'Unlock', value: protectedSourceUnlock?.sourceAvailable === true ? 'source available' : textValue(protectedSourceUnlock?.state) || 'withheld' }, { label: 'Read license', value: shortIdentifier(ledgerSettlement?.readLicenseId) || 'pending' }, { label: 'BTC fee', value: shortIdentifier(ledgerSettlement?.btcFeeReceiptId) || 'pending' }, - { label: 'PR target', value: textValue(objectValue(sourceSafePreview?.delivery)?.pullRequestTarget) || 'pending' }, + { label: 'PR target', value: textValue(previewDelivery?.pullRequestTarget) || textValue(assetPackDeliveryPosture?.pullRequestTarget) || 'pending' }, ].map((row) => (
{row.label}
@@ -982,6 +1099,19 @@ export default function TerminalDepositReadWorkbench({
))} +
+ + Finding Fits preview, quote, and provenance + +
+ {assetPackPreviewBoundaryRows.map((row) => ( +
+
{row.label}
+
{row.value}
+
+ ))} +
+
diff --git a/uapi/app/terminal/terminal-pipeline-harness-client.ts b/uapi/app/terminal/terminal-pipeline-harness-client.ts index 51b5b2d7..487d10a4 100644 --- a/uapi/app/terminal/terminal-pipeline-harness-client.ts +++ b/uapi/app/terminal/terminal-pipeline-harness-client.ts @@ -635,8 +635,16 @@ export function summarizeTerminalReadFitsFindingSynthesisHarnessEvent( const depositorySearch = recordValue(evidence?.depositorySearch); const ledgerSettlement = recordValue(evidence?.ledgerSettlement); const sourceSafePreview = recordValue(evidence?.sourceSafePreview); + const assetPackPreviewBoundary = recordValue(evidence?.assetPackPreviewBoundary); + const boundaryQuoteReceipt = recordValue(assetPackPreviewBoundary?.quoteReceipt); + const boundarySelectedFitProvenance = recordValue(assetPackPreviewBoundary?.selectedFitProvenance); + const boundarySettlementInstructions = recordValue(assetPackPreviewBoundary?.settlementInstructions); + const boundaryDeliveryPosture = recordValue(assetPackPreviewBoundary?.deliveryPosture); const assetPackDisclosureReview = recordValue(evidence?.assetPackDisclosureReview); - const feeQuote = recordValue(sourceSafePreview?.feeQuote); + const feeQuote = + boundaryQuoteReceipt || + recordValue(evidence?.assetPackQuoteReceipt) || + recordValue(sourceSafePreview?.feeQuote); const unlock = recordValue(sourceSafePreview?.unlock) || recordValue(ledgerSettlement?.protectedSourceUnlock); const disclosureAccess = recordValue(assetPackDisclosureReview?.access); const disclosureLeakage = recordValue(assetPackDisclosureReview?.sourceLeakage); @@ -646,7 +654,9 @@ export function summarizeTerminalReadFitsFindingSynthesisHarnessEvent( ? `ledger ${String(ledgerSettlement.status)}` : null; const selectedCandidateText = summarizeCandidateIds( - fitResult?.selectedCandidateAssetIds || depositorySearch?.selectedCandidateAssetIds, + boundarySelectedFitProvenance?.selectedCandidateAssetIds || + fitResult?.selectedCandidateAssetIds || + depositorySearch?.selectedCandidateAssetIds, ); const telemetryLineCount = Number(data?.telemetryLineCount || 0); const telemetryText = telemetryLineCount > 0 @@ -655,6 +665,15 @@ export function summarizeTerminalReadFitsFindingSynthesisHarnessEvent( const feeQuoteText = typeof feeQuote?.sats === 'number' ? ` fee ${feeQuote.sats} sats` : null; + const quoteText = boundaryQuoteReceipt?.quoteRoot + ? ` quote ${shortIdentifier(boundaryQuoteReceipt.quoteRoot)}` + : null; + const settlementText = boundarySettlementInstructions?.state + ? ` settlement ${String(boundarySettlementInstructions.state)}` + : null; + const deliveryText = boundaryDeliveryPosture?.state + ? ` delivery ${String(boundaryDeliveryPosture.state)}` + : null; const unlockText = unlock?.sourceAvailable === true ? ` source ${String(unlock.state || 'available')}` : unlock?.state @@ -678,6 +697,9 @@ export function summarizeTerminalReadFitsFindingSynthesisHarnessEvent( selectedCandidateText, ledgerStatus, feeQuoteText, + quoteText, + settlementText, + deliveryText, unlockText, disclosureText, leakageText, diff --git a/uapi/tests/api/pipelineHarnessRoute.test.ts b/uapi/tests/api/pipelineHarnessRoute.test.ts index 1d94e05b..81dd1a44 100644 --- a/uapi/tests/api/pipelineHarnessRoute.test.ts +++ b/uapi/tests/api/pipelineHarnessRoute.test.ts @@ -79,12 +79,117 @@ jest.mock('@bitcode/pipeline-hosts', () => ({ }, sourceSafePreview: { schema: 'bitcode.asset-pack.source-safe-preview', + assetPackId: 'asset-pack-route-test', + feeQuote: { + sats: 546, + quoteRoot: 'sha256:route-quote-root', + }, unlock: { state: 'denied', sourceAvailable: false, reason: 'settlement blocked in route test', }, }, + assetPackPreviewBoundary: { + schema: 'bitcode.asset-pack.preview-boundary', + boundaryId: 'asset-pack-preview-boundary-route-test', + assetPackId: 'asset-pack-route-test', + quoteReceipt: { + quoteId: 'quote-route-test', + deterministic: true, + formula: 'sum(measurement.weight * measurement.volume * admitted_fit_quality)', + needId: 'need-route-test', + admittedFitQuality: 0.91, + weightedRequestedVolume: 3, + weightedAdmittedVolume: 2.73, + sats: 546, + finalityState: 'preview_not_paid', + payer: 'reader', + quoteRoot: 'sha256:route-quote-root', + receiptRoot: 'sha256:route-quote-receipt-root', + }, + selectedFitProvenance: { + resultState: 'worthy_fit', + selectedCandidateAssetIds: ['asset-repository-revision'], + fitDepositAssetIds: ['deposit-route-test'], + queryRoot: 'sha256:route-query-root', + rankingRoot: 'sha256:route-ranking-root', + selectedCandidates: [ + { + assetId: 'asset-repository-revision', + finalScore: 0.91, + semanticScore: 0.82, + proofRoot: 'sha256:route-proof-root', + measurementRoot: 'sha256:route-measurement-root', + reconciliationReadbackRoot: 'sha256:route-readback-root', + }, + ], + provenanceRoot: 'sha256:route-provenance-root', + }, + settlementInstructions: { + state: 'quote_ready_settlement_required', + payer: 'reader', + payee: 'depositor', + btcNetwork: 'testnet', + sats: 546, + quoteRoot: 'sha256:route-quote-root', + serverCustody: false, + settlementRequiredBeforeUnlock: true, + instructionsRoot: 'sha256:route-instructions-root', + }, + deliveryPosture: { + state: 'withheld_until_settlement', + deliveryMechanism: 'pull_request_after_settlement', + pullRequestTarget: null, + sourceBearingDeliveryVisible: false, + availableAfterSettlement: true, + blockerCodes: ['btc_fee_unpaid'], + deliveryRoot: 'sha256:route-delivery-root', + }, + replayReceipt: { + replayMode: 'source-safe-preview-quote-disclosure-boundary-replay', + previewRoot: 'sha256:route-preview-root', + quoteRoot: 'sha256:route-quote-root', + selectedFitProvenanceRoot: 'sha256:route-provenance-root', + settlementInstructionsRoot: 'sha256:route-instructions-root', + deliveryPostureRoot: 'sha256:route-delivery-root', + storageRoot: 'sha256:route-storage-root', + replayRoot: 'sha256:route-replay-root', + verified: { + protectedSourceLeakageAbsent: true, + }, + }, + proofRoots: { + previewRoot: 'sha256:route-preview-root', + quoteRoot: 'sha256:route-quote-root', + selectedFitProvenanceRoot: 'sha256:route-provenance-root', + settlementInstructionsRoot: 'sha256:route-instructions-root', + deliveryPostureRoot: 'sha256:route-delivery-root', + replayRoot: 'sha256:route-replay-root', + boundaryRoot: 'sha256:route-boundary-root', + }, + sourceSafety: { + sourceSafeMetadataOnly: true, + protectedSourceVisible: false, + unpaidAssetPackSourceVisible: false, + credentialsSerialized: false, + }, + storageProjection: [ + { + recordId: 'route-storage-record', + recordKind: 'deterministic_btc_quote', + namespace: 'asset-pack/preview', + key: 'quoteReceipt', + root: 'sha256:route-storage-record-root', + sourceSafety: { + sourceSafeMetadataOnly: true, + }, + payload: { + hiddenFromRouteSummary: true, + }, + }, + ], + }, }, }, telemetry: '{"type":"pipeline-stream-event","runId":"route-run","stage":"asset-pack-synthesis"}\n', @@ -474,11 +579,47 @@ describe('POST /api/pipeline-harness/asset-pack', () => { }, }, sourceSafePreview: { + assetPackId: 'asset-pack-route-test', unlock: { state: 'denied', sourceAvailable: false, }, }, + assetPackPreviewBoundary: { + boundaryId: 'asset-pack-preview-boundary-route-test', + assetPackId: 'asset-pack-route-test', + quoteReceipt: { + sats: 546, + quoteRoot: 'sha256:route-quote-root', + }, + selectedFitProvenance: { + selectedCandidateAssetIds: ['asset-repository-revision'], + fitDepositAssetIds: ['deposit-route-test'], + provenanceRoot: 'sha256:route-provenance-root', + }, + settlementInstructions: { + state: 'quote_ready_settlement_required', + serverCustody: false, + }, + deliveryPosture: { + state: 'withheld_until_settlement', + sourceBearingDeliveryVisible: false, + }, + storageRecordCount: 1, + }, + assetPackQuoteReceipt: { + sats: 546, + deterministic: true, + quoteRoot: 'sha256:route-quote-root', + }, + assetPackSettlementInstructions: { + state: 'quote_ready_settlement_required', + serverCustody: false, + }, + assetPackDeliveryPosture: { + state: 'withheld_until_settlement', + sourceBearingDeliveryVisible: false, + }, }, }); expect(eventText).toContain('[redacted]'); diff --git a/uapi/tests/terminalPipelineHarnessClient.test.ts b/uapi/tests/terminalPipelineHarnessClient.test.ts index 7165b582..06882e99 100644 --- a/uapi/tests/terminalPipelineHarnessClient.test.ts +++ b/uapi/tests/terminalPipelineHarnessClient.test.ts @@ -188,13 +188,29 @@ describe('terminal pipeline harness client', () => { }, sourceSafePreview: { feeQuote: { - sats: 546, + sats: 500, }, unlock: { state: 'licensed_read', sourceAvailable: true, }, }, + assetPackPreviewBoundary: { + quoteReceipt: { + sats: 546, + quoteRoot: 'sha256:asset-pack-preview-quote-root', + }, + selectedFitProvenance: { + selectedCandidateAssetIds: ['asset-repository-revision'], + fitDepositAssetIds: ['deposit-asset-pack'], + }, + settlementInstructions: { + state: 'quote_ready_settlement_required', + }, + deliveryPosture: { + state: 'withheld_until_settlement', + }, + }, assetPackDisclosureReview: { access: { sourceVisibility: 'available_after_settlement', @@ -214,6 +230,9 @@ describe('terminal pipeline harness client', () => { expect(summary).toContain('candidate asset-repository-revision'); expect(summary).toContain('ledger settled'); expect(summary).toContain('fee 546 sats'); + expect(summary).toContain('quote sha256:asset'); + expect(summary).toContain('settlement quote_ready_settlement_required'); + expect(summary).toContain('delivery withheld_until_settlement'); expect(summary).toContain('source licensed_read'); expect(summary).toContain('disclosure available_after_settlement'); expect(summary).toContain('leakage none');