acorn-stack
Use when changing how Acorn boots or is wired on a Sage theme, Acorn mu-plugin or Radicle root - Application::configure(), withProviders(), withRouting(), ACORN_BASEPATH, base_path(), provider register() vs boot(), where providers are registered (functions.php, config/app.php, composer.json extra.acorn.providers), config publishing, optimize:clear, "'acorn' is not a registered wp command". For generating providers, blocks or composers use acorn-scaffolding; for hook timing use wp-hooks-lifecycle; for unexplained breakage use roots-debugging.
- License 许可
- MIT
- 所属仓库
- hekivo/kivo-wise
- 最近更新
- 2026-11-08
- Stars
- ★ 14
- Forks
- 0
01 前言 Front Matter
- name
- acorn-stack
- description
- Use when changing how Acorn boots or is wired on a Sage theme, Acorn mu-plugin or Radicle root - Application::configure(), withProviders(), withRouting(), ACORN_BASEPATH, base_path(), provider register() vs boot(), where providers are registered (functions.php, config/app.php, composer.json extra.acorn.providers), config publishing, optimize:clear, "'acorn' is not a registered wp command". For generating providers, blocks or composers use acorn-scaffolding; for hook timing use wp-hooks-lifecycle; for unexplained breakage use roots-debugging.
02 资源文件 4
- 参考文档references/boot-and-paths.md6.5 KB
- 参考文档references/caches-and-config.md5.0 KB
- 参考文档references/providers-and-container.md5.6 KB
- 参考文档references/views-composers-components.md4.7 KB
03 SKILL.md 正文
Acorn stack (theme, mu-plugin, project root)
Acorn is the same library in all three profiles. What changes is where it boots, which directory it treats as base_path(), and where providers are listed. Read the profile, namespace and Acorn base path from the session context before touching anything.
Runner placeholders come from the session context: {{runner.wp}} (WP-CLI), {{runner.acorn}} (WP-CLI acorn subcommand), {{runner.composer}}, {{runner.node}}, {{runner.test}}, {{runner.cache}} (the project's cache-clear wrapper, if any). On a Lando project the defaults are lando wp, lando wp acorn, lando composer, a Node tooling command such as lando npm or lando yarn, and whatever cache tooling .lando.yml defines. Never run WP-CLI, Composer, PHP or Node on the host for project work.
When to use
- Adding or changing a service provider, binding, View Composer, Blade component or config file.
- Deciding where a piece of logic lives (provider, composer, component, bootstrap file, service class).
- Acorn does not boot, boots with the wrong base path, or
{{runner.acorn}}is missing. - A change is not picked up and you suspect an Acorn cache (config, routes, views, packages, ACF Composer manifest).
Not for: scaffolding new artifacts (acorn-scaffolding), hook placement and timing (wp-hooks-lifecycle), Livewire (acorn-livewire), routes and middleware (acorn-routes-api), triage of errors (roots-debugging).
Inputs required
- Profile (
sage-theme,acorn-mu-plugin,radicle-app), the Acorn base path and the PSR-4 namespace mapped toapp/(session context; confirm with the command in step 0). - The Composer project that requires
roots/acorn(theme folder, plugin folder, or project root). Itscomposer.jsondecides the namespace and which packages are discovered. - What is being added or changed, and whether it needs the container (DI, config) or is a plain WordPress hook.
Profiles at a glance
| Concern | sage-theme |
acorn-mu-plugin |
radicle-app |
|---|---|---|---|
| Base path | Theme folder, inferred from the theme's composer.json |
Plugin folder: the plugin must define ACORN_BASEPATH or pass a path to Application::configure($path) |
Project root: ACORN_BASEPATH defined in bedrock/application.php |
| Boot call | functions.php calls Application::configure()->withProviders([...])->boot() while the theme loads (after setup_theme, before after_setup_theme) |
Main plugin file defers the boot to after_setup_theme, often through a small configurator class |
A committed mu-plugin boots on after_setup_theme |
| Provider list | withProviders([...]) in functions.php (Sage ships App\Providers\ThemeServiceProvider) |
withProviders([...]) in the boot code or configurator class |
composer.json → extra.acorn.providers |
| Namespace | App\ |
Project namespace from the plugin's composer.json |
App\ |
| Blade templates for WordPress pages | SageServiceProvider filters the template hierarchy |
No Sage filters: the plugin hooks template_include / *_template_hierarchy itself, or only renders views from blocks, shortcodes and routes |
Root resources/views; the theme folder holds only index.php and style.css |
{{runner.acorn}} available |
Only while the theme is active | Always (mu-plugins cannot be deactivated) | Always |
| Usually installed | ACF Composer, Poet only if required | ACF Composer, Poet, Livewire are common | No ACF, no Poet, no Livewire by default |
Deep dive: references/boot-and-paths.md.
Procedure
Commands: write every command with the runner value from the session context filled in (for example lando wp acorn optimize:clear), never a bare wp/composer/php/npm, never the placeholder, never "prefix with lando if you use it". Commands that run in CI or a deploy script go in a separate block labelled with where they run.
0) Confirm the runtime facts, do not assume them
{{runner.wp}} eval 'echo base_path(), PHP_EOL, app()->getNamespace(), PHP_EOL, storage_path(), PHP_EOL;'
{{runner.acorn}} about
If base_path() points at the theme on an acorn-mu-plugin project, the plugin is missing ACORN_BASEPATH (see Failure modes). If {{runner.acorn}} prints 'acorn' is not a registered wp command, the theme or plugin that boots Acorn is not active or crashed before booting.
1) Decide where the code goes
| Logic | Location | Native mechanism |
|---|---|---|
Plain add_action / add_filter, no dependencies |
Bootstrap files: Sage app/setup.php, app/filters.php; mu-plugin bootstrap files required by the main file; Radicle theme config or a provider |
WordPress Plugin API |
| Hooks that need services or config | A provider's boot() |
Illuminate\Support\ServiceProvider |
| Container bindings | A provider's register() |
singleton, bind, instance |
| Business logic, API clients | app/Services/ (or a domain folder), bound in a provider when it needs config |
Container auto-resolution of type-hinted constructors |
| Data for an existing template | View Composer in app/View/Composers/ |
Auto-discovered by Acorn's ViewServiceProvider |
| Reusable markup with props | Blade component (app/View/Components/ + resources/views/components/, or anonymous view only) |
Blade <x-…> convention |
| Editor-placed section | Block (see acorn-scaffolding) |
ACF Composer or register_block_type |
| Settings that differ per environment | config/<file>.php reading env() |
Acorn config repository |
Hook placement details per profile live in wp-hooks-lifecycle. Search app/, the bootstrap files and the existing providers before adding a class.
2) Providers
- Generate with
{{runner.acorn}} make:provider ExampleServiceProvider(or followacorn-scaffolding). register(): bindings only.boot(): hooks, view composers, anything that resolves services. Neveradd_actioninregister()or in the constructor.- Register it where this profile reads providers (table above). The generator only appends to a bootstrap providers file (
<bootstrap path>/providers.php, by default insidestorage/framework/) when one already exists, which is rare, so assume it did not. - Base class:
Illuminate\Support\ServiceProviderfor feature providers. Only a Sage theme's main provider extendsRoots\Acorn\Sage\SageServiceProvider(it adds the template-hierarchy filters). Do not extend it in an mu-plugin or Radicle project. - A provider that throws during
register()/boot()is skipped, the exception is reported to the log, and it is re-thrown after boot only inlocal/development/testingweb requests. On production the feature silently disappears: check the log.
Full rules, discovery and examples: references/providers-and-container.md.
3) Views, composers, components
- Views resolve from
config('view.paths'), by defaultresources/viewsunder the base path. - View Composers: extend
Roots\Acorn\View\Composer, live anywhere underapp/View/Composers/, setprotected static $views = [...]. Acorn discovers them; no manual wiring. Data fromwith()does not overwrite data already passed to the view;override()does. - Blade components: class components in
app/View/Components/resolve by name (<x-product-card>); anonymous components are files inresources/views/components/.config/view.php→componentsis only for aliases. - Escape once:
{{ $value }}already escapes; never wrapesc_*()inside it (wp-security-escaping).
Details: references/views-composers-components.md.
4) Config, storage, publishing
- Every file in the base path's
config/is loaded; Acorn ships defaults forapp,cache,database,logging,viewand others, so publish only what you change:{{runner.acorn}} vendor:publish --tag=<tag>or{{runner.acorn}} acorn:init storage. storage_path()is<base>/storagewhen that folder exists, otherwisewp-content/cache/acorn(created on demand). Compiled views, caches and logs live there.- An mu-plugin with no
config/,resources/orstorage/folder of its own falls back to the active theme's folder of that name before the default. Create the folders in the plugin.
5) Caches: know which layer you are clearing
| Layer | Files | Rebuild | Clear |
|---|---|---|---|
| Config, routes | <storage>/framework/cache/{config,routes-v7}.php |
{{runner.acorn}} optimize |
{{runner.acorn}} optimize:clear |
| Provider and package manifests | <storage>/framework/cache/{services,packages}.php |
{{runner.acorn}} package:discover |
delete the files |
| Compiled Blade | <storage>/framework/views/ |
{{runner.acorn}} view:cache |
{{runner.acorn}} view:clear |
ACF Composer manifest + generated block.json |
<storage>/framework/cache/acf-composer/ |
{{runner.acorn}} acf:cache (also run by optimize) |
{{runner.acorn}} acf:clear (also run by optimize:clear) |
| Laravel cache store | per config/cache.php (file, Redis…) |
n/a | {{runner.acorn}} cache:clear (also run by optimize:clear) |
| WordPress object cache | Redis/Memcached drop-in | n/a | {{runner.wp}} cache flush |
Rules:
- Treat
optimize:clearandoptimizeas a pair.optimize:clearalone also deletes the ACF Composer manifest and flushes the Laravel cache store; at least one production mu-plugin returned 500 on every request until the manifest was rebuilt. - While a cache exists, edits to what it holds are ignored: cached config ignores
config/*.phpand.envedits; a cached ACF Composer manifest ignores edits tofields()and to block properties. - Prefer
{{runner.cache}}when the project defines one; it usually chains the steps in the right order.
Details: references/caches-and-config.md.
Verification
{{runner.wp}} eval 'echo base_path(), PHP_EOL, app()->getNamespace(), PHP_EOL;'
{{runner.wp}} eval 'var_dump(app()->getProvider(App\Providers\ExampleServiceProvider::class) !== null);' # use your namespace
{{runner.acorn}} list # your commands and generators are present
{{runner.acorn}} route:list # when routes changed
{{runner.cache}} # or: {{runner.acorn}} optimize:clear && {{runner.acorn}} optimize
{{runner.test}}
Then load the affected page and read storage/logs/ (or wp-content/cache/acorn/logs/) for Skipping provider lines. Report what you ran and what you saw.
Failure modes
| Symptom | Cause | Fix |
|---|---|---|
'acorn' is not a registered wp command |
The theme or plugin that boots Acorn is inactive, or it fatals before boot() |
Activate the theme (sage-theme); check the PHP error log; confirm vendor/autoload.php exists in the Composer project that owns Acorn |
base_path() is the theme on an mu-plugin project |
No ACORN_BASEPATH and no path passed to configure(); inference falls through to the theme's composer.json or app/ |
Define ACORN_BASEPATH in the main plugin file before booting, or call Application::configure($pluginPath) |
| Provider never runs | Not listed where this profile reads providers, or skipped after an exception | Add it to withProviders() / extra.acorn.providers / config/app.php; search the log for Skipping provider |
Package provider not found after composer require |
Stale package manifest, or the package lives in a Composer project Acorn does not scan | {{runner.acorn}} package:discover; mu-plugin packages are found only because the base path points at the plugin |
| Composer data missing in the view | Class outside app/View/Composers/, does not extend Roots\Acorn\View\Composer, or $views does not match the view name |
Fix the class; {{runner.acorn}} view:clear |
Config or .env edit ignored |
Config cache from optimize |
{{runner.acorn}} config:clear then optimize |
| ACF field or block property edit ignored | ACF Composer manifest still holds the old definition | {{runner.acorn}} acf:cache (or optimize) |
500 on every request right after optimize:clear |
Project depends on the ACF Composer manifest or a cached config | {{runner.acorn}} optimize; afterwards always run the pair |
Hook registered in register() behaves oddly |
Container not booted; other providers not registered yet | Move it to boot() |
| Sage template filters missing in an mu-plugin | SageServiceProvider belongs to the theme profile |
Hook template_include in the plugin's own provider |
Escalation
Stop and ask when: the base path or namespace in the session context disagrees with step 0; a provider must be ordered relative to others and you cannot tell why; the change needs optimize behaviour on a remote environment; a fix would edit wp-config.php, .env, bedrock/*.php or files under vendor/; or the profile is none of the three.
<!-- verified:
vendor/roots/acorn/src/Roots/Acorn/Configuration/Concerns/Paths.php:16-27 — inferBasePath order APP_BASE_PATH, ACORN_BASEPATH, theme composer.json, theme app/, closest composer.json, fallback;
Paths.php:85-101 — findPath tries app path, basePath(type), get_theme_file_path(type); :115-129 storage fallback wp-content/cache/acorn;
Application.php:69-84 (acorn 6.2.0) — configure() builds withPaths/withKernels/withEvents/withCommands/withProviders/withMiddleware/withExceptions;
ApplicationBuilder.php:140-150,167-170 — withProviders merges into bootstrap providers path; boot() calls bootAcorn immediately (no hook);
Application/Concerns/Bootable.php:41-57,79-110 — WP-CLI registers the acorn command only when Acorn booted;
Application.php:247-275 — package discovery scans base path, content root, template, stylesheet and active_plugins dirs;
PackageManifest.php:62-97 — reads extra.acorn / extra.laravel providers including the root composer.json;
Application.php:320-339 — config app.providers + package providers registered; :307-313,367-391 — failing providers skipped and reported;
View/ViewServiceProvider.php:169-197 — composers auto-discovered in app/View/Composers; View/Composer.php:61-90 — with() does not overwrite view data, override() does;
Illuminate/Foundation/Console/OptimizeClearCommand.php:59-70 — clear runs config, cache, compiled, events, routes, views + package tasks;
log1x/acf-composer 3.4.6 src/Providers/AcfComposerServiceProvider.php:67-73 — optimizes acf:cache / acf:clear; src/Composer.php:109-117 — fields read from manifest when cached;
refs/roots-sage/functions.php:35-39 — Sage boots at include time with withProviders; refs/roots-docs/acorn/installation.md:126-128 — inactive booter error;
wp/wp-settings.php:705,741-757 — setup_theme, theme functions.php, after_setup_theme order (WP 7.1.3)
-->
04 同仓库更多 Skills
wp-rest-api
Use when building, extending, or debugging WordPress REST API endpoints/routes: register_rest_route, WP_REST_Controller/controller classes, schema/argument validation, permission_callback/authentication, response shaping, register_rest_field/register_meta, or exposing CPTs/taxonomies via show_in_rest.
wp-abilities-api
Use when working with the WordPress Abilities API (wp_register_ability, wp_register_ability_category, /wp-json/wp-abilities/v1/*, @wordpress/abilities) including defining abilities, categories, meta, REST exposure, and permissions checks for clients.
wp-security-escaping
Use when writing or reviewing code that outputs data, handles input or checks permissions in a WordPress + Acorn project - Blade {{ }} vs {!! !!}, esc_html, esc_attr, esc_url, wp_kses_post, "& in the URL", double escaping, sanitize_text_field, nonces, current_user_can, map_meta_cap, permission_callback, $wpdb->prepare, whereRaw, file uploads, escaping ACF values, secrets in .env. For route middleware use acorn-routes-api; for ACF validation use acf-native; for Livewire actions use acorn-livewire.
wp-phpstan
Use when configuring, running or fixing PHPStan in a WordPress project that uses Acorn - phpstan.neon, baseline, szepeviktor/phpstan-wordpress stubs, larastan, level raising, "Cannot access property on mixed", get_field() types, paths for app/. For Pint or Mago formatting use testing-wp; for runtime errors use roots-debugging.
wp-performance
Use when a WordPress page, query or request is slow or needs profiling - TTFB, slow or N+1 queries, Query Monitor, Server-Timing, wp profile and wp doctor, autoloaded options, Redis object cache hit rate, transients, WP_CACHE_KEY_SALT. Adds the runner and Redis notes on top of the official procedure. For Eloquent query design use acorn-data; for PHP type errors use wp-phpstan.
wp-hooks-lifecycle
Use when adding, moving or debugging WordPress actions and filters in an Acorn project - add_action/add_filter priority and accepted_args, where a hook belongs (setup or filters files vs a provider boot()), load order (mu-plugins, plugins_loaded, after_setup_theme, init), when Acorn boots per profile, hooks that never fire on Acorn routes, REST or Livewire requests (template_redirect, wp_head), save_post firing twice in the block editor, the_content priorities. For ACF hooks use acf-native; for provider structure use acorn-stack.