From d99b557f542cece545713a30f95b3299f25b606d Mon Sep 17 00:00:00 2001 From: bordumb Date: Sun, 27 Sep 2026 16:29:42 +0100 Subject: [PATCH 1/2] fix(api): stop writing pre-013 investigation columns and remove approvals Since 013_unified_investigation.sql the investigations table only has id, tenant_id, alert, main_branch_id, outcome, created_at, created_by, root_hash and a generated status. Four call sites still used the old schema: - Snapshot import inserted a value into the generated status column (GeneratedAlwaysError). It now lets status derive from outcome. - The EE spawn_investigation automation action queried investigations.issue_id and inserted status/source/metadata. It now builds a valid AnomalyAlert from the issue and starts the investigation through InvestigationStarterService (row + Temporal workflow), links it via issue_investigation_runs (trigger_type 'rule'), reuses an existing run, and takes the datasource from a datasource_id param or the tenant's only datasource. ExecutionContext gains a required investigation_starter. - AppDatabase.create_investigation had no callers; deleted. - The approvals stack targeted the pre-013 approval_requests table, which 013 re-keyed on branch/snapshot ids that Temporal investigations never create, and nothing creates approval requests. Removed the /approvals routes, the AppDatabase approval methods and update_investigation_status, the dashboard pending_approvals stat, the unused approval notification producers (service, email, Slack), and the ContextReviewPage UI, route, notification link and settings entries. The committed OpenAPI spec loses only the approvals paths/schemas and pending_approvals; the client is regenerated from it and the stale approval model files are removed. Tests run against the real migrated schema via the migrated_db fixture. tests/integration/conftest.py is byte-identical to the one in #162; EE integration tests load that module by path. Co-Authored-By: Claude Opus 5.5 Signed-off-by: Claude --- .flow/tasks/fn-16.13.json | 21 + .flow/tasks/fn-16.13.md | 19 + .flow/tasks/fn-16.14.json | 21 + .flow/tasks/fn-16.14.md | 22 + .flow/tasks/fn-16.15.json | 22 + .flow/tasks/fn-16.15.md | 17 + .flow/tasks/fn-16.16.json | 23 + .flow/tasks/fn-16.16.md | 21 + frontend/app/src/App.tsx | 9 - .../src/features/dashboard/dashboard-page.tsx | 37 +- .../investigation/ContextReviewPage.tsx | 178 ----- .../features/investigation/context-review.tsx | 194 ----- .../components/notification-card.tsx | 42 -- .../settings/notification-settings.tsx | 5 - .../features/settings/webhook-settings.tsx | 1 - frontend/app/src/lib/api/dashboard.ts | 2 - .../lib/api/generated/approvals/approvals.ts | 700 ------------------ .../lib/api/model/approvalDecisionResponse.ts | 20 - .../model/approvalDecisionResponseComment.ts | 9 - .../lib/api/model/approvalRequestResponse.ts | 36 - .../model/approvalRequestResponseComment.ts | 9 - .../model/approvalRequestResponseContext.ts | 9 - .../model/approvalRequestResponseDatasetId.ts | 9 - .../model/approvalRequestResponseDecidedAt.ts | 9 - .../model/approvalRequestResponseDecidedBy.ts | 9 - .../model/approvalRequestResponseDecision.ts | 9 - .../approvalRequestResponseMetricName.ts | 9 - .../approvalRequestResponseModifications.ts | 11 - ...provalRequestResponseModificationsAnyOf.ts | 11 - .../model/approvalRequestResponseSeverity.ts | 9 - .../app/src/lib/api/model/approveRequest.ts | 15 - .../lib/api/model/approveRequestComment.ts | 9 - .../lib/api/model/createApprovalRequest.ts | 18 - .../api/model/createApprovalRequestContext.ts | 9 - .../app/src/lib/api/model/dashboardStats.ts | 1 - frontend/app/src/lib/api/model/index.ts | 22 - .../app/src/lib/api/model/modifyRequest.ts | 17 - .../src/lib/api/model/modifyRequestComment.ts | 9 - .../api/model/modifyRequestModifications.ts | 9 - .../lib/api/model/pendingApprovalsResponse.ts | 16 - .../app/src/lib/api/model/rejectRequest.ts | 18 - frontend/app/src/lib/api/query-keys.ts | 7 - .../dataing_ee/core/automation/executor.py | 109 ++- .../dataing-ee/tests/integration/conftest.py | 18 + .../tests/integration/core/__init__.py | 0 .../integration/core/automation/__init__.py | 0 .../automation/test_spawn_investigation.py | 223 ++++++ python-packages/dataing/openapi.json | 669 +---------------- .../dataing/src/dataing/adapters/db/app_db.py | 150 ---- .../dataing/adapters/notifications/email.py | 54 -- .../dataing/adapters/notifications/slack.py | 21 - .../entrypoints/api/routes/__init__.py | 2 - .../entrypoints/api/routes/approvals.py | 361 --------- .../entrypoints/api/routes/dashboard.py | 3 - .../entrypoints/api/routes/investigations.py | 10 +- .../src/dataing/services/notification.py | 20 - .../api/test_investigation_snapshot_import.py | 91 +++ .../dataing/tests/integration/conftest.py | 12 +- .../unit/adapters/notifications/test_email.py | 19 - .../unit/adapters/notifications/test_slack.py | 15 - .../tests/unit/services/test_notification.py | 20 - 61 files changed, 604 insertions(+), 2836 deletions(-) create mode 100644 .flow/tasks/fn-16.13.json create mode 100644 .flow/tasks/fn-16.13.md create mode 100644 .flow/tasks/fn-16.14.json create mode 100644 .flow/tasks/fn-16.14.md create mode 100644 .flow/tasks/fn-16.15.json create mode 100644 .flow/tasks/fn-16.15.md create mode 100644 .flow/tasks/fn-16.16.json create mode 100644 .flow/tasks/fn-16.16.md delete mode 100644 frontend/app/src/features/investigation/ContextReviewPage.tsx delete mode 100644 frontend/app/src/features/investigation/context-review.tsx delete mode 100644 frontend/app/src/lib/api/generated/approvals/approvals.ts delete mode 100644 frontend/app/src/lib/api/model/approvalDecisionResponse.ts delete mode 100644 frontend/app/src/lib/api/model/approvalDecisionResponseComment.ts delete mode 100644 frontend/app/src/lib/api/model/approvalRequestResponse.ts delete mode 100644 frontend/app/src/lib/api/model/approvalRequestResponseComment.ts delete mode 100644 frontend/app/src/lib/api/model/approvalRequestResponseContext.ts delete mode 100644 frontend/app/src/lib/api/model/approvalRequestResponseDatasetId.ts delete mode 100644 frontend/app/src/lib/api/model/approvalRequestResponseDecidedAt.ts delete mode 100644 frontend/app/src/lib/api/model/approvalRequestResponseDecidedBy.ts delete mode 100644 frontend/app/src/lib/api/model/approvalRequestResponseDecision.ts delete mode 100644 frontend/app/src/lib/api/model/approvalRequestResponseMetricName.ts delete mode 100644 frontend/app/src/lib/api/model/approvalRequestResponseModifications.ts delete mode 100644 frontend/app/src/lib/api/model/approvalRequestResponseModificationsAnyOf.ts delete mode 100644 frontend/app/src/lib/api/model/approvalRequestResponseSeverity.ts delete mode 100644 frontend/app/src/lib/api/model/approveRequest.ts delete mode 100644 frontend/app/src/lib/api/model/approveRequestComment.ts delete mode 100644 frontend/app/src/lib/api/model/createApprovalRequest.ts delete mode 100644 frontend/app/src/lib/api/model/createApprovalRequestContext.ts delete mode 100644 frontend/app/src/lib/api/model/modifyRequest.ts delete mode 100644 frontend/app/src/lib/api/model/modifyRequestComment.ts delete mode 100644 frontend/app/src/lib/api/model/modifyRequestModifications.ts delete mode 100644 frontend/app/src/lib/api/model/pendingApprovalsResponse.ts delete mode 100644 frontend/app/src/lib/api/model/rejectRequest.ts create mode 100644 python-packages/dataing-ee/tests/integration/conftest.py create mode 100644 python-packages/dataing-ee/tests/integration/core/__init__.py create mode 100644 python-packages/dataing-ee/tests/integration/core/automation/__init__.py create mode 100644 python-packages/dataing-ee/tests/integration/core/automation/test_spawn_investigation.py delete mode 100644 python-packages/dataing/src/dataing/entrypoints/api/routes/approvals.py create mode 100644 python-packages/dataing/tests/integration/api/test_investigation_snapshot_import.py diff --git a/.flow/tasks/fn-16.13.json b/.flow/tasks/fn-16.13.json new file mode 100644 index 000000000..89be26f85 --- /dev/null +++ b/.flow/tasks/fn-16.13.json @@ -0,0 +1,21 @@ +{ + "assignee": "bordumbb@gmail.com", + "claim_note": "", + "claimed_at": "2026-09-26T02:11:50.122844Z", + "created_at": "2026-09-26T02:08:42.244248Z", + "depends_on": [], + "epic": "fn-16", + "evidence": { + "commits": [], + "prs": [], + "tests": [ + "DATABASE_URL=... uv run --no-sync pytest python-packages/dataing/tests/integration/api/test_investigation_snapshot_import.py -m integration --no-cov" + ] + }, + "id": "fn-16.13", + "priority": null, + "spec_path": ".flow/tasks/fn-16.13.md", + "status": "done", + "title": "Snapshot import: stop inserting generated investigations.status", + "updated_at": "2026-09-26T02:18:24.729448Z" +} diff --git a/.flow/tasks/fn-16.13.md b/.flow/tasks/fn-16.13.md new file mode 100644 index 000000000..a3f752726 --- /dev/null +++ b/.flow/tasks/fn-16.13.md @@ -0,0 +1,19 @@ +# fn-16.13 Snapshot import: stop inserting generated investigations.status + +## Description +TBD + +## Acceptance +## Acceptance +- POST /investigations/import no longer writes the generated `status` column +- Integration test (migrated schema) imports a valid archive: 200, row stored with replay outcome, status derived as 'completed' + + +## Done summary +Dropped the generated `status` column (and its "imported" value) from the snapshot import INSERT; status now derives from `outcome` ('completed'). +Test: tests/integration/api/test_investigation_snapshot_import.py builds a real archive with SnapshotBuilder and imports it against the migrated schema (RED: GeneratedAlwaysError, GREEN after fix). +Also added shared `fixtures/database.py` (migrated_dsn/migrated_db) re-exported by CE and EE integration conftests. +## Evidence +- Commits: +- Tests: DATABASE_URL=... uv run --no-sync pytest python-packages/dataing/tests/integration/api/test_investigation_snapshot_import.py -m integration --no-cov +- PRs: diff --git a/.flow/tasks/fn-16.14.json b/.flow/tasks/fn-16.14.json new file mode 100644 index 000000000..28526e91e --- /dev/null +++ b/.flow/tasks/fn-16.14.json @@ -0,0 +1,21 @@ +{ + "assignee": "bordumbb@gmail.com", + "claim_note": "", + "claimed_at": "2026-09-26T02:18:24.996044Z", + "created_at": "2026-09-26T02:08:42.507450Z", + "depends_on": [], + "epic": "fn-16", + "evidence": { + "commits": [], + "prs": [], + "tests": [ + "DATABASE_URL=... uv run --no-sync pytest python-packages/dataing-ee/tests/integration/core/automation/test_spawn_investigation.py -m integration --no-cov" + ] + }, + "id": "fn-16.14", + "priority": null, + "spec_path": ".flow/tasks/fn-16.14.md", + "status": "done", + "title": "Automation spawn_investigation: valid AnomalyAlert via InvestigationStarterService", + "updated_at": "2026-09-26T02:29:51.445475Z" +} diff --git a/.flow/tasks/fn-16.14.md b/.flow/tasks/fn-16.14.md new file mode 100644 index 000000000..c1ed72dd2 --- /dev/null +++ b/.flow/tasks/fn-16.14.md @@ -0,0 +1,22 @@ +# fn-16.14 Automation spawn_investigation: valid AnomalyAlert via InvestigationStarterService + +## Description +TBD + +## Acceptance +## Acceptance +- `ActionExecutor._spawn_investigation` stores `alert` as a valid AnomalyAlert dump + `datasource_id`, via InvestigationStarterService (row + Temporal workflow) +- Issue linked through `issue_investigation_runs` (trigger_type 'rule'); re-running returns the existing investigation +- Fails cleanly (ActionResult.success False) when the issue has no dataset or no datasource resolves +- Integration tests against the migrated schema + + +## Done summary +`_spawn_investigation` now builds an AnomalyAlert from the issue row (description-type metric, dataset from the issue, date from created_at, source_* from the issue) and starts it through InvestigationStarterService (alert JSONB = AnomalyAlert dump + datasource_id, Temporal workflow started). +ExecutionContext gains a required `investigation_starter`. Datasource: `datasource_id` action param (must belong to the tenant), else the tenant's only active datasource, else a failed ActionResult. +Issue link + dedupe via issue_investigation_runs (trigger_type 'rule', trigger_ref {rule_id}, execution_profile = profile). +Tests: dataing-ee/tests/integration/core/automation/test_spawn_investigation.py (6 cases; RED: `column "issue_id" does not exist`). +## Evidence +- Commits: +- Tests: DATABASE_URL=... uv run --no-sync pytest python-packages/dataing-ee/tests/integration/core/automation/test_spawn_investigation.py -m integration --no-cov +- PRs: diff --git a/.flow/tasks/fn-16.15.json b/.flow/tasks/fn-16.15.json new file mode 100644 index 000000000..4476b49e4 --- /dev/null +++ b/.flow/tasks/fn-16.15.json @@ -0,0 +1,22 @@ +{ + "assignee": "bordumbb@gmail.com", + "claim_note": "", + "claimed_at": "2026-09-26T02:29:52.197558Z", + "created_at": "2026-09-26T02:08:42.791298Z", + "depends_on": [], + "epic": "fn-16", + "evidence": { + "commits": [], + "prs": [], + "tests": [ + "uv run --no-sync mypy python-packages/dataing/src python-packages/dataing-ee/src", + "uv run --no-sync pytest python-packages/dataing/tests --no-cov" + ] + }, + "id": "fn-16.15", + "priority": null, + "spec_path": ".flow/tasks/fn-16.15.md", + "status": "done", + "title": "Delete dead AppDatabase.create_investigation", + "updated_at": "2026-09-26T02:58:33.360753Z" +} diff --git a/.flow/tasks/fn-16.15.md b/.flow/tasks/fn-16.15.md new file mode 100644 index 000000000..2065ab0c2 --- /dev/null +++ b/.flow/tasks/fn-16.15.md @@ -0,0 +1,17 @@ +# fn-16.15 Delete dead AppDatabase.create_investigation + +## Description +TBD + +## Acceptance +## Acceptance +- `AppDatabase.create_investigation` (old-schema insert, no callers) deleted + + +## Done summary +Deleted `AppDatabase.create_investigation` (pre-013 column insert). No callers: the other `create_investigation` hits are PostgresInvestigationRepository / the repository protocol / an entitlements test route. +Verified: grep, mypy clean, CE 2044 passed / EE 458 passed. +## Evidence +- Commits: +- Tests: uv run --no-sync mypy python-packages/dataing/src python-packages/dataing-ee/src, uv run --no-sync pytest python-packages/dataing/tests --no-cov +- PRs: diff --git a/.flow/tasks/fn-16.16.json b/.flow/tasks/fn-16.16.json new file mode 100644 index 000000000..9cf05a375 --- /dev/null +++ b/.flow/tasks/fn-16.16.json @@ -0,0 +1,23 @@ +{ + "assignee": "bordumbb@gmail.com", + "claim_note": "", + "claimed_at": "2026-09-26T02:33:18.975571Z", + "created_at": "2026-09-26T02:08:43.082252Z", + "depends_on": [], + "epic": "fn-16", + "evidence": { + "commits": [], + "prs": [], + "tests": [ + "uv run --no-sync pytest python-packages/dataing/tests --no-cov", + "uv run --no-sync pytest python-packages/dataing-ee/tests --no-cov", + "pnpm typecheck && pnpm lint && pnpm test" + ] + }, + "id": "fn-16.16", + "priority": null, + "spec_path": ".flow/tasks/fn-16.16.md", + "status": "done", + "title": "Delete approvals stack (pre-013 approval_requests)", + "updated_at": "2026-09-26T02:58:33.643752Z" +} diff --git a/.flow/tasks/fn-16.16.md b/.flow/tasks/fn-16.16.md new file mode 100644 index 000000000..023a0fa9a --- /dev/null +++ b/.flow/tasks/fn-16.16.md @@ -0,0 +1,21 @@ +# fn-16.16 Delete approvals stack (pre-013 approval_requests) + +## Description +TBD + +## Acceptance +## Acceptance +- /approvals route, AppDatabase approval methods and `update_investigation_status` deleted (pre-013 approval_requests schema; no producer; Temporal HITL uses POST /investigations/{id}/input) +- Frontend ContextReviewPage, its route and the approval notification link removed; client regenerated +- ruff, ruff format --check, mypy, CE/EE unit tests and frontend checks pass + + +## Done summary +Deleted the approvals stack (user decision): routes/approvals.py + registration; AppDatabase create_approval_request / get_pending_approvals / make_approval_decision / update_investigation_status; dashboard pendingApprovals KPI (API field + card); dead producers notify_approval_required, EmailNotifier.send_approval_required, Slack approval.required formatting (+ their unit tests); frontend ContextReviewPage + context-review, /approvals route, notification-card approval branch, notification/webhook settings entries, query-keys. +OpenAPI: removed the 7 approvals paths + 7 approval-only schemas + DashboardStats.pending_approvals from the committed spec (full regen adds +3222/-2087 unrelated drift); orval regenerated; 23 stale generated approval files + 22 index exports removed by hand (orval does not clean). +Left + flagged: EE require_approval_for_queries setting (unenforced), domain_types Approval* models (unused), dashboard other old-schema queries. +Verified: CE 2044 passed, EE 458 passed, mypy clean, ruff clean, frontend tsc/eslint/prettier/vitest (12) clean. +## Evidence +- Commits: +- Tests: uv run --no-sync pytest python-packages/dataing/tests --no-cov, uv run --no-sync pytest python-packages/dataing-ee/tests --no-cov, pnpm typecheck && pnpm lint && pnpm test +- PRs: diff --git a/frontend/app/src/App.tsx b/frontend/app/src/App.tsx index 9185d3769..aa55546b7 100644 --- a/frontend/app/src/App.tsx +++ b/frontend/app/src/App.tsx @@ -27,7 +27,6 @@ import { DashboardPage } from "@/features/dashboard/dashboard-page"; import { InvestigationList } from "@/features/investigation/InvestigationList"; import { InvestigationDetail } from "@/features/investigation/InvestigationDetail"; import { NewInvestigation } from "@/features/investigation/NewInvestigation"; -import { ContextReviewPage } from "@/features/investigation/ContextReviewPage"; import { DataSourcePage } from "@/features/datasources/datasource-page"; import { DatasetListPage, DatasetDetailPage } from "@/features/datasets"; import { SettingsPage } from "@/features/settings/settings-page"; @@ -167,14 +166,6 @@ function AppWithEntitlements() { } /> - - - - } - /> {/* Stats Grid */} -
+
@@ -93,32 +86,6 @@ export function DashboardPage() { )} - - - - - Pending Approvals - - - - - {isLoading ? ( - - ) : ( - <> -
- {stats?.pendingApprovals ?? 0} -
- {(stats?.pendingApprovals ?? 0) > 0 && ( -

- - Requires attention -

- )} - - )} -
-
{/* Recent Investigations */} diff --git a/frontend/app/src/features/investigation/ContextReviewPage.tsx b/frontend/app/src/features/investigation/ContextReviewPage.tsx deleted file mode 100644 index 9c2f6fd2a..000000000 --- a/frontend/app/src/features/investigation/ContextReviewPage.tsx +++ /dev/null @@ -1,178 +0,0 @@ -import { useParams, useNavigate, Link } from "react-router-dom"; -import { ArrowLeft, Loader2 } from "lucide-react"; -import { toast } from "sonner"; -import { useQueryClient } from "@tanstack/react-query"; - -import { Button } from "@/components/ui/Button"; -import { Card, CardContent } from "@/components/ui/Card"; -import { EmptyState } from "@/components/shared/empty-state"; -import { - useGetApprovalRequestApiV1ApprovalsApprovalIdGet, - useApproveRequestApiV1ApprovalsApprovalIdApprovePost, - useRejectRequestApiV1ApprovalsApprovalIdRejectPost, - getListPendingApprovalsApiV1ApprovalsPendingGetQueryKey, -} from "@/lib/api/generated/approvals/approvals"; -import { ContextReview } from "./context-review"; - -export function ContextReviewPage() { - const { approvalId } = useParams<{ approvalId: string }>(); - const navigate = useNavigate(); - const queryClient = useQueryClient(); - - const { - data: approval, - isLoading, - error, - } = useGetApprovalRequestApiV1ApprovalsApprovalIdGet(approvalId || "", { - query: { enabled: !!approvalId }, - }); - - const approveMutation = useApproveRequestApiV1ApprovalsApprovalIdApprovePost({ - mutation: { - onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: getListPendingApprovalsApiV1ApprovalsPendingGetQueryKey(), - }); - toast.success("Investigation approved and resumed"); - navigate(`/investigations/${approval?.investigation_id}`); - }, - onError: (err: Error) => { - toast.error(`Failed to approve: ${err.message}`); - }, - }, - }); - - const rejectMutation = useRejectRequestApiV1ApprovalsApprovalIdRejectPost({ - mutation: { - onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: getListPendingApprovalsApiV1ApprovalsPendingGetQueryKey(), - }); - toast.success("Investigation rejected"); - navigate("/investigations"); - }, - onError: (err: Error) => { - toast.error(`Failed to reject: ${err.message}`); - }, - }, - }); - - const handleApprove = async (comment?: string) => { - if (!approvalId) return; - await approveMutation.mutateAsync({ - approvalId, - data: { comment: comment || null }, - }); - }; - - const handleReject = async (reason: string) => { - if (!approvalId) return; - await rejectMutation.mutateAsync({ - approvalId, - data: { reason }, - }); - }; - - if (!approvalId) { - return ( - - - - - - ); - } - - if (isLoading) { - return ( -
- -
- ); - } - - if (error || !approval) { - return ( - - - - - - } - /> - - - ); - } - - // Check if already decided - if (approval.decision) { - return ( -
-
- - - -

Context Review

-
- - - - - - } - /> - - -
- ); - } - - // Build context object for ContextReview component - const context = { - query: (approval.context?.query as string) || "Query not available", - purpose: - (approval.context?.purpose as string) || - `Review context for investigation ${approval.investigation_id}`, - tables_accessed: (approval.context?.tables_accessed as string[]) || [], - estimated_rows: (approval.context?.estimated_rows as number) || 0, - }; - - return ( -
-
- - - -

Context Review

-
- - -
- ); -} diff --git a/frontend/app/src/features/investigation/context-review.tsx b/frontend/app/src/features/investigation/context-review.tsx deleted file mode 100644 index e2b09d3f5..000000000 --- a/frontend/app/src/features/investigation/context-review.tsx +++ /dev/null @@ -1,194 +0,0 @@ -import * as React from "react"; -import { Check, X, AlertCircle } from "lucide-react"; -import { toast } from "sonner"; - -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/Card"; -import { Button } from "@/components/ui/Button"; -import { Textarea } from "@/components/ui/textarea"; -import { Badge } from "@/components/ui/Badge"; -import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; - -interface ContextReviewProps { - investigationId: string; - context: { - query: string; - purpose: string; - tables_accessed: string[]; - estimated_rows: number; - }; - onApprove: (comment?: string) => void; - onReject: (reason: string) => void; -} - -export function ContextReview({ - investigationId, - context, - onApprove, - onReject, -}: ContextReviewProps) { - const [comment, setComment] = React.useState(""); - const [rejectReason, setRejectReason] = React.useState(""); - const [showRejectForm, setShowRejectForm] = React.useState(false); - const [isSubmitting, setIsSubmitting] = React.useState(false); - - const handleApprove = async () => { - setIsSubmitting(true); - try { - await onApprove(comment || undefined); - toast.success("Context approved"); - } catch (error) { - toast.error("Failed to approve context"); - } finally { - setIsSubmitting(false); - } - }; - - const handleReject = async () => { - if (!rejectReason.trim()) { - toast.error("Please provide a reason for rejection"); - return; - } - setIsSubmitting(true); - try { - await onReject(rejectReason); - toast.success("Context rejected"); - } catch (error) { - toast.error("Failed to reject context"); - } finally { - setIsSubmitting(false); - } - }; - - return ( - - -
-
- Human-in-the-Loop Review - - Review and approve the proposed context for investigation{" "} - {investigationId} - -
- Pending Approval -
-
- - - - Context Review Required - - The system is requesting approval to access the following data. - Please review carefully before approving. - - - -
-
-

Purpose

-

{context.purpose}

-
- -
-

Tables to Access

-
- {context.tables_accessed.map((table) => ( - - {table} - - ))} -
-
- -
-

Estimated Data Volume

-

- Approximately {context.estimated_rows.toLocaleString()} rows will - be analyzed -

-
- -
-

Query

-
-              {context.query}
-            
-
-
- - {!showRejectForm ? ( - <> -
-

Comment (Optional)

-