Found while verifying #9136. Not named anywhere in that issue.
handleInternalDecision (src/review/ops.ts), routed at /v1/internal/decision and bearer-protected, looked its target up with:
const id = rowId(config.slug, kind, repo, number);
const target = await storage(env).prepare(`SELECT * FROM review_targets WHERE id = ?`).bind(id).first();
if (!target) return Response.json({ error: "no such target", id }, { status: 404 });
review_targets has had no writer since the 2026-06-22 convergence cutover. So this endpoint has returned 404 for every pull request opened since then — silently, while remaining routed, authenticated, and documented as the "explain any verdict on demand" surface.
A second, independent break in the same handler
The audit-trail query bound rowId(...) — project:kind:owner/repo#n — against review_audit.target_id, which is owner/repo#n. Different namespaces, so the trail came back empty even where rows existed. This one would have survived any repopulation of review_targets.
Fields with no live equivalent
decided_sha and approved_sha (the per-head-SHA approve-once cache) were dropped as a concept at the cutover, so the endpoint cannot be restored to its exact former shape. The nearest honest answer is decision_records.head_sha — the sha the standing decision was actually made on, which is what decided_sha was a cache of.
Likewise review_targets' processing-status enum (queued/reviewing/error/error_retryable) has no live source; the realized disposition (merged/closed/open) does, and is the half every consumer reads.
Found while verifying #9136. Not named anywhere in that issue.
handleInternalDecision(src/review/ops.ts), routed at/v1/internal/decisionand bearer-protected, looked its target up with:review_targetshas had no writer since the 2026-06-22 convergence cutover. So this endpoint has returned 404 for every pull request opened since then — silently, while remaining routed, authenticated, and documented as the "explain any verdict on demand" surface.A second, independent break in the same handler
The audit-trail query bound
rowId(...)—project:kind:owner/repo#n— againstreview_audit.target_id, which isowner/repo#n. Different namespaces, so the trail came back empty even where rows existed. This one would have survived any repopulation ofreview_targets.Fields with no live equivalent
decided_shaandapproved_sha(the per-head-SHA approve-once cache) were dropped as a concept at the cutover, so the endpoint cannot be restored to its exact former shape. The nearest honest answer isdecision_records.head_sha— the sha the standing decision was actually made on, which is whatdecided_shawas a cache of.Likewise
review_targets' processing-status enum (queued/reviewing/error/error_retryable) has no live source; the realized disposition (merged/closed/open) does, and is the half every consumer reads.