docs(solutions): BUG-001 part 2 — deleteAll doesn't rebuild page-tree index
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 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Mpdu3Dt1iVoozHwAMyjrbn
This commit is contained in:
+101
@@ -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=<route>`), 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.
|
||||||
@@ -9,6 +9,12 @@ Backlog of confirmed bugs with root cause analysis and implementation spec for t
|
|||||||
**Status:** fixed 2026-06-18
|
**Status:** fixed 2026-06-18
|
||||||
**Reported:** 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
|
### Symptom
|
||||||
|
|
||||||
After submitting a new post via `/post`, the entry page file is created correctly on disk but does not appear in the `/trips/<active_trip>/dailies` feed or in the Grav Admin panel until the cache is manually flushed.
|
After submitting a new post via `/post`, the entry page file is created correctly on disk but does not appear in the `/trips/<active_trip>/dailies` feed or in the Grav Admin panel until the cache is manually flushed.
|
||||||
|
|||||||
Reference in New Issue
Block a user