pbakaus/impeccable · error · Error

surface brief requires a concrete project-relative primary t

Error message

surface brief requires a concrete project-relative primary target or URL

What it means

Thrown by writeSurfaceBrief() in skill/scripts/lib/surface-briefs.mjs when normalizeSurfaceTarget() returns null for the primaryTarget. normalizeSurfaceTarget accepts an http(s) URL, a `route:/path`, a lone `/`, or a project-relative path that does not escape the project root (no `..`, stays inside projectRoot). Anything else — absolute path outside the project, a `..` traversal, an unparseable URL, or a non-string — yields null and this error.

Source

Thrown at skill/scripts/lib/surface-briefs.mjs:134

  const exactPath = surfaceBriefPathForTarget(normalized, { projectRoot });
  const exact = briefs.find((brief) => brief.path === exactPath && (!brief.targets.length || brief.targets.includes(normalized)));
  if (exact) return { brief: exact, candidates: briefs, reason: 'slug' };
  const mapped = briefs.filter((brief) => brief.targets.includes(normalized));
  return {
    brief: mapped.length === 1 ? mapped[0] : null,
    candidates: mapped.length > 1 ? mapped : briefs,
    reason: mapped.length === 1 ? 'mapping' : mapped.length > 1 ? 'ambiguous-target' : 'not-found',
  };
}

export function writeSurfaceBrief({
  projectRoot = process.cwd(),
  primaryTarget,
  relatedTargets = [],
  body,
}) {
  const normalizedPrimary = normalizeSurfaceTarget(primaryTarget, { projectRoot });
  if (!normalizedPrimary) throw new Error('surface brief requires a concrete project-relative primary target or URL');
  const normalizedRelated = [...new Set(relatedTargets
    .map((target) => normalizeSurfaceTarget(target, { projectRoot }))
    .filter((target) => target && target !== normalizedPrimary))];
  const slug = slugFromTarget(normalizedPrimary, { cwd: projectRoot });
  const filePath = surfaceBriefPathForTarget(normalizedPrimary, { projectRoot });
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
  const frontmatter = [
    '---',
    `version: ${SURFACE_BRIEF_VERSION}`,
    `slug: ${JSON.stringify(slug)}`,
    `primary_target: ${JSON.stringify(normalizedPrimary)}`,
    `related_targets: ${JSON.stringify(normalizedRelated)}`,
    '---',
  ].join('\n');
  fs.writeFileSync(filePath, `${frontmatter}\n\n${String(body || '').trim()}\n`, 'utf-8');
  return filePath;
}

View on GitHub (pinned to d14711ae3d)

Solutions

  1. Pass a project-relative path (e.g. `src/pages/home.astro`), an `http(s)://` URL, or a `route:/path` for an in-app route.
  2. If you meant a route, prefix it with `route:` or pass it starting with `/` that does not resolve to an existing outside file.
  3. Confirm projectRoot is the repo root so the relative-path normalization does not misclassify the target.

Example fix

// before
writeSurfaceBrief({ primaryTarget: '/Users/me/site/src/pages/home.astro' });
// after
writeSurfaceBrief({ projectRoot, primaryTarget: 'src/pages/home.astro' });
Defensive patterns

Strategy: validation

Validate before calling

import { normalizeSurfaceTarget } from './surface-briefs.mjs';
const normalized = normalizeSurfaceTarget(primaryTarget, { projectRoot });
if (!normalized) {
  throw new Error('primaryTarget must be a project-relative path, http(s) URL, or route:/path');
}

Type guard

function isUsableSurfaceTarget(target, projectRoot) {
  return normalizeSurfaceTarget(target, { projectRoot }) !== null;
}

Prevention

When it happens

Trigger: Calling writeSurfaceBrief({ primaryTarget }) with a value like `/etc/passwd` (absolute, outside project, existing file so not treated as a route), `../outside`, a malformed URL (`http://[broken`), an empty string, or a non-string. Also a bare string that resolves outside projectRoot.

Common situations: Passing an absolute filesystem path instead of a project-relative one; passing a URL with a typo; forgetting to set projectRoot so the relative-path check uses the wrong base; feeding user input unchecked.

Related errors


AI-assisted analysis of pbakaus/impeccable@d14711ae3d (2026-08-13). Data as JSON: /api/errors/1fd0b1d9b79cc909. Report an issue: GitHub.