# WP Agentic Writer Thirteenth Retrace Audit Audit date: 2026-05-26 Baseline retraced: `docs/architecture/PLUGIN_AUDIT_RETRACE_TWELFTH_PASS_2026-05-26.md` Scope: thirteenth pass after twelfth-retrace implementation, covering legacy chat migration, conversation context continuity, cost attribution, provider metadata coverage, model registry ownership, UI/UX readiness, and release verification. Status: COMPLETE / RETRACED Completion marker: 2026-05-26 Follow-up retrace: `docs/architecture/PLUGIN_AUDIT_RETRACE_FOURTEENTH_PASS_2026-05-26.md` > This thirteenth-pass report has been implemented and retraced. Keep this document as historical evidence only; use the fourteenth-pass report for current remaining work. ## Executive Summary The twelfth-pass implementation closed several items from the previous report: - The canonical `/conversation/{post_id}` handler now attempts legacy `_wpaw_chat_history` migration. - The specific keyword and improvement suggestion cost hooks called out in the twelfth report now pass the full hook contract. - The missed frontend provider metadata calls at the previously listed generation/clarity branches were added. - The deprecated chat-history docblock now describes migration behavior accurately. - Backup JavaScript endpoint references were removed from `assets/`. However, a new P0 runtime regression was found in the legacy migration implementation: the new `/conversation/{post_id}` migration branch directly instantiates `WP_Agentic_Writer_Context_Service`, but that service has a private constructor. This will fatal when a legacy post has `_wpaw_chat_history` and no existing conversation session. Static syntax checks still pass, but PHP linting cannot detect this runtime visibility error. ## Verification Performed - PHP syntax check across plugin PHP files: passed. - `node -c assets/js/sidebar.js`: passed. - `node -c assets/js/settings-v2.js`: passed. - `node -c assets/js/sidebar-utils.js`: passed. - Static retrace of twelfth-pass findings against current code. - Static sweep of legacy chat migration, provider metadata propagation, raw cost hooks, model default ownership, stale backup files, and browser-verification evidence. - No live WordPress editor/browser workflow was run in this pass. ## Twelfth-Pass Status Trace | Twelfth-pass item | Current status | Evidence | |---|---:|---| | `/conversation/{post_id}` should migrate legacy chat history | Implemented, but broken by P0 | Migration branch exists at `includes/class-gutenberg-sidebar.php:1411-1432`, but calls a private constructor at line `1415`. | | Keyword suggestion cost attribution | Fixed for called-out path | `includes/class-keyword-suggester.php:140-151` now passes provider/session/status fields. | | Improvement suggestion cost attribution | Fixed for called-out path | `includes/class-gutenberg-sidebar.php:6911-6922` now passes provider/session/status fields. | | Provider metadata calls for listed frontend branches | Mostly fixed | `applyProviderMetadata()` is now present at the previously missed branches, including `assets/js/sidebar.js:3698`, `3932`, and `4821`. | | Model preset/default ownership | Partially de-scoped | JS and legacy presets now document that they are curated, but duplicated hard-coded presets remain active. | | Browser verification | Still open | Static checks passed; no live editor workflow evidence was found. | | Stale chat-history comments and backup files | Fixed | Docblock updated at `includes/class-gutenberg-sidebar.php:1346-1350`; no `*.bak` or `*.backup` files remain under `assets/`. | ## Remaining Findings ### P0: Legacy Chat Migration Path Can Fatal Due Private Singleton Constructor The twelfth implementation added the needed migrate-on-read behavior to the canonical conversation endpoint: - `includes/class-gutenberg-sidebar.php:1411-1432` checks `_wpaw_chat_history`, attempts migration, then returns the newly created session. But the migration branch instantiates the context service directly: - `includes/class-gutenberg-sidebar.php:1415` uses `new WP_Agentic_Writer_Context_Service()`. That class is explicitly a singleton: - `includes/class-context-service.php:41-45` exposes `WP_Agentic_Writer_Context_Service::get_instance()`. - `includes/class-context-service.php:51-53` declares `private function __construct()`. Impact: - A legacy post with `_wpaw_chat_history` and no existing conversation session can trigger a fatal error when the editor loads chat through `/conversation/{post_id}`. - This is exactly the legacy continuity path the twelfth pass intended to repair. - PHP syntax checks still pass because this is a runtime visibility error, not a parse error. Recommended fix: - Replace `new WP_Agentic_Writer_Context_Service()` with `WP_Agentic_Writer_Context_Service::get_instance()`. - After migration, prefer the returned `$migrated_session_id` when fetching/returning the session, with a fallback to `get_session_by_post_id()`. - Add a regression test or manual fixture for: post has `_wpaw_chat_history`, has no conversation row, editor loads `/conversation/{post_id}`. ### P1: Raw Cost Hook Drift Still Leaves Provider Attribution Gaps The two cost examples from the twelfth report were fixed, but a broader scan still found multiple seven-argument `wp_aw_after_api_request` calls in `includes/class-gutenberg-sidebar.php`. These calls still omit provider, session id, and status. Examples: - Section execution cost at `includes/class-gutenberg-sidebar.php:3034-3042`. - Write-from-outline section cost at `includes/class-gutenberg-sidebar.php:3675-3683`. - Block refinement cost at `includes/class-gutenberg-sidebar.php:4590-4598`. - Streaming block refinement cost at `includes/class-gutenberg-sidebar.php:4718-4726`. - Chat refinement planning/streaming costs at `includes/class-gutenberg-sidebar.php:5360-5368` and `5516-5524`. - Meta description cost at `includes/class-gutenberg-sidebar.php:6316-6324`. - Intent detection cost at `includes/class-gutenberg-sidebar.php:6708-6716`. Impact: - The cost ledger can still show `unknown` provider for several real AI workflows. - Provider/fallback transparency can differ between UI responses and persisted cost rows. - Future fixes remain fragile while some paths use `track_ai_cost()` and others manually fire a shorter hook contract. Recommended fix: - Convert all internal AI cost tracking in `class-gutenberg-sidebar.php` to `track_ai_cost()` where the provider result is in scope. - Where provider result is not currently in scope, pass it through with the generated response, or explicitly document why attribution is unavailable. - Add a static guard that fails when `do_action( 'wp_aw_after_api_request' )` is called without the full provider/session/status contract outside the central helper. ### P2: Provider Metadata Coverage Still Has Stream Completion Gaps The frontend now applies provider metadata in the branches called out by the twelfth pass: - `assets/js/sidebar.js:3698` - `assets/js/sidebar.js:3932` - `assets/js/sidebar.js:4821` Remaining gaps: - `assets/js/sidebar.js:4222-4224` handles another stream `complete` event and updates cost without calling `applyProviderMetadata(data)`. - Some backend stream completion payloads still include only completion/cost fields and no provider metadata, including `includes/class-gutenberg-sidebar.php:3730-3732`, `4824-4827`, and `5552-5555`. Impact: - Even when the frontend calls `applyProviderMetadata()`, some completion payloads do not contain metadata to apply. - Provider badges can remain stale after specific streaming generation/refinement flows. Recommended fix: - Include `provider_metadata` on every stream `complete` payload that follows a provider call. - Call `applyProviderMetadata(data)` in every frontend `data.type === 'complete'` branch that can receive provider-backed output. - For completion events that are intentionally non-provider events, add a short comment so future audits do not treat them as missing. ### P2: Live Browser Verification Is Still Required Static checks passed, but no live WordPress editor workflow evidence was found. The next browser pass should verify: - Legacy `_wpaw_chat_history` migrates through `/conversation/{post_id}` without fatal error. - Sidebar conversation state persists after editor reload. - Provider badge updates after chat, clarity, planning, generation, block refinement, chat refinement, meta, keyword, intent, and improvement actions. - Cost log rows include provider/session/status for the same actions. - Model settings changes affect generated requests. - Unauthorized REST access remains denied. ### P3: Model Presets Are Documented As Curated, But Still Duplicated The twelfth criteria allowed model presets/default ownership to be centralized or documented as intentional. The implementation chose documentation: - `assets/js/settings-v2.js:32-34` says presets are curated product decisions, not registry-derived. - `includes/class-settings.php:1026` says the legacy inline presets are curated, not registry-derived. This is acceptable as a product decision, but it leaves duplicated preset data in active code: - Active Settings V2 presets are hard-coded at `assets/js/settings-v2.js:35-59`. - Legacy settings presets are hard-coded at `includes/class-settings.php:1027-1051`. - The legacy settings class can still be instantiated at `wp-agentic-writer.php:100-104`. Impact: - Presets can drift between Settings V2 and legacy settings. - Model updates still require edits in more than one place. Recommended fix: - If curated presets remain intentional, centralize them in one PHP source and localize them into both UIs. - If legacy settings is only a fallback, add a deprecation note and a smaller test surface for preset parity. ## Priority Queue 1. P0: Replace direct `new WP_Agentic_Writer_Context_Service()` with `WP_Agentic_Writer_Context_Service::get_instance()` in the canonical conversation migration path. 2. P1: Convert remaining seven-argument cost hooks to the full provider/session/status contract or `track_ai_cost()`. 3. P2: Finish provider metadata coverage for every stream `complete` branch and payload. 4. P2: Run the live WordPress editor browser verification pass. 5. P3: Centralize curated model presets or document legacy preset parity ownership. ## Completion Criteria For Next Pass The next retrace can mark this pass complete when: - Legacy chat migration through `/conversation/{post_id}` cannot fatal on the context service constructor. - Static scan finds no short-form `wp_aw_after_api_request` calls in provider-backed workflows. - Provider-backed stream completion payloads include metadata, and frontend completion branches apply it or explicitly de-scope it. - Browser verification evidence exists for chat persistence, migration, provider badge updates, cost log attribution, model settings, and auth denial.