Skip to content

Distinguish npm and workspace sources with the same package name#191

Merged
LadyBluenotes merged 5 commits into
mainfrom
m2/pr1-identity
Jul 3, 2026
Merged

Distinguish npm and workspace sources with the same package name#191
LadyBluenotes merged 5 commits into
mainfrom
m2/pr1-identity

Conversation

@LadyBluenotes

@LadyBluenotes LadyBluenotes commented Jul 3, 2026

Copy link
Copy Markdown
Member

Summary

intent.skills entries and the discovery scanner both compared sources by package name alone, so workspace:foo and npm:foo were treated as the same source. A project could unintentionally authorize the wrong foo — e.g. an npm-installed dependency slipping through an allowlist entry meant only for a local workspace package of the same name.

This tightens matching to (kind, id) everywhere a source's identity is checked:

1. Allowlist matching

isSourcePermitted now takes the discovered package's kind and requires it to match the declared source's kind, not just the name. The "declared but not discovered" notice is keyed the same way.

2. Load-path late gate

intent load resolves a skill before the scanner has told it kind — a name-only pre-check stays for the fast, no-I/O rejection path. Once resolution reveals the real kind (fast path or full scan), a second gate re-checks it and refuses with the same package-not-listed error either way, so the two resolution paths can't disagree on the same input.

3. Excludes stay name-only (deliberate)

intent.skills.exclude intentionally continues to match by name regardless of kind — an exclude is a blanket "don't load skills from anything named X," not tied to a specific source. Documented in place, no behavior change.

Impact / compatibility

  • Breaking for the narrow case of workspace:foo currently authorizing an npm-discovered foo (or vice versa) — that combination now requires its own matching allowlist entry.

Summary by CodeRabbit

  • Bug Fixes
    • Package loading now distinguishes between workspace and npm sources, preventing a workspace rule from unintentionally authorizing a same-named npm package.
    • intent load now rejects mismatched packages more consistently, including after fallback resolution.
    • Notice messages were updated to better reflect when a declared package was not found.
    • Exclude rules remain name-based and apply to both package kinds with the same name.

- Updated `isSourcePermitted` to accept an optional `packageKind` parameter, allowing for more granular source validation based on package type.
- Modified the logic in `applySourcePolicy` to pass the package kind when checking source permissions.
- Improved the discovery check for sources in explicit mode by utilizing a `sourceIdentityKey` function to create unique identifiers based on both package kind and name.
- Added a comment in `excludes.ts` to clarify the intentional design choice regarding package exclusion handling.
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR tightens skill-source authorization in the intent package to use a (kind, id) identity tuple instead of name alone, preventing an npm-installed package from being authorized by a workspace: allowlist entry with the same name, and vice versa. It adds a SourceIdentity type with helper functions, propagates package kind through fast-path resolution, updates isSourcePermitted and discovery-notice logic to be kind-aware, adds a droppedNames field to scan results, and enforces a late refusal gate in core resolution paths for both fast-path and full-scan flows. Tests and a changeset are included.

Changes

Kind-aware skill source identity

Layer / File(s) Summary
SourceIdentity contract
packages/intent/src/core/types.ts, packages/intent/tests/core-types.test.ts
Adds SourceIdentity interface with sourceIdentityKey and sourceIdentityEquals helpers, and tests validating differentiation/equality semantics.
Fast-path resolution kind propagation
packages/intent/src/core/load-resolution.ts
Adds FastPathResolveResult interface with a kind discriminator and updates resolveScannedPackageSkill, resolveFromPackageRoots, and resolveSkillUseFastPath return types/values accordingly.
Kind-aware source policy
packages/intent/src/core/source-policy.ts, packages/intent/src/core/excludes.ts
Makes isSourcePermitted kind-aware with an optional packageKind parameter, extracts a reusable packageNotListedRefusal, updates discovery-notice tracking via sourceIdentityKey, treats git sources as always-not-discovered, adds droppedNames to PolicedScan, and clarifies exclude's kind-agnostic behavior.
Core late-gate enforcement and tests
packages/intent/src/core/intent-core.ts, packages/intent/tests/core.test.ts, packages/intent/tests/source-policy.test.ts, .changeset/tidy-buses-hunt.md
Adds post-resolution permission checks using droppedNames/isSourcePermitted for fast-path and full-scan flows, throwing IntentCoreError on kind mismatch; adds corresponding tests and a changeset entry.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • TanStack/intent#156: Both PRs enforce package.json#intent.skills via the source-policy/load gating code, with this PR tightening allowlist matching to kind+id identity.
  • TanStack/intent#171: Adds the kind discriminator to discovered packages/scan results that this PR now uses for kind-aware identity checks.
  • TanStack/intent#176: Both PRs modify scanForPolicedIntents and source-policy.ts notice logic, entangling this PR's identity tightening with audience-aware notice handling.

