File size: 11,172 Bytes
f69e867
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# 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 |