Skip to content

Commit 061048d

Browse files
V48 Gate 3 (implementation-only): telemetry display sweep — naming laws, detail-gated chevrons, DIV iteration markers, deterministic tool-row names
Naming laws (display only; machine/wire names unchanged): - Agent pills drop the 'Agent' suffix and the ':deposit'/':read' lens qualifier ('ASSET PACK MEASURE ABSOLUTES AGENT:DEPOSIT' -> 'ASSET PACK MEASURE ABSOLUTES'); the processing sentence reads 'agent Input Comprehension is Trying' (agent-prefixed clause, suffix trimmed). - 'Inherent Regurgitation' displays as 'Training Regurgitation'. - Failsafe pills: 'handle large inputs' -> 'Handle Prompts', 'handle large outputs' -> 'Handle Completions' (sentence nouns follow: 'Reasoning over Prompts', 'Judging the Completions'). - Thinkings pill: 'Structured Output' -> 'Structure'. - CS badges follow SC's xN pattern: '· chunk ×N' beside '· stitch ×N'. Log rows: - The Initializing empty-state renders as a regular collapsed row (was a mismatched sky-tinted block) and rows show a chevron / toggle only when a detail payload exists. - DIV-loop iteration (1-based, latched from phase/iteration // pipeline/currentIteration stores, cleared at Finish) stamps onto every row ('iter N' beside the timestamp) and renders beside the run clock in /deposit's Telemetry header. - Tool rows take their name from the execution node id ('…/tool:<Name>') deterministically: run 61c3b0cf's events show same-millisecond scrambling delivering a call's tool/name store after its result, which left rows named just 'tool' (no duplication — all rows were registry-level events). ReadyToFinish verify (no change needed): the DIV loop re-iterates ONLY while the gate returns false; the default gate reads the cross-phase validation:readyToFinish artifact's finalApproval (plus validation:passed / threaded passed|ready|finalApproval), and the agent's prompts frame a binary admit-into-Finish vs short-circuit decision — a satisfied first pass exits after one iteration. Verified: uapi 155 suites / 605 tests green (new pins for every rename, the node-id tool naming, and the iteration latch/stamp/clear); tsc clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent d0c86c7 commit 061048d

10 files changed

Lines changed: 191 additions & 79 deletions

