refactor(ai): re-check every chokepoint through the one egress helper

The status chokepoint, the comprehensive briefing, reaction lines and
AI-composed nudges now ask `aiEgressRefusal` about exactly the chain they
are about to send to, instead of each pairing the operator-key check
with a surface receipt lookup of its own. The consent kinds come from the
capability table, so the wire and the published capability answer from
the same rule.

The document summary, its catch-up pass and the index backfill stop on a
closed switch or module before anything is picked, and leave "no
provider" and "consent needed" to the document pick, which re-checks the
wire for the provider it chose. A closed switch still only heals PENDING
to NONE; a withheld pick records UNAVAILABLE. The per-pick document
consent helper is removed, since nothing calls it any more.

The auto-read toggle mints the shared `AI_EXTRACTION_CONSENT_KIND`, and
the web grant helper goes back to minting the master grant only.

Refs #975
This commit is contained in:
Marc-André Bombeck committed 2026-09-24 09:29:00 +02:00
1 parent f158f01f18
commit ce72fe226e
33 files changed
+628 -637

No files matched your search

@@ -37,6 +37,7 @@ import { annotate } from "@/lib/logging/context";
import { auditLog } from "@/lib/auth/audit";
import { getAiCapability } from "@/lib/ai/capabilities/gate";
import { AI_CAPABILITIES } from "@/lib/ai/capabilities/types";
import { AI_EXTRACTION_CONSENT_KIND } from "@/lib/consent/receipts";
import { prisma } from "@/lib/db";
import { isP2002 } from "@/lib/prisma-errors";
import { enqueueSummaryCatchUp } from "@/lib/jobs/document-summary-catchup";
@@ -57,7 +58,7 @@ type DocumentsAutoAiReadResponse = {
* master `ai_full`. The capability table lists it (with `ai_full`) as what
* satisfies the document consent rule.
*/
const AUTO_READ_CONSENT_KIND = "ai_extraction";
const AUTO_READ_CONSENT_KIND = AI_EXTRACTION_CONSENT_KIND;
/**
* Mint the extraction receipt unless one that satisfies document reads is