fix(i18n): retain historical checklist text for unknown system keys
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
# Historical checklist text fallback
|
||||
|
||||
2 November 2026. CONFIRMED functional compatibility defect: an unknown/retired persisted system key was converted into nonexistent catalogue keys, hiding the supplied historical task text. Diagnostic RED: three unknown-token cases and EN/NB UI cases failed; nine unselected cases were not run in that diagnostic execution.
|
||||
|
||||
Minimal shared fix in checklistSystemTranslationToken: retain kebab-to-camel conversion, but return a token only when the English fallback catalogue owns both title and description keys. Otherwise return null, activating the existing original-text fallback in ApplicationChecklist and workspace next-step presentation. Learning/custom boundaries remain intact. This does not delete tasks, rewrite stored keys/text, change backend generation, enable AI, or change revision/ownership/API contracts. English is the existing reliable translation fallback; current 14 system title/description pairs also exist in Bokmål.
|
||||
|
||||
PASSED: 37 tests in checklist-translation-token, application-checklist and application-workspace-overlay, zero failures/pending, exit 0. New tests prove unknown/inherited-name keys do not manufacture labels, historical text survives EN/NB, and known/learning/null behavior remains. Full typecheck and scoped three-file ESLint passed. Native local report: job-tracker-ui/.artifacts/checklist-unknown-system-keys.json. Full build/browser and integrated security acceptance are not claimed here. Rollback: revert the helper/import and new tests; no database rollback.
|
||||
|
||||
Four dynamic checklist/next-step sites are now bounded by actual catalogue membership, not merely a presumed backend key list. The machine register binds applicationWorkspace.ts as their owner dependency.
|
||||
|
||||
Two further unchanged sites reviewed: ApplicationWorkspacePage.tsx:505 receives only WORKSPACE_SECTIONS.map(s.key), a fixed seven-key label domain; CareerEvidenceBank.tsx:83 first checks a six-value literal category array before constructing the prefixed key, leaving unknown category text untouched. All 41 distinct system/workspace/evidence keys are defined in both catalogues. This is key-domain evidence, not native language quality or complete every-string context.
|
||||
|
||||
Reconciliation: 108 original + 62 supplemental sites = 170 resolved / 18 pending of 188. This supersedes the earlier open checklist gap without reclassifying broader i18n/security acceptance.
|
||||
@@ -3,9 +3,9 @@
|
||||
"inventorySha256": "79928688a7b1a4961286aaaf48c028eb13ae8286d72d132561d6b5fdd192e323",
|
||||
"historicalCalls": 188,
|
||||
"previouslyResolved": 108,
|
||||
"additionalResolved": 56,
|
||||
"resolved": 164,
|
||||
"pending": 24,
|
||||
"additionalResolved": 62,
|
||||
"resolved": 170,
|
||||
"pending": 18,
|
||||
"evidence": [
|
||||
"CHECKLIST_DYNAMIC_REVIEW.md",
|
||||
"FINITE_OWNER_MAP_REVIEW.md",
|
||||
@@ -15,7 +15,8 @@
|
||||
"REMINDER_INTERVIEW_KEY_REVIEW.md",
|
||||
"ASSET_KEY_REVIEW.md",
|
||||
"LANDING_DOCUMENT_KEY_REVIEW.md",
|
||||
"CV_BOARD_KEY_REVIEW.md"
|
||||
"CV_BOARD_KEY_REVIEW.md",
|
||||
"CHECKLIST_UNKNOWN_SYSTEM_KEYS.md"
|
||||
],
|
||||
"entries": [
|
||||
{
|
||||
@@ -575,6 +576,90 @@
|
||||
"reviewedSourceSha256": "0c772feb6bde8324676b0884aaf343283465c5cd3b3318128363063df4ea22ab",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "static control-flow review; no PDF metric or model rerun"
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/ApplicationChecklist.tsx",
|
||||
"historicalLine": 214,
|
||||
"expression": "t(`checklistItem_${token}`)",
|
||||
"historicalSourceSha256": "88fdcd24914cf074aedee72a94b0c420c96a16dfe7194142daf22ec0ad8370ff",
|
||||
"reviewedSourceSha256": "88fdcd24914cf074aedee72a94b0c420c96a16dfe7194142daf22ec0ad8370ff",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "CHECKLIST_UNKNOWN_SYSTEM_KEYS.md; 37 focused cases; other finite labels statically reviewed",
|
||||
"ownerDependencies": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/applicationWorkspace.ts",
|
||||
"sha256": "286c5f6b5c360dddaebcf1eaaaa1157b8dfb750e8a204b7863d59e8fd33ecf2f"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/components/ApplicationChecklist.tsx",
|
||||
"historicalLine": 215,
|
||||
"expression": "t(`checklistItem_${token}Description`)",
|
||||
"historicalSourceSha256": "88fdcd24914cf074aedee72a94b0c420c96a16dfe7194142daf22ec0ad8370ff",
|
||||
"reviewedSourceSha256": "88fdcd24914cf074aedee72a94b0c420c96a16dfe7194142daf22ec0ad8370ff",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "CHECKLIST_UNKNOWN_SYSTEM_KEYS.md; 37 focused cases; other finite labels statically reviewed",
|
||||
"ownerDependencies": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/applicationWorkspace.ts",
|
||||
"sha256": "286c5f6b5c360dddaebcf1eaaaa1157b8dfb750e8a204b7863d59e8fd33ecf2f"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/ApplicationWorkspacePage.tsx",
|
||||
"historicalLine": 392,
|
||||
"expression": "t(`checklistItem_${nextStepToken}` as any)",
|
||||
"historicalSourceSha256": "107b4db54ef15d88254e01bc404136b987755588c1d171b800c2a47f93488b50",
|
||||
"reviewedSourceSha256": "107b4db54ef15d88254e01bc404136b987755588c1d171b800c2a47f93488b50",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "CHECKLIST_UNKNOWN_SYSTEM_KEYS.md; 37 focused cases; other finite labels statically reviewed",
|
||||
"ownerDependencies": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/applicationWorkspace.ts",
|
||||
"sha256": "286c5f6b5c360dddaebcf1eaaaa1157b8dfb750e8a204b7863d59e8fd33ecf2f"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/ApplicationWorkspacePage.tsx",
|
||||
"historicalLine": 395,
|
||||
"expression": "t(`checklistItem_${nextStepToken}Description` as any)",
|
||||
"historicalSourceSha256": "107b4db54ef15d88254e01bc404136b987755588c1d171b800c2a47f93488b50",
|
||||
"reviewedSourceSha256": "107b4db54ef15d88254e01bc404136b987755588c1d171b800c2a47f93488b50",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "CHECKLIST_UNKNOWN_SYSTEM_KEYS.md; 37 focused cases; other finite labels statically reviewed",
|
||||
"ownerDependencies": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/applicationWorkspace.ts",
|
||||
"sha256": "286c5f6b5c360dddaebcf1eaaaa1157b8dfb750e8a204b7863d59e8fd33ecf2f"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/ApplicationWorkspacePage.tsx",
|
||||
"historicalLine": 505,
|
||||
"expression": "t(keys[section])",
|
||||
"historicalSourceSha256": "107b4db54ef15d88254e01bc404136b987755588c1d171b800c2a47f93488b50",
|
||||
"reviewedSourceSha256": "107b4db54ef15d88254e01bc404136b987755588c1d171b800c2a47f93488b50",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "CHECKLIST_UNKNOWN_SYSTEM_KEYS.md; 37 focused cases; other finite labels statically reviewed",
|
||||
"ownerDependencies": [
|
||||
{
|
||||
"file": "job-tracker-ui/src/applicationWorkspace.ts",
|
||||
"sha256": "286c5f6b5c360dddaebcf1eaaaa1157b8dfb750e8a204b7863d59e8fd33ecf2f"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"file": "job-tracker-ui/src/views/career/CareerEvidenceBank.tsx",
|
||||
"historicalLine": 83,
|
||||
"expression": "t(`careerEvidenceCategory_${value}` as \"careerEvidenceCategory_achievement\")",
|
||||
"historicalSourceSha256": "fc85ccfc1addd48af3a29bc263b089858765b2ac3ec7db3b66dedc55ddfab625",
|
||||
"reviewedSourceSha256": "fc85ccfc1addd48af3a29bc263b089858765b2ac3ec7db3b66dedc55ddfab625",
|
||||
"status": "finite-key-domain-reviewed",
|
||||
"runtimeCoverage": "CHECKLIST_UNKNOWN_SYSTEM_KEYS.md; 37 focused cases; other finite labels statically reviewed"
|
||||
}
|
||||
],
|
||||
"limitations": [
|
||||
|
||||
@@ -68,6 +68,14 @@ test("localizes system checklist content without changing user-authored tasks",
|
||||
expect(screen.getByText("Ask Sara for a referral")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test.each(['en', 'nb'])('preserves unknown historical system task text in %s', async language => {
|
||||
window.localStorage.setItem('uiLanguage', language);
|
||||
mockedApi.get.mockResolvedValue({ data: checklist([item({ systemKey: 'retired-system-task', title: 'Reviewed historical task', description: 'Original task details' })]) } as any);
|
||||
renderChecklist();
|
||||
expect(await screen.findByText('Reviewed historical task')).toBeInTheDocument();
|
||||
expect(screen.getByText('Original task details')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
test("renders the checklist grouped by category with progress", async () => {
|
||||
renderChecklist();
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { api } from "./api";
|
||||
import { translations } from "./i18n/translations";
|
||||
|
||||
// Mirrors WorkspaceOverviewDto. Read-only aggregate — the workspace owns no data.
|
||||
export type WorkspaceCv = {
|
||||
@@ -93,7 +94,9 @@ export const WORKSPACE_SECTIONS: { key: WorkspaceSectionKey; label: string }[] =
|
||||
|
||||
export function checklistSystemTranslationToken(systemKey?: string | null): string | null {
|
||||
if (!systemKey || systemKey.startsWith("learning:")) return null;
|
||||
return systemKey.replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase());
|
||||
const token = systemKey.replace(/-([a-z])/g, (_, letter: string) => letter.toUpperCase());
|
||||
return Object.hasOwn(translations.en, `checklistItem_${token}`)
|
||||
&& Object.hasOwn(translations.en, `checklistItem_${token}Description`) ? token : null;
|
||||
}
|
||||
|
||||
const LEGACY_WORKSPACE_SECTIONS: Record<string, WorkspaceSectionKey> = {
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { checklistSystemTranslationToken } from './applicationWorkspace';
|
||||
|
||||
test.each(['retired-system-task', 'constructor', '__proto__'])('does not manufacture translation keys for %s', key => {
|
||||
expect(checklistSystemTranslationToken(key)).toBeNull();
|
||||
});
|
||||
|
||||
test('retains the known system translation and learning/custom boundaries', () => {
|
||||
expect(checklistSystemTranslationToken('prepare-cv')).toBe('prepareCv');
|
||||
expect(checklistSystemTranslationToken('learning:prepare-cv')).toBeNull();
|
||||
expect(checklistSystemTranslationToken(null)).toBeNull();
|
||||
});
|
||||
Reference in New Issue
Block a user