# ACF page cache — request

Audience: AI coding agents first.

**Goal:** Make ACF Fields admin route paint canonical skeleton/browser snapshots immediately, receive permission-safe WordPress snapshots, and lazily revalidate without rebuilding fresh ACF graph on every navigation.

**Context:** Fresh worktree `/home/user/Projects/Press.zone/wordpress/wp-content/.worktrees/acf-page-cache`, branch `wt/acf-page-cache`, base `f3739082c` from freshly fetched `origin/master`. Current `admin/src/pages/fields-translate.js` performs direct `API.request()` with `cache: 'no-store'`, replaces shared route skeleton, and reloads complete graph after mutation. Canonical seams already exist in `admin/src/utils/DataStore.js`, `admin/src/data/adminStores.js`, `admin/src/components/DataRegion.js`, `includes/Performance/AdminDataPolicy.php`, `includes/API/RestResponseCache.php`, and `includes/Performance/CacheInvalidation.php`. Follow `docs/specs/2026-08-12-acf-page-cache-design.md` and `.claude/agents/expert.md`.

**Files:**

- Modify `admin/src/utils/DataStore.js` — optional validator for hydrated and network snapshots before acceptance/persistence.
- Modify `admin/src/data/adminStores.js` — bind descriptor validator and preserve canonical single-store/mutation invalidation behavior.
- Modify `admin/src/pages/fields-translate.js` — replace direct graph fetch/loading shell with `getDataStore('acf-fields:all')`, one `DataRegion`, keyed graph reconciliation, and explicit mutation rollback.
- Modify `includes/Performance/AdminDataPolicy.php` — ACF GET descriptor and exact PATCH invalidation mappings.
- Modify `includes/API/RestResponseCache.php` — bounded deferred-refresh eligibility for ACF graph GET.
- Modify existing ACF/plugin integration class selected by current hook ownership — narrow native ACF/global-option/availability generation invalidation; no unrelated post-save invalidation.
- Modify `admin/tests/data-store.test.js`, `admin/tests/admin-stores.test.js`, and `admin/tests/acf-field-table-accordions.test.js` — validation, policy binding, reconciliation, mutation, and race coverage.
- Create `admin/tests/acf-fields-loading.test.js` — loading/cache lifecycle contract.
- Modify `tests/unit/Performance/AdminDataPolicyLocalizationTest.php` and `tests/unit/API/RestResponseCacheTest.php` — policy, cache, refresh, identity, invalidation, and race coverage.
- Add or extend focused PHP ACF integration tests in existing matching test file — native writer hook coverage.
- Modify focused Playwright spec only when needed to prove deterministic cache/page behavior through canonical IPZ E2E process.

**Contract:**

- Existing API: `getDataStore(key, params = {}, options = {}): Store`.
- Extend store options with optional `validate(data): data`. Validator may return normalized validated data or throw. Existing stores without validator MUST retain current behavior.
- Validation MUST run before hydration acceptance and before network `_setData()`, persistence, metadata assignment, or listener notification. Invalid hydrated entry MUST be evicted. Invalid network result MUST reject revalidation and retain prior valid snapshot.
- Descriptor key: `acf-fields:all`.
- GET endpoint: `/translations/fields`; no query fields; browser TTL `300000`; persist `true`; user server scope; server soft TTL `120`; server hard TTL `300`; tag `acf-fields`; no live fields.
- Mutation mappings: `PATCH /translations/fields/default` and `PATCH /translations/fields/{field_key}` invalidate `acf-fields` only after success.
- Deferred-refresh route: `/international-press-zone/v1/translations/fields` through existing `RestResponseCache` refresh contract.
- Page store binding MUST be `getDataStore('acf-fields:all')`; NEVER invent `getAdminStore`.
- Region graph key MUST be stable (`acf-fields-graph` or equivalent constant independent of payload identity).
- Region view MUST implement existing `renderItem()`, `updateItem()`, `keyOf()`, optional `removeItem()`, and `isBusy()` contracts. `updateItem()` MUST reconcile settings/groups/nested fields/layouts/controls by stable ACF keys; it MUST NOT clear/repaint whole subtree.
- `removeItem()` MUST disconnect graph-owned observers/listeners. `isBusy()` MUST cover queued mutation and active control interaction.

**Behavior:**

- Cold navigation keeps route skeleton until first valid snapshot.
- Valid memory/localStorage snapshot paints before network. Stale snapshot remains visible through deferred revalidation.
- WordPress fresh/stale snapshots bypass graph builder according to canonical permission-safe cache lifecycle. Hard miss runs builder.
- Unchanged payload preserves DOM identity. Changed payload preserves disclosure snapshot, active search, scroll, logical focus, controls, and observers. Deleted focused key falls back to owning surviving summary or page heading.
- Successful PATCH invalidates canonical tag, joins store's one authoritative in-flight refresh, and keeps control busy until reconciliation. NEVER launch duplicate reload.
- Failed PATCH explicitly restores captured pre-mutation control value even when returned/cached payload compares equal. Never persist optimistic state.
- Invalidation generations out/aborts older graph GET. Old completion MUST NOT repopulate browser/server cache after mutation rotation. Two serialized PATCH operations reconcile in order.
- Rotate `acf-fields` after successful global field-policy option writes; native ACF field/group create/update/delete/trash and relevant local-JSON synchronization; ACF activation/deactivation or availability transition. Use narrow hooks. Runtime PHP field registration without write event may remain bounded by hard TTL.
- Cold error shows retry. Background error retains visible graph and exposes retry. Authorization/error/unsafe-header/nonce/non-GET responses never cache.
- Destroy/remount aborts request and removes subscriptions, timers, follow-ups, listeners, and observers.

