feat: consolidate docs, backend/session infra, and settings updates
This commit is contained in:
249
docs/architecture/PLUGIN_AUDIT_RETRACE_THIRD_PASS_2026-05-24.md
Normal file
249
docs/architecture/PLUGIN_AUDIT_RETRACE_THIRD_PASS_2026-05-24.md
Normal file
@@ -0,0 +1,249 @@
|
||||
# WP Agentic Writer Third Retrace Audit
|
||||
|
||||
Status: COMPLETE / SUPERSEDED
|
||||
Completion marker date: 2026-05-25
|
||||
Next retrace report: `docs/architecture/PLUGIN_AUDIT_RETRACE_FOURTH_PASS_2026-05-25.md`
|
||||
|
||||
Audit date: 2026-05-24
|
||||
Baseline retraced: `docs/architecture/PLUGIN_AUDIT_RETRACE_SECOND_PASS_2026-05-24.md`
|
||||
Scope: third pass after second-retrace implementation, covering UI/UX, editor runtime, REST authorization, conversation context/history, cost tracking, provider/model metadata, migrations, and release readiness.
|
||||
|
||||
## Executive Summary
|
||||
|
||||
The second-pass implementation closed several concrete blockers:
|
||||
|
||||
- `assets/js/sidebar.js` now passes JavaScript syntax validation.
|
||||
- `assets/js/settings-v2.js` still passes JavaScript syntax validation.
|
||||
- Full PHP syntax validation passes.
|
||||
- `WP_Agentic_Writer_Cost_Tracker::record_usage()` now exists, so the previous missing-method fatal is closed.
|
||||
- Conversation migration now reads `wpaw_conversations_db_version`.
|
||||
- Deactivation now clears `wpaw_cleanup_old_sessions`.
|
||||
- Previously named post-config, cost, section-block, and image REST handlers now check target-post permissions.
|
||||
|
||||
The plugin is still not ready to move exclusively into new chat/context feature work. The largest remaining risks are now more subtle:
|
||||
|
||||
1. The sidebar debug logger is syntactically valid but recursively calls itself, so any error log path can crash into a stack overflow.
|
||||
2. Core generation/chat REST endpoints still accept `postId` under generic `edit_posts` permission and then read or write target post meta.
|
||||
3. Clear context accepts `sessionId`, but the current frontend calls do not send one, and the sidebar no longer appears to maintain a session id in state.
|
||||
4. The legacy chat-history route and legacy post-meta helper methods remain active, while the context service comment says legacy history should migrate on read.
|
||||
5. Cost compatibility tracking no longer fatals, but it records misleading provider/model metadata.
|
||||
|
||||
## 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.
|
||||
- Static trace of second-pass findings against current code.
|
||||
- No live WordPress browser workflow was run in this pass.
|
||||
|
||||
## Second-Pass Status Trace
|
||||
|
||||
| Second-pass item | Current status | Evidence |
|
||||
|---|---:|---|
|
||||
| Sidebar JS parse failure | Fixed, but runtime logger bug remains | `node -c assets/js/sidebar.js` passes; logger recursively calls itself at `assets/js/sidebar.js:17-20`. |
|
||||
| Missing `record_usage()` method | Fixed, but attribution is inaccurate | Method exists at `includes/class-cost-tracker.php:164-176`; wrapper still passes provider names through the `$model` parameter. |
|
||||
| Conversation migration version gate | Improved | `wpaw_run_migrations()` reads `wpaw_conversations_db_version` at `includes/class-conversation-migration.php:67-72`; main table bootstrap also checks it at `wp-agentic-writer.php:250-259`. |
|
||||
| Clear-context uses context service | Partially fixed | Handler accepts `sessionId` and calls context service at `includes/class-gutenberg-sidebar.php:1230-1244`; frontend clear calls omit `sessionId`. |
|
||||
| Post-config/cost/section/image permissions | Fixed for named handlers | Target post checks added in the handlers traced below. |
|
||||
| Cost table base schema | Improved | Base schema includes `session_id`, `provider`, and `status`; runtime upgrade still assumes the table exists. |
|
||||
| Deactivation cleanup | Fixed | `wp_clear_scheduled_hook( 'wpaw_cleanup_old_sessions' )` added at `wp-agentic-writer.php:271-278`. |
|
||||
| Model registry/default unification | Still open | Defaults remain spread across activation, settings PHP, settings JS, providers, image manager, and WP AI wrapper. |
|
||||
|
||||
## Critical Findings
|
||||
|
||||
### P0: Sidebar Logger Recurses Instead of Calling Console
|
||||
|
||||
The syntax error from the last report is fixed, but the logger implementation is still broken:
|
||||
|
||||
- `assets/js/sidebar.js:17` defines `log` as `wpawLog.log('[WPAW]', ...args)`.
|
||||
- `assets/js/sidebar.js:18` defines `error` as `wpawLog.error('[WPAW]', ...args)`.
|
||||
- `assets/js/sidebar.js:20` defines `warn` as `wpawLog.warn('[WPAW]', ...args)`.
|
||||
|
||||
Impact:
|
||||
|
||||
- Any `wpawLog.error()` call can recurse until the browser throws a maximum call stack error.
|
||||
- With debug enabled, any `wpawLog.log()` or `wpawLog.warn()` call can do the same.
|
||||
- Error handling becomes unreliable exactly when the sidebar needs it most, especially during streaming, generation, migration, and cost refresh failures.
|
||||
|
||||
Recommended fix:
|
||||
|
||||
- Change the sidebar logger to match the working settings logger:
|
||||
- `log: (...args) => { if (isDebug) console.log('[WPAW]', ...args); }`
|
||||
- `error: (...args) => console.error('[WPAW]', ...args)`
|
||||
- `info: (...args) => { if (isDebug) console.info('[WPAW]', ...args); }`
|
||||
- `warn: (...args) => { if (isDebug) console.warn('[WPAW]', ...args); }`
|
||||
- Add a simple static check or unit test that rejects `wpawLog.*` inside the logger object itself.
|
||||
|
||||
### P0: Core Post-Scoped Generation Routes Still Lack Upfront `edit_post` Checks
|
||||
|
||||
The named endpoints from the previous report were patched, but the larger route family still relies on generic `edit_posts` route permission:
|
||||
|
||||
- `/chat` is registered with only `check_permissions` at `includes/class-gutenberg-sidebar.php:325-333`.
|
||||
- `/generate-plan` is registered with only `check_permissions` at `includes/class-gutenberg-sidebar.php:377-385`.
|
||||
- `/execute-article` is registered with only `check_permissions` at `includes/class-gutenberg-sidebar.php:398-406`.
|
||||
- `check_permissions()` only checks `current_user_can( 'edit_posts' )` at `includes/class-gutenberg-sidebar.php:930-932`.
|
||||
|
||||
Handlers then use `postId` to read or mutate post-scoped data:
|
||||
|
||||
- `handle_chat_request()` reads post config, detected language, and focus keyword from the target post at `includes/class-gutenberg-sidebar.php:955-978`.
|
||||
- `handle_generate_plan()` reads post memory and writes `_wpaw_plan`, `_wpaw_detected_language`, and `_wpaw_memory` at `includes/class-gutenberg-sidebar.php:1825-1969`.
|
||||
- `handle_execute_article()` reads `_wpaw_plan` and can update `_wpaw_plan` after generation at `includes/class-gutenberg-sidebar.php:2925-3176`.
|
||||
- `handle_reformat_blocks()` reads `_wpaw_plan` at `includes/class-gutenberg-sidebar.php:3509-3529`.
|
||||
- `handle_regenerate_block()` accepts `postId` and records cost against it at `includes/class-gutenberg-sidebar.php:3594-3642`.
|
||||
|
||||
Impact:
|
||||
|
||||
- A user with generic author/editor access can potentially read or affect plugin state for posts they cannot edit.
|
||||
- Cost records can be attributed to unauthorized posts.
|
||||
- Chat/context behavior can leak focus keywords, detected language, memory summaries, and plans across post boundaries.
|
||||
|
||||
Recommended fix:
|
||||
|
||||
- Add an upfront helper such as `require_post_permission_from_params( $params, 'postId' )`.
|
||||
- Call it in every handler that accepts `postId` or reads/writes post meta, not only the endpoints listed in a prior audit.
|
||||
- For postless conversation mode, allow `postId = 0` only when no post meta is read or written.
|
||||
|
||||
## High Priority Findings
|
||||
|
||||
### P1: Clear Context Still Does Not Clear the Active Session From the UI
|
||||
|
||||
The backend handler now accepts `sessionId` and calls the context service:
|
||||
|
||||
- `handle_clear_context()` reads `sessionId` at `includes/class-gutenberg-sidebar.php:1230-1234`.
|
||||
- It calls `$this->context_service->clear_context( $session_id, $post_id )` at `includes/class-gutenberg-sidebar.php:1243-1244`.
|
||||
|
||||
But both frontend clear calls still send only the post id:
|
||||
|
||||
- Reset command sends `JSON.stringify({ postId: postId })` at `assets/js/sidebar.js:1661-1669`.
|
||||
- Clear chat context sends `JSON.stringify({ postId })` at `assets/js/sidebar.js:2023-2031`.
|
||||
- A search of `assets/js/sidebar.js` found no active `sessionId`/`currentSession` state being maintained.
|
||||
|
||||
Impact:
|
||||
|
||||
- Clear context deletes legacy post meta but does not clear the active conversation table row when the session id is omitted.
|
||||
- Users can see a cleared frontend, then later reload or resume a session that still contains old messages.
|
||||
- This undermines the goal of making the conversations table the source of truth.
|
||||
|
||||
Recommended fix:
|
||||
|
||||
- Reintroduce explicit active session state in the sidebar.
|
||||
- Send `sessionId` on `/clear-context`.
|
||||
- If the UI cannot provide a session id, the backend should clear active sessions for that post owned by the current user or return a clear error explaining that the session id is required.
|
||||
|
||||
### P1: Legacy Chat History Read/Write Helpers Still Exist Beside the Session Store
|
||||
|
||||
The main chat send path no longer writes `_wpaw_chat_history`, but old route/helper code remains:
|
||||
|
||||
- `/chat-history/(?P<post_id>\d+)` is still registered at `includes/class-gutenberg-sidebar.php:346-354`.
|
||||
- `handle_get_chat_history()` reads legacy post meta at `includes/class-gutenberg-sidebar.php:1261-1277`.
|
||||
- `update_post_chat_history()` still writes `_wpaw_chat_history` at `includes/class-gutenberg-sidebar.php:1289-1322`.
|
||||
- `migrate_legacy_chat_history()` still keeps `_wpaw_chat_history` after migration because deletion is commented out at `includes/class-context-service.php:299-300`.
|
||||
- The context service header says legacy history migrates on read, but `get_context()` does not call migration when no session exists at `includes/class-context-service.php:62-87`.
|
||||
|
||||
Impact:
|
||||
|
||||
- The system still has two history stores and one migration route, rather than one reliable source of truth.
|
||||
- Legacy meta can be migrated repeatedly or surfaced by old endpoints after the active session has diverged.
|
||||
- Future chat/context work remains likely to regress because old and new paths coexist.
|
||||
|
||||
Recommended fix:
|
||||
|
||||
- Remove or deprecate the legacy `/chat-history` endpoint once the frontend uses conversation sessions.
|
||||
- Delete legacy post meta after successful migration, or write a `_wpaw_chat_history_migrated` marker and never re-import it.
|
||||
- Make `get_context()` perform migrate-on-read if the post has legacy history and no session exists.
|
||||
|
||||
### P1: Cost Compatibility Method Prevents Fatal, But Mislabels Model/Provider
|
||||
|
||||
`record_usage()` now exists, but its signature and callers do not align with the newer cost schema:
|
||||
|
||||
- Method signature is `record_usage( $post_id, $action, $model, $cost, $session_id = '' )` at `includes/class-cost-tracker.php:164`.
|
||||
- It always records provider as `'openrouter'` at `includes/class-cost-tracker.php:172`.
|
||||
- The WP AI core path passes `'core'` as the `$model` argument at `includes/class-wp-ai-client-wrapper.php:197-202`.
|
||||
- The legacy provider path passes `$provider_result->actual_provider` as the `$model` argument at `includes/class-wp-ai-client-wrapper.php:249-254`.
|
||||
|
||||
Impact:
|
||||
|
||||
- Cost rows from WP AI wrapper paths can show model=`core` or model=`local_backend/openrouter` and provider=`openrouter`, regardless of the actual provider/model.
|
||||
- Cost analytics, provider comparison, and budget debugging become unreliable.
|
||||
|
||||
Recommended fix:
|
||||
|
||||
- Change `record_usage()` to accept an options array or explicit `$provider`, `$model`, `$input_tokens`, `$output_tokens`, `$session_id`, `$status`.
|
||||
- Update wrapper callers to pass the selected model and actual provider separately.
|
||||
- Keep the old positional method only as a deprecated compatibility wrapper.
|
||||
|
||||
### P1: Cost Table Runtime Upgrade Still Does Not Self-Heal a Missing Table
|
||||
|
||||
The base schema is improved, but runtime upgrade remains fragile:
|
||||
|
||||
- `maybe_upgrade_table()` calls `DESCRIBE {$table_name}` at `includes/class-cost-tracker.php:71-76`.
|
||||
- It does not check whether the table exists before calling `in_array()` on the column list.
|
||||
- Main table creation can still be skipped when `wpaw_db_version` is current at `wp-agentic-writer.php:230-260`.
|
||||
|
||||
Impact:
|
||||
|
||||
- A site with a current main DB version but missing cost table may not recover cleanly.
|
||||
- First access to the cost tracker can warn or fail before any cost row is recorded.
|
||||
|
||||
Recommended fix:
|
||||
|
||||
- Add a `SHOW TABLES LIKE` guard before `DESCRIBE`.
|
||||
- If the table is missing, call the idempotent cost table creator.
|
||||
- Prefer per-table schema checks over relying only on `wpaw_db_version`.
|
||||
|
||||
## Medium Priority Findings
|
||||
|
||||
### P2: Provider Metadata Is Still Incomplete Outside Chat
|
||||
|
||||
Chat responses and streaming completion events include provider metadata, but several non-chat routes still return only generated content, variants, or blocks. Cost records often receive provider metadata through the hook, but the UI/API response contract is not uniform.
|
||||
|
||||
Recommended fix:
|
||||
|
||||
- Standardize generated response envelopes across plan, execute, refine, keyword, image recommendation, and image variant endpoints.
|
||||
- Include `provider`, `selected_provider`, `fallback_used`, `warnings`, `model`, and `cost` where available.
|
||||
|
||||
### P2: Model Defaults Remain Fragmented
|
||||
|
||||
Defaults still exist in multiple places:
|
||||
|
||||
- Activation defaults in `wp-agentic-writer.php`.
|
||||
- Sidebar localized defaults in `includes/class-gutenberg-sidebar.php`.
|
||||
- Settings V1/V2 PHP defaults in `includes/class-settings.php` and `includes/class-settings-v2.php`.
|
||||
- JS presets in `assets/js/settings-v2.js`.
|
||||
- Provider defaults in `includes/class-openrouter-provider.php`, `includes/class-local-backend-provider.php`, `includes/class-codex-provider.php`, and `includes/class-wp-ai-client-wrapper.php`.
|
||||
- Image defaults in `includes/class-image-manager.php`.
|
||||
|
||||
Impact:
|
||||
|
||||
- A model update can appear fixed in settings but remain stale in provider/runtime paths.
|
||||
- Cost estimates, UI presets, and actual generation model can diverge.
|
||||
|
||||
Recommended fix:
|
||||
|
||||
- Create a PHP model registry/default resolver and localize its resolved values to JS.
|
||||
- Keep legacy keys such as `execution_model` only as migrations into canonical task keys.
|
||||
|
||||
### P2: Sidebar Uses `wp.editPost` Without the Previous Fallback
|
||||
|
||||
The current sidebar imports `PluginSidebar` from `wp.editPost` at `assets/js/sidebar.js:8-10`. The previous code had a fallback for `wp.editor` versus `wp.editPost`. This may be intentional, but it should be verified against the WordPress versions the plugin supports.
|
||||
|
||||
Recommended fix:
|
||||
|
||||
- Browser-test the sidebar on the minimum supported WordPress version.
|
||||
- Restore a compatibility fallback if needed.
|
||||
|
||||
## Definition of Done Gates for This Pass
|
||||
|
||||
Before considering this retrace implementation complete:
|
||||
|
||||
- Fix sidebar logger recursion and verify error/debug calls in browser devtools.
|
||||
- Add target-post permission checks to all handlers that accept `postId`, especially chat, generate-plan, execute-article, reformat-blocks, regenerate-block, clarity/SEO/GEO, refinement, and any post-scoped streaming path.
|
||||
- Make clear-context clear the actual active session from the UI, not only legacy post meta.
|
||||
- Remove, migrate, or hard-deprecate legacy `_wpaw_chat_history` routes and helper writes.
|
||||
- Fix `record_usage()` metadata attribution so model and provider are not swapped or hardcoded.
|
||||
- Add a missing-table guard to the cost tracker migration path.
|
||||
- Keep PHP and JS syntax checks passing.
|
||||
|
||||
## Current Decision
|
||||
|
||||
Do not move fully into new chat/context implementation yet. The second-pass checklist is mostly implemented, but the current code still has one sidebar runtime blocker, one broad post-authorization gap, and an incomplete conversation source-of-truth transition. Fix those first, then chat/context work will have a much firmer base.
|
||||
Reference in New Issue
Block a user