Skip to content

Commit f1e8df9

Browse files
Merge pull request #217 from engineeredsoftware/v45/gate-14-btc-settlement-rights-delivery-readback
V45 Gate 14: BTC Settlement Rights Delivery Readback
2 parents 6ea0619 + 34f3591 commit f1e8df9

11 files changed

Lines changed: 495 additions & 32 deletions

.bitcode/v39-settlement-rights-delivery.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,7 @@
464464
"sourceSafetyVerdict": "source-safe-settlement-rights-delivery",
465465
"runtimeType": "AssetPackSettlementRightsDeliveryBoundary",
466466
"paymentType": "AssetPackSettlementPaymentObservation",
467+
"btcSettlementReadbackType": "AssetPackBtcSettlementReadback",
467468
"finalityType": "AssetPackSettlementFinalityReceipt",
468469
"rightsTransferType": "BtdRightsTransferReceipt",
469470
"readReceiptType": "BtdReadReceipt",
@@ -473,6 +474,7 @@
473474
"replayType": "AssetPackSettlementRightsDeliveryReplayReceipt",
474475
"storageRecordKinds": [
475476
"btc_payment_observation",
477+
"btc_settlement_readback",
476478
"settlement_finality",
477479
"source_to_shares_compensation",
478480
"btd_read_receipt",
@@ -506,5 +508,5 @@
506508
"failedPredicateIds": []
507509
},
508510
"passed": true,
509-
"artifactRoot": "v39-settlement-rights-delivery:ef0207da866bfb8375772f46"
511+
"artifactRoot": "v39-settlement-rights-delivery:ac368ea9dcc45bda1c5ef73d"
510512
}

.bitcode/v40-ledger-storage-sync.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
"rowCount": 10,
1212
"synchronizationSurfaceCount": 6,
1313
"failClosedStateCount": 4,
14-
"storageRecordKindCount": 9,
14+
"storageRecordKindCount": 10,
1515
"requiredReadbackCount": 5,
1616
"walletAuthorityBoundaryCount": 4,
1717
"deliveryUnlockConditionCount": 5
@@ -595,7 +595,7 @@
595595
"rowCount": 10,
596596
"synchronizationSurfaceCount": 6,
597597
"failClosedStateCount": 4,
598-
"storageRecordKindCount": 9,
598+
"storageRecordKindCount": 10,
599599
"requiredReadbackCount": 5,
600600
"walletAuthorityBoundaryCount": 4,
601601
"deliveryUnlockConditionCount": 5
@@ -619,8 +619,8 @@
619619
"proofRoots": {
620620
"rowSetRoot": "sha256:bb6d02003baa62525f760c53",
621621
"predicateRoot": "sha256:f4ca6728fac60849d7140424",
622-
"coverageRoot": "sha256:3b6c32dec61478478438ef74",
623-
"artifactRoot": "sha256:84e6aae8d9795dcb9dd757a1"
622+
"coverageRoot": "sha256:a15ad35380e94dcf91814adc",
623+
"artifactRoot": "sha256:a68d8a8ea5ff7c19827537d5"
624624
},
625625
"sourceSafety": {
626626
"sourceSafeMetadataOnly": true,

.bitcode/v42-settlement-rights-delivery.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,7 @@
551551
"sourceSafetyVerdict": "source-safe-v42-settlement-rights-delivery-metadata",
552552
"runtimeType": "AssetPackSettlementRightsDeliveryBoundary",
553553
"paymentType": "AssetPackSettlementPaymentObservation",
554+
"btcSettlementReadbackType": "AssetPackBtcSettlementReadback",
554555
"finalityType": "AssetPackSettlementFinalityReceipt",
555556
"rightsTransferType": "BtdRightsTransferReceipt",
556557
"readReceiptType": "BtdReadReceipt",
@@ -583,5 +584,5 @@
583584
"failedPredicateIds": []
584585
},
585586
"passed": true,
586-
"artifactRoot": "v42-settlement-rights-delivery:6866f71624c147178c383189"
587+
"artifactRoot": "v42-settlement-rights-delivery:03357cba132e98e24c3c6b23"
587588
}

BITCODE_SPEC_V45_PARITY_MATRIX.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,12 @@ Acceptance:
167167
- Repair tests cover underpayment, stale quote, missing finality, projection drift, missing delivery, and compensation conservation failure.
168168
- `check:v45-gate14` passes locally and in gate-quality CI.
169169

170+
Gate 14 implementation readback:
171+
172+
- BTC settlement, rights, delivery, repair, and source-to-shares readback binding: `packages/pipelines/asset-pack/src/asset-pack-settlement-rights-delivery.ts`.
173+
- Settlement success, non-finality, stale quote, underpayment, compensation failure, projection drift, missing delivery, persistence, and source-safety coverage: `packages/pipelines/asset-pack/src/__tests__/asset-pack-settlement-rights-delivery.test.ts`.
174+
- Closure checker: `check:v45-gate14`.
175+
170176
### Gate 15: Interface Disclosure, Route Vocabulary, And Public Documentation
171177

