feat(cockpit): quick wins — sync chip, cost budget guard, digest, recent commits (W3)
Four small, high-delight features that turn passive capture into glanceable signals.
- Sync chip on project cards: ahead/behind vs upstream + last-commit time, folded
into the existing concurrent per-repo metadata pass (git rev-list --count
--left-right @{u}...HEAD + git log -1 --format=%ct; no upstream → undefined, no route).
- Cost budget guard: COST_BUDGET_USD env (0 = off); a per-session one-shot latch
(Session.budgetNotified, cost is monotonic so never re-armed) fires a single push
on threshold crossing in manager.handleStatusLine; the already-broadcast telemetry
frame carries the warn (tg-cost-warn styling derived from costUsd>=budget via
/config/ui — no new ServerMessage). web-push title added to sw-push.js.
- "While you were away" digest: GET /digest?since= → {finished, needsInput, stuck,
totalCostUsd, sessions[]} aggregate over manager.list(); FE banner on reconnect.
- Recent commits per project: src/http/git-log.ts (NUL-delimited git log → CommitInfo[]),
GET /projects/log?path= (isValidGitDir), textContent-inert render in project detail.
All git via execFile (no shell) + validated cwd; new routes read-only; commit
messages rendered via textContent. Verified: typecheck + build:web clean, 1904 pass
at --test-timeout=30000 (two default-5s failures are slow-sandbox real-subprocess
timeout flakes — the known ring-buffer test + a new real-git-clone sync test — not
logic regressions).
This commit is contained in:
@@ -864,6 +864,78 @@ describe('handleStatusLine', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ── handleStatusLine — W3(b) cost-budget latch ────────────────────────────────
|
||||
describe('handleStatusLine — cost-budget one-shot latch (W3 b)', () => {
|
||||
const BUDGET_CFG: Config = { ...CFG, costBudgetUsd: 1 };
|
||||
|
||||
it('does NOT fire below the threshold and leaves the latch unset', () => {
|
||||
const { service, notify } = createMockNotify();
|
||||
const mgr = createSessionManager(BUDGET_CFG, service);
|
||||
const s = mgr.handleAttach(createMockWs(), null, DIMS, 1_000);
|
||||
|
||||
mgr.handleStatusLine(s.meta.id, { at: 1, costUsd: 0.5 });
|
||||
|
||||
expect(notify).not.toHaveBeenCalled();
|
||||
expect(s.budgetNotified).toBe(false);
|
||||
});
|
||||
|
||||
it('fires exactly once with (session, "budget") on crossing, then latches', () => {
|
||||
const { service, notify } = createMockNotify();
|
||||
const mgr = createSessionManager(BUDGET_CFG, service);
|
||||
const s = mgr.handleAttach(createMockWs(), null, DIMS, 1_000);
|
||||
|
||||
// Below → nothing.
|
||||
mgr.handleStatusLine(s.meta.id, { at: 1, costUsd: 0.5 });
|
||||
expect(notify).not.toHaveBeenCalled();
|
||||
|
||||
// Crosses the threshold → fire once, latch set.
|
||||
mgr.handleStatusLine(s.meta.id, { at: 2, costUsd: 1.2 });
|
||||
expect(notify).toHaveBeenCalledTimes(1);
|
||||
expect(notify).toHaveBeenCalledWith(s, 'budget');
|
||||
expect(s.budgetNotified).toBe(true);
|
||||
|
||||
// Further over-budget frames do NOT re-fire (latch, never re-armed).
|
||||
mgr.handleStatusLine(s.meta.id, { at: 3, costUsd: 2 });
|
||||
mgr.handleStatusLine(s.meta.id, { at: 4, costUsd: 5 });
|
||||
expect(notify).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('never fires when the budget is 0 (disabled)', () => {
|
||||
const { service, notify } = createMockNotify();
|
||||
const mgr = createSessionManager({ ...CFG, costBudgetUsd: 0 }, service);
|
||||
const s = mgr.handleAttach(createMockWs(), null, DIMS, 1_000);
|
||||
|
||||
mgr.handleStatusLine(s.meta.id, { at: 1, costUsd: 999 });
|
||||
|
||||
expect(notify).not.toHaveBeenCalled();
|
||||
expect(s.budgetNotified).toBe(false);
|
||||
});
|
||||
|
||||
it('does not fire when a telemetry frame carries no cost', () => {
|
||||
const { service, notify } = createMockNotify();
|
||||
const mgr = createSessionManager(BUDGET_CFG, service);
|
||||
const s = mgr.handleAttach(createMockWs(), null, DIMS, 1_000);
|
||||
|
||||
mgr.handleStatusLine(s.meta.id, { at: 1 }); // no costUsd
|
||||
|
||||
expect(notify).not.toHaveBeenCalled();
|
||||
expect(s.budgetNotified).toBe(false);
|
||||
});
|
||||
|
||||
it('still broadcasts telemetry even when the latch fires (existing frame is the warning)', () => {
|
||||
const { service } = createMockNotify();
|
||||
const mgr = createSessionManager(BUDGET_CFG, service);
|
||||
const ws = createMockWs();
|
||||
const s = mgr.handleAttach(ws, null, DIMS, 1_000);
|
||||
ws.sent.length = 0;
|
||||
|
||||
mgr.handleStatusLine(s.meta.id, { at: 2, costUsd: 1.2 });
|
||||
|
||||
const telemetry = parseSent(ws).find((m) => m.type === 'telemetry');
|
||||
expect(telemetry).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
// ── handleAttach Case 2 — late-join telemetry/status replay (M3 / AC-B2.3) ─────
|
||||
describe('handleAttach — late-join replay (M3)', () => {
|
||||
it('sends the current telemetry to a device joining a live session', () => {
|
||||
|
||||
Reference in New Issue
Block a user