uapi/app/deposit/DepositPageClient.tsx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1450,6 +1450,14 @@ export default function DepositPageClient() {
14501450
endedAtMs={synthesisRunEndMs}
14511451
className="font-mono text-[0.72rem] text-emerald-100/90"
14521452
/>
1453+
{typeof synthesisActivity.currentIteration === "number" && (
1454+
<span
1455+
title="DIV loop iteration (Discovery → Implementation → Validation)"
1456+
className="border border-emerald-300/15 bg-emerald-300/10 px-3 py-2 font-mono text-[0.62rem] uppercase tracking-[0.16em] text-emerald-100"
1457+
>
1458+
iter {synthesisActivity.currentIteration}
1459+
</span>
1460+
)}
14531461
<span className="border border-white/10 bg-black/30 px-3 py-2 font-mono text-[0.62rem] text-neutral-400">
14541462
{synthesisRunId}
14551463
</span>

uapi/app/terminal/terminal-run-activity.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ export interface TerminalRunActivitySnapshot {
2828
* Null until the store event arrives.
2929
*/
3030
mode: 'deposit' | 'read' | null;
31+
/**
32+
* The DIV loop's CURRENT iteration (1-based, latched from the
33+
* pipeline/currentIteration store) — null before the loop starts and once
34+
* the finish phase begins. Drives the header's 'iter N' marker.
35+
*/
36+
currentIteration: number | null;
3137
/**
3238
* The CURRENT active call-chain: the rolling Phase→Agent→Step→Failsafe→
3339
* Generation context after the last streamed event — drives live header
@@ -104,6 +110,8 @@ interface ExecContext {
104110
step?: string | null;
105111
failsafe?: string | null;
106112
generation?: string | null;
113+
/** DIV-loop iteration (1-based), latched from pipeline/currentIteration. */
114+
iteration?: number | null;
107115
}
108116

109117
function readEventExecutionState(payload: any): ExecContext {
@@ -145,6 +153,17 @@ function updateRollingContext(ctx: ExecContext, payload: any): void {
145153
}
146154
if (payload?.type === 'phase' && payload?.phase) ctx.phase = String(payload.phase);
147155
if (payload?.type === 'agent' && payload?.agent) ctx.agent = String(payload.agent);
156+
157+
// DIV-loop iteration (1-based): the SDIVF executor variant stores
158+
// phase/iteration at each observed D/I/V phase start; the classic variant
159+
// stores pipeline/currentIteration at the top of each pass. Setup precedes
160+
// the first store and Finish runs outside the loop, so clear the latch once
161+
// the finish phase starts.
162+
if ((ns === 'phase' && key === 'iteration') || (ns === 'pipeline' && key === 'currentIteration')) {
163+
const iteration = Number(value);
164+
if (Number.isFinite(iteration) && iteration > 0) ctx.iteration = iteration;
165+
}
166+
if (String(ctx.phase || '').toLowerCase().includes('finish')) ctx.iteration = null;
148167
}
149168

150169
type FormalLogLineKind = 'llm' | 'tool';
@@ -311,6 +330,7 @@ export function buildTerminalRunActivityFromEvents(
311330
step: own.step ?? rollingContext.step ?? null,
312331
failsafe: own.failsafe ?? null,
313332
generation: own.generation ?? null,
333+
iteration: rollingContext.iteration ?? null,
314334
};
315335
const text = String(payload?.message || payload?.status?.message || '[content withheld — source-safe]');
316336
pushRow(text, stampExecutionState(merged, { ...payload, type: 'generation' }, deriveFailsafeRepairMarkers(payload)));
@@ -319,13 +339,27 @@ export function buildTerminalRunActivityFromEvents(
319339

320340
if (kind === 'tool') {
321341
const acc = toolByNode.get(nodeId) || {};
342+
// The execution node id carries the tool identity ('…/tool:<Name>')
343+
// deterministically; the name-store accumulator is only a fallback —
344+
// same-millisecond event scrambling can deliver a call's 'name' store
345+
// after its 'result', which used to leave rows named just 'tool'.
346+
const nodeToolSegment = nodeId
347+
.split('/')
348+
.reverse()
349+
.find((segment) => segment.startsWith('tool:'));
350+
const toolNameFromNode = nodeToolSegment ? nodeToolSegment.slice('tool:'.length) : '';
322351
const toolName =
323-
acc.name || payload?.data?.tool || payload?.metadata?.toolName || (key === 'error' ? 'tool (failed)' : 'tool');
352+
toolNameFromNode ||
353+
acc.name ||
354+
payload?.data?.tool ||
355+
payload?.metadata?.toolName ||
356+
(key === 'error' ? 'tool (failed)' : 'tool');
324357
// Tool uses have Phase/Agent/Step but no Failsafe/Thinkings.
325358
const merged: ExecContext = {
326359
phase: rollingContext.phase ?? null,
327360
agent: rollingContext.agent ?? null,
328361
step: rollingContext.step ?? null,
362+
iteration: rollingContext.iteration ?? null,
329363
};
330364
const enriched = stampExecutionState(merged, { ...payload, type: 'tool-use' }, { tool: toolName });
331365
enriched.metadata = {
@@ -366,6 +400,7 @@ export function buildTerminalRunActivityFromEvents(
366400
generation: rollingContext.generation ?? null,
367401
}
368402
: null,
403+
currentIteration: rollingContext.iteration ?? null,
369404
};
370405
}
371406

@@ -397,5 +432,6 @@ export function buildTerminalRunActivityFromMock(
397432
iterationUpdates: snapshot.iterationUpdates || [],
398433
mode: null,
399434
latestContext: null,
435+
currentIteration: null,
400436
};
401437
}

uapi/components/base/bitcode/execution/ExecutionContextPillRow.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,10 @@ export interface ExecutionContextPillRowProps {
3131
}
3232

3333
/**
34-
* Badge real failsafe-handling work on the pill label: 'stitch ×N' marks the
35-
* Nth stitch repair; 'chunk N' a chunk task generation; 'sum' the chunk
36-
* summing generation. Absent markers = the non-triggering path.
34+
* Badge real failsafe-handling work on the pill label with one shared ×N
35+
* pattern: CS counts chunks ('chunk ×N' = the Nth chunk task generation,
36+
* 'sum' the combining generation), SC counts stitches ('stitch ×N' = the Nth
37+
* repair). Absent markers = the non-triggering path.
3738
*/
3839
export function buildFailsafePillLabel(args: {
3940
failsafe: string;
@@ -47,7 +48,7 @@ export function buildFailsafePillLabel(args: {
4748
} else if (args.chunkSum) {
4849
label = `${label} · sum`;
4950
} else if (typeof args.chunkIndex === 'number') {
50-
label = `${label} · chunk ${args.chunkIndex}`;
51+
label = `${label} · chunk ×${args.chunkIndex}`;
5152
}
5253
return label;
5354
}

uapi/components/base/bitcode/execution/execution-telemetry-format.ts

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,24 @@ export function humanizeNounPhrase(value: string): string {
4545
return titleCaseWords(value.replace(/[_-]/g, ' '));
4646
}
4747

48-
// Client-side normalized failsafe names: ChunkThenSum handles LARGE INPUTS,
49-
// StitchComplete handles LARGE OUTPUTS; PrepareConciseContext displays as
50-
// 'Prepare Context' (the 'Concise' qualifier is an internal naming detail,
51-
// dropped from the label). The log title-line (pill) reads 'handle large
52-
// inputs'; the processing sentence reads title-cased without 'handle'
53-
// ('Reasoning over Large Inputs'). PrepareConciseContext reads simply
54-
// 'Context' in the sentence ('Reasoning over Context').
48+
// Client-side normalized failsafe names: ChunkThenSum handles the PROMPT side
49+
// (oversized requests), StitchComplete the COMPLETION side (incomplete
50+
// responses); PrepareConciseContext displays as 'Prepare Context' (the
51+
// 'Concise' qualifier is an internal naming detail, dropped from the label).
52+
// The log title-line (pill) reads 'Handle Prompts' / 'Handle Completions';
53+
// the processing sentence reads the bare noun ('Reasoning over Prompts',
54+
// 'Judging the Completions'). PrepareConciseContext reads simply 'Context'
55+
// in the sentence ('Reasoning over Context').
5556
export const FAILSAFE_PILL_NAMES: Record<string, string> = {
5657
prepare_concise_context: 'Prepare Context',
57-
chunk_then_sum: 'handle large inputs',
58-
stitch_until_complete: 'handle large outputs',
58+
chunk_then_sum: 'Handle Prompts',
59+
stitch_until_complete: 'Handle Completions',
5960
};
6061

6162
export const FAILSAFE_SENTENCE_NAMES: Record<string, string> = {
6263
prepare_concise_context: 'Context',
63-
chunk_then_sum: 'Large Inputs',
64-
stitch_until_complete: 'Large Outputs',
64+
chunk_then_sum: 'Prompts',
65+
stitch_until_complete: 'Completions',
6566
};
6667

6768
function normalizeFailsafeKey(value: string): string {
@@ -76,9 +77,15 @@ export function formatFailsafeSentenceName(value: string): string {
7677
return FAILSAFE_SENTENCE_NAMES[normalizeFailsafeKey(value)] || humanizeNounPhrase(value);
7778
}
7879

79-
// "structured_output" -> "Structured Output" for the generation pill.
80+
// Thinkings generation pill names: 'structured_output' reads 'Structure';
81+
// 'reason'/'judge' humanize to 'Reason'/'Judge'.
82+
export const GENERATION_PILL_NAMES: Record<string, string> = {
83+
structured_output: 'Structure',
84+
};
85+
8086
export function formatGenerationName(value: string): string {
81-
return humanizeNounPhrase(value);
87+
const key = value.trim().toLowerCase().replace(/[\s-]+/g, '_');
88+
return GENERATION_PILL_NAMES[key] || humanizeNounPhrase(value);
8289
}
8390

8491
/**
@@ -103,32 +110,53 @@ export function trimPipelineAgentName(value: string): string {
103110
return trimmed || String(value || '');
104111
}
105112

106-
// Pill label: pipeline prefix trimmed AND CamelCase split into words, keeping
107-
// the trailing 'Agent' — 'DepositInputComprehensionAgent' -> 'Input
108-
// Comprehension Agent' (the pill's CSS uppercases it to word-spaced caps).
113+
// Display-name overrides applied after humanization (raw/machine agent names
114+
// are unchanged): the regurgitation lens displays as TRAINING regurgitation.
115+
const AGENT_DISPLAY_OVERRIDES: Record<string, string> = {
116+
'inherent regurgitation': 'Training Regurgitation',
117+
};
118+
119+
function applyAgentDisplayOverrides(spacedName: string): string {
120+
return AGENT_DISPLAY_OVERRIDES[spacedName.trim().toLowerCase()] || spacedName;
121+
}
122+
123+
// Strip display-noise suffixes from an already pipeline-trimmed agent name:
124+
// the ':deposit'/':read' lens qualifier and the trailing 'Agent' word (every
125+
// pill/sentence names agents WITHOUT the 'Agent' suffix).
126+
function stripAgentSuffixes(value: string): string {
127+
return value
128+
.replace(/:[a-z][a-z0-9_-]*$/i, '')
129+
.replace(/Agent$/, '')
130+
.replace(/[-_]agent$/i, '')
131+
.trim();
132+
}
133+
134+
// Pill label: pipeline prefix + ':lens' qualifier + trailing 'Agent' trimmed,
135+
// CamelCase split into words — 'DepositInputComprehensionAgent' -> 'Input
136+
// Comprehension'; 'AssetPackMeasureAbsolutesAgent:deposit' -> 'Asset Pack
137+
// Measure Absolutes' (the pill's CSS uppercases it to word-spaced caps).
109138
export function formatAgentPillName(value: string): string {
110-
const trimmed = trimPipelineAgentName(value);
139+
const trimmed = stripAgentSuffixes(trimPipelineAgentName(value));
111140
const spaced = trimmed
112141
.replace(/[_-]/g, ' ')
113142
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
114143
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
115144
.replace(/\s{2,}/g, ' ')
116145
.trim();
117-
return spaced || trimmed;
146+
return applyAgentDisplayOverrides(spaced || trimmed);
118147
}
119148

120-
// "DepositInputComprehensionAgent" -> "Input Comprehension" (pipeline prefix
121-
// trimmed, trailing "Agent" stripped — the sentence template appends the
122-
// literal word "Agent").
149+
// "DepositInputComprehensionAgent" -> "Input Comprehension" (pipeline prefix,
150+
// ':lens' qualifier, and trailing "Agent" stripped — the sentence template
151+
// prefixes the literal word "agent").
123152
export function humanizeAgentName(value: string): string {
124-
const withoutPipeline = trimPipelineAgentName(value);
125-
const withoutTrailingAgent = withoutPipeline.replace(/Agent$/, '').replace(/-agent$/, '');
126-
const spaced = withoutTrailingAgent
153+
const withoutSuffixes = stripAgentSuffixes(trimPipelineAgentName(value));
154+
const spaced = withoutSuffixes
127155
.replace(/[_-]/g, ' ')
128156
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
129157
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
130158
.trim();
131-
return titleCaseWords(spaced || withoutTrailingAgent);
159+
return applyAgentDisplayOverrides(titleCaseWords(spaced || withoutSuffixes));
132160
}
133161

134162
export function gerundFor(map: Record<string, string>, raw: string): string {
@@ -214,7 +242,7 @@ export function normalizePhaseName(phase: string | undefined): string {
214242

215243
/**
216244
* Render the live execution context as a natural-language sentence:
217-
* "While {Depositing|Reading}, during {Phase}, {Agent} Agent is {Step-ing},
245+
* "While {Depositing|Reading}, during {Phase}, agent {Name} is {Step-ing},
218246
* by {Thinkings-ing} the {Failsafe}." The 'While {Pipeline}, ' prefix is only
219247
* added when the pipeline mode is known (latched from the stream or passed by
220248
* the page); without it the sentence starts at 'During {Phase}, '. Degrades
@@ -233,7 +261,7 @@ export function describeExecutionContext(ctx: {
233261
if (!ctx.phase || !ctx.agent || !ctx.step) return null;
234262
const modeKey = String(ctx.mode || '').trim().toLowerCase() as SynthesisPipelineMode;
235263
const pipelineGerund = PIPELINE_GERUNDS[modeKey];
236-
const during = `uring ${humanizeNounPhrase(ctx.phase)}, ${humanizeAgentName(ctx.agent)} Agent is ${gerundFor(STEP_GERUNDS, ctx.step)}`;
264+
const during = `uring ${humanizeNounPhrase(ctx.phase)}, agent ${humanizeAgentName(ctx.agent)} is ${gerundFor(STEP_GERUNDS, ctx.step)}`;
237265
let sentence = pipelineGerund ? `While ${pipelineGerund}, d${during}` : `D${during}`;
238266
if (ctx.generation && ctx.failsafe) {
239267
sentence += `, by ${thinkingsGerundPhrase(ctx.generation)} ${formatFailsafeSentenceName(ctx.failsafe)}`;

0 commit comments

Comments
 (0)