# FTWZ Automation — Complete Conversation Handoff
**Project:** FTWZ BOE Import & DTA Sale Automation (Windows VM → AWS Lambda)
**Status:** Architecture complete, Claude Code prompt ready, ready to build
**Date:** June 2026

---

## 1. Project Context

### What This Is
A customs/logistics automation for **FTWZ (Free Trade Warehousing Zone)** operations.
The client is **Endress+Hauser India** (IEC: `300014953`), managed through **Siddhartha Logistics FTWZ Pvt Ltd**.
ERP system is **Canofy** — a Frappe-based product hosted on AWS.

### Current State (Broken)
- Windows VM running Power Automate
- Selenium script logs into NSDL SEZ Online portal, downloads trade data
- `win32com.client` (COM automation) converts `.xls` → `.xlsx` — **requires live Windows desktop session**
- Python scripts transform the Excel and produce Canofy import files
- Manual upload to Canofy via Frappe Data Import
- Brittle, requires human babysitting, silent failures

### Target State (Being Built)
- Fully serverless AWS Lambda (Docker-based)
- Playwright headless browser replaces Selenium
- EasyOCR solves CAPTCHA programmatically — no manual intervention
- `xlrd` reads `.xls` directly — no win32com needed
- Records POSTed to Canofy via Frappe REST API with nested child tables
- EventBridge daily schedule — fully unattended

---

## 2. Two Reference Codebases

### 2A — AAI Automation (`aai_automation_erpnext`)
**Production Lambda** doing a similar job for a different portal (AMAX + KALE).
**This is the primary architectural pattern to follow exactly.**

Key files:
- `src/lambda_handler.py` — orchestrator, two pipelines (run_amax + run_kale), asyncio.run() pattern
- `src/frappe_client.py` — Canofy REST API client (headers, record_exists, upload_pdf, create_staging_record)
- `src/schema.py` — dataclass payloads + to_api_dict()
- `src/portal_client.py` — Playwright async scraper (login, cookie injection, Excel download, PDF generation)
- `Dockerfile` — AL2023 base + Playwright Chromium, Docker-based Lambda
- `template.yaml` — AWS SAM, 900s timeout, 3008MB, EventBridge schedule
- `.github/workflows/deploy.yml` — GitHub Actions CI/CD

Key patterns established:
- `get_secrets()` → AWS Secrets Manager
- `resolve_target_date()` → 3 date format handlers
- `cleanup_tmp_files()` → /tmp cleanup
- `asyncio.run()` called twice per pipeline (Phase 1: Excel, Phase 2: PDFs)
- Per-record try/except — one failure never kills the batch
- Idempotency via `record_exists()` before every create

### 2B — SEZ Automation Framework (`sez-automation-framework`)
**Working Selenium scraper** currently on Windows VM. Must be ported to Playwright.

Key files:
- `pages/login_page.py` — NSDL login (username, password, CAPTCHA)
- `pages/dashboard_page.py` — Excel export navigation + BOE PDF download
- `pages/base_page.py` — base Selenium helpers
- `tests/conftest.py` — Chrome driver setup (to be removed, replaced by Playwright)
- `tests/test_print_boe.py` — BOE PDF print flow (CAPTCHA → search → iframe → PDF)
- `utils/ocr_engine.py` — EasyOCR + OpenCV CAPTCHA solver
- `utils/logger.py` — structured logger

Portal details extracted:
- URL: `https://sezonline-ndml.co.in/`
- Auth: Username + Password + **Image CAPTCHA** (EasyOCR solves it)
- Tour popup after login: remove via JS (`.driver-overlay`, `.driver-popover`)
- Two modules: `BILL OF ENTRY` and `DTA SALE` (dropdown select)
- Excel export: date range → Export button → `.xls` file
- BOE PDF: Search by Request ID → View Print → iframe → Original checkbox → OK → new tab → CDP print

---

## 3. Existing Transform Scripts (Windows VM — to be ported)

