Refresh audit of all 13 docs/solutions learnings against the current codebase. Core guidance verified accurate everywhere; three docs had reference drift: - dual-repo-submodule-workflow: point worktree setup/teardown at the make worktree-new/worktree-rm targets (manual procedure misses .worktree-env isolation) - docker-exec-root-owned-bind-mount-files: tracked-plugin list now includes entry-actions; fix-perms description matches actual target - grav-plugin-config-without-code-wont-enable: 3-category model's custom-in-repo list now includes entry-actions CONCEPTS.md: add Container, Content repo, Outer repo, Pin, Env tree, Remote-only plugin; refresh Active Trip (switching is one setting now). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0195b3cDdMeize2Mm1FgC2aU
8.0 KiB
title, date, category, module, problem_type, component, severity, symptoms, root_cause, resolution_type, related_components, tags
| title | date | category | module | problem_type | component | severity | symptoms | root_cause | resolution_type | related_components | tags | ||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Grav plugin won't enable because its code is missing while its config persists in the env tree | 2026-07-05 | integration-issues | git-sync | integration_issue | tooling | high |
|
incomplete_setup | dependency_update |
|
|
Grav plugin won't enable because its code is missing while its config persists in the env tree
Problem
After remediating an unrelated git-sync secret leak on prod (intotheeast.com, Grav 2.0.7 on a DirectAdmin/Apache shared host), the operator went to re-enable the git-sync plugin. Setting enabled: true in its config had no effect: the plugin would not appear as enabled in the Grav Admin plugins UI, and toggling it on manually in the browser did not take either. It looked fully "configured" — the config file was right there — but the plugin was inert and sync never ran.
The trap is that the plugin's config file existed (in the per-host env tree at user/env/intotheeast.com/config/plugins/git-sync.yaml), so any inspection that reads only config concluded the plugin was present and just needed enabling. The actual problem was one layer down: the plugin's code was missing from user/plugins/git-sync/. A Grav plugin cannot load or enable without its code on disk, no matter what its config says.
Symptoms
- The git-sync plugin does not appear as enabled, and cannot be enabled, in the Grav Admin UI — despite
enabled: truebeing present in its config. - Toggling
enabledin config, or flipping the toggle in the Admin UI, produces no working plugin. Sync does not run. - The plugin's config file DOES exist (in the per-environment tree
user/env/intotheeast.com/config/plugins/git-sync.yaml), so the plugin appears "present" whenever only the config is inspected — masking the real state.
What Didn't Work
- Setting / ensuring
enabled: truein the git-sync config. No effect. Config was never the problem. - Enabling the plugin manually in the Admin UI. The toggle wouldn't take.
Both failed attempts operate on the config layer. But the plugin's code was absent from user/plugins/git-sync/, and Grav can't load a plugin without its code. Grav (and any tooling that reads the config tree) reports a plugin as "configured" purely from the presence of its config file, which masks the absence of code. Diagnosing and poking at the config layer could never fix a missing-code problem — and guessing at config changes before running a simple ls on the plugin directory cost real time here.
Solution
First, run the decisive diagnostic on the server — confirm whether the plugin code actually exists before touching config:
ls -la $WEBROOT/user/plugins/git-sync/ # empty/absent => missing code, reinstall
(In this project, do that via a make target or an ssh one-liner the user runs — never raw SSH by the assistant. All server ops go through make remote-*.)
With the directory confirmed empty/absent, reinstall the plugin's code via GPM:
make remote-gpm-install-prod PKG=git-sync # GPM fresh-installs Git Sync v3.4.4
That make target runs, on the server:
php bin/gpm index -f && php bin/gpm install git-sync -y && php bin/grav clearcache
The install output read "Preparing to install Git Sync [v3.4.4] ... Success!" — a fresh install, not "already installed." That fresh-install line is exactly what confirmed the code had been absent all along. After the reinstall plus cache clear, the plugin enabled and sync worked.
Why This Works
Grav resolves a plugin from two independent locations:
- Code at
user/plugins/<name>/— installed by GPM. Noteuser/plugins/is gitignored (/plugins/*) and is NOT tracked by the content repo. - Config — the tracked
user/config/plugins/<name>.yamland/or the per-hostuser/env/<host>/config/plugins/<name>.yaml.
These two can desync: config can exist with no code behind it. Config alone makes the plugin look present to any tool that only reads config, but the plugin stays inert until its code is on disk. GPM reinstall restores the code; clearcache makes Grav re-scan and pick it up.
A project-specific amplifier made this worse: git-sync is a remote-only, GPM-managed plugin. It is deliberately NOT in plugins.txt, so make install-plugins and the normal make remote-install flow do not restore it. Only an explicit php bin/gpm install git-sync (via make remote-gpm-install-prod PKG=git-sync) does. So when its code goes missing, it does not self-heal through the standard install path — you must reinstall it explicitly.
How the code went missing here is unconfirmed. It happened around the git-sync secret-leak remediation, but the exact step that wiped user/plugins/git-sync/ was not established — don't assume a specific cause.
Prevention
- Check the code layer before the config layer. When a Grav plugin "won't enable" and config toggles do nothing, FIRST verify the code exists:
ls user/plugins/<name>/on the server. Config-without-code is the failure class; the empty directory is the tell. - Enumerate both layers in all locations when diagnosing. Plugins have a code layer (
user/plugins/<name>/) and a config layer, and on prod the config can live in the env tree (user/env/<host>/config/plugins/<name>.yaml) and persist completely independently of the code. Remember: onceuser/env/<host>/exists, Grav Admin writes ALL config there, so always check bothuser/config/...and the env path (env wins). - Know which plugins are remote-only. The 3-category model: GPM-via-
plugins.txt(admin2 / api / flex-objects), custom-in-repo (cache-on-save / story-blocks / entry-actions), and remote-only (git-sync — never inplugins.txt). Remote-only plugins are NOT restored by the standard install/content flows, so reinstall them explicitly via GPM after any operation that could have wipeduser/plugins/. - Diagnose actual state before proposing config fixes. An
lsis cheaper than a guess. Establishing that the code was missing would have pointed straight at the reinstall instead of a round of config poking.
Related
docs/solutions/integration-issues/stale-grav-version-blocks-api-plugin-install.md— closest sibling. Same family (a plugin non-functional on prod, resolved by a GPM install + cache clear), same 2026-07-04/05 cutover context, sameplugins.txt/make remote-*/ GPM machinery. Distinct trigger: there, GPM refuses to offer the plugin because the installed core is below the version floor; here, the plugin's code folder is simply missing while its config persists (config-without-code desync). Two different ways a plugin ends up absent/inert on prod.docs/solutions/architecture-patterns/git-sync-secret-exposure-and-tracked-file-boomerang.md— same module (git-sync) and explains why the config survived without code: Grav Admin writesgit-sync.yamlinto the per-environment treeuser/env/<host>/config/plugins/, which is untracked/gitignored and not part of the plugin package. The orphaned config here is the flip side of that env-tree behavior.docs/solutions/conventions/grav-plugin-config-must-be-tracked-override.md— establishes the plugin code (GPM/gitignored) vs config (tracked override / env tree) split that this bug exploits. This doc is a concrete failure of that split going the other way: config present (in the env tree), code absent.docs/working/git-sync-notes.md— operational notes on git-sync's per-environment tree, wheregit-sync.yamllives server-only. Context for where the orphaned config resided.