Migrations:
- 0004_investigation_bureau.sql: 7 new tables (investigation_jobs + evidence,
hypotheses, contradictions, witnesses, gaps, residual_uncertainties), id
sequences, pg_notify trigger on investigation_jobs, RLS read-only public,
investigator role with least-privilege grants (no service_role).
- 0005_investigator_write_policies.sql: fixup adding RLS INSERT/UPDATE
policies bound to investigator + service_role + postgres (RLS with only a
SELECT policy was silently blocking the worker's claim UPDATE).
investigator-runtime/ (new Bun + TS container):
- src/main.ts: LISTEN/NOTIFY poller, claim-with-SKIP-LOCKED, drain pool,
healthcheck file, graceful SIGTERM shutdown.
- src/orchestrator.ts: chief-detective dispatch (evidence_chain → Locard).
Marks job failed when all per-item outputs error; surfaces first errors.
- src/lib/{env,pg,audit,ids,claude}.ts: typed config (gate #8), pool +
dedicated LISTEN client, NDJSON audit, sequence allocator (E-NNNN etc),
claude -p subprocess with quota detection (api_error_status=429).
- src/tools/write_evidence.ts: schema-validate (grade A/B/C custody steps),
resolve chunk_pk via FK, verify verbatim_excerpt actually appears in
chunk content, INSERT + render case/evidence/E-NNNN.md + audit.
- src/detectives/locard.ts: load chunk → call Claude with locard.md system
prompt → parse strict JSON → call writeEvidence locally.
- Dockerfile installs `claude` CLI (OAuth) at build time.
Compose:
- new `investigator` service builds from investigator-runtime/, connects
with low-privilege role, mounts case/ RW and wiki/+raw/ RO, 512m mem cap.
Web:
- /api/admin/investigate/test (POST+GET) gated by middleware (W0-F1).
POST creates a job, GET polls status. For W3.6 it becomes the chat tool.
End-to-end smoke: INSERT job → pg_notify → claim → Locard dispatch →
claude subprocess invoked. Auth works (CLI v2.1.150). Currently quota
exhausted (weekly limit · resets 3pm UTC) — pipeline catches the typed
isQuota error, marks job failed with surfaced reason. Architecture proven;
quota reset enables real evidence creation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
52 lines
2.8 KiB
PL/PgSQL
52 lines
2.8 KiB
PL/PgSQL
-- 0005_investigator_write_policies.sql
|
|
--
|
|
-- Fix-up on top of 0004: the investigation tables had RLS enabled but ONLY
|
|
-- a SELECT policy. With RLS active, INSERT/UPDATE/DELETE are blocked unless
|
|
-- a policy permits them, regardless of GRANT. So the `investigator` role
|
|
-- could connect and read but its `UPDATE … RETURNING` claim query silently
|
|
-- returned zero rows — jobs sat in `queued` forever.
|
|
--
|
|
-- This migration adds INSERT/UPDATE policies tied to the `investigator` role.
|
|
-- Public anon/authenticated remain READ-ONLY.
|
|
--
|
|
-- Idempotent. Apply as supabase_admin.
|
|
|
|
BEGIN;
|
|
|
|
-- ─────────────────────────────────────────────────────────────────────────
|
|
-- investigation_jobs — writes from the runtime + INSERTS from the web admin
|
|
-- ─────────────────────────────────────────────────────────────────────────
|
|
DROP POLICY IF EXISTS investigation_jobs_insert ON public.investigation_jobs;
|
|
DROP POLICY IF EXISTS investigation_jobs_update ON public.investigation_jobs;
|
|
|
|
-- The web admin creates jobs through the service_role (gated by middleware
|
|
-- /api/admin/*); the investigator role updates them as it claims and runs.
|
|
CREATE POLICY investigation_jobs_insert ON public.investigation_jobs
|
|
FOR INSERT TO investigator, service_role, postgres
|
|
WITH CHECK (TRUE);
|
|
CREATE POLICY investigation_jobs_update ON public.investigation_jobs
|
|
FOR UPDATE TO investigator, service_role, postgres
|
|
USING (TRUE) WITH CHECK (TRUE);
|
|
|
|
-- ─────────────────────────────────────────────────────────────────────────
|
|
-- All 6 investigation tables: writes only by the investigator + service_role.
|
|
-- ─────────────────────────────────────────────────────────────────────────
|
|
DO $$
|
|
DECLARE
|
|
t TEXT;
|
|
BEGIN
|
|
FOREACH t IN ARRAY ARRAY['evidence','hypotheses','contradictions','witnesses','gaps','residual_uncertainties']
|
|
LOOP
|
|
EXECUTE format('DROP POLICY IF EXISTS %I_insert ON public.%I', t, t);
|
|
EXECUTE format('DROP POLICY IF EXISTS %I_update ON public.%I', t, t);
|
|
EXECUTE format(
|
|
'CREATE POLICY %I_insert ON public.%I FOR INSERT TO investigator, service_role, postgres WITH CHECK (TRUE)',
|
|
t, t);
|
|
EXECUTE format(
|
|
'CREATE POLICY %I_update ON public.%I FOR UPDATE TO investigator, service_role, postgres USING (TRUE) WITH CHECK (TRUE)',
|
|
t, t);
|
|
END LOOP;
|
|
END
|
|
$$;
|
|
|
|
COMMIT;
|