index · all work
08 · multi-agent

OmegaHack · CAROL

CAROL: a pipeline of agents that receives, de-identifies, classifies, and legally clocks a city hall's citizen complaints.

Year2026
Statusprototype
RoleLead · design + engineering (2-person hackathon team)
Built withTypeScript · Turborepo · Next.js 14 · React 19 / 18 · Vite + TanStack Router · +7
omega-landing-zeta.vercel.appopen ↗

Live demo. Click Run to load the real app and use it right here, or open it full-screen.

01Overview

In Colombia, any citizen can file a PQRSD (a petition, complaint, claim, suggestion, or report) and the government has a fixed number of business days to answer. City halls receive thousands of these across email, web forms, and spreadsheets, misroute them, and blow the legal deadlines, which turns into lawsuits. CAROL is the system that catches each complaint the moment it arrives, strips out personal data, works out what it is and who should handle it, and starts the legal clock.

It was built as a hackathon submission for the 2026 Alcaldía de Medellín challenge, but the engineering is real: a Turborepo monorepo with a public citizen portal, an internal legal-review queue, a per-secretaría dashboard, and a public transparency site, all sitting on one multi-tenant Postgres with row-level security. The hard part is the law. A dependency-free deadline engine encodes Colombian business days, movable holidays, and the extension rules of Ley 1755/2015, and it ships with 407 tests at 100% line coverage.

02The problem

Colombian public entities must answer every citizen PQRSD within strict business-day deadlines (Ley 1755/2015) while protecting personal data (Ley 1581/2012). City halls receive these complaints as unstructured text across many channels, misroute them, and miss deadlines, which exposes them to tutela lawsuits.

03Highlights
  • Dependency-free legal deadline engine: Colombian business days, Ley Emiliani movable holidays, Easter-derived dates, per-tenant suspensions, and the Ley 1755/2015 'extension cannot exceed 2x the original term' rule. 407 tests at 100% line coverage, including a 10,000-iteration property test via fast-check.
  • Eight-stage intake agent per complaint: schema validation, a SHA-256 source_hash for idempotent dedup, Claude classification, format-preserving PII redaction into raw/display/llm text, a validity check with municipal-competence gates, then auto-summary, tagging, and problem-group clustering. The run emits a structured intake event; a Postgres trigger mirrors every pqr_events row into an append-only audit log.
  • Claude classifier turns free text into one of 6 PQRSD types, 26 official secretaria codes, a comuna (1-16) or corregimiento, namespaced tags, and auxiliary signals like a 0-1 tutela-risk score.
  • PII handled by law: every field is classified into four Ley 1581/2012 sensitivity levels (public / semiprivate / private / sensitive) and only the scrubbed llm_text is ever sent to a model.
  • Public transparency dashboards enforce k-anonymity (k>=5): comuna density maps (Leaflet) and secretaria SLA rankings (Recharts) suppress any bucket with fewer than 5 complaints before it leaves the server.
  • Tenant isolation lives in the database: every business table carries tenant_id under RLS, and three role-scoped Postgres clients (app_operational / app_qa_reader / service-role) issue SET ROLE on connect.
04By the numbers
407
tests / 100% line coverage in the deadline engine
10,000-iteration property test (fast-check)
26
official secretaría codes; comunas 1-16 + corregimientos
18
Postgres migrations
k-anonymity k>=5 on public transparency data
4
apps · 11 shared packages · 4 edge functions
What's inside9 parts
  • @omega/intake-agentlibrary

    Orchestrates the whole PQRSD intake: schema validation, PII redaction, a validity agent, the Article 16 gates of Ley 1755, classification, tags and deadline, writing one pqr row plus an audit event.

  • @omega/classifierlibrary

    Sends the PII-free text to Claude and returns a structured verdict: request type, the competent secretaría (one of 26 official codes), comuna, namespaced thematic tags and signals like tutela risk.

  • @omega/deadline-enginelibrary

    Computes Colombian legal deadlines in business days on America/Bogota, covering movable holidays, the extension capped at twice the original term (Ley 1755/2015) and per-tenant suspensions, fully offline with 393 tests.

  • @omega/habeas-datalibrary

    Classifies and redacts personal data under Ley 1581/2012 (Colombian habeas data) with zero external dependencies, so raw citizen text never reaches a model unredacted.

  • @omega/problem-groupslibrary

    Clusters recurring PQRs from the same tenant by cosine similarity, shared tags and matching comuna, and flags a group as hot once its volume and velocity cross tenant thresholds.

  • @omega/raglibrary

    Retrieval utilities for the legal Q&A: header-aware chunking, 1024-dim Azure embeddings, a hybrid vector plus full-text retriever, and a client for the internal Nella API.

  • @omega/workbenchapp

    Internal Next.js console for the legal team: case review and reply drafting, queues filtered by secretaría and status, problem-group navigation and an append-only audit trail (port 3001).

  • @omega/webapp

    Public Next.js site for citizens plus the /transparencia dashboard, which serves comuna and secretaría aggregates behind a k-anonymity floor of 5 (port 3000).

  • @omega/secretariaapp

    Per-secretaría Next.js console scoped strictly to one department's cases, staff and deadline-compliance KPIs (port 3002).

·Tags
multi-agentgovtechLLM classificationRAGlegal-compliancemonorepo
Full tech stack 12
TypeScriptTurborepoNext.js 14React 19 / 18Vite + TanStack RouterSupabase (Postgres + pgvector + RLS)Deno edge functionsClaude (Anthropic)Tailwind CSSLeafletRechartsVitest + fast-check