Skip to content

Challenge reload uses stale ESM module after same-name package upgrade #124

Description

@tomcasaburi

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:

  1. Install and exercise test-challenge@1.0.0.
  2. While the daemon remains running, replace it with test-challenge@2.0.0 under the same package name, with observably different challenge text/answer.
  3. Let the install command trigger reload, or call /api/challenges/reload.
  4. Assert the response reports v2 and a publication exercises v2 behavior without restarting the daemon.
  5. 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.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions