Skip to content

Commit 0072df2

Browse files
wip v28
1 parent 4ced691 commit 0072df2

16 files changed

Lines changed: 414 additions & 24 deletions

BITCODE_V28_QA.md

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,6 +1256,40 @@ Commercial blockers:
12561256
- Terminal enables settlement, minting, branch materialization, BTC fee
12571257
broadcast, or ledger finality from a host smoke result.
12581258
1259+
Observed staging-testnet harness evidence on 2026-05-17:
1260+
1261+
- Vercel Sandbox run `sbx_ktb5Z6VnP5A16m9k4a0FkBcJg1d3` completed all six
1262+
host commands and exported artifacts to
1263+
`.bitcode/pipeline-harness-runs/2026-05-17T16-37-38-466Z-sbx_ktb5Z6VnP5A16m9k4a0FkBcJg1d3/`.
1264+
- Pipeline run `d21240bd-ebc7-41ae-b082-06d7beb244a7` returned
1265+
`pipelineResultState='worthy_fit'` and final `resultState='blocked_readiness'`
1266+
because the run used `BITCODE_SANDBOX_APPLY_LOCAL_PATCH=1`.
1267+
- The synthesized AssetPack evidence was source-bound to
1268+
`engineeredsoftware/ENGI@main@4ced69180958143254639a8a5af94c0991545a91`,
1269+
selected candidate `manual-deposit-qa`, query root
1270+
`sha256:1d58f3d0e16af0700a702b2b307c744145a9196ac63841345a20ff597f5a3ca3`,
1271+
ranking root
1272+
`sha256:c04599ac4782dadf3b63842381dd20c76f577401cc35d82fbee4a63797496aa1`,
1273+
and embedding policy `openai text-embedding-3-small`, 1536 dimensions,
1274+
`match_deliverable_vectors`, cosine.
1275+
- Supabase readback showed `pipeline_runs.status='completed'`,
1276+
`deliverable_pipeline_runs.status='completed'`, 603 deliverable pipeline
1277+
events, 12 completed phase rows, 40 completed agent-step rows, and no running
1278+
phase or agent rows for the run.
1279+
- The default harness path uses deterministic setup/discovery/synthesis/
1280+
validation/finish agents unless a phase-specific `*_USE_PTRR=1` flag is set;
1281+
therefore this observed run had zero generation rows and zero tool execution
1282+
rows. A model/tool-mediated commercial Fit claim still requires generation
1283+
and tool rows with prompt, raw response, parsed/cast output, provider/model,
1284+
usage, and phase/agent/step correlation.
1285+
- Ledger readback correctly showed zero BTD range, BTC fee, journal, anchor, and
1286+
crypto telemetry rows, and `btd_supply_state.total_minted=0`, because source
1287+
overlay QA evidence cannot mint BTD, claim BTC fee settlement, or anchor
1288+
finality.
1289+
- The next promotion step is a clean source-revision run after these pipeline
1290+
harness changes are deployed in the deposited source revision. Only a clean
1291+
no-overlay run may write and read back ledger settlement rows.
1292+
12591293
## 2026-05-13 Staging Deployment Readiness Gate
12601294
12611295
Purpose:

packages/pipeline-hosts/src/asset-pack-harness.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1390,6 +1390,7 @@ try {
13901390
...postprocessedOutput,
13911391
}
13921392
: rawOutput;
1393+
await pipelineStreamer.flushStructuredWrites?.();
13931394
const pipelineResultState = normalizeResultState(
13941395
output?.resultState || output?.fitResult?.resultState || output?.fit?.resultState
13951396
);

packages/pipelines-generics/src/execution/PipelineExecutor.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
ShortCircuitSignal
1414
} from '@bitcode/execution-generics';
1515
import { Executor } from '@bitcode/execution-generics';
16+
import { resolveRegisteredAgent } from './resolve-agent';
1617

