@@ -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