# Site Content Repository

Audience: AI coding agents first.

- **ID:** IPZ-SITE-CONTENT-REPOSITORY-2026-08-14
- **Status:** DONE
- **Task:** #80 / SC-REPO-01
- **Source request:** “Return the main thread to SC-REPO-01 now. Delivery-controller repair continues separately and must not distract the main thread; begin dormant Site Content repository work now, but do not publish through the controller until its shared repair is installed and verified.” The owner subsequently directed a clean project-native replacement of the raw preserved-WIP copy in the current isolated worktree: use the published lowercase class-file convention, WPCS naming/docblocks, trusted fixed-table SQL patterns and CRUD helpers where compatible, one compliant test-double object per dedicated fixture file, all seven behavioral tests, the exact clean verification sequence, receipt updates, and a local Task #80 commit.
- **Execution owner:** Current isolated worktree session.
- **Blocker investigation limit:** One complete published-contract/preserved-evidence comparison and 45 minutes; no excluded dependency change or alternate delivery path.

## Exact outcome

Prepare the smallest independently verifiable, dormant repository-only slice: add `SiteContentRepository` and its focused standalone test from the preserved repository evidence, compatible with the already-published Site Content storage schema, catalog, and request-context contracts. The repository may be instantiated by a future consumer but must not be registered, invoked, exposed, or published through the delivery controller until the separate shared repair is installed and verified.

## Acceptance

- The repository addresses only `{$wpdb->prefix}ipz_site_content_translations`, the schema introduced by migration `1.8.0` in published commit `31df4e76d32c30c02e20f3cc33548ab2ea58d1ec`.
- Reads are parameterized and return an array or `null`; creates persist the canonical identity, language, revision, lifecycle, audit, and active-state fields.
- State writes use source-revision and translation-revision compare-and-swap conditions; a successful mutation increments `translation_revision` exactly once.
- Review persistence, overlay deletion, source reconciliation, theme deactivation, and theme reactivation retain the preserved contracts. Theme lifecycle query failures return the same generic `WP_Error` with HTTP status 500 while zero affected rows remain successful no-ops. Reactivating a retained `ready` record whose compiled content was cleared changes it to regeneration-required `needs_update`; other retained statuses are restored. Database failure returns a generic `WP_Error` with HTTP status 500; stale writes return a conflict `WP_Error` with HTTP status 409 and disclose no database error.
- Public input and every stored row are validated fail-closed against the published Site Entity identity contract and schema bounds for language, revisions, lifecycle, active state, and audit fields; malformed values are rejected rather than persisted or returned. Source and target language input is canonicalized to lowercase before lookup, validation, and persistence so alternate case cannot create distinct identities; stored rows with noncanonical language casing are rejected fail-closed.
- Focused tests prove all intended behaviors and assertions: create/read persistence, compare-and-swap predicates, overlay clearing, retained theme status, regeneration-required theme reactivation, generic failures for both lifecycle updates, database failure handling, stale-write handling, and adversarial malformed input/stored-row rejection.
- Test-only doubles follow the published Site Content fixture pattern: dedicated files under `tests/Fixtures/SiteContentRepository/`, one object structure per file, compliant namespaced/prefixed names, and no redefinition of bootstrap-owned `ABSPATH` or `ARRAY_A`. The focused test loads its fixtures with `require_once` and contains only its test class.
- Database SQL uses one canonical fixed table-name source compatible with WordPress 6.0 and WPCS. No user-controlled identifier, inline suppression, PHPCS configuration/baseline change, or weakened lint is permitted.
- No route, REST endpoint, hook, controller registration, translation dispatch/finalization registration, scheduled job, migration, feature flag, UI, runtime write path, or visible behavior is added or activated.
- All applicable deterministic gates are clean with no new suppression, baseline, or warning-ignore entry.

## Allowed paths

- `plugins/international-press-zone/includes/SiteContent/class-sitecontentrepository.php`
- `plugins/international-press-zone/tests/Unit/SiteContent/SiteContentRepositoryTest.php`
- `plugins/international-press-zone/tests/Fixtures/SiteContentRepository/class-sitecontentrepositorywpdbstub.php`
- `plugins/international-press-zone/tests/Fixtures/SiteContentRepository/class-sitecontentrepositorywperror.php`
- This plan and the two plan indexes only.

## Excluded paths

Everything else, including the published schema migration/database registry, catalog, request-context contracts, `BlockTemplateTranslationAdapter`, `SiteContentTranslationResourceHandler`, controller code, REST/API code, admin assets, flags, runtime wiring, and shared delivery infrastructure.

## Immutable and dormant activation state

The published schema, catalog, and request-context contracts are immutable dependencies for this slice; consume their published definitions and do not copy or alter preserved-WIP variants. This slice is dormant compatible library code, not a feature: no service container, plugin bootstrap, hook, route, or job may construct it. Its default and only state is unreachable. No feature flag is appropriate because there is no runtime behavior to gate.

## Preserved evidence and dependency seams

