type: meta

slug: ipz-advanced-settings

base_branch: fix/content-generate-all

gate0_mode: strict

preset: codex


type: goal

text: Port translate-press-zone's full advanced settings surface into international-press-zone: translation tone, auto-publish, debug mode/detailed logging, and exact/contains exceptions with bulk import — wired end to end (model, REST, admin UI, backend sync).


type: methodology

text: Maximally parallel waves of small subagent tasks with strictly disjoint file ownership; no two tasks may touch the same file. Code review on every task. Fine-grained decomposition so a small coder model can complete each task without cross-file reasoning.


type: intent

text: translate-press-zone is superseded by international-press-zone; every setting that still lives only in the old plugin must exist in the new one so the old tree can be retired.


type: direction

verbatim: i want this plan to be paralelized as much as possible to get fast results, run with paralel luna subagents for non conflicting tasks in a way they wont touch each others work


verbatim: I want all the advanced settings to be on international-press-zone, since translate plugin is supreseded by it.


verbatim: for luna, a small model, you must break down the tasks to smaller sub tasks that luna can handle easily


verbatim: check translate-press-zone for full functionality, see how it works.


verbatim: never modify the abandoned translate-press-zone or multilingual-press-zone trees — read-only reference only


note: Backend rejects a regex match_type: api/src/routes/exceptions.ts pins z.enum(['exact','contains']). TPZ's UI offered regex; IPZ must not.


note: api/src/routes/translate.ts has NO exceptions field in translateSchema/bulkTranslateSchema — inline exceptions are stripped by zod. Enforcement is server-side from Prisma rows keyed by userId, so backend sync is mandatory for the feature to work at all.


note: Backend exceptions are keyed per user account, not per site: all sites on one license share one exception list.


note: Canonical debug option is presszone_international_debug_mode (read by Translation/Logger.php:41). ipz_debug_mode is a dead duplicate written by API/SettingsController and is migrated away. ipz_log_queries is a separate concern and stays.


note: Settings wire shape is FLAT in both directions; the nested branch in admin/src/pages/settings.js:494-508 is dead legacy handling. Do not introduce nesting.


note: No luna preset exists in the harness engine and v2 dropped runconfig overrides (v2/seats.js loads presets from the read-only engine dir only). Run uses preset codex: coder gpt-5.6-terra-medium, reviewer gpt-5.6-sol-low.


note: REST controllers are registered in includes/Core/Plugin.php around line 650; that file is owned exclusively by the ExceptionsController task.


note: E2E must run via ~/.claude/bin/e2e-remote — local headless launches exit 97 by design.


type: task

seat: coder

tier: medium

desc: Read docs/plans/2026-08-04-ipz-advanced-settings.md Task 1 (Settings model — exceptions sync state) for the full contract. Add get/set/clear_exceptions_synced_at() to Translation\Settings and clear the timestamp on every local exceptions mutation. Acceptance: podman exec devzone-wordpress php -r 'require "/var/www/html/wp-load.php"; $s = \InternationalPressZone\Translation\Settings::instance(); $s->set_exceptions([]); $s->set_exceptions_synced_at(); echo (is_string($s->get_exceptions_synced_at()) ? "SET_OK " : "SET_FAIL "); $s->add_exception("Acme Corp","exact"); echo (($s->get_exceptions_synced_at() === null) ? "DIRTY_OK" : "DIRTY_FAIL"), PHP_EOL;' → SET_OK DIRTY_OK. Commit only: plugins/international-press-zone/includes/Translation/Settings.php.


desc: Read docs/plans/2026-08-04-ipz-advanced-settings.md Task 2 (Debug-mode consolidation migration) for the full contract. New AbstractMigration that copies ipz_debug_mode into presszone_international_debug_mode and deletes the legacy option. Acceptance: podman exec devzone-wordpress php -r 'require "/var/www/html/wp-load.php"; delete_option("presszone_international_debug_mode"); update_option("ipz_debug_mode", true); (new \InternationalPressZone\Migrations\Migration20260804MigrateDebugMode())->up(); echo ((get_option("presszone_international_debug_mode") == true && get_option("ipz_debug_mode") === false) ? "PASS" : "FAIL"), PHP_EOL;' → PASS. Commit only: plugins/international-press-zone/includes/Migrations/Migration20260804MigrateDebugMode.php.


desc: Read docs/plans/2026-08-04-ipz-advanced-settings.md Task 3 (ExceptionSync transport + dead request key removal) for the full contract. New Translation\ExceptionSync with push()/pull() against /v1/exceptions, plus removal of the dead $body['exceptions'] key in BulkActions. Acceptance: podman exec devzone-wordpress php -r 'require "/var/www/html/wp-load.php"; $r = (new \InternationalPressZone\Translation\ExceptionSync())->push(); echo (is_array($r) && array_key_exists("success",$r) && array_key_exists("count",$r) && array_key_exists("message",$r) ? "SHAPE_OK" : "SHAPE_FAIL"), PHP_EOL;' && ! grep -q "body\['exceptions'\]" plugins/international-press-zone/includes/Translation/BulkActions.php && echo DEADKEY_GONE. Commit only: plugins/international-press-zone/includes/Translation/ExceptionSync.php, plugins/international-press-zone/includes/Translation/BulkActions.php.


