From fe7cdd8e9f194d7268e9edca8a20bd733cae4308 Mon Sep 17 00:00:00 2001 From: Garrett Maring Date: Fri, 29 May 2026 17:51:46 -0300 Subject: [PATCH] V44 Gate 6: Add BTD BTC compensation statements Add source-safe BtdBtcCompensationStatements over settlement rights delivery boundaries, including BTD range state, BTC settlement observations, source-to-shares contributor allocations, depositor summaries, treasury routes, reconciliation, repair statements, and Packs accounting readback. Bind the V44 BTD/BTC compensation statements artifact, generator, checker, protocol exports, workflow hooks, route tests, package tests, and draft specification documentation updates. Validated with V44 gate checks, focused package/UAPI/protocol tests, typechecks, spec posture checks, workflow YAML parsing, diff whitespace, ASCII hygiene, and changed-file secret scanning. --- .../v44-btd-btc-compensation-statements.json | 421 +++++++++++++ ...positor-earnings-supply-opportunities.json | 30 +- .bitcode/v44-economic-domain-model.json | 26 +- ...4-packs-portfolio-market-intelligence.json | 32 +- .bitcode/v44-reading-budget-quote-policy.json | 26 +- .github/workflows/bitcode-canon-quality.yml | 3 + .github/workflows/bitcode-gate-quality.yml | 6 + BITCODE_SPEC_V44.md | 32 +- BITCODE_SPEC_V44_DELTA.md | 17 +- BITCODE_SPEC_V44_NOTES.md | 17 +- BITCODE_SPEC_V44_PARITY_MATRIX.md | 10 +- README.md | 17 +- SPECIFICATIONS_ROADMAP.md | 5 +- package.json | 3 + packages/pipelines/asset-pack/package.json | 1 + .../btd-btc-compensation-statements.test.ts | 238 ++++++++ .../src/btd-btc-compensation-statements.ts | 570 ++++++++++++++++++ packages/pipelines/asset-pack/src/index.ts | 2 + packages/protocol/README.md | 12 + .../v44-btd-btc-compensation-statements.js | 311 ++++++++++ packages/protocol/src/index.d.ts | 15 + packages/protocol/src/index.js | 17 + ...44-btd-btc-compensation-statements.test.js | 71 +++ ...-gate6-btd-btc-compensation-statements.mjs | 201 ++++++ ...te-v44-btd-btc-compensation-statements.mjs | 27 + uapi/app/packs/PacksPageClient.tsx | 55 ++ .../bitcode/activity/pack-activity-model.ts | 89 +++ uapi/tests/packActivityModel.test.ts | 39 ++ uapi/tests/packsPageClient.test.tsx | 28 + 29 files changed, 2245 insertions(+), 76 deletions(-) create mode 100644 .bitcode/v44-btd-btc-compensation-statements.json create mode 100644 packages/pipelines/asset-pack/src/__tests__/btd-btc-compensation-statements.test.ts create mode 100644 packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts create mode 100644 packages/protocol/src/canonical/v44-btd-btc-compensation-statements.js create mode 100644 packages/protocol/test/v44-btd-btc-compensation-statements.test.js create mode 100644 scripts/check-v44-gate6-btd-btc-compensation-statements.mjs create mode 100644 scripts/generate-v44-btd-btc-compensation-statements.mjs diff --git a/.bitcode/v44-btd-btc-compensation-statements.json b/.bitcode/v44-btd-btc-compensation-statements.json new file mode 100644 index 00000000..543d72ed --- /dev/null +++ b/.bitcode/v44-btd-btc-compensation-statements.json @@ -0,0 +1,421 @@ +{ + "artifactId": "v44-btd-btc-compensation-statements", + "schemaId": "bitcode.v44.btdBtcCompensationStatements.v1", + "version": "V44", + "currentTarget": "V43", + "sourceSafetyVerdict": "source-safe-btd-btc-compensation-statement-metadata", + "generatedAt": "deterministic", + "artifactRoot": "v44-btd-btc-compensation-statements:7f735a88ce95c297b2f2bf5f", + "passed": true, + "objectIds": [ + "BtdBtcCompensationStatements", + "BtdRangeAccountingStatement", + "BtcSettlementAccountingStatement", + "ContributorCompensationStatement", + "DepositorEarningSummary", + "TreasuryRouteStatement", + "BtdBtcReconciliationStatement", + "BtdBtcRepairStatement", + "PackEconomicStatement" + ], + "accountingStateIds": [ + "settlement-accounted", + "pending-btc-finality", + "repair-required", + "withheld-before-settlement" + ], + "btdRangeStateIds": [ + "transferred-to-reader", + "allocated-pending-rights", + "withheld-before-settlement" + ], + "btcSettlementStateIds": [ + "final-settlement-observed", + "observed-payment-pending-finality", + "repair-required" + ], + "contributorCompensationStateIds": [ + "allocated", + "pending-settlement-finality", + "repair-required" + ], + "valueLabelIds": [ + "observed-payment", + "final-settlement", + "contributor-allocation", + "delivery", + "repair-state" + ], + "forbiddenPayloadIds": [ + "protected-source-payloads", + "raw-source-text", + "unpaid-assetpack-source", + "raw-prompts", + "interpolated-prompts", + "raw-provider-responses", + "credentials", + "wallet-private-material", + "private-settlement-payloads", + "value-bearing-mainnet-admission" + ], + "rows": [ + { + "rowId": "btd-range-accounting", + "owner": "packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts", + "contract": "BTD range accounting distinguishes transferred-to-reader, allocated-pending-rights, and withheld-before-settlement state.", + "requiredFields": [ + "BtdRangeAccountingStatement", + "rangeState", + "rightsTransferRoot", + "readReceiptRoot" + ], + "rowRoot": "v44-btd-btc-compensation-row:011e3ecdb07b1424fa0e59df", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "unpaidAssetPackSourceVisible": false + }, + { + "rowId": "btc-settlement-observation", + "owner": "packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts", + "contract": "BTC settlement accounting separates observed payment, final settlement, finality, txid, custody, and repair state.", + "requiredFields": [ + "BtcSettlementAccountingStatement", + "expectedSats", + "observedDebitSats", + "serverCustody: false" + ], + "rowRoot": "v44-btd-btc-compensation-row:0ff5931422a009e3518a6f22", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "unpaidAssetPackSourceVisible": false + }, + { + "rowId": "source-to-shares-contributor-statements", + "owner": "packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts", + "contract": "Contributor compensation statements derive allocated sats, share bps, range slices, and proof roots from source-to-shares allocations.", + "requiredFields": [ + "ContributorCompensationStatement", + "contributor-allocation", + "allocatedSats", + "sourceToSharesRoot" + ], + "rowRoot": "v44-btd-btc-compensation-row:5d52fce695912c99117db2fb", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "unpaidAssetPackSourceVisible": false + }, + { + "rowId": "depositor-earning-summaries", + "owner": "packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts", + "contract": "Depositor earning summaries group contributor statements by depositor wallet without serializing wallet private material.", + "requiredFields": [ + "DepositorEarningSummary", + "depositorWalletId", + "allocatedSats", + "summaryRoot" + ], + "rowRoot": "v44-btd-btc-compensation-row:7aa68d5dc0da56fb6c7283dc", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "unpaidAssetPackSourceVisible": false + }, + { + "rowId": "treasury-routes", + "owner": "packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts", + "contract": "Treasury routes describe reader-to-contributor BTC flows, server custody false, and route state without private settlement payloads.", + "requiredFields": [ + "TreasuryRouteStatement", + "reader-to-contributor-source-to-shares", + "serverCustody: false" + ], + "rowRoot": "v44-btd-btc-compensation-row:1e9c586b9c059dd3e5017ae8", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "unpaidAssetPackSourceVisible": false + }, + { + "rowId": "reconciliation-and-repair", + "owner": "packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts", + "contract": "Accounting statements preserve ledger/database/object-storage reconciliation and repair blockers as source-safe readback.", + "requiredFields": [ + "BtdBtcReconciliationStatement", + "BtdBtcRepairStatement", + "ledgerDatabaseObjectStorageAligned" + ], + "rowRoot": "v44-btd-btc-compensation-row:05095f7a768311aa101e8059", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "unpaidAssetPackSourceVisible": false + }, + { + "rowId": "packs-accounting-readback", + "owner": "uapi/app/packs/PacksPageClient.tsx", + "contract": "/packs renders BTD/BTC accounting state, BTD range, BTC settlement, treasury route, contributor counts, allocation, and accounting root.", + "requiredFields": [ + "Accounting", + "BTD/BTC state", + "Accounting root", + "allocatedContributorSats" + ], + "rowRoot": "v44-btd-btc-compensation-row:596db42d920bee16f6905558", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "unpaidAssetPackSourceVisible": false + }, + { + "rowId": "btd-source-to-shares-primitives", + "owner": "packages/btd/src/source-to-shares.ts", + "contract": "Source-to-shares remains the deterministic largest-remainder basis for contributor allocation and settlement conservation.", + "requiredFields": [ + "source-to-shares-largest-remainder", + "settlementAllocations", + "settlementConservation" + ], + "rowRoot": "v44-btd-btc-compensation-row:27ad23554b98ffa07a12f9d4", + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "unpaidAssetPackSourceVisible": false + } + ], + "sourceRoots": { + "activePointer": "BITCODE_SPEC.txt:4ea77b214c66f69c697bff3d", + "spec": "BITCODE_SPEC_V44.md:992ae5c680f4035900a87f6d", + "delta": "BITCODE_SPEC_V44_DELTA.md:8b0510905db9a702fec88696", + "notes": "BITCODE_SPEC_V44_NOTES.md:9897826394794b53459c4359", + "parity": "BITCODE_SPEC_V44_PARITY_MATRIX.md:5d9b0b7f8b510621a518bbb5", + "roadmap": "SPECIFICATIONS_ROADMAP.md:411696552a2be710bcdc9d01", + "readme": "README.md:4f63eaea3d2660cdc80b7c0f", + "protocolReadme": "packages/protocol/README.md:03c496b8817bbd8cf8b6927d", + "packageJson": "package.json:45bafd0a9f3cd7cea6a1bcff", + "gateWorkflow": ".github/workflows/bitcode-gate-quality.yml:10d2fccdd6178ca00ef821c0", + "canonWorkflow": ".github/workflows/bitcode-canon-quality.yml:6f97f7499bd7d4b1d69aed83", + "economicModel": "packages/protocol/src/canonical/v44-economic-domain-model.js:21b8f5c80ad8322dff2909ed", + "settlementBoundary": "packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts:4e5eebe66c2075acef628682", + "accountingStatements": "packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts:a239683c231a0127c4777a3f", + "accountingStatementsTest": "packages/pipelines/asset-pack/src/__tests__/btd-btc-compensation-statements.test.ts:80c9df62424d8b0df0d155aa", + "sourceToShares": "packages/btd/src/source-to-shares.ts:6e2e84251b29ba2477766708", + "btdSettlement": "packages/btd/src/settlement.ts:871ea57357f464d8fbb0c6af", + "btdReceipts": "packages/btd/src/receipts.ts:663e799baf0715924cb67c14", + "btdReconciliation": "packages/btd/src/reconciliation.ts:d4bc6c129196572ae673c05b", + "packActivityModel": "uapi/components/base/bitcode/activity/pack-activity-model.ts:dcbe49cf25b13fea054dfff6", + "packsClient": "uapi/app/packs/PacksPageClient.tsx:3404c916cdd9f96ed1708065", + "packActivityModelTest": "uapi/tests/packActivityModel.test.ts:972f495ee8658eac72c234ca", + "packsClientTest": "uapi/tests/packsPageClient.test.tsx:60075282ab5c41917233c44f", + "assetPackPackageIndex": "packages/pipelines/asset-pack/src/index.ts:c55bc5e29f7d0b30f9060222", + "assetPackPackageManifest": "packages/pipelines/asset-pack/package.json:d43190e869c08b6d84ee81fa", + "packageIndex": "packages/protocol/src/index.js:eac2606fb210e2c0dd38ae7a", + "packageTypes": "packages/protocol/src/index.d.ts:621beb875ce903727d5daf24", + "packageSource": "packages/protocol/src/canonical/v44-btd-btc-compensation-statements.js:b7cd49efa70c18f93be91661", + "packageTest": "packages/protocol/test/v44-btd-btc-compensation-statements.test.js:8c8bff4a58a07f01c94b1d90", + "generator": "scripts/generate-v44-btd-btc-compensation-statements.mjs:603d3af279f716d625f891c9", + "checker": "scripts/check-v44-gate6-btd-btc-compensation-statements.mjs:754e88d1e8a1aec3645aafb1" + }, + "predicateResults": [ + { + "id": "active-canon-pointer-remains-v43", + "sourcePath": "BITCODE_SPEC.txt", + "passed": true + }, + { + "id": "spec-defines-gate6", + "sourcePath": "BITCODE_SPEC_V44.md", + "passed": true + }, + { + "id": "spec-names-gate6-artifact", + "sourcePath": "BITCODE_SPEC_V44.md", + "passed": true + }, + { + "id": "delta-records-gate6", + "sourcePath": "BITCODE_SPEC_V44_DELTA.md", + "passed": true + }, + { + "id": "notes-records-gate6", + "sourcePath": "BITCODE_SPEC_V44_NOTES.md", + "passed": true + }, + { + "id": "parity-records-gate6", + "sourcePath": "BITCODE_SPEC_V44_PARITY_MATRIX.md", + "passed": true + }, + { + "id": "roadmap-records-gate6", + "sourcePath": "SPECIFICATIONS_ROADMAP.md", + "passed": true + }, + { + "id": "readme-records-gate6", + "sourcePath": "README.md", + "passed": true + }, + { + "id": "protocol-readme-records-gate6", + "sourcePath": "packages/protocol/README.md", + "passed": true + }, + { + "id": "economic-model-prerequisite-present", + "sourcePath": "packages/protocol/src/canonical/v44-economic-domain-model.js", + "passed": true + }, + { + "id": "settlement-boundary-prerequisite-present", + "sourcePath": "packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts", + "passed": true + }, + { + "id": "accounting-builder-defined", + "sourcePath": "packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts", + "passed": true + }, + { + "id": "accounting-state-ids-defined", + "sourcePath": "packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts", + "passed": true + }, + { + "id": "btd-range-state-ids-defined", + "sourcePath": "packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts", + "passed": true + }, + { + "id": "btc-state-ids-defined", + "sourcePath": "packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts", + "passed": true + }, + { + "id": "contributor-state-ids-defined", + "sourcePath": "packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts", + "passed": true + }, + { + "id": "accounting-forbids-source-leakage", + "sourcePath": "packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts", + "passed": true + }, + { + "id": "asset-pack-package-exports-accounting", + "sourcePath": "packages/pipelines/asset-pack/src/index.ts", + "passed": true + }, + { + "id": "asset-pack-manifest-exports-accounting", + "sourcePath": "packages/pipelines/asset-pack/package.json", + "passed": true + }, + { + "id": "accounting-test-covers-builder", + "sourcePath": "packages/pipelines/asset-pack/src/__tests__/btd-btc-compensation-statements.test.ts", + "passed": true + }, + { + "id": "pack-activity-model-projects-accounting", + "sourcePath": "uapi/components/base/bitcode/activity/pack-activity-model.ts", + "passed": true + }, + { + "id": "packs-client-renders-accounting", + "sourcePath": "uapi/app/packs/PacksPageClient.tsx", + "passed": true + }, + { + "id": "pack-activity-test-covers-accounting", + "sourcePath": "uapi/tests/packActivityModel.test.ts", + "passed": true + }, + { + "id": "packs-client-test-covers-accounting", + "sourcePath": "uapi/tests/packsPageClient.test.tsx", + "passed": true + }, + { + "id": "source-to-shares-prerequisite-present", + "sourcePath": "packages/btd/src/source-to-shares.ts", + "passed": true + }, + { + "id": "btd-settlement-prerequisite-present", + "sourcePath": "packages/btd/src/settlement.ts", + "passed": true + }, + { + "id": "btd-receipts-prerequisite-present", + "sourcePath": "packages/btd/src/receipts.ts", + "passed": true + }, + { + "id": "btd-reconciliation-prerequisite-present", + "sourcePath": "packages/btd/src/reconciliation.ts", + "passed": true + }, + { + "id": "package-test-covers-gate6", + "sourcePath": "packages/protocol/test/v44-btd-btc-compensation-statements.test.js", + "passed": true + }, + { + "id": "package-exports-gate6", + "sourcePath": "packages/protocol/src/index.js", + "passed": true + }, + { + "id": "package-types-export-gate6", + "sourcePath": "packages/protocol/src/index.d.ts", + "passed": true + }, + { + "id": "package-json-exposes-gate6", + "sourcePath": "package.json", + "passed": true + }, + { + "id": "gate-workflow-runs-gate6", + "sourcePath": ".github/workflows/bitcode-gate-quality.yml", + "passed": true + }, + { + "id": "canon-workflow-runs-gate6", + "sourcePath": ".github/workflows/bitcode-canon-quality.yml", + "passed": true + }, + { + "id": "generator-exists", + "sourcePath": "scripts/generate-v44-btd-btc-compensation-statements.mjs", + "passed": true + }, + { + "id": "checker-exists", + "sourcePath": "scripts/check-v44-gate6-btd-btc-compensation-statements.mjs", + "passed": true + } + ], + "coverage": { + "btdBtcCompensationStatementsImplemented": true, + "btdRangeStateImplemented": true, + "btcSettlementObservationImplemented": true, + "sourceToSharesContributorStatementsImplemented": true, + "depositorEarningSummariesImplemented": true, + "treasuryRoutesImplemented": true, + "reconciliationStatementImplemented": true, + "repairStatementImplemented": true, + "packsAccountingReadbackImplemented": true, + "sourceSafeMetadataOnly": true, + "protectedSourceVisible": false, + "rawSourceTextVisible": false, + "sourceSnippetVisible": false, + "rawPromptVisible": false, + "interpolatedPromptVisible": false, + "rawProviderResponseVisible": false, + "unpaidAssetPackSourceVisible": false, + "credentialsSerialized": false, + "walletPrivateMaterialVisible": false, + "settlementPrivatePayloadVisible": false, + "valueBearingMainnetAdmitted": false, + "requiredPredicateCount": 36, + "passedPredicateCount": 36, + "failedPredicateIds": [] + } +} diff --git a/.bitcode/v44-depositor-earnings-supply-opportunities.json b/.bitcode/v44-depositor-earnings-supply-opportunities.json index 713b80c5..1548d060 100644 --- a/.bitcode/v44-depositor-earnings-supply-opportunities.json +++ b/.bitcode/v44-depositor-earnings-supply-opportunities.json @@ -5,7 +5,7 @@ "currentTarget": "V43", "sourceSafetyVerdict": "source-safe-depositor-earnings-supply-opportunity-metadata", "generatedAt": "deterministic", - "artifactRoot": "v44-depositor-earnings-supply-opportunities:7d08756f3b7ca3d960bcdce1", + "artifactRoot": "v44-depositor-earnings-supply-opportunities:1dc94c212a971a2277424d63", "passed": true, "objectIds": [ "DepositorEarningSupplyIntelligence", @@ -136,16 +136,16 @@ ], "sourceRoots": { "activePointer": "BITCODE_SPEC.txt:4ea77b214c66f69c697bff3d", - "spec": "BITCODE_SPEC_V44.md:f42417c87b28658f95bfc5e2", - "delta": "BITCODE_SPEC_V44_DELTA.md:67047539c440859038b21e0e", - "notes": "BITCODE_SPEC_V44_NOTES.md:65d2a226a68ddc14e10d4f1d", - "parity": "BITCODE_SPEC_V44_PARITY_MATRIX.md:720f3406bcadd2098ba3a509", - "roadmap": "SPECIFICATIONS_ROADMAP.md:2ef8f1bf59c26479728c3153", - "readme": "README.md:a6409b31ee2230d6889bf26f", - "protocolReadme": "packages/protocol/README.md:e33e94fa5584a2a44cd49c1f", - "packageJson": "package.json:8d6cf25f9345ca24f1a6aa3b", - "gateWorkflow": ".github/workflows/bitcode-gate-quality.yml:11c68267a63ed03d95524e98", - "canonWorkflow": ".github/workflows/bitcode-canon-quality.yml:dfd0de96bd301ec722e4ea67", + "spec": "BITCODE_SPEC_V44.md:992ae5c680f4035900a87f6d", + "delta": "BITCODE_SPEC_V44_DELTA.md:8b0510905db9a702fec88696", + "notes": "BITCODE_SPEC_V44_NOTES.md:9897826394794b53459c4359", + "parity": "BITCODE_SPEC_V44_PARITY_MATRIX.md:5d9b0b7f8b510621a518bbb5", + "roadmap": "SPECIFICATIONS_ROADMAP.md:411696552a2be710bcdc9d01", + "readme": "README.md:4f63eaea3d2660cdc80b7c0f", + "protocolReadme": "packages/protocol/README.md:03c496b8817bbd8cf8b6927d", + "packageJson": "package.json:45bafd0a9f3cd7cea6a1bcff", + "gateWorkflow": ".github/workflows/bitcode-gate-quality.yml:10d2fccdd6178ca00ef821c0", + "canonWorkflow": ".github/workflows/bitcode-canon-quality.yml:6f97f7499bd7d4b1d69aed83", "economicModel": "packages/protocol/src/canonical/v44-economic-domain-model.js:21b8f5c80ad8322dff2909ed", "portfolioMarketIntelligence": "packages/protocol/src/canonical/v44-packs-portfolio-market-intelligence.js:f5d37e086c9bbbdb6202da53", "optionPolicy": "packages/pipelines/asset-pack/src/deposit-asset-pack-option-policy.ts:12816b23b2d8e304ab4a1638", @@ -155,11 +155,11 @@ "depositClient": "uapi/app/deposit/DepositPageClient.tsx:3aee0372e480aa8212be7fe0", "depositRouteTest": "uapi/tests/depositRouteModel.test.ts:f60d0f4541bb0dae5d455f13", "depositPageTest": "uapi/tests/depositPageClient.test.tsx:44960f6c465c87194a689958", - "assetPackPackageIndex": "packages/pipelines/asset-pack/src/index.ts:4d900afeb3eb6ab1202403ee", - "assetPackPackageManifest": "packages/pipelines/asset-pack/package.json:7f4a82493f32385420e2082c", + "assetPackPackageIndex": "packages/pipelines/asset-pack/src/index.ts:c55bc5e29f7d0b30f9060222", + "assetPackPackageManifest": "packages/pipelines/asset-pack/package.json:d43190e869c08b6d84ee81fa", "sourceToShares": "packages/btd/src/source-to-shares.ts:6e2e84251b29ba2477766708", - "packageIndex": "packages/protocol/src/index.js:806cfbf3ee86977fead7b928", - "packageTypes": "packages/protocol/src/index.d.ts:907bd1de313f4d5503462765", + "packageIndex": "packages/protocol/src/index.js:eac2606fb210e2c0dd38ae7a", + "packageTypes": "packages/protocol/src/index.d.ts:621beb875ce903727d5daf24", "packageSource": "packages/protocol/src/canonical/v44-depositor-earnings-supply-opportunities.js:cf9cf5f99f00dbf3201c8c8e", "packageTest": "packages/protocol/test/v44-depositor-earnings-supply-opportunities.test.js:1b025c1904f94a1069b64ddf", "generator": "scripts/generate-v44-depositor-earnings-supply-opportunities.mjs:0844124207cbfe8264f12a2f", diff --git a/.bitcode/v44-economic-domain-model.json b/.bitcode/v44-economic-domain-model.json index 1db3e59b..7c1de5c2 100644 --- a/.bitcode/v44-economic-domain-model.json +++ b/.bitcode/v44-economic-domain-model.json @@ -5,7 +5,7 @@ "currentTarget": "V43", "sourceSafetyVerdict": "source-safe-economic-domain-model-metadata", "generatedAt": "deterministic", - "artifactRoot": "v44-economic-domain-model:a043eb060041550b6d966f70", + "artifactRoot": "v44-economic-domain-model:49cb52f14d9b584c13c190e2", "passed": true, "economicObjectIds": [ "EnterprisePackPortfolio", @@ -759,18 +759,18 @@ ], "sourceRoots": { "activePointer": "BITCODE_SPEC.txt:4ea77b214c66f69c697bff3d", - "spec": "BITCODE_SPEC_V44.md:f42417c87b28658f95bfc5e2", - "delta": "BITCODE_SPEC_V44_DELTA.md:67047539c440859038b21e0e", - "notes": "BITCODE_SPEC_V44_NOTES.md:65d2a226a68ddc14e10d4f1d", - "parity": "BITCODE_SPEC_V44_PARITY_MATRIX.md:720f3406bcadd2098ba3a509", - "roadmap": "SPECIFICATIONS_ROADMAP.md:2ef8f1bf59c26479728c3153", - "readme": "README.md:a6409b31ee2230d6889bf26f", - "protocolReadme": "packages/protocol/README.md:e33e94fa5584a2a44cd49c1f", - "packageJson": "package.json:8d6cf25f9345ca24f1a6aa3b", - "gateWorkflow": ".github/workflows/bitcode-gate-quality.yml:11c68267a63ed03d95524e98", - "canonWorkflow": ".github/workflows/bitcode-canon-quality.yml:dfd0de96bd301ec722e4ea67", - "packageIndex": "packages/protocol/src/index.js:806cfbf3ee86977fead7b928", - "packageTypes": "packages/protocol/src/index.d.ts:907bd1de313f4d5503462765", + "spec": "BITCODE_SPEC_V44.md:992ae5c680f4035900a87f6d", + "delta": "BITCODE_SPEC_V44_DELTA.md:8b0510905db9a702fec88696", + "notes": "BITCODE_SPEC_V44_NOTES.md:9897826394794b53459c4359", + "parity": "BITCODE_SPEC_V44_PARITY_MATRIX.md:5d9b0b7f8b510621a518bbb5", + "roadmap": "SPECIFICATIONS_ROADMAP.md:411696552a2be710bcdc9d01", + "readme": "README.md:4f63eaea3d2660cdc80b7c0f", + "protocolReadme": "packages/protocol/README.md:03c496b8817bbd8cf8b6927d", + "packageJson": "package.json:45bafd0a9f3cd7cea6a1bcff", + "gateWorkflow": ".github/workflows/bitcode-gate-quality.yml:10d2fccdd6178ca00ef821c0", + "canonWorkflow": ".github/workflows/bitcode-canon-quality.yml:6f97f7499bd7d4b1d69aed83", + "packageIndex": "packages/protocol/src/index.js:eac2606fb210e2c0dd38ae7a", + "packageTypes": "packages/protocol/src/index.d.ts:621beb875ce903727d5daf24", "packageSource": "packages/protocol/src/canonical/v44-economic-domain-model.js:21b8f5c80ad8322dff2909ed", "packageTest": "packages/protocol/test/v44-economic-domain-model.test.js:b89f71aa0cf578dabbec3cd9", "generator": "scripts/generate-v44-economic-domain-model.mjs:3390fbc087e919bedb14550c", diff --git a/.bitcode/v44-packs-portfolio-market-intelligence.json b/.bitcode/v44-packs-portfolio-market-intelligence.json index 01fc336a..9bdc5895 100644 --- a/.bitcode/v44-packs-portfolio-market-intelligence.json +++ b/.bitcode/v44-packs-portfolio-market-intelligence.json @@ -5,7 +5,7 @@ "currentTarget": "V43", "sourceSafetyVerdict": "source-safe-packs-portfolio-market-intelligence-metadata", "generatedAt": "deterministic", - "artifactRoot": "v44-packs-portfolio-market-intelligence:7285b92f4d7d0c1634de4446", + "artifactRoot": "v44-packs-portfolio-market-intelligence:1a78e4a8b514891a3e8b6c0b", "passed": true, "portfolioViewIds": [ "enterprise-pack-portfolio", @@ -109,23 +109,23 @@ ], "sourceRoots": { "activePointer": "BITCODE_SPEC.txt:4ea77b214c66f69c697bff3d", - "spec": "BITCODE_SPEC_V44.md:f42417c87b28658f95bfc5e2", - "delta": "BITCODE_SPEC_V44_DELTA.md:67047539c440859038b21e0e", - "notes": "BITCODE_SPEC_V44_NOTES.md:65d2a226a68ddc14e10d4f1d", - "parity": "BITCODE_SPEC_V44_PARITY_MATRIX.md:720f3406bcadd2098ba3a509", - "roadmap": "SPECIFICATIONS_ROADMAP.md:2ef8f1bf59c26479728c3153", - "readme": "README.md:a6409b31ee2230d6889bf26f", - "protocolReadme": "packages/protocol/README.md:e33e94fa5584a2a44cd49c1f", - "packageJson": "package.json:8d6cf25f9345ca24f1a6aa3b", - "gateWorkflow": ".github/workflows/bitcode-gate-quality.yml:11c68267a63ed03d95524e98", - "canonWorkflow": ".github/workflows/bitcode-canon-quality.yml:dfd0de96bd301ec722e4ea67", + "spec": "BITCODE_SPEC_V44.md:992ae5c680f4035900a87f6d", + "delta": "BITCODE_SPEC_V44_DELTA.md:8b0510905db9a702fec88696", + "notes": "BITCODE_SPEC_V44_NOTES.md:9897826394794b53459c4359", + "parity": "BITCODE_SPEC_V44_PARITY_MATRIX.md:5d9b0b7f8b510621a518bbb5", + "roadmap": "SPECIFICATIONS_ROADMAP.md:411696552a2be710bcdc9d01", + "readme": "README.md:4f63eaea3d2660cdc80b7c0f", + "protocolReadme": "packages/protocol/README.md:03c496b8817bbd8cf8b6927d", + "packageJson": "package.json:45bafd0a9f3cd7cea6a1bcff", + "gateWorkflow": ".github/workflows/bitcode-gate-quality.yml:10d2fccdd6178ca00ef821c0", + "canonWorkflow": ".github/workflows/bitcode-canon-quality.yml:6f97f7499bd7d4b1d69aed83", "economicModel": "packages/protocol/src/canonical/v44-economic-domain-model.js:21b8f5c80ad8322dff2909ed", - "model": "uapi/components/base/bitcode/activity/pack-activity-model.ts:77058cc82039a3bd1b7514bf", + "model": "uapi/components/base/bitcode/activity/pack-activity-model.ts:dcbe49cf25b13fea054dfff6", "route": "uapi/app/api/packs/activity/route.ts:97c5a4539d48562eb5de66e8", - "client": "uapi/app/packs/PacksPageClient.tsx:b70aa97a8022a9c42995a030", - "uapiTest": "uapi/tests/packActivityModel.test.ts:541e71f2806dc1497379d0f1", - "packageIndex": "packages/protocol/src/index.js:806cfbf3ee86977fead7b928", - "packageTypes": "packages/protocol/src/index.d.ts:907bd1de313f4d5503462765", + "client": "uapi/app/packs/PacksPageClient.tsx:3404c916cdd9f96ed1708065", + "uapiTest": "uapi/tests/packActivityModel.test.ts:972f495ee8658eac72c234ca", + "packageIndex": "packages/protocol/src/index.js:eac2606fb210e2c0dd38ae7a", + "packageTypes": "packages/protocol/src/index.d.ts:621beb875ce903727d5daf24", "packageTest": "packages/protocol/test/v44-packs-portfolio-market-intelligence.test.js:ea02fa03d210e6480036fc0f", "generator": "scripts/generate-v44-packs-portfolio-market-intelligence.mjs:c106ef7138e3a330d405bec2", "checker": "scripts/check-v44-gate3-packs-portfolio-market-intelligence.mjs:0de0b87cbc96bb307cc1533a" diff --git a/.bitcode/v44-reading-budget-quote-policy.json b/.bitcode/v44-reading-budget-quote-policy.json index 47924f23..35bd50da 100644 --- a/.bitcode/v44-reading-budget-quote-policy.json +++ b/.bitcode/v44-reading-budget-quote-policy.json @@ -5,7 +5,7 @@ "currentTarget": "V43", "sourceSafetyVerdict": "source-safe-reading-budget-quote-policy-metadata", "generatedAt": "deterministic", - "artifactRoot": "v44-reading-budget-quote-policy:89af8933000a377911238076", + "artifactRoot": "v44-reading-budget-quote-policy:eb9bdf61b50a1d5bc8a210f3", "passed": true, "objectIds": [ "ReadingBudgetPolicy", @@ -145,24 +145,24 @@ ], "sourceRoots": { "activePointer": "BITCODE_SPEC.txt:4ea77b214c66f69c697bff3d", - "spec": "BITCODE_SPEC_V44.md:f42417c87b28658f95bfc5e2", - "delta": "BITCODE_SPEC_V44_DELTA.md:67047539c440859038b21e0e", - "notes": "BITCODE_SPEC_V44_NOTES.md:65d2a226a68ddc14e10d4f1d", - "parity": "BITCODE_SPEC_V44_PARITY_MATRIX.md:720f3406bcadd2098ba3a509", - "roadmap": "SPECIFICATIONS_ROADMAP.md:2ef8f1bf59c26479728c3153", - "readme": "README.md:a6409b31ee2230d6889bf26f", - "protocolReadme": "packages/protocol/README.md:e33e94fa5584a2a44cd49c1f", - "packageJson": "package.json:8d6cf25f9345ca24f1a6aa3b", - "gateWorkflow": ".github/workflows/bitcode-gate-quality.yml:11c68267a63ed03d95524e98", - "canonWorkflow": ".github/workflows/bitcode-canon-quality.yml:dfd0de96bd301ec722e4ea67", + "spec": "BITCODE_SPEC_V44.md:992ae5c680f4035900a87f6d", + "delta": "BITCODE_SPEC_V44_DELTA.md:8b0510905db9a702fec88696", + "notes": "BITCODE_SPEC_V44_NOTES.md:9897826394794b53459c4359", + "parity": "BITCODE_SPEC_V44_PARITY_MATRIX.md:5d9b0b7f8b510621a518bbb5", + "roadmap": "SPECIFICATIONS_ROADMAP.md:411696552a2be710bcdc9d01", + "readme": "README.md:4f63eaea3d2660cdc80b7c0f", + "protocolReadme": "packages/protocol/README.md:03c496b8817bbd8cf8b6927d", + "packageJson": "package.json:45bafd0a9f3cd7cea6a1bcff", + "gateWorkflow": ".github/workflows/bitcode-gate-quality.yml:10d2fccdd6178ca00ef821c0", + "canonWorkflow": ".github/workflows/bitcode-canon-quality.yml:6f97f7499bd7d4b1d69aed83", "economicModel": "packages/protocol/src/canonical/v44-economic-domain-model.js:21b8f5c80ad8322dff2909ed", "readModel": "uapi/app/read/read-route-model.ts:a31ae532904b0ba357eb9e67", "readClient": "uapi/app/read/ReadPageClient.tsx:8c810ed63b6cf895cead0a01", "readModelTest": "uapi/tests/readRouteModel.test.ts:622a125a72c8457baf4c9d6d", "sourceToShares": "packages/btd/src/source-to-shares.ts:6e2e84251b29ba2477766708", "btcFeeOperation": "packages/btd/src/btc-fee-operation.ts:65f910e8e471fe199939e9c4", - "packageIndex": "packages/protocol/src/index.js:806cfbf3ee86977fead7b928", - "packageTypes": "packages/protocol/src/index.d.ts:907bd1de313f4d5503462765", + "packageIndex": "packages/protocol/src/index.js:eac2606fb210e2c0dd38ae7a", + "packageTypes": "packages/protocol/src/index.d.ts:621beb875ce903727d5daf24", "packageSource": "packages/protocol/src/canonical/v44-reading-budget-quote-policy.js:d663034fe86ccbe85d5749f5", "packageTest": "packages/protocol/test/v44-reading-budget-quote-policy.test.js:2d9b19c509e0b28a829c44a3", "generator": "scripts/generate-v44-reading-budget-quote-policy.mjs:e2c376cb03ef80c210eef688", diff --git a/.github/workflows/bitcode-canon-quality.yml b/.github/workflows/bitcode-canon-quality.yml index d3ac55c4..04bf3e4d 100644 --- a/.github/workflows/bitcode-canon-quality.yml +++ b/.github/workflows/bitcode-canon-quality.yml @@ -376,6 +376,9 @@ jobs: if [ -f scripts/check-v44-gate5-depositor-earnings-supply-opportunities.mjs ]; then node scripts/check-v44-gate5-depositor-earnings-supply-opportunities.mjs --skip-branch-check --skip-uapi-tests --skip-package-tests fi + if [ -f scripts/check-v44-gate6-btd-btc-compensation-statements.mjs ]; then + node scripts/check-v44-gate6-btd-btc-compensation-statements.mjs --skip-branch-check --skip-uapi-tests --skip-package-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 c72646b2..4fd0187e 100644 --- a/.github/workflows/bitcode-gate-quality.yml +++ b/.github/workflows/bitcode-gate-quality.yml @@ -505,6 +505,9 @@ jobs: if [ -f scripts/check-v44-gate5-depositor-earnings-supply-opportunities.mjs ]; then node scripts/check-v44-gate5-depositor-earnings-supply-opportunities.mjs --skip-branch-check --skip-uapi-tests --skip-package-tests fi + if [ -f scripts/check-v44-gate6-btd-btc-compensation-statements.mjs ]; then + node scripts/check-v44-gate6-btd-btc-compensation-statements.mjs --skip-branch-check --skip-uapi-tests --skip-package-tests + fi fi else echo "Unexpected BITCODE_SPEC.txt pointer: $POINTER" >&2 @@ -552,6 +555,9 @@ jobs: if [ "$POINTER" = "V43" ] && [ -f scripts/check-v44-gate5-depositor-earnings-supply-opportunities.mjs ]; then node scripts/check-v44-gate5-depositor-earnings-supply-opportunities.mjs --skip-branch-check --skip-uapi-tests --skip-package-tests fi + if [ "$POINTER" = "V43" ] && [ -f scripts/check-v44-gate6-btd-btc-compensation-statements.mjs ]; then + node scripts/check-v44-gate6-btd-btc-compensation-statements.mjs --skip-branch-check --skip-uapi-tests --skip-package-tests + fi if [ "$POINTER" != "V43" ]; then if [ -f scripts/check-v43-gate1-packs-read-deposit-roadmap.mjs ]; then node scripts/check-v43-gate1-packs-read-deposit-roadmap.mjs --skip-branch-check diff --git a/BITCODE_SPEC_V44.md b/BITCODE_SPEC_V44.md index 4c6300b7..55e7ae4c 100644 --- a/BITCODE_SPEC_V44.md +++ b/BITCODE_SPEC_V44.md @@ -3,12 +3,12 @@ ## Status - Version: `V44` -- V44 state: draft Gate 5 Depositor earnings supply opportunity work over promoted V43 product routes +- V44 state: draft Gate 6 BTD/BTC compensation statement work over promoted V43 product routes - Current canonical/latest target: `V43` - Prior canonical anchor: `BITCODE_SPEC_V43.md` - Prior generated proof appendix: `BITCODE_SPEC_V43_PROVEN.md` -- Generated structured artifact inventory: draft `.bitcode/v44-*` artifacts now include Gate 2 economic domain, Gate 3 Packs portfolio market intelligence, Gate 4 Reading budget quote policy, and Gate 5 Depositor earnings supply opportunity reports; all remain source-safe metadata only -- Source parity state: V44 begins from promoted `/packs`, `/read`, `/deposit`, agentic Depositing, five-step Reading, BTD/BTC settlement, and PackActivity canon; Gate 5 binds `/deposit` earning, demand, ROI, criticality, compensation range, and source-safe supply recommendation readback without minting BTD or admitting value-bearing mainnet +- Generated structured artifact inventory: draft `.bitcode/v44-*` artifacts now include Gate 2 economic domain, Gate 3 Packs portfolio market intelligence, Gate 4 Reading budget quote policy, Gate 5 Depositor earnings supply opportunity, and Gate 6 BTD/BTC compensation statement reports; all remain source-safe metadata only +- Source parity state: V44 begins from promoted `/packs`, `/read`, `/deposit`, agentic Depositing, five-step Reading, BTD/BTC settlement, and PackActivity canon; Gate 6 binds source-safe BTD range state, BTC settlement observations, source-to-shares contributor allocations, depositor summaries, treasury routes, reconciliation, repair state, and `/packs` accounting readback without exposing unpaid source or admitting value-bearing mainnet - Notes companion: `BITCODE_SPEC_V44_NOTES.md` - Delta companion: `BITCODE_SPEC_V44_DELTA.md` - Parity companion: `BITCODE_SPEC_V44_PARITY_MATRIX.md` @@ -266,6 +266,20 @@ Gate 6 must bind BTD range state, BTC settlement observations, source-to-shares allocation, contributor statements, depositor earning summaries, treasury routes, repair states, and ledger/database/object-storage reconciliation. +Gate 6 closes through `V44BtdBtcCompensationStatements` in +`packages/protocol/src/canonical/v44-btd-btc-compensation-statements.js`, +deterministic `.bitcode/v44-btd-btc-compensation-statements.json`, +`generate:v44-btd-btc-compensation-statements`, +`check:v44-btd-btc-compensation-statements`, and `check:v44-gate6`. +The settlement boundary now projects `BtdBtcCompensationStatements` with +source-safe BTD range state, BTC settlement observation, source-to-shares +contributor allocations, depositor earning summaries, treasury routes, +ledger/database/object-storage reconciliation, and repair statements. `/packs` +renders the accounting readback without exposing protected source, raw source +text, unpaid AssetPack source, raw prompts, interpolated prompts, raw provider +responses, wallet private material, private settlement payloads, or +value-bearing mainnet admission. + ## V44 Gate 7 Organization Policy, Approval, And Wallet Authority Gate 7 must bind organization-level policy across `/read`, `/deposit`, and @@ -622,7 +636,7 @@ quality, accessibility, visual inspectability, and generated quality evidence. | `.bitcode/v44-packs-portfolio-market-intelligence.json` | Packs portfolio and market intelligence | implemented-source-safe | | `.bitcode/v44-reading-budget-quote-policy.json` | Reading budget and quote policy | implemented-source-safe | | `.bitcode/v44-depositor-earnings-supply-opportunities.json` | depositor earnings and supply opportunities | implemented-source-safe | -| `.bitcode/v44-btd-btc-compensation-statements.json` | BTD/BTC compensation statements | planned | +| `.bitcode/v44-btd-btc-compensation-statements.json` | BTD/BTC compensation statements | implemented-source-safe | | `.bitcode/v44-organization-policy-wallet-authority.json` | organization policy and wallet authority | planned | | `.bitcode/v44-enterprise-economic-ux.json` | enterprise economic UX | planned | | `.bitcode/v44-scaled-network-rehearsal.json` | scaled network rehearsal | planned | @@ -700,6 +714,10 @@ Gate 5 validates with `pnpm run generate:v44-depositor-earnings-supply-opportunities`, `pnpm run check:v44-depositor-earnings-supply-opportunities`, and `pnpm run check:v44-gate5`. +Gate 6 validates with +`pnpm run generate:v44-btd-btc-compensation-statements`, +`pnpm run check:v44-btd-btc-compensation-statements`, and +`pnpm run check:v44-gate6`. Shared draft posture validates with `node scripts/check-bitcode-spec-family.mjs --version V44 --mode draft --current-target V43`, `node scripts/check-bitcode-canon-posture-drift.mjs --active-canon V43 --draft-target V44`, @@ -775,7 +793,7 @@ Inherited. | `.bitcode/v44-packs-portfolio-market-intelligence.json` | portfolio and market intelligence | implemented-source-safe | | `.bitcode/v44-reading-budget-quote-policy.json` | budget and quote policy | implemented-source-safe | | `.bitcode/v44-depositor-earnings-supply-opportunities.json` | depositor earning opportunity | implemented-source-safe | -| `.bitcode/v44-btd-btc-compensation-statements.json` | BTD/BTC/source-to-shares statement | planned | +| `.bitcode/v44-btd-btc-compensation-statements.json` | BTD/BTC/source-to-shares statement | implemented-source-safe | | `.bitcode/v44-organization-policy-wallet-authority.json` | organization policy and wallet authority | planned | | `.bitcode/v44-enterprise-economic-ux.json` | enterprise economic UX | planned | | `.bitcode/v44-scaled-network-rehearsal.json` | scaled rehearsal | planned | @@ -789,6 +807,10 @@ Inherited. Gate 2 adds deterministic `.bitcode/v44-economic-domain-model.json`. Gate 3 adds deterministic `.bitcode/v44-packs-portfolio-market-intelligence.json`. +Gate 4 adds deterministic `.bitcode/v44-reading-budget-quote-policy.json`. +Gate 5 adds deterministic +`.bitcode/v44-depositor-earnings-supply-opportunities.json`. +Gate 6 adds deterministic `.bitcode/v44-btd-btc-compensation-statements.json`. ### Shared generated-artifact fields diff --git a/BITCODE_SPEC_V44_DELTA.md b/BITCODE_SPEC_V44_DELTA.md index 1e79a4c6..ea740c16 100644 --- a/BITCODE_SPEC_V44_DELTA.md +++ b/BITCODE_SPEC_V44_DELTA.md @@ -3,12 +3,12 @@ ## Status - Version: `V44` -- V44 state: draft Gate 5 Depositor earnings supply opportunity work over promoted V43 +- V44 state: draft Gate 6 BTD/BTC compensation statement work over promoted V43 - Current canonical/latest target: `V43` - Prior canonical anchor: `BITCODE_SPEC_V43.md` - Prior generated proof appendix: `BITCODE_SPEC_V43_PROVEN.md` -- Generated structured artifact inventory: Gate 2 adds deterministic `.bitcode/v44-economic-domain-model.json`; Gate 3 adds deterministic `.bitcode/v44-packs-portfolio-market-intelligence.json`; Gate 4 adds deterministic `.bitcode/v44-reading-budget-quote-policy.json`; Gate 5 adds deterministic `.bitcode/v44-depositor-earnings-supply-opportunities.json` -- Source parity state: Gate 5 binds package-backed Depositor earning supply intelligence, `/deposit` route/UI projections, docs, workflow, package script, checker, and protocol/package/UAPI tests +- Generated structured artifact inventory: Gate 2 adds deterministic `.bitcode/v44-economic-domain-model.json`; Gate 3 adds deterministic `.bitcode/v44-packs-portfolio-market-intelligence.json`; Gate 4 adds deterministic `.bitcode/v44-reading-budget-quote-policy.json`; Gate 5 adds deterministic `.bitcode/v44-depositor-earnings-supply-opportunities.json`; Gate 6 adds deterministic `.bitcode/v44-btd-btc-compensation-statements.json` +- Source parity state: Gate 6 binds package-backed BTD/BTC compensation statements, `/packs` accounting readback, docs, workflow, package script, checker, and protocol/package/UAPI tests - Notes companion: `BITCODE_SPEC_V44_NOTES.md` - Delta companion: `BITCODE_SPEC_V44_DELTA.md` - Parity companion: `BITCODE_SPEC_V44_PARITY_MATRIX.md` @@ -74,6 +74,13 @@ intelligence over source-safe deposit options. It does not execute payout, mint BTD, disclose protected source, or convert estimated compensation ranges into final settlement truth. +V44 Gate 6 closes BTD/BTC/source-to-shares compensation statements over the +settlement rights delivery boundary. It projects BTD range state, BTC +settlement observations, contributor allocations, depositor earning summaries, +treasury routes, reconciliation, and repair posture into source-safe `/packs` +accounting readback. It does not disclose unpaid source, serialize wallet +private material, execute payout, or admit value-bearing mainnet operation. + ## Pre-Implementation Sequence 1. Open V44 spec family, roadmap, checker, package script, workflow posture, @@ -105,6 +112,10 @@ Gate 5 validates with `pnpm run generate:v44-depositor-earnings-supply-opportunities`, `pnpm run check:v44-depositor-earnings-supply-opportunities`, and `pnpm run check:v44-gate5`. +Gate 6 validates with +`pnpm run generate:v44-btd-btc-compensation-statements`, +`pnpm run check:v44-btd-btc-compensation-statements`, and +`pnpm run check:v44-gate6`. Shared draft posture validates with `node scripts/check-bitcode-spec-family.mjs --version V44 --mode draft --current-target V43`, `node scripts/check-bitcode-canon-posture-drift.mjs --active-canon V43 --draft-target V44`, diff --git a/BITCODE_SPEC_V44_NOTES.md b/BITCODE_SPEC_V44_NOTES.md index 2810b62b..2007059f 100644 --- a/BITCODE_SPEC_V44_NOTES.md +++ b/BITCODE_SPEC_V44_NOTES.md @@ -3,12 +3,12 @@ ## Status - Version: `V44` -- V44 state: draft notes include Gate 5 Depositor earnings supply opportunity work over promoted V43 +- V44 state: draft notes include Gate 6 BTD/BTC compensation statement work over promoted V43 - Current canonical/latest target: `V43` - Prior canonical anchor: `BITCODE_SPEC_V43.md` - Prior generated proof appendix: `BITCODE_SPEC_V43_PROVEN.md` -- Generated structured artifact inventory: Gate 2 adds deterministic `.bitcode/v44-economic-domain-model.json`; Gate 3 adds deterministic `.bitcode/v44-packs-portfolio-market-intelligence.json`; Gate 4 adds deterministic `.bitcode/v44-reading-budget-quote-policy.json`; Gate 5 adds deterministic `.bitcode/v44-depositor-earnings-supply-opportunities.json` -- Source parity state: notes align roadmap, docs, workflow posture, package source, generated artifact, receipt taxonomy, `/packs` portfolio market intelligence, `/read` budget quote policy, and `/deposit` earning supply opportunity intelligence for active V43 / draft V44 +- Generated structured artifact inventory: Gate 2 adds deterministic `.bitcode/v44-economic-domain-model.json`; Gate 3 adds deterministic `.bitcode/v44-packs-portfolio-market-intelligence.json`; Gate 4 adds deterministic `.bitcode/v44-reading-budget-quote-policy.json`; Gate 5 adds deterministic `.bitcode/v44-depositor-earnings-supply-opportunities.json`; Gate 6 adds deterministic `.bitcode/v44-btd-btc-compensation-statements.json` +- Source parity state: notes align roadmap, docs, workflow posture, package source, generated artifact, receipt taxonomy, `/packs` portfolio market intelligence and accounting readback, `/read` budget quote policy, and `/deposit` earning supply opportunity intelligence for active V43 / draft V44 - Scope: V44 notes for digitizing and tokenizing scaled engineering economies through enterprise AssetPack portfolio, market intelligence, BTD/BTC accounting, governance, and compensation operation - Last fully realized canonical target preserved in source: `V43` @@ -116,6 +116,17 @@ compensation, disclose protected source, or treat estimated earning ranges as final settlement truth. Source-to-shares remains the future receipt-backed allocation method after a paid Need-Fit settlement selects admitted supply. +## Gate 6 note + +Gate 6 makes paid settlement accounting inspectable without changing settlement +law. The settlement rights delivery boundary can now emit source-safe BTD/BTC +compensation statements that distinguish BTD range state, BTC settlement +observation, source-to-shares contributor allocations, depositor earning +summaries, treasury routes, reconciliation state, and repair blockers. `/packs` +can render those statements as accounting readback. It cannot disclose unpaid +AssetPack source, raw source text, wallet private material, private settlement +payloads, raw prompts, provider responses, or value-bearing mainnet authority. + ## Gate 3 note Gate 3 makes `/packs` the first scaled economic inspection surface. It keeps diff --git a/BITCODE_SPEC_V44_PARITY_MATRIX.md b/BITCODE_SPEC_V44_PARITY_MATRIX.md index 88d3f6aa..fa1a7c5c 100644 --- a/BITCODE_SPEC_V44_PARITY_MATRIX.md +++ b/BITCODE_SPEC_V44_PARITY_MATRIX.md @@ -3,12 +3,12 @@ ## Status - Version: `V44` -- V44 state: draft parity includes Gate 5 Depositor earnings supply opportunity work +- V44 state: draft parity includes Gate 6 BTD/BTC compensation statement work - Current canonical/latest target: `V43` - Prior canonical anchor: `BITCODE_SPEC_V43.md` - Prior generated proof appendix: `BITCODE_SPEC_V43_PROVEN.md` -- Generated structured artifact inventory: Gate 2 adds deterministic `.bitcode/v44-economic-domain-model.json`; Gate 3 adds deterministic `.bitcode/v44-packs-portfolio-market-intelligence.json`; Gate 4 adds deterministic `.bitcode/v44-reading-budget-quote-policy.json`; Gate 5 adds deterministic `.bitcode/v44-depositor-earnings-supply-opportunities.json` -- Source parity state: Gate 5 requires package/route/UI/docs/workflow/checker/test parity for Depositor earning supply opportunity intelligence +- Generated structured artifact inventory: Gate 2 adds deterministic `.bitcode/v44-economic-domain-model.json`; Gate 3 adds deterministic `.bitcode/v44-packs-portfolio-market-intelligence.json`; Gate 4 adds deterministic `.bitcode/v44-reading-budget-quote-policy.json`; Gate 5 adds deterministic `.bitcode/v44-depositor-earnings-supply-opportunities.json`; Gate 6 adds deterministic `.bitcode/v44-btd-btc-compensation-statements.json` +- Source parity state: Gate 6 requires package/route/UI/docs/workflow/checker/test parity for BTD/BTC compensation statements and Packs accounting readback - Scope: parity for V44 enterprise economic operation over promoted V43 product routes - Last fully realized canonical target preserved in source: `V43` @@ -33,7 +33,7 @@ artifacts, workflow checks, and local/staging rehearsal receipts. | Packs portfolio | `/packs` portfolio search, market intelligence, saved filters, economic facets | implemented | | Reading procurement | Budget, quote policy, approval thresholds, purchase governance through `.bitcode/v44-reading-budget-quote-policy.json` | implemented | | Depositor earnings | ROI, demand, compensation opportunity, supply recommendations through `.bitcode/v44-depositor-earnings-supply-opportunities.json` | implemented | -| Accounting statements | BTD/BTC/source-to-shares statements and reconciliation | drafted | +| Accounting statements | BTD/BTC/source-to-shares statements and reconciliation through `.bitcode/v44-btd-btc-compensation-statements.json` | implemented | | Organization governance | Roles, budgets, wallet authority, source criticality, approvals | drafted | | Enterprise UX | Dense economic surfaces, proof expansion, responsive/accessibility proof | drafted | | Scaled rehearsal | Many-pack, many-org local/staging-testnet economic rehearsal | drafted | @@ -45,7 +45,7 @@ artifacts, workflow checks, and local/staging rehearsal receipts. | --- | --- | --- | | Source safety | No protected source, unpaid source, raw prompts, provider payloads, credentials, wallet secrets | implemented | | Economic labels | Estimate, quote, observed payment, settlement, allocation, delivery, repair are distinct | implemented | -| Ledger reconciliation | Portfolio statements reconcile to receipts before finality | drafted | +| Ledger reconciliation | Portfolio statements reconcile to receipts before finality | implemented | | Route authority | `/packs`, `/read`, `/deposit` do not bypass protocol law | implemented prerequisite | | Tests and proofs | Each gate has generated artifacts, package tests, route tests, and workflow checks | implemented prerequisite | diff --git a/README.md b/README.md index 923bf1db..612168b0 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,9 @@ The active V44 draft target makes those routes economically operable at enterprise scale: `/packs` becomes the portfolio and market-intelligence surface, `/read` gains budgeted procurement and quote governance, and `/deposit` gains earning, demand, ROI, compensation-range, and source-safe -supply opportunity visibility. +supply opportunity visibility. Gate 6 adds `/packs` accounting readback for +source-safe BTD/BTC/source-to-shares compensation statements over paid +settlement receipts. V41 Gate 1 opens the prompts-as-programs specification family over active V40 with `check:v41-gate1`. V41 will catalogue every raw PromptPart and composed @@ -335,6 +337,19 @@ continue withholding protected source, raw source text, unpaid AssetPack source, prompts, provider payloads, wallet private material, private settlement payloads, and value-bearing mainnet operation. +V44 Gate 6 adds `V44BtdBtcCompensationStatements`, +`.bitcode/v44-btd-btc-compensation-statements.json`, +`generate:v44-btd-btc-compensation-statements`, +`check:v44-btd-btc-compensation-statements`, and `check:v44-gate6`. It binds +settlement rights delivery boundaries to source-safe +`BtdBtcCompensationStatements`: BTD range state, BTC settlement observations, +source-to-shares contributor allocations, depositor earning summaries, +treasury routes, ledger/database/object-storage reconciliation, repair +statements, `/packs` accounting readback, and tests that continue withholding +protected source, raw source text, unpaid AssetPack source, prompts, provider +payloads, wallet private material, private settlement payloads, and +value-bearing mainnet operation. + Exchange is inherited V36 canon: market-wide activity master-detail, buy/sell/ bid/ask/cancel/accept/settle/history flows, AssetPack range trading, rights-transfer review, pricing/liquidity/wrapper analysis, settlement diff --git a/SPECIFICATIONS_ROADMAP.md b/SPECIFICATIONS_ROADMAP.md index 7ac9ad43..40b8ad82 100644 --- a/SPECIFICATIONS_ROADMAP.md +++ b/SPECIFICATIONS_ROADMAP.md @@ -5,8 +5,8 @@ - Current active canonical pointer: `BITCODE_SPEC.txt` -> `V43` - Current active canon: `BITCODE_SPEC_V43.md` - Current draft target: `BITCODE_SPEC_V44.md`. -- Current working gate: V44 Gate 5 Depositor Earnings, ROI, And Supply Opportunity Intelligence. -- Next queued work after V44 Gate 5: BTD/BTC compensation statements, organization policy, enterprise UX, scaled local/staging rehearsal, and V44 promotion readiness. +- Current working gate: V44 Gate 6 BTD/BTC Accounting And Contributor Compensation Statements. +- Next queued work after V44 Gate 6: organization policy, enterprise UX, scaled local/staging rehearsal, and V44 promotion readiness. - Latest closed version: V43 Route Product Cleanup, which promoted `/packs`, `/read`, and `/deposit`; PackActivity master-detail; five-step Reading route UX; deposit AssetPack option synthesis; deposit criticality/ROI/compensation policy; option admission; product route UX; cross-route rehearsal; and V43 promotion readiness. - Recent V43 canonical promotion anchor: V43 canonical promotion updated `BITCODE_SPEC.txt` to `V43`, generated `BITCODE_SPEC_V43_PROVEN.md`, preserved active V43 / draft V44 runtime posture, and closed route-product cleanup canon. - V44 Gate 1 opening anchor: scaled engineering economy opens over promoted V43 with V44 SPEC, DELTA, NOTES, and PARITY files, `check:v44-gate1`, active V43 / draft V44 posture, and a ten-gate plan for economic domain models, Packs portfolio intelligence, Reading budget/quote governance, Depositor earnings/ROI intelligence, BTD/BTC compensation statements, organization policy/wallet authority, enterprise economic UX, scaled local/staging rehearsal, and promotion readiness. @@ -14,6 +14,7 @@ - V44 Gate 3 closure anchor: scaled engineering economy now owns package-backed `V44PacksPortfolioMarketIntelligence`, deterministic `.bitcode/v44-packs-portfolio-market-intelligence.json`, `/api/packs/activity` `marketIntelligence` projection, source-safe `PackPortfolioPositionProjection`, `PackMarketSignalProjection`, saved filter presets, organization views, demand/supply/unfit-Need/settlement/compensation/delivery/repair signals, settlement/compensation/delivery/repair facets, `/packs` portfolio and market panels, proof-root drilldown continuity, no-source-leak tests, package exports, UAPI model tests, protocol tests, workflow wiring, and `check:v44-gate3`. - V44 Gate 4 closure anchor: scaled engineering economy now owns package-backed `V44ReadingBudgetQuotePolicy`, deterministic `.bitcode/v44-reading-budget-quote-policy.json`, `/read` `ReadProcurementGovernance`, source-safe Reading budget envelopes, approval thresholds, quote expiry, deterministic measurement-weight-volume share-to-fee policy, buyer authorization, wallet authority, BTC/BTD settlement readiness blockers, pre-purchase review boundaries, route UI readback, no-source-leak tests, package exports, UAPI route model tests, protocol tests, workflow wiring, and `check:v44-gate4`. - V44 Gate 5 closure anchor: scaled engineering economy now owns package-backed `V44DepositorEarningsSupplyOpportunities`, deterministic `.bitcode/v44-depositor-earnings-supply-opportunities.json`, `DepositorEarningSupplyIntelligence`, source-safe likely demand, unfit Need opportunities, ROI posture, source criticality posture, estimate-only BTC compensation ranges, earning statements, source-to-shares proof boundaries, supply recommendations, `/deposit` route UI readback, package exports, pipeline/package/UAPI/protocol tests, workflow wiring, and `check:v44-gate5`. +- V44 Gate 6 closure anchor: scaled engineering economy now owns package-backed `V44BtdBtcCompensationStatements`, deterministic `.bitcode/v44-btd-btc-compensation-statements.json`, `BtdBtcCompensationStatements`, source-safe BTD range accounting, BTC settlement observations, source-to-shares contributor compensation statements, depositor earning summaries, treasury routes, ledger/database/object-storage reconciliation, repair statements, `/packs` accounting readback, package exports, pipeline/package/UAPI/protocol tests, workflow wiring, and `check:v44-gate6`. - Latest prior closed version: V42 Reliable MVP Experience, which promoted shortest-path Depositing, five-step Reading, ReadNeed review/resynthesis, ReadFitsFinding source-safe preview and quote, settlement rights transfer, repository delivery, AI-reading demonstration, local/staging MVP rehearsal, and V42 promotion readiness. - Recent V42 canonical promotion anchor: V42 canonical promotion updated `BITCODE_SPEC.txt` to `V42`, generated `BITCODE_SPEC_V42_PROVEN.md`, preserved active V42 / draft V43 runtime posture, and closed reliable MVP experience canon. - 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. diff --git a/package.json b/package.json index 2f9e6b13..aaf66b90 100644 --- a/package.json +++ b/package.json @@ -371,6 +371,9 @@ "generate:v44-depositor-earnings-supply-opportunities": "node scripts/generate-v44-depositor-earnings-supply-opportunities.mjs", "check:v44-depositor-earnings-supply-opportunities": "node scripts/generate-v44-depositor-earnings-supply-opportunities.mjs --check", "check:v44-gate5": "node scripts/check-v44-gate5-depositor-earnings-supply-opportunities.mjs", + "generate:v44-btd-btc-compensation-statements": "node scripts/generate-v44-btd-btc-compensation-statements.mjs", + "check:v44-btd-btc-compensation-statements": "node scripts/generate-v44-btd-btc-compensation-statements.mjs --check", + "check:v44-gate6": "node scripts/check-v44-gate6-btd-btc-compensation-statements.mjs", "generate:v38-inference-surface-inventory": "node scripts/generate-v38-inference-surface-inventory.mjs", "check:v38-inference-surface-inventory": "node scripts/generate-v38-inference-surface-inventory.mjs --check", "check:v38-gate2": "node scripts/check-v38-gate2-inference-surface-inventory.mjs", diff --git a/packages/pipelines/asset-pack/package.json b/packages/pipelines/asset-pack/package.json index a86ea670..97acc104 100644 --- a/packages/pipelines/asset-pack/package.json +++ b/packages/pipelines/asset-pack/package.json @@ -16,6 +16,7 @@ "./deposit-asset-pack-option-policy": "./src/deposit-asset-pack-option-policy.ts", "./deposit-asset-pack-option-admission": "./src/deposit-asset-pack-option-admission.ts", "./depositor-earning-supply-intelligence": "./src/depositor-earning-supply-intelligence.ts", + "./btd-btc-compensation-statements": "./src/btd-btc-compensation-statements.ts", "./read-need": "./src/read-need.ts", "./read-need-review-resynthesis": "./src/read-need-review-resynthesis.ts", "./read-fits-finding-runtime": "./src/read-fits-finding-runtime.ts", diff --git a/packages/pipelines/asset-pack/src/__tests__/btd-btc-compensation-statements.test.ts b/packages/pipelines/asset-pack/src/__tests__/btd-btc-compensation-statements.test.ts new file mode 100644 index 00000000..4c91be94 --- /dev/null +++ b/packages/pipelines/asset-pack/src/__tests__/btd-btc-compensation-statements.test.ts @@ -0,0 +1,238 @@ +import { buildAssetPackPreviewBoundary } from '../asset-pack-preview-boundary'; +import { + buildAssetPackSettlementRightsDeliveryBoundary, +} from '../asset-pack-settlement-rights-delivery'; +import { + assertBtdBtcCompensationStatementsSourceSafe, + buildBtdBtcCompensationStatements, +} from '../btd-btc-compensation-statements'; +import { + acceptReadNeed, + synthesizeReadNeedForPipelineInput, +} from '../read-need'; + +function acceptedNeed() { + return acceptReadNeed( + synthesizeReadNeedForPipelineInput({ + read: { + id: 'read-accounting', + prompt: 'Settle an AssetPack, account for BTD/BTC rights, compensate contributors, and reconcile projections.', + }, + sourceRevision: { + repositoryFullName: 'engineeredsoftware/ENGI', + branch: 'main', + commit: '31bbc0c5227b6b3aed5d107fd8507d35ec22970a', + }, + targetArtifactKinds: ['asset-pack', 'settlement-proof', 'compensation-statement'], + closureCriteria: [ + 'BTC payment finality is observed.', + 'BTD range rights are transferred.', + 'Source-to-shares contributor allocations conserve sats.', + 'Ledger, database, and object storage reconcile.', + ], + }), + '2026-05-29T00:00:00.000Z', + ); +} + +function fitResult(finalScore = 0.93): any { + return { + schema: 'bitcode.asset-pack.fit-result', + resultState: 'worthy_fit', + resultReasons: ['Selected proof-bearing fit deposits for BTD/BTC accounting.'], + fitDepositAssetIds: ['fit-deposit-accounting-1', 'fit-deposit-accounting-2'], + selectedCandidateAssetIds: ['fit-deposit-accounting-1', 'fit-deposit-accounting-2'], + queryRoot: 'sha256:query-accounting', + rankingRoot: 'sha256:ranking-accounting', + searchedAssetCount: 11, + embeddingPolicy: { + provider: 'openai', + model: 'text-embedding-3-small', + dimensions: 1536, + distanceMetric: 'cosine', + vectorMatchRpc: 'match_deliverable_vectors', + }, + selectionTrace: { + selectedCandidates: [ + { + assetId: 'fit-deposit-accounting-1', + title: 'Accounting fit deposit one', + artifactKind: 'asset-pack', + useTier: 'settlement-eligible', + sourceBinding: { + repositoryFullName: 'engineeredsoftware/ENGI', + sourceBranch: 'main', + sourceCommit: '31bbc0c5227b6b3aed5d107fd8507d35ec22970a', + contentRoot: 'sha256:content-accounting-1', + }, + selectedUnits: [{ unitId: 'unit-1', unitKind: 'summary', path: 'README.md', unitHash: 'sha256:unit-1' }], + scores: { + finalScore, + semanticScore: 0.9, + textScore: 0.86, + unitScore: 0.84, + repositoryScore: 1, + revisionScore: 1, + artifactKindScore: 0.95, + proofScore: 1, + measurementScore: 0.94, + providerScore: 0.8, + penaltyMass: 0, + }, + verification: { + repositoryBound: true, + sourceRevisionBound: true, + hasWalletOrAttestationProof: true, + hasAssetMeasurementEvidence: true, + proofRootRequired: true, + proofRootPresent: true, + reconciliationReadbackRequired: true, + reconciliationReadbackPresent: true, + blockers: [], + warnings: [], + }, + recall: { + matchedTerms: ['settlement', 'accounting'], + matchedTargetKinds: ['asset-pack'], + matchedUnitIds: ['unit-1'], + providerMatchCount: 1, + providerIds: ['lexical'], + }, + proofEvidence: { + hasWalletOrAttestationProof: true, + attestationCount: 1, + signingSurfacePresent: true, + identitySurfacePresent: true, + githubBoundaryPresent: true, + githubAppAuthSurfacePresent: true, + proofRoot: 'sha256:proof-accounting-1', + }, + measurementEvidence: { + hasAssetMeasurementEvidence: true, + assetMeasurementPresent: true, + measurementProvenanceCount: 1, + measurementRoot: 'sha256:measurement-accounting-1', + }, + readbackEvidence: { + proofRootRequired: true, + proofRootPresent: true, + reconciliationReadbackRequired: true, + reconciliationReadbackPresent: true, + reconciliationReadbackRoot: 'sha256:readback-accounting-1', + }, + rejectionReasons: [], + }, + { + assetId: 'fit-deposit-accounting-2', + scores: { + finalScore: 0.88, + semanticScore: 0.84, + }, + proofEvidence: { proofRoot: 'sha256:proof-accounting-2' }, + measurementEvidence: { measurementRoot: 'sha256:measurement-accounting-2' }, + readbackEvidence: { reconciliationReadbackRoot: 'sha256:readback-accounting-2' }, + }, + ], + fitDeposits: [], + blockedCandidates: [], + candidateRanking: [], + rejectedCandidateCount: 0, + }, + }; +} + +function previewBoundary() { + return buildAssetPackPreviewBoundary({ + need: acceptedNeed(), + fitResult: fitResult(), + pullRequestTarget: 'https://github.com/engineeredsoftware/ENGI/pull/441', + createdAt: '2026-05-29T00:00:00.000Z', + }); +} + +describe('BTD/BTC compensation statements', () => { + it('accounts confirmed settlement, BTD range transfer, contributor compensation, and reconciliation', () => { + const settlementBoundary = buildAssetPackSettlementRightsDeliveryBoundary({ + previewBoundary: previewBoundary(), + readerWalletId: 'reader-wallet-accounting', + depositorWalletId: 'depositor-wallet-accounting', + createdAt: '2026-05-29T00:00:00.000Z', + }); + const statements = buildBtdBtcCompensationStatements({ + settlementBoundary, + createdAt: '2026-05-29T00:00:00.000Z', + }); + + expect(statements).toMatchObject({ + schema: 'bitcode.asset-pack.btd-btc-compensation-statements', + statements: 'BtdBtcCompensationStatements', + state: 'settlement-accounted', + btdRange: { + rangeState: 'transferred-to-reader', + rangeSliceCount: 2, + }, + btcSettlement: { + state: 'final-settlement-observed', + valueLabel: 'final-settlement', + serverCustody: false, + }, + reconciliation: { + ledgerDatabaseObjectStorageAligned: true, + }, + disclosure: { + sourceSafeMetadataOnly: true, + protectedSourcePayloadSerialized: false, + walletPrivateMaterialVisible: false, + }, + }); + expect(statements.contributorCompensationStatements).toHaveLength(2); + expect(statements.depositorEarningSummaries).toHaveLength(2); + expect(statements.treasuryRoutes).toHaveLength(2); + expect(statements.aggregate.allocatedContributorSats).toBe(statements.aggregate.finalSettlementSats); + expect(statements.aggregate.unallocatedSats).toBe(0); + expect(statements.aggregate.sourceToSharesAdmissible).toBe(true); + expect(statements.roots.accountingRoot).toMatch(/^btd-btc-compensation-statements:/); + expect(JSON.stringify(statements)).not.toContain('diff --git'); + expect(JSON.stringify(statements)).not.toContain('PRIVATE_SOURCE_DO_NOT_SERIALIZE'); + assertBtdBtcCompensationStatementsSourceSafe(statements); + }); + + it('marks finality-pending settlement without transferring source-bearing visibility', () => { + const settlementBoundary = buildAssetPackSettlementRightsDeliveryBoundary({ + previewBoundary: previewBoundary(), + finality: { + finalityState: 'broadcast', + confirmations: 0, + blockHeight: null, + }, + createdAt: '2026-05-29T00:00:00.000Z', + }); + const statements = buildBtdBtcCompensationStatements({ settlementBoundary }); + + expect(statements.state).toBe('pending-btc-finality'); + expect(statements.btcSettlement.state).toBe('observed-payment-pending-finality'); + expect(statements.btdRange.rangeState).toBe('allocated-pending-rights'); + expect(statements.contributorCompensationStatements[0].state).toBe('pending-settlement-finality'); + expect(statements.aggregate.sourceBearingDeliveryUnlockedToReader).toBe(false); + expect(statements.treasuryRoutes[0].routeState).toBe('pending-finality'); + }); + + it('surfaces conservation repair when payment accounting is underpaid', () => { + const preview = previewBoundary(); + const settlementBoundary = buildAssetPackSettlementRightsDeliveryBoundary({ + previewBoundary: preview, + paymentObservation: { + observedDebitSats: preview.quoteReceipt.sats - 4, + observedCreditSats: preview.quoteReceipt.sats - 4, + }, + createdAt: '2026-05-29T00:00:00.000Z', + }); + const statements = buildBtdBtcCompensationStatements({ settlementBoundary }); + + expect(statements.state).toBe('repair-required'); + expect(statements.btcSettlement.state).toBe('repair-required'); + expect(statements.contributorCompensationStatements[0].state).toBe('repair-required'); + expect(statements.repair.blockers).toContain('source_to_shares_conservation_failed'); + expect(statements.aggregate.sourceToSharesAdmissible).toBe(false); + }); +}); diff --git a/packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts b/packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts new file mode 100644 index 00000000..bc0cf752 --- /dev/null +++ b/packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts @@ -0,0 +1,570 @@ +import { createHash } from 'node:crypto'; +import type { + AssetPackSettlementRightsDeliveryBoundary, + AssetPackSettlementRightsDeliveryState, +} from './asset-pack-settlement-rights-delivery'; + +export type BtdBtcAccountingState = + | 'settlement-accounted' + | 'pending-btc-finality' + | 'repair-required' + | 'withheld-before-settlement'; + +export type BtdRangeAccountingState = + | 'transferred-to-reader' + | 'allocated-pending-rights' + | 'withheld-before-settlement'; + +export type BtcSettlementAccountingState = + | 'final-settlement-observed' + | 'observed-payment-pending-finality' + | 'repair-required'; + +export type ContributorCompensationStatementState = + | 'allocated' + | 'pending-settlement-finality' + | 'repair-required'; + +export interface BtdRangeAccountingStatement { + schema: 'bitcode.asset-pack.btd-range-accounting-statement'; + assetPackId: string; + rangeState: BtdRangeAccountingState; + tokenCount: number; + rangeStart: number | null; + rangeEndExclusive: number | null; + rangeSliceCount: number; + rightsTransferRoot: string | null; + readReceiptRoot: string | null; + statementRoot: string; +} + +export interface BtcSettlementAccountingStatement { + schema: 'bitcode.asset-pack.btc-settlement-accounting-statement'; + state: BtcSettlementAccountingState; + valueLabel: 'observed-payment' | 'final-settlement' | 'repair-state'; + priceAsset: 'BTC'; + btcNetwork: 'testnet' | 'mainnet' | 'signet' | 'regtest'; + expectedSats: number; + observedDebitSats: number; + observedCreditSats: number; + txid: string; + confirmations: number; + finalityState: string; + finalityRoot: string; + paymentReceiptRoot: string; + serverCustody: false; + statementRoot: string; +} + +export interface ContributorCompensationStatement { + schema: 'bitcode.asset-pack.contributor-compensation-statement'; + statementId: string; + contributorRef: string; + depositId: string; + assetPackId: string; + state: ContributorCompensationStatementState; + valueLabel: 'contributor-allocation'; + shareBps: number; + allocatedSats: number; + rangeTokenCount: number; + allocationRoot: string; + rangeSliceRoot: string | null; + sourceToSharesRoot: string | null; + settlementId: string; + proofRoot: string; +} + +export interface DepositorEarningSummary { + schema: 'bitcode.deposit.depositor-earning-summary'; + depositorWalletId: string; + state: ContributorCompensationStatementState; + valueLabel: 'contributor-allocation'; + contributorStatementCount: number; + allocatedSats: number; + shareBps: number; + settlementId: string; + summaryRoot: string; +} + +export interface TreasuryRouteStatement { + schema: 'bitcode.asset-pack.treasury-route-statement'; + routeId: string; + routeState: 'routed' | 'pending-finality' | 'repair-required'; + fromWalletId: string; + toWalletId: string; + routeKind: 'reader-to-contributor-source-to-shares'; + priceAsset: 'BTC'; + amountSats: number; + serverCustody: false; + walletPrivateMaterialVisible: false; + routeRoot: string; +} + +export interface BtdBtcReconciliationStatement { + schema: 'bitcode.asset-pack.btd-btc-reconciliation-statement'; + state: string; + blocking: boolean; + ledgerDatabaseObjectStorageAligned: boolean; + repairActionCount: number; + reconciliationRoot: string; + statementRoot: string; +} + +export interface BtdBtcRepairStatement { + schema: 'bitcode.asset-pack.btd-btc-repair-statement'; + state: AssetPackSettlementRightsDeliveryState; + blockers: string[]; + warnings: string[]; + nextActions: string[]; + repairRoot: string; + statementRoot: string; +} + +export interface BtdBtcCompensationStatements { + schema: 'bitcode.asset-pack.btd-btc-compensation-statements'; + statements: 'BtdBtcCompensationStatements'; + createdAt: string; + assetPackId: string; + readId: string; + orderId: string; + state: BtdBtcAccountingState; + settlementBoundaryState: AssetPackSettlementRightsDeliveryState; + valueLabels: [ + 'observed-payment', + 'final-settlement', + 'contributor-allocation', + 'delivery', + 'repair-state', + ]; + btdRange: BtdRangeAccountingStatement; + btcSettlement: BtcSettlementAccountingStatement; + contributorCompensationStatements: ContributorCompensationStatement[]; + depositorEarningSummaries: DepositorEarningSummary[]; + treasuryRoutes: TreasuryRouteStatement[]; + reconciliation: BtdBtcReconciliationStatement; + repair: BtdBtcRepairStatement; + aggregate: { + finalSettlementSats: number; + allocatedContributorSats: number; + unallocatedSats: number; + contributorCount: number; + depositorCount: number; + settlementConservationState: string; + sourceToSharesAdmissible: boolean; + ledgerDatabaseObjectStorageReconciled: boolean; + sourceBearingDeliveryUnlockedToReader: boolean; + aggregateRoot: string; + }; + disclosure: { + sourceSafeMetadataOnly: true; + protectedSourcePayloadSerialized: false; + rawSourceTextVisible: false; + unpaidAssetPackSourceVisible: false; + rawPromptVisible: false; + interpolatedPromptVisible: false; + rawProviderResponseVisible: false; + walletPrivateMaterialVisible: false; + settlementPrivatePayloadVisible: false; + valueBearingMainnetAdmitted: false; + }; + roots: { + accountingRoot: string; + settlementBoundaryRoot: string; + btdRangeStatementRoot: string; + btcSettlementStatementRoot: string; + contributorStatementRoots: string[]; + depositorSummaryRoots: string[]; + treasuryRouteRoots: string[]; + reconciliationStatementRoot: string; + repairStatementRoot: string; + aggregateRoot: string; + }; +} + +const FORBIDDEN_SOURCE_MARKERS = [ + 'PRIVATE_SOURCE_DO_NOT_SERIALIZE', + `BEGIN_${'PRIVATE'}_KEY`, + 'wallet_private_material', + 'raw_provider_response', + 'unpaid_assetpack_source', + 'protected_source_payload', + 'private_settlement_payload', + 'value_bearing_mainnet', +]; + +function stableStringify(value: unknown): string { + if (typeof value === 'undefined') return 'null'; + if (typeof value === 'bigint') return JSON.stringify(value.toString()); + if (value === null || typeof value !== 'object') return JSON.stringify(value); + if (Array.isArray(value)) return `[${value.map((entry) => stableStringify(entry)).join(',')}]`; + return `{${Object.keys(value as Record) + .sort() + .map((key) => `${JSON.stringify(key)}:${stableStringify((value as Record)[key])}`) + .join(',')}}`; +} + +function root(prefix: string, value: unknown) { + return `${prefix}:${createHash('sha256').update(stableStringify(value)).digest('hex').slice(0, 24)}`; +} + +function recordValue(value: unknown): Record { + return value && typeof value === 'object' && !Array.isArray(value) + ? value as Record + : {}; +} + +function arrayValue(value: unknown): Record[] { + return Array.isArray(value) + ? value.map(recordValue).filter((entry) => Object.keys(entry).length > 0) + : []; +} + +function stringValue(value: unknown, fallback = '') { + return typeof value === 'string' && value.trim() ? value.trim() : fallback; +} + +function numberValue(value: unknown, fallback = 0) { + if (typeof value === 'number' && Number.isFinite(value)) return value; + if (typeof value === 'bigint') return Number(value); + if (typeof value === 'string' && value.trim() && Number.isFinite(Number(value))) return Number(value); + return fallback; +} + +function proofRecord(boundary: AssetPackSettlementRightsDeliveryBoundary) { + return recordValue(boundary.sourceToSharesProof); +} + +function settlementConservation(boundary: AssetPackSettlementRightsDeliveryBoundary) { + return recordValue(proofRecord(boundary).settlementConservation); +} + +function sourceToSharesAdmissible(boundary: AssetPackSettlementRightsDeliveryBoundary) { + const conservation = settlementConservation(boundary); + return conservation.state === 'balanced' && conservation.settlementAdmissible === true; +} + +function accountingState(boundary: AssetPackSettlementRightsDeliveryBoundary): BtdBtcAccountingState { + if (boundary.state === 'settlement_delivered') return 'settlement-accounted'; + if (boundary.finalityReceipt.finalityState !== 'confirmed') return 'pending-btc-finality'; + if (boundary.state.includes('repair') || boundary.repairPosture.blockers.length > 0) return 'repair-required'; + return 'withheld-before-settlement'; +} + +function btcState(boundary: AssetPackSettlementRightsDeliveryBoundary): BtcSettlementAccountingState { + const paymentMatches = + boundary.paymentObservation.expectedSats === boundary.paymentObservation.observedDebitSats && + boundary.paymentObservation.expectedSats === boundary.paymentObservation.observedCreditSats; + if (!paymentMatches) return 'repair-required'; + if (boundary.finalityReceipt.finalityState !== 'confirmed') return 'observed-payment-pending-finality'; + return 'final-settlement-observed'; +} + +function contributorState(boundary: AssetPackSettlementRightsDeliveryBoundary): ContributorCompensationStatementState { + if (!sourceToSharesAdmissible(boundary)) return 'repair-required'; + if (boundary.finalityReceipt.finalityState !== 'confirmed') return 'pending-settlement-finality'; + return 'allocated'; +} + +function treasuryRouteState( + boundary: AssetPackSettlementRightsDeliveryBoundary, +): TreasuryRouteStatement['routeState'] { + if (!sourceToSharesAdmissible(boundary)) return 'repair-required'; + if (boundary.finalityReceipt.finalityState !== 'confirmed') return 'pending-finality'; + return 'routed'; +} + +function btdRangeStatement(boundary: AssetPackSettlementRightsDeliveryBoundary): BtdRangeAccountingStatement { + const proof = proofRecord(boundary); + const rangeSlices = arrayValue(proof.rangeSlices); + const tokenCount = rangeSlices.reduce((total, slice) => total + numberValue(slice.tokenCount, 0), 0); + const starts = rangeSlices + .map((slice) => numberValue(slice.rangeStart, Number.NaN)) + .filter(Number.isFinite); + const ends = rangeSlices + .map((slice) => numberValue(slice.rangeEndExclusive, Number.NaN)) + .filter(Number.isFinite); + const withoutRoot = { + schema: 'bitcode.asset-pack.btd-range-accounting-statement' as const, + assetPackId: boundary.assetPackId, + rangeState: boundary.rightsTransferReceipt + ? 'transferred-to-reader' as const + : boundary.sourceToSharesProof + ? 'allocated-pending-rights' as const + : 'withheld-before-settlement' as const, + tokenCount, + rangeStart: starts.length ? Math.min(...starts) : null, + rangeEndExclusive: ends.length ? Math.max(...ends) : null, + rangeSliceCount: rangeSlices.length, + rightsTransferRoot: boundary.proofRoots.rightsTransferRoot, + readReceiptRoot: boundary.proofRoots.btdReadReceiptRoot, + }; + return { + ...withoutRoot, + statementRoot: root('btd-range-accounting-statement', withoutRoot), + }; +} + +function btcSettlementStatement(boundary: AssetPackSettlementRightsDeliveryBoundary): BtcSettlementAccountingStatement { + const state = btcState(boundary); + const withoutRoot = { + schema: 'bitcode.asset-pack.btc-settlement-accounting-statement' as const, + state, + valueLabel: + state === 'final-settlement-observed' + ? 'final-settlement' as const + : state === 'observed-payment-pending-finality' + ? 'observed-payment' as const + : 'repair-state' as const, + priceAsset: 'BTC' as const, + btcNetwork: boundary.paymentObservation.btcNetwork, + expectedSats: boundary.paymentObservation.expectedSats, + observedDebitSats: boundary.paymentObservation.observedDebitSats, + observedCreditSats: boundary.paymentObservation.observedCreditSats, + txid: boundary.paymentObservation.txid, + confirmations: boundary.finalityReceipt.confirmations, + finalityState: boundary.finalityReceipt.finalityState, + finalityRoot: boundary.finalityReceipt.finalityRoot, + paymentReceiptRoot: boundary.paymentObservation.paymentReceiptRoot, + serverCustody: false as const, + }; + return { + ...withoutRoot, + statementRoot: root('btc-settlement-accounting-statement', withoutRoot), + }; +} + +function contributorStatements( + boundary: AssetPackSettlementRightsDeliveryBoundary, +): ContributorCompensationStatement[] { + const proof = proofRecord(boundary); + const rangeSlicesByDepositId = new Map( + arrayValue(proof.rangeSlices).map((slice) => [stringValue(slice.depositId), slice]), + ); + const state = contributorState(boundary); + return arrayValue(proof.settlementAllocations).map((allocation, index) => { + const depositId = stringValue(allocation.depositId, `deposit-${index + 1}`); + const rangeSlice = rangeSlicesByDepositId.get(depositId) || {}; + const withoutRoot = { + schema: 'bitcode.asset-pack.contributor-compensation-statement' as const, + statementId: `contributor-compensation-${boundary.orderId}-${index + 1}`, + contributorRef: stringValue(allocation.depositorWalletId, 'depositor-wallet-unbound'), + depositId, + assetPackId: stringValue(allocation.assetPackId, boundary.assetPackId), + state, + valueLabel: 'contributor-allocation' as const, + shareBps: numberValue(allocation.shareBps, 0), + allocatedSats: numberValue(allocation.allocatedSats, 0), + rangeTokenCount: numberValue(rangeSlice.tokenCount, 0), + allocationRoot: stringValue(allocation.allocationRoot, root('allocation', allocation)), + rangeSliceRoot: stringValue(rangeSlice.sliceRoot, '') || null, + sourceToSharesRoot: boundary.proofRoots.sourceToSharesRoot, + settlementId: boundary.orderId, + }; + return { + ...withoutRoot, + proofRoot: root('contributor-compensation-statement', withoutRoot), + }; + }); +} + +function depositorSummaries( + boundary: AssetPackSettlementRightsDeliveryBoundary, + statements: ContributorCompensationStatement[], +): DepositorEarningSummary[] { + const byWallet = new Map(); + for (const statement of statements) { + const entries = byWallet.get(statement.contributorRef) || []; + entries.push(statement); + byWallet.set(statement.contributorRef, entries); + } + return [...byWallet.entries()] + .sort(([left], [right]) => left.localeCompare(right)) + .map(([depositorWalletId, entries]) => { + const allocatedSats = entries.reduce((total, entry) => total + entry.allocatedSats, 0); + const shareBps = entries.reduce((total, entry) => total + entry.shareBps, 0); + const withoutRoot = { + schema: 'bitcode.deposit.depositor-earning-summary' as const, + depositorWalletId, + state: contributorState(boundary), + valueLabel: 'contributor-allocation' as const, + contributorStatementCount: entries.length, + allocatedSats, + shareBps, + settlementId: boundary.orderId, + }; + return { + ...withoutRoot, + summaryRoot: root('depositor-earning-summary', withoutRoot), + }; + }); +} + +function treasuryRoutes( + boundary: AssetPackSettlementRightsDeliveryBoundary, + statements: ContributorCompensationStatement[], +): TreasuryRouteStatement[] { + return statements.map((statement, index) => { + const withoutRoot = { + schema: 'bitcode.asset-pack.treasury-route-statement' as const, + routeId: `treasury-route-${boundary.orderId}-${index + 1}`, + routeState: treasuryRouteState(boundary), + fromWalletId: boundary.paymentObservation.payerWalletId, + toWalletId: statement.contributorRef, + routeKind: 'reader-to-contributor-source-to-shares' as const, + priceAsset: 'BTC' as const, + amountSats: statement.allocatedSats, + serverCustody: false as const, + walletPrivateMaterialVisible: false as const, + }; + return { + ...withoutRoot, + routeRoot: root('treasury-route-statement', withoutRoot), + }; + }); +} + +function reconciliationStatement(boundary: AssetPackSettlementRightsDeliveryBoundary): BtdBtcReconciliationStatement { + const withoutRoot = { + schema: 'bitcode.asset-pack.btd-btc-reconciliation-statement' as const, + state: boundary.reconciliationReport.state, + blocking: boundary.reconciliationReport.blocking, + ledgerDatabaseObjectStorageAligned: + boundary.reconciliationReport.state === 'aligned' && boundary.reconciliationReport.blocking === false, + repairActionCount: boundary.reconciliationReport.repairActions.length, + reconciliationRoot: boundary.proofRoots.reconciliationRoot, + }; + return { + ...withoutRoot, + statementRoot: root('btd-btc-reconciliation-statement', withoutRoot), + }; +} + +function repairStatement(boundary: AssetPackSettlementRightsDeliveryBoundary): BtdBtcRepairStatement { + const withoutRoot = { + schema: 'bitcode.asset-pack.btd-btc-repair-statement' as const, + state: boundary.repairPosture.state, + blockers: [...boundary.repairPosture.blockers], + warnings: [...boundary.repairPosture.warnings], + nextActions: [...boundary.repairPosture.nextActions], + repairRoot: boundary.repairPosture.repairRoot, + }; + return { + ...withoutRoot, + statementRoot: root('btd-btc-repair-statement', withoutRoot), + }; +} + +export function buildBtdBtcCompensationStatements(input: { + settlementBoundary: AssetPackSettlementRightsDeliveryBoundary; + createdAt?: string | null; +}): BtdBtcCompensationStatements { + const boundary = input.settlementBoundary; + const btdRange = btdRangeStatement(boundary); + const btcSettlement = btcSettlementStatement(boundary); + const contributorCompensationStatements = contributorStatements(boundary); + const depositorEarningSummaries = depositorSummaries(boundary, contributorCompensationStatements); + const routes = treasuryRoutes(boundary, contributorCompensationStatements); + const reconciliation = reconciliationStatement(boundary); + const repair = repairStatement(boundary); + const conservation = settlementConservation(boundary); + const allocatedContributorSats = contributorCompensationStatements.reduce( + (total, statement) => total + statement.allocatedSats, + 0, + ); + const aggregateWithoutRoot = { + finalSettlementSats: boundary.paymentObservation.observedCreditSats, + allocatedContributorSats, + unallocatedSats: boundary.paymentObservation.observedCreditSats - allocatedContributorSats, + contributorCount: contributorCompensationStatements.length, + depositorCount: depositorEarningSummaries.length, + settlementConservationState: stringValue(conservation.state, 'not-recorded'), + sourceToSharesAdmissible: sourceToSharesAdmissible(boundary), + ledgerDatabaseObjectStorageReconciled: reconciliation.ledgerDatabaseObjectStorageAligned, + sourceBearingDeliveryUnlockedToReader: boundary.sourceSafety.sourceBearingDeliveryUnlockedToReader, + }; + const aggregateRoot = root('btd-btc-compensation-aggregate', aggregateWithoutRoot); + const withoutRoot = { + schema: 'bitcode.asset-pack.btd-btc-compensation-statements' as const, + statements: 'BtdBtcCompensationStatements' as const, + createdAt: input.createdAt || new Date(0).toISOString(), + assetPackId: boundary.assetPackId, + readId: boundary.readId, + orderId: boundary.orderId, + state: accountingState(boundary), + settlementBoundaryState: boundary.state, + valueLabels: [ + 'observed-payment', + 'final-settlement', + 'contributor-allocation', + 'delivery', + 'repair-state', + ] as BtdBtcCompensationStatements['valueLabels'], + btdRange, + btcSettlement, + contributorCompensationStatements, + depositorEarningSummaries, + treasuryRoutes: routes, + reconciliation, + repair, + aggregate: { + ...aggregateWithoutRoot, + aggregateRoot, + }, + disclosure: { + sourceSafeMetadataOnly: true as const, + protectedSourcePayloadSerialized: false as const, + rawSourceTextVisible: false as const, + unpaidAssetPackSourceVisible: false as const, + rawPromptVisible: false as const, + interpolatedPromptVisible: false as const, + rawProviderResponseVisible: false as const, + walletPrivateMaterialVisible: false as const, + settlementPrivatePayloadVisible: false as const, + valueBearingMainnetAdmitted: false as const, + }, + }; + const accountingRoot = root('btd-btc-compensation-statements', withoutRoot); + const statements = { + ...withoutRoot, + roots: { + accountingRoot, + settlementBoundaryRoot: boundary.proofRoots.boundaryRoot, + btdRangeStatementRoot: btdRange.statementRoot, + btcSettlementStatementRoot: btcSettlement.statementRoot, + contributorStatementRoots: contributorCompensationStatements.map((statement) => statement.proofRoot), + depositorSummaryRoots: depositorEarningSummaries.map((summary) => summary.summaryRoot), + treasuryRouteRoots: routes.map((route) => route.routeRoot), + reconciliationStatementRoot: reconciliation.statementRoot, + repairStatementRoot: repair.statementRoot, + aggregateRoot, + }, + }; + assertBtdBtcCompensationStatementsSourceSafe(statements); + return statements; +} + +export function assertBtdBtcCompensationStatementsSourceSafe( + statements: BtdBtcCompensationStatements, +): asserts statements is BtdBtcCompensationStatements { + const serialized = JSON.stringify(statements); + for (const marker of FORBIDDEN_SOURCE_MARKERS) { + if (serialized.includes(marker)) { + throw new Error(`BtdBtcCompensationStatements serialized forbidden payload marker: ${marker}`); + } + } + if (!statements.disclosure.sourceSafeMetadataOnly) { + throw new Error('BtdBtcCompensationStatements must remain source-safe metadata only.'); + } + if (statements.disclosure.protectedSourcePayloadSerialized) { + throw new Error('BtdBtcCompensationStatements must not serialize protected source payloads.'); + } + if (statements.disclosure.walletPrivateMaterialVisible) { + throw new Error('BtdBtcCompensationStatements must not expose wallet private material.'); + } + if (statements.disclosure.settlementPrivatePayloadVisible) { + throw new Error('BtdBtcCompensationStatements must not expose private settlement payloads.'); + } + if (statements.disclosure.valueBearingMainnetAdmitted) { + throw new Error('BtdBtcCompensationStatements must not admit value-bearing mainnet operation.'); + } +} diff --git a/packages/pipelines/asset-pack/src/index.ts b/packages/pipelines/asset-pack/src/index.ts index f031cdcb..444b2117 100644 --- a/packages/pipelines/asset-pack/src/index.ts +++ b/packages/pipelines/asset-pack/src/index.ts @@ -442,6 +442,8 @@ export * from './reading-local-staging-rehearsal'; export * from './deposit-asset-pack-options'; export * from './deposit-asset-pack-option-policy'; export * from './deposit-asset-pack-option-admission'; +export * from './depositor-earning-supply-intelligence'; +export * from './btd-btc-compensation-statements'; export * from './embedding-config'; export * from './asset-pack-disclosure'; export * from './read-need-review-resynthesis'; diff --git a/packages/protocol/README.md b/packages/protocol/README.md index 154fa41e..db2d3414 100644 --- a/packages/protocol/README.md +++ b/packages/protocol/README.md @@ -231,6 +231,18 @@ estimate-only BTC compensation ranges, source-to-shares proof boundaries, earning statements, supply recommendations, route UI readback, and source-safety tests. +V44 Gate 6 adds `V44BtdBtcCompensationStatements` through +`packages/protocol/src/canonical/v44-btd-btc-compensation-statements.js`, +`packages/protocol/test/v44-btd-btc-compensation-statements.test.js`, +`.bitcode/v44-btd-btc-compensation-statements.json`, +`generate:v44-btd-btc-compensation-statements`, +`check:v44-btd-btc-compensation-statements`, and `check:v44-gate6`. It binds +settlement rights delivery boundaries to source-safe BTD range accounting, BTC +settlement observations, source-to-shares contributor compensation statements, +depositor earning summaries, treasury routes, ledger/database/object-storage +reconciliation, repair statements, `/packs` accounting readback, package +exports, workflow wiring, and source-safety tests. + Historical V39 promotion moved this package through the `V39` active, `V40` draft posture. V40 promotion has since advanced the current package posture to `V40` active, `V41` draft. diff --git a/packages/protocol/src/canonical/v44-btd-btc-compensation-statements.js b/packages/protocol/src/canonical/v44-btd-btc-compensation-statements.js new file mode 100644 index 00000000..403ee4b8 --- /dev/null +++ b/packages/protocol/src/canonical/v44-btd-btc-compensation-statements.js @@ -0,0 +1,311 @@ +// @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 V44_BTD_BTC_COMPENSATION_STATEMENTS_ARTIFACT_PATH = + '.bitcode/v44-btd-btc-compensation-statements.json'; +export const V44_BTD_BTC_COMPENSATION_STATEMENTS_SCHEMA_ID = + 'bitcode.v44.btdBtcCompensationStatements.v1'; +export const V44_BTD_BTC_COMPENSATION_STATEMENTS_VERSION = 'V44'; +export const V44_BTD_BTC_COMPENSATION_STATEMENTS_CURRENT_TARGET = 'V43'; +export const V44_BTD_BTC_COMPENSATION_STATEMENTS_SOURCE_SAFETY_VERDICT = + 'source-safe-btd-btc-compensation-statement-metadata'; + +export const V44_BTD_BTC_COMPENSATION_OBJECT_IDS = Object.freeze([ + 'BtdBtcCompensationStatements', + 'BtdRangeAccountingStatement', + 'BtcSettlementAccountingStatement', + 'ContributorCompensationStatement', + 'DepositorEarningSummary', + 'TreasuryRouteStatement', + 'BtdBtcReconciliationStatement', + 'BtdBtcRepairStatement', + 'PackEconomicStatement', +]); + +export const V44_BTD_BTC_ACCOUNTING_STATE_IDS = Object.freeze([ + 'settlement-accounted', + 'pending-btc-finality', + 'repair-required', + 'withheld-before-settlement', +]); + +export const V44_BTD_RANGE_ACCOUNTING_STATE_IDS = Object.freeze([ + 'transferred-to-reader', + 'allocated-pending-rights', + 'withheld-before-settlement', +]); + +export const V44_BTC_SETTLEMENT_ACCOUNTING_STATE_IDS = Object.freeze([ + 'final-settlement-observed', + 'observed-payment-pending-finality', + 'repair-required', +]); + +export const V44_CONTRIBUTOR_COMPENSATION_STATE_IDS = Object.freeze([ + 'allocated', + 'pending-settlement-finality', + 'repair-required', +]); + +export const V44_BTD_BTC_COMPENSATION_VALUE_LABEL_IDS = Object.freeze([ + 'observed-payment', + 'final-settlement', + 'contributor-allocation', + 'delivery', + 'repair-state', +]); + +export const V44_BTD_BTC_COMPENSATION_FORBIDDEN_PAYLOAD_IDS = Object.freeze([ + 'protected-source-payloads', + 'raw-source-text', + 'unpaid-assetpack-source', + 'raw-prompts', + 'interpolated-prompts', + 'raw-provider-responses', + 'credentials', + 'wallet-private-material', + 'private-settlement-payloads', + 'value-bearing-mainnet-admission', +]); + +const SOURCE_ROOTS = Object.freeze({ + activePointer: 'BITCODE_SPEC.txt', + spec: 'BITCODE_SPEC_V44.md', + delta: 'BITCODE_SPEC_V44_DELTA.md', + notes: 'BITCODE_SPEC_V44_NOTES.md', + parity: 'BITCODE_SPEC_V44_PARITY_MATRIX.md', + roadmap: 'SPECIFICATIONS_ROADMAP.md', + readme: 'README.md', + protocolReadme: 'packages/protocol/README.md', + packageJson: 'package.json', + gateWorkflow: '.github/workflows/bitcode-gate-quality.yml', + canonWorkflow: '.github/workflows/bitcode-canon-quality.yml', + economicModel: 'packages/protocol/src/canonical/v44-economic-domain-model.js', + settlementBoundary: 'packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts', + accountingStatements: 'packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts', + accountingStatementsTest: 'packages/pipelines/asset-pack/src/__tests__/btd-btc-compensation-statements.test.ts', + sourceToShares: 'packages/btd/src/source-to-shares.ts', + btdSettlement: 'packages/btd/src/settlement.ts', + btdReceipts: 'packages/btd/src/receipts.ts', + btdReconciliation: 'packages/btd/src/reconciliation.ts', + packActivityModel: 'uapi/components/base/bitcode/activity/pack-activity-model.ts', + packsClient: 'uapi/app/packs/PacksPageClient.tsx', + packActivityModelTest: 'uapi/tests/packActivityModel.test.ts', + packsClientTest: 'uapi/tests/packsPageClient.test.tsx', + assetPackPackageIndex: 'packages/pipelines/asset-pack/src/index.ts', + assetPackPackageManifest: 'packages/pipelines/asset-pack/package.json', + packageIndex: 'packages/protocol/src/index.js', + packageTypes: 'packages/protocol/src/index.d.ts', + packageSource: 'packages/protocol/src/canonical/v44-btd-btc-compensation-statements.js', + packageTest: 'packages/protocol/test/v44-btd-btc-compensation-statements.test.js', + generator: 'scripts/generate-v44-btd-btc-compensation-statements.mjs', + checker: 'scripts/check-v44-gate6-btd-btc-compensation-statements.mjs', +}); + +function digest(value) { + return crypto.createHash('sha256').update(value).digest('hex').slice(0, 24); +} + +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 sourceRoot(sourcePath) { + return `${sourcePath}:${digest(readSource(DEFAULT_REPO_ROOT, sourcePath))}`; +} + +export const V44_BTD_BTC_COMPENSATION_ROWS = Object.freeze([ + { + rowId: 'btd-range-accounting', + owner: SOURCE_ROOTS.accountingStatements, + contract: + 'BTD range accounting distinguishes transferred-to-reader, allocated-pending-rights, and withheld-before-settlement state.', + requiredFields: ['BtdRangeAccountingStatement', 'rangeState', 'rightsTransferRoot', 'readReceiptRoot'], + }, + { + rowId: 'btc-settlement-observation', + owner: SOURCE_ROOTS.accountingStatements, + contract: + 'BTC settlement accounting separates observed payment, final settlement, finality, txid, custody, and repair state.', + requiredFields: ['BtcSettlementAccountingStatement', 'expectedSats', 'observedDebitSats', 'serverCustody: false'], + }, + { + rowId: 'source-to-shares-contributor-statements', + owner: SOURCE_ROOTS.accountingStatements, + contract: + 'Contributor compensation statements derive allocated sats, share bps, range slices, and proof roots from source-to-shares allocations.', + requiredFields: ['ContributorCompensationStatement', 'contributor-allocation', 'allocatedSats', 'sourceToSharesRoot'], + }, + { + rowId: 'depositor-earning-summaries', + owner: SOURCE_ROOTS.accountingStatements, + contract: + 'Depositor earning summaries group contributor statements by depositor wallet without serializing wallet private material.', + requiredFields: ['DepositorEarningSummary', 'depositorWalletId', 'allocatedSats', 'summaryRoot'], + }, + { + rowId: 'treasury-routes', + owner: SOURCE_ROOTS.accountingStatements, + contract: + 'Treasury routes describe reader-to-contributor BTC flows, server custody false, and route state without private settlement payloads.', + requiredFields: ['TreasuryRouteStatement', 'reader-to-contributor-source-to-shares', 'serverCustody: false'], + }, + { + rowId: 'reconciliation-and-repair', + owner: SOURCE_ROOTS.accountingStatements, + contract: + 'Accounting statements preserve ledger/database/object-storage reconciliation and repair blockers as source-safe readback.', + requiredFields: ['BtdBtcReconciliationStatement', 'BtdBtcRepairStatement', 'ledgerDatabaseObjectStorageAligned'], + }, + { + rowId: 'packs-accounting-readback', + owner: SOURCE_ROOTS.packsClient, + contract: + '/packs renders BTD/BTC accounting state, BTD range, BTC settlement, treasury route, contributor counts, allocation, and accounting root.', + requiredFields: ['Accounting', 'BTD/BTC state', 'Accounting root', 'allocatedContributorSats'], + }, + { + rowId: 'btd-source-to-shares-primitives', + owner: SOURCE_ROOTS.sourceToShares, + contract: + 'Source-to-shares remains the deterministic largest-remainder basis for contributor allocation and settlement conservation.', + requiredFields: ['source-to-shares-largest-remainder', 'settlementAllocations', 'settlementConservation'], + }, +]); + +function buildPredicateResults(repoRoot) { + const sources = Object.fromEntries( + Object.entries(SOURCE_ROOTS).map(([key, sourcePath]) => [key, readSource(repoRoot, sourcePath)]), + ); + + return [ + predicateResult('active-canon-pointer-remains-v43', SOURCE_ROOTS.activePointer, sources.activePointer.trim() === 'V43'), + predicateResult('spec-defines-gate6', SOURCE_ROOTS.spec, sources.spec.includes('V44 Gate 6 BTD/BTC Accounting And Contributor Compensation Statements')), + predicateResult('spec-names-gate6-artifact', SOURCE_ROOTS.spec, sources.spec.includes('v44-btd-btc-compensation-statements')), + predicateResult('delta-records-gate6', SOURCE_ROOTS.delta, sources.delta.includes('Gate 6') && sources.delta.includes('v44-btd-btc-compensation-statements')), + predicateResult('notes-records-gate6', SOURCE_ROOTS.notes, sources.notes.includes('Gate 6') && sources.notes.includes('source-to-shares')), + predicateResult('parity-records-gate6', SOURCE_ROOTS.parity, sources.parity.includes('v44-btd-btc-compensation-statements')), + predicateResult('roadmap-records-gate6', SOURCE_ROOTS.roadmap, sources.roadmap.includes('V44 Gate 6 closure anchor')), + predicateResult('readme-records-gate6', SOURCE_ROOTS.readme, sources.readme.includes('V44 Gate 6')), + predicateResult('protocol-readme-records-gate6', SOURCE_ROOTS.protocolReadme, sources.protocolReadme.includes('V44 Gate 6')), + predicateResult('economic-model-prerequisite-present', SOURCE_ROOTS.economicModel, sources.economicModel.includes('ContributorCompensationStatement') && sources.economicModel.includes('PackEconomicStatement')), + predicateResult('settlement-boundary-prerequisite-present', SOURCE_ROOTS.settlementBoundary, sources.settlementBoundary.includes('AssetPackSettlementRightsDeliveryBoundary') && sources.settlementBoundary.includes('source_to_shares_compensation')), + predicateResult('accounting-builder-defined', SOURCE_ROOTS.accountingStatements, sources.accountingStatements.includes('buildBtdBtcCompensationStatements') && sources.accountingStatements.includes('BtdBtcCompensationStatements')), + predicateResult('accounting-state-ids-defined', SOURCE_ROOTS.accountingStatements, V44_BTD_BTC_ACCOUNTING_STATE_IDS.every((id) => sources.accountingStatements.includes(id))), + predicateResult('btd-range-state-ids-defined', SOURCE_ROOTS.accountingStatements, V44_BTD_RANGE_ACCOUNTING_STATE_IDS.every((id) => sources.accountingStatements.includes(id))), + predicateResult('btc-state-ids-defined', SOURCE_ROOTS.accountingStatements, V44_BTC_SETTLEMENT_ACCOUNTING_STATE_IDS.every((id) => sources.accountingStatements.includes(id))), + predicateResult('contributor-state-ids-defined', SOURCE_ROOTS.accountingStatements, V44_CONTRIBUTOR_COMPENSATION_STATE_IDS.every((id) => sources.accountingStatements.includes(id))), + predicateResult('accounting-forbids-source-leakage', SOURCE_ROOTS.accountingStatements, sources.accountingStatements.includes('protectedSourcePayloadSerialized: false') && sources.accountingStatements.includes('valueBearingMainnetAdmitted: false')), + predicateResult('asset-pack-package-exports-accounting', SOURCE_ROOTS.assetPackPackageIndex, sources.assetPackPackageIndex.includes("export * from './btd-btc-compensation-statements'")), + predicateResult('asset-pack-manifest-exports-accounting', SOURCE_ROOTS.assetPackPackageManifest, sources.assetPackPackageManifest.includes('"./btd-btc-compensation-statements"')), + predicateResult('accounting-test-covers-builder', SOURCE_ROOTS.accountingStatementsTest, sources.accountingStatementsTest.includes('buildBtdBtcCompensationStatements') && sources.accountingStatementsTest.includes('pending-btc-finality') && sources.accountingStatementsTest.includes('repair-required')), + predicateResult('pack-activity-model-projects-accounting', SOURCE_ROOTS.packActivityModel, sources.packActivityModel.includes('PackActivityAccountingReadback') && sources.packActivityModel.includes('BtdBtcCompensationStatements')), + predicateResult('packs-client-renders-accounting', SOURCE_ROOTS.packsClient, sources.packsClient.includes('Accounting') && sources.packsClient.includes('BTD/BTC state') && sources.packsClient.includes('Accounting root')), + predicateResult('pack-activity-test-covers-accounting', SOURCE_ROOTS.packActivityModelTest, sources.packActivityModelTest.includes('btd-btc-accounting-root-abc') && sources.packActivityModelTest.includes('settlement-accounted')), + predicateResult('packs-client-test-covers-accounting', SOURCE_ROOTS.packsClientTest, sources.packsClientTest.includes('btd-btc-accounting-root-abc') && sources.packsClientTest.includes('Accounting')), + predicateResult('source-to-shares-prerequisite-present', SOURCE_ROOTS.sourceToShares, sources.sourceToShares.includes('settlementConservation') && sources.sourceToShares.includes('largest_remainder')), + predicateResult('btd-settlement-prerequisite-present', SOURCE_ROOTS.btdSettlement, sources.btdSettlement.includes('AssetPackSettlementUnlock') && sources.btdSettlement.includes('settlementAdmissible')), + predicateResult('btd-receipts-prerequisite-present', SOURCE_ROOTS.btdReceipts, sources.btdReceipts.includes('buildBtdRightsTransferReceipt') && sources.btdReceipts.includes('buildBtdReadReceipt')), + predicateResult('btd-reconciliation-prerequisite-present', SOURCE_ROOTS.btdReconciliation, sources.btdReconciliation.includes('reconcileLedgerDatabaseProjection') && sources.btdReconciliation.includes('objectStorageArtifacts')), + predicateResult('package-test-covers-gate6', SOURCE_ROOTS.packageTest, sources.packageTest.includes('buildV44BtdBtcCompensationStatements')), + predicateResult('package-exports-gate6', SOURCE_ROOTS.packageIndex, sources.packageIndex.includes('buildV44BtdBtcCompensationStatements')), + predicateResult('package-types-export-gate6', SOURCE_ROOTS.packageTypes, sources.packageTypes.includes('buildV44BtdBtcCompensationStatements')), + predicateResult('package-json-exposes-gate6', SOURCE_ROOTS.packageJson, sources.packageJson.includes('"generate:v44-btd-btc-compensation-statements"') && sources.packageJson.includes('"check:v44-gate6"')), + predicateResult('gate-workflow-runs-gate6', SOURCE_ROOTS.gateWorkflow, sources.gateWorkflow.includes('check-v44-gate6-btd-btc-compensation-statements.mjs')), + predicateResult('canon-workflow-runs-gate6', SOURCE_ROOTS.canonWorkflow, sources.canonWorkflow.includes('check-v44-gate6-btd-btc-compensation-statements.mjs')), + predicateResult('generator-exists', SOURCE_ROOTS.generator, sources.generator.includes('buildV44BtdBtcCompensationStatements')), + predicateResult('checker-exists', SOURCE_ROOTS.checker, sources.checker.includes('V44 Gate 6 BTD/BTC compensation statements check')), + ]; +} + +export function buildV44BtdBtcCompensationStatements(options = {}) { + const repoRoot = options.repoRoot || DEFAULT_REPO_ROOT; + const predicateResults = buildPredicateResults(repoRoot); + const failedPredicateIds = predicateResults + .filter((predicate) => !predicate.passed) + .map((predicate) => predicate.id); + const sourceRoots = Object.fromEntries( + Object.entries(SOURCE_ROOTS).map(([key, sourcePath]) => [key, `${sourcePath}:${digest(readSource(repoRoot, sourcePath))}`]), + ); + const artifactRoot = `v44-btd-btc-compensation-statements:${digest(JSON.stringify({ + objectIds: V44_BTD_BTC_COMPENSATION_OBJECT_IDS, + accountingStateIds: V44_BTD_BTC_ACCOUNTING_STATE_IDS, + btdRangeStateIds: V44_BTD_RANGE_ACCOUNTING_STATE_IDS, + btcSettlementStateIds: V44_BTC_SETTLEMENT_ACCOUNTING_STATE_IDS, + contributorCompensationStateIds: V44_CONTRIBUTOR_COMPENSATION_STATE_IDS, + valueLabelIds: V44_BTD_BTC_COMPENSATION_VALUE_LABEL_IDS, + rowIds: V44_BTD_BTC_COMPENSATION_ROWS.map((row) => row.rowId), + sourceRoots, + failedPredicateIds, + }))}`; + + return { + artifactId: 'v44-btd-btc-compensation-statements', + schemaId: V44_BTD_BTC_COMPENSATION_STATEMENTS_SCHEMA_ID, + version: V44_BTD_BTC_COMPENSATION_STATEMENTS_VERSION, + currentTarget: V44_BTD_BTC_COMPENSATION_STATEMENTS_CURRENT_TARGET, + sourceSafetyVerdict: V44_BTD_BTC_COMPENSATION_STATEMENTS_SOURCE_SAFETY_VERDICT, + generatedAt: 'deterministic', + artifactRoot, + passed: failedPredicateIds.length === 0, + objectIds: [...V44_BTD_BTC_COMPENSATION_OBJECT_IDS], + accountingStateIds: [...V44_BTD_BTC_ACCOUNTING_STATE_IDS], + btdRangeStateIds: [...V44_BTD_RANGE_ACCOUNTING_STATE_IDS], + btcSettlementStateIds: [...V44_BTC_SETTLEMENT_ACCOUNTING_STATE_IDS], + contributorCompensationStateIds: [...V44_CONTRIBUTOR_COMPENSATION_STATE_IDS], + valueLabelIds: [...V44_BTD_BTC_COMPENSATION_VALUE_LABEL_IDS], + forbiddenPayloadIds: [...V44_BTD_BTC_COMPENSATION_FORBIDDEN_PAYLOAD_IDS], + rows: V44_BTD_BTC_COMPENSATION_ROWS.map((row) => ({ + ...row, + rowRoot: `v44-btd-btc-compensation-row:${digest(row.rowId)}`, + sourceSafeMetadataOnly: true, + protectedSourceVisible: false, + unpaidAssetPackSourceVisible: false, + })), + sourceRoots, + predicateResults, + coverage: { + btdBtcCompensationStatementsImplemented: true, + btdRangeStateImplemented: true, + btcSettlementObservationImplemented: true, + sourceToSharesContributorStatementsImplemented: true, + depositorEarningSummariesImplemented: true, + treasuryRoutesImplemented: true, + reconciliationStatementImplemented: true, + repairStatementImplemented: true, + packsAccountingReadbackImplemented: true, + sourceSafeMetadataOnly: true, + protectedSourceVisible: false, + rawSourceTextVisible: false, + sourceSnippetVisible: false, + rawPromptVisible: false, + interpolatedPromptVisible: false, + rawProviderResponseVisible: false, + unpaidAssetPackSourceVisible: false, + credentialsSerialized: false, + walletPrivateMaterialVisible: false, + settlementPrivatePayloadVisible: false, + valueBearingMainnetAdmitted: false, + requiredPredicateCount: predicateResults.length, + passedPredicateCount: predicateResults.length - failedPredicateIds.length, + failedPredicateIds, + }, + }; +} + +export const V44_BTD_BTC_COMPENSATION_STATEMENTS_SOURCE_ROOTS = Object.freeze( + Object.fromEntries(Object.entries(SOURCE_ROOTS).map(([key, sourcePath]) => [key, sourceRoot(sourcePath)])), +); diff --git a/packages/protocol/src/index.d.ts b/packages/protocol/src/index.d.ts index 710c11a8..f1c0768a 100644 --- a/packages/protocol/src/index.d.ts +++ b/packages/protocol/src/index.d.ts @@ -733,6 +733,21 @@ export const V44_DEPOSITOR_DEMAND_OPPORTUNITY_STATE_IDS: readonly string[]; export const V44_DEPOSITOR_EARNINGS_SUPPLY_FORBIDDEN_PAYLOAD_IDS: readonly string[]; export const V44_DEPOSITOR_EARNINGS_SUPPLY_ROWS: readonly Record[]; export function buildV44DepositorEarningsSupplyOpportunities(input?: Record): BitcodeProtocolReport; +export const V44_BTD_BTC_COMPENSATION_STATEMENTS_ARTIFACT_PATH: string; +export const V44_BTD_BTC_COMPENSATION_STATEMENTS_CURRENT_TARGET: string; +export const V44_BTD_BTC_COMPENSATION_STATEMENTS_SCHEMA_ID: string; +export const V44_BTD_BTC_COMPENSATION_STATEMENTS_VERSION: string; +export const V44_BTD_BTC_COMPENSATION_STATEMENTS_SOURCE_SAFETY_VERDICT: string; +export const V44_BTD_BTC_COMPENSATION_STATEMENTS_SOURCE_ROOTS: Readonly>; +export const V44_BTD_BTC_COMPENSATION_OBJECT_IDS: readonly string[]; +export const V44_BTD_BTC_ACCOUNTING_STATE_IDS: readonly string[]; +export const V44_BTD_RANGE_ACCOUNTING_STATE_IDS: readonly string[]; +export const V44_BTC_SETTLEMENT_ACCOUNTING_STATE_IDS: readonly string[]; +export const V44_CONTRIBUTOR_COMPENSATION_STATE_IDS: readonly string[]; +export const V44_BTD_BTC_COMPENSATION_VALUE_LABEL_IDS: readonly string[]; +export const V44_BTD_BTC_COMPENSATION_FORBIDDEN_PAYLOAD_IDS: readonly string[]; +export const V44_BTD_BTC_COMPENSATION_ROWS: readonly Record[]; +export function buildV44BtdBtcCompensationStatements(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 3a504193..8b5aa205 100644 --- a/packages/protocol/src/index.js +++ b/packages/protocol/src/index.js @@ -833,6 +833,23 @@ export { V44_DEPOSITOR_SUPPLY_RECOMMENDATION_IDS, buildV44DepositorEarningsSupplyOpportunities } from './canonical/v44-depositor-earnings-supply-opportunities.js'; +export { + V44_BTC_SETTLEMENT_ACCOUNTING_STATE_IDS, + V44_BTD_BTC_ACCOUNTING_STATE_IDS, + V44_BTD_BTC_COMPENSATION_FORBIDDEN_PAYLOAD_IDS, + V44_BTD_BTC_COMPENSATION_OBJECT_IDS, + V44_BTD_BTC_COMPENSATION_ROWS, + V44_BTD_BTC_COMPENSATION_STATEMENTS_ARTIFACT_PATH, + V44_BTD_BTC_COMPENSATION_STATEMENTS_CURRENT_TARGET, + V44_BTD_BTC_COMPENSATION_STATEMENTS_SCHEMA_ID, + V44_BTD_BTC_COMPENSATION_STATEMENTS_SOURCE_ROOTS, + V44_BTD_BTC_COMPENSATION_STATEMENTS_SOURCE_SAFETY_VERDICT, + V44_BTD_BTC_COMPENSATION_STATEMENTS_VERSION, + V44_BTD_BTC_COMPENSATION_VALUE_LABEL_IDS, + V44_BTD_RANGE_ACCOUNTING_STATE_IDS, + V44_CONTRIBUTOR_COMPENSATION_STATE_IDS, + buildV44BtdBtcCompensationStatements +} from './canonical/v44-btd-btc-compensation-statements.js'; export { EXCHANGE_INTENT_ACTION_KINDS, EXCHANGE_INTENT_ORDER_CONTRACTS_ARTIFACT_PATH, diff --git a/packages/protocol/test/v44-btd-btc-compensation-statements.test.js b/packages/protocol/test/v44-btd-btc-compensation-statements.test.js new file mode 100644 index 00000000..ec0c1105 --- /dev/null +++ b/packages/protocol/test/v44-btd-btc-compensation-statements.test.js @@ -0,0 +1,71 @@ +import assert from 'node:assert/strict'; +import { test } from 'node:test'; +import { + V44_BTC_SETTLEMENT_ACCOUNTING_STATE_IDS, + V44_BTD_BTC_ACCOUNTING_STATE_IDS, + V44_BTD_BTC_COMPENSATION_OBJECT_IDS, + V44_BTD_BTC_COMPENSATION_ROWS, + V44_BTD_BTC_COMPENSATION_STATEMENTS_ARTIFACT_PATH, + V44_BTD_BTC_COMPENSATION_STATEMENTS_SCHEMA_ID, + V44_BTD_BTC_COMPENSATION_STATEMENTS_SOURCE_SAFETY_VERDICT, + V44_BTD_BTC_COMPENSATION_VALUE_LABEL_IDS, + V44_BTD_RANGE_ACCOUNTING_STATE_IDS, + V44_CONTRIBUTOR_COMPENSATION_STATE_IDS, + buildV44BtdBtcCompensationStatements, +} from '../src/canonical/v44-btd-btc-compensation-statements.js'; + +test('V44 BTD/BTC compensation statements artifact is source-safe and complete', () => { + const artifact = buildV44BtdBtcCompensationStatements(); + + assert.equal(V44_BTD_BTC_COMPENSATION_STATEMENTS_ARTIFACT_PATH, '.bitcode/v44-btd-btc-compensation-statements.json'); + assert.equal(artifact.artifactId, 'v44-btd-btc-compensation-statements'); + assert.equal(artifact.schemaId, V44_BTD_BTC_COMPENSATION_STATEMENTS_SCHEMA_ID); + assert.equal(artifact.version, 'V44'); + assert.equal(artifact.currentTarget, 'V43'); + assert.equal(artifact.sourceSafetyVerdict, V44_BTD_BTC_COMPENSATION_STATEMENTS_SOURCE_SAFETY_VERDICT); + assert.equal(artifact.passed, true); + assert.match(artifact.artifactRoot, /^v44-btd-btc-compensation-statements:/); + assert.deepEqual(artifact.objectIds, [...V44_BTD_BTC_COMPENSATION_OBJECT_IDS]); + assert.deepEqual(artifact.accountingStateIds, [...V44_BTD_BTC_ACCOUNTING_STATE_IDS]); + assert.deepEqual(artifact.btdRangeStateIds, [...V44_BTD_RANGE_ACCOUNTING_STATE_IDS]); + assert.deepEqual(artifact.btcSettlementStateIds, [...V44_BTC_SETTLEMENT_ACCOUNTING_STATE_IDS]); + assert.deepEqual(artifact.contributorCompensationStateIds, [...V44_CONTRIBUTOR_COMPENSATION_STATE_IDS]); + assert.deepEqual(artifact.valueLabelIds, [...V44_BTD_BTC_COMPENSATION_VALUE_LABEL_IDS]); + assert.equal(artifact.coverage.btdBtcCompensationStatementsImplemented, true); + assert.equal(artifact.coverage.btdRangeStateImplemented, true); + assert.equal(artifact.coverage.btcSettlementObservationImplemented, true); + assert.equal(artifact.coverage.sourceToSharesContributorStatementsImplemented, true); + assert.equal(artifact.coverage.depositorEarningSummariesImplemented, true); + assert.equal(artifact.coverage.treasuryRoutesImplemented, true); + assert.equal(artifact.coverage.reconciliationStatementImplemented, true); + assert.equal(artifact.coverage.repairStatementImplemented, true); + assert.equal(artifact.coverage.packsAccountingReadbackImplemented, true); + assert.equal(artifact.coverage.sourceSafeMetadataOnly, true); + assert.equal(artifact.coverage.protectedSourceVisible, false); + assert.equal(artifact.coverage.rawSourceTextVisible, false); + assert.equal(artifact.coverage.unpaidAssetPackSourceVisible, false); + assert.equal(artifact.coverage.rawPromptVisible, false); + assert.equal(artifact.coverage.rawProviderResponseVisible, false); + assert.equal(artifact.coverage.walletPrivateMaterialVisible, false); + assert.equal(artifact.coverage.settlementPrivatePayloadVisible, false); + assert.equal(artifact.coverage.valueBearingMainnetAdmitted, false); +}); + +test('V44 BTD/BTC compensation rows bind accounting, UI, and primitive evidence', () => { + const artifact = buildV44BtdBtcCompensationStatements(); + const rowIds = artifact.rows.map((row) => row.rowId); + + assert.equal(artifact.rows.length, V44_BTD_BTC_COMPENSATION_ROWS.length); + assert.ok(rowIds.includes('btd-range-accounting')); + assert.ok(rowIds.includes('btc-settlement-observation')); + assert.ok(rowIds.includes('source-to-shares-contributor-statements')); + assert.ok(rowIds.includes('packs-accounting-readback')); + + for (const row of artifact.rows) { + assert.equal(row.sourceSafeMetadataOnly, true); + assert.equal(row.protectedSourceVisible, false); + assert.equal(row.unpaidAssetPackSourceVisible, false); + assert.ok(row.rowRoot.startsWith('v44-btd-btc-compensation-row:')); + assert.ok(row.requiredFields.length > 0); + } +}); diff --git a/scripts/check-v44-gate6-btd-btc-compensation-statements.mjs b/scripts/check-v44-gate6-btd-btc-compensation-statements.mjs new file mode 100644 index 00000000..a6bedea8 --- /dev/null +++ b/scripts/check-v44-gate6-btd-btc-compensation-statements.mjs @@ -0,0 +1,201 @@ +#!/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'; +import { + V44_BTD_BTC_COMPENSATION_STATEMENTS_ARTIFACT_PATH, + buildV44BtdBtcCompensationStatements, +} from '../packages/protocol/src/canonical/v44-btd-btc-compensation-statements.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const defaultRepoRoot = path.resolve(__dirname, '..'); + +function read(root, relativePath) { + return readFileSync(path.join(root, relativePath), 'utf8'); +} + +function exists(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) { + execFileSync(command, args, { cwd: root, stdio: 'pipe', encoding: 'utf8' }); +} + +function assertCheck(failures, condition, message) { + if (!condition) failures.push(message); +} + +function parseArgs(argv) { + const args = { + repoRoot: defaultRepoRoot, + skipBranchCheck: false, + skipUapiTests: false, + skipPackageTests: 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 === '--skip-branch-check') args.skipBranchCheck = true; + else if (arg === '--skip-uapi-tests') args.skipUapiTests = true; + else if (arg === '--skip-package-tests') args.skipPackageTests = true; + else if (arg === '--help' || arg === '-h') args.help = true; + else throw new Error(`Unknown argument ${arg}`); + } + return args; +} + +function printHelp() { + process.stdout.write( + [ + 'Usage: node scripts/check-v44-gate6-btd-btc-compensation-statements.mjs [--skip-branch-check] [--skip-uapi-tests] [--skip-package-tests] [--repo-root ]', + '', + 'V44 Gate 6 BTD/BTC compensation statements check: validates BTD range state, BTC settlement observations, source-to-shares contributor allocation, depositor earning summaries, treasury routes, reconciliation, repair state, Packs readback, tests, workflows, and generated artifact freshness.', + ].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 === 'V43', `BITCODE_SPEC.txt must remain V43 during V44 gate work. Observed ${pointer || 'empty'}.`); + + if (!args.skipBranchCheck) { + const branch = git(root, ['branch', '--show-current']); + assertCheck( + failures, + branch === 'version/v44' || /^v44\/gate-\d+-[a-z0-9][a-z0-9-]*$/u.test(branch), + `V44 work must occur on version/v44 or v44/gate-N-* branches. Observed ${branch || 'detached HEAD'}.`, + ); + } + + for (const relativePath of [ + V44_BTD_BTC_COMPENSATION_STATEMENTS_ARTIFACT_PATH, + 'packages/pipelines/asset-pack/src/btd-btc-compensation-statements.ts', + 'packages/pipelines/asset-pack/src/__tests__/btd-btc-compensation-statements.test.ts', + 'packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts', + 'packages/btd/src/source-to-shares.ts', + 'packages/btd/src/settlement.ts', + 'packages/btd/src/receipts.ts', + 'packages/btd/src/reconciliation.ts', + 'uapi/components/base/bitcode/activity/pack-activity-model.ts', + 'uapi/app/packs/PacksPageClient.tsx', + 'uapi/tests/packActivityModel.test.ts', + 'uapi/tests/packsPageClient.test.tsx', + 'packages/protocol/src/canonical/v44-btd-btc-compensation-statements.js', + 'packages/protocol/test/v44-btd-btc-compensation-statements.test.js', + 'scripts/generate-v44-btd-btc-compensation-statements.mjs', + 'scripts/check-v44-gate6-btd-btc-compensation-statements.mjs', + 'BITCODE_SPEC_V44.md', + 'BITCODE_SPEC_V44_DELTA.md', + 'BITCODE_SPEC_V44_NOTES.md', + 'BITCODE_SPEC_V44_PARITY_MATRIX.md', + 'SPECIFICATIONS_ROADMAP.md', + 'README.md', + 'packages/protocol/README.md', + '.github/workflows/bitcode-gate-quality.yml', + '.github/workflows/bitcode-canon-quality.yml', + 'package.json', + ]) { + assertCheck(failures, exists(root, relativePath), `Missing required V44 Gate 6 file: ${relativePath}`); + } + + const artifact = buildV44BtdBtcCompensationStatements({ repoRoot: root }); + assertCheck(failures, artifact.passed, `V44 BTD/BTC compensation statements predicates failed: ${artifact.coverage.failedPredicateIds.join(', ')}`); + assertCheck(failures, artifact.coverage.btdBtcCompensationStatementsImplemented === true, 'BTD/BTC compensation statements must be implemented.'); + assertCheck(failures, artifact.coverage.btdRangeStateImplemented === true, 'BTD range state must be implemented.'); + assertCheck(failures, artifact.coverage.btcSettlementObservationImplemented === true, 'BTC settlement observations must be implemented.'); + assertCheck(failures, artifact.coverage.sourceToSharesContributorStatementsImplemented === true, 'Source-to-shares contributor statements must be implemented.'); + assertCheck(failures, artifact.coverage.depositorEarningSummariesImplemented === true, 'Depositor earning summaries must be implemented.'); + assertCheck(failures, artifact.coverage.treasuryRoutesImplemented === true, 'Treasury routes must be implemented.'); + assertCheck(failures, artifact.coverage.reconciliationStatementImplemented === true, 'Reconciliation statements must be implemented.'); + assertCheck(failures, artifact.coverage.repairStatementImplemented === true, 'Repair statements must be implemented.'); + assertCheck(failures, artifact.coverage.packsAccountingReadbackImplemented === true, '/packs accounting readback must be implemented.'); + assertCheck(failures, artifact.coverage.sourceSafeMetadataOnly === true, 'Artifact must be source-safe metadata only.'); + assertCheck(failures, artifact.coverage.protectedSourceVisible === false, 'Artifact must not expose protected source.'); + assertCheck(failures, artifact.coverage.rawSourceTextVisible === false, 'Artifact must not expose raw source text.'); + assertCheck(failures, artifact.coverage.unpaidAssetPackSourceVisible === false, 'Artifact must not expose unpaid AssetPack source.'); + assertCheck(failures, artifact.coverage.rawPromptVisible === false, 'Artifact must not expose raw prompts.'); + assertCheck(failures, artifact.coverage.rawProviderResponseVisible === false, 'Artifact must not expose raw provider responses.'); + assertCheck(failures, artifact.coverage.walletPrivateMaterialVisible === false, 'Artifact must not expose wallet private material.'); + assertCheck(failures, artifact.coverage.settlementPrivatePayloadVisible === false, 'Artifact must not expose private settlement payloads.'); + assertCheck(failures, artifact.coverage.valueBearingMainnetAdmitted === false, 'Artifact must not admit value-bearing mainnet operation.'); + + const serialized = `${JSON.stringify(artifact, null, 2)}\n`; + assertCheck( + failures, + exists(root, V44_BTD_BTC_COMPENSATION_STATEMENTS_ARTIFACT_PATH) && + read(root, V44_BTD_BTC_COMPENSATION_STATEMENTS_ARTIFACT_PATH) === serialized, + `${V44_BTD_BTC_COMPENSATION_STATEMENTS_ARTIFACT_PATH} must be generated and current.`, + ); + + const packageJson = read(root, 'package.json'); + const gateWorkflow = read(root, '.github/workflows/bitcode-gate-quality.yml'); + const canonWorkflow = read(root, '.github/workflows/bitcode-canon-quality.yml'); + assertCheck(failures, packageJson.includes('"generate:v44-btd-btc-compensation-statements"'), 'package.json must expose generate:v44-btd-btc-compensation-statements.'); + assertCheck(failures, packageJson.includes('"check:v44-btd-btc-compensation-statements"'), 'package.json must expose check:v44-btd-btc-compensation-statements.'); + assertCheck(failures, packageJson.includes('"check:v44-gate6"'), 'package.json must expose check:v44-gate6.'); + assertCheck(failures, gateWorkflow.includes('check-v44-gate6-btd-btc-compensation-statements.mjs'), 'Gate workflow must run V44 Gate 6 checker.'); + assertCheck(failures, canonWorkflow.includes('check-v44-gate6-btd-btc-compensation-statements.mjs'), 'Canon workflow must run V44 Gate 6 checker.'); + + try { + run(root, 'node', ['scripts/generate-v44-btd-btc-compensation-statements.mjs', '--check']); + } catch { + failures.push('V44 BTD/BTC compensation statements artifact must be fresh.'); + } + + if (!args.skipPackageTests) { + try { + run(root, 'pnpm', ['--dir', 'packages/protocol', 'exec', 'node', '--test', '--test-force-exit', 'test/v44-btd-btc-compensation-statements.test.js']); + } catch { + failures.push('packages/protocol/test/v44-btd-btc-compensation-statements.test.js must pass.'); + } + + try { + run(root, 'pnpm', ['--filter', '@bitcode/pipeline-asset-pack', 'test', '--', 'btd-btc-compensation-statements.test.ts', '--runInBand']); + } catch { + failures.push('packages/pipelines/asset-pack BTD/BTC compensation statement tests must pass.'); + } + } + + if (!args.skipUapiTests) { + try { + run(root, 'pnpm', ['--dir', 'uapi', 'exec', 'jest', 'packActivityModel.test.ts', 'packsPageClient.test.tsx', '--runInBand']); + } catch { + failures.push('uapi Packs activity model/page tests must pass.'); + } + } + + if (failures.length > 0) { + process.stderr.write('V44 Gate 6 BTD/BTC compensation statements check failed:\n'); + for (const failure of failures.filter(Boolean)) process.stderr.write(`- ${failure}\n`); + process.exitCode = 1; + return; + } + + process.stdout.write('V44 Gate 6 BTD/BTC compensation statements check passed.\n'); +} + +try { + main(); +} catch (error) { + const detail = error instanceof Error ? error.message : String(error); + process.stderr.write(`${detail}\n`); + process.exitCode = 1; +} diff --git a/scripts/generate-v44-btd-btc-compensation-statements.mjs b/scripts/generate-v44-btd-btc-compensation-statements.mjs new file mode 100644 index 00000000..e934468c --- /dev/null +++ b/scripts/generate-v44-btd-btc-compensation-statements.mjs @@ -0,0 +1,27 @@ +#!/usr/bin/env node + +import { existsSync, readFileSync, writeFileSync } from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { + V44_BTD_BTC_COMPENSATION_STATEMENTS_ARTIFACT_PATH, + buildV44BtdBtcCompensationStatements, +} from '../packages/protocol/src/canonical/v44-btd-btc-compensation-statements.js'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const repoRoot = path.resolve(__dirname, '..'); +const artifactPath = path.join(repoRoot, V44_BTD_BTC_COMPENSATION_STATEMENTS_ARTIFACT_PATH); +const check = process.argv.includes('--check'); +const artifact = buildV44BtdBtcCompensationStatements({ repoRoot }); +const serialized = `${JSON.stringify(artifact, null, 2)}\n`; + +if (check) { + if (!existsSync(artifactPath) || readFileSync(artifactPath, 'utf8') !== serialized) { + process.stderr.write(`${V44_BTD_BTC_COMPENSATION_STATEMENTS_ARTIFACT_PATH} is stale. Run pnpm run generate:v44-btd-btc-compensation-statements.\n`); + process.exitCode = 1; + } +} else { + writeFileSync(artifactPath, serialized); + process.stdout.write(`wrote ${V44_BTD_BTC_COMPENSATION_STATEMENTS_ARTIFACT_PATH}\n`); +} diff --git a/uapi/app/packs/PacksPageClient.tsx b/uapi/app/packs/PacksPageClient.tsx index bec6f0b4..fa467ba9 100644 --- a/uapi/app/packs/PacksPageClient.tsx +++ b/uapi/app/packs/PacksPageClient.tsx @@ -240,6 +240,10 @@ export default function PacksPageClient() { label: "Settlement", value: formatCount(summary?.settlementReady || 0), }, + { + label: "Compensation", + value: formatCount(summary?.compensationReady || 0), + }, ]} >
@@ -687,6 +691,57 @@ export default function PacksPageClient() { + {detail.accounting && ( + +
+
+
BTD/BTC state
+
+ {detail.accounting.state || "not recorded"} +
+
+
+
BTD range
+
+ {detail.accounting.btdRangeState || "not recorded"} +
+
+
+
BTC settlement
+
+ {detail.accounting.btcSettlementState || "not recorded"} +
+
+
+
Treasury route
+
+ {detail.accounting.treasuryRouteState || "not recorded"} +
+
+
+
Contributors
+
+ {detail.accounting.contributorCount} +
+
+
+
Allocated
+
+ {formatSats(detail.accounting.allocatedContributorSats)} +
+
+ {detail.accounting.statementRoot && ( +
+
Accounting root
+
+ {detail.accounting.statementRoot} +
+
+ )} +
+
+ )} +
{detail.proofRoots.length ? ( diff --git a/uapi/components/base/bitcode/activity/pack-activity-model.ts b/uapi/components/base/bitcode/activity/pack-activity-model.ts index 022b6c12..25ed0c5e 100644 --- a/uapi/components/base/bitcode/activity/pack-activity-model.ts +++ b/uapi/components/base/bitcode/activity/pack-activity-model.ts @@ -57,6 +57,20 @@ export interface PackActivitySourceSafety { sourceSnippetVisible: false; } +export interface PackActivityAccountingReadback { + state: string | null; + btdRangeState: string | null; + btcSettlementState: string | null; + compensationState: string | null; + reconciliationState: string | null; + treasuryRouteState: string | null; + contributorCount: number; + depositorCount: number; + finalSettlementSats: number; + allocatedContributorSats: number; + statementRoot: string | null; +} + export interface PackActivityRecord { id: string; type: PackActivityType; @@ -74,6 +88,7 @@ export interface PackActivityRecord { measurements: PackActivityMeasurement[]; values: PackActivityValue[]; proofRoots: PackActivityProofRoot[]; + accounting: PackActivityAccountingReadback | null; sourceSafety: PackActivitySourceSafety; metadata: Record; } @@ -114,6 +129,7 @@ export interface PackActivityDetailProjection { measurements: PackActivityMeasurement[]; values: PackActivityValue[]; proofRoots: PackActivityProofRoot[]; + accounting: PackActivityAccountingReadback | null; states: { settlement: string | null; compensation: string | null; @@ -294,6 +310,29 @@ function findFirstNumber(source: unknown, keys: string[], depth = 0): number | n return null; } +function findFirstRecord( + source: unknown, + predicate: (record: Record) => boolean, + depth = 0, +): Record | null { + if (depth > 7 || source === null || source === undefined) return null; + if (Array.isArray(source)) { + for (const item of source) { + const found = findFirstRecord(item, predicate, depth + 1); + if (found) return found; + } + return null; + } + + const record = asRecord(source); + if (Object.keys(record).length > 0 && predicate(record)) return record; + for (const value of Object.values(record)) { + const found = findFirstRecord(value, predicate, depth + 1); + if (found) return found; + } + return null; +} + function normalizeLabel(value: string) { return value .replace(/([a-z])([A-Z])/gu, '$1 $2') @@ -440,6 +479,47 @@ function buildValues(record: BitcodeActivityRecord): PackActivityValue[] { return values; } +function buildAccountingReadback(record: BitcodeActivityRecord): PackActivityAccountingReadback | null { + const payload = asRecord(record.payload); + const statements = findFirstRecord( + payload, + (candidate) => + candidate.schema === 'bitcode.asset-pack.btd-btc-compensation-statements' || + candidate.statements === 'BtdBtcCompensationStatements', + ); + const aggregate = asRecord(statements?.aggregate); + const btdRange = asRecord(statements?.btdRange); + const btcSettlement = asRecord(statements?.btcSettlement); + const reconciliation = asRecord(statements?.reconciliation); + const firstTreasuryRoute = findFirstRecord( + statements?.treasuryRoutes, + (candidate) => candidate.schema === 'bitcode.asset-pack.treasury-route-statement', + ); + const roots = asRecord(statements?.roots); + const hasAccounting = + Boolean(statements) || + Boolean(readString(payload, 'accountingState', 'btdBtcAccountingState')) || + Boolean(findFirstString(payload, ['accountingRoot', 'btdBtcAccountingRoot'])); + + if (!hasAccounting) return null; + + return { + state: readString(statements, 'state') || readString(payload, 'accountingState', 'btdBtcAccountingState'), + btdRangeState: readString(btdRange, 'rangeState'), + btcSettlementState: readString(btcSettlement, 'state'), + compensationState: readString(payload, 'compensationState', 'compensation_state', 'sourceToSharesState'), + reconciliationState: readString(reconciliation, 'state') || readString(payload, 'reconciliationState'), + treasuryRouteState: readString(firstTreasuryRoute, 'routeState'), + contributorCount: findFirstNumber(aggregate, ['contributorCount']) || 0, + depositorCount: findFirstNumber(aggregate, ['depositorCount']) || 0, + finalSettlementSats: findFirstNumber(aggregate, ['finalSettlementSats']) || 0, + allocatedContributorSats: findFirstNumber(aggregate, ['allocatedContributorSats']) || 0, + statementRoot: + readString(roots, 'accountingRoot') || + findFirstString(payload, ['accountingRoot', 'btdBtcAccountingRoot', 'packEconomicStatementRoot']), + }; +} + function collectProofRoots(source: unknown, roots = new Map(), depth = 0) { if (depth > 7 || source === null || source === undefined) return roots; @@ -515,6 +595,7 @@ export function normalizePackActivityRecord(record: BitcodeActivityRecord): Pack measurements: buildMeasurements(record), values: buildValues(record), proofRoots: [...collectProofRoots(record.payload).values()].slice(0, 24), + accounting: buildAccountingReadback(record), sourceSafety: SOURCE_SAFETY, metadata, }; @@ -569,6 +650,13 @@ function buildSearchText(record: PackActivityRecord) { ]), ...record.values.flatMap((value) => [value.id, value.label, String(value.amount), value.unit]), ...record.proofRoots.flatMap((proofRoot) => [proofRoot.id, proofRoot.label, proofRoot.root]), + record.accounting?.state, + record.accounting?.btdRangeState, + record.accounting?.btcSettlementState, + record.accounting?.compensationState, + record.accounting?.reconciliationState, + record.accounting?.treasuryRouteState, + record.accounting?.statementRoot, ] .filter(Boolean) .join(' ') @@ -703,6 +791,7 @@ export function buildPackActivityDetailProjection( measurements: record.measurements, values: record.values, proofRoots: record.proofRoots, + accounting: record.accounting, states: { settlement: record.settlementState, compensation: record.compensationState, diff --git a/uapi/tests/packActivityModel.test.ts b/uapi/tests/packActivityModel.test.ts index 41f8a4f9..6fb9798a 100644 --- a/uapi/tests/packActivityModel.test.ts +++ b/uapi/tests/packActivityModel.test.ts @@ -32,6 +32,35 @@ describe('pack-activity-model', () => { compensationState: 'source_to_shares_preview_ready', deliveryState: 'locked_until_settlement', repairState: 'not_required', + btdBtcCompensationStatements: { + schema: 'bitcode.asset-pack.btd-btc-compensation-statements', + statements: 'BtdBtcCompensationStatements', + state: 'settlement-accounted', + btdRange: { + rangeState: 'transferred-to-reader', + }, + btcSettlement: { + state: 'final-settlement-observed', + }, + treasuryRoutes: [ + { + schema: 'bitcode.asset-pack.treasury-route-statement', + routeState: 'routed', + }, + ], + reconciliation: { + state: 'aligned', + }, + aggregate: { + contributorCount: 2, + depositorCount: 2, + finalSettlementSats: 3200, + allocatedContributorSats: 3200, + }, + roots: { + accountingRoot: 'btd-btc-accounting-root-abc', + }, + }, assetPackMeasurementRoot: 'measurement-root-abc', settlementReceiptRoot: 'settlement-root-def', protectedSource: 'protected source body', @@ -58,6 +87,15 @@ describe('pack-activity-model', () => { expect(record.measurements.some((measurement) => measurement.id === 'measured-btd')).toBe(true); expect(record.values.some((value) => value.id === 'btc-fee')).toBe(true); expect(record.proofRoots.map((proofRoot) => proofRoot.root)).toContain('settlement-root-def'); + expect(record.accounting).toMatchObject({ + state: 'settlement-accounted', + btdRangeState: 'transferred-to-reader', + btcSettlementState: 'final-settlement-observed', + treasuryRouteState: 'routed', + contributorCount: 2, + allocatedContributorSats: 3200, + statementRoot: 'btd-btc-accounting-root-abc', + }); expect(assertPackActivitySourceSafe(record)).toBe(true); expect(JSON.stringify(record)).not.toContain('protected source body'); expect(JSON.stringify(record)).not.toContain('raw prompt text'); @@ -94,6 +132,7 @@ describe('pack-activity-model', () => { const detail = buildPackActivityDetailProjection(result.records[0]); expect(detail.states.settlement).toBe('quote_ready'); + expect(detail.accounting?.statementRoot).toBe('btd-btc-accounting-root-abc'); expect(detail.proofRoots.length).toBeGreaterThan(0); expect(assertPackActivitySourceSafe(detail)).toBe(true); }); diff --git a/uapi/tests/packsPageClient.test.tsx b/uapi/tests/packsPageClient.test.tsx index 7d55511c..cede33be 100644 --- a/uapi/tests/packsPageClient.test.tsx +++ b/uapi/tests/packsPageClient.test.tsx @@ -54,6 +54,19 @@ describe("PacksPageClient", () => { values: [ { id: "btc-fee", label: "Btc fee", amount: 3200, unit: "sats" }, ], + accounting: { + state: "settlement-accounted", + btdRangeState: "transferred-to-reader", + btcSettlementState: "final-settlement-observed", + compensationState: "allocated", + reconciliationState: "aligned", + treasuryRouteState: "routed", + contributorCount: 2, + depositorCount: 2, + finalSettlementSats: 3200, + allocatedContributorSats: 3200, + statementRoot: "btd-btc-accounting-root-abc", + }, proofRoots: [ { id: "settlement-root", @@ -106,6 +119,19 @@ describe("PacksPageClient", () => { values: [ { id: "btc-fee", label: "Btc fee", amount: 3200, unit: "sats" }, ], + accounting: { + state: "settlement-accounted", + btdRangeState: "transferred-to-reader", + btcSettlementState: "final-settlement-observed", + compensationState: "allocated", + reconciliationState: "aligned", + treasuryRouteState: "routed", + contributorCount: 2, + depositorCount: 2, + finalSettlementSats: 3200, + allocatedContributorSats: 3200, + statementRoot: "btd-btc-accounting-root-abc", + }, proofRoots: [ { id: "settlement-root", @@ -157,6 +183,8 @@ describe("PacksPageClient", () => { ).toBeGreaterThan(0), ); expect(screen.getByText("Proof roots")).toBeInTheDocument(); + expect(screen.getByText("Accounting")).toBeInTheDocument(); + expect(screen.getByText("btd-btc-accounting-root-abc")).toBeInTheDocument(); expect(screen.getByText("settlement-root-def")).toBeInTheDocument(); expect(screen.getAllByText("quote_ready").length).toBeGreaterThan(0); expect(