Part of the Unified Mapper Model PR3 close-out. Tracked as PR3-G in ocl-online-docs/mapper/unified-mapper-model.md.
Summary
After PR2b (unified-model read-flip) and PR3-D1 (LOINC critical-path payload fixes), AI Assistant recommendable_concepts[*] entries for bridge cascade targets are missing display_name (and the rest of the enriched fields — names, concept_class, datatype, rerank_score) in a significant fraction of cases, even when the lookup repo returned 200 OK for the cascade target.
The thin entries have only 3 keys (evidence, concept_key, canonical_reference) while fully-enriched entries have 8 keys (adds display_name, names, concept_class, datatype, rerank_score).
This causes the LLM to reason over a degraded payload — postcoordination clusters and stem codes both end up with no display name, so the model can't compare semantic content and falls back to code-only reasoning.
Scope clarification — distinct from postcoord lookup-404 issue
PR3-G is the lookup-OK case (162 instances in run-2 evidence below). A separate bug (postcoord URL double-encoding causing 404s — mapper-roadmap.md Phase 0 #14) accounts for an additional 41 instances with the same surface symptom. The two are independent; PR3-G can't fix the lookup-404 case and vice versa.
Evidence — 2026-05-21 ICD-11 IMO Deep Analysis (project 126, 100 rows)
| Bucket |
Instances |
Unique codes |
Diagnosis |
| Lookup 200, but `display_name` missing in AI payload |
162 |
162 |
PR3-G — this ticket. |
| Lookup 4xx (postcoord URL encoding 404s) |
41 |
34 |
Separate — `mapper-roadmap` Phase 0 #14. |
| No lookup attempted |
0 |
0 |
n/a |
Paired test case within a single row (run-2 `prompt-executions/3710.json`, row 1 "Need for vaccination"):
- QC02.Z — bridge response `cascade_target_concept_name: null`, lookup 200 (returns `'Need for immunization against certain specified single infectious diseases, unspecified'`), AI payload entry has full enrichment ✓
- QC01.2 — bridge response `cascade_target_concept_name: null`, lookup 200 (returns `'Need for immunization against rabies'`), AI payload entry has only `{evidence, concept_key, canonical_reference}` ❌
Both took identical code paths. The difference is somewhere between lookup-success and AI-payload-build, intermittently affecting some keys but not others.
Architectural framing (per @paynejd 2026-05-23)
For ICD-11 projects, the bridge response carrying `cascade_target_concept_name: null` is expected — ICD-11-WHO is a metadata-only target repo, so cascade-target names aren't populated on the CIEL→ICD-11 mappings. The custom `lookup_config` repo (here: `/orgs/OpenMRS-OCL-Squad/sources/ICD-11-WHO-Mapper/`) is the authoritative source for `display_name` and other concept fields. PR3-G must ensure lookup-enriched ConceptDefinitions reach the AI payload without being dropped.
For LOINC projects, the bridge response itself typically carries `cascade_target_concept_name` (LOINC target repo has full concept data), so the bug primarily affects the AI-payload-build step when lookup is the data source.
Static analysis ruled out
The single-write paths look correct:
- `mergeIntoRowMatchState` rank guard (MapProject.jsx:378): strict `>` prevents pending stubs from overwriting full entries.
- `writeConceptCachePatch` (MapProject.jsx:3308): spreads lookup response `data` after `existing`, so `display_name` lands.
- `ensureLoaded` in-flight dedup (MapProject.jsx:3356): synchronous `forEach` works.
- `buildV2RecommendationPayload` read (MapProject.jsx:3864): reads `conceptCacheRef.current[concept_key]` directly; no transformation strips `display_name`.
- Concept-key construction: same `resolveReference` + `makeConceptKey` chain on both bridge-stub and lookup write paths.
Two suspect inter-render interactions
Static analysis can't conclusively distinguish these — needs runtime instrumentation to confirm.
(A) State-sync effect at MapProject.jsx:1738:
```js
React.useEffect(() => { conceptCacheRef.current = conceptCache; }, [conceptCache]);
```
If any code path calls `setConceptCache(value)` where `value` was built from a stale closure of `conceptCache` (React state), this effect copies the stale state back into the ref, wiping out any synchronous `writeConceptCachePatch` enrichments in between.
(B) Legacy URL-keyed `setConceptCache` at MapProject.jsx:2407:
```js
setConceptCache({...conceptCache, [url]: res})
```
Spreads `conceptCache` (closure-captured React state) and adds a URL-keyed entry. Combined with (A), could wipe in-flight concept-key-keyed enrichments.
Investigation plan
- Ship a small instrumentation PR (localStorage-gated debug logging on every `conceptCache[<target_key>]` write). Land first; not the fix.
- Reproduce locally on project 126 (or a small ICD-11 fixture). Capture the QC01.2 write trace.
- Confirm which of (A), (B), or a third path is wiping the enrichment.
- Ship the targeted fix as a follow-up PR. Either bump `lookupStatusRank` to factor `display_name` presence, or refactor the ref/state sync to not copy stale state back.
Acceptance criteria
Related
Summary
After PR2b (unified-model read-flip) and PR3-D1 (LOINC critical-path payload fixes), AI Assistant
recommendable_concepts[*]entries for bridge cascade targets are missingdisplay_name(and the rest of the enriched fields —names,concept_class,datatype,rerank_score) in a significant fraction of cases, even when the lookup repo returned 200 OK for the cascade target.The thin entries have only 3 keys (
evidence,concept_key,canonical_reference) while fully-enriched entries have 8 keys (addsdisplay_name,names,concept_class,datatype,rerank_score).This causes the LLM to reason over a degraded payload — postcoordination clusters and stem codes both end up with no display name, so the model can't compare semantic content and falls back to code-only reasoning.
Scope clarification — distinct from postcoord lookup-404 issue
PR3-G is the lookup-OK case (162 instances in run-2 evidence below). A separate bug (postcoord URL double-encoding causing 404s —
mapper-roadmap.mdPhase 0 #14) accounts for an additional 41 instances with the same surface symptom. The two are independent; PR3-G can't fix the lookup-404 case and vice versa.Evidence — 2026-05-21 ICD-11 IMO Deep Analysis (project 126, 100 rows)
Paired test case within a single row (run-2 `prompt-executions/3710.json`, row 1 "Need for vaccination"):
Both took identical code paths. The difference is somewhere between lookup-success and AI-payload-build, intermittently affecting some keys but not others.
Architectural framing (per @paynejd 2026-05-23)
For ICD-11 projects, the bridge response carrying `cascade_target_concept_name: null` is expected — ICD-11-WHO is a metadata-only target repo, so cascade-target names aren't populated on the CIEL→ICD-11 mappings. The custom `lookup_config` repo (here: `/orgs/OpenMRS-OCL-Squad/sources/ICD-11-WHO-Mapper/`) is the authoritative source for `display_name` and other concept fields. PR3-G must ensure lookup-enriched ConceptDefinitions reach the AI payload without being dropped.
For LOINC projects, the bridge response itself typically carries `cascade_target_concept_name` (LOINC target repo has full concept data), so the bug primarily affects the AI-payload-build step when lookup is the data source.
Static analysis ruled out
The single-write paths look correct:
Two suspect inter-render interactions
Static analysis can't conclusively distinguish these — needs runtime instrumentation to confirm.
(A) State-sync effect at MapProject.jsx:1738:
```js
React.useEffect(() => { conceptCacheRef.current = conceptCache; }, [conceptCache]);
```
If any code path calls `setConceptCache(value)` where `value` was built from a stale closure of `conceptCache` (React state), this effect copies the stale state back into the ref, wiping out any synchronous `writeConceptCachePatch` enrichments in between.
(B) Legacy URL-keyed `setConceptCache` at MapProject.jsx:2407:
```js
setConceptCache({...conceptCache, [url]: res})
```
Spreads `conceptCache` (closure-captured React state) and adds a URL-keyed entry. Combined with (A), could wipe in-flight concept-key-keyed enrichments.
Investigation plan
Acceptance criteria
Related