Problem
Replacing an installed challenge package while bitsocial daemon is running can leave the daemon executing the old package code even though:
bitsocial challenge list reports the new version;
POST /api/challenges/reload returns ok: true and reports the new version.
A daemon restart then makes the new behavior appear.
Root cause
loadChallengesIntoPKC() imports every installed package from the same entry URL on every reload:
const imported = await import(pathToFileURL(entryPath).href);
Node caches ESM modules by URL. The installer atomically replaces the package at the same destination path, so reloading the unchanged URL returns the previously evaluated module. The endpoint therefore updates metadata from the new package.json while assigning the old cached factory back to PKC.challenges[name].
The current integration test only installs a new package name, so it does not exercise replacement of an already-imported package.
Scope / ownership
This should be solvable in bitsocial-cli only:
- bitsocial-cli owns package installation and
loadChallengesIntoPKC();
- bitsocial-cli owns
/api/challenges/reload;
- the loader already assigns factories into the mutable
PKC.challenges registry.
No pkc-js API change appears necessary.
The maintained Bitsocial challenge packages currently build to a single bundled dist/index.js, so importing the entry under a stable content/version-specific URL is sufficient for the supported packages. Prefer a content-derived key over an ever-changing nonce so repeated reloads of unchanged code do not create unnecessary module instances. If arbitrary multi-file package graphs are intended to be hot-reloadable too, document the bundled-entry constraint or use a generation-specific real path.
Proposed fix
- Make
loadChallengesIntoPKC() import changed challenge entry code under a URL that changes when the installed package contents change.
- Keep repeated reloads of unchanged package contents stable/idempotent.
- Ensure the reload response cannot claim a new version while the previous factory remains active.
- Preserve startup loading and installation of entirely new challenge names.
Regression test
Extend test/cli/challenge-integration.test.ts:
- Install and exercise
test-challenge@1.0.0.
- While the daemon remains running, replace it with
test-challenge@2.0.0 under the same package name, with observably different challenge text/answer.
- Let the install command trigger reload, or call
/api/challenges/reload.
- Assert the response reports v2 and a publication exercises v2 behavior without restarting the daemon.
- Optionally reload again without changing files and assert behavior remains v2.
Acceptance criteria
- Same-name challenge upgrades take effect without restarting
bitsocial daemon.
- Reported package version and active factory behavior agree.
- The regression test fails against the current loader and passes with the fix.
- No pkc-js modification is required unless the investigation uncovers a concrete missing API.
Problem
Replacing an installed challenge package while
bitsocial daemonis running can leave the daemon executing the old package code even though:bitsocial challenge listreports the new version;POST /api/challenges/reloadreturnsok: trueand reports the new version.A daemon restart then makes the new behavior appear.
Root cause
loadChallengesIntoPKC()imports every installed package from the same entry URL on every reload:Node caches ESM modules by URL. The installer atomically replaces the package at the same destination path, so reloading the unchanged URL returns the previously evaluated module. The endpoint therefore updates metadata from the new
package.jsonwhile assigning the old cached factory back toPKC.challenges[name].The current integration test only installs a new package name, so it does not exercise replacement of an already-imported package.
Scope / ownership
This should be solvable in bitsocial-cli only:
loadChallengesIntoPKC();/api/challenges/reload;PKC.challengesregistry.No pkc-js API change appears necessary.
The maintained Bitsocial challenge packages currently build to a single bundled
dist/index.js, so importing the entry under a stable content/version-specific URL is sufficient for the supported packages. Prefer a content-derived key over an ever-changing nonce so repeated reloads of unchanged code do not create unnecessary module instances. If arbitrary multi-file package graphs are intended to be hot-reloadable too, document the bundled-entry constraint or use a generation-specific real path.Proposed fix
loadChallengesIntoPKC()import changed challenge entry code under a URL that changes when the installed package contents change.Regression test
Extend
test/cli/challenge-integration.test.ts:test-challenge@1.0.0.test-challenge@2.0.0under the same package name, with observably different challenge text/answer./api/challenges/reload.Acceptance criteria
bitsocial daemon.