# WP Agentic Writer Second Retrace Audit Status: COMPLETE / SUPERSEDED Completion marker date: 2026-05-24 Next retrace report: `docs/architecture/PLUGIN_AUDIT_RETRACE_THIRD_PASS_2026-05-24.md` Audit date: 2026-05-24 Baseline retraced: `docs/architecture/PLUGIN_AUDIT_RETRACE_2026-05-24.md` Scope: second pass after retrace implementation, covering UI/UX, editor runtime, system boundaries, conversation context/history, cost tracker, provider/model routing, migrations, data lifecycle, and release readiness. ## Executive Summary Several of the previous retrace findings were implemented correctly. The provider selection result is now unwrapped in the image, keyword, and WP AI legacy provider paths. Streaming chat state variables are initialized again. The cost table base schema and hook argument count were expanded. Uninstall cleanup is also much more complete, and a changelog now exists. However, the plugin is not ready to move only into chat/context feature work yet. Two release blockers remain: 1. `assets/js/sidebar.js` currently does not parse. The attempted debug-logger conversion damaged string literals and logger calls, so the Gutenberg sidebar can fail before any UI renders. 2. `includes/class-wp-ai-client-wrapper.php` still calls `WP_Agentic_Writer_Cost_Tracker::record_usage()`, but that method does not exist. Successful WP AI Client text generation or legacy wrapper tracking can fatal. After those are fixed, the next biggest risks are still the conversation migration/version gate, legacy context source-of-truth behavior, and post-scoped authorization gaps outside the conversation routes. ## Verification Performed - PHP syntax check across plugin PHP files: passed. - `node -c assets/js/settings-v2.js`: passed. - `node -c assets/js/sidebar.js`: failed at line 17. - Static trace of prior retrace recommendations against the current code. - No live WordPress browser workflow was run in this pass because the sidebar JavaScript parse failure blocks meaningful UI verification. ## Retrace Implementation Status | Area | Current status | Evidence | |---|---:|---| | Provider selection result callers | Fixed for previously named helper paths | Image manager, keyword suggester, and WP AI legacy wrapper now use `$provider_result->provider`. | | Streaming chat variables | Fixed | `stream_chat_request()` initializes accumulated content, chunk count, total cost, and last user message before streaming. | | Cost hook expanded args | Fixed | `includes/class-cost-tracker.php:48-53` registers `add_request` with 9 accepted args. | | Cost base schema | Improved | `wp-agentic-writer.php:198-216` now creates `session_id`, `provider`, and `status` columns and indexes. | | Uninstall/data cleanup | Improved | Main uninstall removes settings, custom models, tables, transients, user meta, post meta, scheduled events, and temp images. | | Changelog | Added but currently inaccurate | `CHANGELOG.md` says sidebar logging was fixed, but `assets/js/sidebar.js` now fails syntax validation. | | Conversation migration versioning | Still open | Conversation migration still checks `wpaw_db_version` instead of `wpaw_conversations_db_version`. | | Context/history source of truth | Still partial | Legacy migration is manual, `get_context()` does not migrate on read, and clear-context route only deletes post meta. | | Post-scoped REST authorization | Still partial | Several post-id routes rely on generic `edit_posts` route permission and do not check `edit_post` for the target post. | | Model registry/default unification | Still open | Defaults and model lists remain spread across activation, settings PHP, settings JS, providers, and wrapper classes. | ## Critical Findings ### P0: Gutenberg Sidebar JavaScript Does Not Parse The sidebar bundle currently fails before runtime: - `node -c assets/js/sidebar.js` fails with `SyntaxError: Invalid or unexpected token`. - The first parse error is at `assets/js/sidebar.js:17`, where the logger calls `wpawLog.log(', ...args);`. - `assets/js/sidebar.js:18` has the same pattern for errors. - Many later converted calls lost opening quotes or brackets, for example `wpawLog.error( Failed to load session for post:', error);`, `wpawLog.log( Found legacy chat history, triggering migration...');`, and `wpawLog.warn([WPAW] No questions returned from clarity check!);`. Impact: - The editor sidebar can fail to load entirely. - Chat, context migration prompts, cost UI refresh, plan generation, article generation, and image UI are all unreachable if the script is enqueued. - This also invalidates the changelog claim that the sidebar debug logging conversion is complete. Recommended fix: - Restore the logger definition to call `console.log`, `console.error`, `console.info`, and `console.warn`. - Repair every malformed `wpawLog.*` call in `assets/js/sidebar.js`. - Prefer a mechanical but syntax-aware conversion from `console.*` to `wpawLog.*`, not a plain text replacement. - Gate completion on `node -c assets/js/sidebar.js` plus a browser load of the Gutenberg sidebar. ### P0: WP AI Wrapper Still Calls Missing Cost Tracker Method `includes/class-wp-ai-client-wrapper.php` still calls a method that does not exist: - Core AI text path calls `WP_Agentic_Writer_Cost_Tracker::get_instance()->record_usage()` at `includes/class-wp-ai-client-wrapper.php:197-202`. - Legacy text path calls the same missing method at `includes/class-wp-ai-client-wrapper.php:249-254`. - Repository search found no `record_usage()` implementation in `WP_Agentic_Writer_Cost_Tracker`. Impact: - If WP AI Client text generation succeeds and cost tracking class is loaded, the request can fatal after the model returns. - If the legacy wrapper succeeds and tries to track cost, it can fatal. - This is a cross-path cost-tracker regression because most of the plugin uses `do_action( 'wp_aw_after_api_request', ... )`, but this wrapper uses a different contract. Recommended fix: - Either add a `record_usage()` compatibility method to `WP_Agentic_Writer_Cost_Tracker`, or convert both wrapper call sites to `do_action( 'wp_aw_after_api_request', ... )`. - The compatibility method is safer if external or older internal code might call it. - Include model, action/task type, input/output token estimates, cost, provider, session id, and status where available. ## High Priority Findings ### P1: Conversation Migration Still Uses the Main DB Version Gate The conversation migration stores a dedicated option but does not consistently read it: - `wpaw_create_conversations_table()` stores `wpaw_conversations_db_version` at `includes/class-conversation-migration.php:43-47`. - `wpaw_run_migrations()` still reads `wpaw_db_version` at `includes/class-conversation-migration.php:67-72`. - Main table creation only runs when `wpaw_db_version < 1.1.0` in `wp-agentic-writer.php:230-249`. Impact: - Sites where the main plugin DB version is already current can skip conversation table repair or creation. - A failed partial migration can leave the plugin thinking the main schema is current while conversation storage is missing or stale. - Chat/history work remains risky until table creation is idempotent per table/version, not only per global plugin version. Recommended fix: - Make conversation table creation check `wpaw_conversations_db_version`. - In `wp_agentic_writer_maybe_create_tables()`, either always call idempotent table creators or use per-table schema versions. - Update `wpaw_drop_conversations_table()` to delete `wpaw_conversations_db_version`, not `wpaw_db_version`. ### P1: Clear Context and Legacy Migration Still Do Not Respect the New Source of Truth The code still has split context behavior: - `WP_Agentic_Writer_Context_Service::get_context()` returns an empty context when no session exists and does not try legacy migration on read. - `migrate_legacy_chat_history()` keeps `_wpaw_chat_history` after migration at `includes/class-context-service.php:299-300`. - `handle_clear_context()` only deletes `_wpaw_memory` and `_wpaw_chat_history` at `includes/class-gutenberg-sidebar.php:1216-1236`; it does not clear the active conversation session. - The context service has a better `clear_context( $session_id, $post_id )` method, but the REST handler does not use it. Impact: - Users can clear visible legacy meta while the active session still keeps messages. - Legacy meta can be re-migrated later, causing duplicate or surprising history. - The system still has two competing history stores, which is the core reason A/B regressions keep recurring around chat. Recommended fix: - Pass `sessionId` through the clear-context endpoint and call `WP_Agentic_Writer_Context_Service::clear_context()`. - Decide migration policy: either delete legacy `_wpaw_chat_history` after successful migration or mark it migrated with a durable meta flag. - Add migrate-on-read for post-linked sessions so old posts behave consistently. ### P1: Post-Scoped REST Authorization Is Still Incomplete Outside Conversation Routes The conversation routes received targeted post authorization, but older post-id endpoints still depend mostly on generic `edit_posts` permission: - Route registration uses `check_permissions`, which returns `current_user_can( 'edit_posts' )` at `includes/class-gutenberg-sidebar.php:931`. - `handle_get_post_config()` and `handle_update_post_config()` do not check `edit_post` for the target post at `includes/class-gutenberg-sidebar.php:1728-1760`. - `handle_get_cost_tracking()` does not check target post access at `includes/class-gutenberg-sidebar.php:3629-3635`. - `handle_save_section_blocks()` and `handle_get_section_blocks()` do not check target post access at `includes/class-gutenberg-sidebar.php:4782-4842`. - Image recommendation/generation/commit handlers do not check target post access at `includes/class-gutenberg-sidebar.php:6463-6525`. Impact: - An authenticated author/editor with broad `edit_posts` can potentially read or mutate plugin data for posts they cannot edit. - Cost history, post config, image recommendations, image variants, and section mappings can leak or be modified across post boundaries. Recommended fix: - Every REST handler accepting `post_id` or `postId` should validate `current_user_can( 'edit_post', $post_id )` before read or write. - Centralize this in helper methods so future endpoints inherit the same rule. - Include negative permission tests for another user's private/draft post. ## Medium Priority Findings ### P2: Cost Tracker Migration Still Assumes the Table Exists `WP_Agentic_Writer_Cost_Tracker::maybe_upgrade_table()` runs `DESCRIBE {$table_name}` and immediately uses `in_array()` on the result. If the table is missing, `$wpdb->get_col()` can return an unexpected value and the method does not create the table. Impact: - Sites with a missing cost table but a current `wpaw_db_version` may not self-heal. - The first cost tracker access can produce warnings or fail to record usage. Recommended fix: - Detect missing table with `SHOW TABLES LIKE` before `DESCRIBE`. - If missing, call `wp_agentic_writer_create_cost_table()` or move schema creation into the cost tracker class. - Make the table creator idempotent and callable regardless of global plugin DB version. ### P2: Provider Metadata Is Still Not a Uniform Response Contract Chat responses now include provider metadata, but non-chat endpoints still often return only content or variants. As provider fallback expands, users and support logs need a consistent answer to "which provider/model actually served this?". Recommended fix: - Standardize response metadata for generated text, image analysis, image variants, keyword suggestions, and plan/edit endpoints. - Include `provider`, `fallback_used`, `warnings`, `model`, and `cost` where available. - Mirror the same metadata into cost records. ### P2: Model Defaults and Registry Remain Fragmented Model defaults still appear in multiple layers: - Activation defaults in `wp-agentic-writer.php`. - Settings presets in `assets/js/settings-v2.js`. - Settings PHP model transforms and saved options. - Provider defaults in OpenRouter/local/WP AI wrapper classes. - Image model fallback logic in the image manager. Impact: - A model replacement can fix one UI path while leaving old defaults in runtime paths. - Cost estimation and provider selection may disagree with the model shown in settings. Recommended fix: - Create a single model registry/default resolver in PHP. - Localize the resolved defaults to JS instead of duplicating presets. - Give each task type one canonical key: chat, clarity, planning, writing, refinement, image. ### P2: Deactivation Still Leaves the Conversation Cleanup Event Scheduled Uninstall now clears `wpaw_cleanup_old_sessions`, but deactivation only clears `wpaw_cleanup_temp_images` in `wp-agentic-writer.php:260-266`. Impact: - A deactivated plugin can leave scheduled cleanup hooks behind until uninstall. Recommended fix: - Add `wp_clear_scheduled_hook( 'wpaw_cleanup_old_sessions' )` to deactivation. - Prefer scheduling cron events on activation or migration, not at include time. ## Definition of Done Gates for This Pass Before considering the retrace implementation complete, require: - `node -c assets/js/sidebar.js` passes. - `node -c assets/js/settings-v2.js` passes. - Full PHP syntax check passes. - No references to undefined methods such as `record_usage()` remain, unless the method is intentionally added. - Conversation table creation is controlled by `wpaw_conversations_db_version` or always-idempotent table checks. - Clear-context clears the active session and legacy meta in one path. - Every post-scoped endpoint checks `edit_post` for its target post. - Changelog entries match verified behavior. ## Recommended Next Work Order 1. Fix `assets/js/sidebar.js` syntax and logger conversion, then browser-test the sidebar. 2. Fix the missing `record_usage()` contract or convert the WP AI wrapper to the existing cost hook. 3. Fix conversation table versioning and table self-healing. 4. Align clear-context and legacy migration around the conversations table as the source of truth. 5. Add post-scoped authorization checks for config, cost, section-block, and image routes. 6. Then continue into deeper chat/context implementation. ## Current Decision Do not move fully into new chat/context feature work yet. The sidebar parse failure and missing cost tracker method should be fixed first because they can block or fatal the existing editor workflow. Once those P0s are closed, chat/context can become the main focus with fewer regressions.