From fb42f285db7a7172fcaea6d815e52e2c4b130894 Mon Sep 17 00:00:00 2001 From: Garrett Maring Date: Thu, 28 May 2026 19:12:37 -0300 Subject: [PATCH] V42 Gate 6: Close settlement rights delivery Implement the paid AssetPack settlement boundary through the live harness, route summaries, and Terminal readback. Add the V42 settlement-rights proof artifact, protocol exports, gate checker, workflow wiring, and documentation updates for BTC payment observation, BTD rights transfer, source-to-shares compensation, reconciliation, and repository delivery unlock. Validation: pnpm run check:v42-gate6; pnpm -C uapi exec tsc --noEmit --pretty false; pnpm -C uapi lint; pnpm --filter @bitcode/protocol test; git diff --check; changed-file value-marker secret scan. --- .bitcode/v42-settlement-rights-delivery.json | 587 ++++++++++++++++++ .github/workflows/bitcode-canon-quality.yml | 3 + .github/workflows/bitcode-gate-quality.yml | 3 + BITCODE_SPEC_V42.md | 3 + BITCODE_SPEC_V42_DELTA.md | 1 + BITCODE_SPEC_V42_NOTES.md | 7 + BITCODE_SPEC_V42_PARITY_MATRIX.md | 4 +- README.md | 11 + SPECIFICATIONS_ROADMAP.md | 5 +- package.json | 3 + .../src/__tests__/asset-pack-harness.test.ts | 7 + .../pipeline-hosts/src/asset-pack-harness.ts | 100 ++- packages/pipelines/asset-pack/README.md | 9 + packages/protocol/README.md | 15 + .../v42-settlement-rights-delivery.js | 301 +++++++++ packages/protocol/src/index.d.ts | 8 + packages/protocol/src/index.js | 10 + .../v42-settlement-rights-delivery.test.js | 68 ++ ...k-v42-gate6-settlement-rights-delivery.mjs | 252 ++++++++ ...enerate-v42-settlement-rights-delivery.mjs | 61 ++ .../api/pipeline-harness/asset-pack/runner.ts | 209 +++++++ uapi/app/terminal/README.md | 13 + .../terminal/TerminalDepositReadWorkbench.tsx | 117 ++++ .../terminal-pipeline-harness-client.ts | 32 + uapi/tests/api/pipelineHarnessRoute.test.ts | 148 +++++ .../terminalPipelineHarnessClient.test.ts | 23 + 26 files changed, 1976 insertions(+), 24 deletions(-) create mode 100644 .bitcode/v42-settlement-rights-delivery.json create mode 100644 packages/protocol/src/canonical/v42-settlement-rights-delivery.js create mode 100644 packages/protocol/test/v42-settlement-rights-delivery.test.js create mode 100644 scripts/check-v42-gate6-settlement-rights-delivery.mjs create mode 100644 scripts/generate-v42-settlement-rights-delivery.mjs diff --git a/.bitcode/v42-settlement-rights-delivery.json b/.bitcode/v42-settlement-rights-delivery.json new file mode 100644 index 00000000..f8517199 --- /dev/null +++ b/.bitcode/v42-settlement-rights-delivery.json @@ -0,0 +1,587 @@ +{ + "artifactId": "v42-settlement-rights-delivery", + "schemaId": "bitcode.v42.settlementRightsDelivery.v1", + "version": "V42", + "currentTarget": "V41", + "sourceSafetyVerdict": "source-safe-v42-settlement-rights-delivery-metadata", + "rowIds": [ + "purchase:quote-to-payment-observation", + "finality:btc-testnet-confirmation-gate", + "rights:btd-read-right-transfer", + "compensation:source-to-shares-conservation", + "delivery:source-bearing-pull-request-unlock", + "sync:ledger-database-object-storage-reconciliation", + "repair:fail-closed-settlement-posture", + "route:harness-settlement-summary", + "ui:terminal-settlement-readback", + "host:live-harness-boundary-materialization", + "proof:tests-artifact-workflow" + ], + "rows": [ + { + "rowId": "purchase:quote-to-payment-observation", + "purpose": "Convert the Gate 5 deterministic quote into a reader BTC payment observation without server custody or private wallet material.", + "sourceRoots": [ + "packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts", + "packages/pipelines/asset-pack/src/asset-pack-preview-boundary.ts", + "uapi/app/api/pipeline-harness/asset-pack/runner.ts" + ], + "emittedTypes": [ + "AssetPackSettlementPaymentObservation", + "AssetPackPreviewQuoteReceipt" + ], + "requiredEvidence": [ + "expectedSats", + "observedDebitSats", + "observedCreditSats", + "serverCustody: false" + ], + "rowRoot": "v42-settlement-rights-delivery-row:6a9db7cbd29a673da5745714", + "sourceSafetyClass": "source_safe_v42_settlement_rights_delivery_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourcePayloadSerialized": 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": "finality:btc-testnet-confirmation-gate", + "purpose": "Require confirmed BTC/testnet finality before BTD rights transfer, source unlock, or source-bearing repository delivery.", + "sourceRoots": [ + "packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts", + "packages/pipelines/asset-pack/src/__tests__/asset-pack-settlement-rights-delivery.test.ts", + "packages/btd/src/receipts.ts" + ], + "emittedTypes": [ + "AssetPackSettlementFinalityReceipt", + "BtdRightsTransferReceipt" + ], + "requiredEvidence": [ + "finalityState", + "confirmed", + "Rights transfer receipt requires confirmed BTC fee finality" + ], + "rowRoot": "v42-settlement-rights-delivery-row:48bb991b2af8b770ed85c7f6", + "sourceSafetyClass": "source_safe_v42_settlement_rights_delivery_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourcePayloadSerialized": 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": "rights:btd-read-right-transfer", + "purpose": "Transfer the BTD read right to the paying reader only after the quote, payment, finality, and conservation checks agree.", + "sourceRoots": [ + "packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts", + "packages/btd/src/receipts.ts", + "packages/pipelines/asset-pack/src/__tests__/asset-pack-settlement-rights-delivery.test.ts" + ], + "emittedTypes": [ + "BtdRightsTransferReceipt", + "BtdReadReceipt" + ], + "requiredEvidence": [ + "buildBtdRightsTransferReceipt", + "buildBtdReadReceipt", + "paid_unlocked", + "licensed_read" + ], + "rowRoot": "v42-settlement-rights-delivery-row:74d15d0a778a706f9859887e", + "sourceSafetyClass": "source_safe_v42_settlement_rights_delivery_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourcePayloadSerialized": 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": "compensation:source-to-shares-conservation", + "purpose": "Allocate BTC sats to selected fit deposits with deterministic source-to-shares conservation and proof roots.", + "sourceRoots": [ + "packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts", + "packages/btd/src/source-to-shares.ts", + "packages/pipelines/asset-pack/src/__tests__/asset-pack-settlement-rights-delivery.test.ts" + ], + "emittedTypes": [ + "SourceToSharesProof", + "settlementConservation" + ], + "requiredEvidence": [ + "buildSourceToSharesProof", + "settlementAllocations", + "balanced" + ], + "rowRoot": "v42-settlement-rights-delivery-row:8d69b6c060bb87916dd00328", + "sourceSafetyClass": "source_safe_v42_settlement_rights_delivery_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourcePayloadSerialized": 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": "delivery:source-bearing-pull-request-unlock", + "purpose": "Unlock the source-bearing pull request only after payment finality, BTD rights, compensation, and reconciliation readbacks pass.", + "sourceRoots": [ + "packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts", + "packages/btd/src/settlement.ts", + "uapi/app/terminal/TerminalDepositReadWorkbench.tsx" + ], + "emittedTypes": [ + "AssetPackDeliveryUnlockReceipt", + "AssetPackSettlementUnlock" + ], + "requiredEvidence": [ + "source_bearing_pull_request_ready", + "pull_request_after_settlement", + "sourceBearingDeliveryVisibleToReader" + ], + "rowRoot": "v42-settlement-rights-delivery-row:9cbc9e6c25316593954f2c88", + "sourceSafetyClass": "source_safe_v42_settlement_rights_delivery_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourcePayloadSerialized": 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": "sync:ledger-database-object-storage-reconciliation", + "purpose": "Synchronize ledger facts, database projections, object storage roots, and staging-testnet readback before delivery is visible.", + "sourceRoots": [ + "packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts", + "packages/btd/src/reconciliation.ts", + "uapi/app/api/pipeline-harness/asset-pack/runner.ts" + ], + "emittedTypes": [ + "LedgerDatabaseReconciliationReport" + ], + "requiredEvidence": [ + "reconcileLedgerDatabaseProjection", + "buildSupabaseStagingTestnetProjectionReadback", + "tkpyosihuouusyaxtbau" + ], + "rowRoot": "v42-settlement-rights-delivery-row:51d4635144f588839effe4f4", + "sourceSafetyClass": "source_safe_v42_settlement_rights_delivery_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourcePayloadSerialized": 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": "repair:fail-closed-settlement-posture", + "purpose": "Represent underpayment, missing finality, projection drift, and missing delivery as repairable blockers without leaking source.", + "sourceRoots": [ + "packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts", + "packages/pipelines/asset-pack/src/__tests__/asset-pack-settlement-rights-delivery.test.ts" + ], + "emittedTypes": [ + "AssetPackSettlementRightsDeliveryRepairPosture" + ], + "requiredEvidence": [ + "blocked_until_payment_finality", + "blocked_until_compensation_conservation", + "blocked_until_projection_repair" + ], + "rowRoot": "v42-settlement-rights-delivery-row:b5698bf4f3b93121a624bd5f", + "sourceSafetyClass": "source_safe_v42_settlement_rights_delivery_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourcePayloadSerialized": 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-settlement-summary", + "purpose": "Summarize payment, finality, rights, source-to-shares, delivery, reconciliation, replay, and storage roots through the harness route.", + "sourceRoots": [ + "uapi/app/api/pipeline-harness/asset-pack/runner.ts", + "uapi/tests/api/pipelineHarnessRoute.test.ts" + ], + "emittedTypes": [ + "assetPackSettlementRightsDeliveryBoundary", + "assetPackDeliveryUnlock" + ], + "requiredEvidence": [ + "summarizeAssetPackSettlementRightsDeliveryBoundary", + "assetPackLedgerDatabaseStorageReconciliation" + ], + "rowRoot": "v42-settlement-rights-delivery-row:7fffe81a5aff0562ee30737d", + "sourceSafetyClass": "source_safe_v42_settlement_rights_delivery_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourcePayloadSerialized": 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-settlement-readback", + "purpose": "Render settlement rights, compensation, delivery, replay, and reconciliation readback in Terminal expandable metadata.", + "sourceRoots": [ + "uapi/app/terminal/TerminalDepositReadWorkbench.tsx", + "uapi/app/terminal/terminal-pipeline-harness-client.ts", + "uapi/tests/terminalPipelineHarnessClient.test.ts" + ], + "emittedTypes": [ + "assetPackSettlementBoundaryRows", + "TerminalReadFitsFindingSynthesisHarnessStreamSnapshot" + ], + "requiredEvidence": [ + "Settlement rights, compensation, and delivery", + "settlement-boundary", + "delivery-unlock" + ], + "rowRoot": "v42-settlement-rights-delivery-row:4a0ae64335797f60b63c3742", + "sourceSafetyClass": "source_safe_v42_settlement_rights_delivery_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourcePayloadSerialized": 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": "host:live-harness-boundary-materialization", + "purpose": "Materialize AssetPackPreviewBoundary and AssetPackSettlementRightsDeliveryBoundary inside the live sandbox harness output.", + "sourceRoots": [ + "packages/pipeline-hosts/src/asset-pack-harness.ts", + "packages/pipeline-hosts/src/__tests__/asset-pack-harness.test.ts" + ], + "emittedTypes": [ + "assetPackPreviewBoundary", + "assetPackSettlementRightsDeliveryBoundary" + ], + "requiredEvidence": [ + "buildAssetPackSettlementRightsDeliveryBoundary", + "persistAssetPackSettlementRightsDeliveryBoundary" + ], + "rowRoot": "v42-settlement-rights-delivery-row:e3999d37f9ba9d8d0c3fd9de", + "sourceSafetyClass": "source_safe_v42_settlement_rights_delivery_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourcePayloadSerialized": 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 6 closure to generated artifact, protocol test, package tests, harness route tests, docs, and workflow checks.", + "sourceRoots": [ + ".github/workflows/bitcode-gate-quality.yml", + ".github/workflows/bitcode-canon-quality.yml", + "package.json" + ], + "emittedTypes": [ + "V42SettlementRightsDelivery" + ], + "requiredEvidence": [ + "check-v42-gate6-settlement-rights-delivery.mjs", + "v42-settlement-rights-delivery" + ], + "rowRoot": "v42-settlement-rights-delivery-row:b09ae61cf4664f8a3914be8e", + "sourceSafetyClass": "source_safe_v42_settlement_rights_delivery_metadata", + "sourceSafeMetadataOnly": true, + "protectedSourcePayloadSerialized": 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" + ] + } + ], + "predicateResults": [ + { + "id": "boundary-defines-core-types", + "sourcePath": "packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts", + "passed": true + }, + { + "id": "boundary-composes-btd-primitives", + "sourcePath": "packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts", + "passed": true + }, + { + "id": "boundary-builds-source-to-shares", + "sourcePath": "packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts", + "passed": true + }, + { + "id": "boundary-builds-reconciliation", + "sourcePath": "packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts", + "passed": true + }, + { + "id": "boundary-source-safety", + "sourcePath": "packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts", + "passed": true + }, + { + "id": "tests-cover-confirmed-delivery", + "sourcePath": "packages/pipelines/asset-pack/src/__tests__/asset-pack-settlement-rights-delivery.test.ts", + "passed": true + }, + { + "id": "tests-cover-underpayment", + "sourcePath": "packages/pipelines/asset-pack/src/__tests__/asset-pack-settlement-rights-delivery.test.ts", + "passed": true + }, + { + "id": "tests-cover-finality", + "sourcePath": "packages/pipelines/asset-pack/src/__tests__/asset-pack-settlement-rights-delivery.test.ts", + "passed": true + }, + { + "id": "tests-cover-reconciliation-drift", + "sourcePath": "packages/pipelines/asset-pack/src/__tests__/asset-pack-settlement-rights-delivery.test.ts", + "passed": true + }, + { + "id": "host-materializes-boundary", + "sourcePath": "packages/pipeline-hosts/src/asset-pack-harness.ts", + "passed": true + }, + { + "id": "host-test-covers-boundary", + "sourcePath": "packages/pipeline-hosts/src/__tests__/asset-pack-harness.test.ts", + "passed": true + }, + { + "id": "route-summarizes-boundary", + "sourcePath": "uapi/app/api/pipeline-harness/asset-pack/runner.ts", + "passed": true + }, + { + "id": "route-test-covers-boundary", + "sourcePath": "uapi/tests/api/pipelineHarnessRoute.test.ts", + "passed": true + }, + { + "id": "terminal-renders-boundary", + "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-test-covers-boundary", + "sourcePath": "uapi/tests/terminalPipelineHarnessClient.test.ts", + "passed": true + }, + { + "id": "v42-spec-expanded", + "sourcePath": "BITCODE_SPEC_V42.md", + "passed": true + }, + { + "id": "v42-delta-expanded", + "sourcePath": "BITCODE_SPEC_V42_DELTA.md", + "passed": true + }, + { + "id": "v42-notes-expanded", + "sourcePath": "BITCODE_SPEC_V42_NOTES.md", + "passed": true + }, + { + "id": "v42-parity-implemented", + "sourcePath": "BITCODE_SPEC_V42_PARITY_MATRIX.md", + "passed": true + }, + { + "id": "roadmap-advanced-to-gate6", + "sourcePath": "SPECIFICATIONS_ROADMAP.md", + "passed": true + }, + { + "id": "readmes-document-gate6", + "sourcePath": "README.md", + "passed": true + }, + { + "id": "protocol-exports-gate6", + "sourcePath": "packages/protocol/src/index.js", + "passed": true + }, + { + "id": "package-scripts-wire-gate6", + "sourcePath": "package.json", + "passed": true + }, + { + "id": "workflows-run-gate6-check", + "sourcePath": ".github/workflows/bitcode-gate-quality.yml", + "passed": true + } + ], + "coverage": { + "rowCount": 11, + "sourceSafetyVerdict": "source-safe-v42-settlement-rights-delivery-metadata", + "runtimeType": "AssetPackSettlementRightsDeliveryBoundary", + "paymentType": "AssetPackSettlementPaymentObservation", + "finalityType": "AssetPackSettlementFinalityReceipt", + "rightsTransferType": "BtdRightsTransferReceipt", + "readReceiptType": "BtdReadReceipt", + "sourceToSharesType": "SourceToSharesProof", + "reconciliationType": "LedgerDatabaseReconciliationReport", + "deliveryType": "AssetPackDeliveryUnlockReceipt", + "replayType": "AssetPackSettlementRightsDeliveryReplayReceipt", + "requiredReadbacksBeforeUnlock": [ + "btc_payment_observation", + "settlement_finality", + "source_to_shares_compensation", + "btd_rights_transfer", + "ledger_database_storage_reconciliation" + ], + "stagingProjectRef": "tkpyosihuouusyaxtbau", + "stagingRestHost": "https://tkpyosihuouusyaxtbau.supabase.co/rest/v1/", + "sourceSafeMetadataOnly": true, + "protectedSourcePayloadSerialized": false, + "unpaidAssetPackSourceVisible": false, + "walletPrivateMaterialVisible": false, + "settlementPrivatePayloadVisible": false, + "credentialsSerialized": false, + "hostBoundaryMaterializationCovered": true, + "routeReadbackCovered": true, + "terminalReadbackCovered": true, + "confirmedPaymentCovered": true, + "underpaymentBlockedCovered": true, + "finalityBlockedCovered": true, + "reconciliationRepairCovered": true, + "failedPredicateIds": [] + }, + "passed": true, + "artifactRoot": "v42-settlement-rights-delivery:09c7a0fd5dd0b1952aa75eb9" +} diff --git a/.github/workflows/bitcode-canon-quality.yml b/.github/workflows/bitcode-canon-quality.yml index 23a88608..e8a99519 100644 --- a/.github/workflows/bitcode-canon-quality.yml +++ b/.github/workflows/bitcode-canon-quality.yml @@ -307,6 +307,9 @@ jobs: 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 + if [ -f scripts/check-v42-gate6-settlement-rights-delivery.mjs ]; then + node scripts/check-v42-gate6-settlement-rights-delivery.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 9c2a28c0..1ebd2c12 100644 --- a/.github/workflows/bitcode-gate-quality.yml +++ b/.github/workflows/bitcode-gate-quality.yml @@ -436,6 +436,9 @@ jobs: 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 + if [ -f scripts/check-v42-gate6-settlement-rights-delivery.mjs ]; then + node scripts/check-v42-gate6-settlement-rights-delivery.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 316527d3..81be977c 100644 --- a/BITCODE_SPEC_V42.md +++ b/BITCODE_SPEC_V42.md @@ -148,6 +148,9 @@ The artifact binds the accepted-Need gate, many-channel Depository search, candi Gate 6 must make purchase and delivery reliable. The accepted user path is: review the AssetPack preview, choose to buy, receive settlement instructions, observe BTC/testnet settlement in the admitted lane, transfer BTD rights to the Reader, unlock source-bearing AssetPack delivery, and create the repository pull request. It must prove ledger/database/object-storage synchronization, delivery locks, no pre-settlement source leakage, compensation accounting, repair actions, and operator readback. +Gate 6 is implemented by `.bitcode/v42-settlement-rights-delivery.json`. +The artifact binds `AssetPackSettlementRightsDeliveryBoundary`, `AssetPackSettlementPaymentObservation`, confirmed BTC/testnet finality, `BtdRightsTransferReceipt`, `BtdReadReceipt`, `SourceToSharesProof`, `AssetPackDeliveryUnlockReceipt`, ledger/database/object-storage reconciliation, live harness materialization, source-safe route summaries, and Terminal settlement rights readback. +Source-bearing AssetPack delivery remains withheld until payment, finality, BTD rights transfer, source-to-shares compensation conservation, reconciliation readback, and pull-request delivery all agree. ## V42 Gate 7 AI-Reading Dominant Demonstration MVP diff --git a/BITCODE_SPEC_V42_DELTA.md b/BITCODE_SPEC_V42_DELTA.md index 177b9a08..f0e9c5a7 100644 --- a/BITCODE_SPEC_V42_DELTA.md +++ b/BITCODE_SPEC_V42_DELTA.md @@ -57,6 +57,7 @@ Gate 5 now binds `ReadFitsFindingRuntime`, many-channel Depository search, `Asse ### Gate 6: Settlement Rights Transfer And Repository Delivery Closure Implement and prove purchase, settlement observation, BTD rights transfer, source unlock, repository pull request delivery, compensation accounting, and repair posture. +Gate 6 now binds settlement rights transfer through `AssetPackSettlementRightsDeliveryBoundary` to the live harness, route summary, Terminal readback, source-to-shares conservation, BTD read/right receipts, and ledger/database/object-storage reconciliation through `.bitcode/v42-settlement-rights-delivery.json`. ### Gate 7: AI-Reading Dominant Demonstration MVP diff --git a/BITCODE_SPEC_V42_NOTES.md b/BITCODE_SPEC_V42_NOTES.md index 139f5316..27b6ba24 100644 --- a/BITCODE_SPEC_V42_NOTES.md +++ b/BITCODE_SPEC_V42_NOTES.md @@ -69,6 +69,13 @@ Selected-fit provenance carries selected candidate ids, fit deposit ids, query a `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. +## Gate 6 implementation notes + +Gate 6 closes the paid settlement and source-bearing delivery boundary. +`AssetPackSettlementRightsDeliveryBoundary` is the reviewable post-payment object: it records BTC payment observation, confirmed finality, source-to-shares compensation, BTD read and rights transfer receipts, settlement unlock, source-bearing pull-request delivery unlock, ledger/database/object-storage reconciliation, storage projection, replay receipt, and repair posture. +The live Vercel Sandbox harness materializes this boundary after ledger readback, the route summarizes it as source-safe metadata, and Terminal renders settlement rights, source-to-shares compensation, delivery unlock, reconciliation, and replay roots. +Protected source, private settlement payloads, wallet private material, raw protected prompts, raw provider responses, and credentials remain absent from the generated artifact, route summaries, and pre-delivery readback. + ## 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 65c010b7..174f9176 100644 --- a/BITCODE_SPEC_V42_PARITY_MATRIX.md +++ b/BITCODE_SPEC_V42_PARITY_MATRIX.md @@ -37,7 +37,7 @@ This matrix records the reliable MVP product surfaces that must become promotion | 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 | `.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 | +| Settlement and delivery | BTC/BTD settlement, rights transfer, compensation, and repository PR delivery are synchronized | `.bitcode/v42-settlement-rights-delivery.json`, `AssetPackSettlementRightsDeliveryBoundary`, live harness settlement boundary, route summary, Terminal settlement readback | implemented | | 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 | | Promotion readiness | V42 proof and workflow promotion ready | later V42 Gate 9 artifact | draft-required | @@ -51,7 +51,7 @@ This matrix records the reliable MVP product surfaces that must become promotion | 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 | implemented | -| Gate 6 | Settlement rights transfer and repository delivery closure artifact | draft-required | +| Gate 6 | Settlement rights transfer and repository delivery closure artifact | implemented | | Gate 7 | AI-reading dominant demonstration MVP artifact | draft-required | | Gate 8 | Local and staging-testnet full MVP rehearsal artifact | draft-required | | Gate 9 | Promotion readiness artifact and workflow | draft-required | diff --git a/README.md b/README.md index dbc40710..43f245fd 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,17 @@ readback, `.bitcode/v42-readfitsfinding-preview-quote.json`, and 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. +V42 Gate 6 adds paid settlement, BTD rights transfer, source-to-shares +compensation, and repository delivery closure with +`AssetPackSettlementRightsDeliveryBoundary`, +`.bitcode/v42-settlement-rights-delivery.json`, and `check:v42-gate6`. +The boundary observes the quote payment, confirms BTC/testnet finality, +allocates depositor compensation, records BTD read-right receipts, reconciles +ledger/database/object-storage projections, and unlocks source-bearing +pull-request delivery only after paid readback agrees. Route summaries and +Terminal detail remain source-safe metadata: unpaid AssetPack source, wallet +private material, private settlement payloads, credentials, raw protected +prompts, and raw provider responses are withheld. 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 2d2fe120..173d0d2d 100644 --- a/SPECIFICATIONS_ROADMAP.md +++ b/SPECIFICATIONS_ROADMAP.md @@ -5,14 +5,15 @@ - 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 5 ReadFitsFinding AssetPack Preview And Quote Closure. -- Next queued gate after V42 Gate 5: V42 Gate 6 Settlement Rights Transfer And Repository Delivery Closure. +- Current working gate: V42 Gate 6 Settlement Rights Transfer And Repository Delivery Closure. +- Next queued gate after V42 Gate 6: V42 Gate 7 AI-Reading Dominant Demonstration MVP. - 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`. +- V42 Gate 6 closure anchor: reliable MVP experience now owns settlement rights transfer and repository delivery closure through `AssetPackSettlementRightsDeliveryBoundary`, BTC payment observation/finality, BTD read and rights transfer receipts, source-to-shares compensation conservation, delivery unlock, ledger/database/object-storage reconciliation, live harness materialization, route settlement summaries, Terminal settlement rights readback, deterministic `.bitcode/v42-settlement-rights-delivery.json`, focused package/host/route/protocol tests, workflow wiring, and `check:v42-gate6`. - 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 6593a435..0036a41c 100644 --- a/package.json +++ b/package.json @@ -316,6 +316,9 @@ "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:v42-settlement-rights-delivery": "node scripts/generate-v42-settlement-rights-delivery.mjs", + "check:v42-settlement-rights-delivery": "node scripts/generate-v42-settlement-rights-delivery.mjs --check", + "check:v42-gate6": "node scripts/check-v42-gate6-settlement-rights-delivery.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/pipeline-hosts/src/__tests__/asset-pack-harness.test.ts b/packages/pipeline-hosts/src/__tests__/asset-pack-harness.test.ts index 29e84e66..10a43257 100644 --- a/packages/pipeline-hosts/src/__tests__/asset-pack-harness.test.ts +++ b/packages/pipeline-hosts/src/__tests__/asset-pack-harness.test.ts @@ -163,8 +163,12 @@ describe('asset-pack sandbox harness plan', () => { expect(source).toContain('pipeline-stream-event'); expect(source).toContain('synthesizeReadNeedForPipelineInput'); expect(source).toContain('buildAssetPackSourceSafePreview'); + expect(source).toContain('buildAssetPackPreviewBoundary'); + expect(source).toContain('persistAssetPackPreviewBoundary'); expect(source).toContain('buildAssetPackDisclosureReview'); expect(source).toContain('assertAssetPackDisclosureSourceSafe'); + expect(source).toContain('buildAssetPackSettlementRightsDeliveryBoundary'); + expect(source).toContain('persistAssetPackSettlementRightsDeliveryBoundary'); expect(source).toContain('buildAssetPackSettlementUnlock'); expect(source).toContain('applyAssetPackSettlementUnlockToPreview'); expect(source).toContain('acceptedReadNeed: readNeed'); @@ -212,6 +216,9 @@ describe('asset-pack sandbox harness plan', () => { expect(source).toContain('asset_pack_source_safe_preview'); expect(source).toContain('staging-testnet-readback-'); expect(source).toContain("execution.store('asset-pack/settlement', 'ledgerDatabaseReconciliation'"); + expect(source).toContain('assetPackSettlementRightsDeliveryBoundary'); + expect(source).toContain('assetPackSettlementReplayReceipt'); + expect(source).toContain('assetPackDeliveryUnlock'); expect(source).toContain("execution.store('asset-pack/settlement', 'organizationAuthority'"); expect(source).toContain('organizationAuthorityDecision'); expect(source).toContain('organizationAuthorityRoot'); diff --git a/packages/pipeline-hosts/src/asset-pack-harness.ts b/packages/pipeline-hosts/src/asset-pack-harness.ts index d1d943ef..d0a64084 100644 --- a/packages/pipeline-hosts/src/asset-pack-harness.ts +++ b/packages/pipeline-hosts/src/asset-pack-harness.ts @@ -1678,10 +1678,14 @@ try { assetPackPipeline, acceptReadNeed, buildAssetPackDisclosureReview, + buildAssetPackPreviewBoundary, + buildAssetPackSettlementRightsDeliveryBoundary, buildAssetPackSourceSafePreview, buildReadingPipelineObservabilityInventory, assertAssetPackDisclosureSourceSafe, isAcceptedReadNeed, + persistAssetPackPreviewBoundary, + persistAssetPackSettlementRightsDeliveryBoundary, resolveReadingPipelineTelemetryProjection, summarizeReadingPipelineObservabilityCoverage, synthesizeReadNeedForPipelineInput, @@ -1824,6 +1828,16 @@ try { const sourceSafeDisclosureReview = buildAssetPackDisclosureReview({ preview: sourceSafePreview }); assertAssetPackDisclosureSourceSafe(sourceSafeDisclosureReview); execution.store('asset-pack/preview', 'disclosureReview', sourceSafeDisclosureReview); + let assetPackPreviewBoundary = output?.assetPackPreviewBoundary || null; + if (!assetPackPreviewBoundary || assetPackPreviewBoundary.schema !== 'bitcode.asset-pack.preview-boundary') { + assetPackPreviewBoundary = buildAssetPackPreviewBoundary({ + need: readNeed, + fitResult, + sourceSafePreview, + pullRequestTarget: pullRequestUrl || null, + }); + } + persistAssetPackPreviewBoundary(execution, assetPackPreviewBoundary); const pipelineResultReasons = Array.isArray(fitResult?.resultReasons) ? fitResult.resultReasons : Array.isArray(depositorySearch?.resultReasons) @@ -2014,26 +2028,64 @@ try { reason: settlementUnlock.reason, }); assertAssetPackDisclosureSourceSafe(assetPackDisclosureReview); - const organizationAuthority = [ - evaluateBtdOrganizationInterfaceAuthority({ - actorId: userId || readerWalletId, - organizationId: manifest.organizationId || manifest.organization?.id || 'staging-testnet-organization', - organizationRole: 'admin', - organizationPermissionGrants: ['asset_pack:deliver'], - interfaceSurface: 'terminal', - action: 'deliver_asset_pack', - walletId: readerWalletId, - targetAnchor: pullRequestUrl || null, - readAccessDecision: settlementUnlock.sourceAvailable - ? { - decision: 'licensed_read', - accessPolicyHash: ledgerSettlement.accessPolicyHash || sourceSafePreview?.accessPolicy?.accessPolicyHash || 'policy-pending', - reason: settlementUnlock.reason, - } - : null, - settlementState: settlementUnlock.sourceAvailable ? 'settled' : 'pending', - confirmed: ledgerSettlement.settlementAdmissible === true, - repairApprovalState: 'not_required', + const settlementFinalityConfirmed = + ledgerSettlement?.status === 'settled' && ledgerSettlement?.settlementAdmissible === true; + const settlementObservedSats = + Number(ledgerSettlement?.btcFee?.satsPaid || assetPackPreviewBoundary?.quoteReceipt?.sats || 0) || + Number(assetPackPreviewBoundary?.quoteReceipt?.sats || 0); + const assetPackSettlementRightsDeliveryBoundary = assetPackPreviewBoundary + ? buildAssetPackSettlementRightsDeliveryBoundary({ + previewBoundary: assetPackPreviewBoundary, + paymentObservation: { + paymentReceiptId: ledgerSettlement?.btcFeeReceiptId || undefined, + payerWalletId: readerWalletId, + payeeWalletId: depositorWalletId, + btcNetwork: ledgerSettlement?.btcFee?.network || 'testnet', + expectedSats: assetPackPreviewBoundary.quoteReceipt.sats, + observedDebitSats: settlementObservedSats, + observedCreditSats: settlementObservedSats, + txid: + ledgerSettlement?.btcFee?.txid || + ledgerSettlement?.btcFeeReceiptId || + 'staging-testnet-' + runId, + }, + finality: { + finalityState: settlementFinalityConfirmed ? 'confirmed' : 'prepared', + confirmations: settlementFinalityConfirmed ? 1 : 0, + blockHeight: null, + }, + readerWalletId, + depositorWalletId, + orderId: ledgerSettlement?.ownershipEventId || undefined, + readId: manifest.read?.id || runId, + readLicenseId: ledgerSettlement?.readLicenseId || undefined, + ledgerAnchorId: ledgerSettlement?.ledgerAnchorId || undefined, + pullRequestTarget: pullRequestUrl || null, + }) + : null; + if (assetPackSettlementRightsDeliveryBoundary) { + persistAssetPackSettlementRightsDeliveryBoundary(execution, assetPackSettlementRightsDeliveryBoundary); + } + const organizationAuthority = [ + evaluateBtdOrganizationInterfaceAuthority({ + actorId: userId || readerWalletId, + organizationId: manifest.organizationId || manifest.organization?.id || 'staging-testnet-organization', + organizationRole: 'admin', + organizationPermissionGrants: ['asset_pack:deliver'], + interfaceSurface: 'terminal', + action: 'deliver_asset_pack', + walletId: readerWalletId, + targetAnchor: pullRequestUrl || null, + readAccessDecision: settlementUnlock.sourceAvailable + ? { + decision: 'licensed_read', + accessPolicyHash: ledgerSettlement.accessPolicyHash || sourceSafePreview?.accessPolicy?.accessPolicyHash || 'policy-pending', + reason: settlementUnlock.reason, + } + : null, + settlementState: settlementUnlock.sourceAvailable ? 'settled' : 'pending', + confirmed: ledgerSettlement.settlementAdmissible === true, + repairApprovalState: 'not_required', }), ]; execution.store('asset-pack/preview', 'sourceSafe', settledSourceSafePreview); @@ -2044,7 +2096,13 @@ try { output = { ...(output || {}), sourceSafePreview: settledSourceSafePreview, + assetPackPreviewBoundary, assetPackDisclosureReview, + assetPackSettlementRightsDeliveryBoundary, + assetPackSettlementReplayReceipt: assetPackSettlementRightsDeliveryBoundary?.replayReceipt || null, + assetPackDeliveryUnlock: assetPackSettlementRightsDeliveryBoundary?.deliveryUnlock || null, + assetPackLedgerDatabaseStorageReconciliation: + assetPackSettlementRightsDeliveryBoundary?.reconciliationReport || null, organizationAuthority, ledgerSettlement: { ...ledgerSettlement, @@ -2090,6 +2148,8 @@ try { fitResult, depositorySearch, sourceSafePreview: settledSourceSafePreview, + assetPackPreviewBoundary, + assetPackSettlementRightsDeliveryBoundary, assetPackDisclosureReview, assetPackSynthesisArtifacts: output?.assetPackSynthesisArtifacts || null, writtenAssets: output?.writtenAssets || null, diff --git a/packages/pipelines/asset-pack/README.md b/packages/pipelines/asset-pack/README.md index 9d328152..1da60ea5 100644 --- a/packages/pipelines/asset-pack/README.md +++ b/packages/pipelines/asset-pack/README.md @@ -212,6 +212,15 @@ source, raw protected prompts, raw provider responses, wallet private material, private settlement payloads, credentials, or unpaid source-bearing AssetPack content. +V42 Gate 6 binds Settlement Rights Delivery into product closure through +`.bitcode/v42-settlement-rights-delivery.json` and `check:v42-gate6`. +That proof requires paid BTC observation, finality gating, BTD rights transfer, +source-to-shares conservation, repository delivery unlock, +ledger/database/object-storage reconciliation, live harness materialization, +route readback, Terminal readback, focused package/API/protocol tests, and +source-safe docs before the paid AssetPack can cross the Reader visibility +boundary. + ## Operational Telemetry Repair Readback `ReadingOperationalTelemetryRepairReadback` is the source-safe package diff --git a/packages/protocol/README.md b/packages/protocol/README.md index 1fa40812..d56d8f35 100644 --- a/packages/protocol/README.md +++ b/packages/protocol/README.md @@ -59,6 +59,7 @@ Current exported commercial helpers include: - `V41RegistryInterpolationContracts` helpers for V41 source-safe registry composition, interpolation key, execution ancestry, tool prompt injection, context handling, and parser target contracts; - `V41ReadingPromptBenchmarkBaselines` helpers for V41 source-safe Reading prompt baseline rows across `ReadNeedComprehensionSynthesis`, `ReadFitsFindingSynthesis`, all five Reading UX steps, V38 benchmark fixtures, Gate 2 inventory roots, Gate 3 registry/interpolation roots, parser targets, deterministic scores, and source-safe disclosure tiers; - `V41PromotionReadinessReport` helpers for V41 source-safe prompt-program promotion readiness across all V41 prompt artifacts, generated proof support, workflow posture, promotion dry-run support, and active V41 / draft V42 runtime preparation; +- `V42SettlementRightsDelivery` helpers for V42 source-safe BTC payment observation, finality gating, source-to-shares compensation, BTD read-right transfer, repository delivery unlock, ledger/database/object-storage reconciliation, Terminal readback, and source-safe paid-boundary proof; - canonical proven-generation helpers; - the package app/server context used by commercial interfaces. @@ -220,6 +221,20 @@ 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. +V42 Gate 6 adds `V42SettlementRightsDelivery` through +`packages/protocol/src/canonical/v42-settlement-rights-delivery.js`, +`packages/protocol/test/v42-settlement-rights-delivery.test.js`, +`.bitcode/v42-settlement-rights-delivery.json`, +`generate:v42-settlement-rights-delivery`, +`check:v42-settlement-rights-delivery`, and `check:v42-gate6`. +The artifact is source-safe metadata only and covers paid quote observation, +BTC/testnet finality, BTD rights transfer, paid read receipts, +source-to-shares compensation conservation, repository pull-request delivery +unlock, ledger/database/object-storage reconciliation, fail-closed repair +posture, harness route summaries, Terminal settlement readback, and workflow +proof wiring without serializing protected source, unpaid AssetPack source, +wallet private material, private settlement payloads, credentials, raw +protected prompts, or raw provider responses. 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-settlement-rights-delivery.js b/packages/protocol/src/canonical/v42-settlement-rights-delivery.js new file mode 100644 index 00000000..4b621554 --- /dev/null +++ b/packages/protocol/src/canonical/v42-settlement-rights-delivery.js @@ -0,0 +1,301 @@ +// @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_SETTLEMENT_RIGHTS_DELIVERY_ARTIFACT_PATH = + '.bitcode/v42-settlement-rights-delivery.json'; +export const V42_SETTLEMENT_RIGHTS_DELIVERY_SCHEMA_ID = + 'bitcode.v42.settlementRightsDelivery.v1'; +export const V42_SETTLEMENT_RIGHTS_DELIVERY_VERSION = 'V42'; +export const V42_SETTLEMENT_RIGHTS_DELIVERY_CURRENT_TARGET = 'V41'; +export const V42_SETTLEMENT_RIGHTS_DELIVERY_SOURCE_SAFETY_VERDICT = + 'source-safe-v42-settlement-rights-delivery-metadata'; + +export const V42_SETTLEMENT_RIGHTS_DELIVERY_ROW_IDS = Object.freeze([ + 'purchase:quote-to-payment-observation', + 'finality:btc-testnet-confirmation-gate', + 'rights:btd-read-right-transfer', + 'compensation:source-to-shares-conservation', + 'delivery:source-bearing-pull-request-unlock', + 'sync:ledger-database-object-storage-reconciliation', + 'repair:fail-closed-settlement-posture', + 'route:harness-settlement-summary', + 'ui:terminal-settlement-readback', + 'host:live-harness-boundary-materialization', + 'proof:tests-artifact-workflow', +]); + +const SOURCE_ROOTS = Object.freeze({ + boundary: 'packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts', + boundaryTest: 'packages/pipelines/asset-pack/src/__tests__/asset-pack-settlement-rights-delivery.test.ts', + previewBoundary: 'packages/pipelines/asset-pack/src/asset-pack-preview-boundary.ts', + postprocess: 'packages/pipelines/asset-pack/src/postprocess.ts', + packageIndex: 'packages/pipelines/asset-pack/src/index.ts', + pipelineHostHarness: 'packages/pipeline-hosts/src/asset-pack-harness.ts', + pipelineHostHarnessTest: 'packages/pipeline-hosts/src/__tests__/asset-pack-harness.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', + btdReceipts: 'packages/btd/src/receipts.ts', + btdSourceToShares: 'packages/btd/src/source-to-shares.ts', + btdSettlement: 'packages/btd/src/settlement.ts', + btdReconciliation: 'packages/btd/src/reconciliation.ts', + protocolReadme: 'packages/protocol/README.md', + assetPackReadme: 'packages/pipelines/asset-pack/README.md', + terminalReadme: 'uapi/app/terminal/README.md', + rootReadme: 'README.md', + 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', + packageJson: 'package.json', + protocolIndex: 'packages/protocol/src/index.js', + protocolTypes: 'packages/protocol/src/index.d.ts', + gateWorkflow: '.github/workflows/bitcode-gate-quality.yml', + canonWorkflow: '.github/workflows/bitcode-canon-quality.yml', +}); + +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-settlement-rights-delivery-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_v42_settlement_rights_delivery_metadata', + sourceSafeMetadataOnly: true, + protectedSourcePayloadSerialized: false, + rawProtectedPromptVisible: false, + rawProviderResponseVisible: false, + unpaidAssetPackSourceVisible: false, + walletPrivateMaterialVisible: false, + settlementPrivatePayloadVisible: false, + credentialsSerialized: false, + forbiddenPayloadClasses: [...FORBIDDEN_PAYLOAD_CLASSES], + }; +} + +export const V42_SETTLEMENT_RIGHTS_DELIVERY_ROWS = Object.freeze([ + row({ + rowId: 'purchase:quote-to-payment-observation', + purpose: + 'Convert the Gate 5 deterministic quote into a reader BTC payment observation without server custody or private wallet material.', + sourceRoots: [SOURCE_ROOTS.boundary, SOURCE_ROOTS.previewBoundary, SOURCE_ROOTS.harnessRunner], + emittedTypes: ['AssetPackSettlementPaymentObservation', 'AssetPackPreviewQuoteReceipt'], + requiredEvidence: ['expectedSats', 'observedDebitSats', 'observedCreditSats', 'serverCustody: false'], + }), + row({ + rowId: 'finality:btc-testnet-confirmation-gate', + purpose: + 'Require confirmed BTC/testnet finality before BTD rights transfer, source unlock, or source-bearing repository delivery.', + sourceRoots: [SOURCE_ROOTS.boundary, SOURCE_ROOTS.boundaryTest, SOURCE_ROOTS.btdReceipts], + emittedTypes: ['AssetPackSettlementFinalityReceipt', 'BtdRightsTransferReceipt'], + requiredEvidence: ['finalityState', 'confirmed', 'Rights transfer receipt requires confirmed BTC fee finality'], + }), + row({ + rowId: 'rights:btd-read-right-transfer', + purpose: + 'Transfer the BTD read right to the paying reader only after the quote, payment, finality, and conservation checks agree.', + sourceRoots: [SOURCE_ROOTS.boundary, SOURCE_ROOTS.btdReceipts, SOURCE_ROOTS.boundaryTest], + emittedTypes: ['BtdRightsTransferReceipt', 'BtdReadReceipt'], + requiredEvidence: ['buildBtdRightsTransferReceipt', 'buildBtdReadReceipt', 'paid_unlocked', 'licensed_read'], + }), + row({ + rowId: 'compensation:source-to-shares-conservation', + purpose: + 'Allocate BTC sats to selected fit deposits with deterministic source-to-shares conservation and proof roots.', + sourceRoots: [SOURCE_ROOTS.boundary, SOURCE_ROOTS.btdSourceToShares, SOURCE_ROOTS.boundaryTest], + emittedTypes: ['SourceToSharesProof', 'settlementConservation'], + requiredEvidence: ['buildSourceToSharesProof', 'settlementAllocations', 'balanced'], + }), + row({ + rowId: 'delivery:source-bearing-pull-request-unlock', + purpose: + 'Unlock the source-bearing pull request only after payment finality, BTD rights, compensation, and reconciliation readbacks pass.', + sourceRoots: [SOURCE_ROOTS.boundary, SOURCE_ROOTS.btdSettlement, SOURCE_ROOTS.terminalWorkbench], + emittedTypes: ['AssetPackDeliveryUnlockReceipt', 'AssetPackSettlementUnlock'], + requiredEvidence: ['source_bearing_pull_request_ready', 'pull_request_after_settlement', 'sourceBearingDeliveryVisibleToReader'], + }), + row({ + rowId: 'sync:ledger-database-object-storage-reconciliation', + purpose: + 'Synchronize ledger facts, database projections, object storage roots, and staging-testnet readback before delivery is visible.', + sourceRoots: [SOURCE_ROOTS.boundary, SOURCE_ROOTS.btdReconciliation, SOURCE_ROOTS.harnessRunner], + emittedTypes: ['LedgerDatabaseReconciliationReport'], + requiredEvidence: ['reconcileLedgerDatabaseProjection', 'buildSupabaseStagingTestnetProjectionReadback', 'tkpyosihuouusyaxtbau'], + }), + row({ + rowId: 'repair:fail-closed-settlement-posture', + purpose: + 'Represent underpayment, missing finality, projection drift, and missing delivery as repairable blockers without leaking source.', + sourceRoots: [SOURCE_ROOTS.boundary, SOURCE_ROOTS.boundaryTest], + emittedTypes: ['AssetPackSettlementRightsDeliveryRepairPosture'], + requiredEvidence: ['blocked_until_payment_finality', 'blocked_until_compensation_conservation', 'blocked_until_projection_repair'], + }), + row({ + rowId: 'route:harness-settlement-summary', + purpose: + 'Summarize payment, finality, rights, source-to-shares, delivery, reconciliation, replay, and storage roots through the harness route.', + sourceRoots: [SOURCE_ROOTS.harnessRunner, SOURCE_ROOTS.harnessRouteTest], + emittedTypes: ['assetPackSettlementRightsDeliveryBoundary', 'assetPackDeliveryUnlock'], + requiredEvidence: ['summarizeAssetPackSettlementRightsDeliveryBoundary', 'assetPackLedgerDatabaseStorageReconciliation'], + }), + row({ + rowId: 'ui:terminal-settlement-readback', + purpose: + 'Render settlement rights, compensation, delivery, replay, and reconciliation readback in Terminal expandable metadata.', + sourceRoots: [SOURCE_ROOTS.terminalWorkbench, SOURCE_ROOTS.terminalHarnessClient, SOURCE_ROOTS.terminalHarnessClientTest], + emittedTypes: ['assetPackSettlementBoundaryRows', 'TerminalReadFitsFindingSynthesisHarnessStreamSnapshot'], + requiredEvidence: ['Settlement rights, compensation, and delivery', 'settlement-boundary', 'delivery-unlock'], + }), + row({ + rowId: 'host:live-harness-boundary-materialization', + purpose: + 'Materialize AssetPackPreviewBoundary and AssetPackSettlementRightsDeliveryBoundary inside the live sandbox harness output.', + sourceRoots: [SOURCE_ROOTS.pipelineHostHarness, SOURCE_ROOTS.pipelineHostHarnessTest], + emittedTypes: ['assetPackPreviewBoundary', 'assetPackSettlementRightsDeliveryBoundary'], + requiredEvidence: ['buildAssetPackSettlementRightsDeliveryBoundary', 'persistAssetPackSettlementRightsDeliveryBoundary'], + }), + row({ + rowId: 'proof:tests-artifact-workflow', + purpose: + 'Bind V42 Gate 6 closure to generated artifact, protocol test, package tests, harness route tests, docs, and workflow checks.', + sourceRoots: [SOURCE_ROOTS.gateWorkflow, SOURCE_ROOTS.canonWorkflow, SOURCE_ROOTS.packageJson], + emittedTypes: ['V42SettlementRightsDelivery'], + requiredEvidence: ['check-v42-gate6-settlement-rights-delivery.mjs', 'v42-settlement-rights-delivery'], + }), +]); + +function buildPredicateResults(repoRoot) { + const sources = Object.fromEntries( + Object.entries(SOURCE_ROOTS).map(([key, sourcePath]) => [key, readSource(repoRoot, sourcePath)]), + ); + + return [ + predicateResult('boundary-defines-core-types', SOURCE_ROOTS.boundary, sources.boundary.includes('AssetPackSettlementRightsDeliveryBoundary') && sources.boundary.includes('AssetPackSettlementPaymentObservation') && sources.boundary.includes('AssetPackDeliveryUnlockReceipt')), + predicateResult('boundary-composes-btd-primitives', SOURCE_ROOTS.boundary, sources.boundary.includes('buildBtdRightsTransferReceipt') && sources.boundary.includes('buildBtdReadReceipt') && sources.boundary.includes('buildAssetPackSettlementUnlock')), + predicateResult('boundary-builds-source-to-shares', SOURCE_ROOTS.boundary, sources.boundary.includes('buildSourceToSharesProof') && sources.btdSourceToShares.includes('settlementConservation')), + predicateResult('boundary-builds-reconciliation', SOURCE_ROOTS.boundary, sources.boundary.includes('reconcileLedgerDatabaseProjection') && sources.btdReconciliation.includes('objectStorageArtifacts')), + predicateResult('boundary-source-safety', SOURCE_ROOTS.boundary, sources.boundary.includes('source_safe_settlement_rights_delivery_boundary') && sources.boundary.includes('walletPrivateMaterialVisible: false')), + predicateResult('tests-cover-confirmed-delivery', SOURCE_ROOTS.boundaryTest, sources.boundaryTest.includes('unlocks BTD rights') && sources.boundaryTest.includes('settlement_delivered')), + predicateResult('tests-cover-underpayment', SOURCE_ROOTS.boundaryTest, sources.boundaryTest.includes('fails closed when BTC payment is underpaid') && sources.boundaryTest.includes('blocked_until_compensation_conservation')), + predicateResult('tests-cover-finality', SOURCE_ROOTS.boundaryTest, sources.boundaryTest.includes('fails closed until BTC finality is confirmed') && sources.boundaryTest.includes('blocked_until_payment_finality')), + predicateResult('tests-cover-reconciliation-drift', SOURCE_ROOTS.boundaryTest, sources.boundaryTest.includes('ledger, database, or object storage projections drift') && sources.boundaryTest.includes('blocked_until_projection_repair')), + predicateResult('host-materializes-boundary', SOURCE_ROOTS.pipelineHostHarness, sources.pipelineHostHarness.includes('buildAssetPackSettlementRightsDeliveryBoundary') && sources.pipelineHostHarness.includes('assetPackSettlementRightsDeliveryBoundary')), + predicateResult('host-test-covers-boundary', SOURCE_ROOTS.pipelineHostHarnessTest, sources.pipelineHostHarnessTest.includes('buildAssetPackSettlementRightsDeliveryBoundary') && sources.pipelineHostHarnessTest.includes('assetPackSettlementRightsDeliveryBoundary')), + predicateResult('route-summarizes-boundary', SOURCE_ROOTS.harnessRunner, sources.harnessRunner.includes('summarizeAssetPackSettlementRightsDeliveryBoundary') && sources.harnessRunner.includes('assetPackLedgerDatabaseStorageReconciliation')), + predicateResult('route-test-covers-boundary', SOURCE_ROOTS.harnessRouteTest, sources.harnessRouteTest.includes('assetPackSettlementRightsDeliveryBoundary') && sources.harnessRouteTest.includes('source_bearing_pull_request_ready')), + predicateResult('terminal-renders-boundary', SOURCE_ROOTS.terminalWorkbench, sources.terminalWorkbench.includes('Settlement rights, compensation, and delivery') && sources.terminalWorkbench.includes('assetPackSettlementBoundaryRows')), + predicateResult('terminal-client-summarizes-boundary', SOURCE_ROOTS.terminalHarnessClient, sources.terminalHarnessClient.includes('settlement-boundary') && sources.terminalHarnessClient.includes('delivery-unlock')), + predicateResult('terminal-client-test-covers-boundary', SOURCE_ROOTS.terminalHarnessClientTest, sources.terminalHarnessClientTest.includes('settlement-boundary settlement_delivered') && sources.terminalHarnessClientTest.includes('reconciliation aligned')), + predicateResult('v42-spec-expanded', SOURCE_ROOTS.v42Spec, sources.v42Spec.includes('V42 Gate 6') && sources.v42Spec.includes('AssetPackSettlementRightsDeliveryBoundary')), + predicateResult('v42-delta-expanded', SOURCE_ROOTS.v42Delta, sources.v42Delta.includes('Gate 6') && sources.v42Delta.includes('settlement rights transfer')), + predicateResult('v42-notes-expanded', SOURCE_ROOTS.v42Notes, sources.v42Notes.includes('Gate 6') && sources.v42Notes.includes('source-to-shares compensation')), + predicateResult('v42-parity-implemented', SOURCE_ROOTS.v42Parity, sources.v42Parity.includes('Settlement and delivery') && sources.v42Parity.includes('implemented')), + predicateResult('roadmap-advanced-to-gate6', SOURCE_ROOTS.roadmap, sources.roadmap.includes('Current working gate: V42 Gate 6') && sources.roadmap.includes('V42 Gate 6 closure anchor')), + predicateResult('readmes-document-gate6', SOURCE_ROOTS.rootReadme, sources.rootReadme.includes('V42 Gate 6') && sources.assetPackReadme.includes('Settlement Rights Delivery') && sources.protocolReadme.includes('V42SettlementRightsDelivery') && sources.terminalReadme.includes('Settlement rights')), + predicateResult('protocol-exports-gate6', SOURCE_ROOTS.protocolIndex, sources.protocolIndex.includes('buildV42SettlementRightsDelivery') && sources.protocolTypes.includes('buildV42SettlementRightsDelivery')), + predicateResult('package-scripts-wire-gate6', SOURCE_ROOTS.packageJson, sources.packageJson.includes('generate:v42-settlement-rights-delivery') && sources.packageJson.includes('check:v42-gate6')), + predicateResult('workflows-run-gate6-check', SOURCE_ROOTS.gateWorkflow, sources.gateWorkflow.includes('check-v42-gate6-settlement-rights-delivery.mjs') && sources.canonWorkflow.includes('check-v42-gate6-settlement-rights-delivery.mjs')), + ]; +} + +function buildCoverage(rows, predicateResults) { + const failedPredicateIds = predicateResults.filter((predicate) => !predicate.passed).map((predicate) => predicate.id); + return { + rowCount: rows.length, + sourceSafetyVerdict: V42_SETTLEMENT_RIGHTS_DELIVERY_SOURCE_SAFETY_VERDICT, + runtimeType: 'AssetPackSettlementRightsDeliveryBoundary', + paymentType: 'AssetPackSettlementPaymentObservation', + finalityType: 'AssetPackSettlementFinalityReceipt', + rightsTransferType: 'BtdRightsTransferReceipt', + readReceiptType: 'BtdReadReceipt', + sourceToSharesType: 'SourceToSharesProof', + reconciliationType: 'LedgerDatabaseReconciliationReport', + deliveryType: 'AssetPackDeliveryUnlockReceipt', + replayType: 'AssetPackSettlementRightsDeliveryReplayReceipt', + requiredReadbacksBeforeUnlock: [ + 'btc_payment_observation', + 'settlement_finality', + 'source_to_shares_compensation', + 'btd_rights_transfer', + 'ledger_database_storage_reconciliation', + ], + stagingProjectRef: 'tkpyosihuouusyaxtbau', + stagingRestHost: 'https://tkpyosihuouusyaxtbau.supabase.co/rest/v1/', + sourceSafeMetadataOnly: true, + protectedSourcePayloadSerialized: false, + unpaidAssetPackSourceVisible: false, + walletPrivateMaterialVisible: false, + settlementPrivatePayloadVisible: false, + credentialsSerialized: false, + hostBoundaryMaterializationCovered: predicateResults.some((predicate) => predicate.id === 'host-materializes-boundary' && predicate.passed), + routeReadbackCovered: predicateResults.some((predicate) => predicate.id === 'route-summarizes-boundary' && predicate.passed), + terminalReadbackCovered: predicateResults.some((predicate) => predicate.id === 'terminal-renders-boundary' && predicate.passed), + confirmedPaymentCovered: predicateResults.some((predicate) => predicate.id === 'tests-cover-confirmed-delivery' && predicate.passed), + underpaymentBlockedCovered: predicateResults.some((predicate) => predicate.id === 'tests-cover-underpayment' && predicate.passed), + finalityBlockedCovered: predicateResults.some((predicate) => predicate.id === 'tests-cover-finality' && predicate.passed), + reconciliationRepairCovered: predicateResults.some((predicate) => predicate.id === 'tests-cover-reconciliation-drift' && predicate.passed), + failedPredicateIds, + }; +} + +export function buildV42SettlementRightsDelivery(input = {}) { + const repoRoot = input.repoRoot || DEFAULT_REPO_ROOT; + const predicateResults = buildPredicateResults(repoRoot); + const rows = [...V42_SETTLEMENT_RIGHTS_DELIVERY_ROWS]; + const coverage = buildCoverage(rows, predicateResults); + const artifactRoot = `v42-settlement-rights-delivery:${digest(JSON.stringify({ + rowIds: V42_SETTLEMENT_RIGHTS_DELIVERY_ROW_IDS, + predicateResults, + coverage, + }))}`; + + return { + artifactId: 'v42-settlement-rights-delivery', + schemaId: V42_SETTLEMENT_RIGHTS_DELIVERY_SCHEMA_ID, + version: V42_SETTLEMENT_RIGHTS_DELIVERY_VERSION, + currentTarget: V42_SETTLEMENT_RIGHTS_DELIVERY_CURRENT_TARGET, + sourceSafetyVerdict: V42_SETTLEMENT_RIGHTS_DELIVERY_SOURCE_SAFETY_VERDICT, + rowIds: [...V42_SETTLEMENT_RIGHTS_DELIVERY_ROW_IDS], + rows, + predicateResults, + coverage, + passed: coverage.failedPredicateIds.length === 0, + artifactRoot, + }; +} diff --git a/packages/protocol/src/index.d.ts b/packages/protocol/src/index.d.ts index 3efaa2a1..57bfdf07 100644 --- a/packages/protocol/src/index.d.ts +++ b/packages/protocol/src/index.d.ts @@ -546,6 +546,14 @@ 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 V42_SETTLEMENT_RIGHTS_DELIVERY_ARTIFACT_PATH: string; +export const V42_SETTLEMENT_RIGHTS_DELIVERY_CURRENT_TARGET: string; +export const V42_SETTLEMENT_RIGHTS_DELIVERY_SCHEMA_ID: string; +export const V42_SETTLEMENT_RIGHTS_DELIVERY_VERSION: string; +export const V42_SETTLEMENT_RIGHTS_DELIVERY_SOURCE_SAFETY_VERDICT: string; +export const V42_SETTLEMENT_RIGHTS_DELIVERY_ROW_IDS: readonly string[]; +export const V42_SETTLEMENT_RIGHTS_DELIVERY_ROWS: readonly Record[]; +export function buildV42SettlementRightsDelivery(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 0d56bca1..1669b4e7 100644 --- a/packages/protocol/src/index.js +++ b/packages/protocol/src/index.js @@ -612,6 +612,16 @@ export { V42_READFITSFINDING_PREVIEW_QUOTE_VERSION, buildV42ReadFitsFindingPreviewQuote } from './canonical/v42-readfitsfinding-preview-quote.js'; +export { + V42_SETTLEMENT_RIGHTS_DELIVERY_ARTIFACT_PATH, + V42_SETTLEMENT_RIGHTS_DELIVERY_CURRENT_TARGET, + V42_SETTLEMENT_RIGHTS_DELIVERY_ROW_IDS, + V42_SETTLEMENT_RIGHTS_DELIVERY_ROWS, + V42_SETTLEMENT_RIGHTS_DELIVERY_SCHEMA_ID, + V42_SETTLEMENT_RIGHTS_DELIVERY_SOURCE_SAFETY_VERDICT, + V42_SETTLEMENT_RIGHTS_DELIVERY_VERSION, + buildV42SettlementRightsDelivery +} from './canonical/v42-settlement-rights-delivery.js'; export { EXCHANGE_INTENT_ACTION_KINDS, EXCHANGE_INTENT_ORDER_CONTRACTS_ARTIFACT_PATH, diff --git a/packages/protocol/test/v42-settlement-rights-delivery.test.js b/packages/protocol/test/v42-settlement-rights-delivery.test.js new file mode 100644 index 00000000..3bc42a16 --- /dev/null +++ b/packages/protocol/test/v42-settlement-rights-delivery.test.js @@ -0,0 +1,68 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { + V42_SETTLEMENT_RIGHTS_DELIVERY_ARTIFACT_PATH, + V42_SETTLEMENT_RIGHTS_DELIVERY_ROW_IDS, + V42_SETTLEMENT_RIGHTS_DELIVERY_SCHEMA_ID, + V42_SETTLEMENT_RIGHTS_DELIVERY_SOURCE_SAFETY_VERDICT, + buildV42SettlementRightsDelivery, +} from '../src/canonical/v42-settlement-rights-delivery.js'; + +test('V42 settlement rights delivery proof is source-safe and complete', () => { + const report = buildV42SettlementRightsDelivery(); + + assert.equal(V42_SETTLEMENT_RIGHTS_DELIVERY_ARTIFACT_PATH, '.bitcode/v42-settlement-rights-delivery.json'); + assert.equal(report.artifactId, 'v42-settlement-rights-delivery'); + assert.equal(report.schemaId, V42_SETTLEMENT_RIGHTS_DELIVERY_SCHEMA_ID); + assert.equal(report.version, 'V42'); + assert.equal(report.currentTarget, 'V41'); + assert.equal(report.sourceSafetyVerdict, V42_SETTLEMENT_RIGHTS_DELIVERY_SOURCE_SAFETY_VERDICT); + assert.deepEqual(report.rowIds, [...V42_SETTLEMENT_RIGHTS_DELIVERY_ROW_IDS]); + assert.equal(report.coverage.rowCount, 11); + assert.equal(report.coverage.runtimeType, 'AssetPackSettlementRightsDeliveryBoundary'); + assert.equal(report.coverage.paymentType, 'AssetPackSettlementPaymentObservation'); + assert.equal(report.coverage.finalityType, 'AssetPackSettlementFinalityReceipt'); + assert.equal(report.coverage.rightsTransferType, 'BtdRightsTransferReceipt'); + assert.equal(report.coverage.sourceToSharesType, 'SourceToSharesProof'); + assert.equal(report.coverage.reconciliationType, 'LedgerDatabaseReconciliationReport'); + assert.equal(report.coverage.deliveryType, 'AssetPackDeliveryUnlockReceipt'); + assert.equal(report.coverage.stagingProjectRef, 'tkpyosihuouusyaxtbau'); + assert.equal(report.coverage.sourceSafeMetadataOnly, true); + assert.equal(report.coverage.protectedSourcePayloadSerialized, 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.hostBoundaryMaterializationCovered, true); + assert.equal(report.coverage.routeReadbackCovered, true); + assert.equal(report.coverage.terminalReadbackCovered, true); + assert.equal(report.coverage.confirmedPaymentCovered, true); + assert.equal(report.coverage.underpaymentBlockedCovered, true); + assert.equal(report.coverage.finalityBlockedCovered, true); + assert.equal(report.coverage.reconciliationRepairCovered, true); + assert.deepEqual(report.coverage.failedPredicateIds, []); + assert.equal(report.passed, true); + assert.match(report.artifactRoot, /^v42-settlement-rights-delivery:/); +}); + +test('V42 settlement rights delivery rows cover purchase through post-settlement delivery', () => { + const report = buildV42SettlementRightsDelivery(); + const rowIds = report.rows.map((row) => row.rowId); + + assert.deepEqual(rowIds, [...V42_SETTLEMENT_RIGHTS_DELIVERY_ROW_IDS]); + assert.ok(rowIds.includes('purchase:quote-to-payment-observation')); + assert.ok(rowIds.includes('rights:btd-read-right-transfer')); + assert.ok(rowIds.includes('compensation:source-to-shares-conservation')); + assert.ok(rowIds.includes('delivery:source-bearing-pull-request-unlock')); + assert.ok(rowIds.includes('sync:ledger-database-object-storage-reconciliation')); + assert.ok(rowIds.includes('route:harness-settlement-summary')); + assert.ok(rowIds.includes('ui:terminal-settlement-readback')); + for (const row of report.rows) { + assert.equal(row.sourceSafeMetadataOnly, true); + assert.equal(row.protectedSourcePayloadSerialized, false); + assert.equal(row.walletPrivateMaterialVisible, false); + assert.equal(row.credentialsSerialized, false); + assert.ok(Array.isArray(row.requiredEvidence)); + assert.ok(row.requiredEvidence.length >= 2); + } +}); diff --git a/scripts/check-v42-gate6-settlement-rights-delivery.mjs b/scripts/check-v42-gate6-settlement-rights-delivery.mjs new file mode 100644 index 00000000..1655e605 --- /dev/null +++ b/scripts/check-v42-gate6-settlement-rights-delivery.mjs @@ -0,0 +1,252 @@ +#!/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-settlement-rights-delivery.json'; + +const SECRET_MARKERS = [ + `${['sk', 'proj'].join('-')}-`, + `${['sb', 'secret'].join('_')}__`, + ['service', 'role'].join('_'), + ['eyJ', 'hbGci', 'Oi', 'JIUzI1Ni'].join(''), + ['OPENAI', 'API', 'KEY'].join('_'), + ['SUPABASE', 'SERVICE', 'ROLE'].join('_'), + ['VERCEL', 'TOKEN'].join('_'), + ['VERCEL', 'OIDC', 'TOKEN'].join('_'), + ['PRIVATE', 'KEY'].join('_'), +]; + +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 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 printHelp() { + process.stdout.write( + [ + 'Usage: node scripts/check-v42-gate6-settlement-rights-delivery.mjs [--skip-branch-check] [--skip-package-tests] [--skip-uapi-tests] [--repo-root ]', + '', + 'Checks V42 Gate 6 settlement, BTD rights transfer, source-to-shares compensation, delivery unlock, route/UI readback, docs, workflow wiring, 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-(?:6|7|8|9)-[a-z0-9][a-z0-9-]*$/u.test(branch), + `V42 Gate 6+ work must occur on version/v42 or v42/gate-6..9-* branches. Observed ${branch || 'detached HEAD'}.`, + ); + } + + const requiredFiles = [ + ARTIFACT_PATH, + 'packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts', + 'packages/pipelines/asset-pack/src/__tests__/asset-pack-settlement-rights-delivery.test.ts', + 'packages/pipeline-hosts/src/asset-pack-harness.ts', + 'packages/pipeline-hosts/src/__tests__/asset-pack-harness.test.ts', + 'uapi/app/api/pipeline-harness/asset-pack/runner.ts', + 'uapi/tests/api/pipelineHarnessRoute.test.ts', + 'uapi/app/terminal/TerminalDepositReadWorkbench.tsx', + 'uapi/app/terminal/terminal-pipeline-harness-client.ts', + 'uapi/tests/terminalPipelineHarnessClient.test.ts', + 'packages/protocol/src/canonical/v42-settlement-rights-delivery.js', + 'packages/protocol/test/v42-settlement-rights-delivery.test.js', + 'scripts/generate-v42-settlement-rights-delivery.mjs', + 'scripts/check-v42-gate6-settlement-rights-delivery.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', + 'packages/pipelines/asset-pack/README.md', + 'packages/protocol/README.md', + 'uapi/app/terminal/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 6 file: ${relativePath}`); + } + + if (failures.length === 0) { + try { + run(root, 'node', ['scripts/generate-v42-settlement-rights-delivery.mjs', '--check']); + } catch (error) { + failures.push(`V42 settlement rights delivery artifact check failed: ${error.stderr || error.message}`); + } + } + + if (failures.length === 0) { + try { + run(root, 'node', [ + '--test', + '--test-force-exit', + 'packages/protocol/test/v42-settlement-rights-delivery.test.js', + ]); + } catch (error) { + failures.push(`V42 settlement rights delivery 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__/asset-pack-settlement-rights-delivery.test.ts', + 'src/__tests__/asset-pack-preview-boundary.test.ts', + 'src/__tests__/postprocess.test.ts', + '--runInBand', + '--forceExit', + ]); + run(root, 'pnpm', [ + '--filter', + '@bitcode/pipeline-hosts', + 'exec', + 'jest', + '--runTestsByPath', + 'src/__tests__/asset-pack-harness.test.ts', + '--runInBand', + '--forceExit', + ]); + } catch (error) { + failures.push(`V42 settlement rights delivery 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', + '--runInBand', + ]); + } catch (error) { + failures.push(`V42 settlement rights delivery 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 6 artifact must not contain secret marker ${marker}.`); + } + + const artifact = serializedArtifact ? JSON.parse(serializedArtifact) : null; + if (artifact) { + assertCheck(failures, artifact.artifactId === 'v42-settlement-rights-delivery', 'Gate 6 artifactId must match.'); + assertCheck(failures, artifact.schemaId === 'bitcode.v42.settlementRightsDelivery.v1', 'Gate 6 schemaId must match.'); + assertCheck(failures, artifact.version === 'V42' && artifact.currentTarget === 'V41', 'Gate 6 artifact must bind V42 over active V41.'); + assertCheck(failures, artifact.passed === true, 'Gate 6 artifact must pass.'); + assertCheck(failures, artifact.coverage.rowCount === 11, 'Gate 6 must cover eleven settlement rows.'); + assertCheck(failures, artifact.coverage.runtimeType === 'AssetPackSettlementRightsDeliveryBoundary', 'Gate 6 must cover AssetPackSettlementRightsDeliveryBoundary.'); + assertCheck(failures, artifact.coverage.rightsTransferType === 'BtdRightsTransferReceipt', 'Gate 6 must cover BTD rights transfer.'); + assertCheck(failures, artifact.coverage.sourceToSharesType === 'SourceToSharesProof', 'Gate 6 must cover source-to-shares.'); + assertCheck(failures, artifact.coverage.reconciliationType === 'LedgerDatabaseReconciliationReport', 'Gate 6 must cover reconciliation.'); + assertCheck(failures, artifact.coverage.deliveryType === 'AssetPackDeliveryUnlockReceipt', 'Gate 6 must cover delivery unlock.'); + assertCheck(failures, artifact.coverage.stagingProjectRef === 'tkpyosihuouusyaxtbau', 'Gate 6 must bind staging-testnet Supabase project ref.'); + assertCheck(failures, artifact.coverage.sourceSafeMetadataOnly === true, 'Gate 6 must remain source-safe metadata only.'); + assertCheck(failures, artifact.coverage.protectedSourcePayloadSerialized === false, 'Gate 6 artifact must not serialize protected source.'); + assertCheck(failures, artifact.coverage.unpaidAssetPackSourceVisible === false, 'Gate 6 artifact must not expose unpaid AssetPack source.'); + assertCheck(failures, artifact.coverage.walletPrivateMaterialVisible === false, 'Gate 6 artifact must not expose private wallet material.'); + assertCheck(failures, artifact.coverage.settlementPrivatePayloadVisible === false, 'Gate 6 artifact must not expose private settlement payloads.'); + assertCheck(failures, artifact.coverage.credentialsSerialized === false, 'Gate 6 artifact must not serialize credentials.'); + assertCheck(failures, artifact.coverage.hostBoundaryMaterializationCovered === true, 'Gate 6 must cover live harness boundary materialization.'); + assertCheck(failures, artifact.coverage.routeReadbackCovered === true, 'Gate 6 must cover route readback.'); + assertCheck(failures, artifact.coverage.terminalReadbackCovered === true, 'Gate 6 must cover Terminal readback.'); + assertCheck(failures, artifact.coverage.confirmedPaymentCovered === true, 'Gate 6 must cover confirmed payment.'); + assertCheck(failures, artifact.coverage.underpaymentBlockedCovered === true, 'Gate 6 must cover underpayment blocking.'); + assertCheck(failures, artifact.coverage.finalityBlockedCovered === true, 'Gate 6 must cover finality blocking.'); + assertCheck(failures, artifact.coverage.reconciliationRepairCovered === true, 'Gate 6 must cover reconciliation repair.'); + assertCheck(failures, Array.isArray(artifact.coverage.failedPredicateIds) && artifact.coverage.failedPredicateIds.length === 0, 'Gate 6 predicates must all pass.'); + } + + if (failures.length > 0) { + process.stderr.write(`V42 Gate 6 settlement rights delivery check failed:\n- ${failures.join('\n- ')}\n`); + process.exitCode = 1; + return; + } + + process.stdout.write(`V42 Gate 6 settlement rights delivery ok artifact=${artifact.artifactRoot}\n`); +} + +main(); diff --git a/scripts/generate-v42-settlement-rights-delivery.mjs b/scripts/generate-v42-settlement-rights-delivery.mjs new file mode 100644 index 00000000..3e9fe1c2 --- /dev/null +++ b/scripts/generate-v42-settlement-rights-delivery.mjs @@ -0,0 +1,61 @@ +#!/usr/bin/env node + +import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { + V42_SETTLEMENT_RIGHTS_DELIVERY_ARTIFACT_PATH, + buildV42SettlementRightsDelivery, +} from '../packages/protocol/src/canonical/v42-settlement-rights-delivery.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const defaultRepoRoot = path.resolve(__dirname, '..'); + +function parseArgs(argv) { + const args = { + repoRoot: defaultRepoRoot, + check: false, + }; + for (let index = 0; index < argv.length; index += 1) { + const arg = argv[index]; + if (arg === '--repo-root') args.repoRoot = path.resolve(argv[++index]); + else if (arg === '--check') args.check = true; + else if (arg === '--help' || arg === '-h') { + process.stdout.write( + 'Usage: node scripts/generate-v42-settlement-rights-delivery.mjs [--check] [--repo-root ]\n', + ); + process.exit(0); + } else { + throw new Error(`Unknown argument ${arg}`); + } + } + return args; +} + +const args = parseArgs(process.argv.slice(2)); +const artifact = buildV42SettlementRightsDelivery({ repoRoot: args.repoRoot }); +const artifactPath = path.join(args.repoRoot, V42_SETTLEMENT_RIGHTS_DELIVERY_ARTIFACT_PATH); +const serialized = `${JSON.stringify(artifact, null, 2)}\n`; + +if (args.check) { + if (!existsSync(artifactPath)) { + process.stderr.write(`Missing ${V42_SETTLEMENT_RIGHTS_DELIVERY_ARTIFACT_PATH}\n`); + process.exit(1); + } + const current = readFileSync(artifactPath, 'utf8'); + if (current !== serialized) { + process.stderr.write(`${V42_SETTLEMENT_RIGHTS_DELIVERY_ARTIFACT_PATH} is stale. Run pnpm run generate:v42-settlement-rights-delivery.\n`); + process.exit(1); + } + if (!artifact.passed) { + process.stderr.write(`V42 settlement rights delivery artifact predicates failed: ${artifact.coverage.failedPredicateIds.join(', ')}\n`); + process.exit(1); + } + process.stdout.write(`V42 settlement rights delivery artifact ok ${artifact.artifactRoot}\n`); + process.exit(0); +} + +mkdirSync(path.dirname(artifactPath), { recursive: true }); +writeFileSync(artifactPath, serialized); +process.stdout.write(`Wrote ${V42_SETTLEMENT_RIGHTS_DELIVERY_ARTIFACT_PATH} ${artifact.artifactRoot}\n`); diff --git a/uapi/app/api/pipeline-harness/asset-pack/runner.ts b/uapi/app/api/pipeline-harness/asset-pack/runner.ts index 8cf8fd26..e34f8df4 100644 --- a/uapi/app/api/pipeline-harness/asset-pack/runner.ts +++ b/uapi/app/api/pipeline-harness/asset-pack/runner.ts @@ -444,6 +444,34 @@ function summarizeEvidence(evidence: unknown): Record | null { : assetPackPreviewBoundary?.disclosureReview && typeof assetPackPreviewBoundary.disclosureReview === 'object' ? (assetPackPreviewBoundary.disclosureReview as Record) : null; + const assetPackSettlementRightsDeliveryBoundary = + output?.assetPackSettlementRightsDeliveryBoundary && typeof output.assetPackSettlementRightsDeliveryBoundary === 'object' + ? (output.assetPackSettlementRightsDeliveryBoundary as Record) + : record.assetPackSettlementRightsDeliveryBoundary && typeof record.assetPackSettlementRightsDeliveryBoundary === 'object' + ? (record.assetPackSettlementRightsDeliveryBoundary as Record) + : null; + const assetPackSettlementReplayReceipt = + output?.assetPackSettlementReplayReceipt && typeof output.assetPackSettlementReplayReceipt === 'object' + ? (output.assetPackSettlementReplayReceipt as Record) + : assetPackSettlementRightsDeliveryBoundary?.replayReceipt && + typeof assetPackSettlementRightsDeliveryBoundary.replayReceipt === 'object' + ? (assetPackSettlementRightsDeliveryBoundary.replayReceipt as Record) + : null; + const assetPackDeliveryUnlock = + output?.assetPackDeliveryUnlock && typeof output.assetPackDeliveryUnlock === 'object' + ? (output.assetPackDeliveryUnlock as Record) + : assetPackSettlementRightsDeliveryBoundary?.deliveryUnlock && + typeof assetPackSettlementRightsDeliveryBoundary.deliveryUnlock === 'object' + ? (assetPackSettlementRightsDeliveryBoundary.deliveryUnlock as Record) + : null; + const assetPackLedgerDatabaseStorageReconciliation = + output?.assetPackLedgerDatabaseStorageReconciliation && + typeof output.assetPackLedgerDatabaseStorageReconciliation === 'object' + ? (output.assetPackLedgerDatabaseStorageReconciliation as Record) + : assetPackSettlementRightsDeliveryBoundary?.reconciliationReport && + typeof assetPackSettlementRightsDeliveryBoundary.reconciliationReport === 'object' + ? (assetPackSettlementRightsDeliveryBoundary.reconciliationReport as Record) + : null; const summarizeCandidate = (candidate: unknown) => { if (!candidate || typeof candidate !== 'object' || Array.isArray(candidate)) return null; const record = candidate as Record; @@ -606,6 +634,51 @@ function summarizeEvidence(evidence: unknown): Record | null { roots: assetPackDisclosureReview.roots, } : null, + assetPackSettlementRightsDeliveryBoundary: summarizeAssetPackSettlementRightsDeliveryBoundary( + assetPackSettlementRightsDeliveryBoundary, + ), + assetPackSettlementReplayReceipt: assetPackSettlementReplayReceipt + ? { + schema: assetPackSettlementReplayReceipt.schema, + replayMode: assetPackSettlementReplayReceipt.replayMode, + previewBoundaryRoot: assetPackSettlementReplayReceipt.previewBoundaryRoot, + quoteRoot: assetPackSettlementReplayReceipt.quoteRoot, + paymentReceiptRoot: assetPackSettlementReplayReceipt.paymentReceiptRoot, + finalityRoot: assetPackSettlementReplayReceipt.finalityRoot, + sourceToSharesRoot: assetPackSettlementReplayReceipt.sourceToSharesRoot, + rightsTransferRoot: assetPackSettlementReplayReceipt.rightsTransferRoot, + readReceiptRoot: assetPackSettlementReplayReceipt.readReceiptRoot, + deliveryRoot: assetPackSettlementReplayReceipt.deliveryRoot, + reconciliationRoot: assetPackSettlementReplayReceipt.reconciliationRoot, + replayRoot: assetPackSettlementReplayReceipt.replayRoot, + verified: assetPackSettlementReplayReceipt.verified, + } + : null, + assetPackDeliveryUnlock: assetPackDeliveryUnlock + ? { + schema: assetPackDeliveryUnlock.schema, + state: assetPackDeliveryUnlock.state, + deliveryMechanism: assetPackDeliveryUnlock.deliveryMechanism, + pullRequestTarget: assetPackDeliveryUnlock.pullRequestTarget, + sourceBearingDeliveryVisibleToReader: + assetPackDeliveryUnlock.sourceBearingDeliveryVisibleToReader, + protectedSourcePayloadSerialized: + assetPackDeliveryUnlock.protectedSourcePayloadSerialized, + requiredReceipts: assetPackDeliveryUnlock.requiredReceipts, + blockerCodes: assetPackDeliveryUnlock.blockerCodes, + deliveryRoot: assetPackDeliveryUnlock.deliveryRoot, + } + : null, + assetPackLedgerDatabaseStorageReconciliation: assetPackLedgerDatabaseStorageReconciliation + ? { + schema: assetPackLedgerDatabaseStorageReconciliation.schema, + reconciliationId: assetPackLedgerDatabaseStorageReconciliation.reconciliationId, + state: assetPackLedgerDatabaseStorageReconciliation.state, + blocking: assetPackLedgerDatabaseStorageReconciliation.blocking, + repairActions: assetPackLedgerDatabaseStorageReconciliation.repairActions, + proofRoots: assetPackLedgerDatabaseStorageReconciliation.proofRoots, + } + : null, depositorySearch: depositorySearch ? { ...summarizeFitLike(depositorySearch), @@ -787,6 +860,142 @@ function summarizeAssetPackPreviewBoundary( }; } +function summarizeAssetPackSettlementRightsDeliveryBoundary( + boundary: Record | null, +): Record | null { + if (!boundary) return null; + const paymentObservation = boundary.paymentObservation && typeof boundary.paymentObservation === 'object' + ? (boundary.paymentObservation as Record) + : null; + const finalityReceipt = boundary.finalityReceipt && typeof boundary.finalityReceipt === 'object' + ? (boundary.finalityReceipt as Record) + : null; + const deliveryUnlock = boundary.deliveryUnlock && typeof boundary.deliveryUnlock === 'object' + ? (boundary.deliveryUnlock as Record) + : null; + const settlementUnlock = boundary.settlementUnlock && typeof boundary.settlementUnlock === 'object' + ? (boundary.settlementUnlock as Record) + : null; + const reconciliationReport = boundary.reconciliationReport && typeof boundary.reconciliationReport === 'object' + ? (boundary.reconciliationReport 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, + state: boundary.state, + assetPackId: boundary.assetPackId, + readId: boundary.readId, + orderId: boundary.orderId, + previewBoundaryRoot: boundary.previewBoundaryRoot, + paymentObservation: paymentObservation + ? { + paymentReceiptId: paymentObservation.paymentReceiptId, + payer: paymentObservation.payer, + payee: paymentObservation.payee, + payerWalletId: paymentObservation.payerWalletId, + payeeWalletId: paymentObservation.payeeWalletId, + btcNetwork: paymentObservation.btcNetwork, + expectedSats: paymentObservation.expectedSats, + observedDebitSats: paymentObservation.observedDebitSats, + observedCreditSats: paymentObservation.observedCreditSats, + txid: paymentObservation.txid, + serverCustody: paymentObservation.serverCustody, + paymentReceiptRoot: paymentObservation.paymentReceiptRoot, + } + : null, + finalityReceipt: finalityReceipt + ? { + finalityState: finalityReceipt.finalityState, + confirmations: finalityReceipt.confirmations, + blockHeight: finalityReceipt.blockHeight, + txid: finalityReceipt.txid, + finalityRoot: finalityReceipt.finalityRoot, + } + : null, + sourceToSharesRoot: proofRoots?.sourceToSharesRoot ?? null, + btdReadReceiptRoot: proofRoots?.btdReadReceiptRoot ?? null, + rightsTransferRoot: proofRoots?.rightsTransferRoot ?? null, + settlementUnlock: settlementUnlock + ? { + state: settlementUnlock.state, + sourceAvailable: settlementUnlock.sourceAvailable, + reason: settlementUnlock.reason, + readLicenseId: settlementUnlock.readLicenseId, + pullRequestTarget: settlementUnlock.pullRequestTarget, + missingReadbackKeys: settlementUnlock.missingReadbackKeys, + } + : null, + deliveryUnlock: deliveryUnlock + ? { + state: deliveryUnlock.state, + deliveryMechanism: deliveryUnlock.deliveryMechanism, + pullRequestTarget: deliveryUnlock.pullRequestTarget, + sourceBearingDeliveryVisibleToReader: + deliveryUnlock.sourceBearingDeliveryVisibleToReader, + requiredReceipts: deliveryUnlock.requiredReceipts, + blockerCodes: deliveryUnlock.blockerCodes, + deliveryRoot: deliveryUnlock.deliveryRoot, + } + : null, + reconciliationReport: reconciliationReport + ? { + reconciliationId: reconciliationReport.reconciliationId, + state: reconciliationReport.state, + blocking: reconciliationReport.blocking, + repairActions: reconciliationReport.repairActions, + proofRoots: reconciliationReport.proofRoots, + } + : null, + replayReceipt: replayReceipt + ? { + replayMode: replayReceipt.replayMode, + quoteRoot: replayReceipt.quoteRoot, + paymentReceiptRoot: replayReceipt.paymentReceiptRoot, + finalityRoot: replayReceipt.finalityRoot, + sourceToSharesRoot: replayReceipt.sourceToSharesRoot, + rightsTransferRoot: replayReceipt.rightsTransferRoot, + readReceiptRoot: replayReceipt.readReceiptRoot, + deliveryRoot: replayReceipt.deliveryRoot, + reconciliationRoot: replayReceipt.reconciliationRoot, + replayRoot: replayReceipt.replayRoot, + verified: replayReceipt.verified, + } + : null, + repairPosture, + sourceSafety, + proofRoots, + 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 6ec89b32..7c0ea178 100644 --- a/uapi/app/terminal/README.md +++ b/uapi/app/terminal/README.md @@ -135,6 +135,19 @@ and rights-transfer readback. The source-safe proof artifact is `.bitcode/v42-readfitsfinding-preview-quote.json`, checked by `pnpm run check:v42-gate5`. +V42 Gate 6 closes Terminal Settlement rights readback. The paid-boundary panel +now reads `AssetPackSettlementRightsDeliveryBoundary` summaries from pipeline +harness evidence, including BTC payment observation, finality, source-to-shares +compensation, BTD rights transfer, paid read receipt, source-bearing +pull-request delivery unlock, ledger/database/object-storage reconciliation, +replay root, repair posture, and storage record count. Terminal keeps these +rows expandable and source-safe: it may show that delivery is unlocked for the +paid Reader after settlement, but it must not expose unpaid AssetPack source, +protected source, wallet private material, private settlement payloads, +credentials, raw protected prompts, or raw provider responses. The source-safe +proof artifact is `.bitcode/v42-settlement-rights-delivery.json`, checked by +`pnpm run check:v42-gate6`. + ## 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 870f2bad..3ab4a2f7 100644 --- a/uapi/app/terminal/TerminalDepositReadWorkbench.tsx +++ b/uapi/app/terminal/TerminalDepositReadWorkbench.tsx @@ -339,6 +339,25 @@ export default function TerminalDepositReadWorkbench({ const disclosureRoots = objectValue(assetPackDisclosureReview?.roots); const disclosureSourceSafe = disclosureLeakage?.protectedSourceDetected !== true; const ledgerSettlement = objectValue(completedHarnessEvidence?.ledgerSettlement); + const assetPackSettlementRightsDeliveryBoundary = objectValue( + completedHarnessEvidence?.assetPackSettlementRightsDeliveryBoundary, + ); + const assetPackSettlementPaymentObservation = objectValue( + assetPackSettlementRightsDeliveryBoundary?.paymentObservation, + ); + const assetPackSettlementFinalityReceipt = objectValue( + assetPackSettlementRightsDeliveryBoundary?.finalityReceipt, + ); + const assetPackSettlementDeliveryUnlock = + objectValue(assetPackSettlementRightsDeliveryBoundary?.deliveryUnlock) || + objectValue(completedHarnessEvidence?.assetPackDeliveryUnlock); + const assetPackSettlementReplayReceipt = + objectValue(assetPackSettlementRightsDeliveryBoundary?.replayReceipt) || + objectValue(completedHarnessEvidence?.assetPackSettlementReplayReceipt); + const assetPackSettlementReconciliation = + objectValue(assetPackSettlementRightsDeliveryBoundary?.reconciliationReport) || + objectValue(completedHarnessEvidence?.assetPackLedgerDatabaseStorageReconciliation); + const assetPackSettlementProofRoots = objectValue(assetPackSettlementRightsDeliveryBoundary?.proofRoots); const previewFeeQuote = assetPackQuoteReceipt || objectValue(sourceSafePreview?.feeQuote); @@ -534,6 +553,91 @@ export default function TerminalDepositReadWorkbench({ sourceSafePreview?.roots, ], ); + const assetPackSettlementBoundaryRows = useMemo( + () => [ + { + label: 'Boundary', + value: shortIdentifier(assetPackSettlementRightsDeliveryBoundary?.boundaryId) || 'pending', + }, + { + label: 'State', + value: textValue(assetPackSettlementRightsDeliveryBoundary?.state) || 'pending', + }, + { + label: 'Payment', + value: + numericValue(assetPackSettlementPaymentObservation?.observedDebitSats) && + numericValue(assetPackSettlementPaymentObservation?.expectedSats) + ? `${String(assetPackSettlementPaymentObservation?.observedDebitSats)}/${String(assetPackSettlementPaymentObservation?.expectedSats)} sats` + : 'pending', + }, + { + label: 'Payment root', + value: shortIdentifier(assetPackSettlementPaymentObservation?.paymentReceiptRoot) || 'pending', + }, + { + label: 'Finality', + value: textValue(assetPackSettlementFinalityReceipt?.finalityState) || 'pending', + }, + { + label: 'Finality root', + value: shortIdentifier(assetPackSettlementFinalityReceipt?.finalityRoot) || 'pending', + }, + { + label: 'Source-to-shares', + value: shortIdentifier(assetPackSettlementProofRoots?.sourceToSharesRoot) || 'pending', + }, + { + label: 'Rights transfer', + value: shortIdentifier(assetPackSettlementProofRoots?.rightsTransferRoot) || 'pending', + }, + { + label: 'Read receipt', + value: shortIdentifier(assetPackSettlementProofRoots?.btdReadReceiptRoot) || 'pending', + }, + { + label: 'Delivery unlock', + value: textValue(assetPackSettlementDeliveryUnlock?.state) || 'pending', + }, + { + label: 'Delivery root', + value: shortIdentifier(assetPackSettlementDeliveryUnlock?.deliveryRoot) || 'pending', + }, + { + label: 'Reconciliation', + value: textValue(assetPackSettlementReconciliation?.state) || 'pending', + }, + { + label: 'Reconciliation root', + value: + shortIdentifier(objectValue(assetPackSettlementReconciliation?.proofRoots)?.repairPlanRoot) || + shortIdentifier(assetPackSettlementProofRoots?.reconciliationRoot) || + 'pending', + }, + { + label: 'Replay root', + value: + shortIdentifier(assetPackSettlementReplayReceipt?.replayRoot) || + shortIdentifier(assetPackSettlementProofRoots?.replayRoot) || + 'pending', + }, + { + label: 'Storage records', + value: numericValue(assetPackSettlementRightsDeliveryBoundary?.storageRecordCount) + ? String(assetPackSettlementRightsDeliveryBoundary?.storageRecordCount) + : String(countList(assetPackSettlementRightsDeliveryBoundary?.storageProjection) || 0), + }, + ], + [ + assetPackSettlementDeliveryUnlock, + assetPackSettlementFinalityReceipt, + assetPackSettlementPaymentObservation, + assetPackSettlementProofRoots, + assetPackSettlementReconciliation, + assetPackSettlementReplayReceipt, + assetPackSettlementRightsDeliveryBoundary, + ], + ); const readNeedRows = useMemo(() => { if (!currentReadNeed) return []; return [ @@ -1112,6 +1216,19 @@ export default function TerminalDepositReadWorkbench({ ))} +
+ + Settlement rights, compensation, and delivery + +
+ {assetPackSettlementBoundaryRows.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 487d10a4..e1443134 100644 --- a/uapi/app/terminal/terminal-pipeline-harness-client.ts +++ b/uapi/app/terminal/terminal-pipeline-harness-client.ts @@ -636,6 +636,13 @@ export function summarizeTerminalReadFitsFindingSynthesisHarnessEvent( const ledgerSettlement = recordValue(evidence?.ledgerSettlement); const sourceSafePreview = recordValue(evidence?.sourceSafePreview); const assetPackPreviewBoundary = recordValue(evidence?.assetPackPreviewBoundary); + const settlementBoundary = recordValue(evidence?.assetPackSettlementRightsDeliveryBoundary); + const deliveryUnlock = recordValue(settlementBoundary?.deliveryUnlock) || + recordValue(evidence?.assetPackDeliveryUnlock); + const paymentObservation = recordValue(settlementBoundary?.paymentObservation); + const finalityReceipt = recordValue(settlementBoundary?.finalityReceipt); + const reconciliationReport = recordValue(settlementBoundary?.reconciliationReport) || + recordValue(evidence?.assetPackLedgerDatabaseStorageReconciliation); const boundaryQuoteReceipt = recordValue(assetPackPreviewBoundary?.quoteReceipt); const boundarySelectedFitProvenance = recordValue(assetPackPreviewBoundary?.selectedFitProvenance); const boundarySettlementInstructions = recordValue(assetPackPreviewBoundary?.settlementInstructions); @@ -674,6 +681,25 @@ export function summarizeTerminalReadFitsFindingSynthesisHarnessEvent( const deliveryText = boundaryDeliveryPosture?.state ? ` delivery ${String(boundaryDeliveryPosture.state)}` : null; + const settlementBoundaryText = settlementBoundary?.state + ? ` settlement-boundary ${String(settlementBoundary.state)}` + : null; + const paymentText = typeof paymentObservation?.observedDebitSats === 'number' && + typeof paymentObservation?.expectedSats === 'number' + ? ` paid ${paymentObservation.observedDebitSats}/${paymentObservation.expectedSats} sats` + : null; + const finalityText = finalityReceipt?.finalityState + ? ` finality ${String(finalityReceipt.finalityState)}` + : null; + const rightsText = settlementBoundary?.rightsTransferRoot + ? ` rights ${shortIdentifier(settlementBoundary.rightsTransferRoot)}` + : null; + const deliveredText = deliveryUnlock?.state + ? ` delivery-unlock ${String(deliveryUnlock.state)}` + : null; + const reconciliationText = reconciliationReport?.state + ? ` reconciliation ${String(reconciliationReport.state)}` + : null; const unlockText = unlock?.sourceAvailable === true ? ` source ${String(unlock.state || 'available')}` : unlock?.state @@ -700,6 +726,12 @@ export function summarizeTerminalReadFitsFindingSynthesisHarnessEvent( quoteText, settlementText, deliveryText, + settlementBoundaryText, + paymentText, + finalityText, + rightsText, + deliveredText, + reconciliationText, unlockText, disclosureText, leakageText, diff --git a/uapi/tests/api/pipelineHarnessRoute.test.ts b/uapi/tests/api/pipelineHarnessRoute.test.ts index 81dd1a44..935185f0 100644 --- a/uapi/tests/api/pipelineHarnessRoute.test.ts +++ b/uapi/tests/api/pipelineHarnessRoute.test.ts @@ -190,6 +190,125 @@ jest.mock('@bitcode/pipeline-hosts', () => ({ }, ], }, + assetPackSettlementRightsDeliveryBoundary: { + schema: 'bitcode.asset-pack.settlement-rights-delivery-boundary', + boundaryId: 'asset-pack-settlement-rights-delivery-route-test', + state: 'settlement_delivered', + assetPackId: 'asset-pack-route-test', + readId: 'read-route-test', + orderId: 'order-route-test', + previewBoundaryRoot: 'sha256:route-boundary-root', + paymentObservation: { + paymentReceiptId: 'btc-fee-route-test', + payer: 'reader', + payee: 'depositor', + payerWalletId: 'reader-wallet-route-test', + payeeWalletId: 'depositor-wallet-route-test', + btcNetwork: 'testnet', + expectedSats: 546, + observedDebitSats: 546, + observedCreditSats: 546, + txid: 'testnet-route-txid', + serverCustody: false, + paymentReceiptRoot: 'sha256:route-payment-root', + }, + finalityReceipt: { + finalityState: 'confirmed', + confirmations: 6, + blockHeight: 840000, + txid: 'testnet-route-txid', + finalityRoot: 'sha256:route-finality-root', + }, + settlementUnlock: { + schema: 'bitcode.asset-pack.settlement-unlock', + state: 'licensed_read', + sourceAvailable: true, + reason: 'settlement delivered in route test', + readLicenseId: 'read-license-route-test', + pullRequestTarget: 'https://github.com/engineeredsoftware/ENGI/pull/42', + missingReadbackKeys: [], + }, + deliveryUnlock: { + schema: 'bitcode.asset-pack.delivery-unlock', + state: 'source_bearing_pull_request_ready', + deliveryMechanism: 'pull_request_after_settlement', + pullRequestTarget: 'https://github.com/engineeredsoftware/ENGI/pull/42', + sourceBearingDeliveryVisibleToReader: true, + protectedSourcePayloadSerialized: false, + requiredReceipts: ['btc_payment_observation', 'btd_rights_transfer'], + blockerCodes: [], + deliveryRoot: 'sha256:route-delivery-unlock-root', + }, + reconciliationReport: { + schema: 'bitcode.ledger-database-reconciliation-report', + reconciliationId: 'reconciliation-route-test', + state: 'aligned', + blocking: false, + repairActions: [], + proofRoots: { + repairPlanRoot: 'sha256:route-reconciliation-root', + }, + }, + replayReceipt: { + schema: 'bitcode.asset-pack.settlement-rights-delivery.replay-receipt', + replayMode: 'settlement-rights-delivery-replay', + previewBoundaryRoot: 'sha256:route-boundary-root', + quoteRoot: 'sha256:route-quote-root', + paymentReceiptRoot: 'sha256:route-payment-root', + finalityRoot: 'sha256:route-finality-root', + sourceToSharesRoot: 'sha256:route-source-to-shares-root', + rightsTransferRoot: 'sha256:route-rights-root', + readReceiptRoot: 'sha256:route-read-root', + deliveryRoot: 'sha256:route-delivery-unlock-root', + reconciliationRoot: 'sha256:route-reconciliation-root', + replayRoot: 'sha256:route-settlement-replay-root', + verified: { + paymentMatchesQuote: true, + finalityConfirmed: true, + sourceToSharesConserved: true, + rightsTransferConfirmed: true, + reconciliationAligned: true, + deliveryUnlockedOnlyAfterSettlement: true, + protectedSourcePayloadAbsent: true, + }, + }, + sourceSafety: { + sourceSafeMetadataOnly: true, + protectedSourcePayloadSerialized: false, + sourceBearingDeliveryUnlockedToReader: true, + walletPrivateMaterialVisible: false, + credentialsSerialized: false, + }, + proofRoots: { + previewBoundaryRoot: 'sha256:route-boundary-root', + paymentReceiptRoot: 'sha256:route-payment-root', + finalityRoot: 'sha256:route-finality-root', + sourceToSharesRoot: 'sha256:route-source-to-shares-root', + btdReadReceiptRoot: 'sha256:route-read-root', + rightsTransferRoot: 'sha256:route-rights-root', + settlementUnlockRoot: 'sha256:route-settlement-unlock-root', + deliveryRoot: 'sha256:route-delivery-unlock-root', + reconciliationRoot: 'sha256:route-reconciliation-root', + storageRoot: 'sha256:route-settlement-storage-root', + replayRoot: 'sha256:route-settlement-replay-root', + boundaryRoot: 'sha256:route-settlement-boundary-root', + }, + storageProjection: [ + { + recordId: 'route-settlement-storage-record', + recordKind: 'btd_rights_transfer', + namespace: 'asset-pack/settlement', + key: 'rightsTransferReceipt', + root: 'sha256:route-settlement-storage-record-root', + sourceSafety: { + sourceSafeMetadataOnly: true, + }, + payload: { + hiddenFromRouteSummary: true, + }, + }, + ], + }, }, }, telemetry: '{"type":"pipeline-stream-event","runId":"route-run","stage":"asset-pack-synthesis"}\n', @@ -620,6 +739,35 @@ describe('POST /api/pipeline-harness/asset-pack', () => { state: 'withheld_until_settlement', sourceBearingDeliveryVisible: false, }, + assetPackSettlementRightsDeliveryBoundary: { + state: 'settlement_delivered', + assetPackId: 'asset-pack-route-test', + paymentObservation: { + observedDebitSats: 546, + expectedSats: 546, + serverCustody: false, + }, + finalityReceipt: { + finalityState: 'confirmed', + }, + deliveryUnlock: { + state: 'source_bearing_pull_request_ready', + sourceBearingDeliveryVisibleToReader: true, + }, + reconciliationReport: { + state: 'aligned', + blocking: false, + }, + storageRecordCount: 1, + }, + assetPackDeliveryUnlock: { + state: 'source_bearing_pull_request_ready', + sourceBearingDeliveryVisibleToReader: true, + }, + assetPackLedgerDatabaseStorageReconciliation: { + state: 'aligned', + blocking: false, + }, }, }); expect(eventText).toContain('[redacted]'); diff --git a/uapi/tests/terminalPipelineHarnessClient.test.ts b/uapi/tests/terminalPipelineHarnessClient.test.ts index 06882e99..52ceaf4e 100644 --- a/uapi/tests/terminalPipelineHarnessClient.test.ts +++ b/uapi/tests/terminalPipelineHarnessClient.test.ts @@ -211,6 +211,23 @@ describe('terminal pipeline harness client', () => { state: 'withheld_until_settlement', }, }, + assetPackSettlementRightsDeliveryBoundary: { + state: 'settlement_delivered', + rightsTransferRoot: 'sha256:settlement-rights-root', + paymentObservation: { + expectedSats: 546, + observedDebitSats: 546, + }, + finalityReceipt: { + finalityState: 'confirmed', + }, + deliveryUnlock: { + state: 'source_bearing_pull_request_ready', + }, + reconciliationReport: { + state: 'aligned', + }, + }, assetPackDisclosureReview: { access: { sourceVisibility: 'available_after_settlement', @@ -233,6 +250,12 @@ describe('terminal pipeline harness client', () => { expect(summary).toContain('quote sha256:asset'); expect(summary).toContain('settlement quote_ready_settlement_required'); expect(summary).toContain('delivery withheld_until_settlement'); + expect(summary).toContain('settlement-boundary settlement_delivered'); + expect(summary).toContain('paid 546/546 sats'); + expect(summary).toContain('finality confirmed'); + expect(summary).toContain('rights sha256:settl'); + expect(summary).toContain('delivery-unlock source_bearing_pull_request_ready'); + expect(summary).toContain('reconciliation aligned'); expect(summary).toContain('source licensed_read'); expect(summary).toContain('disclosure available_after_settlement'); expect(summary).toContain('leakage none');