What
reprint-ui/reprint-import.php's run_local_activation() (lines ~536–600) walks wp-content/{plugins,themes,mu-plugins} and collapses Atomic's versioned subdirectory layout in place — for each entry that contains exactly one child whose name matches ^\d+(\.\d+)*([\-+][\w.+]+)?$, it either re-points the symlink one level down or moves the version's contents up.
Move this out of the wizard and into the importer, surfaced through RuntimeManifest.
Why
The wizard hard-codes platform-specific knowledge about how Atomic lays out plugin/theme/mu-plugin trees. Layering-wise it doesn't belong there:
flat-docroot is the layer that produces the versioned tree by faithfully copying Atomic's layout — so it should also be the layer that makes the result bootable on a non-Atomic host.
WpcloudHostAnalyzer already declares Atomic-specific cleanups via paths_to_remove. Layout repair is the same shape of declaration.
- Any embedder running
reprint.phar pull against an Atomic source today gets a working SQLite but a broken plugin/theme tree unless they replicate the wizard's collapse block.
How
-
Add a new field to RuntimeManifest for declaring auto_prepend_file scripts to ship — e.g. array<string, string> keyed by absolute target path with the script contents as the value. The runtime applier (NginxFpmApplier / PhpBuiltinApplier / PlaygroundCliApplier) writes each script to disk and emits a matching php_admin_value[auto_prepend_file]=... INI directive (or whatever the equivalent is for that runtime).
-
Have WpcloudHostAnalyzer populate the field with a small shim — call it wpcloud-versioned-layout-prepend.php — that on every request walks the three plugin/theme/mu-plugin trees and creates flat symlinks for any single-child versioned-subdirectory pattern. Use a sentinel file (__atomic_layout_resolved next to the prepend) so successive requests are an is_file() no-op.
-
Drop the collapse block from reprint-ui/reprint-import.php. The wizard goes back to owning only Playground-specific concerns (SQLite move, mu-plugin glue).
Why auto_prepend_file and not a mu-plugin
A mu-plugin would only run after WordPress has already located the mu-plugins directory — so it can't help an embedder whose own mu-plugins follow the same versioned schema (which is a real possibility for sites that ship multiple mu-plugins via the Atomic layout).
auto_prepend_file runs before WordPress boots at all, so the version-collapse symlinks are already in place by the time wp-settings.php walks WP_CONTENT_DIR/mu-plugins.
Acceptance criteria
- New manifest field accepts a map of paths → script contents.
- All three current runtime appliers write the scripts and configure
auto_prepend_file correctly for their runtime model.
WpcloudHostAnalyzer ships the layout shim by default; other analyzers leave the field empty.
- The wizard's
run_local_activation() no longer contains the versioned-plugin walk.
- Existing E2E coverage of an Atomic source import (Playground wizard scenario at
tests/e2e/tests/import-50-playground-wizard.test.js) still passes — the imported site renders without 404s on plugin/theme assets.
Context
Surfaced as item #3 in the self-review of #185. Items 1, 2, 4, 5, 6, 8 from that review have been addressed in #188, #189, #190, #191. Item 7 (a separate "name the /wordpress convention" cleanup) is the only other follow-up.
What
reprint-ui/reprint-import.php'srun_local_activation()(lines ~536–600) walkswp-content/{plugins,themes,mu-plugins}and collapses Atomic's versioned subdirectory layout in place — for each entry that contains exactly one child whose name matches^\d+(\.\d+)*([\-+][\w.+]+)?$, it either re-points the symlink one level down or moves the version's contents up.Move this out of the wizard and into the importer, surfaced through
RuntimeManifest.Why
The wizard hard-codes platform-specific knowledge about how Atomic lays out plugin/theme/mu-plugin trees. Layering-wise it doesn't belong there:
flat-docrootis the layer that produces the versioned tree by faithfully copying Atomic's layout — so it should also be the layer that makes the result bootable on a non-Atomic host.WpcloudHostAnalyzeralready declares Atomic-specific cleanups viapaths_to_remove. Layout repair is the same shape of declaration.reprint.phar pullagainst an Atomic source today gets a working SQLite but a broken plugin/theme tree unless they replicate the wizard's collapse block.How
Add a new field to
RuntimeManifestfor declaringauto_prepend_filescripts to ship — e.g.array<string, string>keyed by absolute target path with the script contents as the value. The runtime applier (NginxFpmApplier/PhpBuiltinApplier/PlaygroundCliApplier) writes each script to disk and emits a matchingphp_admin_value[auto_prepend_file]=...INI directive (or whatever the equivalent is for that runtime).Have
WpcloudHostAnalyzerpopulate the field with a small shim — call itwpcloud-versioned-layout-prepend.php— that on every request walks the three plugin/theme/mu-plugin trees and creates flat symlinks for any single-child versioned-subdirectory pattern. Use a sentinel file (__atomic_layout_resolvednext to the prepend) so successive requests are anis_file()no-op.Drop the collapse block from
reprint-ui/reprint-import.php. The wizard goes back to owning only Playground-specific concerns (SQLite move, mu-plugin glue).Why
auto_prepend_fileand not a mu-pluginA mu-plugin would only run after WordPress has already located the mu-plugins directory — so it can't help an embedder whose own mu-plugins follow the same versioned schema (which is a real possibility for sites that ship multiple mu-plugins via the Atomic layout).
auto_prepend_fileruns before WordPress boots at all, so the version-collapse symlinks are already in place by the timewp-settings.phpwalksWP_CONTENT_DIR/mu-plugins.Acceptance criteria
auto_prepend_filecorrectly for their runtime model.WpcloudHostAnalyzerships the layout shim by default; other analyzers leave the field empty.run_local_activation()no longer contains the versioned-plugin walk.tests/e2e/tests/import-50-playground-wizard.test.js) still passes — the imported site renders without 404s on plugin/theme assets.Context
Surfaced as item #3 in the self-review of #185. Items 1, 2, 4, 5, 6, 8 from that review have been addressed in #188, #189, #190, #191. Item 7 (a separate "name the
/wordpressconvention" cleanup) is the only other follow-up.