- Read-only evidence: `/home/user/Projects/Press.zone/wordpress/wp-content/.worktrees/ipz-site-content-persistence-v2/plugins/international-press-zone/includes/SiteContent/SiteContentRepository.php` and `tests/Unit/SiteContent/SiteContentRepositoryTest.php`.
- Published catalog seam: `includes/SiteContent/class-sitecontentcatalog.php`; it supplies canonical entity identity, theme scope, source revision, and block-content discovery, but this slice must not call it.
- Published request-context seam: `includes/SiteContent/class-sitecontentrequestcontext.php`; future resource handling owns context authorization and must remain outside this slice.
- Published storage seam: `includes/Migrations/Migration008SiteContentStorage.php` and `includes/Core/Database.php`; use their existing table/column contract without changing it.
- Future adapter seam: `BlockTemplateTranslationAdapter` may consume repository records only in SC-ADAPTER-01 after this slice is verified. Future handler seam: `SiteContentTranslationResourceHandler` consumes the repository only in SC-HANDLER-01 after adapter completion.
- Separate delivery-controller repair seam: shared installed infrastructure, not this repository. It is a publication dependency only and never an implementation dependency for Task #80.

## Execution and blocker limit

1. Re-read the published schema, catalog, request-context contracts, preserved repository/test evidence, project autoload and focused test conventions.
2. Confirm the preserved repository's table/column assumptions exactly match the published schema; reconcile only an actual compatibility mismatch within the allowed paths.
3. Add the repository and focused test without runtime wiring.
4. Run PHP syntax on both files, the focused PHPUnit test, exact/candidate-aware PHPCS, PHPStan for the owned source, and `git diff --check`.
5. Obtain independent code/security review and repair confirmed Task #80 findings only.
6. Record the local verification receipt. Do not commit, land, publish, or invoke the controller while its separately-owned repair lacks installed, green regression proof.

Investigation limit: one complete published-contract/preserved-evidence comparison and 45 minutes. On a genuine incompatibility or unavailable deterministic gate, record exact command/output and stop; do not alter excluded dependencies, bypass gates, or substitute a delivery path.

## Rollback

Before any later publication, remove only the two repository-owned files as one unit and rerun the focused absence/syntax checks. Do not drop or roll back the already-published compatible schema. After publication is permitted, a later corrective commit must revert these two files together; no destructive database operation is allowed.

## Gates

- `php -l` for both owned PHP files.
- Focused PHPUnit execution of `tests/Unit/SiteContent/SiteContentRepositoryTest.php` using the repository's established PHPUnit entrypoint.
- Exact PHPCS and candidate-aware `php tools/phpcs-baseline.php check` for both owned paths.
- PHPStan with `phpstan.neon.dist` for `includes/SiteContent/SiteContentRepository.php`.
- `git diff --check`.
- Independent code/security review limited to the allowed paths.
- Publication gate, deliberately deferred: the shared delivery-controller repair must be independently reviewed, installed in its live path, and have its installed-entrypoint regression green.

## Current receipt

The raw preserved-WIP copy was replaced with the project-native lowercase production class file and WPCS snake_case API. The repository uses only the trusted fixed `ipz_site_content_translations` table suffix, CRUD insertion, prepared value-bearing reads and fixed-shape atomic compare-and-swap updates. Test doubles now live as one namespaced object per dedicated fixture file; bootstrap-owned constants are reused; the focused test contains only its test class. The CamelCase source duplicate and obsolete combined fixture were removed as Task #80 orphans.

Confirmed review findings were repaired without widening dormant scope. Both theme lifecycle updates now return a generic HTTP-500 `WP_Error` on database failure while zero affected rows remain successful no-ops. Reactivation maps retained `ready` status to regeneration-required `needs_update` because deactivation clears compiled content, while preserving other retained statuses. Public create/read/mutation inputs enforce the published `SiteEntityIdentity` contract plus schema bounds for identity keys, language codes, SHA-256 revisions, positive revisions and audit identifiers, and theme scopes; fetched rows validate identity, language, revision, lifecycle, active state, audit identifiers, and timestamps before return and fail closed when malformed. Source and target language inputs now canonicalize to lowercase before validation, lookup, and persistence; alternate-case lookups use the same canonical database identity, and noncanonical stored casing is rejected. Clean latest receipts: uncached PHPCS and candidate-aware PHPCS returned no output; focused PHPUnit passed `12 tests, 63 assertions`; PHPStan reported `No errors`; both changed PHP files passed syntax; `git diff --check` passed; and the dormant no-registration/consumer search returned no matches. No suppression, baseline/config change, runtime wiring, registration, consumer, or unrelated source edit was introduced.

## Next executable action

The canonicalization repair and this delivery receipt are included in the current candidate commit. The commit hook passed every applicable check; the focused production checks and tests recorded above are clean. Hook notices are expected repository-environment omissions: no root JavaScript package, no root Composer lock, and no installed Trivy binary. The installed shared delivery-controller regression is green, so Task #80 is ready for immediate publication while remaining dormant.