Two Python scripts currently doing the Excel transformation on the VM:
- `FTWZ_BE_Data_Import_transformation_v1.py`
- `FTWZ_DTA_Data_Import_transformation_v1.py`

### Critical findings from these scripts:

**win32com block (DELETE in Lambda):**
```python
excel = win32com.client.Dispatch("Excel.Application")
wb = excel.Workbooks.Open(file_path)
wb.SaveAs(new_file_path, FileFormat=51)  # .xls → .xlsx
```
**Lambda fix:** `pd.read_excel(file_path, engine='xlrd')` reads `.xls` directly. No conversion needed.

**Excel structure:**
- `skiprows=2` — 2 metadata rows before column headers
- Filter: `df[df['CLIENT IEC'].astype(str) == '300014953']` → move to Secrets Manager as JSON list
- One NSDL row = one item. Header/invoice/shipment data repeated across rows.
- Complex denormalisation: header + shipment + invoice + items → concat axis=1 → flat per-item rows

**PO extraction from item description:**
```python
import_df['Item Subtext'] = df['Item Description'].str.extract(r'\^([^^]*)\^')
# Split by '/' → PO Number + PO Item No
# e.g. "^1023637424/10^" → PO Number: "1023637424", PO Item No: "10"
```

**5 QC validation checks (must pass before pushing to Canofy):**
1. Header count == final_df Request ID count
2. Invoice count == final_df Invoice No count
3. Item count == len(final_df)
4. Shipment count matches
5. Sum checks: Quantity, Assessable Value, BCD must match input totals

**Shipment consolidation rules:**
- BE: drop shipment rows where `HAWB / HBL No.` is null
- DTA: drop shipment rows where `Total Number of Packages` is null

**Date fields — 3 formats handled:**
- `YYYY-DD-MM HH:MM:SS`
- `YYYY-MM-DD HH:MM:SS`
- `MM/DD/YYYY HH:MM:SS AM/PM`
All converted to `YYYY-MM-DD` for Canofy.

**40 duty fields** zero-filled if NaN (identical list for BE and DTA).

**BE vs DTA key differences:**
| Feature | BE | DTA |
|---|---|---|
| IGM fields (shipment) | ✅ igm_no, igm_date | ❌ |
| Country of Consignment | ✅ | ❌ |
| Supplier fields (invoice) | ✅ supplier_name, supplier_country, suppliers_address | ❌ |
| Buyer fields (header) | ❌ | ✅ buyer_name, buyer_country, buyer_id_type, buyer_id_number, buyer_address, buyer_ie_code |
| Destination | ❌ | ✅ |
| goods_measurement_unit (items) | ✅ | ❌ |
| bdrflag (items) | ❌ | ✅ |
| Shipment drop rule | HAWB null | Total Packages null |
| BOE PDF download | ✅ Phase 2 | ❌ No PDF |

---

## 4. Canofy Doctype Specification

### Doctype Names (exact, for API URLs)
| Doctype | API Path |
|---|---|
| `FTWZ BOE Import` | `/api/resource/FTWZ%20BOE%20Import` |
| `BOE Import Items` | child of FTWZ BOE Import |
| `BOE Import Invoices` | child of FTWZ BOE Import |
| `BOE Import Shipment` | child of FTWZ BOE Import |
| `FTWZ DTA Sale` | `/api/resource/FTWZ%20DTA%20Sale` |
| `DTA Sale Items` | child of FTWZ DTA Sale |
| `DTA Sale Invoices` | child of FTWZ DTA Sale |
| `DTA Sale Shipments` | child of FTWZ DTA Sale |
| `Attachments Details` | child of BOTH (parentfield: `attachments`) |

### Idempotency Key
Both doctypes: `request_id` (NSDL numeric string, e.g. `"172600305544"`)
```
GET /api/resource/FTWZ BOE Import?filters=[["request_id","=","172600305544"]]&fields=["name"]&limit=1
```

### Critical Field Quirks (from live Canofy records)

1. **Dual shipment storage** — BOTH must be populated:
   - Flat header fields: `mawb_mbl_no1`, `hawb_hbl_no1`, `igm_no1`, etc.
   - Child table: `table_all_shipment_details` list

2. **Field name inconsistency (do NOT fix — replicate exactly):**
   - BE header: `uom_for_gross_weight_1` (underscore before 1)
   - DTA header: `uom_for_gross_weight1` (no underscore)

3. **Computed totals** (Lambda must calculate before POST):
   ```
   tot_assval = sum(item.assessable_value_item)
   tot_bcd    = sum(item.basic_customs_duty)
   tot_igst   = sum(item.igst_amount)
   tot_sws    = sum(item.social_welfare_surcharge_on_customs)
   tot_duty   = sum(item.tot_duty_item)
   ```

4. **workflow_state** — always `"Pending Review"` on creation. Never change.

5. **transaction_id == thoka_noting_number** — same NSDL value fills both fields.

6. **Denormalized invoice_no** — appears on each item row AND in invoices child table.

7. **4 empty attachment placeholders** always created on record creation:
   - `AWB_HBL`, `Invoice`, `Packing List`, `Bill of Entry`
   - No file URLs at creation time. PDFs linked later via separate upload+attach call.

8. **DTA has no BOE PDF** — skip Phase 2 (PDF download) entirely for DTA pipeline.

### Live BE Record (verified from Canofy production)
```json
{
  "doctype": "FTWZ BOE Import",
  "naming_series": "FTWZ-BE-.#####.",
  "workflow_state": "Pending Review",
  "request_id": "172600305544",
  "sez_id": "337",
  "client_iec": "300014953",
  "thoka_noting_number": "1001335",
  "transaction_id": "1001335",
  "boe_type": "Home Consumption",
  "source_of_import": "Abroad",
  "country_of_consignment": "Germany",
  "port_of_origin": "Frankfurt/Main",
  "port_of_discharge": "Chennai (Ex Madras)",
  "port_of_shipment": "Frankfurt/Main",
  "authorised_dealer_code": "6500114",
  "authorised_dealer_name": "HONGKONG AND SHANGHAI BANKING CORPN.LTD.",
  "reimport": "N",
  "previous_import": "N",
  "outwards_remittance_involved": "N",
  "entity_name_importer_name": "SIDDHARTHA LOGISTICS FTWZ PRIVATE LIMITED",
  "entity_id": "3417",
  "sez_name": "SRI  CITY  SEZ (MULTI PRODUCT)",
  "joint_filing_client_details": "ENDRESS HAUSER INDIA PRIVATE LIMITED",
  "client_gstin": "27AAACE5283C1ZV",
  "client_country": "India",
  "client_type": "INDIAN",
  "entity_status_on_previous_day": "Active-Operational",
  "purpose_of_transaction": "Service",
  "request_status": "OutOfCharge",
  "request_submission_date": "2026-03-02",
  "request_assessment_date": "2026-03-02",
  "request_out_of_charge_date": "2026-04-28",
  "transaction_date": "2026-03-02",
  "tot_assval": 732013.46,
  "tot_duty": 66647.8914,
  "tot_bcd": 60588.9921,
  "tot_igst": 0,
  "tot_sws": 6058.8993,
  "igm_no1": "2998942",
  "igm_date1": "2026-02-27",
  "mawb_mbl_no1": "17623105224",
  "mawb_mbl_date1": "2026-02-20",
  "hawb_hbl_no1": "FBG26010506",
  "hawb_hbl_date1": "2026-02-20",
  "uom_for_gross_weight_1": "KGS",
  "gross_weight1": "6.7",
  "total_no_of_packages1": 2,
  "uom_for_packages1": "PKG",
  "items": [
    {
      "doctype": "BOE Import Items",
      "item_description": "^1023637424/10^ ...",
      "cth_no": "90279090",
      "country_of_origin": "Germany",
      "currency": "EURO",
      "exchange_rate": 109.2,
      "quantity": 1,
      "unit_price_invoice_currency": 1785.72,
      "product_value_invoice_currency": 1785.72,
      "cif_value_item_in_inr": 195000.624,
      "assessable_value_item": 195000.62,
      "freight_in_inr": "0",
      "insurance_in_inr": 0,
      "goods_measurement_unit": "PCS",
      "mode_of_transport": "Air",
      "item_type": "Traded Goods",
      "provisional_request": "N",
      "sector_finalized": "Trading And Services",
      "duty_forgone_paid_flag": "DUTY FORGONE",
      "duty_forgone_paid_amount": 16087.55,
      "invoice_no": "9270030204",
      "bcd_to_be_considered_for_calculating_igst": "0",
      "basic_customs_duty": 14625.0468,
      "igst_amount": 0,
      "social_welfare_surcharge_on_customs": 1462.5047,
      "tot_duty_item": 16087.5515,
      "po_number": "1023637424",
      "po_item_no": "10",
      "item_subtext": "1023637424/10",
      "product_match": "No",
      "nccd": 0, "compensation_cess_amount": 0, "surcharge_on_bcd": 0,
      "health_cess_customs": 0, "sapta_bcd": 0, "other_customs_duties": 0,
      "ade_under_section_3_1_duty": "0", "sp_excise_duty_sch_ii": 0,
      "cess_excise": 0, "she_cess_excise": 0, "safeguard": 0,
      "aidc_on_customs": 0, "additional_duty_of_excise_ttw": 0,
      "health_cess_excise": 0, "other_additional_customs_duty": 0,
      "additional_duty_of_excise_nccd_excise": 0, "aidc_on_excise": 0,
      "additional_duty_of_excise_tta": 0, "education_cess_customs": 0,
      "other_additional_central_excise_duty": 0,
      "countervailing_duty_of_additional_customs_duty": 0,
      "other_central_excise_duties": 0, "education_cess_excise": 0,
      "sad": 0, "social_welfare_surcharge_on_gst": 0, "cess_customs": 0,
      "she_cess_customs": 0, "anti_dumping": 0,
      "road_and_infrastructure_cess_on_customs": 0,
      "additional_duty_of_excise_gsi": 0
    }
  ],
  "invoices": [
    {
      "doctype": "BOE Import Invoices",
      "invoice_type": "CIF",
      "invoice_no": "9270030204",
      "invoice_date": "2026-02-19",
      "supplier_name": "Endress Hauser SICK GmbHCo. KG",
      "supplier_country": "Germany",
      "suppliers_address": "Bergener Ring 27 01458 Ottendorf Germany"
    }
  ],
  "table_all_shipment_details": [
    {
      "doctype": "BOE Import Shipment",
      "igm_no": "2998942",
      "igm_date": "2026-02-27",
      "mawb_mbl_no": "17623105224",
      "mawb_mbl_date": "2026-02-20",
      "hawb_hbl_no": "FBG26010506",
      "hawb_hbl_date": "2026-02-20",
      "total_no_of_packages": 2,
      "uom_for_packages": "PKG",
      "gross_weight": 6.7,
      "uom_for_gross_weight": "KGS"
    }
  ],
  "attachments": [
    {"doctype": "Attachments Details", "attachment_type": "AWB_HBL"},
    {"doctype": "Attachments Details", "attachment_type": "Invoice"},
    {"doctype": "Attachments Details", "attachment_type": "Packing List"},
    {"doctype": "Attachments Details", "attachment_type": "Bill of Entry"}
  ]
}
```

### Live DTA Record (verified from Canofy production)
```json
{
  "doctype": "FTWZ DTA Sale",
  "naming_series": "FTWZ-DTA-S-.#####.",
  "workflow_state": "Pending Review",
  "request_id": "262600426675",
  "sez_id": "337",
  "client_iec": "300014953",
  "thoka_noting_number": "2004022",
  "transaction_id": "2004022",
  "boe_type": "Home Consumption",
  "buyer_name": "ENDRESS  HAUSER (INDIA) PRIVATE LIMITED",
  "buyer_ie_code": "300014953",
  "buyer_country": "India",
  "buyer_id_type": "G",
  "buyer_id_number": "27AAACE5283C1ZV",
  "destination": "DTA Unit",
  "previous_procurement": "N",
  "previous_dta_sales": "N",
  "outwards_remittance_involved": "Y",
  "entity_name_importer_name": "SIDDHARTHA LOGISTICS FTWZ PRIVATE LIMITED",
  "entity_id": "3417",
  "sez_name": "SRI  CITY  SEZ (MULTI PRODUCT)",
  "joint_filing_client_details": "ENDRESS HAUSER INDIA PRIVATE LIMITED",
  "client_gstin": "27AAACE5283C1ZV",
  "client_country": "India",
  "client_type": "INDIAN",
  "entity_status_on_previous_day": "Active-Operational",
  "purpose_of_transaction": "Service",
  "request_status": "OutOfCharge",
  "request_submission_date": "2026-06-11",
  "request_assessment_date": "2026-06-11",
  "request_out_of_charge_date": "2026-06-11",
  "transaction_date": "2026-06-11",
  "tot_assval": 27878.2,
  "tot_duty": 5018.0752,
  "tot_bcd": 0,
  "tot_igst": 5018.0752,
  "tot_sws": 0,
  "mawb_mbl_no1": "4570449100",
  "mawb_mbl_date1": "2026-06-05",
  "hawb_hbl_no1": "4570449100",
  "gross_weight1": 81.5,
  "uom_for_gross_weight1": "KGS",
  "total_no_of_packages1": 23,
  "uom_for_packages1": "PKG",
  "items": [
    {
      "doctype": "DTA Sale Items",
      "item_description": "^1023659813/30^ Memosens CLS16E",
      "cth_no": "90278930",
      "country_of_origin": "Germany",
      "currency": "EURO",
      "exchange_rate": 113,
      "quantity": 1,
      "unit_price_invoice_currency": 241.85,
      "product_value_invoice_currency": 241.85,
      "cif_value_item_in_inr": 27329.05,
      "assessable_value_item": 27878.2,
      "freight_in_inr": "0",
      "insurance_in_inr": 0,
      "mode_of_transport": "Land",
      "item_type": "Traded Goods",
      "provisional_request": "N",
      "sector_finalized": "Trading And Services",
      "duty_forgone_paid_flag": "Duty Payable",
      "duty_forgone_paid_amount": 5018.08,
      "invoice_no": "6009106794",
      "bcd_to_be_considered_for_calculating_igst": "0",
      "basic_customs_duty": 0,
      "igst_amount": 5018.0752,
      "social_welfare_surcharge_on_customs": 0,
      "bdrflag": "False",
      "tot_duty_item": 5018.0752,
      "po_number": "1023659813",
      "po_item_no": "30",
      "item_subtext": "1023659813/30",
      "product_match": "No"
    }
  ],
  "invoices": [
    {
      "doctype": "DTA Sale Invoices",
      "invoice_type": "CIF",
      "invoice_no": "6009106794",
      "invoice_date": "2026-06-03"
    }
  ],
  "table_all_shipment_details": [
    {
      "doctype": "DTA Sale Shipments",
      "mawb_mbl_no": "4570449100",
      "mawb_mbl_date": "2026-06-05",
      "hawb_hbl_no": "4570449100",
      "total_no_of_packages": 23,
      "uom_for_packages": "PKG",
      "gross_weight": 81.5,
      "uom_for_gross_weight": "KGS"
    }
  ],
  "attachments": [
    {"doctype": "Attachments Details", "attachment_type": "AWB_HBL"},
    {"doctype": "Attachments Details", "attachment_type": "Invoice"},
    {"doctype": "Attachments Details", "attachment_type": "Packing List"},
    {"doctype": "Attachments Details", "attachment_type": "Bill of Entry"}
  ]
}
```

---

## 5. Target Repository Structure

```
ftwz_automation/
├── Dockerfile
├── template.yaml
├── samconfig.toml
├── .github/
│   └── workflows/
│       ├── deploy-dev.yml      # auto-trigger on push to main
│       └── deploy-prod.yml     # manual trigger only (workflow_dispatch)
└── src/
    ├── lambda_handler.py       # orchestrator — AAI pattern
    ├── nsdl_portal_client.py   # Playwright scraper — SEZ → Lambda port
    ├── nsdl_parser.py          # Excel transform — win32com scripts → pure pandas
    ├── frappe_client.py        # Canofy REST API — AAI pattern + FTWZ doctypes
    ├── schema.py               # dataclasses + payload builders
    ├── ocr_engine.py           # EasyOCR CAPTCHA solver — from SEZ
    ├── logger.py               # structured CloudWatch logger
    └── requirements.txt
```

---

## 6. Architecture Decisions (Final, Non-Negotiable)

| Decision | Choice | Reason |
|---|---|---|
| Browser | Playwright async | Lambda-proven in AAI; Selenium needs ChromeDriver version mgmt |
| XLS reading | `pd.read_excel(engine='xlrd')` | NSDL exports `.xls`; xlrd reads directly, no win32com |
| CAPTCHA | EasyOCR + OpenCV | Already working in SEZ; fully programmatic, no manual cookie refresh |
| Pipeline structure | Single Lambda, two internal pipelines | Matches AAI pattern; simpler deployment |
| asyncio calls | Two per pipeline (Excel download, PDF download) | Idempotency check between phases |
| Canofy integration | Frappe REST API POST with nested child tables | Direct JSON insert, no Data Import CLI |
| CLIENT IEC filter | Secrets Manager JSON list | Not hardcoded — multi-client ready |
| Error handling | Per-record try/except, batch continues | One bad record never kills 200 others |
| Docker image size | ~6-7GB (PyTorch CPU + Playwright + EasyOCR) | Within Lambda 10GB limit |
| Env separation | DEV / PROD via `environment` param in event | Same Lambda image, different Frappe URLs |
| Schedule | `cron(0 2 * * ? *)` = 07:30 AM IST | Daily unattended run |
| Timeout | 900s | Headless browser + bulk processing |
| Memory | 3008MB | Headless Chromium requirement |
| Ephemeral storage | 2048MB | XLS files + bulk PDFs in /tmp |

---

## 7. Secrets Manager Schema

**Secret name:** `ftwz-automation`

```json
{
  "NSDL_USERNAME": "...",
  "NSDL_PASSWORD": "...",
  "CLIENT_IEC_CODES": "[\"300014953\"]",
  "DEV_FRAPPE_URL": "https://canofy-dev.example.com",
  "DEV_FRAPPE_API_KEY": "...",
  "DEV_FRAPPE_API_SECRET": "...",
  "PROD_FRAPPE_URL": "https://canofy.example.com",
  "PROD_FRAPPE_API_KEY": "...",
  "PROD_FRAPPE_API_SECRET": "..."
}
```

**No NSDL session cookies** — EasyOCR solves CAPTCHA programmatically. Fully unattended from day one (key improvement over AAI which needs daily manual cookie refresh).

---

## 8. Lambda Handler Event Shape

```json
{
  "from_date": "2026-06-01",
  "to_date": "2026-06-25",
  "pipelines": ["BE", "DTA"],
  "environment": "PROD"
}
```

- `from_date` / `to_date`: optional, defaults to last 7 days
- `pipelines`: optional, defaults to both
- `environment`: required — `DEV` or `PROD`

---

## 9. Pipeline Flow (Both Pipelines)

### BE Pipeline
```
Phase 1: asyncio.run(download_excel("BILL OF ENTRY", from_date, to_date))
           → /tmp/be_trade_data.xls

Phase 2: parse_be_excel(xls_path, client_iec_codes)
           → List[List[dict]]  (grouped by request_id)
           → QC validation (5 checks) — raises if fails

Phase 3: Idempotency check per request_id
           → filter to new_records only

Phase 4: asyncio.run(download_boe_pdfs(new_request_ids))
           → {request_id: /tmp/{request_id}.pdf}

Phase 5-6: For each request_id group:
             build_be_payload(group) → to_api_dict()
             upload_pdf() → file_url
             create_be_record() → record_name
             attach_pdf_to_record()

Phase 7: cleanup_tmp_files()
```

