import { codexUnreadTriageForTest } from "./src/code-queue"; type JsonRecord = Record; type RequestRecord = { path: string; method: string; body: unknown }; function assertCondition(condition: unknown, message: string, detail: JsonRecord = {}): void { if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`); } function asRecord(value: unknown): JsonRecord { assertCondition(typeof value === "object" && value !== null && !Array.isArray(value), "expected JSON object", { value }); return value as JsonRecord; } function asArray(value: unknown): unknown[] { assertCondition(Array.isArray(value), "expected JSON array", { value }); return value as unknown[]; } function task(id: string, queueId: string, status: string, updatedAt: string, readAt: string | null, prompt: string): JsonRecord { return { id, queueId, status, currentAttempt: 1, updatedAt, finishedAt: status === "running" ? null : updatedAt, readAt, displayPrompt: prompt, basePrompt: prompt, prompt, cwd: "/root/unidesk", referenceTaskIds: [], lastError: null, }; } function fixtureResponse(path: string, init?: { method?: string; body?: unknown }, requests: RequestRecord[] = []): JsonRecord { const method = init?.method ?? "GET"; requests.push({ path, method, body: init?.body }); if (path.endsWith("/read")) { const taskId = decodeURIComponent(path.split("/api/tasks/")[1]?.split("/")[0] ?? "unknown"); return { ok: true, status: 200, body: { ok: true, task: task(taskId, "default", "succeeded", "2026-05-22T00:10:00.000Z", "2026-05-22T00:10:01.000Z", "read response prompt should stay compact"), queue: { counts: { succeeded: 1 }, unreadTerminal: 0 }, }, }; } assertCondition(path.startsWith("/api/microservices/code-queue/proxy/api/tasks/overview"), "unexpected path", { path, method }); return { ok: true, status: 200, body: { ok: true, queue: { unreadTerminal: 4 }, pagination: { limit: 200, returned: 6, total: 6, hasMore: false, nextBeforeId: null, includeActive: true, }, tasks: [ task("task-new-1", "default", "succeeded", "2026-05-22T00:05:00.000Z", null, "pikasTech/unidesk#20 closeout RAW_PROMPT_SHOULD_NOT_LEAK"), task("task-new-2", "review", "failed", "2026-05-22T00:04:00.000Z", null, "https://github.com/pikasTech/unidesk/issues/20 failed task RAW_PROMPT_SHOULD_NOT_LEAK"), task("task-new-3", "review", "canceled", "2026-05-22T00:03:00.000Z", null, "pikasTech/unidesk#21 canceled task RAW_PROMPT_SHOULD_NOT_LEAK"), task("task-unknown", "misc", "succeeded", "2026-05-22T00:02:00.000Z", null, "no repo marker RAW_PROMPT_SHOULD_NOT_LEAK"), task("task-read", "default", "succeeded", "2026-05-22T00:01:00.000Z", "2026-05-22T00:01:01.000Z", "pikasTech/unidesk#20 already read RAW_PROMPT_SHOULD_NOT_LEAK"), task("task-running", "default", "running", "2026-05-22T00:00:00.000Z", null, "pikasTech/unidesk#20 running RAW_PROMPT_SHOULD_NOT_LEAK"), ], }, }; } function countItems(bucket: JsonRecord): JsonRecord[] { return asArray(bucket.items).map(asRecord); } function itemCount(bucket: JsonRecord, key: string): number { const row = countItems(bucket).find((item) => item.key === key); return typeof row?.count === "number" ? row.count : 0; } export function runCodeQueueUnreadTriageContract(): JsonRecord { const requests: RequestRecord[] = []; const fetcher = (path: string, init?: { method?: string; body?: unknown }): JsonRecord => fixtureResponse(path, init, requests); const summary = codexUnreadTriageForTest(["--limit", "2"], fetcher); const summaryBody = JSON.stringify(summary); const triage = asRecord(asRecord(summary).unreadTriage); const counts = asRecord(triage.counts); const newest = asRecord(triage.newest); const newestItems = asArray(newest.items).map(asRecord); const commands = asRecord(triage.commands); assertCondition(asRecord(summary).ok === true, "default unread triage should succeed", asRecord(summary)); assertCondition(triage.readOnly === true && triage.bounded === true, "default unread triage must be read-only and bounded", triage); assertCondition(counts.totalUnreadTerminal === 4, "counts should include only unread terminal tasks", counts); assertCondition(itemCount(asRecord(counts.byRepo), "pikasTech/unidesk") === 3, "repo counts should group owner/name refs", counts); assertCondition(itemCount(asRecord(counts.byIssue), "#20") === 2, "issue counts should group issue refs", counts); assertCondition(itemCount(asRecord(counts.byStatus), "succeeded") === 2, "status counts should include terminal statuses", counts); assertCondition(itemCount(asRecord(counts.byQueue), "review") === 2, "queue counts should include queues", counts); assertCondition(newest.returned === 2 && newest.hasMore === true, "newest items should obey --limit and expose pagination", newest); assertCondition(newestItems.every((item) => item.commands === undefined && typeof item.nextStep === "string"), "default unread rows must stay compact without repeated per-task command blocks", { newestItems }); assertCondition(typeof commands.perTaskRead === "string" && String(commands.perTaskRead).includes("codex read "), "triage should preserve per-task read drill-down", commands); assertCondition(typeof commands.full === "string" && String(commands.full).includes("codex unread") && String(commands.full).includes("--full"), "triage should expose one full-view expansion command", commands); assertCondition(!summaryBody.includes("RAW_PROMPT_SHOULD_NOT_LEAK"), "triage output must not dump raw prompt text", { summaryBody }); assertCondition(!requests.some((request) => request.path.includes("/summary")), "triage must not fetch per-task summaries by default", { requests }); assertCondition(!requests.some((request) => request.method === "POST"), "default triage must not mutate", { requests }); const full = codexUnreadTriageForTest(["--view", "full", "--limit", "2"], fetcher); const fullBody = JSON.stringify(full); const fullTriage = asRecord(asRecord(full).unreadTriage); const fullNewest = asRecord(fullTriage.newest); const fullItems = asArray(fullNewest.items).map(asRecord); const fullItemCommands = fullItems.map((item) => asRecord(item.commands)); assertCondition(asRecord(fullTriage.filters).view === "full", "codex unread --view full should disclose full view", fullTriage); assertCondition(fullItems.length === 2 && fullItemCommands.every((item) => typeof item.detail === "string" && typeof item.read === "string"), "explicit full unread view should expand per-task commands", { fullItems }); assertCondition(summaryBody.length < fullBody.length, "default unread summary should stay smaller than explicit full view", { summaryChars: summaryBody.length, fullChars: fullBody.length }); const guardStart = requests.length; const guarded = codexUnreadTriageForTest(["mark-read", "--repo", "pikasTech/unidesk", "--issue", "20", "--limit", "2"], fetcher); const guardedTriage = asRecord(asRecord(guarded).unreadTriage); const guardedMutation = asRecord(guardedTriage.mutation); assertCondition(asRecord(guarded).ok === false, "batch mark-read without --confirm should fail closed", asRecord(guarded)); assertCondition(guardedMutation.blocked === true && guardedMutation.confirmed === false, "confirm guard should describe blocked mutation", guardedMutation); assertCondition(!requests.slice(guardStart).some((request) => request.method === "POST"), "missing --confirm must not POST read calls", { requests: requests.slice(guardStart) }); const confirmStart = requests.length; const confirmed = codexUnreadTriageForTest(["mark-read", "--repo", "pikasTech/unidesk", "--issue", "20", "--limit", "2", "--confirm"], fetcher); const confirmedTriage = asRecord(asRecord(confirmed).unreadTriage); const confirmedMutation = asRecord(confirmedTriage.mutation); const readPosts = requests.slice(confirmStart).filter((request) => request.method === "POST" && request.path.endsWith("/read")); assertCondition(asRecord(confirmed).ok === true, "confirmed batch mark-read should succeed", asRecord(confirmed)); assertCondition(confirmedTriage.readOnly === false && confirmedMutation.confirmed === true, "confirmed mutation should be explicit", confirmedMutation); assertCondition(confirmedMutation.attempted === 2 && confirmedMutation.succeeded === 2, "confirmed mutation should respect filters and limit", confirmedMutation); assertCondition(readPosts.length === 2 && readPosts[0]?.path.includes("task-new-1") && readPosts[1]?.path.includes("task-new-2"), "confirmed mutation should POST newest filtered task reads only", { readPosts }); return { ok: true, checks: [ "default unread triage is read-only", "repo/issue/status/queue counts are present", "newest items are bounded", "default rows avoid repeated per-task command blocks", "--view full expands per-task commands", "raw prompt text is omitted", "batch mark-read requires --confirm", "confirmed batch read respects filters and limit", ], summaryChars: summaryBody.length, fullChars: fullBody.length, }; } if (import.meta.main) { process.stdout.write(`${JSON.stringify(runCodeQueueUnreadTriageContract(), null, 2)}\n`); }