Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
129 changes: 129 additions & 0 deletions packages/cli/src/__tests__/pi-goal.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import assert from 'node:assert/strict';
import { describe, test } from 'node:test';
import { GOAL_STATUSES, type GoalStatus } from '@maka/core/goal';
import type { GoalProjection } from '@maka/runtime-host/protocol';
import { formatTokenCount } from '../pi-transcript-format.js';
import {
formatGoalElapsed,
goalElapsedMs,
goalStatusLabel,
goalStatusLineText,
goalSummaryLines,
isLiveGoalStatus,
} from '../pi-goal.js';

function goal(overrides: Partial<GoalProjection> = {}): GoalProjection {
return {
goalId: 'goal-1',
revision: 1,
sessionId: 'session-1',
condition: 'Ship the feature',
status: 'active',
setAt: 1_000,
iterations: 3,
maxIterations: 50,
consecutiveNoProgress: 0,
blockCap: 8,
tokenBudget: null,
tokensSpent: 0,
lastReason: null,
achievedAt: null,
pausedAt: null,
...overrides,
};
}

describe('pi-goal display helpers', () => {
test('every declared goal status has a label and a live/terminal classification', () => {
// Exhaustiveness guard: a new GoalStatus must make a deliberate choice in
// both places instead of silently falling through.
for (const status of GOAL_STATUSES) {
assert.equal(typeof goalStatusLabel(status), 'string', status);
assert.notEqual(goalStatusLabel(status), '', status);
assert.equal(typeof isLiveGoalStatus(status), 'boolean', status);
}
assert.deepEqual(
GOAL_STATUSES.filter((status) => isLiveGoalStatus(status)),
['active', 'waiting', 'paused'],
);
});

test('elapsed freezes at pausedAt for a paused goal and never goes negative', () => {
const paused = goal({ status: 'paused', setAt: 1_000, pausedAt: 61_000 });
assert.equal(goalElapsedMs(paused, 600_000), 60_000);
assert.equal(goalElapsedMs(goal({ setAt: 10_000 }), 5_000), 0);
});

test('formatGoalElapsed is compact', () => {
assert.equal(formatGoalElapsed(0), '0s');
assert.equal(formatGoalElapsed(59_000), '59s');
assert.equal(formatGoalElapsed(60_000), '1m');
assert.equal(formatGoalElapsed(90 * 60_000), '1h 30m');
assert.equal(formatGoalElapsed(2 * 3_600_000), '2h');
assert.equal(formatGoalElapsed(26 * 3_600_000), '1d 2h');
});

test('status-line text: active shows elapsed, waiting and paused show the state name', () => {
const now = 61_000;
assert.equal(goalStatusLineText(goal({ setAt: 1_000 }), now), 'goal 3/50 1m');
assert.equal(goalStatusLineText(goal({ status: 'waiting' }), now), 'goal waiting 3/50');
assert.equal(
goalStatusLineText(goal({ status: 'paused', pausedAt: 31_000 }), now),
'goal paused 3/50',
);
});

test('summary lines include budget only when set and the evaluator note only when present', () => {
const plain = goalSummaryLines(goal(), 61_000);
assert.equal(plain.length, 2);
assert.match(plain[0]!, /^Goal: Ship the feature$/);
assert.match(plain[1]!, /active · 3\/50 iterations · 1m$/);

const detailed = goalSummaryLines(
goal({ tokenBudget: 100_000, tokensSpent: 45_200, lastReason: 'tests still failing' }),
61_000,
);
assert.deepEqual(detailed.slice(2), [
'Tokens: 45k / 100k',
'Last evaluator note: tests still failing',
]);
});

test('summary collapses embedded whitespace and labels a cleared goal as cleared', () => {
const messy = goalSummaryLines(
goal({ condition: 'Ship the\n feature', lastReason: 'line one\nline two' }),
61_000,
);
assert.equal(messy[0], 'Goal: Ship the feature');
assert.equal(messy.at(-1), 'Last evaluator note: line one line two');

// A cleared goal keeps its terminal record; the summary must not present
// the condition as if it were still armed.
const cleared = goalSummaryLines(goal({ status: 'cleared' }), 61_000);
assert.equal(cleared[0], 'Cleared goal: Ship the feature');
assert.match(cleared[1]!, /^Status: cleared /);
});

test('summary hides elapsed for terminal verdicts that carry no freeze timestamp', () => {
const terminal = [
'cleared',
'impossible',
'stalled',
'budget_limited',
'max_iterations',
] as const;
for (const status of terminal) {
const lines = goalSummaryLines(goal({ status }), 61_000);
assert.equal(lines[1], `Status: ${goalStatusLabel(status)} · 3/50 iterations`, status);
}
});

test('summary keeps the frozen elapsed for an achieved goal', () => {
const lines = goalSummaryLines(goal({ status: 'achieved', achievedAt: 61_000 }), 600_000);
assert.match(lines[1]!, /achieved · 3\/50 iterations · 1m$/);
});

test('token formatting is the shared status-line formatter', () => {
assert.equal(formatTokenCount(45_200), '45k');
});
});
60 changes: 60 additions & 0 deletions packages/cli/src/__tests__/pi-transcript.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
applyShellRunUpdateToTranscript,
createMakaPiTranscriptState,
renderMakaPiActivityStrip,
renderMakaPiStatusLine,
renderMakaPiTranscript,
reconcileToolsWithStoredMessages,
replaceTranscriptWithStoredMessages,
Expand Down Expand Up @@ -58,6 +59,65 @@ describe('Maka Pi TUI transcript', () => {
assert.match(chinese, /\/session\s+切换或恢复会话/);
});

