Files
wp-agentic-writer/docs/architecture/PLUGIN_AUDIT_RETRACE_SEVENTH_PASS_2026-05-25.md

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_id and 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 as unknown instead of hard-coding openrouter.

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_history instead 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 in includes/class-gutenberg-sidebar.php:346-354.
  • handle_get_chat_history() still returns messages from get_post_chat_history() in includes/class-gutenberg-sidebar.php:1300-1326.
  • get_post_chat_history() still reads _wpaw_chat_history directly in includes/class-gutenberg-sidebar.php:1354-1364.

More importantly, this is still an active frontend dependency:

  • assets/js/sidebar.js:644-668 calls ${wpAgenticWriter.apiUrl}/chat-history/${postId} and seeds messages from 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-history return session-backed data only, not raw _wpaw_chat_history.
  • Add a regression check that assets/js/sidebar.js has no /chat-history fetch 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 in includes/class-cost-tracker.php:48-50.
  • add_request() still defaults $provider = 'openrouter' in includes/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.provider records 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 openrouter to unknown.
  • Add provider/session/status to every AI wp_aw_after_api_request call.
  • 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() returns provider, selected_provider, fallback_used, warnings, and model at includes/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, and warnings at includes/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 at includes/class-gutenberg-sidebar.php:3727-3732, and article refinement response at includes/class-gutenberg-sidebar.php:6965-6970.
  • assets/js/sidebar.js has no handling for provider_metadata, fallback_used, or warnings, 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, and warnings.
  • 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 0 without recording a failed provider attempt in includes/class-gutenberg-sidebar.php:4054-4071.
  • Regenerate block returns a regeneration_error without cost tracking in includes/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.php and includes/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.editPost package 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.
  1. Remove the sidebar dependency on /chat-history; load active conversation/session context instead.
  2. Replace raw do_action( 'wp_aw_after_api_request', ... ) calls with one cost helper that always records provider/session/status.
  3. Standardize provider metadata response shape and add it to the remaining AI endpoints.
  4. Render provider/fallback metadata in the sidebar near cost/status feedback.
  5. Change add_request() default provider from openrouter to unknown.
  6. Start model registry consolidation after the chat/context and cost contracts are stable.
  7. 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.