remove a dangling export, and write down the lockfile question

`pluginPackageJsonExists` was exported for an uninstall hook that was never
wired. an export with no caller is an unfinished thought pretending to be a
feature, so it is gone — and the behaviour it would have served is right
anyway: uninstall KEEPS the plugin directory, so its packages are still
declared and still wanted. only "delete and all its data" removes the
directory, after which the next install regenerates the merged file without it.
the packages linger until then and go on their own.

and the thing the owner asked to come back to: CLAUDE.md's frozen-lockfile rule
reads as universal, and npmDependencies does not follow it. ranges resolving
per machine is what lets two plugins share one sharp, and it was chosen
knowingly — but the codebase now holds two postures on one risk with only one
of them written down. recorded in TODO.md with the three specific things worth
settling rather than left as a feeling that something is inconsistent.
This commit is contained in:
2026-08-20 18:26:27 +00:00
parent 21bedf1418
commit c336042b12
2 changed files with 28 additions and 3 deletions
+17
View File
@@ -731,6 +731,23 @@ outside the example's new README:
- The same trigger would serve `COMMS/command-hq/watcher-loop-proposal.md`, which wants
infrastructure to own a loop rather than a model holding a protocol across turns.
- [ ] **Revisit the frozen-lockfile rule in `CLAUDE.md`, now that plugins resolve at install time.**
Raised by the owner 2026-08-20, to be discussed rather than decided alone.
The rule as written is absolute: `bunfig.toml` sets `frozenLockfile = true`, `bun install` resolves
from `bun.lock` and nothing else, and an unexplained lockfile change in a diff is the signal the
whole arrangement exists to produce. The reasoning is the 2026-08-04 compromise, and it is good.
`npmDependencies` does not follow it. Plugin packages are declared as RANGES and installed with a
plain `bun install` into `plugins/node_modules/`, so they resolve per machine and per install. That
was chosen deliberately — ranges are what let two plugins share one copy of `sharp` — but it means
the codebase now holds two different postures on the same risk, and only one of them is written down.
Worth settling: whether the rule should say "the platform's own dependencies" rather than reading as
universal; whether the merged `plugins/bun.lock` should be committed after all (it would pin the
tree, at the cost of a per-machine file in git); and whether `bun install --frozen-lockfile` in
`plugins/` is achievable once that lockfile exists.
## Backburner
- [ ] **Four foreign keys are over Postgres's 63-character limit and re-create on every `db:push`.**
+11 -3
View File
@@ -1,4 +1,3 @@
import { existsSync } from 'node:fs';
import { join } from 'node:path';
import { PLATFORM_DIR } from '../data-path';
import type { DiscoveredPlugin } from './manifest';
@@ -149,5 +148,14 @@ export function unresolvableDependencies(plugin: DiscoveredPlugin): string[] {
export const anyPluginWantsPackages = (plugins: DiscoveredPlugin[]): boolean =>
plugins.some((p) => Object.keys(p.manifest.npmDependencies ?? {}).length > 0);
/** For the uninstall path: the generated file exists and can be regenerated without the removed plugin. */
export const pluginPackageJsonExists = (): boolean => existsSync(PLUGIN_PACKAGE_JSON);
// ── Uninstall deliberately does NOT shrink this ──
//
// There was an exported `pluginPackageJsonExists` here, written for an uninstall hook that was never
// wired. Removed rather than left dangling — an export with no caller is an unfinished thought pretending
// to be a feature.
//
// The behaviour it would have served is right anyway, for a reason worth stating. Uninstall keeps the
// plugin's DIRECTORY (see the runbook), so `discoverPlugins` still finds it and its packages are still
// declared. Only "Delete … and all its data" removes the directory, and after that the next install of
// any plugin regenerates the merged file without it. So the packages linger until the next install and
// then go, with nothing needing to remember them.