**Out of scope:**

- ACF graph/REST response schema changes.
- Field-policy semantic changes.
- Accordion visual redesign or new CSS.
- New cache service, page-specific localStorage, polling, bypass headers, or refresh scheduler.
- Unrelated admin routes or refactors.
- dev1 deployment is explicitly owner-authorized and REQUIRED for this task. Publish exact verified `0.9.41` artifact, then verify installed version/archive/admin asset identity and provide exact preview route.

**Acceptance:**

- Run from `plugins/international-press-zone/admin`: `npm test -- --run tests/data-store.test.js tests/admin-stores.test.js tests/acf-field-table-accordions.test.js tests/acf-fields-loading.test.js`; expected PASS with no warnings: validation precedes persistence, skeleton/cache paint ordering, bounded stale refresh, unchanged DOM, state preservation, equal-payload rollback, mutation/race/cross-tab/teardown contracts.
- Run from plugin root: `vendor/bin/phpunit tests/unit/Performance/AdminDataPolicyLocalizationTest.php tests/unit/API/RestResponseCacheTest.php <focused-acf-integration-test>`; expected PASS with no warnings: exact policy/TTL/scope/tags, permission-safe hit/stale/miss, identity partitioning, successful-only REST/native invalidation, and old-generation write rejection.
- Run from `admin`: `npm run lint:js && npm run build && npm test`; expected clean ESLint, production build, all Vitest tests PASS, no warnings.
- Run from plugin root: `composer test && composer phpcs && composer phpstan`; expected all applicable PHP gates PASS cleanly. Address every warning/report.
- Run `git diff --check`; expected no output and exit 0.
- Run independent Sol security/correctness review on changed trust/cache boundaries; expected no unresolved finding.
- Run canonical IPZ E2E skill through `e2e-remote` on authorized self-contained local/buildbox WordPress in Chromium and Firefox. Deterministically assert skeleton → memory/localStorage → `X-IPZ-Cache` miss/hit/stale order, request counts, warm navigation without skeleton regression, visible data during refresh, stable accordion/search/focus state, and mutation invalidation. Screenshots alone do not satisfy acceptance.
- Land/release/deploy through canonical project chain after all gates. Verify installed version/archive/admin asset identity only; never browser-test dev1.

**Preserved WIP:** Shared checkout and prior `fix/content-generate-all` WIP MUST remain untouched. Work only in `/home/user/Projects/Press.zone/wordpress/wp-content/.worktrees/acf-page-cache`.

**Status:** ACTIVE — landing and deployment requested; current session is executing the canonical delivery chain.

**Task IDs:** ACF-CACHE-LAND, ACF-CACHE-DEPLOY, ACF-CACHE-IDENTITY.

**Current receipt:** Fresh `origin/master` worktree at base `f3739082c`. Implementation is contiguous through browser-coverage commit `c5e652925`: backend policy/cache `f60bb467d`; store validation `6920a9211`; native invalidation corrected through `6dd982704`; page integration/teardown/lazy refresh through `772680376`; shared DataRegion seam `75f5a8233`; full-suite fixture alignment `687e8506b`; focused E2E route coverage `c5e652925`. Full canonical admin gate passes build, lint, 34 files, and 569 tests. Focused PHP suites pass 88 tests/506 assertions; configured suite passes 28/127 but does not discover focused suites. PHPStan passes 151 files; changed-file PHPCS baseline and `git diff --check` pass. Independent adversarial review approved with no blocker. Canonical `e2e-remote` run on registered `debian3` passed 8/8 focused ACF tests: Chromium 4, Firefox 4, zero failed/skipped, 21.8s browser duration after 59s stack readiness, exit 0, no console/runtime warnings. Teardown left no remote stack/browser process. Artifacts: `tests/e2e/test-results/junit.xml` and `tests/e2e/playwright-report/`; report directory also contains failure artifacts from a preceding unrelated broad diagnostic run (32 passed/6 unrelated dashboard-migration failures), not from focused green ACF run. Documentation/receipt commit: `e26547d03`. Earlier frozen-controller preflight halted because shared main held 14 tracked WIP files; no landing mutation occurred.

**Next executable action:** Commit this execution receipt, run frozen `ship.sh land wt/acf-page-cache ...`, then execute the canonical International Press Zone release/deployment path and verify installed version/archive/admin-asset identity on dev1 without browser testing.