desc: Read docs/plans/2026-08-04-ipz-advanced-settings.md Task 4 (Exceptions tab styles) for the full contract. New _exceptions.scss with the pinned ipz-exceptions BEM classes plus one @use line in main.scss. Acceptance: cd plugins/international-press-zone/admin && npm run build 2>&1 | tail -20 && grep -c "ipz-exceptions" dist/css/main.css → clean build, count > 0. Commit only: plugins/international-press-zone/admin/src/styles/pages/_exceptions.scss, plugins/international-press-zone/admin/src/styles/main.scss.


desc: Read docs/plans/2026-08-04-ipz-advanced-settings.md Task 5 (Exceptions REST controller) for the full contract. New API\ExceptionsController with six routes under presszone-international/v1/exceptions, registered in Plugin.php. Acceptance: podman exec devzone-wordpress php -r 'require "/var/www/html/wp-load.php"; do_action("rest_api_init"); $routes = rest_get_server()->get_routes(); foreach (["/presszone-international/v1/exceptions","/presszone-international/v1/exceptions/import","/presszone-international/v1/exceptions/sync","/presszone-international/v1/exceptions/pull"] as $r) { echo $r, isset($routes[$r]) ? " OK" : " MISSING", PHP_EOL; }' → four OK lines. Commit only: plugins/international-press-zone/includes/API/ExceptionsController.php, plugins/international-press-zone/includes/Core/Plugin.php.


desc: Read docs/plans/2026-08-04-ipz-advanced-settings.md Task 6 (Settings REST delegation) for the full contract. Delegate tone, auto_publish and debug_mode to Translation\Settings in getSettings/updateSettings/resetSettings; PUT response shape unchanged. Acceptance: podman exec devzone-wordpress php -r 'require "/var/www/html/wp-load.php"; \InternationalPressZone\Translation\Settings::instance()->set_tone("casual"); do_action("rest_api_init"); $d = rest_do_request(new WP_REST_Request("GET","/presszone-international/v1/settings"))->get_data()["data"]; echo ((($d["tone"] ?? "") === "casual" && array_key_exists("auto_publish",$d) && array_key_exists("debug_mode",$d)) ? "PASS" : "FAIL"), PHP_EOL;' → PASS. Commit only: plugins/international-press-zone/includes/API/SettingsController.php.


desc: Read docs/plans/2026-08-04-ipz-advanced-settings.md Task 7 (Auto-publish wiring) for the full contract. One rule in authorizedTargetStatus(): with no explicit caller status, auto-publish on means publish. Literal code is in the plan — apply inline. Acceptance: podman exec devzone-wordpress php -l /var/www/html/wp-content/plugins/international-press-zone/includes/Translation/TranslationFinalizer.php → No syntax errors detected. Commit only: plugins/international-press-zone/includes/Translation/TranslationFinalizer.php.


desc: Read docs/plans/2026-08-04-ipz-advanced-settings.md Task 8 (Exceptions tab component) for the full contract. New vanilla-JS ExceptionsTab.js consuming the six exceptions routes; add form, table, bulk import, sync row. Acceptance: cd plugins/international-press-zone/admin && npx eslint src/components/ExceptionsTab.js → exit 0, no warnings. Commit only: plugins/international-press-zone/admin/src/components/ExceptionsTab.js.


desc: Read docs/plans/2026-08-04-ipz-advanced-settings.md Task 9 (Settings page controls) for the full contract. Tone Select and auto-publish Toggle in renderGeneralSettings(), extended save payload, Exceptions tab registered. Acceptance: cd plugins/international-press-zone/admin && npx eslint src/pages/settings.js && grep -c "auto_publish" src/pages/settings.js → clean lint, count >= 2. Commit only: plugins/international-press-zone/admin/src/pages/settings.js.


desc: Read docs/plans/2026-08-04-ipz-advanced-settings.md Task 10 (Admin build) for the full contract. Rebuild the admin bundle so wave-5 journeys run against the new UI; every warning addressed, none suppressed. Acceptance: cd plugins/international-press-zone/admin && npm run build 2>&1 | tee /tmp/ipz-build.log; grep -Ei "warn|error" /tmp/ipz-build.log → build succeeds, no warning or error lines. Commit only: plugins/international-press-zone/admin/dist.


desc: Read docs/plans/2026-08-04-ipz-advanced-settings.md Task 15 (Auto-publish status integration check) for the full contract. PHP integration test asserting post_status across four auto-publish cases with a stubbed payload — no live backend call. Acceptance: podman exec devzone-wordpress php /var/www/html/wp-content/plugins/international-press-zone/tests/integration/AutoPublishStatusTest.php; echo "exit=$?" → four PASS lines and exit=0. Commit only: plugins/international-press-zone/tests/integration/AutoPublishStatusTest.php.


