Skip to content

check:test-source-alias cannot read a template-literal alias replacement, so two packages that DO alias spec subpaths to source are still recorded as unaliased #8020

Description

@hotlong

Filing unassigned; found while implementing #7991 (the packages/qa/downstream-contract source alias). Not fixed there — the card's ruling is that a problem surfaced by aliasing is a finding in its own right, and this one changes a shared gate's semantics plus two other packages' registry entries.

The fact, measured

scripts/check-test-source-alias.mjs reads an alias replacement with asPath(), which takes the last string literal in the expression:

functionasPath(raw){conststrings=[...raw.matchAll(/(['"`])((?:[^\\]|\\.)*?)\1/g)].map((m)=>m[2]);
...
returnstrings[strings.length-1];}

For the path.resolve(__dirname, '../../spec/src/index.ts') form that is the whole answer. For the template-literal form — the one the gate's own two reference configs use for subpath rules —

{find: /^@objectstack\/spec\/([a-z-]+)$/,replacement: `${path.resolve(__dirname,'../../spec/src')}/$1/index.ts`}

the only string literal delimiter reached is the backtick, so asPath returns the entire template body, ${path.resolve(__dirname, '../../spec/src')}/$1/index.ts. pointsAtSource() looks for a src path segment and there is none in that text (spec/src' is followed by a quote, not a separator), so a config that aliases every namespace correctly reads to the gate as aliasing nothing.

Evidence: changing only the SPELLING moves the verdict

packages/services/service-knowledge and packages/plugins/plugin-audit both alias spec subpaths to source with the template form today. Rewriting only the spelling — same resolution, no behaviour change:

replacement: path.join(path.resolve(__dirname,'..','..'),'spec/src/$1/index.ts')

makes the gate declare their registry entries stale:

 x @objectstack/plugin-audit: registry entry is STALE — no longer unaliased: @objectstack/spec.
Narrow the entry to exactly: ["@objectstack/objectql"]
x @objectstack/service-knowledge: registry entry is STALE — no longer unaliased: @objectstack/spec.
Narrow the entry to exactly: ["@objectstack/objectql"]

So @objectstack/spec sits in both entries because of how the replacement is written, not because anything resolves through dist. (Both experiments were reverted; nothing in PR for #7991 touches those two configs.)

Direction, and why it still matters

Fail-closed. The gate never reads an unaliased package as safe, so its core guarantee is intact and this is not a #7991-shaped false green. What it does cost:

  1. The shrink-only registry over-states the remaining work — at least two of the 62 entries name a dependency that is already aliased.
  2. A dev dispatched to remediate either package finds the aliases already correct and the gate still red, with the failure text prescribing the alias they already have. The only thing that moves it is a spelling change; nothing in the message says so.
  3. It quietly penalises the one-rule-for-all-namespaces form. That form exists because hand-listing namespaces goes stale, and both configs carry a paragraph explaining why they use it.

Fix options (not chosen here)

  • A — teach asPath the template form: concatenate the literal chunks and treat each ${...} as an opaque prefix, so the tail /$1/index.ts joins the spec/src chunk. Keeps every existing config as written; the two entries then shrink to ['@objectstack/objectql'] in the same PR (the gate audits both directions, so it names them).
  • B — prescribe the readable spelling in the gate header and convert the two configs; same registry shrink, no gate change.

A is the contract-first shape: the configs are correct, the reader is what cannot see them. Either way the change is one PR and its own card — it edits a shared gate plus two packages' entries, so it should not ride a per-package alias fix.

Prior art (searched before filing)


Generated by Claude Code

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions