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
13 changes: 12 additions & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,15 @@ author GitHub ID and login supplied by the reviewable pull-request webhook.
Billing counts distinct GitHub author IDs on private pull requests within the
entitlement period; bot and service identities count by the same ID rule.

Each review snapshots a closed trigger class and its signed-webhook context at
creation: automatic pull-request event, explicit review command, GitHub check
rerun, or unknown. Historical rows and rolling-deploy jobs without evidence use
unknown; the system does not infer an origin. A database trigger makes review
provenance immutable. Usage events copy the trigger class so billing attribution
survives removal of the related repository or review row. Conversational replies
remain `respond` jobs, record their GitHub mention context in the job payload,
and use the separate `github_mention` usage class.

## Dashboard

The signed-in product surface is three pages, all server-rendered and
Expand All @@ -168,7 +177,9 @@ noindexed:
summary, findings (severity, kind, confidence, sha-pinned GitHub file
links), resolved findings, retained policy-suppressed findings in collapsed
detail, suppressed/ungrounded counts, gate verdict,
model, token usage, timing, and kind-blocking override state.
model, token usage, timing, immutable trigger provenance, and kind-blocking
override state. The recent-review table exposes the same trigger class and
includes it in text filtering.

Authorization is uniform: every page and server action re-derives access from
the session. A sealed GitHub OAuth credential refreshes the complete active
Expand Down
43 changes: 43 additions & 0 deletions drizzle/0029_review_trigger_provenance.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
ALTER TABLE "reviews" ADD COLUMN "trigger_source" text DEFAULT 'unknown' NOT NULL;--> statement-breakpoint
ALTER TABLE "reviews" ADD COLUMN "trigger_context" jsonb;--> statement-breakpoint
ALTER TABLE "usage_events" ADD COLUMN "trigger_source" text DEFAULT 'unknown' NOT NULL;--> statement-breakpoint
ALTER TABLE "reviews" ADD CONSTRAINT "reviews_trigger_source_check" CHECK ("reviews"."trigger_source" IN ('unknown', 'automatic_pull_request', 'requested_review', 'github_check_rerun')) NOT VALID;--> statement-breakpoint
ALTER TABLE "reviews" ADD CONSTRAINT "reviews_trigger_context_check" CHECK (
("reviews"."trigger_source" = 'unknown' AND ("reviews"."trigger_context" IS NULL OR "reviews"."trigger_context" = '{"source":"unknown"}'::jsonb))
OR (
"reviews"."trigger_source" <> 'unknown'
AND "reviews"."trigger_context" IS NOT NULL
AND jsonb_typeof("reviews"."trigger_context") = 'object'
AND "reviews"."trigger_context" - ARRAY['source', 'webhookDeliveryId', 'webhookEvent', 'webhookAction', 'sourceCommentId', 'sourceUrl', 'requestedByGithubId', 'requestedByLogin', 'checkName']::text[] = '{}'::jsonb
AND "reviews"."trigger_context"->>'source' = "reviews"."trigger_source"
AND jsonb_typeof("reviews"."trigger_context"->'webhookDeliveryId') = 'string'
AND COALESCE(length(btrim("reviews"."trigger_context"->>'webhookDeliveryId')), 0) > 0
AND length("reviews"."trigger_context"->>'webhookDeliveryId') <= 200
AND (
("reviews"."trigger_source" = 'automatic_pull_request' AND "reviews"."trigger_context"->>'webhookEvent' = 'pull_request')
OR ("reviews"."trigger_source" = 'requested_review' AND "reviews"."trigger_context"->>'webhookEvent' IN ('issue_comment', 'pull_request_review_comment'))
OR ("reviews"."trigger_source" = 'github_check_rerun' AND "reviews"."trigger_context"->>'webhookEvent' IN ('check_run', 'check_suite'))
)
AND (NOT "reviews"."trigger_context" ? 'webhookAction' OR (jsonb_typeof("reviews"."trigger_context"->'webhookAction') = 'string' AND length("reviews"."trigger_context"->>'webhookAction') <= 100))
AND (NOT "reviews"."trigger_context" ? 'sourceCommentId' OR (jsonb_typeof("reviews"."trigger_context"->'sourceCommentId') = 'number' AND ("reviews"."trigger_context"->>'sourceCommentId')::numeric = trunc(("reviews"."trigger_context"->>'sourceCommentId')::numeric) AND ("reviews"."trigger_context"->>'sourceCommentId')::numeric BETWEEN 1 AND 9007199254740991))
AND (NOT "reviews"."trigger_context" ? 'sourceUrl' OR (jsonb_typeof("reviews"."trigger_context"->'sourceUrl') = 'string' AND length("reviews"."trigger_context"->>'sourceUrl') <= 2048 AND "reviews"."trigger_context"->>'sourceUrl' ~* '^https://github[.]com([/?#]|$)'))
AND (NOT "reviews"."trigger_context" ? 'requestedByGithubId' OR (jsonb_typeof("reviews"."trigger_context"->'requestedByGithubId') = 'number' AND ("reviews"."trigger_context"->>'requestedByGithubId')::numeric = trunc(("reviews"."trigger_context"->>'requestedByGithubId')::numeric) AND ("reviews"."trigger_context"->>'requestedByGithubId')::numeric BETWEEN 1 AND 9007199254740991))
AND (NOT "reviews"."trigger_context" ? 'requestedByLogin' OR (jsonb_typeof("reviews"."trigger_context"->'requestedByLogin') = 'string' AND length("reviews"."trigger_context"->>'requestedByLogin') <= 100))
AND (NOT "reviews"."trigger_context" ? 'checkName' OR (jsonb_typeof("reviews"."trigger_context"->'checkName') = 'string' AND length("reviews"."trigger_context"->>'checkName') <= 200))
)
) NOT VALID;--> statement-breakpoint
ALTER TABLE "usage_events" ADD CONSTRAINT "usage_events_trigger_source_check" CHECK ("usage_events"."trigger_source" IN ('unknown', 'automatic_pull_request', 'requested_review', 'github_check_rerun', 'github_mention')) NOT VALID;--> statement-breakpoint
CREATE OR REPLACE FUNCTION reject_review_trigger_provenance_update() RETURNS trigger
LANGUAGE plpgsql
AS $$
BEGIN
IF NEW.trigger_source IS DISTINCT FROM OLD.trigger_source
OR NEW.trigger_context IS DISTINCT FROM OLD.trigger_context THEN
RAISE EXCEPTION 'review trigger provenance is immutable';
END IF;
RETURN NEW;
END;
$$;--> statement-breakpoint
CREATE TRIGGER reviews_trigger_provenance_immutable
BEFORE UPDATE OF trigger_source, trigger_context ON reviews
FOR EACH ROW EXECUTE FUNCTION reject_review_trigger_provenance_update();
Loading
Loading