\u001b[39m\n\u001b[36m\u001b[39m\n at Object.getElementError (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\@testing-library\\dom\\dist\\config.js:37:19)\n at C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\@testing-library\\dom\\dist\\query-helpers.js:76:38\n at C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\@testing-library\\dom\\dist\\query-helpers.js:52:17\n at C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\@testing-library\\dom\\dist\\query-helpers.js:95:19\n at getByText (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\pro-feature-notice.test.tsx:23:17)\n at Object. (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-each\\build\\index.js:80:140)\n at Promise.finally.completed (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:1561:28)\n at new Promise ()\n at callAsyncCircusFn (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:1501:10)\n at _callCircusTest (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:1011:40)\n at _runTest (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:951:3)\n at C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:853:7\n at _runTestsForDescribeBlock (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:866:11)\n at run (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:765:3)\n at runAndTransformResultsToJestFormat (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:1993:21)\n at jestAdapter (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\runner.js:111:19)\n at runTestInternal (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-runner\\build\\index.js:276:16)\n at runTest (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-runner\\build\\index.js:344:7)"],"fullName":"keeps paid users on the manual path without an upgrade prompt (en)","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793729321804,"status":"failed","title":"keeps paid users on the manual path without an upgrade prompt (en)"},{"ancestorTitles":[],"duration":12,"failing":false,"failureDetails":[{"name":"TestingLibraryElementError"}],"failureMessages":["TestingLibraryElementError: Unable to find an element with the text: KI-hjelp er utilgjengelig. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n\u001b[36m\u001b[39m\n at Object.getElementError (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\@testing-library\\dom\\dist\\config.js:37:19)\n at C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\@testing-library\\dom\\dist\\query-helpers.js:76:38\n at C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\@testing-library\\dom\\dist\\query-helpers.js:52:17\n at C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\@testing-library\\dom\\dist\\query-helpers.js:95:19\n at getByText (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\pro-feature-notice.test.tsx:23:17)\n at Object. (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-each\\build\\index.js:80:140)\n at Promise.finally.completed (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:1561:28)\n at new Promise ()\n at callAsyncCircusFn (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:1501:10)\n at _callCircusTest (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:1011:40)\n at _runTest (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:951:3)\n at C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:853:7\n at _runTestsForDescribeBlock (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:866:11)\n at run (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:765:3)\n at runAndTransformResultsToJestFormat (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:1993:21)\n at jestAdapter (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\runner.js:111:19)\n at runTestInternal (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-runner\\build\\index.js:276:16)\n at runTest (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-runner\\build\\index.js:344:7)"],"fullName":"keeps paid users on the manual path without an upgrade prompt (nb)","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793729321896,"status":"failed","title":"keeps paid users on the manual path without an upgrade prompt (nb)"},{"ancestorTitles":[],"duration":109,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"shows a respectful upgrade action and remembers dismissal for the session","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793729321909,"status":"passed","title":"shows a respectful upgrade action and remembers dismissal for the session"}],"endTime":1793729322037,"message":" ● keeps paid users on the manual path without an upgrade prompt (en)\n\n TestingLibraryElementError: Unable to find an element with the text: AI assistance is unavailable. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.\n\n Ignored nodes: comments, script, style\n \u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n\n 21 | ,\n 22 | );\n > 23 | expect(screen.getByText(title)).toBeInTheDocument();\n | ^\n 24 | expect(screen.getByRole(\"link\", { name: action })).toHaveAttribute(\"href\", \"/settings\");\n 25 | expect(screen.queryByText(\"Upgrade body\")).not.toBeInTheDocument();\n 26 | fireEvent.click(screen.getByRole(\"button\", { name: new RegExp(title) }));\n\n at Object.getElementError (node_modules/@testing-library/dom/dist/config.js:37:19)\n at node_modules/@testing-library/dom/dist/query-helpers.js:76:38\n at node_modules/@testing-library/dom/dist/query-helpers.js:52:17\n at node_modules/@testing-library/dom/dist/query-helpers.js:95:19\n at getByText (src/pro-feature-notice.test.tsx:23:17)\n\n ● keeps paid users on the manual path without an upgrade prompt (nb)\n\n TestingLibraryElementError: Unable to find an element with the text: KI-hjelp er utilgjengelig. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.\n\n Ignored nodes: comments, script, style\n \u001b[36m\u001b[39m\n \u001b[36m
\u001b[39m\n \u001b[36m\u001b[39m\n\n 21 | ,\n 22 | );\n > 23 | expect(screen.getByText(title)).toBeInTheDocument();\n | ^\n 24 | expect(screen.getByRole(\"link\", { name: action })).toHaveAttribute(\"href\", \"/settings\");\n 25 | expect(screen.queryByText(\"Upgrade body\")).not.toBeInTheDocument();\n 26 | fireEvent.click(screen.getByRole(\"button\", { name: new RegExp(title) }));\n\n at Object.getElementError (node_modules/@testing-library/dom/dist/config.js:37:19)\n at node_modules/@testing-library/dom/dist/query-helpers.js:76:38\n at node_modules/@testing-library/dom/dist/query-helpers.js:52:17\n at node_modules/@testing-library/dom/dist/query-helpers.js:95:19\n at getByText (src/pro-feature-notice.test.tsx:23:17)\n","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\pro-feature-notice.test.tsx","startTime":1793729309478,"status":"failed","summary":""}],"wasInterrupted":false}
diff --git a/docs/ui-modernisation/evidence/ai-settings/frontend/a3-ai-notice-tests.json b/docs/ui-modernisation/evidence/ai-settings/frontend/a3-ai-notice-tests.json
new file mode 100644
index 00000000..9a7755b8
--- /dev/null
+++ b/docs/ui-modernisation/evidence/ai-settings/frontend/a3-ai-notice-tests.json
@@ -0,0 +1 @@
+{"numFailedTestSuites":0,"numFailedTests":0,"numPassedTestSuites":2,"numPassedTests":6,"numPendingTestSuites":0,"numPendingTests":0,"numRuntimeErrorTestSuites":0,"numTodoTests":0,"numTotalTestSuites":2,"numTotalTests":6,"openHandles":[],"snapshot":{"added":0,"didUpdate":false,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0},"startTime":1793729337837,"success":true,"testResults":[{"assertionResults":[{"ancestorTitles":[],"duration":139,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"keeps paid users on the manual path without an upgrade prompt (en)","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793729343257,"status":"passed","title":"keeps paid users on the manual path without an upgrade prompt (en)"},{"ancestorTitles":[],"duration":26,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"keeps paid users on the manual path without an upgrade prompt (nb)","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793729343397,"status":"passed","title":"keeps paid users on the manual path without an upgrade prompt (nb)"},{"ancestorTitles":[],"duration":22,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"shows a respectful upgrade action and remembers dismissal for the session","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793729343423,"status":"passed","title":"shows a respectful upgrade action and remembers dismissal for the session"}],"endTime":1793729343447,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\pro-feature-notice.test.tsx","startTime":1793729339253,"status":"passed","summary":""},{"assertionResults":[{"ancestorTitles":[],"duration":10,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"English and Bokmål expose the same UI message keys","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1793729343841,"status":"passed","title":"English and Bokmål expose the same UI message keys"},{"ancestorTitles":[],"duration":5,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"Bokmål preserves every English substitution variable","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1793729343851,"status":"passed","title":"Bokmål preserves every English substitution variable"},{"ancestorTitles":[],"duration":6,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"supported UI catalogues contain no blank messages","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1793729343856,"status":"passed","title":"supported UI catalogues contain no blank messages"}],"endTime":1793729343862,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\translation-catalogue.test.ts","startTime":1793729343468,"status":"passed","summary":""}],"wasInterrupted":false}
diff --git a/docs/ui-modernisation/evidence/ai-settings/frontend/a3-automation-red.json b/docs/ui-modernisation/evidence/ai-settings/frontend/a3-automation-red.json
new file mode 100644
index 00000000..4901167f
--- /dev/null
+++ b/docs/ui-modernisation/evidence/ai-settings/frontend/a3-automation-red.json
@@ -0,0 +1 @@
+{"numFailedTestSuites":1,"numFailedTests":3,"numPassedTestSuites":0,"numPassedTests":0,"numPendingTestSuites":0,"numPendingTests":21,"numRuntimeErrorTestSuites":0,"numTodoTests":0,"numTotalTestSuites":1,"numTotalTests":24,"openHandles":[],"snapshot":{"added":0,"didUpdate":false,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0},"startTime":1793716189064,"success":false,"testResults":[{"assertionResults":[{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"each selected Settings section has a named panel linked to its tab and remains selectable on phones","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716194520,"status":"pending","title":"each selected Settings section has a named panel linked to its tab and remains selectable on phones"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"settings view has no accent picker and uses one follow-up section, one notification section","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716194520,"status":"pending","title":"settings view has no accent picker and uses one follow-up section, one notification section"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"AI privacy settings are server-backed and external processing is local-only by default","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716194520,"status":"pending","title":"AI privacy settings are server-backed and external processing is local-only by default"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03] failed notification save preserves the draft for an explicit retry","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716194520,"status":"pending","title":"[PRES-S03] failed notification save preserves the draft for an explicit retry"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03 PRES-W11] saving one follow-up rule preserves the other scenarios","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716194520,"status":"pending","title":"[PRES-S03 PRES-W11] saving one follow-up rule preserves the other scenarios"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03 PRES-J05] table and theme preferences keep their independent callbacks","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716194520,"status":"pending","title":"[PRES-S03 PRES-J05] table and theme preferences keep their independent callbacks"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03 W11] the Settings session retains independent pending Rules fields across tab unmounts","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716194520,"status":"pending","title":"[PRES-S03 W11] the Settings session retains independent pending Rules fields across tab unmounts"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03] failed Rules save remains actionable after tab return and explicit retry clears dirty state","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716194520,"status":"pending","title":"[PRES-S03] failed Rules save remains actionable after tab return and explicit retry clears dirty state"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03] invalid and empty Rules input and associated validation survive internal navigation","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716194520,"status":"pending","title":"[PRES-S03] invalid and empty Rules input and associated validation survive internal navigation"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03] explicit discard resets only the Rules draft to its saved baseline without writing","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716194520,"status":"pending","title":"[PRES-S03] explicit discard resets only the Rules draft to its saved baseline without writing"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03] later edits stay dirty when an earlier Rules save completes after a tab switch","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716194520,"status":"pending","title":"[PRES-S03] later edits stay dirty when an earlier Rules save completes after a tab switch"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03 E09] leaving the Settings session disposes its draft and pending responses","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716194520,"status":"pending","title":"[PRES-S03 E09] leaving the Settings session disposes its draft and pending responses"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03] switching locale while Rules is hidden does not reload or overwrite its pending draft","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716194520,"status":"pending","title":"[PRES-S03] switching locale while Rules is hidden does not reload or overwrite its pending draft"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03] Rules draft updates do not refetch an unrelated notification preference","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716194520,"status":"pending","title":"[PRES-S03] Rules draft updates do not refetch an unrelated notification preference"},{"ancestorTitles":[],"duration":360,"failing":false,"failureDetails":[{"name":"TestingLibraryElementError"}],"failureMessages":["TestingLibraryElementError: Found multiple elements with the role \"tab\"\n\nHere are the matching elements:\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n\n(If this is intentional, then use the `*AllBy*` variant of the query (like `queryAllByText`, `getAllByText`, or `findAllByText`)).\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n \u001b[36m
(C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-each\\build\\index.js:80:140)\n at Promise.finally.completed (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:1561:28)\n at new Promise ()\n at callAsyncCircusFn (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:1501:10)\n at _callCircusTest (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:1011:40)\n at _runTest (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:951:3)\n at C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:853:7\n at _runTestsForDescribeBlock (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:866:11)\n at run (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:765:3)\n at runAndTransformResultsToJestFormat (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:1993:21)\n at jestAdapter (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\runner.js:111:19)\n at runTestInternal (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-runner\\build\\index.js:276:16)\n at runTest (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-runner\\build\\index.js:344:7)"],"fullName":"en Automation reuses the saved notification preference and explicit save","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716194520,"status":"failed","title":"en Automation reuses the saved notification preference and explicit save"},{"ancestorTitles":[],"duration":142,"failing":false,"failureDetails":[{"name":"TestingLibraryElementError"}],"failureMessages":["TestingLibraryElementError: Found multiple elements with the role \"tab\"\n\nHere are the matching elements:\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n\n(If this is intentional, then use the `*AllBy*` variant of the query (like `queryAllByText`, `getAllByText`, or `findAllByText`)).\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n \u001b[36m
(C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-each\\build\\index.js:80:140)\n at Promise.finally.completed (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:1561:28)\n at new Promise ()\n at callAsyncCircusFn (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:1501:10)\n at _callCircusTest (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:1011:40)\n at _runTest (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:951:3)\n at C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:853:7\n at _runTestsForDescribeBlock (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:866:11)\n at run (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:765:3)\n at runAndTransformResultsToJestFormat (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:1993:21)\n at jestAdapter (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\runner.js:111:19)\n at runTestInternal (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-runner\\build\\index.js:276:16)\n at runTest (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-runner\\build\\index.js:344:7)"],"fullName":"nb Automation reuses the saved notification preference and explicit save","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716194903,"status":"failed","title":"nb Automation reuses the saved notification preference and explicit save"},{"ancestorTitles":[],"duration":121,"failing":false,"failureDetails":[{"name":"TestingLibraryElementError"}],"failureMessages":["TestingLibraryElementError: Found multiple elements with the role \"tab\"\n\nHere are the matching elements:\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n\n(If this is intentional, then use the `*AllBy*` variant of the query (like `queryAllByText`, `getAllByText`, or `findAllByText`)).\n\nIgnored nodes: comments, script, style\n\u001b[36m\u001b[39m\n \u001b[36m
)\n at callAsyncCircusFn (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:1501:10)\n at _callCircusTest (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:1011:40)\n at _runTest (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:951:3)\n at C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:853:7\n at _runTestsForDescribeBlock (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:866:11)\n at run (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:765:3)\n at runAndTransformResultsToJestFormat (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\jestAdapterInit.js:1993:21)\n at jestAdapter (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-circus\\build\\runner.js:111:19)\n at runTestInternal (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-runner\\build\\index.js:276:16)\n at runTest (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\node_modules\\jest-runner\\build\\index.js:344:7)"],"fullName":"Automation AI entry moves focus into existing settings without an AI request","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716195045,"status":"failed","title":"Automation AI entry moves focus into existing settings without an AI request"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 preferences] initial notification failure offers a single GET Retry and recovers its vanished focused action","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716195167,"status":"pending","title":"[P7-09 preferences] initial notification failure offers a single GET Retry and recovers its vanished focused action"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 preferences] notification drafts survive internal sections and locale changes without a GET or save","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716195167,"status":"pending","title":"[P7-09 preferences] notification drafts survive internal sections and locale changes without a GET or save"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 preferences] one held notification save acknowledges its snapshot and preserves later edits across sections","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716195167,"status":"pending","title":"[P7-09 preferences] one held notification save acknowledges its snapshot and preserves later edits across sections"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 preferences] failed notification save keeps its current-locale draft and retries only the original Save","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716195167,"status":"pending","title":"[P7-09 preferences] failed notification save keeps its current-locale draft and retries only the original Save"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 preferences] discarding a notification draft leaves the independent Rules draft untouched","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716195167,"status":"pending","title":"[P7-09 preferences] discarding a notification draft leaves the independent Rules draft untouched"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 preferences] a disposed notification save cannot publish into surviving providers or a new Settings session","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716195167,"status":"pending","title":"[P7-09 preferences] a disposed notification save cannot publish into surviving providers or a new Settings session"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 preferences] Rules load failure follows locale without reloading and Retry retains the six-field contract","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716195167,"status":"pending","title":"[P7-09 preferences] Rules load failure follows locale without reloading and Retry retains the six-field contract"}],"endTime":1793716195191,"message":" ● en Automation reuses the saved notification preference and explicit save\n\n TestingLibraryElementError: Found multiple elements with the role \"tab\"\n\n Here are the matching elements:\n\n Ignored nodes: comments, script, style\n \u001b[36m\u001b[39m\n\n Ignored nodes: comments, script, style\n \u001b[36m\u001b[39m\n\n Ignored nodes: comments, script, style\n \u001b[36m\u001b[39m\n\n Ignored nodes: comments, script, style\n \u001b[36m\u001b[39m\n\n (If this is intentional, then use the `*AllBy*` variant of the query (like `queryAllByText`, `getAllByText`, or `findAllByText`)).\n\n Ignored nodes: comments, script, style\n \u001b[36m\u001b[39m\n \u001b[36m
{\n 323 | localStorage.setItem('uiLanguage', language); renderView();\n > 324 | fireEvent.click(screen.getByRole('tab', { name: preferenceCopy('settingsTabAutomation') }));\n | ^\n 325 | const panel = within(screen.getByRole('tabpanel', { name: preferenceCopy('settingsTabAutomation') }));\n 326 | const toggle = await panel.findByRole('switch', { name: preferenceCopy('settingsNotificationsFollowUpReminders') });\n 327 | expect(toggle).toBeChecked(); fireEvent.click(toggle); expect(mockedApi.put).not.toHaveBeenCalled();\n\n at Object.getElementError (node_modules/@testing-library/dom/dist/config.js:37:19)\n at getElementError (node_modules/@testing-library/dom/dist/query-helpers.js:20:35)\n at getMultipleElementsFoundError (node_modules/@testing-library/dom/dist/query-helpers.js:23:10)\n at node_modules/@testing-library/dom/dist/query-helpers.js:55:13\n at node_modules/@testing-library/dom/dist/query-helpers.js:95:19\n at getByRole (src/settings-view.test.tsx:324:26)\n\n ● Automation AI entry moves focus into existing settings without an AI request\n\n TestingLibraryElementError: Found multiple elements with the role \"tab\"\n\n Here are the matching elements:\n\n Ignored nodes: comments, script, style\n \u001b[36m\u001b[39m\n\n Ignored nodes: comments, script, style\n \u001b[36m\u001b[39m\n\n Ignored nodes: comments, script, style\n \u001b[36m\u001b[39m\n\n Ignored nodes: comments, script, style\n \u001b[36m\u001b[39m\n\n (If this is intentional, then use the `*AllBy*` variant of the query (like `queryAllByText`, `getAllByText`, or `findAllByText`)).\n\n Ignored nodes: comments, script, style\n \u001b[36m\u001b[39m\n \u001b[36m
{\n > 335 | renderView(); fireEvent.click(screen.getByRole('tab', { name: preferenceCopy('settingsTabAutomation') }));\n | ^\n 336 | fireEvent.click(await screen.findByRole('button', { name: preferenceCopy('settingsAutomationAi') }));\n 337 | await waitFor(() => expect(document.getElementById('settings-ai-controls')).toHaveFocus());\n 338 | expect(mockedApi.post).not.toHaveBeenCalled();\n\n at Object.getElementError (node_modules/@testing-library/dom/dist/config.js:37:19)\n at getElementError (node_modules/@testing-library/dom/dist/query-helpers.js:20:35)\n at getMultipleElementsFoundError (node_modules/@testing-library/dom/dist/query-helpers.js:23:10)\n at node_modules/@testing-library/dom/dist/query-helpers.js:55:13\n at node_modules/@testing-library/dom/dist/query-helpers.js:95:19\n at Object.getByRole (src/settings-view.test.tsx:335:40)\n","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\settings-view.test.tsx","startTime":1793716190429,"status":"failed","summary":""}],"wasInterrupted":false}
diff --git a/docs/ui-modernisation/evidence/ai-settings/frontend/a3-automation-tests.json b/docs/ui-modernisation/evidence/ai-settings/frontend/a3-automation-tests.json
new file mode 100644
index 00000000..9e15ca5f
--- /dev/null
+++ b/docs/ui-modernisation/evidence/ai-settings/frontend/a3-automation-tests.json
@@ -0,0 +1 @@
+{"numFailedTestSuites":0,"numFailedTests":0,"numPassedTestSuites":5,"numPassedTests":45,"numPendingTestSuites":0,"numPendingTests":0,"numRuntimeErrorTestSuites":0,"numTodoTests":0,"numTotalTestSuites":5,"numTotalTests":45,"openHandles":[],"snapshot":{"added":0,"didUpdate":false,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0},"startTime":1793716330310,"success":true,"testResults":[{"assertionResults":[{"ancestorTitles":[],"duration":1046,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"each selected Settings section has a named panel linked to its tab and remains selectable on phones","invocations":1,"location":null,"numPassingAsserts":16,"retryReasons":[],"startAt":1793716336267,"status":"passed","title":"each selected Settings section has a named panel linked to its tab and remains selectable on phones"},{"ancestorTitles":[],"duration":629,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"settings view has no accent picker and uses one follow-up section, one notification section","invocations":1,"location":null,"numPassingAsserts":9,"retryReasons":[],"startAt":1793716337314,"status":"passed","title":"settings view has no accent picker and uses one follow-up section, one notification section"},{"ancestorTitles":[],"duration":291,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"AI privacy settings are server-backed and external processing is local-only by default","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793716337943,"status":"passed","title":"AI privacy settings are server-backed and external processing is local-only by default"},{"ancestorTitles":[],"duration":4508,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03] failed notification save preserves the draft for an explicit retry","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793716338235,"status":"passed","title":"[PRES-S03] failed notification save preserves the draft for an explicit retry"},{"ancestorTitles":[],"duration":644,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03 PRES-W11] saving one follow-up rule preserves the other scenarios","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1793716342744,"status":"passed","title":"[PRES-S03 PRES-W11] saving one follow-up rule preserves the other scenarios"},{"ancestorTitles":[],"duration":416,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03 PRES-J05] table and theme preferences keep their independent callbacks","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793716343388,"status":"passed","title":"[PRES-S03 PRES-J05] table and theme preferences keep their independent callbacks"},{"ancestorTitles":[],"duration":880,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03 W11] the Settings session retains independent pending Rules fields across tab unmounts","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793716343804,"status":"passed","title":"[PRES-S03 W11] the Settings session retains independent pending Rules fields across tab unmounts"},{"ancestorTitles":[],"duration":1259,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03] failed Rules save remains actionable after tab return and explicit retry clears dirty state","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793716344684,"status":"passed","title":"[PRES-S03] failed Rules save remains actionable after tab return and explicit retry clears dirty state"},{"ancestorTitles":[],"duration":1577,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03] invalid and empty Rules input and associated validation survive internal navigation","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793716345943,"status":"passed","title":"[PRES-S03] invalid and empty Rules input and associated validation survive internal navigation"},{"ancestorTitles":[],"duration":995,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03] explicit discard resets only the Rules draft to its saved baseline without writing","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793716347520,"status":"passed","title":"[PRES-S03] explicit discard resets only the Rules draft to its saved baseline without writing"},{"ancestorTitles":[],"duration":1408,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03] later edits stay dirty when an earlier Rules save completes after a tab switch","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793716348516,"status":"passed","title":"[PRES-S03] later edits stay dirty when an earlier Rules save completes after a tab switch"},{"ancestorTitles":[],"duration":950,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03 E09] leaving the Settings session disposes its draft and pending responses","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793716349924,"status":"passed","title":"[PRES-S03 E09] leaving the Settings session disposes its draft and pending responses"},{"ancestorTitles":[],"duration":909,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03] switching locale while Rules is hidden does not reload or overwrite its pending draft","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793716350874,"status":"passed","title":"[PRES-S03] switching locale while Rules is hidden does not reload or overwrite its pending draft"},{"ancestorTitles":[],"duration":673,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-S03] Rules draft updates do not refetch an unrelated notification preference","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793716351784,"status":"passed","title":"[PRES-S03] Rules draft updates do not refetch an unrelated notification preference"},{"ancestorTitles":[],"duration":792,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"en Automation reuses the saved notification preference and explicit save","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793716352457,"status":"passed","title":"en Automation reuses the saved notification preference and explicit save"},{"ancestorTitles":[],"duration":696,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"nb Automation reuses the saved notification preference and explicit save","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793716353250,"status":"passed","title":"nb Automation reuses the saved notification preference and explicit save"},{"ancestorTitles":[],"duration":481,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"Automation AI entry moves focus into existing settings without an AI request","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793716353946,"status":"passed","title":"Automation AI entry moves focus into existing settings without an AI request"},{"ancestorTitles":[],"duration":558,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 preferences] initial notification failure offers a single GET Retry and recovers its vanished focused action","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793716354427,"status":"passed","title":"[P7-09 preferences] initial notification failure offers a single GET Retry and recovers its vanished focused action"},{"ancestorTitles":[],"duration":1114,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 preferences] notification drafts survive internal sections and locale changes without a GET or save","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793716354985,"status":"passed","title":"[P7-09 preferences] notification drafts survive internal sections and locale changes without a GET or save"},{"ancestorTitles":[],"duration":1311,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 preferences] one held notification save acknowledges its snapshot and preserves later edits across sections","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793716356099,"status":"passed","title":"[P7-09 preferences] one held notification save acknowledges its snapshot and preserves later edits across sections"},{"ancestorTitles":[],"duration":1039,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 preferences] failed notification save keeps its current-locale draft and retries only the original Save","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793716357411,"status":"passed","title":"[P7-09 preferences] failed notification save keeps its current-locale draft and retries only the original Save"},{"ancestorTitles":[],"duration":1031,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 preferences] discarding a notification draft leaves the independent Rules draft untouched","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793716358450,"status":"passed","title":"[P7-09 preferences] discarding a notification draft leaves the independent Rules draft untouched"},{"ancestorTitles":[],"duration":1006,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 preferences] a disposed notification save cannot publish into surviving providers or a new Settings session","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793716359481,"status":"passed","title":"[P7-09 preferences] a disposed notification save cannot publish into surviving providers or a new Settings session"},{"ancestorTitles":[],"duration":701,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 preferences] Rules load failure follows locale without reloading and Retry retains the six-field contract","invocations":1,"location":null,"numPassingAsserts":11,"retryReasons":[],"startAt":1793716360487,"status":"passed","title":"[P7-09 preferences] Rules load failure follows locale without reloading and Retry retains the six-field contract"}],"endTime":1793716361190,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\settings-view.test.tsx","startTime":1793716331747,"status":"passed","summary":""},{"assertionResults":[{"ancestorTitles":[],"duration":1109,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 lifetime] an accepted import continues once across Notifications and Rules while a real Settings unmount stops queued writes","invocations":1,"location":null,"numPassingAsserts":17,"retryReasons":[],"startAt":1793716362928,"status":"passed","title":"[P7-09 lifetime] an accepted import continues once across Notifications and Rules while a real Settings unmount stops queued writes"},{"ancestorTitles":[],"duration":1051,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 lifetime] the General privacy owner keeps its draft and read across internal tabs while Backup remains lazy and disposable","invocations":1,"location":null,"numPassingAsserts":21,"retryReasons":[],"startAt":1793716364037,"status":"passed","title":"[P7-09 lifetime] the General privacy owner keeps its draft and read across internal tabs while Backup remains lazy and disposable"}],"endTime":1793716365088,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\settings-import-lifetime.test.tsx","startTime":1793716361213,"status":"passed","summary":""},{"assertionResults":[{"ancestorTitles":[],"duration":124,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"keeps its heading and named loading state, then retries only a failed usage GET","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793716366292,"status":"passed","title":"keeps its heading and named loading state, then retries only a failed usage GET"},{"ancestorTitles":[],"duration":94,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"billing failure remains visible beside usage and its Retry only reads billing","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793716366417,"status":"passed","title":"billing failure remains visible beside usage and its Retry only reads billing"},{"ancestorTitles":[],"duration":45,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"locale changes format numbers and byte decimals without reloading; zero storage limit stays finite","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793716366511,"status":"passed","title":"locale changes format numbers and byte decimals without reloading; zero storage limit stays finite"},{"ancestorTitles":[],"duration":58,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"one billing POST ignores a late failure after unmount","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793716366556,"status":"passed","title":"one billing POST ignores a late failure after unmount"},{"ancestorTitles":[],"duration":44,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"one billing POST ignores a late failure after session","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793716366614,"status":"passed","title":"one billing POST ignores a late failure after session"},{"ancestorTitles":[],"duration":46,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"late billing success after unmount never consumes the redirect URL","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1793716366658,"status":"passed","title":"late billing success after unmount never consumes the redirect URL"},{"ancestorTitles":[],"duration":45,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"late billing success after session never consumes the redirect URL","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1793716366705,"status":"passed","title":"late billing success after session never consumes the redirect URL"},{"ancestorTitles":[],"duration":116,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a failed billing POST offers the existing explicit action without automatic replay","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793716366750,"status":"passed","title":"a failed billing POST offers the existing explicit action without automatic replay"},{"ancestorTitles":[],"duration":60,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"shows monthly AI call and token limits","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793716366866,"status":"passed","title":"shows monthly AI call and token limits"},{"ancestorTitles":[],"duration":48,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"does not offer a false upgrade action when billing is not configured","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793716366927,"status":"passed","title":"does not offer a false upgrade action when billing is not configured"}],"endTime":1793716366977,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\ai-usage-card.test.tsx","startTime":1793716365100,"status":"passed","summary":""},{"assertionResults":[{"ancestorTitles":[],"duration":128,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 privacy] failed initial read has a read-only Retry and no perpetual loading","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793716368291,"status":"passed","title":"[P7-09 privacy] failed initial read has a read-only Retry and no perpetual loading"},{"ancestorTitles":[],"duration":130,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 privacy] submitted acknowledgement preserves a newer edit and serializes Save","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793716368419,"status":"passed","title":"[P7-09 privacy] submitted acknowledgement preserves a newer edit and serializes Save"},{"ancestorTitles":[],"duration":170,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 privacy] a failed save retains the draft and never replays through read recovery","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793716368549,"status":"passed","title":"[P7-09 privacy] a failed save retains the draft and never replays through read recovery"},{"ancestorTitles":[],"duration":74,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 privacy] unmount ignores an old save acknowledgement and success notification","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1793716368719,"status":"passed","title":"[P7-09 privacy] unmount ignores an old save acknowledgement and success notification"},{"ancestorTitles":[],"duration":122,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 privacy] pending save uses the current language without refetching the draft","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793716368793,"status":"passed","title":"[P7-09 privacy] pending save uses the current language without refetching the draft"},{"ancestorTitles":[],"duration":63,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P7-09 privacy] local-only capability permits revoking a historical external consent","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793716368915,"status":"passed","title":"[P7-09 privacy] local-only capability permits revoking a historical external consent"}],"endTime":1793716368978,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\ai-privacy-settings.test.tsx","startTime":1793716366988,"status":"passed","summary":""},{"assertionResults":[{"ancestorTitles":[],"duration":14,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"English and Bokmål expose the same UI message keys","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1793716369522,"status":"passed","title":"English and Bokmål expose the same UI message keys"},{"ancestorTitles":[],"duration":7,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"Bokmål preserves every English substitution variable","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1793716369536,"status":"passed","title":"Bokmål preserves every English substitution variable"},{"ancestorTitles":[],"duration":4,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"supported UI catalogues contain no blank messages","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1793716369543,"status":"passed","title":"supported UI catalogues contain no blank messages"}],"endTime":1793716369548,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\translation-catalogue.test.ts","startTime":1793716368991,"status":"passed","summary":""}],"wasInterrupted":false}
diff --git a/docs/ui-modernisation/evidence/ai-settings/frontend/a3-document-ai-language-red.json b/docs/ui-modernisation/evidence/ai-settings/frontend/a3-document-ai-language-red.json
new file mode 100644
index 00000000..b9e22c6a
--- /dev/null
+++ b/docs/ui-modernisation/evidence/ai-settings/frontend/a3-document-ai-language-red.json
@@ -0,0 +1 @@
+{"numFailedTestSuites":1,"numFailedTests":1,"numPassedTestSuites":0,"numPassedTests":0,"numPendingTestSuites":0,"numPendingTests":59,"numRuntimeErrorTestSuites":0,"numTodoTests":0,"numTotalTestSuites":1,"numTotalTests":60,"openHandles":[],"snapshot":{"added":0,"didUpdate":false,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0},"startTime":1793716607608,"success":false,"testResults":[{"assertionResults":[{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"Strict Mode groups a typing burst into one undo step and preserves redo through autosave","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613832,"status":"pending","title":"Strict Mode groups a typing burst into one undo step and preserves redo through autosave"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a pause between typing bursts leaves a separate undo step","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613832,"status":"pending","title":"a pause between typing bursts leaves a separate undo step"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"tablet width keeps both editing and preview available in the stacked document flow","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613832,"status":"pending","title":"tablet width keeps both editing and preview available in the stacked document flow"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"document shortcuts undo and redo renaming, preserve native input undo and discard branched redo","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613832,"status":"pending","title":"document shortcuts undo and redo renaming, preserve native input undo and discard branched redo"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a revision conflict disables document history without silently writing an undo","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613832,"status":"pending","title":"a revision conflict disables document history without silently writing an undo"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"switching documents hides the previous draft while loading and starts fresh history","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613832,"status":"pending","title":"switching documents hides the previous draft while loading and starts fresh history"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"explicit blank editing excludes master content and includes a profile section only after an Add action","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613832,"status":"pending","title":"explicit blank editing excludes master content and includes a profile section only after an Add action"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a blank CV can deliberately add an additional Career Profile section","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613832,"status":"pending","title":"a blank CV can deliberately add an additional Career Profile section"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a historical restore response from the previous document cannot change the newly opened CV","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613832,"status":"pending","title":"a historical restore response from the previous document cannot change the newly opened CV"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"content sections follow the editing-region heading without mutating CV settings","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613832,"status":"pending","title":"content sections follow the editing-region heading without mutating CV settings"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"settings history explains current Career Profile resolution without writing data","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613832,"status":"pending","title":"settings history explains current Career Profile resolution without writing data"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"PDF export locks immediately, rejects a newer unsaved draft, and keeps that draft recoverable","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613832,"status":"pending","title":"PDF export locks immediately, rejects a newer unsaved draft, and keeps that draft recoverable"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"edits during a held PDF response prevent an older document download","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613832,"status":"pending","title":"edits during a held PDF response prevent an older document download"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"moving a section to the first position announces its actual order and focuses its row","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613832,"status":"pending","title":"moving a section to the first position announces its actual order and focuses its row"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"section navigation opens and focuses omitted/custom sections without changing their data","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"section navigation opens and focuses omitted/custom sections without changing their data"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"keyboard move controls retain focus and persist the same section order shown by navigation","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"keyboard move controls retain focus and persist the same section order shown by navigation"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"secondary duplicate action saves the current draft before creating the copy","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"secondary duplicate action saves the current draft before creating the copy"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"pane navigation focuses the existing preview without changing the pending draft","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"pane navigation focuses the existing preview without changing the pending draft"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"deep link loads the exact variant named in the route","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"deep link loads the exact variant named in the route"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a variant that does not exist shows a safe error instead of an empty editor","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"a variant that does not exist shows a safe error instead of an empty editor"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"another user's variant is refused by the server and never rendered","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"another user's variant is refused by the server and never rendered"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a denied CV read has localized permission context without a write or retry","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"a denied CV read has localized permission context without a write or retry"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a temporary CV opening failure retries reads and restores focus to the editor without saving","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"a temporary CV opening failure retries reads and restores focus to the editor without saving"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"Bokmål history localizes known sources while preserving unknown metadata and versions","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"Bokmål history localizes known sources while preserving unknown metadata and versions"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"invalid names are explained and are not silently autosaved","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"invalid names are explained and are not silently autosaved"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"failed autosave is visible and can be retried with the latest data","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"failed autosave is visible and can be retried with the latest data"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"session undo and redo restore content edits before autosave","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"session undo and redo restore content edits before autosave"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"queued saves use acknowledged server versions without replacing newer local editing state","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"queued saves use acknowledged server versions without replacing newer local editing state"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"revision conflict retains the draft and blocks later autosaves until explicit reload","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"revision conflict retains the draft and blocks later autosaves until explicit reload"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"edits made while a confirmed reload is in flight remain recoverable","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"edits made while a confirmed reload is in flight remain recoverable"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"restoring history uses the current version and never replaces edits made during the request","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"restoring history uses the current version and never replaces edits made during the request"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"Content is the primary studio panel and all advanced tabs retain their controls","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"Content is the primary studio panel and all advanced tabs retain their controls"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"choosing a template restores its own structure instead of inheriting the previous template layout","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"choosing a template restores its own structure instead of inheriting the previous template layout"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"editor sections, design controls and document settings render in Bokmål","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"editor sections, design controls and document settings render in Bokmål"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"internal navigation warns and can be cancelled before discarding a pending edit","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"internal navigation warns and can be cancelled before discarding a pending edit"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"custom entries can be added, edited, reordered and deleted with confirmation","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"custom entries can be added, edited, reordered and deleted with confirmation"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P5-10 PRES-C01 C07] source-backed entry edits identify their scope and save only this CV","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"[P5-10 PRES-C01 C07] source-backed entry edits identify their scope and save only this CV"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P5-10 PRES-C07 C10] resetting one entry field preserves omission, sibling override and order and supports undo","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"[P5-10 PRES-C07 C10] resetting one entry field preserves omission, sibling override and order and supports undo"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P5-10 PRES-C07 C10 E08] omitted entries remain editable and retain their draft after a failed save","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"[P5-10 PRES-C07 C10 E08] omitted entries remain editable and retain their draft after a failed save"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P5-10 PRES-C01 C07] section content reset inherits source without changing omission or presentation","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"[P5-10 PRES-C01 C07] section content reset inherits source without changing omission or presentation"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"profile-backed sections expand, reorder, hide and persist variant-only overrides","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"profile-backed sections expand, reorder, hide and persist variant-only overrides"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"focusing a preview retry control keeps its pointer target stationary","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"focusing a preview retry control keeps its pointer target stationary"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"preview failure is visible and retryable without leaving the editor","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"preview failure is visible and retryable without leaving the editor"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P5-11 PRES-C06 E08] automatic fit attaches when the asynchronously loaded preview pane appears","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"[P5-11 PRES-C06 E08] automatic fit attaches when the asynchronously loaded preview pane appears"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P5-11 PRES-C06 C12] page count is announced only after the renderer has settled","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"[P5-11 PRES-C06 C12] page count is announced only after the renderer has settled"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P5-11 PRES-C06 E09] overlapping preview successes and failures cannot replace the newest document","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"[P5-11 PRES-C06 E09] overlapping preview successes and failures cannot replace the newest document"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P5-11 PRES-C06 C10 E08] returning from preview focuses the existing editor without changing the pending draft","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716613833,"status":"pending","title":"[P5-11 PRES-C06 C10 E08] returning from preview focuses the existing editor without changing the pending draft"},{"ancestorTitles":[],"duration":1510,"failing":false,"failureDetails":[{"matcherResult":{"pass":true,"message":"expect(element).not.toBeInTheDocument()\n\nexpected document not to contain element, found
Review 1 proposed change(s)
instead"}}],"failureMessages":["Error: expect(element).not.toBeInTheDocument()\n\nexpected document not to contain element, found
Review 1 proposed change(s)
instead\n at Object.toBeInTheDocument (C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\cv-builder-deep-link.test.tsx:1115:65)"],"fullName":"changing whole-document output language discards the previous-language review without applying it","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793716613833,"status":"failed","title":"changing whole-document output language discards the previous-language review without applying it"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-C11 PRES-C07] whole-document AI Apply changes only an explicitly accepted CV override","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716615372,"status":"pending","title":"[PRES-C11 PRES-C07] whole-document AI Apply changes only an explicitly accepted CV override"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-C11 PRES-C07] whole-document AI Reject changes only an explicitly accepted CV override","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716615372,"status":"pending","title":"[PRES-C11 PRES-C07] whole-document AI Reject changes only an explicitly accepted CV override"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-C11] unsafe whole-document response (invalid JSON) cannot be applied","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716615372,"status":"pending","title":"[PRES-C11] unsafe whole-document response (invalid JSON) cannot be applied"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-C11] unsafe whole-document response (missing original ID) cannot be applied","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716615372,"status":"pending","title":"[PRES-C11] unsafe whole-document response (missing original ID) cannot be applied"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-C11] unsafe whole-document response (duplicate original ID) cannot be applied","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716615372,"status":"pending","title":"[PRES-C11] unsafe whole-document response (duplicate original ID) cannot be applied"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"bound document generation saves the pending draft, reviews server Original, and accepted AI remains undoable","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716615372,"status":"pending","title":"bound document generation saves the pending draft, reviews server Original, and accepted AI remains undoable"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a newer draft is flushed before stale proposal acceptance fails without rebasing or changing content","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716615372,"status":"pending","title":"a newer draft is flushed before stale proposal acceptance fails without rebasing or changing content"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"late document proposals cannot return after tab unmount or a source undo","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716615372,"status":"pending","title":"late document proposals cannot return after tab unmount or a source undo"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"bound section:skills assistant sends explicit scope and edited wording to server acceptance","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716615372,"status":"pending","title":"bound section:skills assistant sends explicit scope and edited wording to server acceptance"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"bound custom:sample assistant sends explicit scope and edited wording to server acceptance","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716615372,"status":"pending","title":"bound custom:sample assistant sends explicit scope and edited wording to server acceptance"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"bound entry:experience:job-1 assistant sends explicit scope and edited wording to server acceptance","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716615372,"status":"pending","title":"bound entry:experience:job-1 assistant sends explicit scope and edited wording to server acceptance"},{"ancestorTitles":[],"duration":null,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"user edits made during server acceptance are retained instead of overwritten by its late saved response","invocations":1,"location":null,"numPassingAsserts":0,"retryReasons":[],"startAt":1793716615372,"status":"pending","title":"user edits made during server acceptance are retained instead of overwritten by its late saved response"}],"endTime":1793716615391,"message":" ● changing whole-document output language discards the previous-language review without applying it\n\n expect(element).not.toBeInTheDocument()\n\n expected document not to contain element, found
Review 1 proposed change(s)
instead\n\n 1113 | fireEvent.mouseDown(screen.getByLabelText('Output language'));\n 1114 | fireEvent.click(screen.getByRole('option', { name: 'Norsk bokmål' }));\n > 1115 | expect(screen.queryByText('Review 1 proposed change(s)')).not.toBeInTheDocument();\n | ^\n 1116 | expect(mockedApi.post.mock.calls.filter(([url]) => url === '/cv/variants/3/ai/apply')).toHaveLength(0);\n 1117 | expect(mockedApi.post.mock.calls.filter(([url]) => url === '/cv/ai/assist')).toHaveLength(1);\n 1118 | });\n\n at Object.toBeInTheDocument (src/cv-builder-deep-link.test.tsx:1115:65)\n","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\cv-builder-deep-link.test.tsx","startTime":1793716609057,"status":"failed","summary":""}],"wasInterrupted":false}
diff --git a/docs/ui-modernisation/evidence/ai-settings/frontend/a3-document-ai-language-tests.json b/docs/ui-modernisation/evidence/ai-settings/frontend/a3-document-ai-language-tests.json
new file mode 100644
index 00000000..6e1984eb
--- /dev/null
+++ b/docs/ui-modernisation/evidence/ai-settings/frontend/a3-document-ai-language-tests.json
@@ -0,0 +1 @@
+{"numFailedTestSuites":0,"numFailedTests":0,"numPassedTestSuites":1,"numPassedTests":60,"numPendingTestSuites":0,"numPendingTests":0,"numRuntimeErrorTestSuites":0,"numTodoTests":0,"numTotalTestSuites":1,"numTotalTests":60,"openHandles":[],"snapshot":{"added":0,"didUpdate":false,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0},"startTime":1793716629407,"success":true,"testResults":[{"assertionResults":[{"ancestorTitles":[],"duration":2914,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"Strict Mode groups a typing burst into one undo step and preserves redo through autosave","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793716635384,"status":"passed","title":"Strict Mode groups a typing burst into one undo step and preserves redo through autosave"},{"ancestorTitles":[],"duration":1814,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a pause between typing bursts leaves a separate undo step","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793716638299,"status":"passed","title":"a pause between typing bursts leaves a separate undo step"},{"ancestorTitles":[],"duration":608,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"tablet width keeps both editing and preview available in the stacked document flow","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793716640113,"status":"passed","title":"tablet width keeps both editing and preview available in the stacked document flow"},{"ancestorTitles":[],"duration":2012,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"document shortcuts undo and redo renaming, preserve native input undo and discard branched redo","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793716640721,"status":"passed","title":"document shortcuts undo and redo renaming, preserve native input undo and discard branched redo"},{"ancestorTitles":[],"duration":1233,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a revision conflict disables document history without silently writing an undo","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793716642734,"status":"passed","title":"a revision conflict disables document history without silently writing an undo"},{"ancestorTitles":[],"duration":1580,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"switching documents hides the previous draft while loading and starts fresh history","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793716643967,"status":"passed","title":"switching documents hides the previous draft while loading and starts fresh history"},{"ancestorTitles":[],"duration":958,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"explicit blank editing excludes master content and includes a profile section only after an Add action","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793716645547,"status":"passed","title":"explicit blank editing excludes master content and includes a profile section only after an Add action"},{"ancestorTitles":[],"duration":699,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a blank CV can deliberately add an additional Career Profile section","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793716646505,"status":"passed","title":"a blank CV can deliberately add an additional Career Profile section"},{"ancestorTitles":[],"duration":2169,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a historical restore response from the previous document cannot change the newly opened CV","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793716647204,"status":"passed","title":"a historical restore response from the previous document cannot change the newly opened CV"},{"ancestorTitles":[],"duration":575,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"content sections follow the editing-region heading without mutating CV settings","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793716649373,"status":"passed","title":"content sections follow the editing-region heading without mutating CV settings"},{"ancestorTitles":[],"duration":445,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"settings history explains current Career Profile resolution without writing data","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793716649948,"status":"passed","title":"settings history explains current Career Profile resolution without writing data"},{"ancestorTitles":[],"duration":1599,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"PDF export locks immediately, rejects a newer unsaved draft, and keeps that draft recoverable","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793716650394,"status":"passed","title":"PDF export locks immediately, rejects a newer unsaved draft, and keeps that draft recoverable"},{"ancestorTitles":[],"duration":1425,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"edits during a held PDF response prevent an older document download","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793716651993,"status":"passed","title":"edits during a held PDF response prevent an older document download"},{"ancestorTitles":[],"duration":927,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"moving a section to the first position announces its actual order and focuses its row","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793716653418,"status":"passed","title":"moving a section to the first position announces its actual order and focuses its row"},{"ancestorTitles":[],"duration":5637,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"section navigation opens and focuses omitted/custom sections without changing their data","invocations":1,"location":null,"numPassingAsserts":9,"retryReasons":[],"startAt":1793716654345,"status":"passed","title":"section navigation opens and focuses omitted/custom sections without changing their data"},{"ancestorTitles":[],"duration":1892,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"keyboard move controls retain focus and persist the same section order shown by navigation","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793716659982,"status":"passed","title":"keyboard move controls retain focus and persist the same section order shown by navigation"},{"ancestorTitles":[],"duration":1521,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"secondary duplicate action saves the current draft before creating the copy","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793716661874,"status":"passed","title":"secondary duplicate action saves the current draft before creating the copy"},{"ancestorTitles":[],"duration":1084,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"pane navigation focuses the existing preview without changing the pending draft","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793716663395,"status":"passed","title":"pane navigation focuses the existing preview without changing the pending draft"},{"ancestorTitles":[],"duration":534,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"deep link loads the exact variant named in the route","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793716664479,"status":"passed","title":"deep link loads the exact variant named in the route"},{"ancestorTitles":[],"duration":30,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a variant that does not exist shows a safe error instead of an empty editor","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793716665013,"status":"passed","title":"a variant that does not exist shows a safe error instead of an empty editor"},{"ancestorTitles":[],"duration":42,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"another user's variant is refused by the server and never rendered","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793716665043,"status":"passed","title":"another user's variant is refused by the server and never rendered"},{"ancestorTitles":[],"duration":53,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a denied CV read has localized permission context without a write or retry","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793716665086,"status":"passed","title":"a denied CV read has localized permission context without a write or retry"},{"ancestorTitles":[],"duration":350,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a temporary CV opening failure retries reads and restores focus to the editor without saving","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793716665139,"status":"passed","title":"a temporary CV opening failure retries reads and restores focus to the editor without saving"},{"ancestorTitles":[],"duration":1161,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"Bokmål history localizes known sources while preserving unknown metadata and versions","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793716665490,"status":"passed","title":"Bokmål history localizes known sources while preserving unknown metadata and versions"},{"ancestorTitles":[],"duration":1039,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"invalid names are explained and are not silently autosaved","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793716666651,"status":"passed","title":"invalid names are explained and are not silently autosaved"},{"ancestorTitles":[],"duration":2083,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"failed autosave is visible and can be retried with the latest data","invocations":1,"location":null,"numPassingAsserts":4,"retryReasons":[],"startAt":1793716667690,"status":"passed","title":"failed autosave is visible and can be retried with the latest data"},{"ancestorTitles":[],"duration":2381,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"session undo and redo restore content edits before autosave","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793716669773,"status":"passed","title":"session undo and redo restore content edits before autosave"},{"ancestorTitles":[],"duration":2624,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"queued saves use acknowledged server versions without replacing newer local editing state","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793716672154,"status":"passed","title":"queued saves use acknowledged server versions without replacing newer local editing state"},{"ancestorTitles":[],"duration":3800,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"revision conflict retains the draft and blocks later autosaves until explicit reload","invocations":1,"location":null,"numPassingAsserts":10,"retryReasons":[],"startAt":1793716674778,"status":"passed","title":"revision conflict retains the draft and blocks later autosaves until explicit reload"},{"ancestorTitles":[],"duration":2605,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"edits made while a confirmed reload is in flight remain recoverable","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793716678578,"status":"passed","title":"edits made while a confirmed reload is in flight remain recoverable"},{"ancestorTitles":[],"duration":2666,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"restoring history uses the current version and never replaces edits made during the request","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793716681183,"status":"passed","title":"restoring history uses the current version and never replaces edits made during the request"},{"ancestorTitles":[],"duration":1649,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"Content is the primary studio panel and all advanced tabs retain their controls","invocations":1,"location":null,"numPassingAsserts":14,"retryReasons":[],"startAt":1793716683849,"status":"passed","title":"Content is the primary studio panel and all advanced tabs retain their controls"},{"ancestorTitles":[],"duration":2667,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"choosing a template restores its own structure instead of inheriting the previous template layout","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793716685498,"status":"passed","title":"choosing a template restores its own structure instead of inheriting the previous template layout"},{"ancestorTitles":[],"duration":2009,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"editor sections, design controls and document settings render in Bokmål","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793716688165,"status":"passed","title":"editor sections, design controls and document settings render in Bokmål"},{"ancestorTitles":[],"duration":1934,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"internal navigation warns and can be cancelled before discarding a pending edit","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793716690174,"status":"passed","title":"internal navigation warns and can be cancelled before discarding a pending edit"},{"ancestorTitles":[],"duration":10565,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"custom entries can be added, edited, reordered and deleted with confirmation","invocations":1,"location":null,"numPassingAsserts":16,"retryReasons":[],"startAt":1793716692108,"status":"passed","title":"custom entries can be added, edited, reordered and deleted with confirmation"},{"ancestorTitles":[],"duration":3918,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P5-10 PRES-C01 C07] source-backed entry edits identify their scope and save only this CV","invocations":1,"location":null,"numPassingAsserts":8,"retryReasons":[],"startAt":1793716702674,"status":"passed","title":"[P5-10 PRES-C01 C07] source-backed entry edits identify their scope and save only this CV"},{"ancestorTitles":[],"duration":5776,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P5-10 PRES-C07 C10] resetting one entry field preserves omission, sibling override and order and supports undo","invocations":1,"location":null,"numPassingAsserts":12,"retryReasons":[],"startAt":1793716706592,"status":"passed","title":"[P5-10 PRES-C07 C10] resetting one entry field preserves omission, sibling override and order and supports undo"},{"ancestorTitles":[],"duration":3299,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P5-10 PRES-C07 C10 E08] omitted entries remain editable and retain their draft after a failed save","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793716712368,"status":"passed","title":"[P5-10 PRES-C07 C10 E08] omitted entries remain editable and retain their draft after a failed save"},{"ancestorTitles":[],"duration":2636,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P5-10 PRES-C01 C07] section content reset inherits source without changing omission or presentation","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793716715667,"status":"passed","title":"[P5-10 PRES-C01 C07] section content reset inherits source without changing omission or presentation"},{"ancestorTitles":[],"duration":6641,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"profile-backed sections expand, reorder, hide and persist variant-only overrides","invocations":1,"location":null,"numPassingAsserts":3,"retryReasons":[],"startAt":1793716718303,"status":"passed","title":"profile-backed sections expand, reorder, hide and persist variant-only overrides"},{"ancestorTitles":[],"duration":2041,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"focusing a preview retry control keeps its pointer target stationary","invocations":1,"location":null,"numPassingAsserts":2,"retryReasons":[],"startAt":1793716724944,"status":"passed","title":"focusing a preview retry control keeps its pointer target stationary"},{"ancestorTitles":[],"duration":1853,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"preview failure is visible and retryable without leaving the editor","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793716726985,"status":"passed","title":"preview failure is visible and retryable without leaving the editor"},{"ancestorTitles":[],"duration":522,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P5-11 PRES-C06 E08] automatic fit attaches when the asynchronously loaded preview pane appears","invocations":1,"location":null,"numPassingAsserts":1,"retryReasons":[],"startAt":1793716728839,"status":"passed","title":"[P5-11 PRES-C06 E08] automatic fit attaches when the asynchronously loaded preview pane appears"},{"ancestorTitles":[],"duration":1029,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P5-11 PRES-C06 C12] page count is announced only after the renderer has settled","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793716729361,"status":"passed","title":"[P5-11 PRES-C06 C12] page count is announced only after the renderer has settled"},{"ancestorTitles":[],"duration":3269,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P5-11 PRES-C06 E09] overlapping preview successes and failures cannot replace the newest document","invocations":1,"location":null,"numPassingAsserts":11,"retryReasons":[],"startAt":1793716730390,"status":"passed","title":"[P5-11 PRES-C06 E09] overlapping preview successes and failures cannot replace the newest document"},{"ancestorTitles":[],"duration":1655,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[P5-11 PRES-C06 C10 E08] returning from preview focuses the existing editor without changing the pending draft","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793716733659,"status":"passed","title":"[P5-11 PRES-C06 C10 E08] returning from preview focuses the existing editor without changing the pending draft"},{"ancestorTitles":[],"duration":1415,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"changing whole-document output language discards the previous-language review without applying it","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793716735315,"status":"passed","title":"changing whole-document output language discards the previous-language review without applying it"},{"ancestorTitles":[],"duration":2382,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-C11 PRES-C07] whole-document AI Apply changes only an explicitly accepted CV override","invocations":1,"location":null,"numPassingAsserts":12,"retryReasons":[],"startAt":1793716736731,"status":"passed","title":"[PRES-C11 PRES-C07] whole-document AI Apply changes only an explicitly accepted CV override"},{"ancestorTitles":[],"duration":1515,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-C11 PRES-C07] whole-document AI Reject changes only an explicitly accepted CV override","invocations":1,"location":null,"numPassingAsserts":11,"retryReasons":[],"startAt":1793716739114,"status":"passed","title":"[PRES-C11 PRES-C07] whole-document AI Reject changes only an explicitly accepted CV override"},{"ancestorTitles":[],"duration":2378,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-C11] unsafe whole-document response (invalid JSON) cannot be applied","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793716740629,"status":"passed","title":"[PRES-C11] unsafe whole-document response (invalid JSON) cannot be applied"},{"ancestorTitles":[],"duration":2065,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-C11] unsafe whole-document response (missing original ID) cannot be applied","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793716743007,"status":"passed","title":"[PRES-C11] unsafe whole-document response (missing original ID) cannot be applied"},{"ancestorTitles":[],"duration":2232,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"[PRES-C11] unsafe whole-document response (duplicate original ID) cannot be applied","invocations":1,"location":null,"numPassingAsserts":7,"retryReasons":[],"startAt":1793716745072,"status":"passed","title":"[PRES-C11] unsafe whole-document response (duplicate original ID) cannot be applied"},{"ancestorTitles":[],"duration":7124,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"bound document generation saves the pending draft, reviews server Original, and accepted AI remains undoable","invocations":1,"location":null,"numPassingAsserts":11,"retryReasons":[],"startAt":1793716747304,"status":"passed","title":"bound document generation saves the pending draft, reviews server Original, and accepted AI remains undoable"},{"ancestorTitles":[],"duration":3520,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"a newer draft is flushed before stale proposal acceptance fails without rebasing or changing content","invocations":1,"location":null,"numPassingAsserts":13,"retryReasons":[],"startAt":1793716754428,"status":"passed","title":"a newer draft is flushed before stale proposal acceptance fails without rebasing or changing content"},{"ancestorTitles":[],"duration":4677,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"late document proposals cannot return after tab unmount or a source undo","invocations":1,"location":null,"numPassingAsserts":6,"retryReasons":[],"startAt":1793716757949,"status":"passed","title":"late document proposals cannot return after tab unmount or a source undo"},{"ancestorTitles":[],"duration":4161,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"bound section:skills assistant sends explicit scope and edited wording to server acceptance","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793716762626,"status":"passed","title":"bound section:skills assistant sends explicit scope and edited wording to server acceptance"},{"ancestorTitles":[],"duration":4882,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"bound custom:sample assistant sends explicit scope and edited wording to server acceptance","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793716766787,"status":"passed","title":"bound custom:sample assistant sends explicit scope and edited wording to server acceptance"},{"ancestorTitles":[],"duration":4123,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"bound entry:experience:job-1 assistant sends explicit scope and edited wording to server acceptance","invocations":1,"location":null,"numPassingAsserts":5,"retryReasons":[],"startAt":1793716771669,"status":"passed","title":"bound entry:experience:job-1 assistant sends explicit scope and edited wording to server acceptance"},{"ancestorTitles":[],"duration":3221,"failing":false,"failureDetails":[],"failureMessages":[],"fullName":"user edits made during server acceptance are retained instead of overwritten by its late saved response","invocations":1,"location":null,"numPassingAsserts":9,"retryReasons":[],"startAt":1793716775792,"status":"passed","title":"user edits made during server acceptance are retained instead of overwritten by its late saved response"}],"endTime":1793716779016,"message":"","name":"C:\\Users\\Connor\\.codex\\worktrees\\direct-roadmap-integration\\Job tracker\\job-tracker-ui\\src\\cv-builder-deep-link.test.tsx","startTime":1793716630880,"status":"passed","summary":""}],"wasInterrupted":false}
diff --git a/job-tracker-ui/e2e/phase7-settings-preferences.spec.ts b/job-tracker-ui/e2e/phase7-settings-preferences.spec.ts
index a2e8d78e..c721e6c7 100644
--- a/job-tracker-ui/e2e/phase7-settings-preferences.spec.ts
+++ b/job-tracker-ui/e2e/phase7-settings-preferences.spec.ts
@@ -64,7 +64,7 @@ async function choose(page: Page, label: string, language: Language = 'en') {
else { await keyboard(tab); await expect(tab).toHaveAttribute('aria-selected', 'true'); }
}
function notificationGroup(page: Page, language: Language = 'en') { return page.getByRole('main').getByRole('group', { name: copy('settingsNotificationsTitle', language), exact: true }); }
-function notificationCheckbox(page: Page, language: Language = 'en') { return page.getByRole('main').getByRole('checkbox', { name: copy('settingsNotificationsFollowUpReminders', language), exact: true }); }
+function notificationCheckbox(page: Page, language: Language = 'en') { return page.getByRole('main').getByRole('switch', { name: copy('settingsNotificationsFollowUpReminders', language), exact: true }); }
async function toggle(control: Locator) { await expect(control).toBeEnabled(); await control.focus(); await control.press('Space'); }
async function persistedNotifications(session: Session, api: APIRequestContext) { return json<{ emailFollowUpRemindersEnabled: boolean }>(await call(session.requests, api, 'GET', '/notification-settings')); }
function response(page: Page, method: string, path: string) { return page.waitForResponse(value => value.request().method() === method && new URL(value.url()).pathname === `/api${path}`); }
diff --git a/job-tracker-ui/e2e/settings-page-layout.spec.ts b/job-tracker-ui/e2e/settings-page-layout.spec.ts
index 51e54d64..abfbb9f2 100644
--- a/job-tracker-ui/e2e/settings-page-layout.spec.ts
+++ b/job-tracker-ui/e2e/settings-page-layout.spec.ts
@@ -135,7 +135,7 @@ for (const mode of ["light", "dark"] as const) for (const language of ["en", "nb
await expect(notifications.getByRole("progressbar", { name: t.loading, exact: true })).toBeVisible();
await capture(page, info, `notifications-loading-${suffix}`);
release();
- const pref = main.getByRole("checkbox", { name: t.settingsNotificationsFollowUpReminders, exact: true });
+ const pref = main.getByRole("switch", { name: t.settingsNotificationsFollowUpReminders, exact: true });
await expect(pref).toBeChecked();
await page.unroute(`${apiUrl}/notification-settings`);
await pref.uncheck();
diff --git a/job-tracker-ui/scripts/verify-a3-ai-settings.mjs b/job-tracker-ui/scripts/verify-a3-ai-settings.mjs
new file mode 100644
index 00000000..9942f93d
--- /dev/null
+++ b/job-tracker-ui/scripts/verify-a3-ai-settings.mjs
@@ -0,0 +1,103 @@
+// Exported React UI with synthetic API responses; no backend, model or provider access.
+import { chromium, expect } from '@playwright/test';
+import AxeBuilder from '@axe-core/playwright';
+import ts from 'typescript';
+import assert from 'node:assert/strict';
+import fs from 'node:fs';
+import path from 'node:path';
+const compiled = ts.transpileModule(fs.readFileSync(new URL('../src/i18n/translations.ts', import.meta.url), 'utf8'), { compilerOptions: { module: ts.ModuleKind.ESNext } }).outputText;
+const { translations } = await import(`data:text/javascript;base64,${Buffer.from(compiled).toString('base64')}`);
+const out = path.resolve(import.meta.dirname, '../../docs/ui-modernisation/evidence/ai-settings/browser');
+fs.mkdirSync(out, { recursive: true });
+const report = { screens: [], journeys: [], errors: [], unexpectedWrites: [], writes: [], proposals: [], unavailableReads: [], blockedExternal: [] };
+const browser = await chromium.launch();
+try {
+ const configurations = process.env.A3_PILOT ? [[320, 'en', 'light']] : [320, 390, 768, 1440].flatMap(width => ['en', 'nb'].flatMap(language => ['light', 'dark'].map(mode => [width, language, mode])));
+ for (const [width, language, mode] of configurations) {
+ console.log(`Checking ${width}/${language}/${mode}`);
+ const t = key => { assert.ok(translations[language][key], key); return translations[language][key]; };
+ const context = await browser.newContext({ viewport: { width, height: 1000 }, reducedMotion: 'reduce' });
+ const page = await context.newPage(); page.on('pageerror', error => report.errors.push(error.message));
+ await page.addInitScript(({ language, mode }) => { localStorage.setItem('uiLanguage', language); localStorage.setItem('jobtracker.themeMode', mode); }, { language, mode });
+ let notifications = { emailFollowUpRemindersEnabled: true };
+ let privacy = { enabled: true, externalProcessingAllowed: false, externalProcessingAvailable: false, effectiveExternalProcessing: false, provider: 'local' };
+ let variant = { id: 3, name: 'Teacher CV', version: 4, settings: { language: 'en', themeId: 'modern', showPhoto: false, showPageNumbers: false, showIcons: true, sections: [], overrides: {}, customSections: [] }, isPublic: false, publicSlug: 'synthetic', jobApplicationId: null, updatedAtUtc: '2026-11-03T10:00:00Z' };
+ let proposal;
+ const writes = [], proposals = [];
+ await page.route('**/*', route => {
+ const req = route.request(), url = new URL(req.url()), p = url.pathname;
+ if (!['localhost', '127.0.0.1'].includes(url.hostname)) { report.blockedExternal.push(url.origin); return route.abort(); }
+ if (!p.startsWith('/api/')) return route.continue();
+ const answer = data => route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify(data) });
+ const note = action => { writes.push(action); report.writes.push({ width, language, mode, action }); };
+ if (req.method() === 'PUT' && p === '/api/notification-settings') { assert.deepEqual(req.postDataJSON(), { emailFollowUpRemindersEnabled: false }); notifications = req.postDataJSON(); note('notificationPreference'); return answer(notifications); }
+ if (req.method() === 'PUT' && p === '/api/ai/settings') { const body = req.postDataJSON(); assert.equal(body.externalProcessingAllowed, false); privacy = { ...privacy, ...body }; note('aiPreference'); return answer(privacy); }
+ if (req.method() === 'PUT' && p === '/api/cv/variants/3') {
+ const body = req.postDataJSON(); assert.equal(body.expectedVersion, variant.version); assert.equal(body.settings.headline, 'Manual teacher headline');
+ variant = { ...variant, settings: body.settings, name: body.name, version: variant.version + 1 }; note('manualCvSave'); return answer(variant);
+ }
+ if (req.method() === 'POST' && p === '/api/cv/preview') return answer({ themeId: 'modern', html: 'Controlled preview