Skip to content

Commit 708214e

Browse files
Merge pull request #36 from engineeredsoftware/v30/gate-4-btd-assetpack-mint-read-receipts
V30 Gate 4: BTD AssetPack Mint And Read Receipts
2 parents a2c3ce8 + 04a095b commit 708214e

16 files changed

Lines changed: 1426 additions & 22 deletions

.github/workflows/bitcode-gate-quality.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ jobs:
8282
node scripts/check-v30-gate1-roadmap-and-gating.mjs --skip-branch-check
8383
node scripts/check-v30-gate2-protocol-package-api-boundaries.mjs --skip-branch-check
8484
node scripts/check-v30-gate3-bitcoin-taproot-psbt-fee-rigor.mjs --skip-branch-check
85+
node scripts/check-v30-gate4-btd-assetpack-mint-read-receipts.mjs --skip-branch-check
8586
else
8687
echo "Unexpected BITCODE_SPEC.txt pointer: $POINTER" >&2
8788
exit 1

BITCODE_SPEC_V30.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,18 @@ V30 closes through ten gates:
209209
9. **Gate 9: Interface Integration And Regression Proof** proves current interfaces consume package-owned objects without V29 behavior regression.
210210
10. **Gate 10: V30 Promotion Readiness** validates local/staging proof, generated artifacts, V30 promotion workflow support, and post-promotion V30 active / V31 draft posture.
211211

212+
Gate 4 receipt precision:
213+
214+
- Mint receipts are source-safe by construction. They may bind a paid unlock
215+
root later, but their own `protectedSourceVisible` posture is always false.
216+
- Read receipts can represent preview, blocked, or paid-unlocked reading. Any
217+
paid delivery admission must include a paid unlock root, delivery admission
218+
root, read right, and ledger projection root.
219+
- Rights-transfer receipts require confirmed BTC fee finality before the BTD
220+
right/license transfer can admit protected source visibility to the Reader.
221+
Prepared, signed, broadcast, replaced, reorged, and failed fee receipts do
222+
not unlock protected source.
223+
212224
## V30 whole Bitcode operator chain
213225

214226
The V30 Terminal operator chain is:

BITCODE_SPEC_V30_DELTA.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,30 @@ Closure acceptance:
143143
- protected source remains hidden before settlement;
144144
- receipts are stored, streamed, and rendered through existing execution and Terminal surfaces.
145145

146+
Gate 4 implementation centers on `packages/btd/src/receipts.ts` and the
147+
package API boundary in `packages/btd/src/api-boundaries.ts`.
148+
`BtdAssetPackMintReceipt` extends mint evidence with depositor identity,
149+
source-safe preview root, Finding Fits result root, settlement conservation
150+
root, paid unlock root, delivery admission root, and ledger projection root.
151+
`BtdReadReceipt` records the Reader/Depositor boundary for source-safe preview
152+
or paid unlock and rejects protected source visibility before paid unlock.
153+
`BtdRightsTransferReceipt` requires confirmed BTC fee finality, a read license,
154+
paid unlock, delivery admission, range projection, and ledger projection before
155+
protected source can cross to the Reader.
156+
157+
The Sandbox harness now stores typed mint/read receipt payloads in settlement
158+
evidence and emits receipt roots in readback telemetry. Terminal detail
159+
snapshots coerce mint/read/rights-transfer receipts from settlement payloads,
160+
and the Terminal transaction read model counts those receipts in closure and
161+
journal surfaces so operators can inspect them through the existing rich
162+
execution detail UI.
163+
164+
Gate 4 evidence is covered by
165+
`packages/btd/__tests__/api-boundaries.test.ts`,
166+
`uapi/tests/terminalTransactionDetailSnapshot.test.ts`,
167+
`uapi/tests/terminalTransactionReadModel.test.ts`, and
168+
`scripts/check-v30-gate4-btd-assetpack-mint-read-receipts.mjs`.
169+
146170
### Gate 5: Testnet Ledger Projection Hardening
147171

148172
Gate 5 hardens ledger/database/object-storage projection and repair.

BITCODE_SPEC_V30_NOTES.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,35 @@ failure. Signed receipt advancement requires a signed PSBT. Broadcast,
108108
confirmed, replaced, and reorged observations require transaction id evidence.
109109
Server-custody signer posture remains rejected before PSBT handoff.
110110

