delete Task Logs

A complete read path over a table nothing could write to. task-logger.ts exported
createTaskLog, appendToLog and finalizeLog, and none of the three was called
anywhere in the tree — so `task_logs` could never gain a row, and the screen was
permanently empty for everyone.

The read half was fully wired: mounted router, capability claim, dock icon, two
routes and a page-title rule. That is why it looked alive.

Gone, in the order it was reached:

  Screens/Dashboard/TaskLogs/       the screen
  App.tsx                           /task-logs and /task-logs/:id
  Dashboard/index.tsx               the export
  Layout/Dock.tsx                   the 'Logs' icon, and ScrollText with it
  state/usePageTitle.ts             the title rule
  api/task-logs/task-logs.ts        the router, and its mount in hono.ts
  api/task-logger.ts                101 lines of orphaned writer
  officer_db/src/operations/        the directory
  officer_db/src/schema.ts          the export line
  officer_db/src/types.ts           TaskLogSelect / TaskLogInsert

capabilities/registry.ts loses '/task-logs' from the `tasks` capability's `api`
AND `routes`. The api half is not optional: assertCapabilityTotality check 2
refuses to boot on a capability claiming a prefix nothing mounts, so unmounting
the router while leaving the claim would have stopped the server starting.

db:push now creates 21 tables, down from 43 at the start of the evening.

officer_db/src/operations was one of the two lopsided directories the
schema/queries merge exposed. integrations/ is the remaining one, and it is
legitimate — it spans server and user-data.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-13 01:56:07 +00:00
co-authored by Claude Opus 5
parent f9cd0a798a
commit 595dd082a7
14 changed files with 5 additions and 379 deletions
-101
View File
@@ -1,101 +0,0 @@
import { db, schema } from 'officerdb';
import type { TaskInfo } from '@@/api/chat-types';
type ChatMessage =
| { role: 'user'; text: string }
| { role: 'assistant'; text: string }
| {
role: 'tool';
toolName: string;
toolInput: Record<string, unknown>;
toolUseId: string;
output?: string;
isError?: boolean;
}
| { role: 'result'; costUsd: number; durationMs: number; numTurns: number; isError: boolean }
| { role: 'error'; text: string };
type TaskLog = {
userId: number;
taskName: string;
taskDirName: string;
entryName: string;
entryType: 'file' | 'directory';
provider: string;
model: string;
startedAt: Date;
messages: ChatMessage[];
};
type LogEntry = {
userId: number;
log: TaskLog;
};
const activeLogs = new Map<string, LogEntry>();
let logCounter = 0;
export function createTaskLog(userId: number, taskInfo: TaskInfo, provider: string, model: string): string {
const logId = `log_${Date.now()}_${++logCounter}`;
const log: TaskLog = {
userId,
taskName: taskInfo.taskName,
taskDirName: taskInfo.taskDirName,
entryName: taskInfo.entryName,
entryType: taskInfo.entryType,
provider,
model,
startedAt: new Date(),
messages: [],
};
activeLogs.set(logId, { userId, log });
return logId;
}
export function appendToLog(logId: string, message: ChatMessage) {
const entry = activeLogs.get(logId);
if (!entry) return;
// For tool results, update the existing tool message instead of appending
if (message.role === 'tool' && message.output !== undefined) {
const existing = entry.log.messages.find((m) => m.role === 'tool' && m.toolUseId === message.toolUseId);
if (existing && existing.role === 'tool') {
existing.output = message.output;
existing.isError = message.isError;
return;
}
}
entry.log.messages.push(message);
}
export async function finalizeLog(logId: string) {
const entry = activeLogs.get(logId);
if (!entry) return;
const { log } = entry;
const lastMessage = log.messages[log.messages.length - 1];
const isError = lastMessage?.role === 'result' ? lastMessage.isError : lastMessage?.role === 'error';
try {
await db.insert(schema.taskLogs).values({
userId: log.userId,
taskName: log.taskName,
taskDirName: log.taskDirName,
entryName: log.entryName,
entryType: log.entryType,
provider: log.provider,
model: log.model,
isError: !!isError,
messages: log.messages,
startedAt: log.startedAt,
completedAt: new Date(),
});
} catch (err) {
console.error('[task-logger] Failed to write log:', err);
}
activeLogs.delete(logId);
}
-47
View File
@@ -1,47 +0,0 @@
import { desc, eq } from 'drizzle-orm';
import { db, schema } from 'officerdb';
import { createRouter } from '../../create-router';
export const taskLogsRouter = createRouter();
// GET / — list all logs (metadata only, no messages)
taskLogsRouter.get('/', async (ctx) => {
const userId = ctx.get('user').id;
const logs = await db
.select({
id: schema.taskLogs.id,
taskName: schema.taskLogs.taskName,
taskDirName: schema.taskLogs.taskDirName,
entryName: schema.taskLogs.entryName,
entryType: schema.taskLogs.entryType,
provider: schema.taskLogs.provider,
model: schema.taskLogs.model,
isError: schema.taskLogs.isError,
startedAt: schema.taskLogs.startedAt,
completedAt: schema.taskLogs.completedAt,
})
.from(schema.taskLogs)
.where(eq(schema.taskLogs.userId, userId))
.orderBy(desc(schema.taskLogs.startedAt));
return ctx.json(logs);
});
// GET /:id — return full log with messages
taskLogsRouter.get('/:id', async (ctx) => {
const userId = ctx.get('user').id;
const id = Number(ctx.req.param('id'));
if (Number.isNaN(id)) {
return ctx.text('Invalid id', 400);
}
const [log] = await db.select().from(schema.taskLogs).where(eq(schema.taskLogs.id, id));
if (!log || log.userId !== userId) {
return ctx.text('Not found', 404);
}
return ctx.json(log);
});