Skip to content

Commit 5d52d3a

Browse files
wip v28
1 parent 3752622 commit 5d52d3a

5 files changed

Lines changed: 115 additions & 22 deletions

File tree

BITCODE_SPEC_V28.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,12 @@ Read/Fit result review remains fail-closed:
504504
parsed/typed output, usage/timing metadata, and phase/agent/step/failsafe
505505
correlation. A harness failure must still export the execution tree so
506506
operators can debug the last successful sub-execution.
507+
- tool telemetry includes both direct `tool-use` emissions and stored
508+
`tools/*` status results from pipeline delivery work. Depository search,
509+
evidence verification, branch creation, file write, and pull-request creation
510+
must appear in the generic event stream and, when database streaming is
511+
enabled, in `deliverable_pipeline_tool_executions` with sanitized input,
512+
output, error, and phase/agent/step correlation.
507513
- artifact telemetry must subscribe to stream events even when database
508514
streaming is disabled; database persistence is an additional acceptance gate,
509515
not the only way to inspect a live failed run.

BITCODE_V28_QA.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -923,6 +923,11 @@ First-run execution boundary:
923923
provider/name, usage tokens, phase, agent, step, failsafe, and generation
924924
context. Missing prompts, missing parsed outputs, or uncorrelated
925925
generation/tool rows are blockers for debugging live Read/Fit execution.
926+
- Structured tool telemetry must include both direct tool-use events and stored
927+
`tools/*` status results emitted by delivery work. Depository search,
928+
evidence verification, branch creation, file writes, and pull-request
929+
creation must be visible in the stream and, when database streaming is on,
930+
in `deliverable_pipeline_tool_executions` without secret-bearing inputs.
926931
- Failed harness runs must still export `evidence.json` with the execution tree
927932
and the last visible stream event summaries. A failing command with no
928933
prompt/context, raw output, parsed/cast output, or phase/agent correlation is

packages/pipeline-hosts/README.md

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,12 @@ verification readback tool for risk-admission and readiness work; a live
5858
`Tool not found` result for that tool is a registry failure, not acceptable
5959
blocked-readiness evidence. Tool-result events must carry summarized input
6060
and output/error so the database row and Terminal stream can show what the
61-
agent asked and what the tool returned. Artifact telemetry must preserve tool
62-
name, ok/error state, and input/output/error presence so the execution log can
63-
identify each phase-agent-step tool invocation.
61+
agent asked and what the tool returned. Stored `tools/*` status results from
62+
finish delivery are tool executions too; branch creation, file writes, and
63+
pull-request creation must land in the same structured table as explicit
64+
`tool-use` events. Artifact telemetry must preserve tool name, ok/error state,
65+
and input/output/error presence so the execution log can identify each
66+
phase-agent-step tool invocation.
6467
Failed runs must still export `evidence.json` with the execution tree and
6568
last-known stream events so staging operators can see which SDIVF phase, PTRR
6669
agent, generation, or tool last produced input/output evidence.

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

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,4 +240,49 @@ describe('enablePipelineStreaming + Execution emits DB events (snapshot stream)'
240240
tool_error: null,
241241
});
242242
});
243+
244+
it('persists stored delivery tool status events into structured tool execution rows', async () => {
245+
const supabase = new MockSupabase() as any;
246+
const runId = '55555555-5555-4555-8555-555555555555';
247+
const userId = '66666666-6666-4666-8666-666666666666';
248+
const agentName = 'finish:asset-pack-delivery-agent';
249+
250+
const exec = new Execution(`exec-${runId}`);
251+
const streamer = enablePipelineStreaming(exec as any, {
252+
runId,
253+
userId,
254+
supabase,
255+
structuredToDatabase: true,
256+
});
257+
258+
await ExecutionStreamAdapter.emitEvent(exec.id, 'phase-start' as any, {
259+
phase: 'finish',
260+
});
261+
await ExecutionStreamAdapter.emitEvent(exec.id, 'agent-start' as any, {
262+
agent: agentName,
263+
executionState: { phase: 'finish', agent: agentName, step: 'try' },
264+
});
265+
await ExecutionStreamAdapter.emitEvent(exec.id, 'status' as any, {
266+
namespace: 'tools',
267+
key: 'vcs_create_pull_request:2',
268+
executionState: { phase: 'finish', agent: agentName, step: 'try' },
269+
data: {
270+
tool: 'vcs_create_pull_request',
271+
ok: true,
272+
input: { repository: 'engineeredsoftware/ENGI', baseBranch: 'main' },
273+
output: { pullRequestNumber: 4, pullRequestUrl: 'https://github.com/engineeredsoftware/ENGI/pull/4' },
274+
},
275+
});
276+
277+
await (streamer as any).flushStructuredWrites?.();
278+
279+
expect(supabase.tables.deliverable_pipeline_tool_executions).toHaveLength(1);
280+
expect(supabase.tables.deliverable_pipeline_tool_executions[0]).toMatchObject({
281+
agent_step_id: 'deliverable_pipeline_agent_steps-1',
282+
tool_name: 'vcs_create_pull_request',
283+
tool_input: { repository: 'engineeredsoftware/ENGI', baseBranch: 'main' },
284+
tool_output: { pullRequestNumber: 4, pullRequestUrl: 'https://github.com/engineeredsoftware/ENGI/pull/4' },
285+
tool_error: null,
286+
});
287+
});
243288
});

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

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,42 @@ export function enablePipelineStreaming(
211211
context.generation || 'none',
212212
].join(':');
213213

