You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(templates): only record a seed run that actually seeded (#865) (#1026)
CertTemplateSeeder::maybe_seed() wrote the seed-version flag unconditionally,
including when the run created nothing. The whole path fails silently:
Utils::read_file_contents() returns '' for a file it cannot read, seed() then
continues past that definition without a word, and the flag short-circuits
every later run — so a first seed that read nothing left the pool permanently
empty and never retried.
That is not cosmetic. An empty pool is exactly the condition under which
AdminAssetsManager::discover_layout_templates() falls through to the deprecated
legacy html/ glob, so the fallback's stated exit condition ("removed once the
pool seeds on every install") could not be met while this hole existed.
The flag is now written only once pool_has_defaults() confirms the pool holds a
shipped default; otherwise the run leaves it alone and retries on the next
admin request, with an off-by-default Debug::log_admin breadcrumb.
The guard is deliberately narrow — "not empty", not "everything seeded". A
partial seed still populates the picker and keeps the fallback dormant, and
gating on completeness would re-run restore()'s meta writes on every admin
request for as long as one file stayed unreadable.
Also registers the html/ fallback in the CLAUDE.md shim inventory with its
exit condition: evidence-gated, not a versioned deprecation cycle, and split
across two releases so a repaired install is observed before losing the net.
Claude-Session: https://claude.ai/code/session_01D1wR59A8Z7d3QGYnm8q2KQ
Co-authored-by: Claude <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: CHANGELOG.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,6 +8,7 @@ The format follows [Keep a Changelog] (https://keepachangelog.com/en/1.1.0/).
8
8
## [Unreleased]
9
9
10
10
### Fixed
11
+
-**A failed first seed left the certificate-template pool permanently empty** (#865): `CertTemplateSeeder::maybe_seed()` recorded the seed version even when it created nothing — an unreadable seed file is skipped silently — and the flag then short-circuits every later run. The install kept an empty pool and the form editor's layout picker was served by the deprecated legacy `html/` fallback instead. The version is now recorded only once a default is actually in the pool, so a failed seed retries on the next admin request.
11
12
-**The activity log was mostly untranslatable** (#1024): `get_action_label()` fell back to `ucwords()` for any action it did not know, silently rendering untranslated English — 49 of the 64 action keys in use, including every recruitment, privacy and migration event, across the log table, the summary and the CSV export. All 49 now have translated labels, with a guard so a new action cannot rely on the fallback again.
12
13
-**Linking a submission to a user logged the wrong action** (#1024): the call passed the action name where the level belongs, so every link and unlink recorded the generic `submission` while `user_linked`/`user_unlinked` landed in the level slot and was discarded by the level validation. Existing rows keep a label; new ones record the real action.
13
14
-**The update screen said the plugin was untested on your WordPress** (#1022): `GitHubUpdater` hard-coded the compatibility fields WordPress actually reads, so raising `Tested up to` in `readme.txt` (#984) left the shipped value at 7.0 and every 7.1 site was told a verified release was untested; `Requires at least` had drifted the same way (6.2 vs 6.4). The values are now read from the plugin header and `readme.txt` at update-check time, removing the copy that drifted.
Copy file name to clipboardExpand all lines: CLAUDE.md
+14-1Lines changed: 14 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -480,10 +480,23 @@ A full security audit confirmed these hold plugin-wide — keep them that way (t
480
480
481
481
Inventory of the legacy compatibility shims that remain in the code by design (snapshot — re-confirm the location in code before removing; paths and lines change with every refactor, so the table cites files/methods, never line numbers). Removing them requires evidence that no production installation depends on them.
482
482
483
-
_The two shims previously tracked here — the `ensure_legacy_caps_renamed()` v1 pre-6.2.0 cap-rename migration and the pre-4.6.15 orphan-cron cleanup (activator + deactivator/uninstall) — were removed in 6.18.0 (#809) as scheduled; the inventory is currently empty._
483
+
_The two shims previously tracked here — the `ensure_legacy_caps_renamed()` v1 pre-6.2.0 cap-rename migration and the pre-4.6.15 orphan-cron cleanup (activator + deactivator/uninstall) — were removed in 6.18.0 (#809) as scheduled._
484
+
485
+
| Shim | Location | Risk if removed | Why it stays |
486
+
| --- | --- | --- | --- |
487
+
|**Legacy `html/` layout fallback** (#865 phase-4) |`AdminAssetsManager::discover_layout_templates()` → `discover_layout_templates_legacy_glob()` (the `html/*.html` glob when the pool is empty), and `FormEditor`'s by-filename load of `FFC_PLUGIN_DIR . 'html/'` for the `filename` param those entries post. Plus the `html/default_certificate_{1,2,3}.html` files that `.distignore` still ships. |**Medium.** An install whose template pool is empty loses the form editor's layout picker entirely — no defaults to choose, and the by-filename load path that the picker's own entries depend on disappears with it. | The pool is not yet guaranteed non-empty on every install. Until 6.22.0 a first seed that read nothing recorded itself as applied and never retried, permanently emptying the pool (fixed by `CertTemplateSeeder::pool_has_defaults()`); installs that hit it before the fix are repaired by the retry, but only after they take the update. |
484
488
485
489
When a new shim is added, log it here (Shim · Location · Risk if removed · Why it stays), and when a new feature makes one unsafe or inadequate, open a specific sub-issue + a breaking-change banner in the CHANGELOG.
486
490
491
+
#### Exit condition for the `html/` fallback — evidence, not a deprecation cycle
492
+
493
+
Retiring it is **evidence-gated** (the `cpf_rf_encrypted` shape below), *not* a versioned deprecation cycle. The cycle exists for surfaces whose consumers a code scan cannot see — a public method an external integration might call. Both sites here are internal render paths with no hook, no filter and no external caller, so nothing invisible can depend on them; what they depend on is **install state**, which is observable. Two conditions, and note they are genuinely different — conflating them is the mistake that nearly retired this shim early:
494
+
495
+
1.**The pool seeds on every install** — the fallback's own written condition, and the one that was *not* met before 6.22.0. The seeder fix is what makes it hold; verify on a real install that the layout picker is served from the pool.
496
+
2.**Settings → Migrations → `import_legacy_templates` reads 0 pending** — every file an admin dropped into `html/` has been imported. This one has read 0 in production for some time, but it measures *imports*, not seeding, and on its own says nothing about condition 1.
497
+
498
+
Ship the seeder fix and the removal in **different releases** (6.22.0 → 6.23.0): an install with an empty pool must receive the repair, and be observed to have taken it, before losing the safety net. The removal is ⚠ breaking for anyone still relying on a file in `html/`, so it carries a CHANGELOG banner.
499
+
487
500
#### Gathering the evidence to remove a **High**-risk shim
488
501
489
502
Not every High shim is provable by data. Before proposing to build a diagnostic, check whether the evidence already exists — the resolved `cpf_rf_encrypted` case below is the exemplar:
0 commit comments