courtmitra / GAP_AUDIT_M1_M9.md
Hetansh Waghela
feat: Full Clerk Google OAuth + user profiles + combined API/Worker deploy
f69e867
|
Raw
History Blame Contribute Delete
11.2 kB

CourtMitra M1-M9 Gap Audit

Audit Date: 2026-05-29

Commit Under Review: f892836 "Implement CourtMitra autopilot roadmap"


M0 β€” Core Automation Loop

Status: Mostly complete. Intake, evidence, facts, legal grounding, route plan, action preview/consent, email send, reply ingestion, follow-up, resolution, ledger all have code paths. Tests green.


M1 β€” Rate-Limit & Action Executor Idempotency

Current:

  • checkRateLimit lives in action-execution-policy.ts and is called from executeAction.
  • Source is submissions table with status = "sent" in a rolling window.

Gaps / Bugs:

  1. Single source of truth risk. The comment says "counted from submissions/action_attempts", but only submissions is queried. If the post-send DB transaction rolls back, the email was sent but submissions row is missing β†’ under-count on retry.
  2. Idempotency gap. canExecute allows status === "executing". If the DB transaction fails after emailAdapter.submit succeeds, Inngest retries and the email is sent again because nothing blocks a second execution of an "executing" action.
  3. Env vars undocumented. ACTION_RATE_WINDOW_MINUTES and ACTION_RATE_MAX_PER_WINDOW are not in HANDOFF.md.

Fix plan:

  • Count from action_attempts (status = "succeeded") as a fallback / dual source.
  • In executeAction, query existing attempts before calling the adapter. If a non-failed attempt exists and the prior attempt has a startedAt older than a short threshold, assume send succeeded and reconcile DB state without re-sending.

M2 β€” Escalation Deadline Timezone

Current:

  • deadline-checker cron runs hourly.
  • escalation-policy.ts addDays() uses new Date(from.getTime() + days * 24 * 60 * 60 * 1000).
  • action-executor.ts sets currentDeadlineAt: addDays(new Date(), deadlineDays).

Gaps / Bugs:

  1. Timezone not Asia/Kolkata. addDays operates on server-local UTC time. If the server is not in IST, deadlines drift by 5.5 hours, causing the hourly checker to fire too early or too late.
  2. State rank mismatch. resolution-verify.ts uses its own STATE_RANK map with different numeric values (resolution_claimed: 17, resolved: 18) compared to the canonical map in other workers (resolution_claimed: 18, resolved: 19). This can cause state regression or incorrect transitions.

Fix plan:

  • Create a deadlineInKolkata(from, days) helper that computes the deadline in Asia/Kolkata timezone.
  • Align resolution-verify.ts STATE_RANK with the canonical map used by route-plan.ts, reply-classify.ts, etc.

M3 β€” Autopilot Triggers & Resolution Flow

Current:

  • autopilot-tick Inngest function listens to mission.autopilot_tick.
  • Emitted by route-plan.ts (after plan ready) and reply-classify.ts (after reply classified).
  • runAutopilotTick uses a ToolLoopAgent with tools: halt, draftFirstAction, openParallelTracks, requestResolution, waitForUser.

Gaps / Bugs:

  1. Auto-claims resolution without user consent. The requestResolution tool creates a resolutionOutcomes row, sets mission state to resolution_claimed, and emits mission.resolution_claimed β€” all without explicit user approval. Phase 1 spec requires user-confirmed resolution.
  2. Missing triggers. Autopilot does NOT tick on:
    • mission.deadline_breached (should evaluate next step)
    • action_executed / action_execution_failed (should decide follow-up)
    • followup.due (should draft escalation)
  3. No mode-gated behavior. autopilotMode on the mission row is read but never used. The requestResolution tool should be blocked in "moderate" mode.

Fix plan:

  • Change requestResolution to emit a resolution.proposed snapshot and wait for user confirmation, instead of auto-claiming.
  • Add autopilot tick emission in escalation.ts (deadline breached) and after action execution.
  • Respect autopilotMode: only auto-claim resolution in an explicit "aggressive" mode; "moderate" should always wait for user.

M4 β€” Parallel Tracks (routeGraph AND/OR, parent_action_id)

Current:

  • Schema has actions.parentActionId and actions.trackLabel.
  • draftConsentGatedAction accepts parentActionId and trackLabel.
  • openParallelTracks tool in autopilot.ts drafts up to 2 actions with different authorities.
  • resolution-verify.ts supersedes dangling proposed/preview_ready/approved actions on resolution.

Gaps / Bugs:

  1. parentActionId never set. In openParallelTracks, each track action is drafted with trackLabel but parentActionId is always null. There is no "parent" action that spawned the parallel tracks, so the link is unused.
  2. Route plan has no AND/OR encoding. The generated RoutePlanStepDto[] is linear. The routeGraph in route_rules has nodes/edges, but the LLM-generated plan steps do not encode which steps can run in parallel.
  3. Escalation policy ignores tracks. getNextEscalationTier advances linearly; it doesn't respect track boundaries. If two parallel tracks are open, escalating one should not automatically advance the other.

Fix plan:

  • When openParallelTracks opens tracks, create a synthetic "parent" action (or use the first action) and set parentActionId on subsequent tracks.
  • Add parallelGroup field to RoutePlanStepDto so the plan can encode parallelizable steps.
  • Update getNextEscalationTier to accept an optional trackLabel filter so each track escalates independently.

M5 β€” Settlement Flow

Current:

  • reply-classify.v2 prompt extracts settlement offers.
  • decideSettlementNextAction in settlement-policy.ts recommends accept/counter/escalate/wait.
  • reply-classify.ts auto-drafts a counter-offer action if policy says "counter".

Gaps / Bugs:

  1. No settlement acceptance action. If the user wants to accept a settlement, there's no UI action or API endpoint to generate an acceptance email.
  2. No integration tests for settlement end-to-end. The unit tests for settlement-policy.ts exist, but there's no test covering the reply-classify β†’ settlement policy β†’ draft action flow.
  3. Counter-offer authority may be wrong. from (the raw reply email) is used as the authority/destination, but it could be a no-reply address.

Fix plan:

  • Add a settlement-accept endpoint stub and UI button.
  • Add an integration test for the settlement classification β†’ counter-offer flow.
  • Document the counter-offer authority caveat.

M6 β€” RTI Seeds & Prompts

Current:

  • legal-data.ts has RTI Act 2005 chunks (Sections 6, 7, 19, 19(6)).
  • route-rules-data.ts has governmentRtiRouteRule with RTI0β†’RTI1β†’RTI2 steps.
  • Schema supports issueDomain = "government".

Gaps / Bugs:

  1. Intake prompt missing government domain. intake.v1 prompt only lists "consumer" | "payment" | "cyber_fraud", so LLM will never classify a government/RTI grievance correctly.
  2. No RTI-specific prompt. There is no dedicated rti-application.v1 or rti-packet.v1 prompt for generating RTI application text.
  3. No E2E test. The test suite has no fixture for a government-domain mission.

Fix plan:

  • Update intake.v1 prompt to include "government" domain.
  • Add rti-packet.v1 prompt stub.
  • Add a government mission fixture to the seed/integration test data.

M7 β€” Voice / Indic Intake

Current:

  • evidence.ts routes audio to ASR adapter, stores transcript in transcripts table.
  • transcripts schema exists (Module 2 migration).
  • Redacted text is fed into claim extraction.

Gaps / Bugs:

  1. Original transcript lost. evidence.ts passes redactedText as rawText to the claim extraction flow. The original unredacted transcript is stored in transcripts.text but never preserved separately for audit.
  2. Missing unique constraint / idempotency. db.insert(schema.transcripts).values(...).onConflictDoNothing() has no explicit target constraint. On Inngest retry, duplicate rows could accumulate if the table has no unique index on evidenceItemId.
  3. No language persistence for intake. ASR returns language, but it's only stored in transcripts.language. It's not propagated to the mission's facts or used to trigger translation.

Fix plan:

  • Store both originalText and redactedText in the ASR result / evidence flow.
  • Add a unique index on transcripts(evidenceItemId) (or ensure one exists) and specify the conflict target.
  • Propagate detected language to mission facts.

M8 β€” Portal Assist

Current:

  • portal-assist.ts contracts define PortalAssistState, PortalAssistSession, etc.
  • Schema has portal_assist_sessions table.
  • No Inngest function, no API controller, no UI panel.

Gaps / Bugs:

  1. No web session viewer. The mission page has no portal-assist panel.
  2. No screenshot/receipt flow. There is no endpoint to upload a receipt after a portal submission.
  3. No honest receipt_verified path. The ProofState enum includes receipt_verified, but no code path sets it. Portal actions are always ready_for_user_submission or user_confirmed_external_submission.

Fix plan:

  • Add POST /api/missions/:id/portal-receipt endpoint that accepts a receipt file and updates the action's proofState to receipt_verified.
  • Add a minimal portal-assist panel in the mission UI.
  • Document that portal automation is manual-packet only until Browserbase/Stagehand is configured.

M9 β€” Swarm / Social / DPDP

Current:

  • Swarm and social contracts exist (swarm.ts, social.ts).
  • Schema has entities, swarms, swarm_members, social_accounts, social_posts tables.
  • copy-ready-fallback.ts social adapter exists.
  • DPDP controller exists in API.

Gaps / Bugs:

  1. No swarm UI on mission page. SwarmWithEntity is imported but never rendered.
  2. Social publisher not consent-gated. social.ts contract has consentGrantId, but there's no check in the adapter or API that a social post has a valid consent grant before publishing.
  3. DPDP consent flow incomplete. No evidence that the first-contact consent notice is actually delivered to WhatsApp/Telegram users.

Fix plan:

  • Add a swarm membership opt-in stub to the mission UI.
  • Add a canPublishSocial guard that validates consentGrantId + action preview hash before any social adapter call.
  • Document DPDP first-contact delivery in the intake flow.

Summary of Priority Fixes

Priority Module Fix
P0 M1 Dual-source rate limit (submissions + attempts); executor attempt guard
P0 M2 Asia/Kolkata deadline helper; align STATE_RANK
P0 M3 Stop autopilot auto-claiming resolution; add missing tick triggers
P1 M4 Set parentActionId in openParallelTracks; add parallelGroup to steps
P1 M6 Add "government" to intake.v1 prompt
P1 M7 Preserve original transcript; fix transcript conflict target
P1 M8 Add receipt_verified endpoint stub
P1 M9 Add social publish consent guard
P2 M5 Settlement integration test