111+
## Gate 4 BTD AssetPack mint and read receipt notes
112+
113+
Gate 4 moves BTD receipt posture from route/runtime convention into package
114+
owned primitives:
115+
116+
- `BtdAssetPackMintReceipt` binds the deposited AssetPack id, BTD range,
117+
depositor wallet, source manifest root, source-safe preview root, Finding
118+
Fits result root, proof root, settlement conservation root, access policy,
119+
and ledger projection root. It is always source-safe and never exposes
120+
protected source.
121+
- `BtdReadReceipt` binds the Reader wallet, Depositor wallet, accepted Need
122+
root, Finding Fits root, source-safe preview root, paid unlock state, read
123+
right state, delivery admission state, and ledger projection root. It rejects
124+
protected source visibility unless the read is paid-unlocked.
125+
- `BtdRightsTransferReceipt` binds the settled BTD range and read license to a
126+
confirmed BTC fee receipt, paid unlock root, delivery admission root, ledger
127+
anchor, ledger projection root, and Reader/Depositor identities. It is the
128+
receipt boundary where protected source can become visible to the paying
129+
Reader.
130+
131+
The package API boundary exposes builders for mint receipts, read-receipt
132+
settlements, and rights-transfer receipts so API routes and harness code do not
133+
reimplement receipt policy. The Sandbox harness emits mint/read receipt payloads
134+
and receipt roots inside ledger settlement evidence; rights-transfer receipts
135+
remain absent until BTC fee finality is confirmed. Terminal detail snapshots
136+
coerce receipt payloads under camelCase or database-style snake_case keys, and
137+
the transaction read model counts receipt payloads in closure and journal
138+
availability.
139+
111140
4. **Gate 4: BTD AssetPack Mint And Read Receipts**
112141
- Make BTD mint, read, and rights-transfer receipts typed, proof-rooted, stored, streamed, and source-safe.
113142

BITCODE_SPEC_V30_PARITY_MATRIX.md

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ No `_legacy/` source is active source truth.
5757
| Roadmap truth | Gate 1 | `SPECIFICATIONS_ROADMAP.md`, README, PR template, workflow posture | drafted | Roadmap states V29 active, V30 draft, and coherent V31-V37 responsibilities. |
5858
| Protocol package API boundaries | Gate 2 | `packages/btd/src/api-boundaries.ts`, `packages/api/src/routes/btd-crypto.ts`, package READMEs/tests | drafted | Shared Protocol/BTD objects have package-owned builders, parsers, validators, JSON-safe serializers, and tests. |
5959
| Bitcoin Taproot PSBT fee rigor | Gate 3 | `packages/btd/src/btc-fee-operation.ts`, `packages/btd/src/bitcoin-fees.ts`, BTD/API tests, gate checker | drafted | BTC fee and signer states are typed, testnet/mainnet-safe, no-custody, Taproot/PSBT aware, and proof-rooted. |
60-
| BTD AssetPack mint/read receipts | Gate 4 | BTD receipt primitives, asset-pack postprocess, harness evidence, Terminal/API readback | pending | Mint, read, and rights-transfer receipts bind BTD range, preview, paid unlock, delivery, and ledger projection. |
60+
| BTD AssetPack mint/read receipts | Gate 4 | `packages/btd/src/receipts.ts`, `packages/btd/src/api-boundaries.ts`, asset-pack harness evidence, Terminal detail snapshot/read model tests, gate checker | drafted | Mint, read, and rights-transfer receipts bind BTD range, preview, paid unlock, delivery, and ledger projection. |
6161
| Testnet ledger projection hardening | Gate 5 | BTD reconciliation, Supabase readback, object-storage evidence, repair tests | pending | Ledger/database/object-storage projections are synchronized or blocked with deterministic repair posture. |
6262
| Source-to-shares proof cleanup | Gate 6 | BTD/source-to-shares proof primitives, settlement conservation tests | pending | Measurement contribution, fee allocation, zero-cell/refit tail, and conservation invariants are testable. |
6363
| Bridge-readiness research boundaries | Gate 7 | Protocol/BTD research notes, policy posture tests, docs | pending | Bridge paths are documented as research until admitted by explicit future proof and policy. |
@@ -93,6 +93,9 @@ No `_legacy/` source is active source truth.
9393

9494
- Gate 1 does not implement Protocol/BTD package hardening.
9595
- Gate 1 does not create `BITCODE_SPEC_V30_PROVEN.md`.
96+
- Gate 1 does not promote `BITCODE_SPEC.txt` to V30.
97+
- Gate 1 may retarget workflows to active V29 / draft V30 so later gates are greenable.
98+
- Gate 1 may update roadmap scope for V31-V37 to align with V28/V29 promotion learning without opening those versions.
9699

97100
## Gate 2 Parity
98101

