Skip to main content
← Back to list
01Issue
BugShippedSwamp CLIPublic
Assigneeshammz

Relationships

#2639 Extension restore drops the lockfile entry's channel and leaks the parent's expectedChecksum into dependency installs

Opened by hammz · 9/28/2026· Shipped 9/28/2026

Summary

Restoring pulled extensions from the lockfile has two correctness bugs. Both affect every repo today, managedConfig or not. This is unit U1 of the swamp-club#2612 split (see the planning ripples there) and has no dependencies.

1. A restore drops the entry's channel

extension install, repo upgrade, serve's extension.install handler and the auto-resolver all restore an entry without passing its channel on:

  • createExtensionInstallDeps builds the install context without a channel (src/cli/create_extension_install_deps.ts:92-103, where createInstallContext(_name, _version) ignores its arguments). extensionInstall passes only the name and version (src/libswamp/extensions/install.ts:241-248).
  • The auto-resolver's install context has no channel either (src/cli/auto_resolver_adapters.ts:289-302).

installExtension then calls downloadArchive and getChecksum with no channel. It also rewrites the entry with channel: ctx.channel, which is undefined (src/libswamp/extensions/pull.ts:1363-1375). So restoring a beta or rc entry queries the registry without its channel, and the lockfile loses the channel. Later extension update and list then treat it as a stable-channel entry.

2. A parent's expectedChecksum leaks into its dependencies

installExtension recurses into dependencies with { ...ctx, depth: ctx.depth + 1, channel: depChannel } (src/libswamp/extensions/pull.ts:1421-1425). The spread copies the parent's expectedChecksum. That field is set by extensionInstall (install.ts:247-249) and by the auto-resolver (auto_resolver_adapters.ts:299-301).

A dependency that is missing from the lockfile is then checked against the parent's archive checksum. The check fails with an integrity error (pull.ts:899-909), after the parent's lockfile entry was already written. The command reports a failure while leaving the parent installed and the dependency missing.

Proposed fix

  • ExtensionInstallDeps.createInstallContext takes the entry's channel, and the CLI factory sets it. extensionInstall passes entry.channel. Check the serve handler (src/serve/handlers/admin_handlers.ts:705) and repo upgrade (src/cli/commands/repo_init.ts:209), which share the factory.
  • The auto-resolver passes the pinned entry's channel.
  • The dependency recursion clears expectedChecksum for the child. The child's channel stays the one resolved for the dependency, as today.
  • While in that code: InstallExtensionService's collision rollback rewrites the prior entry without channel and pulledAt (src/libswamp/extensions/install_extension_service.ts:406-415). Carry both, so a rollback doesn't drop the channel either.

Testing

  • Unit tests in install_test.ts and create_extension_install_deps_test.ts: restoring a beta-channel entry passes the channel to downloadArchive and getChecksum, and the rewritten entry keeps it.
  • Unit test in pull_test.ts: a parent restored with expectedChecksum installs a dependency missing from the lockfile without an integrity error, and the dependency's own download is not checked against the parent's checksum.
  • Unit test in auto_resolver_adapters_test.ts: a pinned beta entry is re-installed with its channel.
  • Unit test in install_extension_service_test.ts: a collision rollback keeps channel and pulledAt.

Compatibility

No output, flag or lockfile-format changes. Existing entries without a channel behave as before.

02Bog Flow
✓OPEN✓TRIAGED✓IN PROGRESS✓SHIPPED+ 1 MOREASSIGNED+ 5 MOREREVIEW+ 7 MOREPR_MERGED+ 2 MORESESSION_SUMMARIZED

Shipped

9/28/2026, 11:04:20 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
hammz assigned hammz9/28/2026, 10:21:09 PM

Sign in to post a ripple.