### DTA Pipeline
```
Same as BE except:
- Module: "DTA SALE"
- No Phase 4 (no PDFs for DTA)
- Uses: parse_dta_excel, record_exists_dta, build_dta_payload, create_dta_record
```

---

## 10. Hard Rules (Non-Negotiable)

1. No `win32com` anywhere
2. No `pytest`, `conftest`, `selenium`
3. No `sleep()` — use `page.wait_for_selector()`, `page.wait_for_download()`, `page.wait_for_url()`
4. No hardcoded credentials
5. No hardcoded `CLIENT IEC` — read from Secrets Manager
6. No file output from parser — returns Python objects only
7. Per-record errors never crash the batch
8. Idempotency checked before every create
9. Both flat header shipment fields AND child table populated
10. `workflow_state` always `"Pending Review"`
11. `transaction_id` always equals `thoka_noting_number`
12. Totals computed in Lambda before POST
13. 4 empty attachment placeholders on every record creation
14. DTA pipeline has no PDF download phase
15. `uom_for_gross_weight_1` (BE) vs `uom_for_gross_weight1` (DTA) — replicate exactly, do not normalize
16. All files complete — no stubs, no TODOs, no `pass`

---

## 11. Build Sequence

```
1. src/logger.py
2. src/ocr_engine.py
3. src/nsdl_parser.py
4. src/schema.py
5. src/frappe_client.py
6. src/nsdl_portal_client.py
7. src/lambda_handler.py
8. src/requirements.txt
9. Dockerfile
10. template.yaml
11. samconfig.toml
12. .github/workflows/deploy-dev.yml
13. .github/workflows/deploy-prod.yml
```

Post-build verification:
```bash
python -m py_compile src/*.py
grep -r "win32com\|selenium\|sleep(" src/   # must be empty
grep -r "300014953" src/                     # must be empty (no hardcoded IEC)
```

---

## 12. What Is Out of Scope (This Phase)

| Item | Status |
|---|---|
| SD File pipeline (SDv1, SDv2) | Phase 2 — format spec not yet received |
| Broker File ingestion | Phase 2 |
| Email notifications | Handled by Canofy server script |
| Canofy Doctype creation | Already exists in production |
| Dashboard / monitoring UI | CloudWatch + structured logs sufficient |

---

## 13. Deliverable Already Produced

**`claude_code_prompt.md`** (~49K tokens) — Complete Claude Code build prompt containing:
- All 16 reference source files embedded inline (AAI Lambda + SEZ Selenium)
- Both live Canofy records as JSON
- All 13 per-file build instructions
- 17 hard rules
- Build sequence with verification steps

**To use:** Open Claude Code in a new `ftwz_automation/` repo, paste the entire `claude_code_prompt.md` as first message.

---

## 14. Open Questions (Pre-Production)

| # | Question | Impacts |
|---|---|---|
| Q1 | NSDL date range strategy — last N days or explicit from/to per EventBridge trigger? | `resolve_date_range()` default logic |
| Q2 | Does DTA Sale have a BOE PDF on NSDL portal under Search Request? | `run_dta_pipeline()` Phase 4 |
| Q3 | Is `FTWZ Automation Error Log` doctype already in Canofy? | `frappe_client.push_error_log()` |
| Q4 | Should Lambda post a success notification to Canofy after each run? | `lambda_handler.py` final block |
| Q5 | Are there other SEZ IDs / FTWZ entities beyond `337` / `3417`? | Filter logic in `nsdl_parser.py` |
| Q6 | AWS ECR repository URL for ftwz-automation? | `template.yaml` + `deploy.yml` |
