ADR-0036: Download blueprint plugin and unzip archives again on reload¶
Status¶
Accepted (2026-10-16). Amends ADR-0035 for archives only; its checkpoints, overlay copies and other filesystem artifacts are unchanged.
Context¶
ADR-0035 restores volatile blueprint code on a persisted reload. For
installMoodlePlugin/installTheme it copied the downloaded ZIP to
/persist/blueprints/<runtimeId>/<fingerprint>/<i>.zip, and unzip copied its
archive to <i>-files-0.bin, so a reload could reuse the exact bytes offline.
/persist is journaled to IndexedDB per scope, and closing a tab does not delete
that database. Playground sessions are short-lived: people open one, use it and
forget it. Each plugin blueprint therefore left multi-megabyte copies in browser
storage that only a reload of that same tab could ever read.
Problem¶
Restore plugin and archive code on reload without persisting copies of it.
Decision drivers¶
- Store nothing that a short-lived session does not need.
- A reload must still restore the code the database registered, without rerunning install or upgrade scripts on the persisted database.
- Keep the change small; do not touch checkpoints, overlays or other artifacts.
Options considered¶
Option 1: Keep exact ZIP copies (ADR-0035)¶
Offline reloads work and a moving URL cannot change the code. Rejected: the copies outlive the session in IndexedDB.
Option 2: Download the archive again on every boot¶
Selected. The same mechanism already runs on a fresh boot; reload only skips the upgrade (plugins) as before. Nothing is stored.
Option 3: Copy only for pinned/immutable URLs¶
Rejected: needs URL classification for little gain in an ephemeral session.
Evidence¶
src/blueprint/steps/moodle-plugins.js(installViaZipDownload) andsrc/blueprint/steps/filesystem.js(handleUnzip) at77d6b1ewrote the copies;src/blueprint/checkpoint.js(getReloadStep) pointedunzipat them./persistjournaling and IndexedDB lifetime:src/runtime/fs-persistence.jsand AGENTS.md ("Closing a tab is not a guarantee that the underlying IndexedDB data has been deleted").
Decision¶
installViaZipDownload always downloads the archive and never writes it to
/persist; with restoreOnly it still only restores discovery (component cache,
plugin manager, MUC and hook registries) without running upgrade_noncore. The
executor no longer passes pluginArchivePath. On reload unzip runs with its
original data resource, and handleUnzip no longer saves the archive.
Consequences¶
Positive¶
- Plugin blueprints no longer leave archive copies in IndexedDB.
- Less code: no copy, no copy-path plumbing.
Negative¶
- A reload needs the network (and the addon proxy) to restore plugins and archives; offline it fails visibly and the plugin is missing from disk.
- A moving URL (branch archive) can bring newer plugin code than the persisted
database. Moodle detects the higher
version.phpand asks the admin to upgrade; it is not applied silently. - An
unzipwhosedatais avfsfile that no longer exists on reload fails.
Neutral¶
- Copies saved by builds between PR #341 and this change stay in those scopes' journals until the scope is reset; nothing reads them any more.
Risks¶
Transient download failures on reload; the existing 502/503 retry still applies.
Validation¶
Unit tests: reload downloads once, never reads or writes /persist, and still
returns before upgrade_noncore; an unfinished plugin is restored by download;
unzip reload keeps the original resource and saves nothing. Browser test
tests/e2e/blueprint-reload.spec.mjs expects one plugin download per boot.
Follow-up work¶
None planned. Revisit only if offline reloads become a requirement.