Skip to content

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

POST /api/internal/coordination/outputs/:eventId/submit/by-last1id/:identityId
Body: {}

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):

{ "document": { "id", "originalFilename", "contentType", "side", ... } }

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.

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):

  1. Pull iOS main with Build 72 (CURRENT_PROJECT_VERSION = 72).
  2. 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.
  3. 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.
  4. Tap Submit; headline becomes "You submitted this." on both surfaces; org Received step goes current.
  5. 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