Skip to content

Commit 9396f3c

Browse files
Merge pull request #218 from engineeredsoftware/v45/gate-15-interface-disclosure-route-vocabulary-docs
V45 Gate 15: Interface Disclosure And Route Vocabulary
2 parents f1e8df9 + e698963 commit 9396f3c

29 files changed

Lines changed: 1299 additions & 379 deletions

BITCODE_SPEC_V45_PARITY_MATRIX.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,16 @@ Acceptance:
189189
- Docs checks prove V45 terminology: AssetPack commodity, BTD scalar volume/rights, BTC settlement money, proof readback authority, `/deposit`, `/read`, `/packs`.
190190
- `check:v45-gate15` passes locally and in gate-quality CI.
191191

192+
Gate 15 implementation readback:
193+
194+
- Interface disclosure boundary matrix: `packages/pipelines/asset-pack/src/interface-disclosure-boundary.ts`.
195+
- Interface disclosure boundary tests: `packages/pipelines/asset-pack/src/__tests__/interface-disclosure-boundary.test.ts`.
196+
- Pipeline storage binding: `packages/pipelines/asset-pack/src/index.ts`.
197+
- Public route vocabulary and landing copy: `uapi/app/page.tsx`, `uapi/components/base/bitcode/layout/bitcode-public-copy.ts`, and `uapi/components/base/bitcode/layout/bitcode-public-explainers.ts`.
198+
- Public docs and compatibility docs: `uapi/app/docs/bitcode-docs-content.ts`, `uapi/app/(root)/components/PublicDocsPageContent.tsx`, and `uapi/app/exchange/README.md`.
199+
- Source-safe collapsed/expanded interface coverage: `uapi/tests/readPageClient.test.tsx`, `uapi/tests/depositPageClient.test.tsx`, `uapi/tests/packsPageClient.test.tsx`, `uapi/tests/publicDocsPageContent.test.tsx`, `uapi/tests/bitcodeDocsContent.test.tsx`, `uapi/tests/marketingLandingPage.test.tsx`, `uapi/tests/marketingOperatorGuideCard.test.tsx`, and `uapi/jest.config.cjs`.
200+
- Closure checker: `check:v45-gate15`.
201+
192202
### Gate 16: V45 Proof Families And Generated Artifacts
193203

