Remove inbox top-level navigation (#120)
* add gated codex goals support * sync README SDK versions * bump claude agent sdk * add codex goals settings toggle * reuse status dashboard message and simplify settings actions * refine settings page UX * fix settings nav hash routing * add dashboard UX verification * refine dashboard settings and inbox UX * remove inbox top-level navigation
This commit is contained in:
@@ -761,7 +761,7 @@ describe('web dashboard room activity data', () => {
|
||||
});
|
||||
|
||||
describe('web dashboard inbox data', () => {
|
||||
it('builds typed inbox items from pending rooms and paired tasks only', () => {
|
||||
it('builds user-action inbox items from merge-ready paired tasks only', () => {
|
||||
const snapshots: StatusSnapshot[] = [
|
||||
{
|
||||
serviceId: 'codex-main',
|
||||
@@ -803,6 +803,8 @@ describe('web dashboard inbox data', () => {
|
||||
review_requested_at: '2026-04-26T05:03:00.000Z',
|
||||
}),
|
||||
makePairedTask({
|
||||
chat_jid: 'dc:ops',
|
||||
group_folder: 'ops',
|
||||
id: 'merge-1',
|
||||
status: 'merge_ready',
|
||||
title: 'Ready to merge',
|
||||
@@ -839,31 +841,27 @@ describe('web dashboard inbox data', () => {
|
||||
],
|
||||
});
|
||||
|
||||
expect(overview.inbox.map((item) => item.kind)).toEqual([
|
||||
'approval',
|
||||
'reviewer-request',
|
||||
'pending-room',
|
||||
]);
|
||||
expect(overview.inbox.map((item) => item.kind)).toEqual(['approval']);
|
||||
expect(overview.inbox).toContainEqual(
|
||||
expect.objectContaining({
|
||||
id: 'room:codex-main:dc:ops',
|
||||
kind: 'pending-room',
|
||||
severity: 'info',
|
||||
id: 'paired:merge-1:merge_ready',
|
||||
kind: 'approval',
|
||||
severity: 'warn',
|
||||
roomJid: 'dc:ops',
|
||||
taskId: 'merge-1',
|
||||
serviceId: 'codex-main',
|
||||
occurredAt: '2026-04-26T05:00:00.000Z',
|
||||
occurredAt: '2026-04-26T05:04:00.000Z',
|
||||
createdAt: '2026-04-26T05:10:00.000Z',
|
||||
}),
|
||||
);
|
||||
expect(overview.inbox).toContainEqual(
|
||||
expect.objectContaining({
|
||||
id: 'paired:review-1:review_ready',
|
||||
kind: 'reviewer-request',
|
||||
severity: 'warn',
|
||||
serviceId: 'claude-reviewer',
|
||||
taskStatus: 'review_ready',
|
||||
occurredAt: '2026-04-26T05:03:00.000Z',
|
||||
}),
|
||||
expect(overview.inbox.some((item) => item.kind === 'pending-room')).toBe(
|
||||
false,
|
||||
);
|
||||
expect(
|
||||
overview.inbox.some((item) => item.kind === 'reviewer-request'),
|
||||
).toBe(false);
|
||||
expect(overview.inbox.some((item) => item.kind === 'arbiter-request')).toBe(
|
||||
false,
|
||||
);
|
||||
expect(overview.inbox.some((item) => item.kind === 'ci-failure')).toBe(
|
||||
false,
|
||||
|
||||
@@ -275,51 +275,15 @@ function pairedTaskInboxKind(
|
||||
status: PairedTask['status'],
|
||||
): InboxItemKind | null {
|
||||
if (status === 'merge_ready') return 'approval';
|
||||
if (status === 'review_ready' || status === 'in_review') {
|
||||
return 'reviewer-request';
|
||||
}
|
||||
if (status === 'arbiter_requested' || status === 'in_arbitration') {
|
||||
return 'arbiter-request';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function collectInboxItems(args: {
|
||||
snapshots: StatusSnapshot[];
|
||||
tasks: ScheduledTask[];
|
||||
pairedTasks: PairedTask[];
|
||||
createdAt: string;
|
||||
}): InboxItem[] {
|
||||
const items: InboxItem[] = [];
|
||||
|
||||
for (const snapshot of args.snapshots) {
|
||||
for (const entry of snapshot.entries) {
|
||||
if (!entry.pendingMessages && entry.pendingTasks === 0) continue;
|
||||
|
||||
const parts: string[] = [];
|
||||
if (entry.pendingMessages) parts.push('pending messages');
|
||||
if (entry.pendingTasks > 0) parts.push(`${entry.pendingTasks} tasks`);
|
||||
|
||||
items.push({
|
||||
id: `room:${snapshot.serviceId}:${entry.jid}`,
|
||||
groupKey: `room:${snapshot.serviceId}:${entry.jid}`,
|
||||
kind: 'pending-room',
|
||||
severity: entry.pendingTasks > 0 ? 'warn' : 'info',
|
||||
title: entry.name || entry.folder || entry.jid,
|
||||
summary: parts.join(' · '),
|
||||
occurredAt: snapshot.updatedAt,
|
||||
lastOccurredAt: snapshot.updatedAt,
|
||||
createdAt: args.createdAt,
|
||||
occurrences: 1,
|
||||
source: 'status-snapshot',
|
||||
roomJid: entry.jid,
|
||||
roomName: entry.name,
|
||||
groupFolder: entry.folder,
|
||||
serviceId: snapshot.serviceId,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
for (const task of args.pairedTasks) {
|
||||
const kind = pairedTaskInboxKind(task.status);
|
||||
if (!kind) continue;
|
||||
@@ -328,30 +292,17 @@ function collectInboxItems(args: {
|
||||
id: `paired:${task.id}:${task.status}`,
|
||||
groupKey: `paired:${task.id}:${task.status}`,
|
||||
kind,
|
||||
severity: kind === 'arbiter-request' ? 'error' : 'warn',
|
||||
severity: 'warn',
|
||||
title: task.title || task.group_folder,
|
||||
summary: task.status,
|
||||
occurredAt:
|
||||
kind === 'arbiter-request'
|
||||
? (task.arbiter_requested_at ?? task.updated_at)
|
||||
: kind === 'reviewer-request'
|
||||
? (task.review_requested_at ?? task.updated_at)
|
||||
: task.updated_at,
|
||||
lastOccurredAt:
|
||||
kind === 'arbiter-request'
|
||||
? (task.arbiter_requested_at ?? task.updated_at)
|
||||
: kind === 'reviewer-request'
|
||||
? (task.review_requested_at ?? task.updated_at)
|
||||
: task.updated_at,
|
||||
occurredAt: task.updated_at,
|
||||
lastOccurredAt: task.updated_at,
|
||||
createdAt: args.createdAt,
|
||||
occurrences: 1,
|
||||
source: 'paired-task',
|
||||
roomJid: task.chat_jid,
|
||||
groupFolder: task.group_folder,
|
||||
serviceId:
|
||||
kind === 'reviewer-request'
|
||||
? task.reviewer_service_id
|
||||
: task.owner_service_id,
|
||||
serviceId: task.owner_service_id,
|
||||
taskId: task.id,
|
||||
taskStatus: task.status,
|
||||
});
|
||||
@@ -794,8 +745,6 @@ export function buildWebDashboardOverview(args: {
|
||||
fetchedAt: usageFetchedAt,
|
||||
},
|
||||
inbox: collectInboxItems({
|
||||
snapshots: args.snapshots,
|
||||
tasks: args.tasks,
|
||||
pairedTasks: args.pairedTasks ?? [],
|
||||
createdAt: generatedAt,
|
||||
}),
|
||||
|
||||
@@ -142,7 +142,9 @@ describe('web dashboard overview route', () => {
|
||||
},
|
||||
];
|
||||
const overview = route('/api/overview', {
|
||||
loadPairedTasks: () => [makePairedTask({ id: 'review-1' })],
|
||||
loadPairedTasks: () => [
|
||||
makePairedTask({ id: 'merge-1', status: 'merge_ready' }),
|
||||
],
|
||||
readSnapshots: () => snapshots,
|
||||
isInboxItemDismissed: () => true,
|
||||
});
|
||||
|
||||
@@ -4,7 +4,6 @@ import path from 'path';
|
||||
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
|
||||
import type { StatusSnapshot } from './status-dashboard.js';
|
||||
import type { NewMessage, PairedTask, RegisteredGroup } from './types.js';
|
||||
import { createWebDashboardHandler } from './web-dashboard-server.js';
|
||||
|
||||
@@ -74,30 +73,17 @@ describe('web dashboard server handler', () => {
|
||||
});
|
||||
|
||||
it('shares inbox dismiss state with overview JSON', async () => {
|
||||
const snapshots: StatusSnapshot[] = [
|
||||
{
|
||||
serviceId: 'codex-main',
|
||||
agentType: 'codex',
|
||||
assistantName: 'Codex',
|
||||
updatedAt: '2026-04-26T05:00:00.000Z',
|
||||
entries: [
|
||||
{
|
||||
jid: 'dc:ops',
|
||||
name: '#ops',
|
||||
folder: 'ops',
|
||||
agentType: 'codex',
|
||||
status: 'waiting',
|
||||
elapsedMs: 2500,
|
||||
pendingMessages: true,
|
||||
pendingTasks: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
const pairedTask = makePairedTask({
|
||||
chat_jid: 'dc:ops',
|
||||
group_folder: 'ops',
|
||||
id: 'merge-1',
|
||||
status: 'merge_ready',
|
||||
updated_at: '2026-04-26T05:00:00.000Z',
|
||||
});
|
||||
const handler = createWebDashboardHandler({
|
||||
readStatusSnapshots: () => snapshots,
|
||||
readStatusSnapshots: () => [],
|
||||
getTasks: () => [],
|
||||
getPairedTasks: () => [],
|
||||
getPairedTasks: () => [pairedTask],
|
||||
now: () => '2026-04-26T05:10:00.000Z',
|
||||
startBackgroundCacheRefresh: false,
|
||||
});
|
||||
@@ -133,10 +119,7 @@ describe('web dashboard server handler', () => {
|
||||
inbox: [],
|
||||
});
|
||||
|
||||
snapshots[0] = {
|
||||
...snapshots[0]!,
|
||||
updatedAt: '2026-04-26T05:01:00.000Z',
|
||||
};
|
||||
pairedTask.updated_at = '2026-04-26T05:01:00.000Z';
|
||||
const changedOverview = await handler(
|
||||
new Request('http://localhost/api/overview'),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user