test('renders goal-origin prompts as autonomous provenance, not as user prompts', () => {
const state = createMakaPiTranscriptState();
replaceTranscriptWithStoredMessages(state, [
{
type: 'user',
id: 'message-1',
turnId: 'turn-1',
ts: 1,
text: '[Goal continuation] The goal is not yet met.',
origin: { kind: 'goal', goalId: 'goal-1' },
},
]);

assert.deepEqual(state.entries, [
{ kind: 'goal_continuation', text: '[Goal continuation] The goal is not yet met.' },
]);
assert.match(
renderMakaPiTranscript(state, meta(), 80).map(stripAnsi).join('\n'),
/Goal continuation \(autonomous\).*Goal continuation\] The goal is not yet met/s,
);
});

test('status line shows a live goal and hides terminal or absent goals', () => {
const base = {
goalId: 'goal-1',
revision: 1,
sessionId: 'session-1',
condition: 'Ship it',
setAt: Date.now() - 60_000,
iterations: 3,
maxIterations: 50,
consecutiveNoProgress: 0,
blockCap: 8,
tokenBudget: null,
tokensSpent: 0,
lastReason: null,
achievedAt: null,
pausedAt: null,
} as const;
const active = stripAnsi(
renderMakaPiStatusLine({ ...meta(), goal: { ...base, status: 'active' as const } }, 120),
);
assert.match(active, /goal 3\/50 1m/);

const paused = stripAnsi(
renderMakaPiStatusLine(
{ ...meta(), goal: { ...base, status: 'paused' as const, pausedAt: Date.now() - 30_000 } },
120,
),
);
assert.match(paused, /goal paused 3\/50/);

const achieved = stripAnsi(
renderMakaPiStatusLine({ ...meta(), goal: { ...base, status: 'achieved' as const } }, 120),
);
assert.doesNotMatch(achieved, /goal/);
assert.doesNotMatch(stripAnsi(renderMakaPiStatusLine({ ...meta(), goal: null }, 120)), /goal/);
});

test('keeps assistant text after a tool call visible after the tool block', () => {
const state = createMakaPiTranscriptState();
appendUserPrompt(state, 'inspect the package');
Expand Down
Loading
Loading