desc: Read docs/plans/2026-08-04-ipz-advanced-settings.md Task 16 (E2E journey runner) for the full contract. Fail-closed wrapper encoding the SSH reverse tunnel + e2e-remote invocation; verified mechanics are literal in the plan. Acceptance: from plugins/international-press-zone/tests/e2e/: ./run-journey.sh journeys/UJ-006-save-settings.spec.js → PASS; ./run-journey.sh with no argument exits non-zero with a usage line. Commit only: plugins/international-press-zone/tests/e2e/run-journey.sh.


desc: Read docs/plans/2026-08-04-ipz-advanced-settings.md Task 11 (Journey UJ-022 — tone and auto-publish persist) for the full contract. Playwright journey asserting tone persists across reload and auto_publish round-trips through GET /settings. Acceptance: from plugins/international-press-zone/tests/e2e/: ./run-journey.sh journeys/UJ-022-set-translation-tone.spec.js → all branches PASS. Commit only: plugins/international-press-zone/tests/e2e/journeys/UJ-022-set-translation-tone.spec.js, plugins/international-press-zone/tests/e2e/journeys/UJ-022-set-translation-tone.spec.manifest.json.


desc: Read docs/plans/2026-08-04-ipz-advanced-settings.md Task 12 (Journey UJ-023 — debug round-trip and reset) for the full contract. Asserts debug_mode reads back from the canonical option and that reset restores tone/auto_publish/debug defaults. Acceptance: from plugins/international-press-zone/tests/e2e/: ./run-journey.sh journeys/UJ-023-debug-mode-round-trip.spec.js → all branches PASS. Commit only: plugins/international-press-zone/tests/e2e/journeys/UJ-023-debug-mode-round-trip.spec.js, plugins/international-press-zone/tests/e2e/journeys/UJ-023-debug-mode-round-trip.spec.manifest.json.


desc: Read docs/plans/2026-08-04-ipz-advanced-settings.md Task 13 (Journey UJ-024 — add and delete an exception) for the full contract. Covers add, duplicate 409, delete, and a slash-containing exception text. Acceptance: from plugins/international-press-zone/tests/e2e/: ./run-journey.sh journeys/UJ-024-manage-exceptions.spec.js → all branches PASS. Commit only: plugins/international-press-zone/tests/e2e/journeys/UJ-024-manage-exceptions.spec.js, plugins/international-press-zone/tests/e2e/journeys/UJ-024-manage-exceptions.spec.manifest.json.


desc: Read docs/plans/2026-08-04-ipz-advanced-settings.md Task 14 (Journey UJ-025 — bulk import counts) for the full contract. Covers added/skipped counts for fresh lines, duplicates plus blanks, and an over-500-character line. Acceptance: from plugins/international-press-zone/tests/e2e/: ./run-journey.sh journeys/UJ-025-bulk-import-exceptions.spec.js → all branches PASS. Commit only: plugins/international-press-zone/tests/e2e/journeys/UJ-025-bulk-import-exceptions.spec.js, plugins/international-press-zone/tests/e2e/journeys/UJ-025-bulk-import-exceptions.spec.manifest.json.


note: 2026-08-04 continuation state: Tasks 1-9 and 15 are implemented, committed, and Sol-approved. Task 10 commit 3233f4ff was rejected because dist was incomplete/non-reproducible; repair workflow wf_94baf233-d29 was stopped on user request before completion. Task 11 commit 30914ca5 exists. Tasks 12-14 files exist untracked. Task 16 has an unstaged redesign and needs acceptance/review/commit. Do not trust stale namespace or reverse-tunnel wording in earlier task records; the corrected plan/spec are authoritative: namespace international-press-zone/v1, authenticated acceptance, and validated direct private home_url transport with no SSH tunnel.


note: Interrupted local work: includes/Translation/Settings.php is unstaged with an idempotency fix because update_option() returns false for unchanged valid values; complete it with SettingsIdempotencyTest.php and review. tests/e2e/journeys/UJ-022-set-translation-tone.spec.js is unstaged because a stopped agent removed the useful updated_fields tone assertion; restore that assertion after the Settings fix. tests/e2e/run-journey.sh is unstaged, strips trailing slash from discovered home_url, validates http/8080/100.64.0.0/10, cd's into tests/e2e, and calls e2e-remote; syntax and no-arg exit 2 pass.


note: Overdeck remote watcher C:<rc> fix is landed, pushed, and deployed at SHA 24316641; remote Chromium launches. Browser journeys must run serially: parallel acceptance caused overlapping/orphan Playwright workers and misleading 143/137 statuses. All dynamic workflows and the resumed Task 11 agent were explicitly stopped for handoff.