Fix #1622: classifySelfProvenance calls a project-local root global-candidate, so the automatic lane probes and reports degraded forever - #1634
Conversation
…andidate A copy of hypaware inside a project's node_modules read as `global-candidate`, because the only node_modules test was "is there a segment at all" and a global root has one too. The apply itself was already refused (`applySelfUpdate` compares the root against npm's prefix), so nothing was ever installed; what the misread cost was everything around that refusal: a daily registry probe the provenance guard exists to prevent, an `npm config get prefix` spawn, and a sticky `apply_failed: not_global_install` error that put a permanent "self-update: degraded" line on `hyp status`. Reuse `isEphemeralBinPath` for the distinction (a manifest beside the outermost node_modules) and return a fourth verdict, `project-local`. Every gate is already written against `!== 'global-candidate'`, so the automatic lane now skips before probing and `describeSelfUpdate`'s staleDaemon comparison stays off. `hyp update` names the shape instead of reaching the generic "install failed" branch. `hyp daemon install`'s `resolveDurableBinPath`, the other lane in the issue, is not touched: widening it spends an unrequested `npm install -g` and needs a consent decision the LLP corpus does not settle. #1622 stays open for it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`project-local` is decided by a manifest beside the outermost `node_modules`, and pnpm's `global/<n>` and yarn's `config/yarn/global` carry one too, so `hyp update`'s new arm asserting "a project's node_modules" told those users to go looking for a checkout they do not have. That is the exact claim issue #1625 removed from `describeEphemeralBinPath` one commit earlier, on this same predicate, so the arm now names the tree it observed and stops there. The repair it points at is unchanged and is right for all of them. The verdict itself is left as it is. `applySelfUpdate` compares the root against npm's own prefix, so a pnpm or yarn global root was already refused before this change; what the verdict takes from those installs is the degraded status line that refusal used to leave, which is the same silence a checkout and an npx cache have had from the text line since LLP 0309. Separating them needs a heuristic #1625 put out of scope, so the consequence is written down in the JSDoc and asserted in the test instead of being rediscovered later. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Review round 1 (head
|
Feature or issue
classifySelfProvenanceclassified a copy ofhypawareinside a project'snode_modulesasglobal-candidate, because its onlynode_modulestest was "is there such a segment at all" and a global root (<prefix>/lib/node_modules/hypaware) has one too. Executed against that shape, the doomednpm install -gnever actually ran:applySelfUpdatecompares the root against npm's prefix and refuses withnot_global_install. What the misread cost was everything around that refusal, every TTL tick and forever: a daily registry probe that LLP 0309 #global-install-only exists to prevent, annpm config get prefixsubprocess to ask what the path already answers, a stickyapply_failed: not_global_installthat renders asself-update: degraded (...)on everyhyp statusfor a machine with nothing wrong with it, anddescribeSelfUpdate'sstaleDaemoncomparison readingidentity.versionas "the version installed on this machine" from a process that is not that install.Solution
classifySelfProvenancereturns a fourth verdict,project-local, decided by the existingisEphemeralBinPath(a manifest beside the outermostnode_modules, which a project carries and<prefix>/libdoes not) rather than new path logic. Every gate in the updater is already written against!== 'global-candidate', so the automatic lane now skips before it probes and the status comparison stays off;hyp updatereaches the refusal already written for a checkout and an npx cache, naming the third shape, instead of the generic "install failed".npm install -groot, an_npxrun, and a HypAware dev clone are all unchanged, asserted in the same test and re-checked against this machine's real npm prefix.hyp daemon install'sresolveDurableBinPath(lane 1 of the issue) is deliberately not touched. Widening it to the same predicate makes adaemon installfrom a project that happens to depend onhypawarespend an unrequested global install with network and global-prefix writes, and the alternatives (refuse with the repair named, or point at an already-installed durablehypviafindInstalledHypawareBin, or warn and record the ephemeral path as the bake sites do) trade consent against version skew in a way LLP 0017 #install-global-package-then-service-manager settles only fornpx. Two core lanes still ask the durable-bin question_npx-only after #1619:hyp daemon installandclassifySelfProvenance#1622 stays open for that lane.Code: +30 / -4 lines
Refs #1622