1718
export interface PhaseConfig {
1819
phaseName: string;
@@ -54,10 +55,7 @@ export class PipelineExecutor {
5455
*/
5556
async executeAgent(agentName: string, input: any): Promise<any> {
5657
// Get agent from registry
57-
const agent = this.execution.agents.getAgent(agentName);
58-
if (!agent) {
59-
throw new Error(`Agent not found: ${agentName}`);
60-
}
58+
const agent = await resolveRegisteredAgent(agentName, this.execution.agents.getAgent(agentName) as any);
6159
const phase = String(this.execution.get('phase', 'current') || 'setup');
6260
const step = 'try';
6361
this.execution.store('agent', 'name', agentName);

packages/pipelines-generics/src/execution/__tests__/events.unit.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// @ts-nocheck
33
import { PipelineExecution } from '../PipelineExecution';
44
import { PipelineExecutor } from '../PipelineExecutor';
5+
import { createAgentExecutor } from '../agent-executor';
56
import { ExecutionStreamAdapter } from '@bitcode/execution-generics';
67
import { Streamer } from '@bitcode/streams';
78

@@ -86,4 +87,28 @@ describe('PipelineExecutor event emission (unit)', () => {
8687
{ explicit: true },
8788
]);
8889
});
90+
91+
it('resolves lazy registered agents before executing through generic executors', async () => {
92+
const exec = new PipelineExecution('pipeline:lazy-agent');
93+
const phaseInput = { read: 'fit deposited source revision' };
94+
95+
(exec as any).agents.registerAgent('unit:lazy-executor', (() =>
96+
Promise.resolve(async (input: any, execution: any) => {
97+
execution.store('lazy-agent', 'executor-input', input);
98+
return { ok: true, read: input.read };
99+
})) as any);
100+
(exec as any).agents.registerAgent('unit:lazy-phase', (() =>
101+
Promise.resolve(async (input: any, execution: any) => {
102+
execution.store('lazy-agent', 'phase-input', input);
103+
return { ok: true, read: input.read };
104+
})) as any);
105+
106+
const executorOutput = await createAgentExecutor('unit:lazy-executor')(phaseInput, exec);
107+
const phaseOutput = await new PipelineExecutor(exec).executeAgent('unit:lazy-phase', phaseInput);
108+
109+
expect(executorOutput).toEqual({ ok: true, read: phaseInput.read });
110+
expect(phaseOutput).toEqual({ ok: true, read: phaseInput.read });
111+
expect(exec.get('lazy-agent', 'executor-input')).toBe(phaseInput);
112+
expect(exec.get('lazy-agent', 'phase-input')).toBe(phaseInput);
113+
});
89114
});

packages/pipelines-generics/src/execution/agent-executor.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@ import { Executor } from '@bitcode/execution-generics';
77
import { PipelineExecution } from './PipelineExecution';
88
import { log } from '@bitcode/logger';
99
import { isExecutionDebugEnabled } from './debug';
10+
import { resolveRegisteredAgent } from './resolve-agent';
1011

