Repository navigation
OpenConceptLab/ocl_issues#2909 | A candidate leads with its name in the input language, and the matched name comes second - #102
Conversation
…he input language, and the matched name comes second A candidate led with the name its match was made on, then an arrow and its display name, so a match on a translation put a name in another language first. For "Pregnancy test" in an English project, LOINC 89497-2 read "Anamnesi Punto nel tempo (episodio) Test prenatale -> Mother's Prenatal tests". - conceptNames.js picks the names. localizedDisplayName is the concept's name in the project's input language (its preferred name, then its fully specified name, then its first), or else display_name, which OCL computes from the repository's default locale. getMatchedName is the name that matched, with its locale, or null when it is the name the candidate leads with. - Concept.jsx leads with that name and shows "Matched name (<locale>): ..." on the line below it. The grid's Target Code cell, the row panel's candidates (with its AI Analysis grouping) and Search tab, and the row panel's Target Code header get the project's input language. - The candidates' table view names each row the same way. - The name-locale filter compares primary subtags, so "it" admits an it-IT name, as it already does for the names sent to the OCL AI Assistant. - New string map_project.matched_name in en, es, pt-BR and zh. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NSAveBj6W6LKJLYTWJ9rwi
…candidates lead with their name in it The helper text said the input language only chose which name locales go to the OCL AI Assistant. It now also sets the name each candidate leads with, so the text says so, in en, es, pt-BR and zh, and in ConfigurationForm's English default. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NSAveBj6W6LKJLYTWJ9rwi
paynejd
left a comment
There was a problem hiding this comment.
Codex review, pass 1 (codex-cli 0.162.1, commit 797f81a)
3 P1.
F1.1 · P1 · src/components/map-projects/MapProject.jsx:1136 · The grid and selected-target summary omit the required matched name and locale. (Select the ticket's candidate with its Italian synonym highlight. Its candidate card shows the secondary matched name, but the grid explicitly suppresses it through noMatchedName. MappingDecisionResult also renders only the localized title and properties. Acceptance criteria 2–3 therefore remain unmet.)
F1.2 · P1 · src/components/map-projects/Search.jsx:212 · Search results revert to the repository language when switched to table view. (In a Spanish project, a result with display_name 'Blood glucose' and preferred Spanish name 'Glucosa en sangre' shows the Spanish title as a card. Switching to table view shows 'Blood glucose': TableResults bypasses this Concept renderer and reads the unchanged response.data display_name.)
F1.3 · P1 · src/components/map-projects/Candidates.jsx:190 · Sorting by display name no longer sorts the names users see. (In a Spanish project, choose display-name sorting for candidates 'Blood glucose' → 'Glucosa en sangre' and 'Diabetes mellitus'. sortRowViews still compares conceptDefinition.display_name before localization, so the visible ascending list is 'Glucosa en sangre', then 'Diabetes mellitus'.)
findings.json
{
"reviewer": "codex-cli 0.162.1",
"perspective": "defects",
"mode": "full",
"commit": "797f81a8fbbe9d39c3b2c477ebbd4d6c45fa600b",
"verdict": "findings",
"prior": [],
"findings": [
{
"id": "F1.1",
"severity": "P1",
"tag": null,
"file": "src/components/map-projects/MapProject.jsx",
"line": 1136,
"claim": "The grid and selected-target summary omit the required matched name and locale.",
"scenario": "Select the ticket's candidate with its Italian synonym highlight. Its candidate card shows the secondary matched name, but the grid explicitly suppresses it through noMatchedName. MappingDecisionResult also renders only the localized title and properties. Acceptance criteria 2–3 therefore remain unmet.",
"action": "Render the distinct matched name and locale in the grid and selected-target summary using the shared helper and retained search highlights.",
"lines_delta": null,
"confidence": "high"
},
{
"id": "F1.2",
"severity": "P1",
"tag": null,
"file": "src/components/map-projects/Search.jsx",
"line": 212,
"claim": "Search results revert to the repository language when switched to table view.",
"scenario": "In a Spanish project, a result with display_name 'Blood glucose' and preferred Spanish name 'Glucosa en sangre' shows the Spanish title as a card. Switching to table view shows 'Blood glucose': TableResults bypasses this Concept renderer and reads the unchanged response.data display_name.",
"action": "Apply localized display-name rendering to the search table as well, preserving the original concept for mapping actions.",
"lines_delta": null,
"confidence": "high"
},
{
"id": "F1.3",
"severity": "P1",
"tag": null,
"file": "src/components/map-projects/Candidates.jsx",
"line": 190,
"claim": "Sorting by display name no longer sorts the names users see.",
"scenario": "In a Spanish project, choose display-name sorting for candidates 'Blood glucose' → 'Glucosa en sangre' and 'Diabetes mellitus'. sortRowViews still compares conceptDefinition.display_name before localization, so the visible ascending list is 'Glucosa en sangre', then 'Diabetes mellitus'.",
"action": "Use localizedDisplayName with the project's input locale as the display-name sort key, including nested bridge candidates, and add a regression test.",
"lines_delta": null,
"confidence": "high"
}
]
}…r and both tables name a concept as its candidate card does Codex review, pass 1: - F1.1: the grid's Target Code cell and the row panel's Target Code header now show the matched name, with its locale, under the name the target leads with, from the mapped concept's search highlights, as a candidate card does. The header's box has a fixed height, so there the line keeps to one line, with the whole of it on hover. The name-locale filter reaches both. Concept's noMatchedName prop, now unused, is gone. - F1.2: the Search tab's table view, like the candidates' one, names each concept in the input language. TableResults lets an extra column with a base column's id replace it in place (withExtraColumns, unit-tested), and both tables pass a name column that renders localizedDisplayName. The rows stay as they were, so Map and the concept panel keep the concept's own display_name. The candidates' rows no longer rename display_name. - F1.3: sorting candidates by display name sorts on the name shown in the input language (sortRowViews takes inputLocale), including bridge children. Tested. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NSAveBj6W6LKJLYTWJ9rwi
paynejd
left a comment
There was a problem hiding this comment.
Codex review, pass 2 (codex-cli 0.162.1, commit 071ef7a)
2 P1.
F2.1 · P1 · src/components/map-projects/MapProject.jsx:1136 · Reopening a saved project loses localized titles and matched names in the grid. (Save and reopen a Spanish project mapping 'Blood glucose', whose preferred Spanish name is 'Glucosa en sangre'. setProjectFromData reconstructs mapSelected from CSV without names or highlights. getConcept searches URL-shaped cache keys, but saved candidate definitions use opaque concept keys. The grid therefore receives the incomplete CSV stub and displays 'Blood glucose' without the matched name, even though both are available in the restored candidate state. The fresh-selection fix for F1.1 does not cover this path.)
F2.2 · P1 · src/components/map-projects/Concept.jsx:73 · Switching candidates or search results to table view still hides the distinct matched name and locale. (For the ticket's English candidate with an Italian synonym highlight, card mode shows the matched Italian name and its locale. Switching to table mode renders only the localized display name because localizedNameColumn never renders MatchedName. Acceptance criteria 2–3 remain unmet in these row-panel views.)
findings.json
{
"reviewer": "codex-cli 0.162.1",
"perspective": "defects",
"mode": "verify",
"commit": "071ef7a4afac23eb9a6379b405d2949dd839a3f2",
"verdict": "findings",
"prior": [
{
"id": "F1.1",
"status": "unresolved"
},
{
"id": "F1.2",
"status": "resolved"
},
{
"id": "F1.3",
"status": "resolved"
}
],
"findings": [
{
"id": "F2.1",
"severity": "P1",
"tag": null,
"file": "src/components/map-projects/MapProject.jsx",
"line": 1136,
"claim": "Reopening a saved project loses localized titles and matched names in the grid.",
"scenario": "Save and reopen a Spanish project mapping 'Blood glucose', whose preferred Spanish name is 'Glucosa en sangre'. setProjectFromData reconstructs mapSelected from CSV without names or highlights. getConcept searches URL-shaped cache keys, but saved candidate definitions use opaque concept keys. The grid therefore receives the incomplete CSV stub and displays 'Blood glucose' without the matched name, even though both are available in the restored candidate state. The fresh-selection fix for F1.1 does not cover this path.",
"action": "Resolve grid targets against the restored concept definitions and merge their row-specific candidate highlights before rendering. Add a save/reload regression test.",
"lines_delta": null,
"confidence": "high"
},
{
"id": "F2.2",
"severity": "P1",
"tag": null,
"file": "src/components/map-projects/Concept.jsx",
"line": 73,
"claim": "Switching candidates or search results to table view still hides the distinct matched name and locale.",
"scenario": "For the ticket's English candidate with an Italian synonym highlight, card mode shows the matched Italian name and its locale. Switching to table mode renders only the localized display name because localizedNameColumn never renders MatchedName. Acceptance criteria 2–3 remain unmet in these row-panel views.",
"action": "Render the distinct matched name and locale below the table title using getMatchedName and MatchedName, and pass through the name-locale filter.",
"lines_delta": null,
"confidence": "high"
}
]
}…d both tables, show the matched name too Codex review, pass 2: - F2.1 (and F1.1, which it reopened): a project reopened from its saved file restores each mapped target from the CSV, with no names or highlights, so its grid cell named it in the repository's language and showed no matched name. withCandidateNames gives the target the names and highlights its row's candidates hold for it, found by findTargetRowView: the URL-then-id match the row panel header already used, now shared. Both are unit-tested with a target as a reopened project restores it. - F2.2: the table views' name column shows the matched name, with its locale, under the name, as a candidate card does, and the name-locale filter reaches it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NSAveBj6W6LKJLYTWJ9rwi
paynejd
left a comment
There was a problem hiding this comment.
Codex review, pass 3 (codex-cli 0.162.1, commit d03da8c)
1 P1.
F3.1 · P1 · src/components/map-projects/viewBuilders.js:442 · Targets mapped from Search still lose localized names and match evidence after reopening. (In a Spanish project, map a Search result absent from the row's algorithm candidates, then save and reopen. Before saving, 'Hemoglobin' displays as 'Hemoglobina' with the English matched name underneath. Reload reconstructs mapSelected from CSV without names or highlights; it does not restore the full concept submitted in matches. This helper finds no candidate and returns the stub, so the grid shows 'Hemoglobin' without the matched name.)
findings.json
{
"reviewer": "codex-cli 0.162.1",
"perspective": "defects",
"mode": "verify",
"commit": "d03da8cdc3b494af978c08dd8b3ef18327a4fec7",
"verdict": "findings",
"prior": [
{
"id": "F1.1",
"status": "resolved"
},
{
"id": "F1.2",
"status": "resolved"
},
{
"id": "F1.3",
"status": "resolved"
},
{
"id": "F2.1",
"status": "resolved"
},
{
"id": "F2.2",
"status": "resolved"
}
],
"findings": [
{
"id": "F3.1",
"severity": "P1",
"tag": null,
"file": "src/components/map-projects/viewBuilders.js",
"line": 442,
"claim": "Targets mapped from Search still lose localized names and match evidence after reopening.",
"scenario": "In a Spanish project, map a Search result absent from the row's algorithm candidates, then save and reopen. Before saving, 'Hemoglobin' displays as 'Hemoglobina' with the English matched name underneath. Reload reconstructs mapSelected from CSV without names or highlights; it does not restore the full concept submitted in matches. This helper finds no candidate and returns the stub, so the grid shows 'Hemoglobin' without the matched name.",
"action": "Restore names and row-specific highlights for Search-only selections, independently of algorithm candidates, and add a save/reopen regression test for this path.",
"lines_delta": null,
"confidence": "high"
}
]
}|
Response to Codex pass 3 F3.1 open: fixing it needs a decision this ticket doesn't make, so it's the question in the "Needs you" comment below. |
|
Needs you Decision: should a reopened project bring back the input-language name and the matched name of a target that was mapped from the Search tab, and if so, how? There are two ways, both beyond this ticket:
Or leave it to a follow-up ticket. This is Codex's F3.1 (P1), still open after the last of the three passes. Here's what happens now:
State of the rest:
|
|
Decision on F3.1 F3.1 accepted for this PR, and moved to OpenConceptLab/ocl_issues#2916. A target mapped from the Search tab keeps today's behavior here: its cell leads with the repository's name after a reopen, until its row is opened. The fix doesn't need a new saved format. Every save already sends each mapped target's full concept, with its names and search highlight, in the project's Next on this PR: the simplification review it skipped. |
…y-name branch Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
paynejd
left a comment
There was a problem hiding this comment.
Simplification review (commit 0bbdb05)
6 items, about -12 lines.
- S1 · delete · src/components/map-projects/Concept.jsx:477 · Bridge children get
matchedName={matchedName}(the parent's matched name), but Item renders MatchedName only when!bridgeConceptDefinition, and every bridge child is passedbridgeConceptDefinition={conceptDefinition}. The prop is never read. This is the old synonymPrefix pass-through under a new name. → Nothing replaces it. Remove the prop from the bridge-children ConceptItem. (-1) - S2 · delete · src/components/map-projects/viewBuilders.js:422 · The pass-2 helpers guard against cases their callers rule out. findTargetRowView's
if(!target) return undefinedand(views || []): MapProject calls it only undertargetConcept ? ... : falsewith an array, and withCandidateNames calls it only from the grid insideif(targetConcept?.id)with buildQualityRowViews' array. withCandidateNames'rowState ? ... : undefinedis also redundant, because buildQualityRowViews already returns [] for a missing rowState (viewBuilders.js:237). → Make findTargetRowViewviews.find(...)with no target guard. Writeconst view = findTargetRowView(buildQualityRowViews(rowState, conceptCache), target)in withCandidateNames. Drop thefindTargetRowView(views, null)assertion (views.test.js:715). The rest of that test and the withCandidateNames tests, which pin the F2.1 fix, stay, and the undefined-rowState assertion still passes. (-2) - S3 · yagni · src/components/map-projects/conceptNames.js:23 · toLocaleList has one caller and accepts an array that no caller passes. Every
locales/filterLocalescomes fromfilters.locale || '', which ConfigurationForm always builds withvalues.join(','). The array branch and its test assertion cover unused flexibility. → Inline it in getMatchedName asconst allowed = (filterLocales || '').split(',').map(locale => primarySubtag(locale.trim())).filter(Boolean). Optionally use the existinguniqByPrimarySubtagfrom normalizers.js if trimming isn't wanted. Drop thefilterLocales: ['it']assertion (conceptNames.test.js:164). (-3) - S4 · shrink · src/components/map-projects/conceptNames.js:16 · plainText has one caller, and its
(html || '')guard can't fire: getMatchedName has already returned null whenfragmentis falsy. → Inline it asconst text = fragment.replace(/<\/?em>/g, '')and delete the helper. (-1) - S5 · dup-test · src/components/search/tests/tableColumns.test.js:19 · 'appends extra columns after the base ones' is covered by the next test, whose id-order assertion
['id', 'name', 'datatype', 'mappings']already checks that a non-matching extra column (mappings) is appended after the base columns. → Nothing replaces it. Delete the first test. The replace-in-place test, which pins the F1.2 fix, stays. (-4) - S6 · dup-test · src/components/map-projects/tests/conceptNames.test.js:87 · In the ticket-case getMatchedName test,
assert.equal(displayName, "Mother's Prenatal tests")repeats the assertionlocalizedDisplayName(PRENATAL_TESTS, 'en')already makes in 'keeps the display name when it is already in the input language'. → Nothing replaces it. Delete the assertion and keep the getMatchedName deepEqual. (-1)
findings.json
{
"reviewer": "claude subagent",
"perspective": "simplify",
"mode": "full",
"commit": "0bbdb050d280e83288ffe49f619910ce1b7fa386",
"verdict": "findings",
"prior": [],
"findings": [
{
"id": "S1",
"severity": null,
"tag": "delete",
"file": "src/components/map-projects/Concept.jsx",
"line": 477,
"claim": "Bridge children get `matchedName={matchedName}` (the parent's matched name), but Item renders MatchedName only when `!bridgeConceptDefinition`, and every bridge child is passed `bridgeConceptDefinition={conceptDefinition}`. The prop is never read. This is the old synonymPrefix pass-through under a new name.",
"scenario": null,
"action": "Nothing replaces it. Remove the prop from the bridge-children ConceptItem.",
"lines_delta": -1,
"confidence": "high"
},
{
"id": "S2",
"severity": null,
"tag": "delete",
"file": "src/components/map-projects/viewBuilders.js",
"line": 422,
"claim": "The pass-2 helpers guard against cases their callers rule out. findTargetRowView's `if(!target) return undefined` and `(views || [])`: MapProject calls it only under `targetConcept ? ... : false` with an array, and withCandidateNames calls it only from the grid inside `if(targetConcept?.id)` with buildQualityRowViews' array. withCandidateNames' `rowState ? ... : undefined` is also redundant, because buildQualityRowViews already returns [] for a missing rowState (viewBuilders.js:237).",
"scenario": null,
"action": "Make findTargetRowView `views.find(...)` with no target guard. Write `const view = findTargetRowView(buildQualityRowViews(rowState, conceptCache), target)` in withCandidateNames. Drop the `findTargetRowView(views, null)` assertion (views.test.js:715). The rest of that test and the withCandidateNames tests, which pin the F2.1 fix, stay, and the undefined-rowState assertion still passes.",
"lines_delta": -2,
"confidence": "high"
},
{
"id": "S3",
"severity": null,
"tag": "yagni",
"file": "src/components/map-projects/conceptNames.js",
"line": 23,
"claim": "toLocaleList has one caller and accepts an array that no caller passes. Every `locales`/`filterLocales` comes from `filters.locale || ''`, which ConfigurationForm always builds with `values.join(',')`. The array branch and its test assertion cover unused flexibility.",
"scenario": null,
"action": "Inline it in getMatchedName as `const allowed = (filterLocales || '').split(',').map(locale => primarySubtag(locale.trim())).filter(Boolean)`. Optionally use the existing `uniqByPrimarySubtag` from normalizers.js if trimming isn't wanted. Drop the `filterLocales: ['it']` assertion (conceptNames.test.js:164).",
"lines_delta": -3,
"confidence": "medium"
},
{
"id": "S4",
"severity": null,
"tag": "shrink",
"file": "src/components/map-projects/conceptNames.js",
"line": 16,
"claim": "plainText has one caller, and its `(html || '')` guard can't fire: getMatchedName has already returned null when `fragment` is falsy.",
"scenario": null,
"action": "Inline it as `const text = fragment.replace(/<\\/?em>/g, '')` and delete the helper.",
"lines_delta": -1,
"confidence": "high"
},
{
"id": "S5",
"severity": null,
"tag": "dup-test",
"file": "src/components/search/__tests__/tableColumns.test.js",
"line": 19,
"claim": "'appends extra columns after the base ones' is covered by the next test, whose id-order assertion `['id', 'name', 'datatype', 'mappings']` already checks that a non-matching extra column (`mappings`) is appended after the base columns.",
"scenario": null,
"action": "Nothing replaces it. Delete the first test. The replace-in-place test, which pins the F1.2 fix, stays.",
"lines_delta": -4,
"confidence": "high"
},
{
"id": "S6",
"severity": null,
"tag": "dup-test",
"file": "src/components/map-projects/__tests__/conceptNames.test.js",
"line": 87,
"claim": "In the ticket-case getMatchedName test, `assert.equal(displayName, \"Mother's Prenatal tests\")` repeats the assertion `localizedDisplayName(PRENATAL_TESTS, 'en')` already makes in 'keeps the display name when it is already in the input language'.",
"scenario": null,
"action": "Nothing replaces it. Delete the assertion and keep the getMatchedName deepEqual.",
"lines_delta": -1,
"confidence": "medium"
}
]
}…ound unused or repeated Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Response to the simplification review S1 applied in 19f6eba Lint and |
paynejd
left a comment
There was a problem hiding this comment.
Codex check of commits d03da8c..19f6eba (codex-cli 0.163.0, commit 19f6eba)
Clean. It checked the merge of main (0bbdb05) and the simplification cuts (19f6eba).
findings.json
{
"reviewer": "codex-cli 0.163.0",
"perspective": "defects",
"mode": "cuts",
"commit": "19f6eba865e75fd43323252b0efcc85a26d4fe7d",
"verdict": "clean",
"prior": [],
"findings": []
}
What changed and why
A candidate led with the name its match was made on, then an arrow and its display name. When that name was a translation, an English project showed candidates titled in another language: for "Pregnancy test" (input language English, LOINC 2.81), 89497-2 read "Anamnesi Punto nel tempo (episodio) Test prenatale → Mother's Prenatal tests".
Now a concept leads with its name in the project's input language, and the name that matched comes second, on the line below, with its locale:
conceptNames.js(new, pure, unit-tested) holds the two rules:localizedDisplayName(concept, inputLocale): the concept's name in the input language, matched on the primary subtag (sopttakes apt-BRname). If the display name is already in that language it stays. Otherwise the concept takes that language's preferred name, then its fully specified name, then its first. With no name in that language, or no input language, it falls back todisplay_name, which OCL computes from the repository's default locale.getMatchedName(...): the name that matched (anamehighlight means the display name matched, otherwise the bestsynonymshighlight, picked as before), with its locale from the concept's names. It returns null when that name is the one the concept leads with (compared ignoring case and spacing).Concept.jsxleads withlocalizedDisplayNameand shows the matched name on its own line, through a smallMatchedNamecomponent, with the new stringmap_project.matched_name("Matched name"). The old prefix and arrow are gone. Bridge rows name their concepts the same way.MappingDecisionResult). Its box has a fixed height, so the matched name keeps to one line there, with all of it on hover.withCandidateNames, found byfindTargetRowViewinviewBuilders.js).findTargetRowViewis the URL-then-id match the row panel header already used; both now share it.TableResultsnow lets an extra column with a base column's id replace it in place (withExtraColumns, insrc/components/search/tableColumns.js), and both tables pass that name column. The rows themselves are unchanged, so Map and the concept panel keep the concept's owndisplay_name, and that's what a mapping stores and the export writes.sortRowViewstakes the input language.filters.locale) still hides a matched name in a language it excludes, everywhere above. It now compares primary subtags, so a filter ofitadmits anit-ITname. That's how the filter already treats the names sent to the OCL AI Assistant (filterAndTrimNames).How I tested it
.github/workflows/pr.yml, run in order on Node 22.23.2:npm ci: passed../node_modules/eslint/bin/eslint.js src --ext .jsx,.js): no problems. I ran it with--no-eslintrc -c .eslintrc.json, which reads only this repo's config: the worktree sits inside another checkout, whose config ESLint would otherwise merge in.npm run test:all: 499 unit tests and 6 integration tests pass, on 19f6eba.src/components/map-projects/__tests__/conceptNames.test.js: 14 tests. One is the ticket's case, LOINC 89497-2 with its it-IT name matched in an English project.src/components/search/__tests__/tableColumns.test.js: 3 tests.src/components/map-projects/__tests__/views.test.js: 4 more. One sorts by the shown name. The others coverfindTargetRowViewandwithCandidateNames, using a target shaped as a reopened project restores it.Concept,MappingDecisionResultand aSearchResultstable in headless Chromium, through a throwaway harness that isn't committed, withConceptfrommainbeside this branch's. The ticket names no widths: the page was 1600px wide, with the candidate lists at 520px, the grid cell at 360px and the header at 640px.main: reproduces the ticket: "Anamnesi Punto nel tempo (episodio) Test prenatale → Mother's Prenatal tests".withCandidateNamesfills it from the row's candidates.es: the it-IT and en matched names are hidden.validateDOMNestingwarning comes fromMappingDecisionResult's existing markup.Acceptance criteria
localizedDisplayNametests (input language, primary subtag, preferred, then fully specified, then first name, fallback todisplay_name) and the browser check above.getMatchedNametests (the ticket's case, the same name, a display-name match, the filter, an unknown locale) and the browser check.Concept.Review
Codex ran three passes:
Then 0bbdb05 merged main (one import-line conflict in
MapProject.jsx, resolved as the union). The simplification review ran on 0bbdb05 and proposed six cuts, all applied in 19f6eba. Codex's check of d03da8c..19f6eba was clean.Docs
ConfigurationForm's English default.Concept.jsx,MappingDecisionResult.jsxandviewBuilders.jsare updated. No README or docs page describes how candidates are named.Translations
New key
map_project.matched_namein all four catalogs: en "Matched name", es "Nombre coincidente", pt-BR "Nome correspondente", zh "匹配名称". A native speaker may want to check the es, pt-BR and zh wording, and the helper text changes.Not done, and why
Noticed, not fixed
display_name: the mapped concept's name (__map_concept_name__) and the candidate columns. The ticket doesn't cover exports.display_nametoo. Neither is a candidate title.closes OpenConceptLab/ocl_issues#2909
🤖 Generated with Claude Code
https://claude.ai/code/session_01NSAveBj6W6LKJLYTWJ9rwi
Written by an Octoad session worker (worker-v0.11, recipe lean-loop, flow public>public, run openconceptlab-ocl-issues-2909-20261014-125457).