Skip to content

Commit fba8715

Browse files
V48 Gate 3 (implementation-only): normalize the processing-indicator label to a plain-English sentence
The stall indicator's raw "Phase → Agent → Step → Failsafe → Thricified" hierarchy string read as machine dump, not guidance. Rewrote it as: "During {Phase}, {Agent} Agent is {Step-ing}, by {Thricified-ing} the {Failsafe}." — e.g. "During Setup, Deposit Input Comprehension Agent is Planning, by Judging the Prepare Concise Context." Degrades gracefully: with no Failsafe/Thricified yet (a Tool-use context only ever carries Phase/Agent/ Step, per F19) it ends after the Step clause; with no Phase/Agent/Step at all it falls back to the bare "Processing" sentence, both still suffixed with "· Ns since last update". PTRR steps and Thricified generation sub-steps map through small fixed gerund lookup tables (plan/try/refine/retry; reason/judge/structured_output -> Structuring); Phase/Failsafe render as humanized noun phrases (underscores -> spaces, title case); Agent names have a trailing "Agent" stripped and camelCase split before the template appends the literal word "Agent" back. Updated/added unit tests: the full sentence exactly matches the requested template + example, the Tool-use partial-context degrade, and the bare Processing fallback. uapi tsc 0; 24/24 across the touched suites.
1 parent 861c868 commit fba8715

2 files changed

Lines changed: 108 additions & 11 deletions

File tree

