feat: consolidate docs, backend/session infra, and settings updates

This commit is contained in:
Dwindi Ramadhana
2026-05-28 00:58:20 +07:00
parent 2424acf726
commit 44e06eed88
102 changed files with 35423 additions and 11181 deletions

View File

@@ -0,0 +1,276 @@
# WP Agentic Writer Retrace Audit
Status: COMPLETE / SUPERSEDED
Completion marker date: 2026-05-24
Next retrace report: `docs/architecture/PLUGIN_AUDIT_RETRACE_SECOND_PASS_2026-05-24.md`
Audit date: 2026-05-24
Baseline retraced: `docs/architecture/PLUGIN_AUDIT_FOLLOWUP_2026-05-24.md`
Scope: current implementation after follow-up fixes, with emphasis on UI/UX, system boundaries, conversation context/history, cost tracker, provider/model routing, migrations, and data lifecycle.
## Executive Summary
The follow-up implementation closed several important items, but the plugin is not yet clean enough to shift only into chat/context implementation. The highest-risk remaining problem is a new provider contract mismatch: `WP_Agentic_Writer_Provider_Manager::get_provider_for_task()` now returns a `WPAW_Provider_Selection_Result`, but several older classes still treat the return value as a provider and call `->chat()` or `->generate_image()` directly. That can fatal in image, keyword, and WP AI wrapper paths.
The second urgent issue is streaming chat state: `stream_chat_request()` currently references `$accumulated_content`, `$chunks_emitted`, and `$last_user_message` without initializing them. That can corrupt streaming persistence and produce warnings or missing session messages.
Good news: OpenRouter cache separation is now implemented, several conversation permissions were tightened, settings cost-log pagination was improved, and chat responses now include provider metadata. But there are still cross-cutting runtime, migration, cost-ledger, and authorization gaps.
## Verification Performed
- PHP syntax check across plugin PHP files: passed.
- JS syntax check: `node -c assets/js/sidebar.js` and `node -c assets/js/settings-v2.js` passed.
- Static trace of follow-up audit items against current code.
- No live WordPress browser workflow was run in this pass.
## Follow-up Status Trace
| Follow-up item | Current status | Evidence |
|---|---:|---|
| Split OpenRouter model cache keys | Fixed | Full objects use `wpaw_openrouter_model_objects`; IDs use `wpaw_openrouter_model_ids` in `includes/class-openrouter-provider.php:106-264`. |
| Add post/session auth for conversation list/create/link | Mostly fixed | `edit_post` added for post-linked conversation routes and link-post checks session access in `includes/class-gutenberg-sidebar.php:7263-7555`. |
| Add writing-state post auth | Fixed | `edit_post` checks and status allowlist added in `includes/class-gutenberg-sidebar.php:823-887`. |
| Provider fallback metadata for chat | Partially fixed | Provider result object and chat response metadata exist in `includes/class-provider-manager.php:20-92` and `includes/class-gutenberg-sidebar.php:987-1029`, but helper classes still use the old API. |
| Cost log SQL pagination | Improved | Grouping/pagination moved into SQL in `includes/class-settings-v2.php:645-667`. |
| Conversation table migration versioning | Still open | Migration still checks `wpaw_db_version`, not `wpaw_conversations_db_version`, in `includes/class-conversation-migration.php:67-72`. |
| Single source of truth for chat messages | Partially fixed | Chat no longer writes new `_wpaw_chat_history`, but legacy methods/routes remain and migration does not delete migrated meta. |
| Cost tracker provider/session/status ledger | Partially fixed | New columns and parameters exist, but hook accepts only 7 args in `includes/class-cost-tracker.php:48-53`, so session/status are dropped. |
| Model registry/default unification | Still open | Defaults remain fragmented across activation, providers, settings PHP, JS, and WP AI wrapper. |
| Uninstall/data lifecycle | Still open | Main uninstall and `uninstall.php` remain inconsistent and incomplete. |
| Debug logging | Still open | Backend and frontend debug logs remain broad. |
## Critical Findings
### P0: Provider Manager Return Contract Breaks Older Callers
`get_provider_for_task()` now returns `WPAW_Provider_Selection_Result`, which is correct for provider transparency. But not every caller was updated to use `$provider_result->provider`.
Broken paths:
- Image placement analysis calls `$provider->chat()` on the selection result in `includes/class-image-manager.php:218-219`.
- Image prompt generation does the same in `includes/class-image-manager.php:295-296`.
- Image variant generation calls `$provider->generate_image()` on the selection result in `includes/class-image-manager.php:478-483`.
- Keyword suggester calls `$provider->chat()` on the selection result in `includes/class-keyword-suggester.php:32-84`.
- WP AI legacy wrapper calls `$provider->chat()`, `$provider->chat_stream()`, and `$provider->get_name()` on the selection result in `includes/class-wp-ai-client-wrapper.php:225-252` and `includes/class-wp-ai-client-wrapper.php:272-280`.
Impact:
- Image generation, image prompt analysis, keyword suggestions, and WP AI fallback paths can fatal with "Call to undefined method WPAW_Provider_Selection_Result::chat()".
- This is a classic fix-A-break-B regression from changing a shared service contract.
Recommended fix:
- Update all callers to:
- `$provider_result = WP_Agentic_Writer_Provider_Manager::get_provider_for_task( ... );`
- `$provider = $provider_result->provider;`
- propagate `$provider_result->actual_provider`, `fallback_used`, and `warnings` where returned or tracked.
- Consider adding `__call()` only as a temporary compatibility shim if too many callers exist, but explicit updates are safer.
### P0: Streaming Chat Uses Uninitialized State
`stream_chat_request()` references variables that are never initialized in the current implementation.
Evidence:
- Closure captures `$accumulated_content` and `$chunks_emitted` by reference in `includes/class-gutenberg-sidebar.php:1089-1109`.
- `$chunks_emitted` is compared at `includes/class-gutenberg-sidebar.php:1111-1121`.
- `$accumulated_content` is used at `includes/class-gutenberg-sidebar.php:1158-1164`.
- `$last_user_message` is stored into session messages at `includes/class-gutenberg-sidebar.php:1169-1177`, but is never assigned inside the method.
Impact:
- Streaming chat can emit warnings/notices and fail to persist the user message correctly.
- Session history may save an empty or undefined user message while the assistant message is stored.
- This directly affects the chat/context path the team wants to focus on.
Recommended fix:
- Restore initializers at the top of `stream_chat_request()`:
- `$accumulated_content = '';`
- `$chunks_emitted = 0;`
- `$last_user_message = $this->get_last_user_message( $messages );`
- `$total_cost = 0;`
### P0: Image Generation Is Broken By Provider Contract Mismatch
Image generation was already a sensitive flow because it touches model routing, temporary files, cost, and DB state. The provider result contract now breaks it before generation.
Evidence:
- `generate_image_variants()` assigns provider result to `$provider` at `includes/class-image-manager.php:478`.
- It calls `$provider->generate_image()` at `includes/class-image-manager.php:483`.
Impact:
- `/generate-image` can fail at runtime even though PHP syntax passes.
- Image generation costs still will not reliably reach the main cost ledger because generation is interrupted before any ledger call.
Recommended fix:
- Unwrap provider result and track image generation through `wp_aw_after_api_request` with provider/session/status metadata.
## High Priority Findings
### P1: Cost Tracker Hook Drops Session and Status Metadata
The Cost Tracker now accepts provider, session ID, and status, but the hook registration only allows 7 arguments.
Evidence:
- Hook registration accepts 7 args in `includes/class-cost-tracker.php:48-53`.
- `add_request()` signature expects 9 args in `includes/class-cost-tracker.php:113-133`.
- Chat emits provider, session ID, and status in `includes/class-gutenberg-sidebar.php:1011-1023` and `includes/class-gutenberg-sidebar.php:1144-1156`.
Impact:
- Provider may be recorded, but `session_id` and explicit `status` are silently dropped.
- Failed calls still are not intentionally recorded.
- Cost records cannot be reconciled to conversation context.
Recommended fix:
- Change hook registration to `add_action( 'wp_aw_after_api_request', array( $this, 'add_request' ), 10, 9 );`.
- Add explicit failure ledger entries around provider errors.
- Add `error_code`/`request_id` if this is meant to be a true ledger.
### P1: Conversation Migration Is Still Version-fragile
The follow-up audit recommended conversation-specific migration versioning, but the migration runner still checks the main DB version.
Evidence:
- Conversation table creation stores `wpaw_conversations_db_version` in `includes/class-conversation-migration.php:43-47`.
- `wpaw_run_migrations()` still reads `wpaw_db_version` in `includes/class-conversation-migration.php:67-72`.
- Main table creation remains gated by `wpaw_db_version < 1.1.0` in `wp-agentic-writer.php:223-241`.
- Conversation cleanup cron is scheduled at include time in `includes/class-conversation-migration.php:94-99`.
Impact:
- Existing installs with `wpaw_db_version=1.1.0` but no conversation table can still be skipped.
- Cleanup cron can run against a missing table.
Recommended fix:
- Run an idempotent `wpaw_ensure_conversations_table()` based on `wpaw_conversations_db_version` or direct table existence.
- Unschedule `wpaw_cleanup_old_sessions` on deactivation.
### P1: Chat History Is Better, But Legacy Read/Migration Still Keeps Two Truths Alive
New chat writes no longer call `update_post_chat_history()`, which is good. But legacy post-meta read/write helpers and migration behavior still keep `_wpaw_chat_history` alive.
Evidence:
- New chat comments say legacy meta is deprecated in `includes/class-gutenberg-sidebar.php:1031-1053` and `includes/class-gutenberg-sidebar.php:1167-1187`.
- `/chat-history/{post_id}` still reads legacy meta in `includes/class-gutenberg-sidebar.php:1240-1256`.
- Legacy update helper still exists and writes meta in `includes/class-gutenberg-sidebar.php:1268-1301`.
- Context Service `get_context()` does not migrate on read in `includes/class-context-service.php:62-87`.
- Migration still leaves legacy meta in place in `includes/class-context-service.php:299-300`.
Impact:
- Legacy history can still be surfaced, migrated more than once, or diverge from the session table.
- Clear context does not clear an active session unless another caller invokes `Context_Service::clear_context()` with a session ID.
Recommended fix:
- Make `/chat-history/{post_id}` migration-only or remove it after frontend no longer needs it.
- Delete or mark `_wpaw_chat_history` after successful migration.
- Call migration from `Context_Service::get_context()` when legacy meta exists.
- Update `handle_clear_context()` to require `edit_post` and clear active session messages when `sessionId` is present.
### P1: Post-scoped Authorization Remains Incomplete Outside Conversation Routes
Conversation routes improved, but other post-scoped routes still trust the broad `edit_posts` permission callback.
Examples:
- Post config get/update read and write `_wpaw_post_config` without `edit_post` in `includes/class-gutenberg-sidebar.php:1722-1756`.
- Cost tracking for a post lacks a target post check in `includes/class-gutenberg-sidebar.php:3623-3629`.
- Section block mapping reads/writes post meta without `edit_post` in `includes/class-gutenberg-sidebar.php:4776-4835`.
- Image recommendation/generate/commit routes are registered with broad permissions at `includes/class-gutenberg-sidebar.php:593-620` and handlers lack target post checks in `includes/class-gutenberg-sidebar.php:6457-6520`.
Impact:
- Multi-author sites can still leak or mutate post-scoped state across users.
- This is larger than conversations and should be fixed as a shared helper.
Recommended fix:
- Add a helper like `require_edit_post_or_error( $post_id, $action )`.
- Apply it to every route that reads or writes post meta, cost, image state, SEO/GEO state, or generated content.
## Medium Priority Findings
### P2: Provider Metadata Is Not Yet Consistent Across All AI Responses
Chat responses now include provider metadata, but planning/writing/refinement/clarity helper responses often only track cost internally and do not return provider/warning metadata to the frontend.
Evidence:
- Chat adds metadata in `includes/class-gutenberg-sidebar.php:1025-1029` and streaming complete event includes metadata in `includes/class-gutenberg-sidebar.php:1190-1199`.
- Many other calls use `$provider_result` internally but return older response shapes.
Opportunity:
- Standardize an AI response envelope for all REST/SSE flows: `content`, `provider`, `selected_provider`, `model`, `fallback_used`, `warnings`, `cost`, `usage`, `request_id`.
### P2: Model Defaults Are Still Fragmented
No single model registry exists yet. Defaults remain spread across activation, OpenRouter provider, settings V2, legacy settings, JS presets, Image Manager, and WP AI wrapper.
Evidence:
- Activation still stores `execution_model` in `wp-agentic-writer.php:140-142`.
- OpenRouter defaults differ across properties in `includes/class-openrouter-provider.php:34-69`.
- Settings V2 still has multiple fallback groups in `includes/class-settings-v2.php:105-111`, `includes/class-settings-v2.php:228-273`, and `includes/class-settings-v2.php:989-994`.
- JS presets remain in `assets/js/settings-v2.js:24-38`.
- Image Manager has its own writing/image fallbacks in `includes/class-image-manager.php:183-248`.
Recommendation:
- Add a PHP `Model_Registry` and localize it to settings JS.
- Stop writing `execution_model` for fresh installs.
### P2: Cost Table Schema Is Split Between Creation and Runtime ALTER
The runtime upgrader adds columns, but the base table creation in the main plugin file still creates the old schema.
Evidence:
- Base schema lacks provider/session/status in `wp-agentic-writer.php:198-209`.
- Runtime `ALTER TABLE` adds provider/session/status in `includes/class-cost-tracker.php:61-97`.
Risk:
- Fresh installs depend on Cost Tracker initialization to complete schema.
- Failed `DESCRIBE` on a missing table is not handled before `in_array()` checks.
Recommendation:
- Move the latest schema into `wp_agentic_writer_create_cost_table()`.
- Make runtime migrations table-existence safe and versioned.
### P2: Uninstall Is Still Incomplete And Duplicated
The main uninstall hook and `uninstall.php` remain inconsistent.
Evidence:
- Main uninstall deletes settings and some tables in `wp-agentic-writer.php:269-294`.
- `uninstall.php` deletes settings, `_wpaw_plan`, and cost table only in `uninstall.php:12-21`.
Still missing:
- `wpaw_conversations`, `wp_agentic_writer_custom_models`, DB version options, transients, scheduled `wpaw_cleanup_old_sessions`, `_wpaw_chat_history`, `_wpaw_memory`, `_wpaw_post_config`, writing-state meta, user preferences, and image post meta.
### P2: Debug Logging Remains Too Broad
There is a localized `debug` flag, but console logs and backend logs remain noisy.
Evidence:
- `wpAgenticWriter.debug` is localized in `includes/class-gutenberg-sidebar.php:257`.
- Frontend still logs migration/session/clarity details in `assets/js/sidebar.js:308-322`, `assets/js/sidebar.js:5619-5630`, and `assets/js/sidebar.js:6130-6157`.
- Settings JS logs model and cost debug info in `assets/js/settings-v2.js:52-130` and `assets/js/settings-v2.js:390-503`.
- Provider/backend `error_log()` calls remain in provider files and `class-gutenberg-sidebar.php`.
### P2: Changelog Policy Still Fails
`docs/DEFINITION_OF_DONE.md` requires updating `CHANGELOG.md`, but no `CHANGELOG.md` file was found.
## Recommended Next Work Queue
### Do Before Chat/Context Focus
1. Fix provider result contract callers in Image Manager, Keyword Suggester, and WP AI Client wrapper.
2. Restore streaming chat variable initialization.
3. Change Cost Tracker hook accepted args from 7 to 9 and add failure recording.
4. Fix conversation migration versioning and cleanup cron scheduling.
### Then Focus Chat/Context
1. Make `Context_Service::get_context()` migrate legacy history on read.
2. Delete or mark migrated `_wpaw_chat_history`.
3. Make clear-context clear both post context and active session messages.
4. Add a visible context inspector in the UI: active session, message count, linked post, focus keyword, language, provider/model, cost estimate.
### Later
1. Apply `edit_post` checks to every post-scoped route.
2. Centralize model defaults in a registry.
3. Consolidate uninstall/data retention.
4. Gate logging.
5. Add `CHANGELOG.md`.
## Conclusion
Do not switch solely to chat/context yet. The implementation is closer, but the provider contract mismatch and streaming-chat uninitialized state should be fixed first because they directly break runtime behavior and chat persistence. After those are resolved, chat/context is the right next focus.