# WP Agentic Writer Sidebar 1:1 Real Migration Tasklist

**Source of truth:** `assets/js/sidebar.js`
**Audit ledger:** `SIDEBAR_1_TO_1_TASKLIST.md`
**Migration plan:** `SIDEBAR_1_TO_1_MIGRATION.md`
**Migration target:** `assets/js/src/index.jsx`

This tasklist tracks real implementation progress only. Do not check an item because the behavior exists in `sidebar.js`; check it only after the migrated target, build pipeline, or runtime has actually been implemented and verified.

## Current Status

- [x] Preserve `SIDEBAR_1_TO_1_TASKLIST.md` as the Phase 0-14 parity/audit ledger.
- [x] Create monolithic migration target at `assets/js/src/index.jsx`.
- [x] Convert all `wp.element.createElement` / `React.createElement` render calls in the monolith to JSX.
- [x] Complete Phase A manual JSX conversion verification.
- [x] Preserve intentional `document.createElement("div")` DOM usage.
- [x] Verify migrated JSX parses with `tsc --allowJs --checkJs false --jsx preserve --noEmit`.
- [x] Verify migrated JSX still contains all 226 class inventory tokens.
- [x] Verify endpoint inventory remains present after JSX conversion.
- [x] Verify Gutenberg/browser side-effect inventory remains present after JSX conversion.
- [x] Verify final plugin registration still renders `ConnectedSidebar`.
- [x] Build pipeline is created and verified.
- [x] Runtime parity smoke is complete with accepted exception for source-unreachable writing empty-state branch.
- [x] Enqueue switch has been made to migrated built output.
- [x] No extraction has been performed.

## Real Migration Rules

- [x] Keep `assets/js/sidebar.js` untouched as the source of truth.
- [x] Keep `assets/js/src/index.jsx` as the monolithic migration target.
- [x] Do not switch WordPress enqueue to migrated output until compiled output is verified.
- [x] Do not extract helper files until monolith runtime parity is proven.
- [x] Do not rename functions, state variables, refs, class names, endpoint strings, storage keys, or event names.
- [x] Do not simplify JSX conditions, children order, callbacks, object shapes, SVG strings, or `dangerouslySetInnerHTML`.
- [x] Record every real migration step here immediately after it is done.

## Phase A - JSX Conversion Verification

- [x] Convert plugin icon from `wp.element.createElement("img")` to JSX.
- [x] Convert render surfaces from `wp.element.createElement` to JSX.
- [x] Convert final main return tree to JSX.
- [x] Preserve `RawHTML` usage as JSX component usage.
- [x] Preserve WordPress components: `PluginSidebarMoreMenuItem`, `PluginSidebar`, `Panel`, `TextareaControl`, `TextControl`, `CheckboxControl`, and `Button`.
- [x] Preserve dynamic class expressions.
- [x] Preserve `dangerouslySetInnerHTML` SVG/html strings.
- [x] Preserve stream/helper/runtime code outside render conversion.
- [x] Manually audit converted JSX comments and expression containers for parser-safe, behavior-neutral output.
- [x] Manually audit `renderRefineAllConfirmModal` against source lines 6389-6455 after full conversion.
- [x] Manually audit `renderClarification` against source lines 8477-8829.
- [x] Manually audit conversation session actions against source lines 8830-9004.
- [x] Fix JSX spread-child artifact in older session list: `...availableSessions.slice(1).map(...)` is now `{availableSessions.slice(1).map(...)}`.
- [x] Manually audit `renderWelcomeScreen` against source lines 9005-9184.
- [x] Manually audit `renderWritingEmptyState` against source lines 9185-9253.
- [x] Manually audit `renderFocusKeywordBar` against source lines 9254-9459.
- [x] Manually audit `renderAgentWorkspaceCard` against source lines 9460-9629.
- [x] Manually audit `renderContextualAction` against source lines 9633-9996.
- [x] Manually audit `renderMessages` against source lines 9997-10917.
- [x] Manually audit `renderConfigTab` against source lines 10918-11434.
- [x] Manually audit `renderGlobalStatusBar` against source lines 11449-11585.
- [x] Manually audit `renderChatTab` against source lines 11586-12020.
- [x] Manually audit `renderCostTab` against source lines 12058-12306.
- [x] Manually audit main component return tree against source lines 12307-12346.

## Phase B - Build Pipeline

- [x] Audit current PHP enqueue path and dependencies before creating a build pipeline.
- [x] Decide the minimal build tool needed for `assets/js/src/index.jsx`: TypeScript CLI only, no React scaffold, no bundler.
- [x] Ensure JSX compiles to the same element factory expected by WordPress: `wp.element.createElement`.
- [x] Ensure generated output keeps WordPress globals and does not introduce new runtime globals unless explicitly required.
- [x] Add only the minimal package/build files needed for the monolith: `tsconfig.sidebar.json`.
- [x] Generate a built file without changing the active enqueue.
- [x] Verify built output contains expected plugin registration and no missing dependencies.
- [x] Verify built output has no syntax errors.
- [x] Record build command and output file path: `tsc -p tsconfig.sidebar.json` -> `assets/js/dist/sidebar.js`.

