proposal-system/web
Adam Moussa 9632c1048c
fix: resolve Phase 6c gate findings (2 high, 5 low) across api, web, lambda, mobile
Gemini scanner sweep (token-bypass), GPT-4.1 cross-review, and the
6-detector /sh-security-review fan-out ran against 6acfdab..HEAD; every
confirmed finding fixed:

HIGH (deployment blockers, logic detector):
- CONC-L1: suggestions lambda's bulk line-item PUT sent no
  proposalVersion — every AI suggestion job would 422 and be silently
  swallowed. Now fetches the proposal's rowVersion, echoes it, and
  retries once with a fresh token on 409. Pytest updated (38 green).
- CONC-L2: mobile admin surface (update/approve/send/revise, bulk line
  items) sent no tokens — the entire mobile admin workflow would 422.
  Tokens threaded through mobile api layer + workspace/line-item
  screens with 409 refetch handling. tsc clean.

MEDIUM-adjacent (scanner):
- VendorProposalsController: the VendorTotalCost write on Proposal now
  bumps Version (was a silent lost-update path bypassing the guard).
- FilesController: GeneratePDF audit staged into the same SaveChanges.

LOW (detectors):
- 409 envelope is schema-validated client-side
  (proposalConcurrencyConflictSchema.safeParse) and id-checked before
  seeding the react-query cache; malformed state degrades to
  invalidation (INJ-409-01/WEB-CONC-L1).
- ProposalConcurrencyException.CurrentState typed ProposalResponse?
  so an EF entity can never serialize into the 409 body (SC-1).
- Guard caller contract documented + GuardedEndpointAuthorizationTests
  reflection tripwire: guard-reaching endpoints must stay admin-gated
  (AUTHZ-CG-01).
- Pre-check currentState now loads display navigations so both 409
  paths return the same shape (CONC-L3).
- Save chain's trailing getById failure no longer misreports a
  committed save; falls back to invalidation (CONC-L4).

Also caught during fix verification: the handler's manual currentState
serialization lacked JsonStringEnumConverter — enums would serialize
as numbers, client schema validation would reject every guarded 409,
and the state would always be discarded. Now matches the MVC pipeline
and is pinned by a wire test.

193 xUnit / 70 vitest / 38 pytest green; mobile + shared tsc clean;
Playwright smoke 2/2.
2026-07-13 21:20:12 -04:00
..
.claude/skills/verify style(web): prettier format pass (mechanical) 2026-07-13 20:14:47 -04:00
e2e feat(contracts+web): thread concurrency tokens and 409 conflict handling through the domain layer 2026-07-13 21:01:46 -04:00
src fix: resolve Phase 6c gate findings (2 high, 5 low) across api, web, lambda, mobile 2026-07-13 21:20:12 -04:00
.env.example Implement Phases 2-5: Frontend, AI/RAG, PDF Generation (#22) 2026-05-17 13:06:23 -04:00
.prettierignore ci(web): Phase 5 — prettier check + Playwright smoke via org frontend workflow 2026-07-13 20:14:41 -04:00
.prettierrc ci(web): Phase 5 — prettier check + Playwright smoke via org frontend workflow 2026-07-13 20:14:41 -04:00
index.html style(web): prettier format pass (mechanical) 2026-07-13 20:14:47 -04:00
package-lock.json ci(web): Phase 5 — prettier check + Playwright smoke via org frontend workflow 2026-07-13 20:14:41 -04:00
package.json ci(web): Phase 5 — prettier check + Playwright smoke via org frontend workflow 2026-07-13 20:14:41 -04:00
playwright.config.ts ci(web): Phase 5 — prettier check + Playwright smoke via org frontend workflow 2026-07-13 20:14:41 -04:00
tsconfig.json fix(web): apply Phase 4 code-review findings (10 correctness + 4 cleanup) 2026-07-13 19:06:08 -04:00
vite.config.ts fix(web): apply Phase 4 code-review findings (10 correctness + 4 cleanup) 2026-07-13 19:06:08 -04:00
vitest.config.ts fix(web): apply Phase 4 code-review findings (10 correctness + 4 cleanup) 2026-07-13 19:06:08 -04:00