214+
const normalizeToolExecutionEvent = (event: any) => {
215+
const type = String(event?.type || '');
216+
if (type === 'tool-use') {
217+
return {
218+
toolName: event?.data?.tool || event?.metadata?.toolName || 'tool',
219+
input: event?.data?.input ?? null,
220+
output: event?.data?.output ?? null,
221+
error: event?.data?.error ?? null,
222+
};
223+
}
224+
225+
const isStoredToolResult =
226+
type === 'status' &&
227+
event?.namespace === 'tools' &&
228+
event?.data &&
229+
typeof event.data === 'object' &&
230+
event.data.tool;
231+
232+
if (!isStoredToolResult) {
233+
return null;
234+
}
235+
236+
return {
237+
toolName: event.data.tool,
238+
input: event.data.input ?? null,
239+
output: event.data.output ?? null,
240+
error: event.data.error ?? null,
241+
};
242+
};
243+
244+
const normalizeToolError = (error: unknown) => {
245+
if (!error) return null;
246+
if (typeof error === 'object') return error;
247+
return { message: String(error) };
248+
};
249+
214250
const markDeliverableRun = async (status: 'completed' | 'failed', payload?: any) => {
215251
if (!(await ensureDeliverableRun())) return;
216252
const now = new Date().toISOString();
@@ -261,6 +297,23 @@ export function enablePipelineStreaming(
261297

262298
await persistDeliverableEvent(event, phaseName, agentName);
263299

300+
const structuredTool = normalizeToolExecutionEvent(event);
301+
if (structuredTool && await ensureDeliverableRun()) {
302+
const phaseId = phaseState.currentPhaseId || (phaseName ? phaseIdByName.get(phaseName) : null);
303+
const key = agentStepKey(phaseId ?? null, phaseName, agentName, stepType);
304+
const agentStepId = agentStepMap.get(key) || null;
305+
const row: import('../types/db').DPToolExecInsert = {
306+
agent_step_id: agentStepId,
307+
substep_id: null,
308+
tool_name: structuredTool.toolName as any,
309+
tool_input: structuredTool.input as any,
310+
tool_output: structuredTool.output as any,
311+
tool_error: normalizeToolError(structuredTool.error) as any,
312+
created_at: now as any
313+
} as any;
314+
await insertRow('deliverable_pipeline_tool_executions', row);
315+
}
316+
264317
if (type === 'phase-start') {
265318
if (!(phaseName && await ensureDeliverableRun())) return;
266319
const row: import('../types/db').DPPhaseDelegationInsert = {
@@ -350,25 +403,6 @@ export function enablePipelineStreaming(
350403
created_at: now as any
351404
} as any;
352405
await insertRow('deliverable_pipeline_generations', row);
353-
} else if (type === 'tool-use') {
354-
if (!(await ensureDeliverableRun())) return;
355-
const phaseId = phaseState.currentPhaseId || (phaseName ? phaseIdByName.get(phaseName) : null);
356-
const key = agentStepKey(phaseId ?? null, phaseName, agentName, stepType);
357-
const agentStepId = agentStepMap.get(key) || null;
358-
const toolName = event?.data?.tool || event?.metadata?.toolName || 'tool';
359-
const toolInput = event?.data?.input || null;
360-
const toolOutput = event?.data?.output || null;
361-
const toolError = event?.data?.error ? { message: event.data.error } : null;
362-
const row: import('../types/db').DPToolExecInsert = {
363-
agent_step_id: agentStepId,
364-
substep_id: null,
365-
tool_name: toolName as any,
366-
tool_input: toolInput as any,
367-
tool_output: toolOutput as any,
368-
tool_error: toolError as any,
369-
created_at: now as any
370-
} as any;
371-
await insertRow('deliverable_pipeline_tool_executions', row);
372406
} else if (type === 'status') {
373407
if (event?.namespace === 'llm' && event?.key === 'input') {
374408
const messages = event?.data?.messages;

0 commit comments

Comments
 (0)