@@ -132,9 +135,28 @@ No `_legacy/` source is active source truth.
132135
- Gate 3 does not implement a bridge chain-of-record path.
133136
- Gate 3 does not custody Reader private keys or sign PSBTs server-side.
134137
- Gate 3 does not replace later Gate 4 BTD mint, read, and rights-transfer receipts.
135-
- Gate 1 does not promote `BITCODE_SPEC.txt` to V30.
136-
- Gate 1 may retarget workflows to active V29 / draft V30 so later gates are greenable.
137-
- Gate 1 may update roadmap scope for V31-V37 to align with V28/V29 promotion learning without opening those versions.
138+
139+
## Gate 4 Parity
140+
141+
| Requirement | Source evidence | Current V30 judgment |
142+
| --- | --- | --- |
143+
| BTD package owns typed AssetPack mint, read, and rights-transfer receipts | `packages/btd/src/receipts.ts` exports `BtdAssetPackMintReceipt`, `BtdReadReceipt`, `BtdRightsTransferReceipt`, and their builders/assertions | drafted |
144+
| Receipts bind AssetPack ids, BTD ranges, Reader/Depositor identities, source-safe preview roots, paid unlock, delivery admission, and ledger projection roots | Receipt interfaces/builders require those roots and identities, with BTD range conservation checks | drafted |
145+
| Protected source remains hidden before paid unlock | `assertReadDisclosureBoundary` rejects protected source visibility before `paid_unlocked`; `BtdAssetPackMintReceipt` is always `source_safe_preview` | drafted |
146+
| Rights transfer requires confirmed BTC finality before protected source unlock | `buildBtdRightsTransferReceipt` and API-boundary tests require `btcFeeFinalityState: 'confirmed'` | drafted |
147+
| API boundary exposes package-owned receipt construction | `buildBtdMintDraft`, `buildBtdReadReceiptBoundarySettlement`, and `buildBtdAssetPackExchangeSettlement` compose receipt builders | drafted |
148+
| Sandbox harness stores and streams receipt evidence | `packages/pipeline-hosts/src/asset-pack-harness.ts` stores mint/read receipt payloads in ledger settlement evidence and emits receipt roots in readback telemetry | drafted |
149+
| Terminal renders receipt readback through existing detail surfaces | `terminal-transaction-detail-snapshot.ts` coerces receipt payloads and `terminal-transaction-read-model.ts` counts them in closure/journal sections | drafted |
150+
| Tests cover package and Terminal receipt posture | `packages/btd/__tests__/api-boundaries.test.ts`, `uapi/tests/terminalTransactionDetailSnapshot.test.ts`, and `uapi/tests/terminalTransactionReadModel.test.ts` | drafted |
151+
| Gate checker protects the receipt surface | `scripts/check-v30-gate4-btd-assetpack-mint-read-receipts.mjs`, `pnpm run check:v30-gate4`, gate-quality workflow | drafted |
152+
153+
## Gate 4 accepted boundaries
154+
155+
- Gate 4 does not require production-mainnet BTC settlement.
156+
- Gate 4 does not broadcast Bitcoin transactions or custody wallet keys.
157+
- Gate 4 does not make protected source visible before paid unlock and delivery admission.
158+
- Gate 4 does not harden all ledger/database/object-storage projection repair; Gate 5 owns that.
159+
- Gate 4 does not finish source-to-shares contribution cleanup; Gate 6 owns that.
138160

139161
## completion condition
140162

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
"check:v30-gate1": "node scripts/check-v30-gate1-roadmap-and-gating.mjs",
5858
"check:v30-gate2": "node scripts/check-v30-gate2-protocol-package-api-boundaries.mjs",
5959
"check:v30-gate3": "node scripts/check-v30-gate3-bitcoin-taproot-psbt-fee-rigor.mjs",
60+
"check:v30-gate4": "node scripts/check-v30-gate4-btd-assetpack-mint-read-receipts.mjs",
6061
"check:spec-quality": "node scripts/run-bitcode-spec-quality.mjs --mode basic",
6162
"check:spec-quality:title": "node scripts/run-bitcode-spec-quality.mjs --mode strict-from-title",
6263
"check:spec-quality:v24": "node scripts/run-bitcode-spec-quality.mjs --mode strict-version --version V24",

