Summary
A target-repo concept that arrives via BOTH the bridge_child cascade path AND a direct-search path lands in recommendable_concepts[] twice, because the two paths produce different concept_key strings:
- Bridge cascade target:
["…","BD50.3Z&XA01A6","HEAD"]
- Direct match:
["…","BD50.3Z&XA01A6",null]
In buildV2RecommendationPayload (src/components/map-projects/MapProject.jsx), defsByKey keys on the full opaque key, so these are treated as distinct entries and the evidence from the two paths never converges. The LLM then sees the same concept twice with split evidence and inflated payload size.
Root cause
cascadeTargetToConceptDefinition in src/components/map-projects/normalizers.js (named toBridgeChildConceptDefinition in the master plan) delegates to resolveReference, which inherits projectContext.target_repo.version (normalizers.js:97-98). In production projects pinned at "HEAD", that string becomes the version slot on the cascade-target reference.
Direct-algorithm matches against the same target repo arrive without a version slot (their reference_source chain either resolves a real version or null, depending on the algo). The two paths therefore disagree on what should be the same concept identity.
Fix
In cascadeTargetToConceptDefinition, drop the version slot on the resolved reference unconditionally. "HEAD" isn't a stable version identifier — it's the project pin, not a per-concept version — and the cascade-target stub is always going to be re-resolved via ensureLoaded anyway. Aligning both paths on no-version is cleaner than pinning both to a sentinel.
Verification
- Unit test in src/components/map-projects/tests/normalizers.test.js: with
projectContext.target_repo.version = 'HEAD', a bridge cascade-target ConceptDefinition's key does not include "HEAD"; it matches the key produced by makeConceptKey({url, code}) for a direct match of the same (url, code).
- Same-(url, code) bridge_child + direct-path produces ONE entry in the v2 payload's
recommendable_concepts with evidence.length === 2. (PR3-G handles the orthogonal merge-order bug; both need to land for full convergence.)
Context
Part of the post-PR2b OCL Mapper unification close-out (PR3-X — auditability and visibility first). PR3-X.1 (audit-trail snapshot) shipped in ocl-ai-assistant#139. This ticket is the oclmap half of PR3-X. See ocl-online-docs/mapper/unified-mapper-model.md -> "PR3 Implementation Plan" -> "PR3-X.2" for the slice spec.
Refs #2337 (umbrella).
Summary
A target-repo concept that arrives via BOTH the bridge_child cascade path AND a direct-search path lands in
recommendable_concepts[]twice, because the two paths produce differentconcept_keystrings:["…","BD50.3Z&XA01A6","HEAD"]["…","BD50.3Z&XA01A6",null]In
buildV2RecommendationPayload(src/components/map-projects/MapProject.jsx),defsByKeykeys on the full opaque key, so these are treated as distinct entries and the evidence from the two paths never converges. The LLM then sees the same concept twice with split evidence and inflated payload size.Root cause
cascadeTargetToConceptDefinitionin src/components/map-projects/normalizers.js (namedtoBridgeChildConceptDefinitionin the master plan) delegates toresolveReference, which inheritsprojectContext.target_repo.version(normalizers.js:97-98). In production projects pinned at "HEAD", that string becomes the version slot on the cascade-target reference.Direct-algorithm matches against the same target repo arrive without a version slot (their
reference_sourcechain either resolves a real version ornull, depending on the algo). The two paths therefore disagree on what should be the same concept identity.Fix
In
cascadeTargetToConceptDefinition, drop the version slot on the resolved reference unconditionally."HEAD"isn't a stable version identifier — it's the project pin, not a per-concept version — and the cascade-target stub is always going to be re-resolved viaensureLoadedanyway. Aligning both paths on no-version is cleaner than pinning both to a sentinel.Verification
projectContext.target_repo.version = 'HEAD', a bridge cascade-target ConceptDefinition'skeydoes not include "HEAD"; it matches the key produced bymakeConceptKey({url, code})for a direct match of the same (url, code).recommendable_conceptswithevidence.length === 2. (PR3-G handles the orthogonal merge-order bug; both need to land for full convergence.)Context
Part of the post-PR2b OCL Mapper unification close-out (PR3-X — auditability and visibility first). PR3-X.1 (audit-trail snapshot) shipped in ocl-ai-assistant#139. This ticket is the oclmap half of PR3-X. See
ocl-online-docs/mapper/unified-mapper-model.md-> "PR3 Implementation Plan" -> "PR3-X.2" for the slice spec.Refs #2337 (umbrella).