Filing unassigned; domain:* routing and grading are the triage seat's. packages/qa/** is not the devx lane — filing at the destination per the file-at-destination rule, from the devx seat (#6023) as the committed first follow-up of PR #7966.
The defect, demonstrated rather than argued
packages/qa/downstream-contract is the repository's frozen third-party consumer fixture. Its own README states that a failure there means a spec change has narrowed something a published-spec consumer already relies on — i.e. that the change is breaking. It is the backward-compatibility gate for @objectstack/spec.
It ships no vitest.config.*, so @objectstack/spec resolves through exports to packages/spec/dist — a build artifact. Measured during PR #7966, direction predicted before running:
| step | tree | result |
|---|
| baseline | spec built, source == dist | 14/14 pass |
a required field injected into ConnectorSchemain source only, no rebuild | the frozen fixture can no longer parse | 14/14 pass |
| identical tree, spec aliased to source | — | 1 failed / 13 passed, naming the injected field |
So the gate whose entire job is to notice breaking spec changes did not notice one, and said nothing about why. This is the silent half of #7668: a dist merely behind the source runs green against old behaviour, with nothing in the output indicating the verdict is about build state rather than the checkout.
Why this one first, ahead of the other 62
PR #7966 (merged into the gate + registry for #7849) measured 63 packages with at least one unaliased artifact import, across 312 package↔dependency pairs. This card is one of those 63 registry entries and is deliberately not a duplicate of the sweep — it is pulled out first because its blast radius is categorically different from the rest:
- Every other entry produces a wrong verdict about its own package.
- This entry produces a wrong verdict about whether the repo is shipping a breaking change to
spec, which is the highest-churn package in the tree, and does so on the one test suite specifically built to answer that question.
A green here is currently consumed as evidence of backward compatibility. It is not evidence of that today.
The fix, and the one thing that could make it non-trivial
Add packages/qa/downstream-contract/vitest.config.ts aliasing @objectstack/spec (and any other workspace dep its tests reach) to source. Use the anchored regex / array form, never the object form — the object form matches by prefix, so a bare @objectstack/spec entry also swallows @objectstack/spec/ui and resolves it to spec/src/index.ts/ui (ENOTDIR). Constraint from PR #7778, mechanically enforced by pnpm check:test-source-alias as of PR #7966.
Then remove this package's entry from KNOWN_UNALIASED_TEST_IMPORTS in scripts/check-test-source-alias.mjs. The registry is audited in both directions: leaving a stale entry behind fails the gate and names itself for deletion, so this is not optional cleanup — it is part of the change.
⚠️Do not assume it is a one-liner, and do not widen the registry if it is not. Aliasing a dep to source can surface dual-instance or circular-import problems that the dist boundary is currently hiding. If that happens here, it is a finding in its own right and should be reported — not a reason to leave the artifact on the resolution path. This caveat is the PR #7966 dev's, carried verbatim because it is the failure mode most likely to turn this card into a silent no-op.
Expect the suite to go red once aliased if any real narrowing has landed in spec since the last dist build. That is the gate starting to work, not a regression introduced by this card.
Prior art (searched before filing)
Nothing open covers this package.
Filing unassigned;
domain:*routing and grading are the triage seat's.packages/qa/**is not the devx lane — filing at the destination per the file-at-destination rule, from the devx seat (#6023) as the committed first follow-up of PR #7966.The defect, demonstrated rather than argued
packages/qa/downstream-contractis the repository's frozen third-party consumer fixture. Its own README states that a failure there means a spec change has narrowed something a published-spec consumer already relies on — i.e. that the change is breaking. It is the backward-compatibility gate for@objectstack/spec.It ships no
vitest.config.*, so@objectstack/specresolves throughexportstopackages/spec/dist— a build artifact. Measured during PR #7966, direction predicted before running:ConnectorSchemain source only, no rebuildSo the gate whose entire job is to notice breaking spec changes did not notice one, and said nothing about why. This is the silent half of #7668: a dist merely behind the source runs green against old behaviour, with nothing in the output indicating the verdict is about build state rather than the checkout.
Why this one first, ahead of the other 62
PR #7966 (merged into the gate + registry for #7849) measured 63 packages with at least one unaliased artifact import, across 312 package↔dependency pairs. This card is one of those 63 registry entries and is deliberately not a duplicate of the sweep — it is pulled out first because its blast radius is categorically different from the rest:
spec, which is the highest-churn package in the tree, and does so on the one test suite specifically built to answer that question.A green here is currently consumed as evidence of backward compatibility. It is not evidence of that today.
The fix, and the one thing that could make it non-trivial
Add
packages/qa/downstream-contract/vitest.config.tsaliasing@objectstack/spec(and any other workspace dep its tests reach) to source. Use the anchored regex / array form, never the object form — the object form matches by prefix, so a bare@objectstack/specentry also swallows@objectstack/spec/uiand resolves it tospec/src/index.ts/ui(ENOTDIR). Constraint from PR #7778, mechanically enforced bypnpm check:test-source-aliasas of PR #7966.Then remove this package's entry from
KNOWN_UNALIASED_TEST_IMPORTSinscripts/check-test-source-alias.mjs. The registry is audited in both directions: leaving a stale entry behind fails the gate and names itself for deletion, so this is not optional cleanup — it is part of the change.distboundary is currently hiding. If that happens here, it is a finding in its own right and should be reported — not a reason to leave the artifact on the resolution path. This caveat is the PR #7966 dev's, carried verbatim because it is the failure mode most likely to turn this card into a silent no-op.Expect the suite to go red once aliased if any real narrowing has landed in
specsince the lastdistbuild. That is the gate starting to work, not a regression introduced by this card.Prior art (searched before filing)
vitest.config.tslets a stale@objectstack/coredist decide its verdicts — #7668 fixed one, nothing stops the next #7849 / PR ci(test-alias): gate that a unit test judges source, not a sibling's dist #7966 — the gate and registry that make this measurable, and the source of the demonstration above.attach-requires-parent-editc3: #4757 multi-delete pin can't execute against the prebuilt@objectstack/core(stale-dist / CI) #7668 / PR test(service-storage): resolve@objectstack/corefrom source so a stale dist can't decide a pin (#7668) #7778 — the loud version of this hazard (17 cases erroring on a symbol its prebuilt core predated) and the anchored-regex constraint.domain:spec-tooling) — "Three more dist-reading spec gates carry the same unenforced freshness precondition asgen:api-surface". Same family, different surface; closed.domain:devx) —check:dev-prereqschecks dist existence only, leaving the stale-dist half ungated. Closed, and this is that half showing up somewhere costly.Nothing open covers this package.