packages/btd/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ This package owns:
1111
- contributor allocation, access evaluation, ancestry review, and revenue routing
1212
- wallet-signed BTC fee receipts, ledger anchors, Exchange rights transfers,
1313
Terminal journals, reconciliation, telemetry, and upgrade receipts
14+
- typed AssetPack mint/read/rights-transfer receipts that bind BTD ranges,
15+
Reader and Depositor identities, source-safe preview roots, paid unlock,
16+
delivery admission, and ledger projection roots without leaking protected
17+
source before settlement
1418
- BTC fee operation posture, including quote lifecycle, signer recovery,
1519
no-server-custody PSBT handoff, Taproot/script posture, broadcast/finality
1620
observation, replacement/reorg repair, and testnet/mainnet network policy
@@ -37,6 +41,8 @@ import {
3741
BTD_MAX_MINTABLE_SUPPLY,
3842
applyBtdMeasureMint,
3943
buildBtdMintDraft,
44+
buildBtdReadReceiptBoundarySettlement,
45+
buildBtdRightsTransferReceipt,
4046
buildBtdRegistrySnapshot,
4147
toBtdJsonSafe,
4248
calculateLlmBtcFeeEstimate,

packages/btd/__tests__/api-boundaries.test.ts

Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import {
2+
buildBtdAssetPackExchangeSettlement,
23
buildBtdMintDraft,
34
buildBtdReadAccessDecision,
5+
buildBtdReadReceiptBoundarySettlement,
46
buildBtdRegistrySnapshot,
57
createBtdMeasureMintState,
68
parseBtdOptionalBigInt,
@@ -38,10 +40,26 @@ describe('BTD API boundaries', () => {
3840
exchangeSequence: 1n,
3941
actorId: 'actor-boundary-1',
4042
issuedAt,
43+
depositorWalletId: 'wallet-depositor-boundary',
44+
sourceSafePreviewRoot: 'source-safe-preview-root-boundary',
45+
findingFitsResultRoot: 'finding-fits-root-boundary',
46+
settlementConservationRoot: 'settlement-conservation-root-boundary',
47+
ledgerProjectionRoot: 'ledger-projection-root-boundary',
4148
});
4249

4350
expect(draft.kind).toBe('btd_mint_draft');
4451
expect(draft.measureMint.tokenCount).toBe(10_500_000);
52+
expect(draft.assetPackMintReceipt).toMatchObject({
53+
kind: 'btd.asset_pack_mint_receipt',
54+
depositorWalletId: 'wallet-depositor-boundary',
55+
sourceSafePreviewRoot: 'source-safe-preview-root-boundary',
56+
findingFitsResultRoot: 'finding-fits-root-boundary',
57+
ledgerProjectionRoot: 'ledger-projection-root-boundary',
58+
protectedSourceVisible: false,
59+
});
60+
expect(draft.terminalJournalEntry.receiptRoots).toContain(
61+
draft.assetPackMintReceipt?.receiptRoot,
62+
);
4563
expect(draft.terminalJournalEntry.transactionKind).toBe('asset_pack_mint');
4664
expect(draft.terminalJournalEntry.actorId).toBe('actor-boundary-1');
4765
});
@@ -119,4 +137,124 @@ describe('BTD API boundaries', () => {
119137
expect(decision.decision.decision).toBe('licensed_read');
120138
expect(decision.policyDisclosure.accessPolicyHash).toBe('policy-boundary-hash');
121139
});
140+
141+
it('binds source-safe read receipts and paid rights-transfer receipts at package boundaries', () => {
142+
const preview = buildBtdReadReceiptBoundarySettlement({
143+
actorId: 'actor-boundary-1',
144+
assetPackId: 'asset-pack-boundary-1',
145+
readId: 'read-boundary-1',
146+
readRequestId: 'read-request-boundary-1',
147+
acceptedNeedRoot: 'accepted-need-root-boundary',
148+
findingFitsResultRoot: 'finding-fits-root-boundary',
149+
readerWalletId: 'wallet-reader-boundary',
150+
depositorWalletId: 'wallet-depositor-boundary',
151+
rangeStart: 0,
152+
rangeEndExclusive: 10,
153+
sourceManifestRoot: 'source-root-boundary',
154+
sourceSafePreviewRoot: 'source-safe-preview-root-boundary',
155+
accessPolicyHash: 'policy-boundary-hash',
156+
disclosureState: 'source_safe_preview',
157+
readRightState: 'none',
158+
deliveryAdmissionState: 'blocked',
159+
ledgerProjectionRoot: 'ledger-projection-root-boundary',
160+
exchangeSequence: 4n,
161+
issuedAt,
162+
});
163+
164+
expect(preview.readReceipt).toMatchObject({
165+
kind: 'btd.read_receipt',
166+
disclosureState: 'source_safe_preview',
167+
deliveryAdmissionState: 'blocked',
168+
protectedSourceVisible: false,
169+
paidUnlockRoot: null,
170+
});
171+
expect(preview.terminalJournalEntry.transactionKind).toBe('read_submission');
172+
expect(preview.terminalJournalEntry.receiptRoots).toContain(preview.readReceipt.receiptRoot);
173+
174+
expect(() =>
175+
buildBtdReadReceiptBoundarySettlement({
176+
...preview.readReceipt,
177+
actorId: 'actor-boundary-1',
178+
exchangeSequence: 5n,
179+
protectedSourceVisible: true,
180+
}),
181+
).toThrow('Protected source cannot be visible before paid unlock.');
182+
183+
const created = buildBtdAssetPackExchangeSettlement({
184+
action: 'create_order',
185+
orderId: 'order-boundary-1',
186+
orderKind: 'sell',
187+
assetPackId: 'asset-pack-boundary-1',
188+
rangeStart: 0,
189+
rangeEndExclusive: 10,
190+
makerWalletId: 'wallet-depositor-boundary',
191+
priceSats: '1200',
192+
accessPolicyHash: 'policy-boundary-hash',
193+
createdAtExchangeSequence: 6n,
194+
actorId: 'actor-boundary-1',
195+
issuedAt,
196+
});
197+
const accepted = buildBtdAssetPackExchangeSettlement({
198+
action: 'accept_order',
199+
previousOrder: created.order,
200+
takerWalletId: 'wallet-reader-boundary',
201+
actorId: 'actor-boundary-1',
202+
issuedAt,
203+
});
204+
const settled = buildBtdAssetPackExchangeSettlement({
205+
action: 'settle_order',
206+
previousOrder: accepted.order,
207+
settledAtExchangeSequence: 7n,
208+
ledgerAnchorId: 'ledger-anchor-boundary-1',
209+
actorId: 'actor-boundary-1',
210+
issuedAt,
211+
});
212+
const transferred = buildBtdAssetPackExchangeSettlement({
213+
action: 'transfer_rights',
214+
previousOrder: settled.order,
215+
receiptId: 'legacy-transfer-boundary-1',
216+
fromWalletId: 'wallet-depositor-boundary',
217+
toWalletId: 'wallet-reader-boundary',
218+
btcFeeReceiptId: 'btc-fee-boundary-1',
219+
btcFeeFinalityState: 'confirmed',
220+
readLicenseId: 'read-license-boundary-1',
221+
sourceSafePreviewRoot: 'source-safe-preview-root-boundary',
222+
paidUnlockRoot: 'paid-unlock-root-boundary',
223+
deliveryAdmissionRoot: 'delivery-admission-root-boundary',
224+
ledgerProjectionRoot: 'ledger-projection-root-boundary',
225+
actorId: 'actor-boundary-1',
226+
issuedAt,
227+
});
228+
229+
expect(transferred.btdRightsTransferReceipt).toMatchObject({
230+
kind: 'btd.rights_transfer_receipt',
231+
readerWalletId: 'wallet-reader-boundary',
232+
depositorWalletId: 'wallet-depositor-boundary',
233+
btcFeeFinalityState: 'confirmed',
234+
deliveryAdmissionState: 'admitted',
235+
protectedSourceVisible: true,
236+
});
237+
expect(transferred.terminalJournalEntry.receiptRoots).toContain(
238+
transferred.btdRightsTransferReceipt?.receiptRoot,
239+
);
240+
241+
expect(() =>
242+
buildBtdAssetPackExchangeSettlement({
243+
action: 'transfer_rights',
244+
previousOrder: settled.order,
245+
receiptId: 'legacy-transfer-boundary-2',
246+
fromWalletId: 'wallet-depositor-boundary',
247+
toWalletId: 'wallet-reader-boundary',
248+
btcFeeReceiptId: 'btc-fee-boundary-2',
249+
btcFeeFinalityState: 'broadcast',
250+
readLicenseId: 'read-license-boundary-2',
251+
sourceSafePreviewRoot: 'source-safe-preview-root-boundary',
252+
paidUnlockRoot: 'paid-unlock-root-boundary',
253+
deliveryAdmissionRoot: 'delivery-admission-root-boundary',
254+
ledgerProjectionRoot: 'ledger-projection-root-boundary',
255+
actorId: 'actor-boundary-1',
256+
issuedAt,
257+
}),
258+
).toThrow('Rights transfer receipt requires confirmed BTC fee finality.');
259+
});
122260
});

0 commit comments

Comments
 (0)