From e10496afe74a990d9ec7a83fc95c0c2cfb3740c0 Mon Sep 17 00:00:00 2001 From: Mischa Date: Tue, 7 Jul 2026 08:32:20 +0200 Subject: [PATCH] =?UTF-8?q?docs(solutions):=20BUG-001=20part=202=20?= =?UTF-8?q?=E2=80=94=20deleteAll=20doesn't=20rebuild=20page-tree=20index?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document why deleteAll() alone left a freshly-posted entry 404-ing on its edit-prefill API lookup (regular-pages index keyed on config->checksum, which survives a create under cache.check.method:folder) and why the fix adds Cache::invalidateCache(). Note the H1/M8 travelling:true coverage gap as a tracked residual. Cross-link from bugs-and-fixes.md. Code review F8 (learnings) + F5 (residual-gap tracking). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01Mpdu3Dt1iVoozHwAMyjrbn --- ...teall-doesnt-invalidate-page-tree-index.md | 101 ++++++++++++++++++ docs/working/bugs-and-fixes.md | 6 ++ 2 files changed, 107 insertions(+) create mode 100644 docs/solutions/integration-issues/grav-deleteall-doesnt-invalidate-page-tree-index.md diff --git a/docs/solutions/integration-issues/grav-deleteall-doesnt-invalidate-page-tree-index.md b/docs/solutions/integration-issues/grav-deleteall-doesnt-invalidate-page-tree-index.md new file mode 100644 index 0000000..53c7766 --- /dev/null +++ b/docs/solutions/integration-issues/grav-deleteall-doesnt-invalidate-page-tree-index.md @@ -0,0 +1,101 @@ +--- +title: "cache.deleteAll() doesn't rebuild the page-tree index — a freshly-posted entry 404s when opened for editing" +date: 2026-07-07 +category: integration-issues +module: cache-on-save +problem_type: integration_issue +component: plugin +severity: high +symptoms: + - "A just-posted journal entry is written to disk but the API 404s on it (GET /api/v1/pages{route})" + - "Opening the entry you just created for editing shows 'This entry no longer exists — it may have been deleted'" + - "The entry DOES appear in the trip feed, but the edit prefill fetch can't find it until the next unrelated cache bump" + - "Intermittent — only bites when the page-tree index survives the create" +root_cause: incomplete_setup +resolution_type: code_fix +related_components: + - documentation + - development_workflow +tags: + - grav + - cache + - forms + - page-tree +--- + +# `cache.deleteAll()` doesn't rebuild the page-tree index + +## Context — this is BUG-001 Part 2 + +[BUG-001](../../working/bugs-and-fixes.md) ("new entry not visible after form +submission") was fixed by wiring `$this->grav['cache']->deleteAll()` into the +`cache-on-save` plugin's `onFormProcessed` hook. That made new entries appear in +the trip feed immediately. It was **not the whole story**: `deleteAll()` drops +the Doctrine store (rendered-page cache, feed HTML, etc.) but does **not** force +Grav to rebuild its **regular-pages index**. + +The gap only surfaced once the shared `/post` form gained an **edit mode** +(`?edit=`), whose prefill does `GET /api/v1/pages{route}`. On a fresh +create that request would 404 — so the owner opening the entry they had *just* +posted saw "This entry no longer exists." + +## Root cause + +Grav's regular-pages index is keyed on: + +``` +md5(dirs + folderHash + config->checksum() + lang) // Pages::buildRegularPages +``` + +With `cache.check.method: folder` (our setting), the `folderHash` component does +not necessarily change when a new child folder is added inside an existing +tree — so the **index key stays the same** and the stale index (missing the new +entry) is reused. `deleteAll()` clears cache *stores* but does not change any of +the inputs to that key, so the tree is not rebuilt. The new page is on disk and +in the feed (which re-reads children), but the **API lookup by route** resolves +through the cached index and 404s. + +## Fix + +Add a second invalidation step alongside `deleteAll()`: + +```php +use Grav\Common\Cache; +// ... +$this->grav['cache']->deleteAll(); +Cache::invalidateCache(); // touch(system.yaml) → bumps config->checksum() +``` + +`Cache::invalidateCache()` is lightweight and idempotent — it `touch()`es +`system.yaml`, calls `clearstatcache()` and `opcache_reset()` (verified in Grav +core `Cache.php`). Touching `system.yaml` bumps `config->checksum()`, which +changes the index key, so the tree rebuilds on the next request and the new +entry becomes resolvable by route. + +### Latch it — the hook fires 4× per submit + +`onFormProcessed` fires once per `process:` action, and `post-form.md` has four +(`add_page`, `upload`, `message`, `reset`). Without a guard the +`deleteAll()` + `invalidateCache()` pair runs four times per post (a full store +wipe + `system.yaml` touch each time). Gate it with a once-per-request latch +(`$cacheInvalidated`), the same pattern already used for photo reconciliation +(`$photosReconciled`). See `user/plugins/cache-on-save/cache-on-save.php`. + +## How to verify + +1. Post a new entry via `/post`. +2. From the trip feed, click the new card's **Edit** link. +3. The form prefills with the entry's title/body — no "no longer exists" banner. + +Regression test: `tests/ui/post/edit-mode.spec.js` **ES1** (create → open the +feed card's Edit link → change title + body → Save → assert on disk). + +## Residual coverage gap (tracked, not fixed here) + +`tests/ui/home/home.spec.js` **H1** and `tests/ui/maps/maps.spec.js` **M8** +require `site.travelling: true` to exercise the active-trip home feed + home GPX +map. The committed local `site.yaml` runs `travelling: false` (owner's testing +config, intentionally not committed as `true`), so both specs **skip loudly** +with a reason rather than fail misleadingly. They validate whenever the site is +in travelling mode. This is a known gap in this environment, not a silent hole — +provisioning `travelling: true` in a dedicated test config would close it. diff --git a/docs/working/bugs-and-fixes.md b/docs/working/bugs-and-fixes.md index cc128f4..d09f66f 100644 --- a/docs/working/bugs-and-fixes.md +++ b/docs/working/bugs-and-fixes.md @@ -9,6 +9,12 @@ Backlog of confirmed bugs with root cause analysis and implementation spec for t **Status:** fixed 2026-06-18 **Reported:** 2026-06-18 +> **Follow-up (2026-07-07):** `deleteAll()` alone does not rebuild Grav's +> page-tree *index*, so once `/post` gained an edit mode a freshly-posted entry +> would 404 on its edit-prefill API lookup. Fixed by also calling +> `Cache::invalidateCache()`. See +> [`docs/solutions/integration-issues/grav-deleteall-doesnt-invalidate-page-tree-index.md`](../solutions/integration-issues/grav-deleteall-doesnt-invalidate-page-tree-index.md). + ### Symptom After submitting a new post via `/post`, the entry page file is created correctly on disk but does not appear in the `/trips//dailies` feed or in the Grav Admin panel until the cache is manually flushed.