IMM-04 · Tier-4 submission and acknowledgement
SOP:
SOP_Immigration_Applications.md§6 / §8 Step 6Actors: IMM Operator —admin@spade.local(PLATFORM_ADMIN) /IMM_OPERATOR. Pre-state: Run isAWAITING_REVIEWwith a tier-4IMM.SUBMIT_APPLICATIONreview task (see IMM-03). Post-state: Operator has filed in EP Online / WPOL and approved the gate. AFilingTransaction(MOM, PASS_APPLICATION, ACKNOWLEDGED)+Document(IMM_APPLICATION)+StatutoryDeadline(PASS_RENEWAL)are written; runCLOSED.
0. Prerequisites
- The application detail (
/dashboard/imm/applications/<runId>) showsreview.awaitingDecision = true. - The operator has the real MOM acknowledgement number obtained by filing manually in the authority portal (the platform never auto-files — FR-IMM-007).
1. Steps
1.1 File with MOM (manual, off-platform)
Using the assembled pack (pack.mappedFields + artifact from the detail response, or the presigned download), the operator keys the application into EP Online / WPOL and obtains the acknowledgement number.
1.2 Approve the gate
The IMM gate reuses the shared review inbox approve endpoint:
POST /ops/review-inbox/:reviewTaskId/approve
Content-Type: application/json
{
"filing": {
"acknowledgementNumber": "MOM-EP-2026-998877",
"filedAt": "2026-06-10T09:30:00.000Z", // optional; defaults to now
"reference": "internal-ref-001" // optional
}
}The review-inbox handler forwards applyInput.filing (only on approve, only for tier-4 filing actions) to the module apply executor and moves the run AWAITING_REVIEW → APPLYING.
1.3 What the apply executor writes (imm-apply.ts)
In one tenant-scoped transaction (the only writer for this workflow):
- Idempotency guard — if a
FilingTransactionalready exists for the run, returns the existing acknowledgement as a no-op (no double-submit on retry). FilingTransaction—registry = MOM,transactionType = PASS_APPLICATION,status = ACKNOWLEDGED, the operator'sacknowledgementNumber,filedById,filedAt. (Falls back toMOM-ACK-<applicationRef>if no operator filing supplied.)- Persists the W3 pack: a
File(real objectKeyimm/<runId>/<fileName>, content metadata, sha-256) — bytes PUT to S3 when a storage writer is wired — and aDocument(IMM_APPLICATION)pointing at it (status = SENT,filingId,source_run_id). StatutoryDeadline(PASS_RENEWAL)via the sharedupsertStatutoryDeadline— duefiledAt + 24 months, or the operator-suppliedrenewalDueDate.- Emits
FILING_OUTCOME(+DEADLINE_STATEvia the deadline upsert).
The run then closes APPLYING → CLOSED.
2. Verification
Database
SELECT registry, transaction_type, status, acknowledgement_number, filed_at
FROM filing_transactions WHERE run_id = '<runId>';
-- expect MOM / PASS_APPLICATION / ACKNOWLEDGED / <ack number>
SELECT doc_type, status, file_id FROM documents
WHERE source_run_id = '<runId>' AND doc_type = 'IMM_APPLICATION';
-- expect status = 'SENT', a file_id
SELECT deadline_type, status, due_date FROM statutory_deadlines
WHERE entity_id = '<entityId>' AND deadline_type = 'PASS_RENEWAL';
-- expect one row, due ≈ filedAt + 24 months (or operator-supplied)
SELECT status, closed_at FROM agent_runs WHERE id = '<runId>';
-- expect 'CLOSED'S3 / MinIO
- When a storage writer is wired, the pack bytes land at
imm/<runId>/<fileName>. The detail/packendpoint presigns this for download.
API
GET /ops/imm/applications/:runId—filingandrenewaltabs now populated;pack.storedDownloadUrlnon-null.GET /ops/imm/applications/:runId/pack—200 { downloadUrl, fileName, expiresAt }(presigned, 10-min TTL).
Events
SELECT event_class, outcome FROM events
WHERE run_id = '<runId>' AND event_class = 'FILING_OUTCOME';
-- outcome = 'ACKNOWLEDGED'3. Negative & edge cases
- No acknowledgement in payload → apply falls back to a synthesised
MOM-ACK-<applicationRef>(dev/adapter path). - Retry after a mid-apply crash → idempotent: returns the existing filing, no second
FilingTransaction. - Reject / request changes → the run returns to
RUNNING(or stays at review) and no filing is written.
Next
Proceed to IMM-05 · Renewal tracking.