# Connect cleanup implementation plan

## Scope and guardrails

Work only in `plugins/international-press-zone` from this worktree. Load the plugin expert instructions plus `wordpress-php-integration`, `settings-management`, `admin-panel-fullstack`, and `frontend-javascript` before editing. Preserve the clean starting worktree and never bypass hooks, add placeholder behavior, skip/delete tests, alter the connect REST contract, expose/log credentials, edit `tests/e2e/`, run Playwright, or touch the abandoned plugin trees or backend. The existing `docs/plans/2026-08-14-connect-e2e.md` remains the separate E2E record.

Use the per-file runner while repairing: it runs PHPUnit classes and procedural standalone tests in separate PHP processes and deliberately fails if a path remains in `tests/known-failing.txt` after it starts passing. Remove each baseline entry only after its repaired test passes. Retain the header comment block and no path entries at completion.

## 1. Make all eleven baselined tests real, runnable coverage

1. Establish a tight test loop with `tests/run-unit-tests.sh <affected-test>` for each repaired file, followed by the full `tools/factory-gate.sh test`. Do not report a pass until the command actually exits successfully.
2. Replace each obsolete-contract assertion with coverage of the current implementation rather than deleting or weakening the test:
   - In `tests/unit/Compatibility/ACFFieldPolicyTest.php`, retain a focused field-policy assertion but derive the expected current Compatibility-layer policy/shape from the active ACF compatibility implementation, not the retired shape.
   - In `tests/unit/ContentFilterLegacyContentTest.php`, replace the assertion for removed legacy filter markup with assertions for the current filter output and safety/translation behavior the old regression was protecting.
   - In `tests/unit/Translation/LegacyGenerateAuthorizationStandaloneTest.php`, stop reaching the removed `TranslationsController::resolveLegacyGenerationContext()` path and exercise the current generation authorization/context entry point, preserving denial of unauthorized generation and acceptance of valid current context.
   - In `tests/unit/Translation/TranslateJobsCompletionStandaloneTest.php`, assert completion through the controller's current bulk-string/job completion callback contract, including its current result/status handling, rather than the removed callback signature.
   - In `tests/unit/Translation/TranslationFinalizerJobIdentityStandaloneTest.php`, assert job identity through the active finalizer/dispatcher resource lookup and cleanup behavior rather than the removed `TranslationFinalizer::$resource_handlers` property.
3. Repair fixture construction without replacing assertions with skips:
   - Add a guarded `wp_parse_url()` compatibility stub to the shared unit-test fixture/bootstrap used by the affected standalone tests (or to the existing common standalone fixture required by each, if that is the project’s actual shared bootstrap), delegating to `parse_url()` and matching WordPress’s `false|array` contract. Keep it guarded so a live WordPress implementation wins.
   - Update `tests/unit/Performance/AdminDataPolicyLocalizationTest.php` and `tests/unit/Performance/TranslationInvalidationTest.php` to load that fixture and construct their localization/invalidation fixtures completely; retain their actual policy and invalidation assertions.
   - Update `tests/unit/Translation/SiteRegistrarIdentityStandaloneTest.php` to load the same stub before `SiteIdentity.php`, then retain canonical site identity and registration assertions. Do not change `SiteIdentity::url()` merely to accommodate a broken harness.
4. Make the three integration-labelled tests execute meaningful coverage without a live bootstrap. Prefer narrowly scoped WordPress-function stubs and existing production collaborators over localhost-dependent execution, so they remain deterministic in the factory runner:
   - Refactor `tests/integration/AutoPublishStatusTest.php` into an executable isolated test of the current auto-publish status transition with stubs for the exact post/status APIs it uses; verify both the enabled success transition and the disabled/non-qualifying no-op path.
   - Refactor `tests/integration/Core/ContentManagerTest.php` into executable isolated cases around the current `Core\ContentManager` behavior, providing only the required WordPress option/content hooks and retaining all formerly skipped behavior assertions.
   - Refactor `tests/integration/SettingsIdempotencyTest.php` into an executable isolated test of repeated Settings application/persistence using an in-memory option API fixture; prove repeat calls leave the same effective settings and do not create duplicate side effects.
   If one of these tests already has a supported Podman bootstrap mechanism, use it only if it is deterministic and available to `factory-gate`; never substitute an unconditional `markTestSkipped()` or an environment-dependent silent pass.
5. For every repaired test, remove its adjacent reason comment and path from `tests/known-failing.txt` only after its individual command is green. Finish with the file’s four-line header block only, then run `tools/factory-gate.sh test` and record the observed `GATE_EXIT=0`.

## 2. Rename the site identity/update service layer

