diff --git a/docs/infrastructure/2026-10-20-route-census-repair.md b/docs/infrastructure/2026-10-20-route-census-repair.md new file mode 100644 index 0000000..3d78040 --- /dev/null +++ b/docs/infrastructure/2026-10-20-route-census-repair.md @@ -0,0 +1,11 @@ +# Route-census compatibility repair — 20 October 2026 + +The diagnostic browser run on `32bc65c` reproduced two ordinary failures in the normal-user and administrator route census. Both expected the legacy Gmail-review redirect to discard unrelated query parameters and its fragment. P7-06 deliberately preserves that context: `App.tsx` redirects `/correspondence/review?route=all#review` to `/correspondence?route=all&view=review#review`, and the approved P7-06 native alias case separately verifies preserved query, fragment and review mode. The application is following its accepted contract; the older census expectation was stale. + +After correcting the exact destination, the same cases exposed a second stale expectation for the former server-generated English error. P7-06 presents the existing translated `gmailReviewNotConnected` message. The repaired census still requires the real HTTP 409, exact `gmail_not_connected` response code and a visible alert containing that approved translation. No permission, error or redirect assertion is removed or made optional. + +The census also includes P7-07's approved `/documents` route, bringing its explicit unique-route assertion from 32 to 33 while retaining every original route. Anonymous entry must remain protected; authenticated entry and refresh must return owned document metadata and the exact Documents heading. This strengthens coverage without adding an application route or feature. + +The initial local whole-suite diagnostic was interrupted after 65 completed cases (63 passed, two failed) once the route failures were reproduced; it is not a full-suite acceptance result. Its logs and both failure traces remain retained. The first corrected census recorded one pass and two failures on the obsolete English alert. The final focused run passed all three ordinary identity cases, zero retries/skips, at 06:57:22–06:59:30 UTC: 198 route entry/refresh combinations plus existing history, ownership and navigation checks. Browser TypeScript passed. The overall browser inventory remains 331 tests in 53 files; the three existing identity cases now cover 33 routes each. + +The source change is confined to the census fixture. Existing product code, CV pagination, persistence, authorization and P5-13 withholding are unchanged. Lint, whitespace and tracked-secret checks pass. The instrumented Linux run remains diagnostic evidence for the previous candidate until the updated main commit receives its own full CI result. Production/operator and manual accessibility qualifications remain open; this repair does not accept the remaining Phase 7 migration. diff --git a/job-tracker-ui/e2e/functional-acceptance.spec.ts b/job-tracker-ui/e2e/functional-acceptance.spec.ts index 215c93a..cb80349 100644 --- a/job-tracker-ui/e2e/functional-acceptance.spec.ts +++ b/job-tracker-ui/e2e/functional-acceptance.spec.ts @@ -43,12 +43,13 @@ function routes(jobId: number, cvId: number): RouteCase[] { { pattern: '/companies', path: '/companies', heading: t.companies, endpoint: '/companies' }, { pattern: '/offers', path: '/offers', heading: t.offerComparisonTitle, endpoint: '/jobapplications' }, { pattern: '/correspondence', path: '/correspondence', heading: t.correspondenceInboxTitle, endpoint: '/correspondence/page' }, - { pattern: '/correspondence/review', path: '/correspondence/review?route=all#review', final: '/correspondence?view=review', heading: t.correspondenceInboxTitle, endpoint: '/gmail/review-candidates', status: 409 }, + { pattern: '/correspondence/review', path: '/correspondence/review?route=all#review', final: '/correspondence?route=all&view=review#review', heading: t.correspondenceInboxTitle, endpoint: '/gmail/review-candidates', status: 409 }, { pattern: '/profile', path: '/profile', heading: t.profile, endpoint: '/auth/me' }, { pattern: '/applications/:id', path: `/applications/${jobId}?section=documents&followMode=waiting-update#workspace`, final: `/jobs/${jobId}?section=documents&followMode=waiting-update`, heading: t.jobDetails, endpoint: `/jobapplications/${jobId}/workspace` }, { pattern: '/career', path: '/career?section=profile', heading: t.careerWorkspace, endpoint: '/career/profile' }, { pattern: '/career/builder', path: '/career/builder', heading: t.cvBuilder, endpoint: '/cv/variants' }, { pattern: '/career/builder/:id', path: `/career/builder/${cvId}`, heading: t.cvBuilder, endpoint: `/cv/variants/${cvId}` }, + { pattern: '/documents', path: '/documents', heading: t.documentsTitle, endpoint: '/documents' }, { pattern: '/admin/audit', path: '/admin/audit', heading: t.auditLog, endpoint: '/admin/audit' }, { pattern: '/admin/users', path: '/admin/users', heading: t.users, endpoint: '/users' }, { pattern: '/admin/system', path: '/admin/system', heading: t.systemStatus, endpoint: '/admin/system' }, @@ -86,7 +87,7 @@ test.beforeEach(async ({ context }) => { }); for (const identity of ['anonymous', 'normal', 'admin'] as const) { - test(`[P5-15-01 E02 E07 A01] all 32 routes retain native entry, refresh and ${identity} permissions`, async ({ page, playwright }, info) => { + test(`[P5-15-01 E02 E07 A01] all 33 routes retain native entry, refresh and ${identity} permissions`, async ({ page, playwright }, info) => { test.setTimeout(300_000); const admin = await playwright.request.newContext(); const evidence: { pattern: string; mode: string; destination: string; status?: number }[] = []; @@ -105,7 +106,7 @@ for (const identity of ['anonymous', 'normal', 'admin'] as const) { expect(cv.status()).toBe(200); cvId = (await cv.json()).id; } const census = routes(jobId, cvId); - expect(new Set(census.map(row => row.pattern)).size).toBe(32); + expect(new Set(census.map(row => row.pattern)).size).toBe(33); for (const row of census) { await test.step(row.pattern, async () => { for (const mode of ['entry', 'refresh']) { @@ -123,7 +124,7 @@ for (const identity of ['anonymous', 'normal', 'admin'] as const) { expect.soft(native.status()).toBe(denied ? 403 : row.status ?? 200); if (row.pattern === '/correspondence/review') { expect(await native.json()).toMatchObject({ code: 'gmail_not_connected' }); - await expect(page.getByRole('alert').filter({ hasText: 'Connect Gmail before using the Gmail review queue.' })).toBeVisible(); + await expect(page.getByRole('alert').filter({ hasText: t.gmailReviewNotConnected })).toBeVisible(); } if (denied) { await expect(page.getByRole('alert').first()).toBeVisible();