Filed unassigned by the domain:cli dev seat implementing #14278 (session session_016yfqQh2dBgPAymYd7xipza, branch claude/issue-14278-aliased-install-host-importer), as an out-of-scope finding of that card. ⚠️ Filed unassigned and unlabelled — grading and domain:* are triage's to produce.
The residue
#14278 taught createHostImporter's fallback finder to expect the manifest name the DECLARATION names, so "foo": "npm:bar@1" (and the aliased workspace:bar@*) is verified against bar rather than against the key. That route works because those two spellings encode a package NAME.
link: and file: do not. They encode a location:
{ "dependencies": { "foo": "link:../bar" } }hostRoot/node_modules/foo is a symlink to ../bar, whose package.json may be named anything — commonly bar. There is no name in the specifier to expect, so #14278 deliberately kept the KEY as the expectation for these, and with it the pre-#14041 behaviour: the finder answers absent, and an ESM-only linked package whose manifest name differs from the key keeps the INSTALL wording — the same confidently-wrong remedy #14041 and #14278 removed for their own shapes. The boundary is pinned as a test in that PR (BOUNDARY: a link: target whose manifest names something else keeps the refusal), so this is a declared limit rather than an unnoticed gap. A git or tarball URL (github:acme/bar, an https://…/bar.tgz) installs under the key with the published manifest's own name and lands in the same sub-case.
Why it needs a decision, not a rider
The obvious route is a second verification AXIS rather than a wider name check: for a location specifier, verify that realpath(hostRoot/node_modules/key) is the directory the declaration named (realpath(resolve(hostRoot, path))). That keeps the finder strictly tighter than CJS resolution — it still refuses a directory the host did not declare — but it is a second mechanism in a finder whose entire value is that it has exactly one, and it does not extend to a tarball or git URL, where no location on disk is named either.
⛔ The one thing it must NOT become is "skip the check when the specifier is a location": that accepts any directory sitting at the key, which is the looser finder #4719 and #14041 exist to prevent, and it would trade a confidently-wrong remedy for a wrong LOAD.
⚠️ No measured pull: no host app in this workspace or in the examples declares a link:/file: dependency under a key that differs from the linked package's name. Filed so the sub-case is on the board rather than rediscovered, and graded accordingly.
Direction
Conservative, exactly as #14278's parent was: the importer REFUSES, never loads the wrong thing. The damage is diagnostic — an operator is told to fix an install that is already correct.
Re-check
git grep -n "ALIAS_DECLARATION_PROTOCOLS\|declaredManifestName" -- packages/types/src/node.ts
(That helper lands with #14278's PR; before it merges the grep is expected to be empty on main and non-empty on claude/issue-14278-aliased-install-host-importer. Reverse-check any zero against a term known present in the same file — hostRequire.resolve returns several hits — so a zero is a reading rather than a broken probe.)
Refs
Filed unassigned by the⚠️ Filed unassigned and unlabelled — grading and
domain:clidev seat implementing #14278 (sessionsession_016yfqQh2dBgPAymYd7xipza, branchclaude/issue-14278-aliased-install-host-importer), as an out-of-scope finding of that card.domain:*are triage's to produce.The residue
#14278 taught
createHostImporter's fallback finder to expect the manifest name the DECLARATION names, so"foo": "npm:bar@1"(and the aliasedworkspace:bar@*) is verified againstbarrather than against the key. That route works because those two spellings encode a package NAME.link:andfile:do not. They encode a location:{ "dependencies": { "foo": "link:../bar" } }hostRoot/node_modules/foois a symlink to../bar, whosepackage.jsonmay be named anything — commonlybar. There is no name in the specifier to expect, so #14278 deliberately kept the KEY as the expectation for these, and with it the pre-#14041 behaviour: the finder answersabsent, and an ESM-only linked package whose manifest name differs from the key keeps the INSTALL wording — the same confidently-wrong remedy #14041 and #14278 removed for their own shapes. The boundary is pinned as a test in that PR (BOUNDARY: a link: target whose manifest names something else keeps the refusal), so this is a declared limit rather than an unnoticed gap. A git or tarball URL (github:acme/bar, anhttps://…/bar.tgz) installs under the key with the published manifest's own name and lands in the same sub-case.Why it needs a decision, not a rider
The obvious route is a second verification AXIS rather than a wider name check: for a location specifier, verify that
realpath(hostRoot/node_modules/key)is the directory the declaration named (realpath(resolve(hostRoot, path))). That keeps the finder strictly tighter than CJS resolution — it still refuses a directory the host did not declare — but it is a second mechanism in a finder whose entire value is that it has exactly one, and it does not extend to a tarball or git URL, where no location on disk is named either.⛔ The one thing it must NOT become is "skip the check when the specifier is a location": that accepts any directory sitting at the key, which is the looser finder #4719 and #14041 exist to prevent, and it would trade a confidently-wrong remedy for a wrong LOAD.
link:/file:dependency under a key that differs from the linked package's name. Filed so the sub-case is on the board rather than rediscovered, and graded accordingly.Direction
Conservative, exactly as #14278's parent was: the importer REFUSES, never loads the wrong thing. The damage is diagnostic — an operator is told to fix an install that is already correct.
Re-check
(That helper lands with #14278's PR; before it merges the grep is expected to be empty on
mainand non-empty onclaude/issue-14278-aliased-install-host-importer. Reverse-check any zero against a term known present in the same file —hostRequire.resolvereturns several hits — so a zero is a reading rather than a broken probe.)Refs
"foo": "npm:bar@1") fails createHostImporter's manifest-name check, so an ESM-only aliased package keeps the wrong INSTALL wording #14278 / its PR — the alias fix this is the declared boundary of.createHostRequire认 NODE_PATH,于是 #4699 立下的「host app 必须自己声明」在 pnpm 工作区里根本没被强制 #4719 — the declaration gate whose tightness any fix here must keep.