Thread: iOS I3b-3b — Coordination write contract (submit + document upload)¶
Not IA 3-5. Not Meridian. Sub-slice 3b of I3b-3 (native coordination write path).
Decisions (Ryan, Sep 14 2026)¶
| Decision | Choice |
|---|---|
| Slice order | 3b after 3a device gate (confirm/concern + 3a.1 stepper honesty). |
| Device cohort | MOCK Pipeline e5c80469-5db2-4fcc-a9b0-dae21f07efab, Last1 ID 2a805b21-5352-4316-bb4b-a5d4f02a8173. |
| Deploy target | R940 only (VM 104 last1.app + VM 90 last.vet-api). Not AWS/ECS. |
| Document view (3f) | Separate. Upload shows filename list in-card; no decrypt/view in 3b. |
Problem¶
Veteran submit and document upload exist on last1.app member routes only. LastVet BFF had confirm/concern (3a). 3b adds submit + multipart upload through the same Ω engine.
Architecture¶
LastVet iOS (Build 72)
→ POST api.last.vet/veteran/me/coordination/outputs/:eventId/submit
→ POST api.last.vet/veteran/me/coordination/outputs/:eventId/documents (multipart file)
→ last.vet-api BFF (veteran JWT → last1_identity_id)
→ POST last1.app/api/internal/coordination/outputs/:eventId/{submit|documents}/by-last1id/:identityId
→ recordVeteranSubmit / storeProgramCoordinationOutputDocument (same as web)
→ submit refreshes getVeteranProgramCoordinationByLast1IdentityId
Drift prevention: Same engine as web member routes. Golden JSON per action. BFF route tests. Side-by-side parity with last1.app/member/progress?programId=.
UI rule (Build 72)¶
Show submit/upload only when server says so:
verification.yourTurn == true
&& verification.nextActor == "veteran"
&& verification.veteranTurnAction == "submit"
Then show Choose file + uploaded filename list + Submit (matching web). No client-side transition logic. After submit success, replace UI from refreshed program + turnCard in write response. Upload success appends filename locally (web invalidates document list; 3f adds read path).
Internal routes (last1.app)¶
Auth: Authorization: Bearer $LAST1_APP_SECRET. Requires live Last1 ID consent link.
POST submit¶
Success: same shape as 3a write response (ok, message, program, loop, turnCard).
POST document upload¶
POST /api/internal/coordination/outputs/:eventId/documents/by-last1id/:identityId
Content-Type: multipart/form-data
Field: file (PDF, JPG, PNG, HEIC; max 10 MB)
Success (201):
BFF routes (last.vet-api)¶
POST /api/veteran/me/coordination/outputs/:eventId/submit
POST /api/veteran/me/coordination/outputs/:eventId/documents (multipart file)
Golden fixtures¶
| Repo | Path |
|---|---|
| last1.app | shared/programCoordination/fixtures/coordinationWriteSubmit.golden.json |
| last1.app | shared/programCoordination/fixtures/coordinationOutputDocumentUpload.golden.json |
| last.vet-api | tests/fixtures/coordinationWriteSubmit.golden.json |
| last.vet-api | tests/fixtures/coordinationOutputDocumentUpload.golden.json |
Org setup for veteran-submit device test¶
Unlike org-evidence outputs (Training & Certification, confirm ladder), veteran-evidence outputs start with your turn to submit. No org ladder steps are required when the output is still not_started.
Important: MOCK Pipeline has no submit-state outputs¶
Prod query for beneficiary b33c18d2... / Last1 ID 2a805b21... shows Demo Pipeline (MOCK) e5c80469-5db2-4fcc-a9b0-dae21f07efab contains only org-evidence Ω outputs. None have veteranTurnAction == submit. You cannot device-test Submit on MOCK until a veteran-evidence output exists on that program.
Upload-only on MOCK (partial 3b): document upload works on org outputs too (curl-verified on training-requirements-met / fea88265-...). Build 72 can test Choose file + upload on MOCK, but not the submit button unless you switch programs or reset state.
Recommended submit cohort: Demo VetClaims (test)¶
Same beneficiary, different program:
| Field | Value |
|---|---|
| Program | Demo VetClaims disability rating (test) fbf54f34-5e58-47ea-882b-f693c627b508 |
| Veteran-evidence outputs | benefit-verification-letter-obtained, records-evidence-gathered, cp-exams-completed |
| Current prod state | All three are Verified/closed on this beneficiary today |
To get "Ready for you to submit." you need a veteran-evidence output reset to not_started (delete Ω rows for that event+beneficiary scope, or re-enroll a fresh test beneficiary on VetClaims test template). Then LastVet program detail for fbf54f34-... shows submit UI on that output.
Admin reference page (same beneficiary):
https://last1.app/beneficiaries/b33c18d2-44e0-4650-b9d9-a5b6d5f59744/programs/fbf54f34-5e58-47ea-882b-f693c627b508
DD-214 wedge note¶
Golden fixtures use slug dd214-upload from the vetclaims-preflight template. That slug is not on the three enrolled programs above. DD-214 is the canonical submit wedge in fixtures/docs; device testing uses whichever veteran-evidence output you reset to not_started.
Device test flow (Build 72):
- Pull iOS
mainwith Build 72 (CURRENT_PROJECT_VERSION = 72). - Upload partial test on MOCK: open MOCK Pipeline in LastVet, expand an output (e.g. Training & Certification), verify Choose file uploads and filename appears in-card. Cross-check on web member documents list.
- Full submit test: open Demo VetClaims (test) after resetting a veteran-evidence output to
not_started. Headline "Ready for you to submit."; Choose file + Submit visible. - Tap Submit; headline becomes "You submitted this." on both surfaces; org Received step goes current.
- Home Your Turn clears for that output.
Curl verify (prod)¶
# Internal submit (wrong state → 400 proves engine reachable)
curl -sS -X POST \
-H "Authorization: Bearer $LAST1_APP_SECRET" \
"https://last1.app/api/internal/coordination/outputs/$EVENT_ID/submit/by-last1id/$LAST1_ID" \
-d '{}' | jq '{ok,message,code}'
# Internal document upload
curl -sS -X POST \
-H "Authorization: Bearer $LAST1_APP_SECRET" \
-F "file=@/tmp/sample.pdf;type=application/pdf" \
"https://last1.app/api/internal/coordination/outputs/$EVENT_ID/documents/by-last1id/$LAST1_ID" | jq '{document:.document.originalFilename}'
# BFF (veteran JWT)
curl -sS -X POST \
-H "Authorization: Bearer $VETERAN_JWT" \
"https://api.last.vet/api/veteran/me/coordination/outputs/$EVENT_ID/submit" \
-d '{}'
Next slices (gated)¶
| Slice | Build | Actions |
|---|---|---|
| 3c | 73 | respond (dialogue) |
| 3d | 74 | resubmit |
| 3e | 75 | loop attest (+ BSL if needed) |
| 3f | TBD | document view/download |