@@ -8,7 +8,7 @@ jest.mock('@/components/base/bitcode/execution/FileDiffViewer', () => ({
88
99import React from 'react' ;
1010import '@testing-library/jest-dom' ;
11- import { render , screen } from '@testing-library/react' ;
11+ import { fireEvent , render , screen } from '@testing-library/react' ;
1212
1313import {
1414 FAILSAFE_SENTENCE_NAMES ,
@@ -19,10 +19,25 @@ import {
1919 trimPipelineAgentName ,
2020} from '@/components/base/bitcode/execution/execution-telemetry-format' ;
2121import { formatRunClock } from '@/components/base/bitcode/execution/RunClock' ;
22- import { buildProcessingStallLabel } from '@/components/base/bitcode/execution/pipeline-execution-log' ;
22+ import {
23+ PipelineExecutionLog ,
24+ buildProcessingStallLabel ,
25+ } from '@/components/base/bitcode/execution/pipeline-execution-log' ;
2326import { ExecutionContextPillRow } from '@/components/base/bitcode/execution/ExecutionContextPillRow' ;
27+ import { TelemetryExplainerTrigger } from '@/components/base/bitcode/execution/TelemetryExplainerTrigger' ;
28+ import {
29+ getTelemetryPillExplainer ,
30+ getTelemetryRowIconExplainer ,
31+ } from '@/components/base/bitcode/execution/telemetry-pill-explainers' ;
2432import { buildTerminalRunActivityFromEvents } from '@/app/terminal/terminal-run-activity' ;
2533
34+ beforeAll ( ( ) => {
35+ ( global as any ) . ResizeObserver = class {
36+ observe ( ) { }
37+ disconnect ( ) { }
38+ } ;
39+ } ) ;
40+
2641// ---------------------------------------------------------------------------
2742// Item 3 — trim the pipeline-name prefix from AGENT names (display only)
2843// ---------------------------------------------------------------------------
@@ -254,3 +269,165 @@ describe('ExecutionContextPillRow', () => {
254269 expect ( container . firstElementChild ) . toBeNull ( ) ;
255270 } ) ;
256271} ) ;
272+
273+ // ---------------------------------------------------------------------------
274+ // Tooltip content — the SPECIFIC section states what the exact element is
275+ // PROMPTED to do and what it RETURNS (output-schema shapes), summarized from
276+ // the real agent/step/failsafe sources; the generic type copy is separate.
277+ // ---------------------------------------------------------------------------
278+ describe ( 'getTelemetryPillExplainer — prompt/return-concrete specific copy' , ( ) => {
279+ it ( 'failsafe PCC names its selection input/output shapes' , ( ) => {
280+ const explainer = getTelemetryPillExplainer ( 'failsafe' , 'prepare_concise_context' ) ;
281+ expect ( explainer . specific ) . toContain ( 'pipeline_execution_keys' ) ;
282+ expect ( explainer . specific ) . toContain ( '{selectedKeys}' ) ;
283+ expect ( explainer . generic ) . toContain ( 'Failsafes are the guards' ) ;
284+ } ) ;
285+
286+ it ( 'failsafe handle-large-inputs states the budget measurement + one-vs-chunked generations' , ( ) => {
287+ const explainer = getTelemetryPillExplainer ( 'failsafe' , 'chunk_then_sum' ) ;
288+ expect ( explainer . specific ) . toContain ( 'request budget' ) ;
289+ expect ( explainer . specific ) . toContain ( 'ONE task generation' ) ;
290+ } ) ;
291+
292+ it ( "failsafe handle-large-outputs references the surrounding agent's output schema when context is passed" , ( ) => {
293+ const explainer = getTelemetryPillExplainer ( 'failsafe' , 'stitch_until_complete' , 'deposit' , {
294+ agent : 'DepositValidationAgent' ,
295+ step : 'try' ,
296+ } ) ;
297+ expect ( explainer . specific ) . toContain ( "the Validation Agent's output schema" ) ;
298+ expect ( explainer . specific ) . toContain ( 'validation error' ) ;
299+ } ) ;
300+
301+ it ( 'deposit agent copy summarizes the prompt purpose + the zod return shape' , ( ) => {
302+ const search = getTelemetryPillExplainer ( 'agent' , 'DepositDepositorySearchAgent' , 'deposit' ) ;
303+ expect ( search . specific ) . toContain ( '{guidance}' ) ;
304+ expect ( search . specific ) . toContain ( 'likelyReadTopics' ) ;
305+
306+ const comprehension = getTelemetryPillExplainer ( 'agent' , 'DepositInputComprehensionAgent' , 'deposit' ) ;
307+ expect ( comprehension . specific ) . toContain ( '{comprehension}' ) ;
308+ expect ( comprehension . specific ) . toContain ( 'obfuscatedPaths' ) ;
309+
310+ const synthesis = getTelemetryPillExplainer ( 'agent' , 'DepositAssetPackSynthesisAgent' , 'deposit' ) ;
311+ expect ( synthesis . specific ) . toContain ( '{options}' ) ;
312+ expect ( synthesis . specific ) . toContain ( 'patchSummary' ) ;
313+ } ) ;
314+
315+ it ( "PTRR step copy references the agent's output schema, sharpened by row context" , ( ) => {
316+ const withContext = getTelemetryPillExplainer ( 'step' , 'try' , 'deposit' , {
317+ agent : 'DepositDepositorySearchAgent' ,
318+ } ) ;
319+ expect ( withContext . specific ) . toContain ( "the Depository Search Agent's output schema" ) ;
320+
321+ const withoutContext = getTelemetryPillExplainer ( 'step' , 'try' ) ;
322+ expect ( withoutContext . specific ) . toContain ( "the agent's output schema" ) ;
323+ } ) ;
324+
325+ it ( 'generation copy names the Thinkings return shapes' , ( ) => {
326+ expect ( getTelemetryPillExplainer ( 'generation' , 'reason' ) . specific ) . toContain (
327+ '{analysis, steps, conclusion, confidence}' ,
328+ ) ;
329+ expect ( getTelemetryPillExplainer ( 'generation' , 'judge' ) . specific ) . toContain (
330+ '{quality, issues, suggestions, approved}' ,
331+ ) ;
332+ expect ( getTelemetryPillExplainer ( 'generation' , 'structured_output' ) . specific ) . toContain (
333+ 'zod output schema' ,
334+ ) ;
335+ } ) ;
336+
337+ it ( 'deposit phase copy states the concrete per-phase SDIVF jobs' , ( ) => {
338+ const discovery = getTelemetryPillExplainer ( 'phase' , 'discovery' , 'deposit' ) ;
339+ expect ( discovery . specific ) . toContain ( "The Depositing Pipeline's" ) ;
340+ expect ( discovery . specific ) . toContain ( 'codebase comprehension' ) ;
341+ expect ( discovery . specific ) . toContain ( 'inherent regurgitation' ) ;
342+
343+ const finish = getTelemetryPillExplainer ( 'phase' , 'finish' , 'deposit' ) ;
344+ expect ( finish . specific ) . toContain ( 'depositor review' ) ;
345+ } ) ;
346+
347+ it ( 'row-icon explainer keeps the specific what-this-row-is copy on the specific field' , ( ) => {
348+ expect ( getTelemetryRowIconExplainer ( 'llm' ) . specific ) . toContain ( 'This row is one LLM call' ) ;
349+ expect ( getTelemetryRowIconExplainer ( 'tool' ) . specific ) . toContain ( 'This row is one Tool use' ) ;
350+ } ) ;
351+ } ) ;
352+
353+ // ---------------------------------------------------------------------------
354+ // Tooltip ordering — the SPECIFIC section renders on TOP, the generic type
355+ // copy BELOW it.
356+ // ---------------------------------------------------------------------------
357+ describe ( 'TelemetryExplainerTrigger — specific section above generic' , ( ) => {
358+ it ( 'renders specific before generic in the tooltip' , ( ) => {
359+ render (
360+ < TelemetryExplainerTrigger
361+ explainer = { {
362+ kicker : 'Failsafe' ,
363+ title : 'Prepare Concise Context' ,
364+ specific : 'SPECIFIC-SECTION-COPY' ,
365+ generic : 'GENERIC-SECTION-COPY' ,
366+ } }
367+ >
368+ < span > trigger</ span >
369+ </ TelemetryExplainerTrigger > ,
370+ ) ;
371+
372+ fireEvent . mouseEnter ( screen . getByText ( 'trigger' ) . parentElement as HTMLElement ) ;
373+ const tooltip = screen . getByRole ( 'tooltip' ) ;
374+ const text = tooltip . textContent || '' ;
375+ expect ( text . indexOf ( 'SPECIFIC-SECTION-COPY' ) ) . toBeGreaterThanOrEqual ( 0 ) ;
376+ expect ( text . indexOf ( 'SPECIFIC-SECTION-COPY' ) ) . toBeLessThan ( text . indexOf ( 'GENERIC-SECTION-COPY' ) ) ;
377+ } ) ;
378+ } ) ;
379+
380+ // ---------------------------------------------------------------------------
381+ // Pill placement — the pills render to the RIGHT of the chevron + title on
382+ // the SAME line (one flex row), not on a row above the title.
383+ // ---------------------------------------------------------------------------
384+ describe ( 'PipelineExecutionLog — pills inline with the title line' , ( ) => {
385+ const line = 'LLM call observed' ;
386+ const outputDetails = {
387+ [ line ] : {
388+ type : 'generation' ,
389+ status : {
390+ executionState : {
391+ phase : 'discovery' ,
392+ agent : 'DepositDepositorySearchAgent' ,
393+ step : 'try' ,
394+ failsafe : 'prepare_concise_context' ,
395+ generation : 'reason' ,
396+ pipelineMode : 'deposit' ,
397+ } ,
398+ timestamp : '2026-07-01T00:00:05.000Z' ,
399+ } ,
400+ } ,
401+ } ;
402+
403+ it ( 'compact layout: title and pill row share ONE flex row, pills after the title' , ( ) => {
404+ render (
405+ < PipelineExecutionLog
406+ output = { `${ line } \n` }
407+ isProcessing = { false }
408+ error = { null }
409+ outputDetails = { outputDetails }
410+ onRetry = { ( ) => { } }
411+ onDismissError = { ( ) => { } }
412+ userHasScrolled = { false }
413+ setUserHasScrolled = { ( ) => { } }
414+ compact
415+ /> ,
416+ ) ;
417+
418+ const title = screen . getByText ( line ) ;
419+ const pillRow = screen . getByText ( 'DISCOVERY' ) . closest ( '.flex-wrap' ) as HTMLElement ;
420+ expect ( pillRow ) . not . toBeNull ( ) ;
421+
422+ // Same parent row — NOT a separate pill line above the title.
423+ expect ( pillRow . parentElement ) . toBe ( title . parentElement ) ;
424+ const row = title . parentElement as HTMLElement ;
425+ expect ( row . className ) . toContain ( 'items-center' ) ;
426+ expect ( row . className ) . not . toContain ( 'flex-col' ) ;
427+
428+ // Pills come AFTER the title in document order (to its right).
429+ expect (
430+ title . compareDocumentPosition ( pillRow ) & Node . DOCUMENT_POSITION_FOLLOWING ,
431+ ) . toBeTruthy ( ) ;
432+ } ) ;
433+ } ) ;
0 commit comments