194204
Gate class: `proof-only`.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@
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",
402402
"check:v45-gate14": "node scripts/check-v45-gate14-btc-settlement-rights-delivery-readback.mjs",
403+
"check:v45-gate15": "node scripts/check-v45-gate15-interface-disclosure-route-vocabulary-docs.mjs",
403404
"generate:v38-inference-surface-inventory": "node scripts/generate-v38-inference-surface-inventory.mjs",
404405
"check:v38-inference-surface-inventory": "node scripts/generate-v38-inference-surface-inventory.mjs --check",
405406
"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
@@ -11,6 +11,7 @@
1111
"./asset-pack-settlement-rights-delivery": "./src/asset-pack-settlement-rights-delivery.ts",
1212
"./reading-operational-telemetry-repair-readback": "./src/reading-operational-telemetry-repair-readback.ts",
1313
"./reading-interface-product-parity": "./src/reading-interface-product-parity.ts",
14+
"./interface-disclosure-boundary": "./src/interface-disclosure-boundary.ts",
1415
"./reading-local-staging-rehearsal": "./src/reading-local-staging-rehearsal.ts",
1516
"./deposit-asset-pack-options": "./src/deposit-asset-pack-options.ts",
1617
"./deposit-asset-pack-option-policy": "./src/deposit-asset-pack-option-policy.ts",
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
import { Execution } from '@bitcode/execution-generics';
2+
import {
3+
assertInterfaceDisclosureBoundarySourceSafe,
4+
buildInterfaceDisclosureBoundary,
5+
INTERFACE_DISCLOSURE_BOUNDARY_STAGES,
6+
INTERFACE_DISCLOSURE_BOUNDARY_SURFACES,
7+
persistInterfaceDisclosureBoundary,
8+
summarizeInterfaceDisclosureBoundary,
9+
} from '../interface-disclosure-boundary';
10+
11+
describe('Interface disclosure boundary', () => {
12+
it('covers every product, interface, teaching, and compatibility surface at every disclosure stage', () => {
13+
const boundary = buildInterfaceDisclosureBoundary();
14+
15+
expect(boundary).toMatchObject({
16+
schema: 'bitcode.interface-disclosure-boundary',
17+
routeVocabulary: {
18+
currentProductRoutes: ['/deposit', '/read', '/packs'],
19+
compatibilityRedirects: {
20+
'/exchange': '/packs',
21+
},
22+
canonicalTerms: {
23+
assetPackCommodity: 'AssetPack commodity',
24+
btdScalarVolumeAndRights: 'BTD scalar volume and rights',
25+
btcSettlementMoney: 'BTC settlement money',
26+
proofReadbackAuthority: 'proof readback authority',
27+
},
28+
},
29+
sourceSafety: {
30+
sourceSafeMetadataOnly: true,
31+
protectedSourceVisible: false,
32+
unpaidAssetPackSourceVisible: false,
33+
credentialsSerialized: false,
34+
},
35+
});
36+
expect(boundary.requiredSurfaces).toEqual([...INTERFACE_DISCLOSURE_BOUNDARY_SURFACES]);
37+
expect(boundary.requiredStages).toEqual([...INTERFACE_DISCLOSURE_BOUNDARY_STAGES]);
38+
expect(boundary.rowCount).toBe(
39+
INTERFACE_DISCLOSURE_BOUNDARY_SURFACES.length *
40+
INTERFACE_DISCLOSURE_BOUNDARY_STAGES.length,
41+
);
42+
expect(
43+
boundary.rows.map((row) => `${row.surface}:${row.stage}`).sort(),
44+
).toEqual(
45+
INTERFACE_DISCLOSURE_BOUNDARY_SURFACES.flatMap((surface) =>
46+
INTERFACE_DISCLOSURE_BOUNDARY_STAGES.map((stage) => `${surface}:${stage}`),
47+
).sort(),
48+
);
49+
});
50+
51+
it('keeps collapsed and expanded interface states source-safe before repository delivery', () => {
52+
const boundary = buildInterfaceDisclosureBoundary();
53+
54+
for (const row of boundary.rows) {
55+
expect(row.collapsedStateSummary).toContain('source-safe status');
56+
expect(row.expandedStateSummary).toContain(
57+
row.stage === 'after-repository-delivery'
58+
? 'purchased AssetPack source'
59+
: 'withholds source-bearing AssetPack contents',
60+
);
61+
expect(row.visibleFields.join(' ')).toMatch(/proof roots|repository delivery proof/u);
62+
expect(row.withheldFields.join(' ')).toMatch(/source|credentials/u);
63+
expect(row.sourceSafety).toMatchObject({
64+
protectedSourcePayloadSerialized: false,
65+
rawProtectedPromptVisible: false,
66+
rawInterpolatedPromptVisible: false,
67+
rawProviderResponseVisible: false,
68+
walletPrivateMaterialVisible: false,
69+
});
70+
if (row.stage !== 'after-repository-delivery') {
71+
expect(row.sourceBearingAssetPackVisibleToReader).toBe(false);
72+
}
73+
}
74+
75+
assertInterfaceDisclosureBoundarySourceSafe(boundary);
76+
});
77+
78+
it('unlocks source-bearing delivery only after BTC finality, BTD rights transfer, and repository delivery', () => {
79+
const boundary = buildInterfaceDisclosureBoundary();
80+
const deliveryRows = boundary.rows.filter(
81+
(row) => row.stage === 'after-repository-delivery',
82+
);
83+
const earlyRows = boundary.rows.filter(
84+
(row) => row.stage !== 'after-repository-delivery',
85+
);
86+
87+
expect(deliveryRows.length).toBe(INTERFACE_DISCLOSURE_BOUNDARY_SURFACES.length);
88+
expect(earlyRows.every((row) => row.sourceBearingAssetPackVisibleToReader === false)).toBe(true);
89+
for (const row of deliveryRows) {
90+
expect(row).toMatchObject({
91+
sourceBearingAssetPackVisibleToReader: true,
92+
btdRightsTransferred: true,
93+
btcFinalityRequired: true,
94+
repositoryDeliveryRequiredForSource: true,
95+
});
96+
}
97+
});
98+
99+
it('treats /exchange as a compatibility redirect rather than a current product route', () => {
100+
const boundary = buildInterfaceDisclosureBoundary();
101+
const redirectRows = boundary.rows.filter((row) => row.surface === 'exchange_redirect');
102+
103+
expect(redirectRows.length).toBe(INTERFACE_DISCLOSURE_BOUNDARY_STAGES.length);
104+
for (const row of redirectRows) {
105+
expect(row).toMatchObject({
106+
surfaceKind: 'compatibility_redirect',
107+
routePath: '/exchange',
108+
compatibilityRedirectTarget: '/packs',
109+
});
110+
}
111+
expect(boundary.routeVocabulary.currentProductRoutes).not.toContain('/exchange');
112+
});
113+
114+
it('persists route vocabulary, rows, source safety, and proof roots on execution storage', () => {
115+
const execution = new Execution('interface-disclosure-boundary-test');
116+
const boundary = buildInterfaceDisclosureBoundary();
117+
118+
persistInterfaceDisclosureBoundary(execution, boundary);
119+
120+
expect(execution.get('interfaces/disclosure', 'boundary')).toMatchObject({
121+
schema: 'bitcode.interface-disclosure-boundary',
122+
rowCount: boundary.rowCount,
123+
});
124+
expect(execution.get('interfaces/disclosure', 'routeVocabulary')).toMatchObject({
125+
currentProductRoutes: ['/deposit', '/read', '/packs'],
126+
});
127+
expect(execution.get('interfaces/disclosure', 'proofRoots')).toMatchObject({
128+
boundaryRoot: expect.stringMatching(/^sha256:/u),
129+
});
130+
expect(summarizeInterfaceDisclosureBoundary(boundary)).toContain('BTD scalar volume and rights');
131+
});
132+
133+
it('does not serialize protected source, secrets, or raw provider responses in boundary records', () => {
134+
const boundary = buildInterfaceDisclosureBoundary();
135+
const serialized = JSON.stringify(boundary);
136+
137+
expect(serialized).not.toContain('diff --git');
138+
expect(serialized).not.toContain(`${['sk', 'proj'].join('-')}-`);
139+
expect(serialized).not.toContain(`${['sb', 'secret'].join('_')}__`);
140+
expect(serialized).not.toContain('protected source body');
141+
expect(serialized).not.toContain('raw provider response body');
142+
});
143+
});

