13 KiB
WP Agentic Writer Seventh Retrace Audit
Status: COMPLETE / RETRACED
Completion marker: 2026-05-25
Follow-up report: docs/architecture/PLUGIN_AUDIT_RETRACE_EIGHTH_PASS_2026-05-25.md
Audit date: 2026-05-25
Baseline retraced: docs/architecture/PLUGIN_AUDIT_RETRACE_SIXTH_PASS_2026-05-25.md
Scope: seventh pass after sixth-retrace implementation, covering provider transparency, conversation context/history, cost tracking, model defaults, UI/UX, and release readiness.
Executive Summary
The sixth-pass implementation fixed two important backend issues:
WP_Agentic_Writer_Context_Service::get_context()now carries an$effective_session_idand returns it, so migrate-on-read no longer returns messages from one session while reporting the original stale session id.- Deprecated
WP_Agentic_Writer_Cost_Tracker::record_usage()now records provider asunknowninstead of hard-codingopenrouter.
I did not find a new P0 authorization blocker. The remaining problems are still meaningful because they affect user trust, cost accuracy, and conversation continuity:
- The sidebar still actively calls deprecated
/chat-history, which reads legacy_wpaw_chat_historyinstead of the authoritative conversation table. - Provider transparency was added only partially: some responses now include nested
provider_metadata, but other AI endpoints still omit it, chat uses a different top-level shape, and the sidebar does not render provider/fallback metadata. - Cost tracking still defaults missing provider arguments to
openrouter, and many AI cost hooks still omit provider/session/status arguments. - Model defaults remain fragmented across PHP activation, settings, providers, JavaScript presets, wrappers, and image helpers.
- Browser-level WordPress editor verification is still not done.
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 sixth-pass findings against current code.
- Static sweep of provider metadata, cost hooks, context migration, sidebar history loading, and model defaults.
- No live WordPress editor/browser workflow was run in this pass.
Sixth-Pass Status Trace
| Sixth-pass item | Current status | Evidence |
|---|---|---|
| Migrated context returns effective session id | Fixed | $effective_session_id is initialized, updated after migration, and returned as session_id in includes/class-context-service.php:62-92. |
Deprecated record_usage() provider attribution |
Fixed narrowly | The deprecated wrapper now passes unknown at includes/class-cost-tracker.php:176-186. |
| Provider metadata helper | Partially fixed | build_provider_metadata() exists at includes/class-gutenberg-sidebar.php:956-963, and several responses now include provider_metadata. |
| Provider transparency response contract | Still partial | Chat uses top-level keys, several non-chat routes use nested provider_metadata, and some AI routes still omit metadata entirely. |
Legacy /chat-history endpoint |
Still open, now confirmed active in UI | Backend still reads _wpaw_chat_history, and assets/js/sidebar.js still calls /chat-history/{postId} on load. |
| Cost tracker provider/session/status integrity | Still open | The cost hook accepts 9 args, but many call sites still pass only 7 args, causing provider fallback to the hook default. |
| Model registry/default unification | Still open | No model registry was found; defaults remain duplicated across runtime surfaces. |
| WordPress editor browser pass | Still open | Syntax checks passed, but no editor workflow was verified. |
Remaining Findings
P1: Sidebar Still Loads Conversation State From Deprecated Post Meta
The backend still registers and serves the legacy route:
/chat-history/(?P<post_id>\d+)remains registered inincludes/class-gutenberg-sidebar.php:346-354.handle_get_chat_history()still returnsmessagesfromget_post_chat_history()inincludes/class-gutenberg-sidebar.php:1300-1326.get_post_chat_history()still reads_wpaw_chat_historydirectly inincludes/class-gutenberg-sidebar.php:1354-1364.
More importantly, this is still an active frontend dependency:
assets/js/sidebar.js:644-668calls${wpAgenticWriter.apiUrl}/chat-history/${postId}and seedsmessagesfrom that response.
Impact:
- The Definition of Done says conversation messages are authoritative in
wpaw_conversations, but the sidebar can still hydrate UI state from legacy post meta. - Migrated or newly created sessions can look empty while old post-meta history appears, or old history can override the expected session model on first render.
- This keeps two mental models alive: "chat belongs to a post meta array" and "chat belongs to a conversation session."
Recommended fix:
- Replace the sidebar history load with a conversation-session/context endpoint.
- If compatibility is still needed, make
/chat-historyreturn session-backed data only, not raw_wpaw_chat_history. - Add a regression check that
assets/js/sidebar.jshas no/chat-historyfetch after migration.
P1: Cost Provider Attribution Is Still Wrong For Many AI Actions
The deprecated record_usage() wrapper was fixed, but the main action hook still defaults missing provider data to OpenRouter:
add_action( 'wp_aw_after_api_request', ..., 10, 9 )expects provider/session/status arguments inincludes/class-cost-tracker.php:48-50.add_request()still defaults$provider = 'openrouter'inincludes/class-cost-tracker.php:124.
Many AI routes still call do_action( 'wp_aw_after_api_request', ... ) with only the first seven arguments, so the cost table will store openrouter even when the actual provider was local backend, Codex, or another fallback:
- Clarity check at
includes/class-gutenberg-sidebar.php:4096-4106. - Meta description at
includes/class-gutenberg-sidebar.php:6217-6228. - Summarize context at
includes/class-gutenberg-sidebar.php:6393-6402. - Multi-pass refinement at
includes/class-gutenberg-sidebar.php:6868-6877. - Article refinement at
includes/class-gutenberg-sidebar.php:6954-6963. - Execution total at
includes/class-gutenberg-sidebar.php:3244-3253. - Regeneration at
includes/class-gutenberg-sidebar.php:3716-3725.
Impact:
- The visible response can say one provider while
wpaw_cost_tracking.providerrecords another. - Cost review, provider debugging, local/cloud usage reporting, and fallback analysis become unreliable.
- This directly undermines the cost counter/tracker area the audit chain is trying to stabilize.
Recommended fix:
- Change the hook default provider from
openroutertounknown. - Add provider/session/status to every AI
wp_aw_after_api_requestcall. - Add a small helper like
track_ai_cost( $post_id, $response, $action, $provider_result, $session_id = '', $status = 'success' )so new routes cannot omit metadata accidentally. - Add one test or static check that no AI cost hook call has only seven payload arguments.
P1: Provider Transparency Is Present But Inconsistent And Not Surfaced In UI
A shared helper now exists:
build_provider_metadata()returnsprovider,selected_provider,fallback_used,warnings, andmodelatincludes/class-gutenberg-sidebar.php:956-963.
Several responses now include nested provider_metadata, for example:
- Plan generation at
includes/class-gutenberg-sidebar.php:2024-2033. - Plan revision at
includes/class-gutenberg-sidebar.php:2189-2197. - Block refinement at
includes/class-gutenberg-sidebar.php:4384-4393. - Summarize context at
includes/class-gutenberg-sidebar.php:6404-6414.
But the contract is not yet consistent:
- Chat responses still write top-level
provider,selected_provider,fallback_used, andwarningsatincludes/class-gutenberg-sidebar.php:1067-1071. - Some non-chat AI responses still omit provider metadata entirely, including execution response at
includes/class-gutenberg-sidebar.php:3255-3260, regenerate block response atincludes/class-gutenberg-sidebar.php:3727-3732, and article refinement response atincludes/class-gutenberg-sidebar.php:6965-6970. assets/js/sidebar.jshas no handling forprovider_metadata,fallback_used, orwarnings, so the user still cannot see fallback/provider behavior in the editor.
Impact:
- API consumers must handle two provider shapes: top-level metadata for chat and nested metadata elsewhere.
- Some workflows still provide no provider transparency.
- The Definition of Done requires the UI to show actual provider used, but the sidebar does not yet render it.
Recommended fix:
- Pick one response shape and enforce it everywhere. The existing Definition of Done examples use top-level
provider,model,cost, andwarnings. - Include the same provider fields in all AI success responses and all stream completion events.
- Update the sidebar to render actual provider and fallback warnings in a compact status line near cost.
- Add a static response-contract checklist for every provider-backed route.
P2: Failed AI Calls Still Usually Do Not Record Failed Cost Attempts
The Definition of Done says failed calls should record an attempt with error status. Many routes still return on provider errors without a cost/status record:
- Clarity check falls back to default questions and returns cost
0without recording a failed provider attempt inincludes/class-gutenberg-sidebar.php:4054-4071. - Regenerate block returns a
regeneration_errorwithout cost tracking inincludes/class-gutenberg-sidebar.php:3706-3714. - Multi-pass refinement returns the provider error directly in
includes/class-gutenberg-sidebar.php:6862-6866. - Article refinement returns the provider error directly in
includes/class-gutenberg-sidebar.php:6945-6949.
Impact:
- Reliability metrics undercount provider failures.
- Users may see no cost but also no durable audit trail explaining why generation failed.
- It becomes harder to debug whether failures are model, provider, prompt, network, or quota related.
Recommended fix:
- Use the same cost helper for failed attempts with
status = 'error',cost = 0, and available provider/model data. - Preserve the user-facing error, but write a cost/event row for observability.
P2: Model Defaults Are Still Fragmented
No central model registry was found. Defaults remain spread across:
- Activation defaults in
wp-agentic-writer.php:140-142. - Sidebar defaults in
includes/class-gutenberg-sidebar.php:278-283. - Settings defaults and fallbacks in
includes/class-settings.phpandincludes/class-settings-v2.php. - OpenRouter provider defaults in
includes/class-openrouter-provider.php:34-69. - JavaScript presets in
assets/js/settings-v2.js:35-56. - Wrapper fallback model groups in
includes/class-wp-ai-client-wrapper.php:94-100. - Image manager fallbacks in
includes/class-image-manager.php:185-249.
Impact:
- A model default can be changed in one layer while another layer silently keeps the old value.
- Cost estimates, UI presets, activation defaults, and provider runtime defaults can disagree.
Recommended fix:
- Create one PHP model registry for task defaults, labels, capabilities, provider support, and deprecation status.
- Localize JS presets from the registry.
- Add a consistency check that activation/settings/provider defaults match the registry.
P2: Editor UI/UX Still Needs Browser Verification
The syntax checks are clean, but no live WordPress editor pass was run.
Impact:
- The exact areas still changing, chat hydration, session persistence, provider warnings, streaming completion, and cost display, are frontend workflow problems as much as backend problems.
- Static checks cannot validate
wp.editPostpackage availability, REST nonce behavior, editor state persistence, or whether new provider metadata appears to users.
Recommended fix:
- Run a browser pass in the block editor after the next implementation pass.
- Verify sidebar open/persist, chat reload continuity, plan/write/refine cost updates, provider warning display, and unauthorized post failures.
Recommended Next Work
- Remove the sidebar dependency on
/chat-history; load active conversation/session context instead. - Replace raw
do_action( 'wp_aw_after_api_request', ... )calls with one cost helper that always records provider/session/status. - Standardize provider metadata response shape and add it to the remaining AI endpoints.
- Render provider/fallback metadata in the sidebar near cost/status feedback.
- Change
add_request()default provider fromopenroutertounknown. - Start model registry consolidation after the chat/context and cost contracts are stable.
- Run the WordPress editor browser workflow pass.
Current Verdict
The sixth-pass implementation is partially proper. It fixed the migrated session id edge case and the deprecated wrapper's misleading provider default.
It is not audit-clean yet. The biggest remaining risk is now cross-layer consistency: the backend moved toward session/provider/cost contracts, but the sidebar and cost ledger still have old assumptions that can make context history and provider cost reporting disagree with what actually happened.