Skip to content

Commit 072b4ff

Browse files
committed
refactor(tui): choose the idle colour from the running phase
The nested ternary tested the negated phase first, which tripped unicorn(no-negated-condition) and made `textMuted` the answer at both ends of the chain. A small `idleColor` helper states the rule the way the panel means it: only a running row can stall. Adds the case that had no coverage — a suspended row keeps its muted count however long it stays silent — so the guard now fails a test when it is removed.
1 parent 0b78a4d commit 072b4ff

2 files changed

Lines changed: 54 additions & 9 deletions

File tree

apps/pythinker-code/src/tui/components/messages/dynamic-workflow-mission-control.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,18 +1046,27 @@ function renderWorkCell(member: DynamicWorkflowMember, nowMs: number): string {
10461046
}
10471047
const idleMs = Math.max(0, nowMs - member.lastEventAtMs);
10481048
const idleSeconds = Math.floor(idleMs / 1000);
1049-
// Only a running row can stall. A suspended one is waiting on the user by
1050-
// design, so it keeps the count without the alarm colours.
1051-
const token = member.phase !== 'running'
1052-
? 'textMuted'
1053-
: idleMs >= DYNAMIC_WORKFLOW_RENDERING.stalledIdleMs
1054-
? 'error'
1055-
: idleMs >= DYNAMIC_WORKFLOW_RENDERING.quietIdleMs
1056-
? 'warning'
1057-
: 'textMuted';
1049+
const token = idleColor(member.phase, idleMs);
10581050
return `${tools} ${currentTheme.fg(token, `${String(idleSeconds)}s`.padStart(4, ' '))}`;
10591051
}
10601052

1053+
/**
1054+
* How loud an idle age reads.
1055+
*
1056+
* Only a running row can stall. A suspended one is waiting on the user by
1057+
* design, so it keeps the count without the alarm colours.
1058+
*/
1059+
function idleColor(
1060+
phase: DynamicWorkflowPhase,
1061+
idleMs: number,
1062+
): 'textMuted' | 'warning' | 'error' {
1063+
if (phase === 'running') {
1064+
if (idleMs >= DYNAMIC_WORKFLOW_RENDERING.stalledIdleMs) return 'error';
1065+
if (idleMs >= DYNAMIC_WORKFLOW_RENDERING.quietIdleMs) return 'warning';
1066+
}
1067+
return 'textMuted';
1068+
}
1069+
10611070
/**
10621071
* The STATE cell for one row.
10631072
*

apps/pythinker-code/test/tui/components/messages/dynamic-workflow-mission-control.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -665,6 +665,42 @@ describe('DynamicWorkflowMissionControlComponent', () => {
665665
}
666666
});
667667

668+
it('keeps a suspended row muted however long it stays silent', () => {
669+
vi.useFakeTimers();
670+
vi.setSystemTime(0);
671+
const previousLevel = chalk.level;
672+
const previousPalette = currentTheme.palette;
673+
chalk.level = 3;
674+
currentTheme.setPalette(darkColors);
675+
676+
try {
677+
const component = createComponent();
678+
component.updateArgs({ items: ['Held work'] });
679+
component.markInputComplete();
680+
component.registerSubagent({ agentId: 'agent-1' });
681+
component.markStarted('agent-1');
682+
// The last event lands a minute in, so the idle age and the elapsed age
683+
// read as different numbers and the assertion cannot match the wrong cell.
684+
vi.setSystemTime(60_000);
685+
component.recordToolCall({ agentId: 'agent-1', name: 'Bash' });
686+
component.markSuspended({ agentId: 'agent-1', reason: 'Waiting for approval' });
687+
688+
// A suspended agent waits on the user by design, so its silence is not a
689+
// stall and must never borrow the alarm colours.
690+
vi.setSystemTime(60_000 + DYNAMIC_WORKFLOW_RENDERING.stalledIdleMs * 2);
691+
const line = component.render(100).find(
692+
(candidate) => strip(candidate).replace(/^\s*/u, '').startsWith('001'),
693+
);
694+
if (line === undefined) throw new Error('Missing Dynamic Workflow member 001');
695+
expect(strip(line)).toContain('1⚒ 360s');
696+
expect(line).toContain(chalk.hex(darkColors.textMuted)('360s'));
697+
} finally {
698+
vi.useRealTimers();
699+
chalk.level = previousLevel;
700+
currentTheme.setPalette(previousPalette);
701+
}
702+
});
703+
668704
it('never marks a row that has not started as stalled', () => {
669705
vi.useFakeTimers();
670706
vi.setSystemTime(0);

0 commit comments

Comments
 (0)