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,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.