Skip to content

Commit d2ed02c

Browse files
Merge pull request #16 from engineeredsoftware/v28/gate-11-read-fits-finding-preview
Gate 11: close Finding Fits preview
2 parents ddaa805 + 1f4a3ee commit d2ed02c

12 files changed

Lines changed: 491 additions & 31 deletions

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ jobs:
4747
node scripts/check-v28-metadevelopment-readiness.mjs --skip-branch-check
4848
node scripts/check-v28-gate9-depository-evidence.mjs
4949
node scripts/check-v28-gate10-read-need-comprehension.mjs
50+
node scripts/check-v28-gate11-read-fits-finding-preview.mjs
5051
node scripts/promote-bitcode-canon.mjs --version V28 --commit HEAD --dry-run
5152
5253
- name: Validate source casing and imports

BITCODE_SPEC_V28.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,31 @@ Gate 11 owns `ReadFitsFindingSynthesis`: depository search, candidate fit
850850
ranking, fitting deposits as synthesis context, AssetPack measurement, source-
851851
safe preview, Share-to-Fee quote, and proof-rich Terminal stream visibility.
852852

853+
Gate 11 acceptance requires:
854+
855+
- discovery records all `fitDepositAssetIds` above the review and semantic
856+
thresholds, preserves the ranked candidate trace, and fails closed with
857+
`blocked_readiness` or `no_worthy_fit` instead of inventing source outside
858+
the depository;
859+
- lexical and vector depository search tool telemetry use canonical
860+
`ReadFitsFindingSynthesis.tool.*` ids and carry input read constraints,
861+
selected fit deposits, query root, ranking root, and embedding policy posture;
862+
- implementation and postprocess carry the Finding Fits result into a
863+
`bitcode.asset-pack.source-safe-preview` object with Need/Fit measurements,
864+
fit score band, candidate ids, proof roots, source-manifest root, preview
865+
root, access policy, settlement boundary, and unlock posture;
866+
- Share-to-Fee quote is deterministic:
867+
`sum(measurement.weight * measurement.volume * admitted_fit_quality)` over
868+
the accepted Need measurement vector and the admitted fit quality, with BTC
869+
sats, quote root, payer, and network-fee posture visible before settlement;
870+
- protected source, full patches, source-bearing manifest entries, and licensed
871+
read payloads stay withheld before settlement. Terminal may show only
872+
measurements, non-source metadata, roots, candidate ids, proof posture,
873+
ownership boundary, settlement boundary, and fee quote;
874+
- Terminal stream/history surfaces render the source-safe preview and fee quote
875+
without requiring Network-panel JSON inspection, while preserving expandable
876+
raw evidence for audit.
877+
853878
### Gate 12: Settlement, Rights, Delivery, And Reconciliation
854879

855880
Gate 12 owns BTC settlement, depositor/reader ownership boundaries, read-license

BITCODE_SPEC_V28_PARITY_MATRIX.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,18 @@ by a later V28 gate before `version/v28` can be promoted to `main`.
184184
| Full demonstration proof-suite alignment | Gate 13 | Refresh the older demonstration proof-member, theorem, public-projection, and V26-proven expectations so they match the active V27/V28 posture or are explicitly retired from V28 promotion proof. | `npm --prefix protocol-demonstration run test`, proof-member/theorem matrix counts, public projection spine assertions, and V26/V28 proven-generator assertions. | The complete demonstration suite is either green under the promoted V28 proof model or every intentionally retired legacy assertion is replaced by a narrower current-canon proof. |
185185
| Local and staging-testnet validation | Gate 13 | Local non-mocked OpenAI/Supabase/Vercel Sandbox validation plus staging-testnet readback evidence for complete Depositing -> Reading -> settlement -> PR delivery. | `check:v28-metadevelopment`, package tests, UAPI tests, build, demonstration tests, `qa:v28:pipeline-readback`, browser QA, SQL ledger/readback queries. | `version/v28` is promotion-ready only when local and staging-testnet evidence prove the full commercial product flow. |
186186

187+
Gate 11 closure narrows the two Gate 11 carryforward rows to source-safe
188+
preview readiness: Finding Fits discovery must now produce canonical
189+
`ReadFitsFindingSynthesis.tool.lexical-depository-search` and
190+
`ReadFitsFindingSynthesis.tool.vector-depository-search` telemetry, all
191+
threshold-passing `fitDepositAssetIds`, query/ranking roots, and embedding
192+
policy posture. AssetPack postprocess must derive a source-safe preview and
193+
Share-to-Fee quote from the accepted Need and Finding Fits result, exposing only
194+
measurements, roots, score bands, candidate ids, proof posture, ownership
195+
boundary, settlement boundary, and BTC quote before settlement. Gate 12 remains
196+
responsible for paid unlock, rights transfer, ledger reconciliation, and PR
197+
delivery.
198+
187199
## V28 implementation matrix
188200

189201
| Area | Current source evidence | Judgment | Gate owner |

BITCODE_V28_QA.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2103,3 +2103,44 @@ Expected V28 behavior:
21032103
- a later background revalidation may make the BTC/BTD widget show its own loading posture, but it must not regress to the disconnected CTA while a known wallet exists;
21042104
- client telemetry must include `nav:chrome-identity`, `user-data:fetch-start`, `user-data:read` or `user-data:anonymous-read`, and any fetch failure;
21052105
- server telemetry must include `auxillaries-data:read-start` and `auxillaries-data:read-finish` or `auxillaries-data:read-failed`.
2106+
2107+
### Gate 11 Finding Fits And Source-Safe Preview QA
2108+
2109+
Gate 11 local closure proves that `ReadFitsFindingSynthesis` can search the
2110+
depository, preserve every qualifying fit deposit, derive a source-safe preview,
2111+
and expose a deterministic Share-to-Fee quote before Gate 12 settlement unlock.
2112+
2113+
Required local checks:
2114+
2115+
```bash
2116+
pnpm run check:v28-gate11
2117+
pnpm --filter @bitcode/pipeline-asset-pack exec jest --config jest.config.cjs --runTestsByPath \
2118+
src/__tests__/depository-search.test.ts \
2119+
src/__tests__/read-need.test.ts \
2120+
src/__tests__/postprocess.test.ts \
2121+
src/__tests__/depository-search-tool.test.ts \
2122+
--runInBand --forceExit
2123+
pnpm --filter @bitcode/pipeline-asset-pack typecheck
2124+
```
2125+
2126+
Expected evidence:
2127+
2128+
- Finding Fits discovery stores `depository/search.result`,
2129+
`depository/search.toolTelemetry`, `fit.result`, `fit.selectionTrace`, all
2130+
threshold-passing `fitDepositAssetIds`, `queryRoot`, `rankingRoot`, and the
2131+
OpenAI `text-embedding-3-small` embedding policy posture.
2132+
- Tool telemetry uses canonical
2133+
`ReadFitsFindingSynthesis.tool.lexical-depository-search` and
2134+
`ReadFitsFindingSynthesis.tool.vector-depository-search` ids. Vector
2135+
telemetry must at minimum expose the declared vector-store and embedding
2136+
policy posture until a later gate replaces local lexical fallback with live
2137+
vector RPC readback.
2138+
- AssetPack postprocess stores `asset-pack/preview.sourceSafe`,
2139+
`asset-pack/preview.feeQuote`, and `asset-pack/preview.previewRoot` whenever
2140+
an accepted Need and Finding Fits result are present.
2141+
- The source-safe preview exposes Need/Fit measurements, score band, fit
2142+
deposit ids, selected candidate ids, roots, proof posture, access policy,
2143+
ownership boundary, settlement boundary, unlock posture, and BTC quote.
2144+
- The preview must not expose protected source content, full patches,
2145+
source-bearing manifest entries, licensed read payloads, or PR source changes
2146+
before settlement. Gate 12 owns paid unlock, rights transfer, and delivery.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"check:v28-metadevelopment": "node scripts/check-v28-metadevelopment-readiness.mjs",
4242
"check:v28-gate9": "node scripts/check-v28-gate9-depository-evidence.mjs",
4343
"check:v28-gate10": "node scripts/check-v28-gate10-read-need-comprehension.mjs",
44+
"check:v28-gate11": "node scripts/check-v28-gate11-read-fits-finding-preview.mjs",
4445
"check:spec-quality": "node scripts/run-bitcode-spec-quality.mjs --mode basic",
4546
"check:spec-quality:title": "node scripts/run-bitcode-spec-quality.mjs --mode strict-from-title",
4647
"check:spec-quality:v24": "node scripts/run-bitcode-spec-quality.mjs --mode strict-version --version V24",

