Thread: iOS I3b-3a — Coordination write contract (confirm + concern)¶
Not IA 3-5. Not Meridian. Sub-slice 3a of I3b-3 (native coordination write path).
Decisions (Ryan, Sep 14 2026)¶
| Decision | Choice |
|---|---|
| Slice order | 3a-first (confirm/concern). 3b (submit/upload) waits for device gate on 3a. |
| Device cohort | MOCK Pipeline e5c80469-5db2-4fcc-a9b0-dae21f07efab, Last1 ID 2a805b21-5352-4316-bb4b-a5d4f02a8173 (continuity with I3b-2 Build 70). |
| Deploy target | R940 only (VM 104 last1.app + VM 90 last.vet-api). Not AWS/ECS. |
Problem¶
Veteran Ω writes exist only on last1.app member routes (POST /api/member/program-coordination/outputs/:eventId/...) with Supabase session auth. LastVet had read-only BFF (I3b-2). I3b-3a adds the first write path without duplicating the engine.
Architecture¶
LastVet iOS (Build 71)
→ POST api.last.vet/veteran/me/coordination/outputs/:eventId/{confirm|concern}
→ last.vet-api BFF (veteran JWT → last1_identity_id)
→ POST last1.app/api/internal/coordination/outputs/:eventId/{confirm|concern}/by-last1id/:identityId
→ recordMemberConfirmOutput / recordMemberConcernViaCutover (same as web)
→ refresh getVeteranProgramCoordinationByLast1IdentityId
Drift prevention: Same engine functions as web. Golden JSON per action. BFF route tests. Side-by-side parity with last1.app/member/progress?programId=.
UI rule (Build 71)¶
Show controls only when server says so:
verification.yourTurn == true
&& verification.nextActor == "veteran"
&& verification.veteranTurnAction == "confirm"
Then show Confirm + I have a concern (matching web). No client-side transition logic. No optimistic ladder advances. After success, replace UI from refreshed program + turnCard in write response.
Internal routes (last1.app)¶
Auth: Authorization: Bearer $LAST1_APP_SECRET (same as I3b-2 reads). Requires live Last1 ID consent link.
POST confirm¶
POST concern¶
POST /api/internal/coordination/outputs/:eventId/concern/by-last1id/:identityId
Body: { "message": "string (required, trimmed non-empty)" }
Success response (both)¶
{
"ok": true,
"message": "Output confirmed." | "Your concern was recorded.",
"program": { "...VeteranProgramProgressDetail..." },
"loop": { "...VeteranLoopCardModel | null..." },
"turnCard": { "...VeteranProgramTurnCard | null..." }
}
Cache-Control: no-store, private
Errors¶
Same shape as member routes where applicable:
403 consent: { "error": "consent_required", "message": "..." }
BFF routes (last.vet-api)¶
Veteran JWT required. Resolves last1_identity_id from LastVet veteran row.
POST /api/veteran/me/coordination/outputs/:eventId/confirm
POST /api/veteran/me/coordination/outputs/:eventId/concern
Body (concern only): { "message": "..." }
Response: verbatim internal write JSON.
Golden fixtures¶
| Repo | Path |
|---|---|
| last1.app | shared/programCoordination/fixtures/coordinationWriteConfirm.golden.json |
| last.vet-api | tests/fixtures/coordinationWriteConfirm.golden.json |
Curl verify (prod, agent-run)¶
# Internal confirm (VM 104 or via last1.app)
curl -sS -X POST \
-H "Authorization: Bearer $LAST1_APP_SECRET" \
-H "Content-Type: application/json" \
"https://last1.app/api/internal/coordination/outputs/$EVENT_ID/confirm/by-last1id/$LAST1_ID" \
-d '{}' | jq '{ok,message,state:.program.phases[0].events[0].state,turn:.turnCard.headline}'
# BFF confirm (needs veteran JWT)
curl -sS -X POST \
-H "Authorization: Bearer $VETERAN_JWT" \
-H "Content-Type: application/json" \
"https://api.last.vet/api/veteran/me/coordination/outputs/$EVENT_ID/confirm" \
-d '{}'
Device test checklist (Build 71)¶
- Side-by-side:
last1.app/member/progress?programId=e5c80469-...and LastVet program detail for MOCK Pipeline. - When
veteranTurnAction == confirm, LastVet shows Confirm + I have a concern (not grey capsule). - Tap Confirm on LastVet → state matches web after refresh (output closed, turn card updates).
- Reset cohort if needed; tap I have a concern, enter text, Send concern → ladder pauses, matches web.
- Home Your Turn card updates after returning from detail (notification refresh).
Next slices (gated)¶
| Slice | Build | Actions |
|---|---|---|
| 3b | 72 | submit + document upload |
| 3c | 73 | respond (dialogue) |
| 3d | 74 | resubmit |
| 3e | 75 | loop attest (+ BSL if needed) |