uapi/components/base/bitcode/execution/pipeline-execution-log.tsx

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -403,11 +403,78 @@ export function buildRawLogCopyText(args: {
403403
// a genuine-hang signal rather than a merely slow generation.
404404
const LIKELY_STALL_SECONDS = 90;
405405

406+
// PTRR step -> present-continuous verb ("Plan" -> "Planning").
407+
const STEP_GERUNDS: Record<string, string> = {
408+
plan: 'Planning',
409+
try: 'Trying',
410+
refine: 'Refining',
411+
retry: 'Retrying',
412+
};
413+
414+
// Thricified generation sub-step -> present-continuous verb (GenerationSubMetaSubStep).
415+
const THRICIFIED_GERUNDS: Record<string, string> = {
416+
reason: 'Reasoning',
417+
judge: 'Judging',
418+
structured_output: 'Structuring',
419+
};
420+
421+
function titleCaseWords(value: string): string {
422+
return value
423+
.split(/\s+/)
424+
.filter(Boolean)
425+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
426+
.join(' ');
427+
}
428+
429+
// "prepare_concise_context" -> "Prepare Concise Context"; "Discovery" -> "Discovery".
430+
function humanizeNounPhrase(value: string): string {
431+
return titleCaseWords(value.replace(/_/g, ' '));
432+
}
433+
434+
// "DepositInputComprehensionAgent" -> "Deposit Input Comprehension" (trailing
435+
// "Agent" stripped — the sentence template appends the literal word "Agent").
436+
function humanizeAgentName(value: string): string {
437+
const withoutTrailingAgent = value.replace(/Agent$/, '');
438+
const spaced = withoutTrailingAgent
439+
.replace(/_/g, ' ')
440+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
441+
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
442+
.trim();
443+
return titleCaseWords(spaced || withoutTrailingAgent);
444+
}
445+
446+
function gerundFor(map: Record<string, string>, raw: string): string {
447+
return map[raw.trim().toLowerCase()] || humanizeNounPhrase(raw);
448+
}
449+
406450
/**
407-
* Build the live "Running: {hierarchy} · Ns since last update" label for the
408-
* processing indicator, from the last known log line + the current tick. Pure
409-
* + exported for unit testing. Returns the bare fallback label when there is no
410-
* prior line yet (nothing streamed since the run started).
451+
* Render the live execution context as a natural-language sentence: "During
452+
* {Phase}, {Agent} Agent is {Step-ing}, by {Thricified-ing} the {Failsafe}."
453+
* Degrades gracefully as fields are unknown (e.g. a Tool-use context only ever
454+
* carries Phase/Agent/Step, never Failsafe/Thricified — F19). Returns null
455+
* when there isn't enough context yet to say anything meaningful.
456+
*/
457+
function describeExecutionContext(ctx: {
458+
phase?: string | null;
459+
agent?: string | null;
460+
step?: string | null;
461+
failsafe?: string | null;
462+
generation?: string | null;
463+
}): string | null {
464+
if (!ctx.phase || !ctx.agent || !ctx.step) return null;
465+
let sentence = `During ${humanizeNounPhrase(ctx.phase)}, ${humanizeAgentName(ctx.agent)} Agent is ${gerundFor(STEP_GERUNDS, ctx.step)}`;
466+
if (ctx.generation && ctx.failsafe) {
467+
sentence += `, by ${gerundFor(THRICIFIED_GERUNDS, ctx.generation)} the ${humanizeNounPhrase(ctx.failsafe)}`;
468+
}
469+
return sentence;
470+
}
471+
472+
/**
473+
* Build the live "During {Phase}, {Agent} Agent is {Step}... · Ns since last
474+
* update" label for the processing indicator, from the last known log line +
475+
* the current tick. Pure + exported for unit testing. Returns the bare
476+
* fallback label when there is no prior line yet (nothing streamed since the
477+
* run started) or not enough context to describe.
411478
*/
412479
export function buildProcessingStallLabel(
413480
lastLine: Pick<LogLine, 'phase' | 'agent' | 'step' | 'failsafe' | 'generation' | 'timestamp'> | undefined,
@@ -418,12 +485,10 @@ export function buildProcessingStallLabel(
418485
if (!Number.isFinite(lastMs)) return { label: 'Processing', likelyStalled: false };
419486

420487
const elapsedSeconds = Math.max(0, Math.round((nowMs - lastMs) / 1000));
421-
const hierarchy = [lastLine.phase, lastLine.agent, lastLine.step, lastLine.failsafe, lastLine.generation]
422-
.filter(Boolean)
423-
.join(' → ');
488+
const sentence = describeExecutionContext(lastLine);
424489
const likelyStalled = elapsedSeconds >= LIKELY_STALL_SECONDS;
425-
const label = hierarchy
426-
? `Running: ${hierarchy} · ${elapsedSeconds}s since last update`
490+
const label = sentence
491+
? `${sentence} · ${elapsedSeconds}s since last update`
427492
: `Processing · ${elapsedSeconds}s since last update`;
428493
return { label, likelyStalled };
429494
}

uapi/tests/pipelineExecutionLogCopy.test.tsx

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ describe('buildProcessingStallLabel — live stall visibility (QA debug aid)', (
5858
});
5959
});
6060

61-
it('renders the hierarchy + elapsed seconds since the last line, not stalled under the threshold', () => {
61+
it('renders a natural-language sentence + elapsed seconds since the last line, not stalled under the threshold', () => {
6262
const lastLine = {
6363
phase: 'Discovery',
6464
agent: 'DepositDepositorySearchAgent',
@@ -69,11 +69,43 @@ describe('buildProcessingStallLabel — live stall visibility (QA debug aid)', (
6969
};
7070
const { label, likelyStalled } = buildProcessingStallLabel(lastLine, 1_000_000 + 30_000);
7171
expect(label).toBe(
72-
'Running: Discovery → DepositDepositorySearchAgent → try → chunk_then_sum → structured_output · 30s since last update',
72+
'During Discovery, Deposit Depository Search Agent is Trying, by Structuring the Chunk Then Sum · 30s since last update',
7373
);
7474
expect(likelyStalled).toBe(false);
7575
});
7676

77+
it('matches the "During {Phase}, {Agent} Agent is {Step}, by {Thricified} the {Failsafe}" template exactly', () => {
78+
const lastLine = {
79+
phase: 'Setup',
80+
agent: 'DepositInputComprehensionAgent',
81+
step: 'plan',
82+
failsafe: 'prepare_concise_context',
83+
generation: 'judge',
84+
timestamp: new Date(0).toISOString(),
85+
};
86+
const { label } = buildProcessingStallLabel(lastLine, 21_000);
87+
expect(label).toBe(
88+
'During Setup, Deposit Input Comprehension Agent is Planning, by Judging the Prepare Concise Context · 21s since last update',
89+
);
90+
});
91+
92+
it('degrades to just the Phase/Agent/Step clause when there is no Failsafe/Thricified yet (e.g. a Tool-use context)', () => {
93+
const lastLine = {
94+
phase: 'Discovery',
95+
agent: 'DepositCodebaseComprehensionAgent',
96+
step: 'try',
97+
timestamp: new Date(0).toISOString(),
98+
};
99+
const { label } = buildProcessingStallLabel(lastLine, 5_000);
100+
expect(label).toBe('During Discovery, Deposit Codebase Comprehension Agent is Trying · 5s since last update');
101+
});
102+
103+
it('falls back to the bare "Processing" sentence when Phase/Agent/Step are not yet known', () => {
104+
const lastLine = { phase: 'Discovery', agent: 'DepositDepositorySearchAgent', timestamp: new Date(0).toISOString() };
105+
const { label } = buildProcessingStallLabel(lastLine, 5_000);
106+
expect(label).toBe('Processing · 5s since last update');
107+
});
108+
77109
it('flags likelyStalled once elapsed time reaches the LLM call timeout default (90s)', () => {
78110
const lastLine = { phase: 'Discovery', agent: 'DepositDepositorySearchAgent', timestamp: new Date(0).toISOString() };
79111
const { likelyStalled, label } = buildProcessingStallLabel(lastLine, 90_000);

0 commit comments

Comments
 (0)