Summary
Low-priority correctness items found in the API review, batched together.
Items
- Task write races (
fhir/client.ts:925-942 assignTask, 965-996 transitionTask): both do GET → mutate in memory → PUT with no If-Match/ETag and no status precondition — two concurrent writes on the same task silently lose one, and transitionTask will "complete" an already-cancelled task.
assignTask accepts nonexistent coordinator ids (routes/tasks.ts:46-51 validates only non-empty string): a typo'd id is stored in FHIR and permanently orphans the task from team-performance reporting (team/service.ts:74 matches ownerCoordinatorId against users.id).
- Duplicate RiskAssessments double-count population (
fhir/client.ts:683-693): one profile per resource, not per patient — a patient with two RiskAssessments appears as two scatter points and inflates totalPatients/projectedCostAvoidance.
- Governance pagination parsing (
routes/governance.ts:84-87): Number('') is 0, so ?limit= (present but empty) yields LIMIT 0 (empty page) instead of the default; there is also no upper bound (?limit=99999999 dumps the whole table).
- Mock-fixture replay after headers sent (
routes/analysis.ts:241-248): calls replayCachedAnalysis after writeHead with no try/catch (the cached-replay path at 208-218 has one) — a throw there propagates after headers are sent.
Summary
Low-priority correctness items found in the API review, batched together.
Items
fhir/client.ts:925-942assignTask,965-996transitionTask): both do GET → mutate in memory → PUT with noIf-Match/ETag and no status precondition — two concurrent writes on the same task silently lose one, andtransitionTaskwill "complete" an already-cancelled task.assignTaskaccepts nonexistent coordinator ids (routes/tasks.ts:46-51validates only non-empty string): a typo'd id is stored in FHIR and permanently orphans the task from team-performance reporting (team/service.ts:74matchesownerCoordinatorIdagainstusers.id).fhir/client.ts:683-693): one profile per resource, not per patient — a patient with two RiskAssessments appears as two scatter points and inflatestotalPatients/projectedCostAvoidance.routes/governance.ts:84-87):Number('')is0, so?limit=(present but empty) yieldsLIMIT 0(empty page) instead of the default; there is also no upper bound (?limit=99999999dumps the whole table).routes/analysis.ts:241-248): callsreplayCachedAnalysisafterwriteHeadwith no try/catch (the cached-replay path at 208-218 has one) — a throw there propagates after headers are sent.