Skip to content

fix(plugin-dev): resolve @objectstack/plugin-security from source in tests, shrink the alias registry - #10415

Merged
os-elon merged 1 commit into
mainfrom
claude/issue-10112-plugin-dev-source-alias
Aug 20, 2026
Merged

fix(plugin-dev): resolve @objectstack/plugin-security from source in tests, shrink the alias registry#10415
os-elon merged 1 commit into
mainfrom
claude/issue-10112-plugin-dev-source-alias

Conversation

@os-elon

@os-elon os-elon commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

Fixes #10112

packages/plugins/plugin-dev/src/dev-plugin.ts boots the real SecurityPlugin through a dynamic await import('@objectstack/plugin-security'), and dev-plugin-security-enforcement-warning.test.ts deliberately leaves that chain unmocked — the real plugin's init()/start() phase split is the subject under test. With no source alias, that import resolved through exports to dist/, so the file's verdict was a function of build state.

Both directions of that were live:

  • loud — on an unbuilt closure the whole file died at collection: Test Files 1 failed (1) / Tests no tests, Failed to resolve entry for package "@objectstack/plugin-security".
  • quiet — on a built-but-stale closure it would pass against the old artifact with nothing in the output saying so. That is the failure check:test-source-alias exists to make impossible, and this file is exactly its shape.

What changed

Six alias entries in packages/plugins/plugin-dev/vitest.config.ts, and one member off the shrink-only registry in scripts/check-test-source-alias.mjs ('@objectstack/plugin-dev': 10 entries to 9).

The alias goes in the config, not on the test's import specifier. A config alias covers the production dynamic import too, because Vite resolves the whole module graph through it — one entry beats an edit at either import site. Rewriting the test's line-51 specifier instead was measured and rejected: it leaves dev-plugin.ts's dynamic import still pointing at dist/, so the two diverge, line 51 pre-warms a copy nothing uses, and the cold transform lands back inside a clocked test body. That line is untouched by this PR.

Aliasing a dep to source imports its entire surface into this package's resolution domain (the gate's own "Where the walk goes" note), so plugin-security's formula / metadata-core / platform-objects imports are aliased with it — without them the resolution failure merely moves outward one package at a time. platform-objects publishes a FILE-shaped ./plugin subpath alongside directory-shaped ones, so that rule is listed separately, ahead of the enumerated group.

Measured

All readings through scripts/pm/os-verify-lock.sh, exit codes captured before any pipe, at 102b1c71c.

Unbuilt closure (pnpm install only, packages/plugins/plugin-security/dist absent):

before after
target file Test Files 1 failed (1) · Tests no tests Test Files 1 passed (1) · Tests 4 passed (4)
whole package Test Files 1 failed | 5 passed (6) · Tests 54 passed (54) Test Files 6 passed (6) · Tests 58 passed (58)

Built closure — the clocked-window cost that PR #10120 moved out of a timed body must not come back. Paired control, same built dist, same box:

config bail #1 file tests phase
pre-change 26ms 47ms
this PR 26ms 45ms

No regression. For contrast, the rejected line-51 variant measured 476ms on bail #1 against the same artifact — a 17x regression, which is why it is not what landed.

Reverse verification — the green above is not vacuous. Collapsing the ./plugin rule into a prefix-matching bare entry (the exact trap rule 5 exists to catch) turns check:test-source-alias red, on this diff, with the ENOTDIR signature:

✗ packages/plugins/plugin-dev: alias table would resolve the PUBLISHED subpath
  `@objectstack/platform-objects/plugin` to `../../platform-objects/src/index.ts/plugin`
  — a path THROUGH a file (ENOTDIR at run time).

Restored and re-confirmed green afterwards.

Gates

Re-derived from the real diff rather than recalled — node scripts/pm/dispatch-gates.mjs with no path arguments (2 path(s) vs merge base 900e48935), then every family it named, plus pnpm lint:

gate verdict line
check:test-source-alias check-test-source-alias OK — 72 packages with tests scanned; 61 registered as still resolving a workspace dep through dist/; 44 published subpath(s) resolved through every alias table.
check:cross-package-test-inputs OK: 12 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.
check:slot-lookup slot-lookup ratchet holds: 107 unswept site(s) in 25 file(s), none new
check:type-source-resolution check-type-source-resolution OK — 76 packages with a tsconfig.json scanned; 51 registered...
scripts/check-cross-package-test-inputs.mjs exit 0
scripts/docs-audit/check-affected-docs.mjs exit 0
pnpm lint exit 0 over the full population (eslint . --no-inline-config); both changed files confirmed in eslint's configured population via --format json (2 files linted, 0 errors, 0 warnings) rather than inferred from a bare exit 0

Scope

No gate was weakened: no ceiling raised, no registry entry added or widened, nothing skipped, quarantined or retry-wrapped. The registry moves in the shrink direction only, which is the direction it documents as sanctioned. The test's assertions are byte-identical and its line-51 import is untouched in both specifier and position. dev-plugin.ts is not modified.

This PR carries skip-changeset: it changes one package's vitest configuration (not published — plugin-dev's files is dist/README/CHANGELOG) and one CI-internal script, so it releases nothing.

⚠️ Scope note for reviewers: this fixes the vitest resolution path only. check:type-source-resolution tracks a separate tsconfig-level ledger that this change does not touch and does not claim to move.


Generated by Claude Code

… the alias registry

`dev-plugin.ts` boots the real SecurityPlugin through a dynamic
`await import('@objectstack/plugin-security')`, and the enforcement-warning test
deliberately leaves that chain unmocked. Without a source alias that import
resolved through `exports` to `dist/`, so the file's verdict was a function of
build state: on an unbuilt closure the whole file died at collection
(`Test Files 1 failed` / `Tests no tests`), and on a built-but-stale closure it
would have passed against the old artifact silently -- the quiet failure
`check:test-source-alias` exists to make impossible.

The alias goes in `vitest.config.ts`, not on the test's import specifier: a
config alias covers the production dynamic import too, because Vite resolves the
whole module graph through it. Aliasing the specifier instead was measured to
leave the two divergent and re-arm the clocked-window cost #10115 removed.

Aliasing a dep to source imports its entire surface into this package's
resolution domain, so plugin-security's own `formula` / `metadata-core` /
`platform-objects` imports are aliased with it. `platform-objects` publishes a
FILE-shaped `./plugin` subpath alongside directory-shaped ones, so that rule is
listed separately ahead of the enumerated group.

With the import off `dist/`, `@objectstack/plugin-security` comes off
`@objectstack/plugin-dev`'s entry in the shrink-only
`KNOWN_UNALIASED_TEST_IMPORTS` registry (10 -> 9), which the gate's
set-equality audit then requires.

Fixes #10112

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019bmVFqoQPq63zhKrxdYG1r
@os-elon os-elon added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed size/s labels Aug 20, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/plugins/plugin-dev/vitest.config.ts) — pages documenting those are invisible to this run

Coarse fallback — 3 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json e502a6a8ebafaee434a14481a45494a1dd4958c9packageMentionDocs.

@github-actions github-actions Bot added the tests label Aug 20, 2026
@os-elon
os-elon marked this pull request as ready for review August 20, 2026 18:56
@os-elon
os-elon added this pull request to the merge queue Aug 20, 2026
Merged via the queue into main with commit 91f303c Aug 20, 2026
34 checks passed
@os-elon
os-elon deleted the claude/issue-10112-plugin-dev-source-alias branch August 20, 2026 19:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-changeset PR has no user-facing published change; bypasses the changeset gate tests

Projects

None yet

2 participants