packages/pipelines/asset-pack/src/__tests__/depository-search.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,36 @@ describe('AssetPack depository search', () => {
292292
expect(findStored(exec, 'fit', 'resultState')).toBe('worthy_fit');
293293
expect(findStored(exec, 'depository/search', 'result')?.resultState).toBe('worthy_fit');
294294
expect(findStored(exec, 'depository/search', 'embeddingPolicy')?.dimensions).toBe(1536);
295+
expect(findStored(exec, 'depository/search', 'toolTelemetry')).toEqual([
296+
expect.objectContaining({
297+
tool: 'ReadFitsFindingSynthesis.tool.lexical-depository-search',
298+
phase: 'ReadFitsFindingSynthesis.discovery',
299+
agent: 'ReadFitsFindingSynthesis.discovery.finding-fits',
300+
step: 'ReadFitsFindingSynthesis.discovery.finding-fits.try',
301+
output: expect.objectContaining({
302+
resultState: 'worthy_fit',
303+
fitDepositAssetIds: ['asset_repository-revision-deposit-engineeredsoftware-engi'],
304+
queryRoot: expect.stringMatching(/^sha256:/),
305+
rankingRoot: expect.stringMatching(/^sha256:/),
306+
}),
307+
}),
308+
expect.objectContaining({
309+
tool: 'ReadFitsFindingSynthesis.tool.vector-depository-search',
310+
output: expect.objectContaining({
311+
resultState: 'embedding_policy_declared',
312+
vectorStore: expect.objectContaining({
313+
table: 'deliverable_vectors',
314+
rpc: 'match_deliverable_vectors',
315+
}),
316+
}),
317+
}),
318+
]);
319+
expect(findStored(exec, 'tools', 'lexical-depository-search')?.tool).toBe(
320+
'ReadFitsFindingSynthesis.tool.lexical-depository-search'
321+
);
322+
expect(findStored(exec, 'tools', 'vector-depository-search')?.tool).toBe(
323+
'ReadFitsFindingSynthesis.tool.vector-depository-search'
324+
);
295325
expect(output.fitResult.resultState).toBe('worthy_fit');
296326
expect(output.fitResult.fitDepositAssetIds).toEqual([
297327
'asset_repository-revision-deposit-engineeredsoftware-engi',

packages/pipelines/asset-pack/src/__tests__/postprocess.test.ts

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// @ts-nocheck
22
import { buildAssetPackPostprocessedResult, normalizeAssetPackOutput } from '../postprocess';
33
import { Execution } from '@bitcode/execution-generics';
4+
import { acceptReadNeed, synthesizeReadNeedForPipelineInput } from '../read-need';
45

56
describe('normalizeAssetPackOutput', () => {
67
it('backfills prUrl, filesModified, and summary from execution', () => {
@@ -138,4 +139,94 @@ describe('normalizeAssetPackOutput', () => {
138139
prUrl: 'https://github.com/acme/repo/pull/26',
139140
});
140141
});
142+
143+
it('derives and stores source-safe preview evidence from an accepted Need and Finding Fits result', () => {
144+
const exec = new Execution('pipeline:asset-pack');
145+
const acceptedNeed = acceptReadNeed(synthesizeReadNeedForPipelineInput({
146+
read: {
147+
id: 'read-1',
148+
prompt: 'Find deposited source evidence for a source-safe Terminal AssetPack preview.',
149+
},
150+
sourceRevision: {
151+
repositoryFullName: 'engineeredsoftware/ENGI',
152+
branch: 'main',
153+
commit: '31bbc0c5227b6b3aed5d107fd8507d35ec22970a',
154+
},
155+
}));
156+
const fitResult = {
157+
schema: 'bitcode.asset-pack.fit-result',
158+
resultState: 'worthy_fit',
159+
resultReasons: ['Selected 1 proof-bearing fit deposit for this Read.'],
160+
fitDepositAssetIds: ['fit-deposit-1'],
161+
selectedCandidateAssetIds: ['fit-deposit-1'],
162+
queryRoot: 'sha256:query',
163+
rankingRoot: 'sha256:ranking',
164+
searchedAssetCount: 1,
165+
embeddingPolicy: {
166+
provider: 'openai',
167+
model: 'text-embedding-3-small',
168+
dimensions: 1536,
169+
},
170+
selectionTrace: {
171+
selectedCandidates: [
172+
{
173+
assetId: 'fit-deposit-1',
174+
scores: { finalScore: 0.84 },
175+
proofEvidence: { proofRoot: 'sha256:proof' },
176+
},
177+
],
178+
fitDeposits: [],
179+
blockedCandidates: [],
180+
candidateRanking: [],
181+
rejectedCandidateCount: 0,
182+
},
183+
};
184+
185+
exec.store('read/need', 'accepted', acceptedNeed);
186+
exec.store('fit', 'result', fitResult);
187+
188+
const normalized = normalizeAssetPackOutput({
189+
success: true,
190+
summary: 'Measured AssetPack preview ready.',
191+
deliveryMechanism: {
192+
prUrl: 'https://github.com/engineeredsoftware/ENGI/pull/28',
193+
},
194+
} as any, exec);
195+
const result = buildAssetPackPostprocessedResult(exec, normalized);
196+
197+
expect(normalized.sourceSafePreview).toMatchObject({
198+
schema: 'bitcode.asset-pack.source-safe-preview',
199+
need: {
200+
needId: acceptedNeed.needId,
201+
reviewState: 'accepted',
202+
},
203+
fit: {
204+
resultState: 'worthy_fit',
205+
fitDepositAssetIds: ['fit-deposit-1'],
206+
scoreBand: 'high',
207+
},
208+
disclosurePolicy: {
209+
protectedSourceDisclosure: 'forbidden_before_settlement',
210+
},
211+
settlementBoundary: {
212+
payer: 'reader',
213+
payee: 'depositor',
214+
serverCustody: false,
215+
},
216+
unlock: {
217+
state: 'pending_settlement',
218+
sourceAvailable: false,
219+
},
220+
});
221+
expect(normalized.sourceSafePreview.delivery.pullRequestTarget).toBe(
222+
'https://github.com/engineeredsoftware/ENGI/pull/28'
223+
);
224+
expect(normalized.feeQuote.quoteRoot).toMatch(/^sha256:/);
225+
expect(result.sourceSafePreview?.roots.previewRoot).toMatch(/^sha256:/);
226+
expect(result.feeQuote?.finalityState).toBe('preview_not_paid');
227+
expect(exec.get('asset-pack/preview', 'sourceSafe')?.previewId).toBe(
228+
normalized.sourceSafePreview.previewId
229+
);
230+
expect(JSON.stringify(normalized.sourceSafePreview)).not.toContain('diff --git');
231+
});
141232
});

packages/pipelines/asset-pack/src/depository-search.ts

Lines changed: 65 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,11 @@ const DEFAULT_THRESHOLDS: DepositorySearchThresholds = {
269269
maxSelectedCandidates: 3,
270270
};
271271

272+
export const READ_FITS_FINDING_SYNTHESIS_TOOL_IDS = {
273+
lexicalDepositorySearch: 'ReadFitsFindingSynthesis.tool.lexical-depository-search',
274+
vectorDepositorySearch: 'ReadFitsFindingSynthesis.tool.vector-depository-search',
275+
} as const;
276+
272277
const STOP_WORDS = new Set([
273278
'the',
274279
'and',
@@ -1280,42 +1285,67 @@ function storeDepositorySearchToolResult(
12801285
): void {
12811286
if (!execution?.store) return;
12821287
const { read, assets, result, providerIds } = input;
1283-
execution.store('tools', 'result', {
1284-
tool: 'bitcode.depository.search',
1285-
ok: true,
1286-
input: {
1287-
read: {
1288-
id: read.id || null,
1289-
repositoryFullName: read.repositoryFullName || null,
1290-
sourceBranch: read.sourceBranch || null,
1291-
sourceCommit: read.sourceCommit || null,
1292-
targetArtifactKinds: read.targetArtifactKinds,
1293-
closureCriteriaCount: read.closureCriteria.length,
1294-
failureModeCount: read.failureModes.length,
1295-
},
1296-
assetCount: assets.length,
1297-
providerIds,
1288+
const toolInput = {
1289+
read: {
1290+
id: read.id || null,
1291+
repositoryFullName: read.repositoryFullName || null,
1292+
sourceBranch: read.sourceBranch || null,
1293+
sourceCommit: read.sourceCommit || null,
1294+
targetArtifactKinds: read.targetArtifactKinds,
1295+
closureCriteriaCount: read.closureCriteria.length,
1296+
failureModeCount: read.failureModes.length,
12981297
},
1298+
assetCount: assets.length,
1299+
providerIds,
1300+
};
1301+
const toolOutput = {
1302+
schema: result.schema,
1303+
resultState: result.resultState,
1304+
resultReasons: result.resultReasons,
1305+
searchedAssetCount: result.searchedAssetCount,
1306+
fitDepositAssetIds: result.fitDepositAssetIds,
1307+
fitDepositCount: result.fitDeposits.length,
1308+
selectedCandidateAssetIds: result.selectedCandidateAssetIds,
1309+
selectedCandidateCount: result.selectedCandidates.length,
1310+
blockedCandidateCount: result.blockedCandidates.length,
1311+
rejectedCandidateCount: result.rejectedCandidates.length,
1312+
queryRoot: result.queryRoot,
1313+
rankingRoot: result.rankingRoot,
1314+
embeddingPolicy: result.embeddingPolicy,
1315+
};
1316+
const lexicalTelemetry = {
1317+
tool: READ_FITS_FINDING_SYNTHESIS_TOOL_IDS.lexicalDepositorySearch,
1318+
ok: true,
1319+
input: toolInput,
1320+
output: toolOutput,
1321+
phase: 'ReadFitsFindingSynthesis.discovery',
1322+
agent: 'ReadFitsFindingSynthesis.discovery.finding-fits',
1323+
step: 'ReadFitsFindingSynthesis.discovery.finding-fits.try',
1324+
generation: 'tools_execution',
1325+
};
1326+
const vectorTelemetry = {
1327+
tool: READ_FITS_FINDING_SYNTHESIS_TOOL_IDS.vectorDepositorySearch,
1328+
ok: true,
1329+
input: toolInput,
12991330
output: {
1300-
schema: result.schema,
1301-
resultState: result.resultState,
1302-
resultReasons: result.resultReasons,
1303-
searchedAssetCount: result.searchedAssetCount,
1304-
fitDepositAssetIds: result.fitDepositAssetIds,
1305-
fitDepositCount: result.fitDeposits.length,
1331+
resultState: 'embedding_policy_declared',
13061332
selectedCandidateAssetIds: result.selectedCandidateAssetIds,
1307-
selectedCandidateCount: result.selectedCandidates.length,
1308-
blockedCandidateCount: result.blockedCandidates.length,
1309-
rejectedCandidateCount: result.rejectedCandidates.length,
1333+
fitDepositAssetIds: result.fitDepositAssetIds,
13101334
queryRoot: result.queryRoot,
13111335
rankingRoot: result.rankingRoot,
13121336
embeddingPolicy: result.embeddingPolicy,
1337+
vectorStore: result.embeddingPolicy.vectorStore,
13131338
},
1314-
phase: 'setup',
1315-
agent: 'setup:depository-search',
1316-
step: 'try',
1339+
phase: 'ReadFitsFindingSynthesis.discovery',
1340+
agent: 'ReadFitsFindingSynthesis.discovery.finding-fits',
1341+
step: 'ReadFitsFindingSynthesis.discovery.finding-fits.try',
13171342
generation: 'tools_execution',
1318-
});
1343+
};
1344+
1345+
execution.store('tools', 'result', lexicalTelemetry);
1346+
execution.store('tools', 'lexical-depository-search', lexicalTelemetry);
1347+
execution.store('tools', 'vector-depository-search', vectorTelemetry);
1348+
execution.store('depository/search', 'toolTelemetry', [lexicalTelemetry, vectorTelemetry]);
13191349
}
13201350

13211351
function buildBlockedReadFitsFindingResult(input: {
@@ -1406,12 +1436,17 @@ export async function runDepositorySearchForPipelineInput(
14061436
if (execution?.store) {
14071437
storeEvidence(execution);
14081438
storeEvidence(execution.parent as { store?: (namespace: string, key: string, value: unknown) => void });
1409-
storeDepositorySearchToolResult(execution, {
1439+
const toolEvidence = {
14101440
read,
14111441
assets,
14121442
result,
14131443
providerIds: providers.map((provider) => provider.id),
1414-
});
1444+
};
1445+
storeDepositorySearchToolResult(execution, toolEvidence);
1446+
storeDepositorySearchToolResult(
1447+
execution.parent as { store?: (namespace: string, key: string, value: unknown) => void },
1448+
toolEvidence
1449+
);
14151450
}
14161451

14171452
return result;

0 commit comments

Comments
 (0)