Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs/PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ Fixtures pinned under `fixtures/real-*.json` (bundle at head SHA). Highlights: "
3. Skipped/neutral/stale checks are ignored instead of making CI look "pending" forever.
4. `sanitizePrBody()` strips bot sections (e.g. Greptile's `<!-- greptile_comment -->` block), HTML comments and tags before extraction; the quote guard runs on the sanitized text.
5. CONTRADICTED below confidence 0.75 is downgraded to UNVERIFIED.
6. `npm run eval` runs every `fixtures/real-*.json` through the live models against `fixtures/expectations.json`. After the fixes: docs#46488 PASS, Cap#3412 MIXED (was a false FAIL), dispenso#129 MIXED, no CONTRADICTED anywhere. Extraction is not deterministic on Bedrock even at temperature 0, so expectations on single claims are "if extracted", not "required".
6. A failed CI check is not always a code failure. Cap's `Format (Biome)` died on `Unexpected HTTP response: 504` before running Biome, and its Vercel status failed on "Authorization required to deploy". The fetch now reads each failed run's first failure annotation (and legacy status descriptions); infrastructure failures and cancelled runs count as "ignored", and a CONTRADICTED verdict resting only on CI checks must cite one that really failed.
7. `npm run eval` runs every `fixtures/real-*.json` through the live models against `fixtures/expectations.json`. After the fixes: docs#46488 PASS, Cap#3412 MIXED (was a false FAIL, twice), dispenso#129 MIXED, no CONTRADICTED anywhere. Extraction is not deterministic on Bedrock even at temperature 0, so expectations on single claims are "if extracted", not "required".
- CLI quirk (Windows PowerShell): `npm run verify -- <url> --fresh --dump-fixture x` drops the unknown flags in npm's argument forwarding (the URL still works, and the flag VALUES become positionals!). `npx tsx scripts/verify.ts <url> --json --fresh --dump-fixture x` works everywhere — document it in the README (P4).

## 6. Signals (deterministic, computed once per PR, no LLM)
Expand Down
6 changes: 6 additions & 0 deletions fixtures/expectations.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@
"verdict": "UNVERIFIED",
"required": true
},
{
"$comment": "'Format (Biome)' failed on 'Unexpected HTTP response: 504' while setting up (it never ran Biome), and 'Lint (Biome)' passed. A Biome claim must not be CONTRADICTED by an infrastructure failure.",
"type": "noClaimVerdict",
"textIncludes": "biome",
"verdict": "CONTRADICTED"
},
{
"$comment": "A tests_pass contradiction is legitimate only if it cites a test-kind check.",
"type": "contradictedMustCiteKind",
Expand Down
95 changes: 63 additions & 32 deletions fixtures/real-failing-ci.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,175 +66,204 @@
"name": "Rust tests (Linux)",
"status": "completed",
"conclusion": "skipped",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121753574971"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121753574971",
"failureMessage": null
},
{
"name": "GPUI patch tests (${{ matrix.settings.name }})",
"status": "completed",
"conclusion": "skipped",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121753574389"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121753574389",
"failureMessage": null
},
{
"name": "Verify Tauri plugin versions",
"status": "completed",
"conclusion": "skipped",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121753574201"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121753574201",
"failureMessage": null
},
{
"name": "Clippy",
"status": "completed",
"conclusion": "skipped",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121753573877"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121753573877",
"failureMessage": null
},
{
"name": "Build GPUI Desktop",
"status": "completed",
"conclusion": "skipped",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121753573825"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121753573825",
"failureMessage": null
},
{
"name": "matrix.platform",
"status": "completed",
"conclusion": "skipped",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374015733/job/121752900251"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374015733/job/121752900251",
"failureMessage": null
},
{
"name": "Deep review label",
"status": "completed",
"conclusion": "skipped",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374015733/job/121752899954"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374015733/job/121752899954",
"failureMessage": null
},
{
"name": "Engine verify",
"status": "completed",
"conclusion": "success",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374015733/job/121752897420"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374015733/job/121752897420",
"failureMessage": null
},
{
"name": "Perf budget",
"status": "completed",
"conclusion": "success",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014999/job/121752729176"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014999/job/121752729176",
"failureMessage": null
},
{
"name": "measure",
"status": "completed",
"conclusion": "skipped",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014999/job/121752715981"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014999/job/121752715981",
"failureMessage": null
},
{
"name": "Socket Security: Pull Request Alerts",
"status": "completed",
"conclusion": "success",
"htmlUrl": "https://github.com/CapSoftware/Cap/runs/121752164629"
"htmlUrl": "https://github.com/CapSoftware/Cap/runs/121752164629",
"failureMessage": null
},
{
"name": "real-capture",
"status": "completed",
"conclusion": "skipped",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014928/job/121752163261"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014928/job/121752163261",
"failureMessage": null
},
{
"name": "real-capture (windows-2022)",
"status": "completed",
"conclusion": "skipped",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014928/job/121752163083"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014928/job/121752163083",
"failureMessage": null
},
{
"name": "Verify recording",
"status": "completed",
"conclusion": "success",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014928/job/121752161138"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014928/job/121752161138",
"failureMessage": null
},
{
"name": "Vercel Agent Review",
"status": "completed",
"conclusion": "neutral",
"htmlUrl": "https://github.com/CapSoftware/Cap/runs/121752116939"
"htmlUrl": "https://github.com/CapSoftware/Cap/runs/121752116939",
"failureMessage": null
},
{
"name": "Superagent Security Scan",
"status": "completed",
"conclusion": "success",
"htmlUrl": "https://github.com/CapSoftware/Cap/runs/121752093068"
"htmlUrl": "https://github.com/CapSoftware/Cap/runs/121752093068",
"failureMessage": null
},
{
"name": "Contributor trust",
"status": "completed",
"conclusion": "neutral",
"htmlUrl": "https://github.com/CapSoftware/Cap/runs/121752086725"
"htmlUrl": "https://github.com/CapSoftware/Cap/runs/121752086725",
"failureMessage": null
},
{
"name": "Socket Security: Project Report",
"status": "completed",
"conclusion": "success",
"htmlUrl": "https://github.com/CapSoftware/Cap/runs/121752040618"
"htmlUrl": "https://github.com/CapSoftware/Cap/runs/121752040618",
"failureMessage": null
},
{
"name": "Vercel Preview Comments",
"status": "completed",
"conclusion": "success",
"htmlUrl": "https://github.com/CapSoftware/Cap/runs/121752040311"
"htmlUrl": "https://github.com/CapSoftware/Cap/runs/121752040311",
"failureMessage": null
},
{
"name": "Greptile Review",
"status": "completed",
"conclusion": "success",
"htmlUrl": "https://github.com/CapSoftware/Cap/runs/121752028683"
"htmlUrl": "https://github.com/CapSoftware/Cap/runs/121752028683",
"failureMessage": null
},
{
"name": "Plan",
"status": "completed",
"conclusion": "success",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374015733/job/121752017555"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374015733/job/121752017555",
"failureMessage": null
},
{
"name": "Typecheck",
"status": "completed",
"conclusion": "success",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121752017000"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121752017000",
"failureMessage": null
},
{
"name": "Format (Cargo)",
"status": "completed",
"conclusion": "success",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121752016774"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121752016774",
"failureMessage": null
},
{
"name": "Detect Changes",
"status": "completed",
"conclusion": "success",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121752016655"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121752016655",
"failureMessage": null
},
{
"name": "Lint (Biome)",
"status": "completed",
"conclusion": "success",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121752016636"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121752016636",
"failureMessage": null
},
{
"name": "Format (Biome)",
"status": "completed",
"conclusion": "failure",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121752016536"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121752016536",
"failureMessage": "Unexpected HTTP response: 504"
},
{
"name": "Mobile",
"status": "completed",
"conclusion": "success",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121752016219"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014764/job/121752016219",
"failureMessage": null
},
{
"name": "Scope",
"status": "completed",
"conclusion": "success",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014928/job/121752012623"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014928/job/121752012623",
"failureMessage": null
},
{
"name": "scope",
"status": "completed",
"conclusion": "success",
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014999/job/121752012479"
"htmlUrl": "https://github.com/CapSoftware/Cap/actions/runs/40374014999/job/121752012479",
"failureMessage": null
}
],
"combinedStatus": {
Expand All @@ -243,16 +272,18 @@
{
"context": "Vercel – cap-status",
"state": "success",
"targetUrl": "https://vercel.com/mc-ilroy/cap-status/7JEpFXmFbEWxyxt7bPaZK6VaxZFD"
"targetUrl": "https://vercel.com/mc-ilroy/cap-status/7JEpFXmFbEWxyxt7bPaZK6VaxZFD",
"description": "Skipped - Not affected"
},
{
"context": "Vercel – cap-web",
"state": "failure",
"targetUrl": "https://vercel.com/git/authorize?team=Cap%20Software%20Inc&slug=mc-ilroy&teamId=team_vbZRU7UW78rpKKIj4c9PfFAC&type=github&job=%7B%22headInfo%22%3A%7B%22sha%22%3A%2292cc9220a77f97d784a6a711facda0ee8c53e3e4%22%7D%2C%22id%22%3A%22QmYD5yvL6jWwdRGE7p9Wa6hhuKz4FvTWPPRBebrgkY5UtK%22%2C%22org%22%3A%22CapSoftware%22%2C%22prId%22%3A3412%2C%22repo%22%3A%22Cap%22%7D"
"targetUrl": "https://vercel.com/git/authorize?team=Cap%20Software%20Inc&slug=mc-ilroy&teamId=team_vbZRU7UW78rpKKIj4c9PfFAC&type=github&job=%7B%22headInfo%22%3A%7B%22sha%22%3A%2292cc9220a77f97d784a6a711facda0ee8c53e3e4%22%7D%2C%22id%22%3A%22QmYD5yvL6jWwdRGE7p9Wa6hhuKz4FvTWPPRBebrgkY5UtK%22%2C%22org%22%3A%22CapSoftware%22%2C%22prId%22%3A3412%2C%22repo%22%3A%22Cap%22%7D",
"description": "Authorization required to deploy."
}
]
},
"fetchedAt": "2026-10-26T18:30:46.321Z",
"fetchedAt": "2026-10-26T19:08:19.974Z",
"filesTruncated": false,
"commitsTruncated": false
}
70 changes: 70 additions & 0 deletions src/app/api/verify/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/**
* POST /api/verify { prUrl, fresh? } -> NDJSON stream of pipeline events
* (one JSON object per line). The final line is either a `done` event
* carrying the full Report or an `error` event.
*/
import { z } from "zod";
import { parsePrUrl } from "@/core/github";
import { runReceipts, type PipelineEvent } from "@/core/pipeline";
import { clientKey, takeRun } from "@/lib/ratelimit";