1. Before renaming, search the active first-party source, tests, factory tooling, configuration, and generated manifests for both `InternationalPressZone\\Licensing` and `includes/Licensing`; distinguish active inputs from historical docs and Composer vendor output. Also inventory every active `licens` spelling so the final source tree does not leave stale identifiers, comments, user-facing copy, tool manifests, baselines, or test paths. Do not modify vendor by hand.
2. Move `includes/Licensing/SiteIdentity.php` and `includes/Licensing/UpdateChecker.php` to `includes/SiteServices/`, change their namespaces and package annotations to `InternationalPressZone\SiteServices`, and preserve their URL validation, client job-ID, credential-store, update transport, signature/package verification, and hook behavior exactly.
3. Update all first-party imports and fully qualified references to `SiteServices\SiteIdentity`/`SiteServices\UpdateChecker`, including the API controllers, Integration/Translation services, Connect service, plugin bootstrap/registration, and the standalone test fixtures that require or namespace-shadow `SiteIdentity`. Rename test directories/files currently named `Licensing` where they describe these services, and update their `require_once` paths and test namespaces.
4. Update every repository-maintained consumer of the old filesystem path: universal-admin surface manifest, contracts, PHP token audit, manifest generator and generated manifest, PHPStan baseline path/namespace matchers, PHPCS baseline paths, and any test/tool list. Run `composer dump-autoload` so Composer regenerates `vendor/composer/autoload_classmap.php` and `autoload_static.php` with only the new class/path names; never hand-edit those generated files.
5. Treat retired legacy credential cleanup separately from service naming: remove obsolete license-key naming only when its migration purpose is no longer required, or preserve a necessary one-time legacy option cleanup without leaving a named identifier/comment/string in active source (for example by using the project’s established migration-key representation). Confirm no connect credential is added to responses, markup, logging, or update payloads.
6. Run case-insensitive searches over the active plugin inputs, then the required acceptance command:
   ```bash
   grep -rin 'licens' includes/ admin/src/ --exclude-dir=node_modules
   ```
   Resolve every non-GPL-license result. Verify the rename with `tools/factory-gate.sh php-syntax`, `tools/factory-gate.sh phpstan`, and `tools/factory-gate.sh test`, each observed at `GATE_EXIT=0`.

## 3. Reformat and decompose the connect/account SPA pages without behavioral drift

1. Use neighbouring class-based pages (notably `languages.js` and `settings.js`) as the formatting reference. Keep the existing two imports in the same order and retain the DOM utility approach (`el`, `clear`, text-node rendering); do not introduce a framework, redesign markup, new UI copy, or a new API abstraction.
2. Expand `admin/src/pages/onboarding.js` into one statement per line with normal constructor/render/handler method layout. Extract only presentation helpers that preserve the exact existing node tree and call sequencing (package button/card creation, welcome rendering, checkout mount, status polling view, language-complete view). Preserve all state values and transitions; popup origin/source/state validation; `begin`, `complete`, `checkout`, `status`, and onboarding-complete REST paths and payloads; Stripe dynamic import/options; five-minute deadline; interval cleanup; popup/embedded-checkout destruction; and the exact current caught-error destinations/messages. Do not render or log an API key, client secret, OAuth code, verifier, or other credential.
3. Expand `admin/src/pages/account.js` into normal class methods and small DOM helpers for disconnected/connected account rendering and account details. Preserve the exact `/connect/status`, `/connect/portal`, and `/connect/disconnect` requests; status fallbacks; button/link markup and text; confirmation semantics; new-tab handling including close-on-error; HTTPS-only portal URL check; hash redirect; and error redraw behavior. Keep credentials absent from DOM and logs.
4. Confirm semantic equivalence by reviewing the diff for the same REST method/path/payload strings, rendered element/attribute order and values, state assignments, branches, and cleanup behavior. Then run `tools/factory-gate.sh admin-lint` and `tools/factory-gate.sh build`, recording only observed `GATE_EXIT=0` results, and run:
   ```bash
   awk 'length($0) > 140 {print FILENAME":" NR}' admin/src/pages/onboarding.js admin/src/pages/account.js
   ```
   Require no output.

## 4. Final integrated validation and delivery evidence

1. Recheck scope with `git diff --check`, ensure no E2E/backend/abandoned-plugin files changed, and verify `tests/known-failing.txt` contains no non-comment test paths.
2. Run `tools/factory-gate.sh all` once on the completed candidate. Do not claim success if a subgate merely prints a passing-looking line: record its process result and the final observed `GATE_EXIT=0`.
3. Re-run the license spelling search and the long-line awk check on the final tree. Review the diff to ensure SiteServices behavior is a rename only and the two JS pages retain behavior and secret non-disclosure.
4. Commit through the normal pre-commit hook (never `--no-verify`) only after all required observed gates are green; include the actual commands/results in the delivery record.
