Summary
A component deployed by payload gets exactly one dependency install — at deploy time — and never again. If that install fails or its node_modules is lost, nothing restores it. Restarts don't help, because boot-time installation never considers the component at all.
Observed on 5.1.x: a component's node_modules was destroyed by a failed install (see #1973). Every subsequent restart left it broken with Cannot find module at resource load, and no npm run was even attempted — the npm logs were unchanged across restarts.
Why
deployComponent (components/operations.js) only writes an application entry to the root config when the request carries a package identifier:
if (req.package) {
const applicationConfig = { package: req.package };
...
await configUtils.addConfig(req.project, applicationConfig);
}
A payload deploy has no req.package, so no entry is written. installApplications() iterates root-config entries that have a package property, so a payload-deployed component is never in that set — it isn't in harper-application-lock.json either. It's loaded from the components directory on every boot, but its dependencies are never verified or reinstalled.
So the install is effectively one-shot, with no integrity check and no recovery path, for a whole class of deploys.
Possibly related, unverified
Even for root-config applications, installApplication early-returns on mere existence of the directory:
await access(join(application.dirPath, 'node_modules'), constants.F_OK);
// -> "already has node_modules; skipping install"
Separately, something in boot creates node_modules/harper (a symlink to the installed Harper package) so components can import harper. In the case observed, node_modules was absent before restart and afterwards contained only that symlink.
If that symlink is created before the existence check runs, then an empty-but-present node_modules would satisfy the check and suppress a needed install. I could not confirm this, because for the component in question installApplication was never reached at all (per the root-config gap above). Flagging it as a hazard worth checking rather than a diagnosis — if real, it affects root-config apps too, and an existence check is the wrong test either way.
Suggested direction
Make dependency state verifiable and repairable independent of how the component was deployed: record what was installed, check it at load, and reinstall when it doesn't match — rather than inferring "installed" from a directory existing. At minimum, payload-deployed components should be reachable by the same boot-time install path as package-identifier ones.
Related: #1973 (how the install gets destroyed), #1975 (why the breakage stays invisible).
🤖 Filed by Claude on behalf of @heskew
Summary
A component deployed by payload gets exactly one dependency install — at deploy time — and never again. If that install fails or its
node_modulesis lost, nothing restores it. Restarts don't help, because boot-time installation never considers the component at all.Observed on 5.1.x: a component's
node_moduleswas destroyed by a failed install (see #1973). Every subsequent restart left it broken withCannot find moduleat resource load, and no npm run was even attempted — the npm logs were unchanged across restarts.Why
deployComponent(components/operations.js) only writes an application entry to the root config when the request carries a package identifier:A payload deploy has no
req.package, so no entry is written.installApplications()iterates root-config entries that have apackageproperty, so a payload-deployed component is never in that set — it isn't inharper-application-lock.jsoneither. It's loaded from the components directory on every boot, but its dependencies are never verified or reinstalled.So the install is effectively one-shot, with no integrity check and no recovery path, for a whole class of deploys.
Possibly related, unverified
Even for root-config applications,
installApplicationearly-returns on mere existence of the directory:Separately, something in boot creates
node_modules/harper(a symlink to the installed Harper package) so components can importharper. In the case observed,node_moduleswas absent before restart and afterwards contained only that symlink.If that symlink is created before the existence check runs, then an empty-but-present
node_moduleswould satisfy the check and suppress a needed install. I could not confirm this, because for the component in questioninstallApplicationwas never reached at all (per the root-config gap above). Flagging it as a hazard worth checking rather than a diagnosis — if real, it affects root-config apps too, and an existence check is the wrong test either way.Suggested direction
Make dependency state verifiable and repairable independent of how the component was deployed: record what was installed, check it at load, and reinstall when it doesn't match — rather than inferring "installed" from a directory existing. At minimum, payload-deployed components should be reachable by the same boot-time install path as package-identifier ones.
Related: #1973 (how the install gets destroyed), #1975 (why the breakage stays invisible).
🤖 Filed by Claude on behalf of @heskew