Suggested reviewers: KevinVandy, schiller-manuel

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description explains the change well, but it doesn't follow the required template and omits the checklist and release-impact sections. Rewrite it using the repository template: add 🎯 Changes, ✅ Checklist, and 🚀 Release Impact sections with the required checkbox items.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: distinguishing npm and workspace sources with the same package name.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch m2/pr1-identity

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@nx-cloud

nx-cloud Bot commented Jul 3, 2026

Copy link
Copy Markdown

View your CI Pipeline Execution ↗ for commit 9dd06a7

Command Status Duration Result
nx run-many --targets=build --exclude=examples/** ✅ Succeeded <1s View ↗

☁️ Nx Cloud last updated this comment at 2026-07-03 03:13:12 UTC

@pkg-pr-new

pkg-pr-new Bot commented Jul 3, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/TanStack/intent/@tanstack/intent@191

commit: 9dd06a7

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/intent/tests/core.test.ts (1)

905-1010: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Solid coverage of the kind-mismatch late gate; consider adding a same-name collision test.

The four new cases correctly cover fast-path rejection, full-scan rejection, positive workspace authorization, and exclusion-before-resolution precedence.

One gap worth adding: a case where an npm-installed copy and a workspace member of the same name coexist in the same project (e.g., a workspace package also reachable under node_modules), with intent.skills: ['workspace:<name>']. This directly exercises the scenario this PR is meant to close and would also validate the droppedNames/discovery-notice assumptions in source-policy.ts under a real collision (see companion comment there).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/intent/tests/core.test.ts` around lines 905 - 1010, The current
tests cover the workspace-only and exclusion paths, but they do not exercise a
same-name collision between an npm-installed package and a workspace member. Add
a new `loadIntentSkill` test alongside the existing `kind-mismatch late gate`
cases that creates both a workspace package and an installed `node_modules` copy
with the same package name, sets `intent.skills` to `workspace:<name>`, and
asserts the workspace authorization wins while the npm-installed copy is
rejected/ignored as intended. Use the existing `loadIntentSkill`,
`writeInstalledIntentPackage`, and workspace setup helpers in `core.test.ts` to
verify the collision behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/intent/tests/core.test.ts`:
- Around line 905-1010: The current tests cover the workspace-only and exclusion
paths, but they do not exercise a same-name collision between an npm-installed
package and a workspace member. Add a new `loadIntentSkill` test alongside the
existing `kind-mismatch late gate` cases that creates both a workspace package
and an installed `node_modules` copy with the same package name, sets
`intent.skills` to `workspace:<name>`, and asserts the workspace authorization
wins while the npm-installed copy is rejected/ignored as intended. Use the
existing `loadIntentSkill`, `writeInstalledIntentPackage`, and workspace setup
helpers in `core.test.ts` to verify the collision behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 4c9a6dfc-9a1e-4f0c-8b54-815f278e873b

📥 Commits

Reviewing files that changed from the base of the PR and between 7e320ab and 9dd06a7.

📒 Files selected for processing (9)
  • .changeset/tidy-buses-hunt.md
  • packages/intent/src/core/excludes.ts
  • packages/intent/src/core/intent-core.ts
  • packages/intent/src/core/load-resolution.ts
  • packages/intent/src/core/source-policy.ts
  • packages/intent/src/core/types.ts
  • packages/intent/tests/core-types.test.ts
  • packages/intent/tests/core.test.ts
  • packages/intent/tests/source-policy.test.ts

@LadyBluenotes LadyBluenotes merged commit 3620cca into main Jul 3, 2026
9 checks passed
@LadyBluenotes LadyBluenotes deleted the m2/pr1-identity branch July 3, 2026 03:29
@github-actions github-actions Bot mentioned this pull request Jul 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant