## 1. Layer 1 — Privacy: Complaint ID PII Protection - [x] 1.1 Add `_complaint_id_deny_recognizer()` function in `src/privacy/redactor.py` that returns a `PatternRecognizer` with entity type `COMPLAINT_REF_ID`, regex matching `REF-`, `TRN-`, `TXN-`, `OD-`, `BK-`, `CLM-`, `CASE-`, `CMP-`, `LN-`, `POL-`, `CR-` prefixed IDs (score 0.99), and a comment explaining intentional exclusion from `_ENTITY_TYPES` - [x] 1.2 Register the `_complaint_id_deny_recognizer()` in `PIIRedactor.__init__` alongside the existing Indian recognizers - [x] 1.3 Verify manually: `PIIRedactor().redact("REF-20260501-001")` returns text with `REF-20260501-001` unchanged; `PIIRedactor().redact("account 9876543210")` still returns `` ## 2. Layer 1 — Infrastructure: Tesseract Pre-flight - [x] 2.1 Add a `_check_tesseract()` function in `start.py` using `shutil.which("tesseract")` that prints a formatted error with macOS/Ubuntu/Windows install commands and calls `sys.exit(1)` if the binary is absent - [x] 2.2 Call `_check_tesseract()` in `start.py` before the `uvicorn` server launch block (after argument parsing, before training or server startup) - [ ] 2.3 Verify manually: rename/hide the tesseract binary temporarily and confirm `python start.py` exits with code 1 and prints install instructions ## 3. Layer 3 — Prompt: Rule 4/5 Tightening - [x] 3.1 In `src/agent/prompts.py` Rule 4, append after the tab-navigation instruction: *"Do not append a yes/no follow-up question ('Does this look correct?', 'Shall I proceed?', etc.). The numbered summary above is the confirmation request. Stop completely and wait for `[USER CONFIRMED]:`."* - [x] 3.2 In Rule 5, replace "The most recent user message begins with" with "You have received a user message that begins with" to eliminate turn-ordering ambiguity - [ ] 3.3 Verify by running Trace 07 scenario: agent should present numbered summary and stop without asking an extra question ## 4. Layer 3 — Prompt: HITL Structured JSON Block - [x] 4.1 In `src/agent/prompts.py` Rule 4, add an instruction immediately after the numbered list template: the agent SHALL append `` with the same values as the numbered list; omit keys for unknown fields; use string values for all fields including `prior_contact` ("Yes"/"No") ## 5. Layer 4 — UI: Structured Entity Parsing - [x] 5.1 Add `_parse_hitl_entities_json(reply: str) -> dict` function in `ui/app.py` that extracts the `` comment, calls `json.loads`, and returns the dict (returns `{}` on any parse error or absent block) - [x] 5.2 Replace the call to `_parse_hitl_entities(reply)` in `_handle_chat` with `_parse_hitl_entities_json(reply)` - [x] 5.3 Remove the `_parse_hitl_entities` function and `_HITL_PATTERNS` dict (now unused) ## 6. Layer 4 — UI: prior_contact Guard - [x] 6.1 In `_handle_confirm` in `ui/app.py`, change the `prior_contact` entry from `"prior_contact": prior == "Yes"` to `"prior_contact": True` only when `prior == "Yes"`, and omit the key entirely when `prior` is `None`, empty string, or `"No"` — align with the filter condition on line 596 ## 7. Layer 4 — UI: _extract_draft Relaxation - [x] 7.1 In `_extract_draft` in `ui/app.py`, add a third extraction path between the `---` primary and the `Subject:` fallback: `re.search(r'(Subject:.+)', reply, re.DOTALL | re.IGNORECASE)` that returns from `Subject:` to end of string when no closing `---` is present - [x] 7.2 Fix the existing `"Subject:" in reply` fallback to return `reply[reply.index("Subject:"):]` instead of the full `reply` string ## 8. Layer 2 — ML: NER ORG List Extension - [x] 8.1 In `src/ner/train.py` `_ENTITY_VALUES["ORG"]` list, add: `"Indian Bank"`, `"Niva Bupa"`, `"Care Health Insurance"`, `"Bajaj Allianz"`, `"Agoda"`, `"OYO Rooms"`, `"Booking.com India"`, `"Razorpay"`, `"BharatPe"`, `"CRED"` - [x] 8.2 Retrain EvidenceNER checkpoint: `python -m src.ner.train --output_dir models/evidence_ner` - [x] 8.3 Verify: `extract_entities("Indian Bank charged 1000 rupee")` returns an entity with `label="ORG"` and text matching "Indian Bank" ## 9. Layer 2 — ML: Domain Classifier Rebalancing - [x] 9.1 In `src/classifier/train.py` `_SUPPLEMENT_TEMPLATES["telecom"]`, add 10 new templates covering SIM activation, prepaid recharge, porting, deactivation scenarios; add 5 Agoda/OYO/Booking.com templates to `ecommerce` - [x] 9.2 Change the `supplement_per_class` default in `_build_supplement()` signature from `2_000` to `5_000` - [x] 9.3 Change the `--supplement_per_class` CLI argument default in `train.py` from `2_000` to `5_000` - [ ] 9.4 Retrain DomainClassifier checkpoint on Kaggle: zip `src/classifier/train.py` + CFPB CSV, run `python -m src.classifier.train --cfpb_csv data/raw/complaints.csv --output_dir models/domain_classifier`, download and replace `models/domain_classifier/` - [ ] 9.5 Verify: `classify("I bought a new sim card from BSNL for rs 450 but sim card is not yet activated")` returns `domain="telecom"` with `low_confidence=False`