packages/pipelines/asset-pack/src/index.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ import {
4343
buildReadingInterfaceProductParity,
4444
persistReadingInterfaceProductParity,
4545
} from './reading-interface-product-parity';
46+
import {
47+
buildInterfaceDisclosureBoundary,
48+
persistInterfaceDisclosureBoundary,
49+
} from './interface-disclosure-boundary';
4650
import {
4751
buildReadingLocalStagingRehearsal,
4852
persistReadingLocalStagingRehearsal,
@@ -246,6 +250,11 @@ function factoryPreprocess(): Executor<any, any> {
246250
processedInput.readingInterfaceProductParity = interfaceParity;
247251
processedInput.readingInterfaceParityRows = interfaceParity.rows;
248252
processedInput.readingInterfaceNoBypassReadback = interfaceParity.noBypassReadback;
253+
const interfaceDisclosureBoundary = buildInterfaceDisclosureBoundary();
254+
persistInterfaceDisclosureBoundary(execution, interfaceDisclosureBoundary);
255+
persistInterfaceDisclosureBoundary(execution.parent as any, interfaceDisclosureBoundary);
256+
processedInput.interfaceDisclosureBoundary = interfaceDisclosureBoundary;
257+
processedInput.interfaceDisclosureBoundaryRows = interfaceDisclosureBoundary.rows;
249258
const localStagingRehearsal = buildReadingLocalStagingRehearsal({
250259
runId: String(execution.id || processedInput?.transactionId || processedInput?.id || ''),
251260
readNeedRuntime:
@@ -438,6 +447,7 @@ export * from './asset-pack-preview-boundary';
438447
export * from './asset-pack-settlement-rights-delivery';
439448
export * from './reading-operational-telemetry-repair-readback';
440449
export * from './reading-interface-product-parity';
450+
export * from './interface-disclosure-boundary';
441451
export * from './reading-local-staging-rehearsal';
442452
export * from './deposit-asset-pack-options';
443453
export * from './deposit-asset-pack-option-policy';

0 commit comments

Comments
 (0)