Skip to content

Commit 94acf54

Browse files
V48 Gate 3 (implementation-only): SDIVF CI integration, fail-closed absolutes, deposit impl key
- Opt-in BITCODE_ENABLE_ASSET_PACK_SDIVF_RUNTIME_IN_TEST runs all five phases under test - Deposit SDIVF integration suite (boundary-mocked LLMs) proves full deposit spine - validateDepositSynthesisOptions fail-closes without formal absolutes (no placeholder fallback) - ReadyToFinish unwraps PTRR envelope (F26-A/F27 class) - Deposit implementation agent key: implementation:deposit-asset-pack-synthesis - Deprecate non-product synthesizeRealDepositOptionCandidates dual-core path
1 parent 5f8eaca commit 94acf54

11 files changed

Lines changed: 356 additions & 62 deletions

packages/pipelines/asset-pack/src/__tests__/agent-measure-absolutes.test.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -144,12 +144,11 @@ describe('validateDepositSynthesisOptions absolutes wiring', () => {
144144
expect(measurements.map((m) => m.measurementKind)).not.toContain('source-coverage');
145145
});
146146

147-
it('falls back to the placeholder catalog when no absolutes are present', () => {
147+
it('fail-closes when formal absolutes are missing (no placeholder catalog fallback)', () => {
148148
const out = validateDepositSynthesisOptions([baseOption], context);
149-
const kinds = out.candidates[0].measurements.map((m) => m.measurementKind);
150-
expect(kinds).toContain('source-coverage');
151-
expect(kinds).toContain('demand-alignment');
152-
expect(kinds).toContain('reuse-likelihood');
149+
expect(out.candidates).toHaveLength(0);
150+
expect(out.droppedCandidateCount).toBe(1);
151+
expect(out.exclusionViolations[0]).toMatch(/missing formal absolute measurements/i);
153152
});
154153
});
155154

