14 KiB
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:
assets/js/sidebar.jscurrently does not parse. The attempted debug-logger conversion damaged string literals and logger calls, so the Gutenberg sidebar can fail before any UI renders.includes/class-wp-ai-client-wrapper.phpstill callsWP_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.jsfails withSyntaxError: Invalid or unexpected token.- The first parse error is at
assets/js/sidebar.js:17, where the logger callswpawLog.log(', ...args);. assets/js/sidebar.js:18has 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...');, andwpawLog.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, andconsole.warn. - Repair every malformed
wpawLog.*call inassets/js/sidebar.js. - Prefer a mechanical but syntax-aware conversion from
console.*towpawLog.*, not a plain text replacement. - Gate completion on
node -c assets/js/sidebar.jsplus 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()atincludes/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 inWP_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 toWP_Agentic_Writer_Cost_Tracker, or convert both wrapper call sites todo_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()storeswpaw_conversations_db_versionatincludes/class-conversation-migration.php:43-47.wpaw_run_migrations()still readswpaw_db_versionatincludes/class-conversation-migration.php:67-72.- Main table creation only runs when
wpaw_db_version < 1.1.0inwp-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 deletewpaw_conversations_db_version, notwpaw_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_historyafter migration atincludes/class-context-service.php:299-300.handle_clear_context()only deletes_wpaw_memoryand_wpaw_chat_historyatincludes/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
sessionIdthrough the clear-context endpoint and callWP_Agentic_Writer_Context_Service::clear_context(). - Decide migration policy: either delete legacy
_wpaw_chat_historyafter 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 returnscurrent_user_can( 'edit_posts' )atincludes/class-gutenberg-sidebar.php:931. handle_get_post_config()andhandle_update_post_config()do not checkedit_postfor the target post atincludes/class-gutenberg-sidebar.php:1728-1760.handle_get_cost_tracking()does not check target post access atincludes/class-gutenberg-sidebar.php:3629-3635.handle_save_section_blocks()andhandle_get_section_blocks()do not check target post access atincludes/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_postscan 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_idorpostIdshould validatecurrent_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_versionmay not self-heal. - The first cost tracker access can produce warnings or fail to record usage.
Recommended fix:
- Detect missing table with
SHOW TABLES LIKEbeforeDESCRIBE. - 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, andcostwhere 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.jspasses.node -c assets/js/settings-v2.jspasses.- 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_versionor always-idempotent table checks. - Clear-context clears the active session and legacy meta in one path.
- Every post-scoped endpoint checks
edit_postfor its target post. - Changelog entries match verified behavior.
Recommended Next Work Order
- Fix
assets/js/sidebar.jssyntax and logger conversion, then browser-test the sidebar. - Fix the missing
record_usage()contract or convert the WP AI wrapper to the existing cost hook. - Fix conversation table versioning and table self-healing.
- Align clear-context and legacy migration around the conversations table as the source of truth.
- Add post-scoped authorization checks for config, cost, section-block, and image routes.
- 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.