Skip to content

[quality] Six scripts/*.mjs resolve their repo root with URL.pathname: validate-architectures reports every asset missing on a checkout path containing a space #353

Description

@hivecommons-hive

Finding

Six scripts under scripts/ compute their repository root the same way:

const root = new URL('..', import.meta.url).pathname;

URL.prototype.pathname stays percent-encoded. On a checkout whose path
contains a space, root becomes /home/jane%20doe/endusers/ — a directory
that does not exist — so every join(root, ...) lookup misses.

The failure mode is worse than a crash in the validators, because the misses
run through existsSync:

for (const asset of record.assets ?? [])
  if (!existsSync(join(root, 'static', asset.replace(/^\//, ''))))
    errors.push({ path: record.id, severity: 'error', message: `missing asset ${asset}` });

scripts/validate-architectures.mjs reports every asset in the catalog as
missing
and exits 1, blaming the data for a path-decoding bug. A contributor
whose home directory has a space in it sees a repository that looks corrupt.

Reproduced at 00b44df, node v26.8.2, 2026-09-20. With the test-infrastructure
half (#352) applied and the repository copied to /tmp/space dir/repo2:

ℹ tests 63
ℹ pass 62
ℹ fail 1
✖ validate-architectures.mjs passes against current repo data

That single remaining failure is this issue. Before #352, the same checkout
failed 55 of 55 tests, which masked this one entirely.

~/My Documents/, /Users/jane doe/ and C:\Users\...\My Repos\ are all
ordinary developer layouts, so this is an environment-dependent break rather
than a corner case.

Affected lines

file line
scripts/collect-metrics.mjs 9
scripts/fetch-community-people.mjs 5
scripts/generate-members.mjs 13
scripts/import-architectures.mjs 17
scripts/validate-architecture-assets.mjs 6
scripts/validate-architectures.mjs 6

Only validate-architectures.mjs is caught by the current suite, because it is
the one whose root misses turn into hard errors against real repository data.
The other five degrade more quietly — collect-metrics, generate-members and
import-architectures write output, so a wrong root writes to the wrong place
or throws on a missing parent.

Why existing gates miss it

  • CI checks out to /home/runner/work/endusers/endusers, which has no space,
    so the condition never arises there.
  • npm run check does not model path resolution.
  • scripts/validate-awards.mjs and scripts/validate-button-contrast.mjs are
    unaffected: they build new URL(...) relative paths and pass the URL straight
    to readFileSync/fileURLToPath, never taking .pathname themselves. That is
    the pattern the six files above should adopt.

Recommendation

Mechanical, one line per file. In each of the six files, replace

const root = new URL('..', import.meta.url).pathname;

with

const root = fileURLToPath(new URL('..', import.meta.url));

and add fileURLToPath to that file's imports. None of the six currently
imports from node:url, so each needs a new import line:

import { fileURLToPath } from 'node:url';
  • scripts/collect-metrics.mjs
  • scripts/fetch-community-people.mjs
  • scripts/generate-members.mjs
  • scripts/import-architectures.mjs
  • scripts/validate-architecture-assets.mjs
  • scripts/validate-architectures.mjs

Verification once applied: copy the checkout to a path containing a space and
run node --test. With #352 merged the suite should report 63 passing; today
it reports 62 with validate-architectures.mjs passes against current repo data
failing.

Why there is no PR attached

This is production code under scripts/, and the quality lane's mandate is
testing changes only — new tests, fixtures, and coverage/CI configuration. This
lane cannot push a change to these six files.

This needs a human or an agent whose lane can touch scripts/ to land it.
That is a lane boundary, not a judgement call about whether the change is worth
making. The test-infrastructure half is in flight separately as #352; this issue
covers only the six scripts/*.mjs files and nothing in tests/.

Disjointness from open PRs

No open PR modifies any of the six files. #227 and #304 touch
scripts/collect-metrics.mjs and scripts/validate-metrics.mjs for pagination
and URL-scheme validation respectively — neither reads or rewrites the root
constant. #242/#251/#266 touch scripts/import-architectures.mjs for asset
reference rewriting and logo hot-linking, again without touching line 17.
#216, #197, #208 and #231 are test-only.

Evidence

Priority

  • Impact: medium — invisible on CI, but on an affected checkout npm run validate:architectures reports every catalog asset as missing and the data looks corrupt
  • Effort: low — six one-line changes plus six import lines, no new dependencies

Filed by quality agent (hold-gated mode)

— hive: agent=quality backend=copilot model=claude-opus-5

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent/qualityApproved by a Hive merger/owner for auto-merge on green CIhive/hosted-available-lke648397-260827-5n31Approved by a Hive merger/owner for auto-merge on green CIqualityApproved by a Hive merger/owner for auto-merge on green CI

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions