Skip to content

Commit 2d738e3

Browse files
Merge pull request #215 from engineeredsoftware/v45/gate-12-state-vocabulary-commodity-model
V45 Gate 12: State Vocabulary And Commodity Model
2 parents 35da679 + 9007e1f commit 2d738e3

10 files changed

Lines changed: 1307 additions & 5 deletions

File tree

BITCODE_SPEC_V45_PARITY_MATRIX.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,15 @@ Acceptance:
119119
- Route/API pack activity projection can display state without protected source.
120120
- `check:v45-gate12` passes locally and in gate-quality CI.
121121

122+
Gate 12 implementation readback:
123+
124+
- Commodity state vocabulary and source-safe projector: `packages/pipelines/asset-pack/src/asset-pack-commodity-state.ts`.
125+
- Commodity state coverage: `packages/pipelines/asset-pack/src/__tests__/asset-pack-commodity-state.test.ts`.
126+
- `/packs` activity display binding: `uapi/components/base/bitcode/activity/pack-activity-model.ts`.
127+
- `/packs` activity regression coverage: `uapi/tests/packActivityModel.test.ts`.
128+
- Package and interface resolution: `packages/pipelines/asset-pack/package.json`, `packages/pipelines/asset-pack/src/index.ts`, and `uapi/jest.config.cjs`.
129+
- Closure checker: `check:v45-gate12`.
130+
122131
### Gate 13: BTD Scalar Volume And Deterministic Quote Conservation
123132

124133
Gate class: `implementation`.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -397,6 +397,7 @@
397397
"check:v45-gate9": "node scripts/check-v45-gate9-gate-taxonomy-formal-spec-readiness.mjs",
398398
"check:v45-gate10": "node scripts/check-v45-gate10-formal-spec-consolidation.mjs",
399399
"check:v45-gate11": "node scripts/check-v45-gate11-implementation-parity-audit.mjs",
400+
"check:v45-gate12": "node scripts/check-v45-gate12-state-vocabulary-commodity-model.mjs",
400401
"generate:v38-inference-surface-inventory": "node scripts/generate-v38-inference-surface-inventory.mjs",
401402
"check:v38-inference-surface-inventory": "node scripts/generate-v38-inference-surface-inventory.mjs --check",
402403
"check:v38-gate2": "node scripts/check-v38-gate2-inference-surface-inventory.mjs",