1112
export function createAgentExecutor(agentName: string): Executor<any, any> {
1213
return async (input: any, execution: any) => {
1314
const px = execution as PipelineExecution;
14-
const agent = px.agents.getAgent(agentName);
15-
if (!agent) throw new Error(`Agent not found: ${agentName}`);
15+
const agent = await resolveRegisteredAgent(agentName, px.agents.getAgent(agentName) as any);
1616
const phase = String(px.get('phase', 'current') || 'setup');
1717
const step = 'try';
1818
px.store('agent', 'name', agentName);
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import type { ExecutionAgent } from './PipelineAgentRegistry';
2+
3+
type RegisteredAgent = ExecutionAgent | (() => Promise<ExecutionAgent | { default?: ExecutionAgent }>);
4+
5+
export async function resolveRegisteredAgent(
6+
agentName: string,
7+
registeredAgent: RegisteredAgent | undefined
8+
): Promise<ExecutionAgent> {
9+
if (!registeredAgent) {
10+
throw new Error(`Agent not found: ${agentName}`);
11+
}
12+
13+
if (typeof registeredAgent !== 'function') {
14+
throw new Error(`Registered agent is not executable: ${agentName}`);
15+
}
16+
17+
if (registeredAgent.length === 0) {
18+
const loaded = await (registeredAgent as () => Promise<ExecutionAgent | { default?: ExecutionAgent }>)();
19+
if (typeof loaded === 'function') {
20+
return loaded;
21+
}
22+
if (typeof loaded?.default === 'function') {
23+
return loaded.default;
24+
}
25+
}
26+
27+
return registeredAgent as ExecutionAgent;
28+
}

packages/pipelines-generics/src/streaming/__tests__/forkable-execution.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ describe('enablePipelineStreaming + Execution emits DB events (snapshot stream)'
102102
const userId = '22222222-2222-4222-8222-222222222222';
103103

104104
const exec = new Execution(`exec-${runId}`);
105-
enablePipelineStreaming(exec as any, {
105+
const streamer = enablePipelineStreaming(exec as any, {
106106
runId,
107107
userId,
108108
supabase,
@@ -177,6 +177,7 @@ describe('enablePipelineStreaming + Execution emits DB events (snapshot stream)'
177177
phase: 'setup',
178178
data: { status: 'completed' },
179179
});
180+
await (streamer as any).flushStructuredWrites?.();
180181

181182
expect(supabase.tables.deliverable_pipeline_runs).toHaveLength(1);
182183
expect(supabase.tables.deliverable_pipeline_phase_delegations).toHaveLength(1);

packages/pipelines-generics/src/streaming/pipeline-stream-integration.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ export function enablePipelineStreaming(
111111
const deliverableRunId = String(config.runId || '');
112112
const canPersistDeliverableHierarchy = uuidRe.test(deliverableRunId);
113113
let deliverableRunReady: Promise<boolean> | null = null;
114+
let structuredWriteQueue: Promise<unknown> = Promise.resolve();
114115

115116
const insertRow = async (table: string, row: any, returningId = false) => {
116117
const query = (supabase as any).from(table).insert(row);
@@ -222,7 +223,36 @@ export function enablePipelineStreaming(
222223
}, 'id', deliverableRunId).catch(() => {});
223224
};
224225

225-
streamer.subscribe(async (event: any) => {
226+
const completeOpenHierarchyRows = async (now: string) => {
227+
if (!(await ensureDeliverableRun())) return;
228+
const { data: openPhases } = await (supabase as any)
229+
.from('deliverable_pipeline_phase_delegations')
230+
.select('id')
231+
.eq('run_id', deliverableRunId)
232+
.eq('status', 'running');
233+
const openPhaseIds = Array.isArray(openPhases)
234+
? openPhases.map((row: any) => row?.id).filter(Boolean)
235+
: [];
236+
if (!openPhaseIds.length) return;
237+
await (supabase as any)
238+
.from('deliverable_pipeline_agent_steps')
239+
.update({
240+
completed_at: now,
241+
status: 'completed',
242+
})
243+
.in('phase_delegation_id', openPhaseIds)
244+
.eq('status', 'running');
245+
await (supabase as any)
246+
.from('deliverable_pipeline_phase_delegations')
247+
.update({
248+
completed_at: now,
249+
status: 'completed',
250+
})
251+
.in('id', openPhaseIds)
252+
.eq('status', 'running');
253+
};
254+
255+
const persistStructuredEvent = async (event: any) => {
226256
try {
227257
const type = String(event?.type || '');
228258
const context = normalizeEventContext(event);
@@ -414,13 +444,21 @@ export function enablePipelineStreaming(
414444

415445
// If Finish phase completes, mark run completed.
416446
if (type === 'phase-complete' && phaseName === 'finish') {
447+
await completeOpenHierarchyRows(now);
417448
await markDeliverableRun('completed', event?.data ?? event);
418449
await updateRows('executions', { status: 'completed', completed_at: now }, 'id', config.runId).catch(() => {});
419450
}
420451
} catch (err) {
421452
console.error('Structured stream persistence error', err);
422453
}
454+
};
455+
456+
streamer.subscribe((event: any) => {
457+
structuredWriteQueue = structuredWriteQueue
458+
.catch(() => {})
459+
.then(() => persistStructuredEvent(event));
423460
});
461+
(streamer as any).flushStructuredWrites = () => structuredWriteQueue.catch(() => {});
424462
}
425463

426464
// Note: Cleanup should be handled by the pipeline implementation
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// @ts-nocheck
2+
import assetPackSynthesizeArtifacts from '../agents/implementation/asset-pack-synthesize-artifacts-agent';
3+
import { Execution } from '@bitcode/execution-generics';
4+
5+
describe('assetPackSynthesizeArtifacts', () => {
6+
it('recovers Read, source revision, and fit evidence from execution context', async () => {
7+
const root = new Execution('pipeline:asset-pack');
8+
root.store('read', 'request', {
9+
prompt: 'Read the deposited source revision for terminal closure.',
10+
});
11+
root.store('harness', 'sourceRevision', {
12+
repositoryFullName: 'engineeredsoftware/ENGI',
13+
branch: 'main',
14+
commit: 'abc123',
15+
});
16+
root.store('fit', 'result', {
17+
resultState: 'worthy_fit',
18+
selectedCandidateAssetIds: ['deposit-1'],
19+
queryRoot: 'sha256:query',
20+
rankingRoot: 'sha256:ranking',
21+
embeddingPolicy: { provider: 'openai', model: 'text-embedding-3-small' },
22+
});
23+
24+
const phase = root.child('phase:implementation');
25+
const result = await assetPackSynthesizeArtifacts({ overallComplexity: 'moderate' }, phase);
26+
27+
expect(result.assetPack.read).toBe('Read the deposited source revision for terminal closure.');
28+
expect(result.assetPackSynthesisArtifacts.summary).toContain('engineeredsoftware/ENGI@main@abc123');
29+
expect(result.assetPackSynthesisArtifacts.summary).toContain('worthy_fit with 1 selected candidate');
30+
expect(result.assetPackSynthesisArtifacts.proofEvidence).toEqual(
31+
expect.arrayContaining([
32+
'Selected candidate assets: deposit-1',
33+
'Query root: sha256:query',
34+
'Ranking root: sha256:ranking',
35+
'Embedding policy: openai text-embedding-3-small',
36+
])
37+
);
38+
});
39+
});

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

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,32 @@ describe('normalizeAssetPackOutput', () => {
8585
});
8686
});
8787

88+
it('preserves implementation artifacts when postprocess runs from a sibling execution node', () => {
89+
const root = new Execution('pipeline:asset-pack');
90+
const implementation = root.child('phase:implementation');
91+
const finish = root.child('phase:finish');
92+
implementation.store('implementation', 'assetPackSynthesisArtifacts', {
93+
summary: 'Sibling implementation AssetPack artifacts are authoritative.',
94+
fileChanges: { edited: 0, created: 1, deleted: 0 },
95+
proofEvidence: ['sibling-implementation-read'],
96+
});
97+
finish.store('finish/asset_pack_completion', 'assetPackSynthesisArtifacts', {
98+
summary: 'Finish wrapper should not override implementation artifacts.',
99+
fileChanges: { edited: 0, created: 0, deleted: 0 },
100+
proofEvidence: ['finish-wrapper'],
101+
});
102+
103+
const normalized = normalizeAssetPackOutput({ success: true, summary: '' } as any, finish);
104+
const result = buildAssetPackPostprocessedResult(finish, normalized);
105+
106+
expect(normalized.assetPackSynthesisArtifacts?.summary).toBe(
107+
'Sibling implementation AssetPack artifacts are authoritative.'
108+
);
109+
expect(result.summary).toBe('Sibling implementation AssetPack artifacts are authoritative.');
110+
expect(result.title).toBe('Sibling implementation AssetPack artifacts are authoritative.');
111+
expect(result.assetPackSynthesisArtifacts?.proofEvidence).toEqual(['sibling-implementation-read']);
112+
});
113+
88114
it('uses pull-request delivery mechanisms as canonical shippable evidence for the written asset', () => {
89115
const exec = new Execution('pipeline:asset-pack');
90116
exec.store('execution', 'id', 'exec-2');

0 commit comments

Comments
 (0)