# Task 9 report — canonical route dispatch ## Status - DONE: matcher, registry collision guard, empty handler aggregate, preview root routing, and catch-all dispatch are implemented. - Base verified before edits: `2970dff56378cf5259fd125794bf0d89bec25b25` on `codex/housekeeping-complete`. - Commit message: `feat(housekeeping): dispatch canonical domain routes`. - `.remember/` remained untouched and untracked. - No pull, push, PR/MR update, deployment, database operation, Task 10 work, or worktree was performed. ## TDD evidence ### RED — tests written before production Exact command: ```text pnpm exec vitest run --coverage.enabled=false src/features/housekeeping/foundation/routing/match-route.test.ts src/features/housekeeping/foundation/registry.test.ts src/features/housekeeping/route-handlers.test.ts src/features/housekeeping/foundation/preview-route-contract.test.ts ``` Observed exit 1: ```text Test Files 4 failed (4) Tests 1 failed | 14 passed (15) Cannot find module './match-route' Cannot find module './route-handlers' Cannot find package '@/app/ase-next/[domain]/[[...segments]]/page' registry > rejects duplicate dynamic route shapes regardless of parameter name AssertionError: expected [Function] to throw an error ``` This proved the three missing production boundaries and the existing registry's acceptance of equivalent `:id` / `:username` route shapes. ### First targeted GREEN The same exact command after the minimum implementation exited 0: ```text Test Files 4 passed (4) Tests 94 passed (94) ``` An intermediate run had 92/94 passing because two import-boundary fixtures still used the old route file's relative depth. The fixture imports were moved one directory higher for the new catch-all location; the forbidden-module assertions were unchanged. ### Full-suite contract correction The first full housekeeping run correctly exposed one obsolete Task 1 expectation: ```text Test Files 1 failed | 37 passed (38) Tests 1 failed | 348 passed (349) Expected NEXT_REDIRECT:/ase-next/operations Received NEXT_NOT_FOUND ``` `server-capability-context.test.ts` was updated to the Task 9 ruling: with the real registered route set still empty, `/ase-next` calls `notFound()` and must not redirect to an empty Operations placeholder. Its request-scoped context isolation assertions remain intact. ## Implemented behavior - `matchHousekeepingRoute` compares decoded path segments without constructing a regular expression from route text. - Static routes win over same-depth dynamic routes; dynamic and nested parameters are returned in a frozen readonly record. - Unknown, cross-domain, malformed, repeated-separator, trailing-separator, query/fragment, invalid-percent, encoded-separator, dot-segment, and backslash paths fail closed. - Registry construction rejects duplicate dynamic shapes even when parameter names differ. - `HousekeepingPageInput` is exactly the readonly `{ context, match }` pair. - The global route-handler aggregate is empty and its test proves one-to-one equality with the currently empty manifest route set; no placeholder handlers were added. - `/ase-next` searches registered routes in manifest order, requires both domain and route capability, redirects to the first permitted route, and calls `notFound()` when none exists. - `/ase-next//` derives the domain's canonical `/ase` path, matches it, finds the exact handler, reacquires the cached request-scoped context, rechecks domain and route capability, and invokes the handler with that same context and match. - Unknown routes fail before context loading; inaccessible matched routes load one context and never invoke a handler. ## Verification evidence Targeted routing/registry/handler/preview tests: ```text pnpm exec vitest run --coverage.enabled=false src/features/housekeeping/foundation/routing/match-route.test.ts src/features/housekeeping/foundation/registry.test.ts src/features/housekeeping/route-handlers.test.ts src/features/housekeeping/foundation/preview-route-contract.test.ts Test Files 4 passed (4) Tests 94 passed (94) ``` Directly affected context contract: ```text pnpm exec vitest run --coverage.enabled=false src/features/housekeeping/foundation/server-capability-context.test.ts Test Files 1 passed (1) Tests 2 passed (2) ``` Full housekeeping suite: ```text pnpm test:housekeeping Test Files 38 passed (38) Tests 349 passed (349) ``` TypeScript: ```text pnpm typecheck $ tsc --noEmit exit 0 ``` The only output note was the existing engine warning: local Node `26.7.0` is below the package request `>=26.8.1 <27`. Targeted Biome with formatting disabled: ```text pnpm exec biome check --formatter-enabled=false <11 changed Task 9 source/test files> Checked 11 files in 47ms. No fixes applied. ``` `git diff --check` exited 0 before staging. Cached-diff and committed-tree checks are run as the final staging/commit gates. ## Exact Task 9 files ```text src/app/ase-next/[domain]/[[...segments]]/page.tsx src/app/ase-next/[domain]/layout.tsx src/app/ase-next/[domain]/page.tsx (deleted) src/app/ase-next/page.tsx src/features/housekeeping/foundation/preview-route-contract.test.ts src/features/housekeeping/foundation/registry.test.ts src/features/housekeeping/foundation/registry.ts src/features/housekeeping/foundation/routing/match-route.test.ts src/features/housekeeping/foundation/routing/match-route.ts src/features/housekeeping/foundation/server-capability-context.test.ts src/features/housekeeping/route-handlers.test.ts src/features/housekeeping/route-handlers.ts .superpowers/sdd/2026-08-26-housekeeping-completion/task-9-report.md ``` ## Self-review and tooling - Mutation check: dynamic-name normalization removal, regex-style static matching, static-priority removal, decoded-separator acceptance, domain mismatch acceptance, skipped route ACL, context reload inside the handler, missing handler lookup, placeholder handler addition, and empty-domain redirect each break a focused test. - The catch-all route imports only housekeeping foundation/manifests/handlers and retains the existing forbidden database/auth/permissions/actions/legacy-page boundary audit. - `apply_patch` created all new files, but the Windows sandbox helper repeatedly failed to read existing files with `apply deny-read ACLs`. Existing-file edits therefore used controller-approved exact-anchor/full-file fallbacks only after resolving absolute paths and validating every target under `E:\Users\simol\Desktop\EpicNext-cms`. ## Fix Round 1 — deterministic encoded route matching ### Status and scope - Fix base: `e5c230ba35aec2b4c471608d32b8a16ecc1ee382`. - Only the two Important matcher blockers were addressed. - The three parked Minor findings remain unchanged; no changed line required an adjustment to them. - Commit message: `fix(housekeeping): make route matching deterministic`. - `.remember/` remained untouched. No worktree, push, PR/MR, database operation, deployment, or Task 10 work was performed. ### Root-cause evidence 1. The catch-all receives decoded Next segments and re-encodes them with `encodeURIComponent`. The matcher decoded the request path into `decodedSegments` but compared static route text against `rawSegments`. Therefore literal `a+b[1]` did not equal `a%2Bb%5B1%5D`; the competing `:id` route captured the request and could change the selected capability/handler. 2. Candidate sorting used only total dynamic-segment count. Intersecting patterns `/:kind/settings` and `/users/:id` have the same count, so stable sort preserved manifest order and allowed registration order to decide dispatch. ### RED Exact command after test setup was validated: ```text pnpm exec vitest run --coverage.enabled=false src/features/housekeeping/foundation/routing/match-route.test.ts src/features/housekeeping/foundation/registry.test.ts src/features/housekeeping/route-handlers.test.ts src/features/housekeeping/foundation/preview-route-contract.test.ts ``` Observed exit 1: ```text Test Files 2 failed | 2 passed (4) Tests 2 failed | 95 passed (97) catch-all encoded literal: Expected routeId people.literal-tool with params {} Received routeId people.tool-detail with params { id: "a+b[1]" } equal-count specificity: Expected routeId people.user-detail with params { id: "settings" } Received routeId people.kind-settings with params { kind: "users" } ``` The registration-order table exercises both orders. Before production changes the general-first order failed while the reverse order passed, proving that order was the deciding variable. An earlier RED attempt exposed a test-table setup error (`manifest.routes is not iterable`); the table was changed from spread array rows to named `{ routes }` rows, then rerun to obtain the behavioral RED above before any production edit. ### Fix - A single `decodeCanonicalSegment` boundary now normalizes request segments and static route-pattern segments exactly once. - Invalid percent encoding, empty values, decoded `/` or `\`, and decoded `.` / `..` remain fail closed. - Dynamic markers retain their parameter names and receive the already-decoded request segment. - Candidate specificity is compared left-to-right. At the earliest static/dynamic difference, the static segment wins; manifest order no longer selects among intersecting patterns. - Existing static-over-dynamic behavior and registry duplicate-shape rejection remain unchanged. ### GREEN and pre-commit verification Targeted command above, exit 0: ```text Test Files 4 passed (4) Tests 97 passed (97) ``` Full housekeeping suite, exit 0: ```text pnpm test:housekeeping Test Files 38 passed (38) Tests 352 passed (352) ``` TypeScript, exit 0: ```text pnpm typecheck $ tsc --noEmit ``` The only output note remained the existing Node warning: local `26.7.0`, package request `>=26.8.1 <27`. Exact changed-file Biome, exit 0: ```text pnpm exec biome check --formatter-enabled=false src/features/housekeeping/foundation/routing/match-route.ts src/features/housekeeping/foundation/routing/match-route.test.ts src/features/housekeeping/foundation/preview-route-contract.test.ts Checked 3 files in 25ms. No fixes applied. ``` `git diff --check` exited 0 before the report update. Cached and committed-tree checks are final staging/commit gates. ### Exact fix files ```text src/features/housekeeping/foundation/routing/match-route.ts src/features/housekeeping/foundation/routing/match-route.test.ts src/features/housekeeping/foundation/preview-route-contract.test.ts .superpowers/sdd/2026-08-26-housekeeping-completion/task-9-report.md ```