packages/pipelines/asset-pack/src/__tests__/asset-packs-synthesis.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,27 @@ describe('deposit lens adapter', () => {
199199
measurements: { 'source-coverage': 0.6, 'demand-alignment': 0.7, 'reuse-likelihood': 0.5 },
200200
measurementRationale: 'Covers the auth path.',
201201
confidence: 0.8,
202+
// Formal absolutes (Validation measure-agent) are required — no
203+
// placeholder catalog fallback on the product projection path.
204+
absolutes: [
205+
{
206+
measurementKind: 'function-count',
207+
label: 'Functions',
208+
weight: 0.12,
209+
volume: 0.5,
210+
category: 'absolute',
211+
magnitude: 8,
212+
unit: 'functions',
213+
},
214+
{
215+
measurementKind: 'correctness-estimate',
216+
label: 'Correctness',
217+
weight: 0.18,
218+
volume: 0.7,
219+
category: 'absolute',
220+
unit: 'estimate',
221+
},
222+
],
202223
patch: {
203224
fileChanges: [
204225
{ path: 'src/app.py', op: 'modify' },
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
// @ts-nocheck
2+
/**
3+
* Deposit SDIVF pipeline integration (V48 Gate 3 — P0 CI confidence).
4+
*
5+
* Under NODE_ENV=test the five phase runtimes are no-ops by default. This suite
6+
* opts into the real phase runtimes via BITCODE_ENABLE_ASSET_PACK_SDIVF_RUNTIME_IN_TEST
7+
* and runs the full deposit lens with boundary-mocked LLMs only — never real
8+
* provider inference. Pins:
9+
* - Setup → Discovery → Implementation → Validation → Finish all execute
10+
* - implementation:options is produced on the shared execution
11+
* - Validation attaches formal absolutes (deterministic measure path)
12+
* - Finish records the upload-for-review artifact
13+
*
14+
* Clone/MCP setup agents that need live VCS are stubbed so the suite stays offline.
15+
*/
16+
jest.mock('@bitcode/generic-llms', () =>
17+
require('./support/generic-llms-mock').makeGenericLLMsMock());
18+
19+
jest.mock('../agents/setup/asset-pack-clone-vcs-repository-agent', () => ({
20+
__esModule: true,
21+
default: jest.fn().mockResolvedValue({
22+
success: true,
23+
repository: {
24+
owner: 'engineeredsoftware',
25+
name: 'ENGI',
26+
fullName: 'engineeredsoftware/ENGI',
27+
branch: 'main',
28+
},
29+
}),
30+
}));
31+
32+
jest.mock('../agents/setup/asset-pack-initialize-mcps-tools-agent', () => ({
33+
__esModule: true,
34+
default: jest.fn().mockResolvedValue({ success: true }),
35+
}));
36+
37+
import { Execution } from '@bitcode/execution-generics';
38+
import {
39+
setBoundaryLLMOutput,
40+
resetBoundaryLLMOutput,
41+
resetBoundaryLLMCalls,
42+
} from './support/generic-llms-mock';
43+
import { synthesizeAssetPacksPipeline } from '../index';
44+
import { validateDepositSynthesisOptions } from '../asset-packs-synthesis';
45+
46+
const INVENTORY = {
47+
paths: ['src/auth/session.ts', 'src/auth/token.ts', 'src/billing/invoice.ts'],
48+
samples: [
49+
{
50+
path: 'src/auth/session.ts',
51+
excerpt: 'export function createSession() { return { id: 1 }; }',
52+
},
53+
],
54+
totalPathCount: 3,
55+
excludedPathCount: 0,
56+
};
57+
58+
/** One boundary response that satisfies every deposit agent schema (non-strict). */
59+
const DEPOSIT_SDIVF_BOUNDARY_OUTPUT = {
60+
// Setup + discovery:comprehension-shaped agents
61+
comprehension: {
62+
summary: 'Auth and billing capabilities with clear module boundaries.',
63+
obfuscatedPaths: [],
64+
obfuscatedConcepts: [],
65+
honorNotes: [],
66+
capabilities: ['session auth', 'token refresh', 'invoicing'],
67+
knowledgeAreas: ['authentication', 'billing'],
68+
notableModules: ['src/auth/session.ts', 'src/billing/invoice.ts'],
69+
},
70+
// Discovery depository-search
71+
guidance: {
72+
summary: 'Readers seek reusable auth and billing slices.',
73+
likelyReadTopics: ['authentication', 'billing'],
74+
demandAlignment: ['session auth'],
75+
underservedTopics: ['token refresh patterns'],
76+
readabilityNotes: ['Keep paths source-safe.'],
77+
},
78+
// Discovery inherent-regurgitation
79+
regurgitation: {
80+
summary: 'Known session/token patterns and invoice domain practices.',
81+
relevantKnowledge: ['JWT refresh rotation', 'idempotent invoicing'],
82+
patterns: ['capability-slice packaging'],
83+
references: ['industry auth handbooks'],
84+
},
85+
// Implementation synthesis
86+
options: [
87+
{
88+
kind: 'capability-slice',
89+
title: 'Auth session capability slice',
90+
summary:
91+
'A bounded source-safe authentication capability covering session creation and token refresh for reusable deposit supply.',
92+
coveredSourcePaths: ['src/auth/session.ts', 'src/auth/token.ts'],
93+
measurements: {},
94+
measurementRationale: '',
95+
confidence: 0.82,
96+
patch: {
97+
fileChanges: [
98+
{ path: 'src/auth/session.ts', op: 'modify' },
99+
{ path: 'src/auth/token.ts', op: 'create' },
100+
],
101+
patchSummary: 'Encodes session and token refresh capability as deposit supply.',
102+
},
103+
needinessSignal: {
104+
demand: 0.7,
105+
saturation: 0.3,
106+
rationale: 'Auth knowledge is frequently read across the network.',
107+
},
108+
},
109+
],
110+
// Deposit validation
111+
issues: [],
112+
qualityScore: 0.88,
113+
coverageGaps: [],
114+
recommendation: 'complete',
115+
// ReadyToFinish
116+
ready: true,
117+
assessment: {
118+
productionReady: true,
119+
qualityLevel: 'good',
120+
riskLevel: 'low',
121+
recommendation: 'Finish and upload for depositor review.',
122+
},
123+
criticalIssues: [],
124+
warnings: [],
125+
suggestions: [],
126+
metrics: {
127+
overallScore: 0.88,
128+
validationScore: 0.9,
129+
qualityScore: 0.88,
130+
readinessScore: 0.9,
131+
},
132+
};
133+
134+
describe('deposit SDIVF pipeline integration (boundary-mocked LLMs)', () => {
135+
const previousSdivf = process.env.BITCODE_ENABLE_ASSET_PACK_SDIVF_RUNTIME_IN_TEST;
136+
const previousInference = process.env.BITCODE_ASSET_PACK_REAL_INFERENCE;
137+
138+
beforeAll(() => {
139+
process.env.BITCODE_ENABLE_ASSET_PACK_SDIVF_RUNTIME_IN_TEST = '1';
140+
// Deterministic absolutes (Tool path); quality volumes stay report-derived.
141+
delete process.env.BITCODE_ASSET_PACK_REAL_INFERENCE;
142+
});
143+
144+
afterAll(() => {
145+
if (previousSdivf === undefined) {
146+
delete process.env.BITCODE_ENABLE_ASSET_PACK_SDIVF_RUNTIME_IN_TEST;
147+
} else {
148+
process.env.BITCODE_ENABLE_ASSET_PACK_SDIVF_RUNTIME_IN_TEST = previousSdivf;
149+
}
150+
if (previousInference === undefined) {
151+
delete process.env.BITCODE_ASSET_PACK_REAL_INFERENCE;
152+
} else {
153+
process.env.BITCODE_ASSET_PACK_REAL_INFERENCE = previousInference;
154+
}
155+
});
156+
157+
beforeEach(() => {
158+
resetBoundaryLLMOutput();
159+
resetBoundaryLLMCalls();
160+
setBoundaryLLMOutput(DEPOSIT_SDIVF_BOUNDARY_OUTPUT);
161+
});
162+
163+
it(
164+
'runs all five deposit SDIVF phases and leaves measured options + finish upload on the shared execution',
165+
async () => {
166+
const execution = new Execution('deposit-sdivf-integration');
167+
168+
await synthesizeAssetPacksPipeline(
169+
{
170+
mode: 'deposit',
171+
synthesizeMode: 'deposit',
172+
repositoryFullName: 'engineeredsoftware/ENGI',
173+
sourceBranch: 'main',
174+
sourceCommit: '31bbc0c5227b6b3aed5d107fd8507d35ec22970a',
175+
repository: {
176+
owner: 'engineeredsoftware',
177+
name: 'ENGI',
178+
repo: 'ENGI',
179+
branch: 'main',
180+
commit: '31bbc0c5227b6b3aed5d107fd8507d35ec22970a',
181+
fullName: 'engineeredsoftware/ENGI',
182+
url: 'https://github.com/engineeredsoftware/ENGI',
183+
},
184+
obfuscations: 'Withhold secret signing internals.',
185+
protectedIpExclusions: [],
186+
demandContext: ['authentication'],
187+
inventory: INVENTORY,
188+
candidateKinds: [
189+
'capability-slice',
190+
'implementation-pattern',
191+
'proof-operations-slice',
192+
],
193+
},
194+
execution,
195+
);
196+
197+
const options =
198+
execution.get('implementation', 'options') ??
199+
execution.findUp('implementation', 'options');
200+
expect(Array.isArray(options)).toBe(true);
201+
expect(options.length).toBeGreaterThanOrEqual(1);
202+
expect(options[0].title).toMatch(/auth/i);
203+
expect(options[0].patch?.fileChanges?.length).toBeGreaterThan(0);
204+
205+
// Validation measure-agent attaches formal absolutes in place.
206+
expect(Array.isArray(options[0].absolutes)).toBe(true);
207+
expect(options[0].absolutes.length).toBeGreaterThan(0);
208+
expect(
209+
options[0].absolutes.some((m) => m.measurementKind === 'function-count'),
210+
).toBe(true);
211+
212+
// Product projection must accept formal absolutes (no placeholder fallback).
213+
const validated = validateDepositSynthesisOptions(options, {
214+
lens: 'deposit',
215+
inventoryPaths: INVENTORY.paths,
216+
protectedIpExclusions: [],
217+
candidateKinds: [
218+
'capability-slice',
219+
'implementation-pattern',
220+
'proof-operations-slice',
221+
],
222+
});
223+
expect(validated.candidates.length).toBeGreaterThanOrEqual(1);
224+
expect(
225+
validated.candidates[0].measurements.map((m) => m.measurementKind),
226+
).not.toContain('source-coverage');
227+
228+
const upload =
229+
execution.get('finish', 'uploadForReview') ??
230+
execution.findUp('finish', 'uploadForReview');
231+
expect(upload?.success).toBe(true);
232+
expect(upload?.deliveryMechanism).toBe('bitcode-review-upload');
233+
expect(upload?.review?.surface).toBe('/deposits');
234+
},
235+
180000,
236+
);
237+
});

packages/pipelines/asset-pack/src/__tests__/synthesize-asset-packs-phase-rosters.test.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -90,19 +90,20 @@ describe('per-mode agent rosters (conditional runtime registries)', () => {
9090
},
9191
);
9292

93-
it('implementation registers the mode-appropriate synthesis agent under the SHARED key', async () => {
94-
const sharedKey = 'implementation:ReadFitsFindingSynthesisAssetPackSynthesisAgent';
93+
it('implementation registers deposit-named vs read-era synthesis keys by mode', async () => {
94+
const depositKey = 'implementation:deposit-asset-pack-synthesis';
95+
const readKey = 'implementation:ReadFitsFindingSynthesisAssetPackSynthesisAgent';
9596

9697
const depositRegistry = fakeRegistry();
9798
registerImplementationAgents(depositRegistry, 'deposit');
98-
expect(Array.from(depositRegistry.entries.keys())).toEqual([sharedKey]);
99-
expect(await resolveEntry(depositRegistry.entries.get(sharedKey)))
99+
expect(Array.from(depositRegistry.entries.keys())).toEqual([depositKey]);
100+
expect(await resolveEntry(depositRegistry.entries.get(depositKey)))
100101
.toBe(depositAssetPackSynthesisAgent);
101102

102103
const readRegistry = fakeRegistry();
103104
registerImplementationAgents(readRegistry, 'read');
104-
expect(Array.from(readRegistry.entries.keys())).toEqual([sharedKey]);
105-
expect(await resolveEntry(readRegistry.entries.get(sharedKey)))
105+
expect(Array.from(readRegistry.entries.keys())).toEqual([readKey]);
106+
expect(await resolveEntry(readRegistry.entries.get(readKey)))
106107
.toBe(readFitsFindingSynthesisAssetPackSynthesisAgent);
107108
});
108109

@@ -228,7 +229,8 @@ const READ_DISCOVERY_KEYS = [
228229
'discovery:plan-implementation',
229230
'discovery:assess-complexity',
230231
];
231-
const IMPLEMENTATION_KEY = 'implementation:ReadFitsFindingSynthesisAssetPackSynthesisAgent';
232+
const DEPOSIT_IMPLEMENTATION_KEY = 'implementation:deposit-asset-pack-synthesis';
233+
const READ_IMPLEMENTATION_KEY = 'implementation:ReadFitsFindingSynthesisAssetPackSynthesisAgent';
232234
const READ_VALIDATION_KEYS = [
233235
'validation:validate-last-iterations-validation-phase',
234236
'validation:validate-discovery-phase',
@@ -271,12 +273,14 @@ describe('phase delegators execute the mode roster in order (execution-tree walk
271273
expect(calls).toEqual(READ_DISCOVERY_KEYS);
272274
});
273275

274-
it('implementation resolves the single shared synthesis key in both modes', async () => {
276+
it('implementation resolves the mode-appropriate synthesis key', async () => {
275277
for (const mode of ['deposit', 'read'] as const) {
276-
const { calls, root } = harness(mode, [IMPLEMENTATION_KEY]);
278+
const key =
279+
mode === 'deposit' ? DEPOSIT_IMPLEMENTATION_KEY : READ_IMPLEMENTATION_KEY;
280+
const { calls, root } = harness(mode, [key]);
277281
const output = await implementationPhase({ seed: true }, root.child('seq-2'));
278-
expect(calls).toEqual([IMPLEMENTATION_KEY]);
279-
expect(output[`ran:${IMPLEMENTATION_KEY}`]).toBe(true);
282+
expect(calls).toEqual([key]);
283+
expect(output[`ran:${key}`]).toBe(true);
280284
}
281285
});
282286

packages/pipelines/asset-pack/src/agents/validation/asset-pack-ready-to-finish-agent.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -157,16 +157,20 @@ export default async function readyToFinishWithShortCircuit(input: any, executio
157157
qualityMetrics: {}
158158
};
159159

160-
// Execute the agent
161-
const result = await readyToFinishAgent(finishInput, execution);
160+
// Execute the agent — factoryAgentWithPTRR returns an envelope
161+
// {context, output, finalOutput}; typed fields live ONLY inside it (F26-A/F27).
162+
const raw = await readyToFinishAgent(finishInput, execution);
163+
const result = ((raw as any)?.finalOutput ??
164+
(raw as any)?.output ??
165+
raw) as Output;
162166

163167
// Define critical failure thresholds
164168
const QUALITY_THRESHOLD = 0.5;
165169
const CONFIDENCE_THRESHOLD = 0.6;
166-
const CRITICAL_RISK = result.assessment.riskLevel === 'critical';
167-
const POOR_QUALITY = result.assessment.qualityLevel === 'poor';
168-
const LOW_CONFIDENCE = result.confidence < CONFIDENCE_THRESHOLD;
169-
const LOW_QUALITY = result.metrics.overallScore < QUALITY_THRESHOLD;
170+
const CRITICAL_RISK = result.assessment?.riskLevel === 'critical';
171+
const POOR_QUALITY = result.assessment?.qualityLevel === 'poor';
172+
const LOW_CONFIDENCE = (result.confidence ?? 0) < CONFIDENCE_THRESHOLD;
173+
const LOW_QUALITY = (result.metrics?.overallScore ?? 0) < QUALITY_THRESHOLD;
170174

171175
// Check if we should short-circuit
172176
const shouldShortCircuit =
@@ -175,7 +179,7 @@ export default async function readyToFinishWithShortCircuit(input: any, executio
175179
POOR_QUALITY ||
176180
LOW_CONFIDENCE ||
177181
LOW_QUALITY ||
178-
result.criticalIssues.length > 0;
182+
(result.criticalIssues?.length ?? 0) > 0;
179183

180184
if (shouldShortCircuit) {
181185
// Persist readiness signal for header rendering
@@ -195,7 +199,7 @@ export default async function readyToFinishWithShortCircuit(input: any, executio
195199
if (POOR_QUALITY) reasons.push('Poor quality assessment');
196200
if (LOW_CONFIDENCE) reasons.push(`Low confidence (${result.confidence.toFixed(2)})`);
197201
if (LOW_QUALITY) reasons.push(`Low quality score (${result.metrics.overallScore.toFixed(2)})`);
198-
if (result.criticalIssues.length > 0) {
202+
if ((result.criticalIssues?.length ?? 0) > 0) {
199203
reasons.push(`Critical issues: ${result.criticalIssues.slice(0, 3).join(', ')}`);
200204
}
201205

0 commit comments

Comments
 (0)