172178
Gate class: `interface-only`.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,7 @@
399399
"check:v45-gate11": "node scripts/check-v45-gate11-implementation-parity-audit.mjs",
400400
"check:v45-gate12": "node scripts/check-v45-gate12-state-vocabulary-commodity-model.mjs",
401401
"check:v45-gate13": "node scripts/check-v45-gate13-btd-scalar-volume-quote-conservation.mjs",
402+
"check:v45-gate14": "node scripts/check-v45-gate14-btc-settlement-rights-delivery-readback.mjs",
402403
"generate:v38-inference-surface-inventory": "node scripts/generate-v38-inference-surface-inventory.mjs",
403404
"check:v38-inference-surface-inventory": "node scripts/generate-v38-inference-surface-inventory.mjs --check",
404405
"check:v38-gate2": "node scripts/check-v38-gate2-inference-surface-inventory.mjs",

packages/pipelines/asset-pack/src/__tests__/asset-pack-settlement-rights-delivery.test.ts

Lines changed: 106 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,19 @@ describe('AssetPack settlement rights delivery boundary', () => {
180180
finalityReceipt: {
181181
finalityState: 'confirmed',
182182
},
183+
btcSettlementReadback: {
184+
quoteAcceptanceState: 'accepted',
185+
walletReadinessState: 'wallet_ready_non_custodial',
186+
psbtPreparationState: 'psbt_prepared_source_safe',
187+
psbtSignatureState: 'psbt_signed_by_reader_wallet',
188+
settlementFinalizationState: 'settlement_finalized',
189+
rightsTransferState: 'rights_transferred',
190+
deliveryState: 'source_unlocked_delivery',
191+
compensationRoutingState: 'compensation_routable',
192+
sourceUnlockAdmissible: true,
193+
serverCustody: false,
194+
walletPrivateMaterialVisible: false,
195+
},
183196
deliveryUnlock: {
184197
state: 'source_bearing_pull_request_ready',
185198
sourceBearingDeliveryVisibleToReader: true,
@@ -204,6 +217,7 @@ describe('AssetPack settlement rights delivery boundary', () => {
204217
expect(boundary.storageProjection.map((record) => record.recordKind)).toEqual(
205218
expect.arrayContaining([
206219
'btc_payment_observation',
220+
'btc_settlement_readback',
207221
'settlement_finality',
208222
'source_to_shares_compensation',
209223
'btd_read_receipt',
@@ -240,25 +254,83 @@ describe('AssetPack settlement rights delivery boundary', () => {
240254
expect(boundary.replayReceipt.verified.paymentMatchesQuote).toBe(false);
241255
});
242256

243-
it('fails closed until BTC finality is confirmed', () => {
257+
it.each(['prepared', 'signed', 'broadcast', 'observed'] as const)(
258+
'fails closed for %s BTC state before finality',
259+
(finalityState) => {
260+
const boundary = buildAssetPackSettlementRightsDeliveryBoundary({
261+
previewBoundary: previewBoundary(),
262+
finality: {
263+
finalityState,
264+
confirmations: 0,
265+
blockHeight: null,
266+
},
267+
createdAt: '2026-05-22T00:00:00.000Z',
268+
});
269+
270+
expect(boundary.state).toBe('blocked_until_payment_finality');
271+
expect(boundary.rightsTransferReceipt).toBeNull();
272+
expect(boundary.btdReadReceipt).toBeNull();
273+
expect(boundary.deliveryUnlock.sourceBearingDeliveryVisibleToReader).toBe(false);
274+
expect(boundary.btcSettlementReadback).toMatchObject({
275+
finalityState,
276+
settlementFinalizationState: 'not_finalized',
277+
rightsTransferState: 'rights_withheld',
278+
deliveryState: 'delivery_withheld',
279+
sourceUnlockAdmissible: false,
280+
rightsTransferAdmissible: false,
281+
});
282+
expect(boundary.repairPosture.nextActions).toEqual(
283+
expect.arrayContaining(['observe_btc_payment', 'wait_for_btc_finality']),
284+
);
285+
},
286+
);
287+
288+
it('fails closed when payment references a stale accepted BTC quote', () => {
289+
const preview = previewBoundary();
244290
const boundary = buildAssetPackSettlementRightsDeliveryBoundary({
245-
previewBoundary: previewBoundary(),
246-
finality: {
247-
finalityState: 'broadcast',
248-
confirmations: 0,
249-
blockHeight: null,
291+
previewBoundary: preview,
292+
paymentObservation: {
293+
expectedSats: preview.quoteReceipt.sats - 10,
294+
observedDebitSats: preview.quoteReceipt.sats - 10,
295+
observedCreditSats: preview.quoteReceipt.sats - 10,
250296
},
251297
createdAt: '2026-05-22T00:00:00.000Z',
252298
});
253299

254-
expect(boundary.state).toBe('blocked_until_payment_finality');
300+
expect(boundary.state).toBe('blocked_until_compensation_conservation');
255301
expect(boundary.rightsTransferReceipt).toBeNull();
256302
expect(boundary.deliveryUnlock.sourceBearingDeliveryVisibleToReader).toBe(false);
303+
expect(boundary.btcSettlementReadback.quoteAcceptanceState).toBe('stale_quote_repair_required');
304+
expect(boundary.btcSettlementReadback.blockerCodes).toContain('stale_btc_quote_or_payment_mismatch');
305+
expect(boundary.repairPosture.blockers).toContain('stale_btc_quote_or_payment_mismatch');
257306
expect(boundary.repairPosture.nextActions).toEqual(
258-
expect.arrayContaining(['observe_btc_payment', 'wait_for_btc_finality']),
307+
expect.arrayContaining(['refresh_stale_btc_quote', 'observe_btc_payment']),
259308
);
260309
});
261310

311+
it('fails closed when contributor compensation conservation fails after debit observation', () => {
312+
const preview = previewBoundary();
313+
const boundary = buildAssetPackSettlementRightsDeliveryBoundary({
314+
previewBoundary: preview,
315+
paymentObservation: {
316+
observedDebitSats: preview.quoteReceipt.sats,
317+
observedCreditSats: preview.quoteReceipt.sats - 1,
318+
},
319+
createdAt: '2026-05-22T00:00:00.000Z',
320+
});
321+
322+
expect(boundary.state).toBe('blocked_until_compensation_conservation');
323+
expect(boundary.sourceToSharesProof).toMatchObject({
324+
settlementConservation: {
325+
state: 'underpayment',
326+
settlementAdmissible: false,
327+
},
328+
});
329+
expect(boundary.rightsTransferReceipt).toBeNull();
330+
expect(boundary.btcSettlementReadback.compensationRoutingState).toBe('compensation_withheld');
331+
expect(boundary.repairPosture.blockers).toContain('source_to_shares_conservation_failed');
332+
});
333+
262334
it('withholds delivery when ledger, database, or object storage projections drift', () => {
263335
const boundary = buildAssetPackSettlementRightsDeliveryBoundary({
264336
previewBoundary: previewBoundary(),
@@ -274,6 +346,28 @@ describe('AssetPack settlement rights delivery boundary', () => {
274346
expect(boundary.repairPosture.nextActions).toContain('repair_ledger_database_storage_projection');
275347
});
276348

349+
it('withholds source unlock and rights delivery when repository delivery is missing', () => {
350+
const boundary = buildAssetPackSettlementRightsDeliveryBoundary({
351+
previewBoundary: previewBoundary(),
352+
pullRequestTarget: null,
353+
createdAt: '2026-05-22T00:00:00.000Z',
354+
});
355+
356+
expect(boundary.state).toBe('blocked_until_pull_request_delivery');
357+
expect(boundary.rightsTransferReceipt?.kind).toBe('btd.rights_transfer_receipt');
358+
expect(boundary.deliveryUnlock).toMatchObject({
359+
state: 'withheld',
360+
sourceBearingDeliveryVisibleToReader: false,
361+
pullRequestTarget: null,
362+
blockerCodes: expect.arrayContaining(['pull_request_target_missing']),
363+
});
364+
expect(boundary.btcSettlementReadback).toMatchObject({
365+
deliveryState: 'delivery_withheld',
366+
sourceUnlockAdmissible: false,
367+
});
368+
expect(boundary.repairPosture.blockers).toContain('pull_request_delivery_missing');
369+
});
370+
277371
it('persists settlement and rights delivery records onto execution state', () => {
278372
const execution = new Execution('asset-pack-settlement-test');
279373
const boundary = buildAssetPackSettlementRightsDeliveryBoundary({
@@ -293,5 +387,9 @@ describe('AssetPack settlement rights delivery boundary', () => {
293387
expect(execution.get('asset-pack/settlement', 'deliveryUnlock')).toMatchObject({
294388
state: 'source_bearing_pull_request_ready',
295389
});
390+
expect(execution.get('asset-pack/settlement', 'btcSettlementReadback')).toMatchObject({
391+
schema: 'bitcode.asset-pack.btc-settlement-readback',
392+
settlementFinalizationState: 'settlement_finalized',
393+
});
296394
});
297395
});

0 commit comments

Comments
 (0)