// Long PRs with Kimi K3 reasoning can take ~45 s; Fluid compute allows 300 s.
export const maxDuration = 300;

const RequestSchema = z.object({
prUrl: z.string().trim().min(1).max(500),
fresh: z.boolean().optional(),
});

export async function POST(request: Request): Promise<Response> {
let body: z.infer<typeof RequestSchema>;
try {
body = RequestSchema.parse(await request.json());
} catch {
return Response.json({ error: "Send JSON like {\"prUrl\": \"https://github.com/o/r/pull/1\"}." }, { status: 400 });
}
try {
parsePrUrl(body.prUrl);
} catch (error) {
return Response.json({ error: error instanceof Error ? error.message : "Not a GitHub PR URL." }, { status: 400 });
}

const limit = takeRun(clientKey(request.headers));
if (!limit.ok) {
const minutes = Math.ceil(limit.retryAfterSec / 60);
return Response.json(
{ error: `Rate limit reached (10 runs per hour). Try again in ${minutes} min, or self-host Receipts.` },
{ status: 429, headers: { "Retry-After": String(limit.retryAfterSec) } },
);
}

const encoder = new TextEncoder();
const stream = new ReadableStream<Uint8Array>({
async start(controller) {
let finished = false;
const send = (event: PipelineEvent): void => {
if (event.type === "done" || event.type === "error") finished = true;
controller.enqueue(encoder.encode(`${JSON.stringify(event)}\n`));
};
try {
await runReceipts(body.prUrl, { onEvent: send, fresh: body.fresh });
} catch (error) {
// runReceipts emits its own error event, except for config errors
// thrown before the pipeline starts.
if (!finished) {
send({ type: "error", error: error instanceof Error ? error.message : String(error) });
}
} finally {
controller.close();
}
},
});

return new Response(stream, {
headers: {
"Content-Type": "application/x-ndjson; charset=utf-8",
"Cache-Control": "no-store",
"X-Accel-Buffering": "no",
},
});
}
Loading