From 21ad321a979e30c8261079ae16e6239d96139797 Mon Sep 17 00:00:00 2001 From: Paul Bakaus Date: Sun, 16 Aug 2026 17:24:31 -0400 Subject: [PATCH] Centralize surface route normalization (#600) Consolidate explicit and inferred surface route canonicalization behind one private rule, with characterization coverage for equivalent and invalid inputs.\n\nAI assistance: OpenAI Codex prepared this change under pbakaus's scheduled architecture-simplification authorization. --- skill/scripts/lib/surface-briefs.mjs | 20 +++++++++----------- tests/surface-brief.test.mjs | 8 ++++++++ 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/skill/scripts/lib/surface-briefs.mjs b/skill/scripts/lib/surface-briefs.mjs index f83416f69..83783517f 100644 --- a/skill/scripts/lib/surface-briefs.mjs +++ b/skill/scripts/lib/surface-briefs.mjs @@ -8,6 +8,12 @@ export function getSurfaceBriefDir(projectRoot) { return path.join(projectRoot, '.impeccable', 'surfaces'); } +function normalizeRouteTarget(route) { + if (!route.startsWith('/') || route.includes('..')) return null; + const normalized = route.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; + return `route:${normalized}`; +} + export function normalizeSurfaceTarget(target, { projectRoot = process.cwd() } = {}) { if (!target || typeof target !== 'string' || !target.trim()) return null; const trimmed = target.trim(); @@ -21,21 +27,13 @@ export function normalizeSurfaceTarget(target, { projectRoot = process.cwd() } = return null; } } - if (/^route:/i.test(trimmed)) { - const route = trimmed.slice(trimmed.indexOf(':') + 1).trim(); - if (!route.startsWith('/') || route.includes('..')) return null; - const normalizedRoute = route.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; - return `route:${normalizedRoute}`; - } - if (trimmed === '/') return 'route:/'; + if (/^route:/i.test(trimmed)) return normalizeRouteTarget(trimmed.slice(trimmed.indexOf(':') + 1).trim()); + if (trimmed === '/') return normalizeRouteTarget(trimmed); if (trimmed.startsWith('/')) { const absolute = path.resolve(trimmed); const relativeToProject = path.relative(projectRoot, absolute); const isProjectFile = relativeToProject && !relativeToProject.startsWith('..') && !path.isAbsolute(relativeToProject); - if (!isProjectFile && !fs.existsSync(absolute) && !trimmed.includes('..')) { - const normalizedRoute = trimmed.split(/[?#]/, 1)[0].replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/'; - return `route:${normalizedRoute}`; - } + if (!isProjectFile && !fs.existsSync(absolute)) return normalizeRouteTarget(trimmed); } const abs = path.isAbsolute(trimmed) ? trimmed : path.resolve(projectRoot, trimmed); const rel = path.relative(projectRoot, abs); diff --git a/tests/surface-brief.test.mjs b/tests/surface-brief.test.mjs index 35aa128f8..346bce86f 100644 --- a/tests/surface-brief.test.mjs +++ b/tests/surface-brief.test.mjs @@ -6,6 +6,7 @@ import path from 'node:path'; import { listSurfaceBriefs, + normalizeSurfaceTarget, resolveSurfaceBrief, writeSurfaceBrief, } from '../skill/scripts/lib/surface-briefs.mjs'; @@ -69,6 +70,13 @@ describe('surface briefs', () => { assert.equal(result.brief?.primaryTarget, 'route:/pricing'); }); + it('canonicalizes explicit and inferred route identifiers consistently', () => { + assert.equal(normalizeSurfaceTarget('route:/docs//intro/?from=nav#top', { projectRoot: cwd }), 'route:/docs/intro'); + assert.equal(normalizeSurfaceTarget('/docs//intro/?from=nav#top', { projectRoot: cwd }), 'route:/docs/intro'); + assert.equal(normalizeSurfaceTarget('route:/docs/../admin', { projectRoot: cwd }), null); + assert.equal(normalizeSurfaceTarget('/docs/../admin', { projectRoot: cwd }), null); + }); + it('supports the root route even though the filesystem root exists', () => { const filePath = writeSurfaceBrief({ projectRoot: cwd,