Skip to the content

Capsid menu

src/portal-stale.ts

21 lines and 1 functions in DrDustinEdwards/capsid, at commit daa875b on 2026-10-24. This file on GitHub.

Functions

The functions of portal-stale.ts
FunctionLinesComplexityCoveredCRAPMutation
handlePortalStale18 to 24386%OkCRAP 3No data: mutation testing has not run on this repo yet

Imports

It imports src/env.ts, src/ops-feed.ts, src/ops-types/views.ts, src/portal-auth.ts, src/stale-jobs.ts. Imported by src/routes.ts.

Source

import type { Env } from "./env";
import { OPS_RETURN_TO } from "./ops-feed";
import type { PortalStale } from "./ops-types/views";
import { portalGate } from "./portal-auth";
import { staleJobs } from "./stale-jobs";

// The Portal's stale jobs: GET /portal/api/stale (capsid/research/design-stale-jobs.md,
// settled: its own route, since OPS_FEED_READS is pinned and the feed does not grow a
// read). The same reader as `capsid_work_jobs` action list with stale: true (src/stale-jobs.ts), so
// the Portal and chat cannot disagree, over every namespace. It costs no GitHub reads:
// the pull request states come from the cache the merge-resume step writes. A route, not
// a tool, so no grant is checked here (one enforcement point, test/route-gates.test.ts): it answers
// to portalGate, and src/scope.ts lists it among the routes gated some other way.

export const PORTAL_STALE_PATH = "/portal/api/stale";

/** GET /portal/api/stale: the jobs that look stuck, each with its rule and reason. */
export async function handlePortalStale(request: Request, env: Env, now: Date = new Date()): Promise<Response> {
  const gate = await portalGate(request, env, now, OPS_RETURN_TO);
  if (!gate.ok) return gate.response;
  const read = await staleJobs(env, now);
  const body: PortalStale = { generated: now.toISOString(), rows: read.rows, truncated: read.truncated, note: read.note ?? null };
  return new Response(JSON.stringify(body), { headers: { "Content-Type": "application/json", "Cache-Control": "no-store" } });
}