## Phase C - Runtime Parity Smoke

- [x] Load Gutenberg editor with the existing `assets/js/sidebar.js` runtime as baseline.
- [x] Load migrated built output without changing behavior.
- [x] Verify baseline CDP smoke loads post `684`, registers `wp-agentic-writer`, opens `wp-agentic-writer/wp-agentic-writer`, and has no captured console errors.
- [x] Verify migrated CDP smoke serves `assets/js/dist/sidebar.js` through request interception without changing PHP enqueue; `interceptedMigratedDist: true`, SHA-256 `9685574363e7489935528340c071fbf0c32f6c85236d735b56d54050ca57a952`.
- [x] Confirm sidebar menu item appears with the same icon and label.
- [x] Verify baseline and migrated runtime plugin icon props: `alt: WP Agentic Writer`, `width: 20px`, `height: 20px`, `assets/img/icon.svg`.
- [x] Verify baseline and migrated Gutenberg options menu contains `WP Agentic Writer` under Panels.
- [x] Confirm sidebar panel title/icon renders.
- [x] Verify baseline and migrated sidebar title images include `alt: WP Agentic Writer`, `width: 24px`, `height: 24px`, `assets/img/icon.svg`.
- [x] Confirm status bar buttons render and preserve active states.
- [x] Confirm chat tab renders welcome, workspace, messages, command input, mentions, slash menu, and modal branches; empty-state is accepted as source-unreachable through normal runtime.
- [x] Verify chat existing-session surface in baseline and migrated runtime: workspace card, messages list, command area, command input, and active Chat status button.
- [x] Verify welcome/session surface in baseline and migrated runtime: Sessions status button, welcome prompt, continue session, explore/start outline/start writing actions.
- [x] Accept chat empty-state branch as source-unreachable through normal runtime.
- [x] Empty-state exception recorded: source guard requires `agentMode === "writing"`, no `currentPlanRef.current`, and zero editor blocks. Source tracing shows empty hydration resets to `chat`, plan hydration sets `planning`, welcome/start UI sets only `chat` or `planning`, and the only natural `writing` path is `executePlanFromCard`, which requires `currentPlanRef.current`; existing empty post `593` also hydrates an older conversation/plan, so no clean natural runtime fixture was available without mutating DB/session state or instrumenting React state.
- [x] Verify mention autocomplete branch.
- [x] Verify baseline and migrated mention autocomplete: takeover enabled input, `.wpaw-mention-autocomplete` rendered, 61 `.wpaw-mention-option` entries including `@this`, `@previous`, `@next`, `@all`, `@title`, and block targets.
- [x] Verify slash menu branch.
- [x] Verify baseline and migrated slash menu: textarea accepted `/`, `.wpaw-mention-autocomplete` rendered, 4 slash command options including `add below`, `add above`, `append code block`, and `reformat`.
- [x] Verify refine-all confirmation modal branch.
- [x] Verify baseline and migrated refine-all confirmation modal: `@all` request on post `684` resolved 35 text blocks, `.wpaw-refine-confirm-overlay` rendered `Confirm @all Refinement`, block-count body text, checkbox, `Cancel`, and `Continue`; `Cancel` closed the overlay; no console errors; no message-persist request fired before page close.
- [x] Confirm config tab renders every control and SEO section.
- [x] Confirm cost tab renders cards, budget status, action summary, history, and settings link.
- [x] Confirm no console syntax/runtime errors from migrated output.
- [x] Confirm no endpoint URL, method, header, body shape, stream handling, or error handling changed.
- [x] Verify static source/dist request inventory: 33 endpoint strings, 44 `fetch(` tokens, 2 `XMLHttpRequest`, 2 `xhr.open`, 40 nonce header values, 29 JSON content-type headers, 29 JSON body serializations, 8 GET, 28 POST, 2 DELETE, 9 stream readers/decoders, 23 thrown errors, 16 abort-error checks, and 28 `wpawLog.error` calls.
- [x] Confirm editor locks, localStorage keys, beforeunload handlers, global listeners, abort handling, and stream reader lifecycle still work.
- [x] Verify runtime session-lock branch in baseline and migrated smoke: session lock banner renders, takeover enables disabled command input.
- [x] Verify static source/dist lifecycle inventory: session lock endpoints, `tab_id`, forced takeover, heartbeat functions, `keepalive`, localStorage calls, beforeunload add/remove pairs, `wpaw:insert-mention` listener, abort/cancel calls, and stream reader assignments/read loops all match.

## Phase D - Enqueue Switch