packages/pipelines/asset-pack/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"./deposit-asset-pack-option-admission": "./src/deposit-asset-pack-option-admission.ts",
1818
"./depositor-earning-supply-intelligence": "./src/depositor-earning-supply-intelligence.ts",
1919
"./btd-btc-compensation-statements": "./src/btd-btc-compensation-statements.ts",
20+
"./asset-pack-commodity-state": "./src/asset-pack-commodity-state.ts",
2021
"./organization-policy-wallet-authority": "./src/organization-policy-wallet-authority.ts",
2122
"./read-need": "./src/read-need.ts",
2223
"./read-need-review-resynthesis": "./src/read-need-review-resynthesis.ts",
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
import {
2+
ASSET_PACK_LIFECYCLE_STATES,
3+
BTD_SCALAR_VOLUME_STATES,
4+
BTC_SETTLEMENT_STATES,
5+
assertAssetPackCommodityStateProjection,
6+
buildAssetPackCommodityStateProjection,
7+
projectAssetPackCommodityStateForPayload,
8+
toSourceSafeAssetPackCommodityStateDisplay,
9+
type AssetPackCommodityStateInput,
10+
type AssetPackLifecycleState,
11+
type BtcSettlementState,
12+
type BtdScalarVolumeState,
13+
} from '../asset-pack-commodity-state';
14+
15+
describe('AssetPack commodity state vocabulary', () => {
16+
const assetPackInputs: Record<AssetPackLifecycleState, AssetPackCommodityStateInput> = {
17+
'deposit-option-synthesized': {},
18+
'deposit-option-reviewed': { depositOptionReviewed: true },
19+
'depository-assetpack-admitted': { depositoryAssetPackAdmitted: true },
20+
'fit-candidates-recalled': { fitCandidatesRecalled: true },
21+
'fit-set-selected': { selectedFitAssetIds: ['fit-deposit-1'] },
22+
'need-fit-assetpack-synthesized': { needFitAssetPackSynthesized: true },
23+
'need-fit-assetpack-quoted': { quoteIssued: true },
24+
'settlement-observed': { quoteIssued: true, settlementObserved: true, btc: { paymentObserved: true } },
25+
'btd-settled-rights-transferred': {
26+
quoteIssued: true,
27+
settlementObserved: true,
28+
rightsTransferred: true,
29+
btd: { rightsTransferred: true },
30+
btc: { finalityConfirmed: true },
31+
},
32+
'source-unlocked-delivery': {
33+
quoteIssued: true,
34+
settlementObserved: true,
35+
rightsTransferred: true,
36+
sourceUnlockedDelivery: true,
37+
btd: { rightsTransferred: true },
38+
btc: { finalityConfirmed: true, settlementFinalized: true },
39+
},
40+
'compensated-and-reconciled': {
41+
quoteIssued: true,
42+
settlementObserved: true,
43+
rightsTransferred: true,
44+
sourceUnlockedDelivery: true,
45+
compensatedAndReconciled: true,
46+
btd: { rightsTransferred: true, sourceToSharesAllocated: true },
47+
btc: { finalityConfirmed: true, settlementFinalized: true, contributorCompensationRoutable: true },
48+
},
49+
'repair-required': { repairRequired: true },
50+
};
51+
52+
const btdInputs: Record<BtdScalarVolumeState, AssetPackCommodityStateInput> = {
53+
'btd-not-applicable': {},
54+
'btd-potential-measured': { btd: { potentialMeasured: true } },
55+
'need-fit-measurements-admitted': { btd: { measurementsAdmitted: true } },
56+
'measurement-weight-policy-locked': { btd: { weightPolicyLocked: true } },
57+
'weighted-scalar-volume-computed': { btd: { weightedScalarVolumeComputed: true } },
58+
'btd-quantized': { btd: { quantized: true } },
59+
'measuremint-applied': { btd: { measuremintApplied: true } },
60+
'btd-range-assigned': { btd: { rangeAssigned: true } },
61+
'btd-quote-bound': { btd: { quoteBound: true } },
62+
'btd-rights-pending': { btd: { rightsPending: true } },
63+
'btd-rights-transferred': {
64+
rightsTransferred: true,
65+
btd: { rightsTransferred: true },
66+
btc: { finalityConfirmed: true },
67+
},
68+
'btd-source-to-shares-allocated': {
69+
rightsTransferred: true,
70+
sourceUnlockedDelivery: true,
71+
compensatedAndReconciled: true,
72+
btd: { rightsTransferred: true, sourceToSharesAllocated: true },
73+
btc: { finalityConfirmed: true, settlementFinalized: true, contributorCompensationRoutable: true },
74+
},
75+
'btd-repair-required': { btd: { repairRequired: true } },
76+
};
77+
78+
const btcInputs: Record<BtcSettlementState, AssetPackCommodityStateInput> = {
79+
'btc-not-quoteable': {},
80+
'btc-quote-issued': { btc: { quoteIssued: true } },
81+
'btc-quote-accepted': { btc: { quoteAccepted: true } },
82+
'btc-quote-inactive': { btc: { quoteInactive: true } },
83+
'btc-wallet-ready': { btc: { walletReady: true } },
84+
'btc-psbt-prepared': { btc: { psbtPrepared: true } },
85+
'btc-psbt-signed': { btc: { psbtSigned: true } },
86+
'btc-broadcast-submitted': { btc: { broadcastSubmitted: true } },
87+
'btc-payment-observed': { btc: { paymentObserved: true } },
88+
'btc-payment-mismatch-repair-required': { btc: { paymentMismatchRepairRequired: true } },
89+
'btc-finality-confirmed': { settlementObserved: true, btc: { finalityConfirmed: true } },
90+
'btc-replaced-reorged-or-failed': { btc: { replacedReorgedOrFailed: true } },
91+
'btc-settlement-finalized': {
92+
rightsTransferred: true,
93+
sourceUnlockedDelivery: true,
94+
btd: { rightsTransferred: true },
95+
btc: { finalityConfirmed: true, settlementFinalized: true },
96+
},
97+
'btc-contributor-compensation-routable': {
98+
rightsTransferred: true,
99+
sourceUnlockedDelivery: true,
100+
compensatedAndReconciled: true,
101+
btd: { rightsTransferred: true, sourceToSharesAllocated: true },
102+
btc: { finalityConfirmed: true, settlementFinalized: true, contributorCompensationRoutable: true },
103+
},
104+
'btc-refund-or-escalation-required': { btc: { refundOrEscalationRequired: true } },
105+
'btc-settlement-repair-required': { btc: { repairRequired: true } },
106+
};
107+
108+
it('declares the exact current AssetPack, BTD, and BTC state sets', () => {
109+
expect(ASSET_PACK_LIFECYCLE_STATES).toHaveLength(12);
110+
expect(BTD_SCALAR_VOLUME_STATES).toHaveLength(13);
111+
expect(BTC_SETTLEMENT_STATES).toHaveLength(16);
112+
expect(ASSET_PACK_LIFECYCLE_STATES).toContain('need-fit-assetpack-quoted');
113+
expect(BTD_SCALAR_VOLUME_STATES).toContain('weighted-scalar-volume-computed');
114+
expect(BTC_SETTLEMENT_STATES).toContain('btc-psbt-signed');
115+
});
116+
117+
it.each(Object.entries(assetPackInputs))('projects AssetPack lifecycle state %s', (state, input) => {
118+
const projection = buildAssetPackCommodityStateProjection(input);
119+
expect(projection.states.assetPack).toBe(state);
120+
expect(assertAssetPackCommodityStateProjection(projection)).toBe(projection);
121+
});
122+
123+
it.each(Object.entries(btdInputs))('projects BTD scalar-volume state %s', (state, input) => {
124+
const projection = buildAssetPackCommodityStateProjection(input);
125+
expect(projection.states.btd).toBe(state);
126+
expect(assertAssetPackCommodityStateProjection(projection)).toBe(projection);
127+
});
128+
129+
it.each(Object.entries(btcInputs))('projects BTC settlement state %s', (state, input) => {
130+
const projection = buildAssetPackCommodityStateProjection(input);
131+
expect(projection.states.btc).toBe(state);
132+
expect(assertAssetPackCommodityStateProjection(projection)).toBe(projection);
133+
});
134+
135+
it('builds source-safe display projection from Pack activity payloads', () => {
136+
const projection = projectAssetPackCommodityStateForPayload({
137+
type: 'pipeline:read-fits',
138+
assetPackLifecycleState: 'need-fit-assetpack-quoted',
139+
btdScalarVolumeState: 'btd-quote-bound',
140+
quoteRoot: 'quote-root-abc',
141+
measurementRoot: 'measurement-root-abc',
142+
protectedSource: 'protected source body',
143+
});
144+
const display = toSourceSafeAssetPackCommodityStateDisplay(projection);
145+
146+
expect(display).toMatchObject({
147+
schema: 'bitcode.asset-pack.commodity-state-display',
148+
assetPackState: 'need-fit-assetpack-quoted',
149+
btdState: 'btd-quote-bound',
150+
btcState: 'btc-quote-issued',
151+
disclosureBoundary: 'after-quote',
152+
repairRequired: false,
153+
sourceSafety: {
154+
sourceSafeMetadataOnly: true,
155+
protectedSourceVisible: false,
156+
unpaidAssetPackSourceVisible: false,
157+
},
158+
});
159+
expect(JSON.stringify(display)).not.toContain('protected source body');
160+
});
161+
162+
it('rejects source unlock before BTC finality and BTD rights transfer', () => {
163+
const projection = buildAssetPackCommodityStateProjection({ sourceUnlockedDelivery: true });
164+
165+
expect(projection.blockers).toEqual(
166+
expect.arrayContaining([
167+
'source_unlock_without_btd_rights_transfer',
168+
'rights_or_delivery_without_btc_finality',
169+
]),
170+
);
171+
expect(() => assertAssetPackCommodityStateProjection(projection)).toThrow(/source_unlock_without_btd_rights_transfer/);
172+
});
173+
174+
it('rejects collapsed quote/payment and observation/finality projections', () => {
175+
const payment = buildAssetPackCommodityStateProjection({ btc: { paymentObserved: true } });
176+
const paymentCollapsedWithFinality = {
177+
...payment,
178+
states: { ...payment.states, btc: 'btc-payment-observed' as const },
179+
evidence: { ...payment.evidence, finalityConfirmed: true },
180+
blockers: [],
181+
};
182+
const quoteCollapsedWithPayment = {
183+
...payment,
184+
states: { ...payment.states, btc: 'btc-quote-issued' as const },
185+
blockers: [],
186+
};
187+
188+
expect(() => assertAssetPackCommodityStateProjection(paymentCollapsedWithFinality)).toThrow(
189+
/btc_payment_observation_collapsed_with_finality/,
190+
);
191+
expect(() => assertAssetPackCommodityStateProjection(quoteCollapsedWithPayment)).toThrow(
192+
/btc_quote_collapsed_with_payment_observation/,
193+
);
194+
});
195+
196+
it('rejects collapsed AssetPack quote, settlement observation, and source visibility projections', () => {
197+
const payment = buildAssetPackCommodityStateProjection({ btc: { paymentObserved: true } });
198+
const assetPackQuoteCollapsedWithObservation = {
199+
...payment,
200+
states: { ...payment.states, assetPack: 'need-fit-assetpack-quoted' as const },
201+
blockers: [],
202+
};
203+
const settlementObservationCollapsedWithRightsTransfer = {
204+
...payment,
205+
states: { ...payment.states, assetPack: 'settlement-observed' as const },
206+
evidence: { ...payment.evidence, finalityConfirmed: true, rightsTransferred: true },
207+
blockers: [],
208+
};
209+
const sourceVisibleBeforeSettlement = buildAssetPackCommodityStateProjection({
210+
sourceSafety: { protectedSourceVisible: true },
211+
});
212+
213+
expect(() => assertAssetPackCommodityStateProjection(assetPackQuoteCollapsedWithObservation)).toThrow(
214+
/assetpack_quote_collapsed_with_settlement_observation/,
215+
);
216+
expect(() => assertAssetPackCommodityStateProjection(settlementObservationCollapsedWithRightsTransfer)).toThrow(
217+
/settlement_observation_collapsed_with_rights_transfer/,
218+
);
219+
expect(sourceVisibleBeforeSettlement.blockers).toContain('protected_source_visibility_before_settlement');
220+
expect(() => assertAssetPackCommodityStateProjection(sourceVisibleBeforeSettlement)).toThrow(/not source-safe/);
221+
});
222+
});

0 commit comments

Comments
 (0)