mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-08 08:29:42 +02:00
fix: date a working row by its turn, not by the session age
`TuiSessionRow` declared `lastSubmitAt`/`inputTokens`/`outputTokens`, `stateSince()` ordered the WORKING group by the first of them and `renderRowLines()` painted the other two, but nothing ever filled any of them in: the unified list carries none, and the `session:updated` payload that does was discarded (an event only schedules a refetch). So a running turn was dated by its SESSION's creation instead. Measured against the live server before the fix: w65 (created 21h ago, turn started one minute earlier) outranked w67 (created 15 minutes ago, turn started five minutes earlier), the reverse of the rule docs/tui.md states, and the elapsed column read `21h` for a turn a minute old. The token column was unreachable code for the same reason. `fetchLiveSessionMetrics()` reads the three fields from `GET /api/sessions` and `applyLiveMetrics()` folds them onto the rows. That route answers from the server's cached LIGHT state (no terminal buffers): 10-20ms measured, against the ~550ms the unified list in the same `Promise.all` already costs, so it is cheap enough to ride every refresh. It is best-effort like the approvals and tmux reads beside it, because losing the anchor is better than losing the list. A ZERO is treated as unknown rather than merged: `stateSince()` reads `lastSubmitAt ?? createdAt` and 0 is not nullish, so a merged 0 would date every never-submitted session to the epoch. The snapshot path gets the same merge, or `codeman tui --list` would number the WORKING group differently from the dashboard that `codeman tui <n>` indexes into. Verified live: working rows now show 28m/8m (turn age, tokens 280.5k/65.2k) where they showed 21h/34m and no tokens. The e2e assertion fails on master's wiring with `[*] 10m` against a session that pressed Enter one minute ago. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -53,6 +53,13 @@ let sessions: UnifiedSessionItem[] = [];
|
||||
let approvals: ApprovalItem[] = [];
|
||||
/** Terminal buffers the preview pane polls, by session id. */
|
||||
const terminals = new Map<string, string>();
|
||||
/**
|
||||
* The LIGHT session state (`GET /api/sessions`), which is where the turn stamp
|
||||
* and the token counters come from: the unified list carries neither, so w2-beta
|
||||
* is deliberately a session created 10 minutes ago whose turn started one minute
|
||||
* ago. A row dated by the wrong one of those reads `10m` instead of `1m`.
|
||||
*/
|
||||
let liveState: Array<Record<string, unknown>> = [];
|
||||
/** Everything the TUI posted, so a test can assert on the exact body. */
|
||||
const answered: Array<{ id: string; body: Record<string, unknown> }> = [];
|
||||
const inputs: Array<{ sessionId: string; body: Record<string, unknown> }> = [];
|
||||
@@ -184,6 +191,10 @@ function resetSessions(): void {
|
||||
lastActivityAt: NOW - 3_600_000,
|
||||
},
|
||||
];
|
||||
liveState = [
|
||||
{ id: BETA, status: 'busy', lastSubmitAt: NOW - 60_000, inputTokens: 42_000, outputTokens: 3_200 },
|
||||
{ id: ALPHA, status: 'idle', lastSubmitAt: NOW - 60_000 },
|
||||
];
|
||||
}
|
||||
|
||||
let server: http.Server;
|
||||
@@ -264,6 +275,9 @@ beforeAll(async () => {
|
||||
return sendJson(res, { success: true, data: { version: '9.9.9', planUsage: PLAN_USAGE } });
|
||||
}
|
||||
if (url.startsWith('/api/sessions/unified')) return sendJson(res, { success: true, data: { sessions } });
|
||||
if (url === '/api/sessions' || url.startsWith('/api/sessions?')) {
|
||||
return sendJson(res, { success: true, data: liveState });
|
||||
}
|
||||
|
||||
const previewFor = sessionRoute(url, 'terminal');
|
||||
if (previewFor) {
|
||||
@@ -424,6 +438,17 @@ describe('codeman tui (under a pty)', () => {
|
||||
expect(index('RECENT')).toBeLessThan(index('w3-gamma'));
|
||||
});
|
||||
|
||||
it('dates a working row by its turn, not by the session age', () => {
|
||||
// w2-beta was created 10 minutes ago and pressed Enter one minute ago, and
|
||||
// only `GET /api/sessions` knows the second number. `1m` proves the merge
|
||||
// ran end to end; `10m` would mean the row fell back to createdAt.
|
||||
const beta = rowFor(output, 'w2-beta');
|
||||
expect(beta).toMatch(/\b1m\b/);
|
||||
expect(beta).not.toMatch(/\b10m\b/);
|
||||
// The token column rides the same read.
|
||||
expect(beta).toContain('45.2k');
|
||||
});
|
||||
|
||||
it('shows the header facts and only the keys that work', () => {
|
||||
const lines = frameLines(output);
|
||||
expect(lines[0]).toContain('codeman');
|
||||
|
||||
Reference in New Issue
Block a user