- [x] Switch enqueue only after Phase A, Phase B, and Phase C are complete, with the recorded source-unreachable empty-state exception accepted.
- [x] Keep rollback path to original `assets/js/sidebar.js`.
- [x] Verify WordPress dependency handles match the migrated output: built globals `wp.blocks`, `wp.components`, `wp.data`, `wp.editPost`, `wp.element`, and `wp.plugins` map to enqueued handles `wp-blocks`, `wp-components`, `wp-data`, `wp-edit-post`, `wp-element`, and `wp-plugins`; no missing handles.
- [x] Verify plugin loads in Gutenberg after enqueue switch: real enqueue loaded `assets/js/dist/sidebar.js` once, did not load `assets/js/sidebar.js`, registered `wp-agentic-writer`, opened `wp-agentic-writer/wp-agentic-writer`, rendered sidebar menu/title/icon, status buttons, chat/config/cost/sessions surfaces, and had no captured console errors.
- [x] Verify `ConnectedSidebar` still receives `postId`: real enqueue smoke on post `684` triggered post-specific REST calls including `/post-config/684`, `/cost-tracking/684`, `/writing-state/684`, `/section-blocks/684`, and `/conversations/post/684`.
- [x] Verify no duplicate sidebar/plugin registration occurs: real enqueue smoke found one `assets/js/dist/sidebar.js` resource, zero `assets/js/sidebar.js` resources, and `pluginNameCount: 1` for `wp-agentic-writer`.

## Phase E - Post-Switch Cleanup

- [x] Keep `assets/js/sidebar.js` until migrated runtime is proven stable.
- [x] Do not delete the monolith source target.
- [x] Do not extract helpers until extraction has a separate source-range task.
- [ ] Commit only the real migration files intended for this step.

## Post-Switch User Test Fixes

- [x] Fix REST fatals from extracted controllers calling sidebar helper methods that were still private: exposed controller-used helper methods on `WP_Agentic_Writer_Gutenberg_Sidebar`.
- [x] Fix `/generate-plan` fatal from missing `WPAW_Rate_Limiter`: load `includes/class-rate-limiter.php` from the main plugin bootstrap.
- [x] Fix `/post-new.php` session carry-over: migrated history loader now does not auto-hydrate any session when `postId` is `0`, and the fallback only treats `post_id === 0` as unassigned.
- [x] Restore welcome Start Writing 1:1 behavior: `handleWelcomeStart` is synchronous, does not call `startNewConversation`, sets the selected mode, hides welcome, and focuses the input like `assets/js/sidebar.js`.
- [x] Fix start-new-session 500: `WP_Agentic_Writer_Conversation_Manager::generate_session_id()` now returns 32-character hex IDs that fit the existing `session_id VARCHAR(32)` table column and the REST route regex.
- [x] Fix blank chat response with local/9router streaming: local backend stream parser now accepts `choices[0].message.content`, text-completion, Ollama-style, and simple content/response payloads in addition to `choices[0].delta.content`.
- [x] Fix local/9router stream-empty edge case: when streaming returns HTTP 200 but no extractable content, `chat_stream()` now falls back to non-streaming `chat()` and emits that response as a single assistant message.
- [x] Restore `/chat` source-of-truth backend path: `handle_chat_request()` and `stream_chat_request()` now run inside `WP_Agentic_Writer_Gutenberg_Sidebar` again instead of delegating to the extracted chat controller.
- [x] Verify restored `/chat` route-level streaming smoke on post `717` emits `conversational_stream`, final `conversational`, and `complete`, and stores both user and assistant messages.
- [x] Verify restored `/chat` route-level streaming smoke on post `717` with the saved `_wpaw_post_config` and `web_search` enabled also emits assistant content and stores both user and assistant messages.
- [x] Fix silent empty provider completion on the active restored `/chat` path: local backend retries once when the non-streaming fallback returns empty content, and the active in-class stream handler emits an error instead of completing silently if content is still empty.
- [x] Verify post `719` browser-equivalent route smoke with saved `_wpaw_post_config` and `web_search` enabled stores both user and assistant messages after the retry/empty guard patch.
- [x] Fix Zed refactor defect report: reverted migrated `handleWelcomeStart` and `startNewConversation` signatures/side effects to match `assets/js/sidebar.js`, then rebuilt `assets/js/dist/sidebar.js`.
- [x] Restore Sessions tab listing on `/post-new.php`: prior sessions are listed for manual navigation while the history loader still prevents automatic continuation.
- [x] Rebuild migrated output with `tsc -p tsconfig.sidebar.json`.
- [x] Verify PHP syntax for changed bootstrap/sidebar/controller files.
- [x] Verify migrated `assets/js/dist/sidebar.js` syntax with `node --check`.
- [x] Verify REST smoke checks: `post-config/684` GET `200`, `post-config/684` update `200`, `check-clarity` JSON request `200` with no AI call, and `generate-plan` empty-topic JSON request returns expected `400 no_topic` instead of a PHP fatal.
- [ ] Re-run authenticated `/wp-admin/post-new.php` browser probe after browser auth is available; current headless probe is blocked at `wp-login.php?reauth=1` because generated WP auth cookies are rejected by the web server even though they validate in WP CLI.

## Optional Phase F - Extraction Later

- [ ] Extract only one helper group at a time.
- [ ] Record source range and destination file before extraction.
- [ ] Move code without renaming public/internal symbols.
- [ ] Re-run the same parity checks for the moved range.
- [ ] Do not delete the monolith source range until the moved range is checked line-by-line.
