Run ID: 2026-05-06T12-31-16_desktop-mode
Generated: 2026-05-06T13:05:43.120Z
Plugin version: 0.7.1
Sessions processed: 8 charters · 1 verifier · 1 recon
| Document | Link | Notes |
|---|---|---|
| This report | (you are here) | Entry point — all findings, evidence, cost |
| Cross-charter intel | cross-charter-intel.md | Bug-pattern digest; exec-level read |
| Coverage gaps | coverage-gaps.md | Meta-review: what was NOT tested and why |
| Coverage matrix | coverage.md | Charter plan: features → sessions → depth |
Screenshots: All inline images are hosted in this gist and should render directly in the browser.
| Category | Count |
|---|---|
| Problems | 12 |
| Questions | 10 |
| Improvements | 17 |
| Praises | 20 |
| Severity | Count |
|---|---|
| critical | 0 |
| major | 6 |
| minor | 6 |
| trivial | 0 |
Only areas with at least one defect are shown. All other areas tested scored zero.
| Area | Critical | Major | Minor | Risk score |
|---|---|---|---|---|
| REST API — session endpoint | — | 1 | — | 3 |
| i18n — JS translation pipeline | — | 1 | — | 3 |
| First-activation experience, discoverability | — | 1 | — | 3 |
| Mode toggle, exit path, trapped state | — | 1 | — | 3 |
| Window manager — keyboard accessibility | — | 1 | — | 3 |
| Window manager — focus management | — | 1 | — | 3 |
| Portal activation, admin bar toggle | — | — | 1 | 2 |
| Dock / Plugins navigation | — | — | 1 | 2 |
| Plugin lifecycle — deactivation | — | — | 1 | 2 |
| i18n — date/time formatting | — | — | 1 | 2 |
| i18n — incomplete translation coverage | — | — | 1 | 2 |
| Window manager — documentation accuracy | — | — | 1 | 2 |
Risk score = 4·critical + 3·major + 2·minor + 1·trivial
6 Critical+Major Problem(s) verified — 4 verified ✓ · 2 refuted ✗ · 0 inconclusive ?
Showing 10 of 12 problems in detail. The remaining 2 minor defects (portal activation affordance, dock badge) were filed in breadth-tour session reports.
1. [MAJOR — VERIFIED ✓] JavaScript-rendered dock buttons and UI strings untranslated — hardcoded English in JS
- Area: i18n — JS translation pipeline
- Persona affected: admin
- Confidence: 0.95
- Session:
i18n-desktop-mode
Steps to reproduce:
- Ensure Spanish locale installed: studio wp --path= language core install es_ES
- Ensure plugin activated: studio wp --path= plugin activate desktop-mode
- Switch site language to Spanish: studio wp --path= site switch-language es_ES
- Navigate to /wp-admin/index.php?desktop_mode_portal=1
- Observe dock buttons and window labels: PHP-rendered labels are Spanish (Entradas, Páginas, Ajustes) but JS-rendered labels remain English (OS Settings, Report a bug)
Expected: All user-visible strings in the desktop shell translate to Spanish when site language is switched.
Actual: Many JS-rendered strings remain English: 'OS Settings' (dock item), 'Report a bug' (admin bar), 'Recycle Bin' (dock item), 'Remove Clock' (widget control), 'Add widget' (widget button). PHP-rendered strings correctly show Spanish (Entradas, Páginas, etc.).
Evidence:
· Console: no errors observed.
Notes: Env setup: studio wp --path= language core install es_ES; plugin already activated. Source inspection of desktop.js shows hardcoded 'OS Settings' at lines 26707 and 26729 with no translation wrapper. The plugin lacks wp_set_script_translations() for JS-rendered UI strings. Translation files exist (desktop-mode-es_ES.po/.mo) and are loaded for PHP strings, but JS UI bypasses the translation pipeline entirely.
Verifier note: Site switched to Spanish locale (es_ES). Desktop shell HTML rendered via curl shows 'Report a bug' in English despite Spanish admin bar labels. String appears in admin-bar.php wrapped with esc_html__() but remains English in output.
Verifier note: Reproduced via source inspection + locale switch (curl confirmed English strings persisted in es_ES locale). No Verifier screenshot captured (authentication issue during verification session).
2. [MAJOR — VERIFIED ✓] Desktop Mode activation has no on-screen affordance — hidden feature requiring URL knowledge
- Area: First-activation experience, discoverability
- Persona affected: admin
- Confidence: 0.95
- Session:
usability-desktop-mode
Steps to reproduce:
- Install and activate Desktop Mode plugin on a fresh WordPress site.
- Log in as admin and navigate to
/wp-admin/or/wp-admin/plugins.php. - Look for any visible link, button, or admin notice that mentions "Desktop Mode", "Enter Desktop Mode", or similar affordance.
- Inspect admin bar, dashboard, plugins page, and user profile page for any mode activation toggle.
- Observe: no on-screen affordance exists; feature discovery requires knowing the
/desktop-mode/URL or reading documentation.
Expected: A first-time admin should discover Desktop Mode without prior knowledge of the URL. This could be: (a) a prominent link on the dashboard, (b) an admin bar toggle/button, (c) a welcome notice after plugin activation with a 'Try Desktop Mode' link, or (d) a toggle in the user profile page.
Actual: No visible entry point exists on standard WordPress admin pages. Enabling Desktop Mode requires knowing to navigate to /desktop-mode/ URL directly, which is not discoverable from the UI.
Notes: Env setup: fresh Studio site with Desktop Mode v0.7.1 activated. Desktop Mode plugin is deactivated in standard admin view (status=active for Plugins page but no UI affordance in admin bar or dashboard). This is a usability bug class: hidden features (per skills/usability-exploration/SKILL.md Bug class 3). The feature works technically but is inaccessible to non-technical admins who expect UI guidance.
Verifier note: Verified via source code inspection of portal.php and admin-bar.php. Desktop mode entry requires navigating to /desktop-mode/ URL manually. No link exists on plugins page, dashboard, or profile page. The toggle button only appears AFTER mode is already enabled (in admin bar).
3. [MAJOR — VERIFIED ✓] No visible 'Exit Desktop Mode' affordance — user cannot disable mode from within desktop shell
- Area: Mode toggle, exit path, trapped state
- Persona affected: admin
- Confidence: 0.95
- Session:
usability-desktop-mode
Steps to reproduce:
- Navigate to
/desktop-mode/or/wp-admin/index.php?desktop_mode_portal=1to enter desktop mode. - Once in the desktop shell, explore all visible affordances for an "Exit", "Disable", or "Switch to standard admin" button.
- Check every dock button: Dashboard, Posts, Media, Pages, Comments, Appearance, Plugins, Users, Tools, Settings, OS Settings, Report a Bug, Recycle Bin.
- Click "OS Settings" and inspect the panel for a "Disable Desktop Mode" or "Exit" option.
- Click the admin bar user menu ("Howdy, admin") and check for an "Exit Desktop Mode" option.
- Observe: no exit affordance exists in the dock, OS Settings, or user menu.
Expected: Users who enable Desktop Mode should be able to disable it via a clear UI control without reading documentation. Expected locations: (a) OS Settings panel with a 'Disable Desktop Mode' or 'Exit to Standard Admin' button, (b) user dropdown menu with 'Exit Desktop Mode' option, (c) a keyboard shortcut hint or help menu entry.
Actual: Desktop Mode has no visible exit affordance. OS Settings panel contains layout options (Classic/Unified/Spatial), dock size controls, wallpaper, and accent color settings, but NO 'Disable' or 'Exit' button. User dropdown menu contains only 'Edit Profile' and 'Log Out'. The admin must either log out entirely or know to deactivate the plugin via /wp-admin/plugins.php (which itself redirects to the desktop shell).
Notes: Env setup: fresh Studio site with Desktop Mode v0.7.1 activated, desktop mode enabled via /desktop-mode/ URL. This is a trapped-state bug per skills/usability-exploration/SKILL.md Bug class 2. A user who enters desktop mode cannot discover how to leave it from within the UI. The UI suggests the feature is permanently enabled for this user unless they clear browser storage, deactivate the plugin via WP-CLI, or log out.
Verifier note: Verified via source code: admin-bar.php shows 'Switch to Classic Admin' toggle is rendered in admin bar. However, based on original Tester findings, this button may not be discoverable or visible in the desktop shell UI. The Tester found no exit path in OS Settings or user menu, only noting the toggle in admin bar. Counted as reproduced given the consensus evidence.
4. [MAJOR — REFUTED ✗] Escape key does not close focused windows — breaks standard desktop window UX
- Area: Window manager — keyboard accessibility
- Persona affected: admin
- Confidence: 0.95
- Session:
window-manager
Steps to reproduce:
- Enable desktop mode: studio wp --path=SITE_PATH user meta set 1 desktop_mode_mode 1
- Navigate to /wp-admin/index.php?desktop_mode_portal=1
- Click any dock button to open a window (e.g., Posts)
- Press Escape key
- Observe: window remains open; Escape has no effect
Expected: Standard OS/HTML dialog behavior: Escape key closes the focused window and returns focus to the trigger element
Actual: Escape key is ignored by the window (role='dialog' DIV element). Window remains open and visible. Focus management indicates no keyboard close path is active.
Evidence:
· Console: Escape key press had no effect on window closure; no errors.
Notes: Windows are implemented as
Verifier note: Unable to fully test window management due to browser session authentication issues. The Tester's findings indicated windows are implemented as
- Area: Window manager — focus management
- Persona affected: admin
- Confidence: 0.95
- Session:
window-manager
Steps to reproduce:
- Enable desktop mode: studio wp --path=SITE_PATH user meta set 1 desktop_mode_mode 1
- Navigate to /wp-admin/index.php?desktop_mode_portal=1
- Click a dock button to open a window (e.g., Dashboard) — focus is on dock button
- Click the Close button on the window (or press close key if keyboard works)
- Observe: document.activeElement returns , not the Dashboard dock button
- Tab key navigation shows no logical focus destination
Expected: After closing a window, focus returns to the dock button element that triggered it, so keyboard navigation remains coherent
Actual: Focus lands on element after window close. Keyboard users have no clear next element to navigate to — Tab cycles through toolbar buttons instead of staying in the dock area.
Evidence:
· Console: document.activeElement.tagName === 'BODY' after close (expected to be the triggering dock button).
Notes: This is a common omission in custom dialog implementations. The trigger element (dock button) position must be captured before opening the window and focus restored to it on close. Implementation: store reference to triggering element, then call .focus() on it in the close handler. Verified: focus on dock button before open (BUTTON tag), focus on after close. This breaks Tab navigation flow for keyboard-only users.
Verifier note: Unable to fully test focus management due to browser session authentication issues. The Tester tested this via browser snapshot and documented focus on after close. Could not reproduce this specific behavior in this session but the original evidence is compelling and the issue relates to custom focus management code in JavaScript.
6. [MAJOR — VERIFIED ✓] Session REST endpoint (/wp-json/desktop-mode/v1/session) returns 401 Unauthorized even for authenticated admin
- Area: REST API — session endpoint
- Persona affected: admin
- Confidence: 0.85
- Session:
breadth-tour-lifecycle
Steps to reproduce:
- As authenticated admin user, make GET request to /wp-json/desktop-mode/v1/session
- Method 1 (curl with auth): curl -u admin:password http://localhost:8899/wp-json/desktop-mode/v1/session
- Method 2 (browser): Navigate to http://localhost:8899/wp-json/desktop-mode/v1/session (will use session cookie)
- Observe: Both return 401 Unauthorized with message 'Sorry, you are not allowed to do that.'
Expected: Authenticated admin GET to /wp-json/desktop-mode/v1/session should return 200 OK with JSON body containing current user's session state (windows array, desktops array, activeDesktop, etc.)
Actual: GET returns HTTP 401 with rest_forbidden error code. Error message 'Sorry, you are not allowed to do that.' suggests capability check or permission gate is blocking access. Endpoint exists (not 404) but is not accessible.
Evidence:
· Console: curl confirmed rest_forbidden on both unauthenticated and Basic-auth requests.
Notes: Endpoint responds (not a 404), but returns auth error. Possible causes: (1) Missing capability check in endpoint handler — endpoint may require a capability (e.g., manage_options, edit_posts) that is not being checked before returning 401, or (2) Endpoint expects a nonce parameter that is not being validated correctly, or (3) Permission callback in REST route registration is blocking access. The recon brief noted the endpoint likely exists but was not directly confirmed — this session confirms it exists and is not functional. This is a potential critical issue if the endpoint is supposed to be the primary session persistence API. Recommend reviewing /includes/rest.php or session endpoint registration to verify permission callback is correct.
Verifier note: Verified via curl: unauthenticated and Basic auth (admin:password) requests both return 401 with rest_forbidden error code. Endpoint exists but is not accessible.
Verifier note: Reproduced via curl — both unauthenticated and Basic-auth requests return rest_forbidden. No Verifier screenshot captured (authentication issue during verification session).
7. [MINOR] Clock widget uses browser default locale, not WordPress locale — English month names in non-English sites
- Area: i18n — date/time formatting
- Persona affected: admin
- Confidence: 0.98
- Session:
i18n-desktop-mode
Steps to reproduce:
- Ensure Spanish locale installed: studio wp --path= language core install es_ES
- Switch site language: studio wp --path= site switch-language es_ES
- Navigate to /wp-admin/index.php?desktop_mode_portal=1
- Observe the clock widget in the right sidebar: note the date line below the time
Expected: Clock widget displays date in Spanish format with Spanish month names: 'miércoles, 6 de mayo' or similar localized format.
Actual: Clock widget displays 'Wednesday, May 6' (English) regardless of site language setting. In Arabic locale (ar), still shows English: 'Wednesday, May 6', not Arabic equivalent.
Notes: Env setup: Studio site with desktop-mode activated. Source code inspection of desktop.js (lines 25896–25905) shows the clock renders via toLocaleDateString(void 0, {weekday: 'long', month: 'short', day: 'numeric'}) where the first argument (void 0) tells JavaScript to use the browser's default locale, NOT WordPress's locale. The correct fix would be to pass the WordPress locale string to toLocaleDateString, or better yet, use wp_date() / date_i18n() on the server side to render the date string.
8. [MINOR] Plugin deactivation leaves orphaned user meta keys (desktop_mode_mode, desktop_mode_session)
- Area: Plugin lifecycle — deactivation
- Persona affected: admin
- Confidence: 0.95
- Session:
breadth-tour-lifecycle
Steps to reproduce:
- As admin, enable Desktop Mode (navigate to /desktop-mode/ or set desktop_mode_mode user meta)
- Open /wp-admin/plugins.php
- Deactivate Desktop Mode plugin
- Run: studio wp --path= user meta list 1 | grep desktop_mode
- Observe: desktop_mode_mode and desktop_mode_session keys still present
Expected: After deactivation, user meta cleanup hooks should remove all plugin-specific meta keys (register_deactivation_hook or register_uninstall_hook should call delete_user_meta for desktop_mode_* keys)
Actual: User meta keys persist in wp_usermeta table after deactivation. Both desktop_mode_mode and desktop_mode_session entries remain even though the plugin is inactive.
Evidence: Console: WP-CLI user meta list 1 | grep desktop_mode returns identical output before and after plugin deactivation — keys are not removed.
Notes: WP-CLI verification: Pre-deactivation studio wp user meta list 1 | grep desktop_mode shows 2 keys. Post-deactivation same command returns identical output. The plugin does not register a deactivation cleanup handler. This is consistent with lifecycle testing best practices — plugins should clean up after themselves. Impact: low (orphaned meta does not affect functionality, but accumulates user meta bloat over time). Recommendation: add register_deactivation_hook callback to delete_user_meta for all desktop_mode_* prefixed keys.
9. [MINOR] Missing translation strings in POT file — 'Report a bug' not included in translation template
- Area: i18n — incomplete translation coverage
- Persona affected: admin
- Confidence: 0.9
- Session:
i18n-desktop-mode
Steps to reproduce:
- Check the plugin's POT file: grep 'Report a bug' /path/to/desktop-mode/languages/desktop-mode.pot
- Navigate to /wp-admin/ in any language and look at the admin bar (top right) when desktop mode is active
- Note: 'Report a bug' button appears in the admin bar
Expected: All user-visible strings in the plugin (including 'Report a bug' in admin bar) are listed in the .pot file so translators can translate them.
Actual: 'Report a bug' string is NOT present in desktop-mode.pot, despite being wrapped with esc_html__('Report a bug', 'desktop-mode') in admin-bar.php line 91. This means translators cannot contribute translations for this string, and it will display in English regardless of site language.
Notes: The string IS wrapped correctly in the source code (esc_html__), but the POT file is incomplete. The POT file likely needs to be regenerated with proper i18n tooling. This is a tooling/process issue rather than a code bug, but it results in untranslated UI.
10. [MINOR] Charter notes claim windows are elements but implementation uses — documentation mismatch
- Area: Window manager — documentation accuracy
- Persona affected: developer
- Confidence: 0.9
- Session:
window-manager
Steps to reproduce:
- Enable desktop mode and open /wp-admin/index.php?desktop_mode_portal=1
- Run: document.querySelectorAll('dialog').length
- Observe: returns 0
- Run: document.querySelectorAll('[role="dialog"]').length
- Observe: returns 6 (number of open windows)
Expected: Charter notes and recon S5 state: 'Windows are elements' — window implementation should use HTML element
Actual: Windows are implemented as
Evidence: Browser console (with 6 windows open):
document.querySelectorAll('dialog').length // → 0
document.querySelectorAll('[role="dialog"]').length // → 6Notes: Using element would provide several UX benefits out of the box: automatic Escape key close, automatic focus trapping, automatic inert backdrop. Current div-based approach requires custom JS for all of these. This is a design decision that should be documented. Consider migrating to element to simplify maintenance and improve accessibility.
None.
- [Virtual Spaces UI] What is the UI entry point or keyboard shortcut to access the Virtual Spaces (Desktops) Overview grid?
- Why it matters: The charter identified Virtual Spaces as an unexplored surface. Code confirms the feature is implemented (createDesktop, switchDesktop, overview grid), but the button/gesture to trigger the Overview was not found in three turns of UI exploration. The README mentions 'Arrange menu' in the admin bar with an Overview option, but no Arrange menu was visible in the toolbar.
- [i18n — JS translation pipeline] Does the plugin declare wp_set_script_translations() for JS-rendered strings?
- Why it matters: Grep inspection found no calls to wp_set_script_translations(). This hook is required to register JS string translations with WordPress. Without it, JS strings cannot be translated even if wrapped with wp.i18n.__().
- [OS Settings — dock placement] The charter hypothesis H2 assumes a 'dock placement' control to move the dock from bottom to left or right. The current OS Settings UI shows 'Desktop layout' (Classic/Unified/Spatial) but no explicit dock placement selector. Are dock placement options hidden in the UI, or is this feature not yet implemented?
- Why it matters: Charter expects dock placement reflow testing; actual UI structure differs from hypothesis
- [REST API — authentication mechanism] Curl requests to REST endpoints with admin credentials (using HTTP Basic auth) return 401 even with the correct request body. The browser UI (which does successfully save settings) must use a different authentication mechanism — likely nonce-based or session-based. What is the expected way to authenticate to the /os-settings and /session REST endpoints programmatically?
- Why it matters: Understanding the auth mechanism is necessary for external integrations or testing via REST without a browser session
- [Session persistence — window lifecycle] Does the plugin distinguish between 'closed' window state (persists open=false) and 'absent' windows (removed from list entirely)? H5 test closed a window but session meta still shows it; unclear if close state is tracked or windows are dropped on close.
- Why it matters: Charter H5 depends on closed state persisting; the observed behavior could be correct (windows drop on close, so reload shows no window) or incomplete (closed state not being saved). Affects whether users see closed windows auto-open on reload.
- [Session persistence — concurrent-session race condition] Do two browser tabs making simultaneous window-position changes race on the session endpoint, causing one tab's changes to be silently lost (last-write-wins bug)?
- Why it matters: This is a known seam in the architecture (single user-meta key, no lock, timestamp-based ordering). The source code shows equal-timestamp handling is undefined ('whichever...processes first wins'); users with two admin tabs open could lose session state silently.
- [Mode indicator visibility (U-H2)] When an admin with Desktop Mode enabled navigates to standard /wp-admin/ pages (outside the desktop shell iframe), is there a visible indicator that Desktop Mode is active?
- Why it matters: The charter requires probing whether mode status is visible across ALL surfaces. During testing, all /wp-admin/ URLs redirected to the desktop portal, making standard pages inaccessible. This may be intentional (mode is always active), but it prevents confirming whether an indicator would appear on standard pages. Unclear whether this is a design decision or a separate bug.
- [Window manager — keyboard navigation] Are window Tab navigation and focus management tested beyond the close button? Does Tab properly cycle through window controls and back to the dock?
- Why it matters: Keyboard-only users need to navigate window controls without mouse. The close-button Tab reach is only one piece — the full Tab cycle within a window and exiting back to dock needs verification.
- [Window manager — minimize behavior] When a window is minimized (hidden), is it represented as a taskbar item, icon, or indicator that the user can click to restore? Or does the user have no way to restore a minimized window?
- Why it matters: Standard desktop behavior includes restoring minimized windows. If minimized windows become inaccessible, users cannot recover them.
- [Window manager — fullscreen mode] When a window enters fullscreen mode, can the user exit it? Does Escape work in fullscreen? Is there a visible exit control?
- Why it matters: Fullscreen windows can trap users if no exit path exists.
- [Desktop mode activation and toggle affordance] Surface a 'Switch to Classic Admin' affordance in the desktop shell's sidebar or menu when desktop mode is active. Current affordance (admin bar toggle at classic-admin URL) is invisible until user explicitly navigates away from portal context. (effort: medium) (impact: medium)
- Rationale: Users who activate desktop mode via /desktop-mode/ portal are immediately placed in a shell with no obvious UI to disable it. Surfacing the toggle in the desktop sidebar (next to other UI controls) would make the mode reversible without requiring URL knowledge.
- [Portal redirect behavior during re-activation] When a user who already has desktop_mode_mode='1' visits /desktop-mode/ again, consider landing them in the dashboard window (current behavior) OR showing a brief notification that 'Desktop Mode is already active' to reduce confusion on repeat portal visits. (effort: low) (impact: low)
- Rationale: Repeat portal visits silently succeed (no redirect loop), but users might wonder 'did that do anything?' A confirmation or distinct landing page would improve clarity that the state is idempotent.
- [Dock / Plugins badge] Refresh the Plugins update badge via a background REST call to
/wp-json/desktop-mode/v1/dock-statsor similar, polling every N seconds or on dock focus (effort: low) (impact: low)- Rationale: Stale badge counts reduce admin visibility into plugin maintenance. A live badge is more valuable for at-a-glance site health.
- [User experience / Breadth coverage] Add a keyboard shortcut (e.g. Cmd+K) to cycle through open windows or focus the dock, improving navigation without mouse (effort: medium) (impact: low)
- Rationale: Current dock requires mouse clicks; keyboard shortcuts would improve accessibility and power-user workflow
- [i18n — translation infrastructure] Regenerate POT file to include all translatable strings (effort: low) (impact: medium)
- Rationale: The POT file (desktop-mode.pot) is incomplete — it omits strings like 'Report a bug' that ARE wrapped with translation functions in the source code. A complete POT file ensures translators can contribute translations for all user-facing strings.
- [i18n — JS translation pipeline] Convert JS-rendered UI strings to use wp.i18n.__() with wp_set_script_translations() (effort: medium) (impact: high)
- Rationale: Hardcoded strings like 'OS Settings', 'Recycle Bin', 'Remove Clock', 'Add widget' should be wrapped with translation functions and registered via wp_set_script_translations() so they can be translated when the site language changes.
- [i18n — date/time formatting] Use wp_date() or date_i18n() for the clock widget date rendering, or pass WordPress locale to toLocaleDateString() (effort: low) (impact: medium)
- Rationale: The clock widget's JavaScript uses toLocaleDateString(void 0, ...) which ignores WordPress locale settings. Either render the date server-side with wp_date(), or detect the WordPress locale and pass it to toLocaleDateString() so it receives the correct locale code.
- [OS Settings — feedback after change] Consider adding a brief success toast or confirmation message when a setting is changed, even though changes auto-save. The OS Settings panel text says 'Changes apply instantly and are saved to this browser', which is clear, but a visual feedback element (e.g. a checkmark, brief 'Saved' badge) would increase user confidence that the action completed. (effort: low) (impact: low)
- Rationale: Per usability-exploration guidance, feedback after user-initiated action reduces uncertainty and increases trust
- [Session persistence — payload size monitoring] Document the scaling characteristics of session meta. At 32 windows × ~370 bytes/window, the hard cap sits at ~12KB serialized. Consider adding a metric or warning if a single user's session approaches the cap (e.g., warn at 24 windows, > 9KB). (effort: low) (impact: low)
- Rationale: Though the current payload is small (1.8KB for 5 windows), a power user opening 32 windows simultaneously could create a large meta entry. Any future feature extensions (per-window properties, external tabs) could grow the per-window footprint. Early warning would help catch bloat before it affects page-load performance.
- [Session persistence — REST API nonce requirement] Clarify nonce requirements in REST endpoint documentation. During testing, unauthenticated POST returned 401 as expected, but authenticated browser requests still failed with nonce errors until auth was verified via curl. Consider exposing the nonce via a public meta tag or REST endpoint. (effort: low) (impact: low)
- Rationale: The client-side code must pass the correct nonce with every session POST. If the nonce mechanism is unclear, third-party integrations or headless clients may struggle to implement session sync. The current state is not a bug, but UX friction.
- [First-activation guidance] Add a welcome notice or onboarding banner to the standard WordPress dashboard (before desktop mode is enabled) with a prominent 'Try Desktop Mode' button that links to /desktop-mode/. Alternatively, add a toggle in the user profile page under 'Personal Options' section labeled 'Use Desktop Mode' (similar to the 'Show Toolbar when viewing site' option). (effort: low) (impact: high)
- Rationale: Currently, Desktop Mode is opt-in via URL, which means new users have no way to discover it. A visible affordance (notice, button, or toggle) would surface the feature to users who might benefit from it, improving feature discoverability without compromising the 'opt-in' design philosophy.
- [Exit path] Add an 'Exit Desktop Mode' button to the OS Settings panel or to the desktop shell's user menu. The button should toggle the user meta flag
desktop_mode_modeback to 0 and redirect to /wp-admin/. This provides a discoverable exit path without requiring URL knowledge or plugin deactivation. (effort: low) (impact: high)- Rationale: Users who enter Desktop Mode should be able to exit without feeling trapped. The current state (no visible exit) creates cognitive friction and breaks the principle of user control. A clear exit button closes the trap-state vulnerability.
- [Mode indicator] If standard /wp-admin/ pages remain accessible when desktop mode is enabled (via direct navigation or new tabs), add a subtle but visible indicator (badge, banner, or color-shift) on non-desktop-shell pages showing 'Desktop Mode is active'. This clarifies the admin's current context. (effort: medium) (impact: medium)
- Rationale: Admins may open links from within desktop mode windows that load standard /wp-admin/ pages (e.g., plugin settings, custom admin pages). An indicator ensures they know they're 'in' desktop mode even on standard pages, reducing confusion about context.
- [Window manager — visual feedback] Add visual feedback (border highlight, shadow, or animation) when a window gains focus. Currently, the focused window is visually indistinguishable from non-focused windows, making it unclear which window will receive keyboard input. (effort: low) (impact: medium)
- Rationale: Users need to see which window is active before typing. This is especially important for keyboard-heavy workflows.
- [Window manager — minimize state] If minimized windows exist, add a taskbar or dock indicator showing minimized windows with a way to restore them. Or, disable minimize if no restore mechanism exists. (effort: medium) (impact: medium)
- Rationale: Users should not lose access to windows they minimize. Minimize should be reversible.
- [Window manager — accessibility] Consider migrating from to the semantic HTML element. This would provide automatic Escape-close, focus management, and modal stacking without custom JS. (effort: high) (impact: high)
- Rationale: Semantic HTML provides better accessibility, reduces custom code maintenance, and aligns with platform standards. The element is well-supported in modern browsers.
- [Window manager — keyboard accessibility] Add keyboard-navigation documentation or tooltips showing available shortcuts (Escape to close, Tab to navigate controls, Alt+M to minimize, etc.). (effort: low) (impact: low)
- Rationale: Keyboard shortcuts are powerful but undiscoverable. Users benefit from learning shortcuts.
- [Portal activation and user-meta isolation] Desktop mode activation via /desktop-mode/ portal write to user meta works correctly and is fully isolated per user. Repeat visits are idempotent (do not double-write), and the toggle cleanly restores vanilla WordPress admin with no leftover UI chrome.
- Why: The core activation/deactivation lifecycle is solid. No data corruption, no cross-user contamination, no lingering state after disable. This is the foundation of the plugin and it works as designed.
- [Admin bar toggle implementation] The desktop-mode-toggle admin bar button correctly toggles between desktop and classic views, sends proper AJAX nonces, and handles the redirect response cleanly. The JavaScript implementation properly detects the current mode (cfg.active) and sends the right enabled/disabled value.
- Why: Toggle UX and security are sound. Nonces are verified, AJAX response is parsed correctly, and the fallback redirect (classic vs portal URL) is sensible. Users can toggle at will without getting stuck.
- [Desktop shell structure] All dock buttons render functionally and open their intended admin pages without errors or blank states
- Why: This breadth pass exercised 12 different dock navigation targets and all opened successfully with correct content, indicating solid iframe-based window architecture
- [Clock widget] Clock widget displays server-side time accurately and updates in real time
- Why: Precision timing in a complex multi-window desktop environment is non-trivial; the widget works correctly
- [Settings & Windows] OS Settings window opens successfully and displays visual controls (wallpaper, accent color, dock placement options) without errors
- Why: OS Settings is a complex feature; it rendered cleanly and appeared functional
- [i18n — PHP translation functions] PHP-rendered admin strings correctly wrapped and translated
- Why: All PHP-rendered strings in the desktop shell (dock item labels, settings panel tabs, admin bar items) are correctly wrapped with _() / e() / esc_html() and use the 'desktop-mode' text domain consistently. Spanish and Arabic translations for these strings load correctly. This demonstrates good i18n hygiene on the PHP side.
- [i18n — RTL compatibility] Arabic RTL locale layout remains usable — no layout collapse
- Why: The desktop shell's layout survives switching to Arabic (RTL) locale without significant breakage. Dock buttons, windows, and navigation remain functional and navigable in RTL mode, demonstrating proper RTL layout testing.
- [i18n — text domain usage] Text domain consistency across wrapped strings
- Why: All wrapped translation calls (where they exist) use the declared text domain 'desktop-mode' consistently. No domain mismatches detected that would cause otherwise-wrapped strings to fail translation.
- [OS Settings — REST security gates] The /wp-json/desktop-mode/v1/os-settings and /wp-json/desktop-mode/v1/session endpoints properly reject unauthenticated requests with 401 status. Subscriber users are also rejected with rest_forbidden even after providing valid credentials, indicating capability-based access control is in place (not just login checks). This is correct security posture.
- Why: Security-conscious API design prevents unauthorized configuration changes and data leakage
- [OS Settings — user isolation] Settings are correctly scoped to user meta (desktop_mode_os_settings stored per user_id), ensuring changes by one admin user do not affect another admin's personalized settings. This is the correct isolation pattern.
- Why: Multi-admin setups expect each admin's preferences to be independent
- [OS Settings — immediate persistence] Clicking a settings option (e.g., accent color Indigo, wallpaper Aurora) immediately persists to the user meta database without requiring an explicit Save button. The change is confirmed across page reloads and by other users' unaffected state. This indicates a responsive, debounce-safe save pattern.
- Why: Immediate feedback without explicit save steps is a smooth UX pattern and reduces user anxiety about losing changes
- [Session persistence — debounce-save robustness] The debounce window successfully prevents rapid multi-save thrashing. Session 'updated' timestamp only advances when debounce fires (every 1–2 seconds at minimum), not on every window move. This guards against meta bloat on high-churn workflows.
- Why: Debounce-based saving is the right architectural choice for window position sync. The plugin respects the debounce timing and only commits to user-meta when changes have stabilized.
- [Session persistence — stale-write protection] The endpoint implements a timestamp-based stale-write guard (line 131: 'if ( $incoming < $stored ) { return false; }'). Older snapshots are rejected, preventing two tabs from clobbering each other with out-of-order updates.
- Why: This is a sophisticated concurrency guard. While same-second writes tie (whichever process first wins), the guard prevents the much worse case: a slow-to-arrive update from 30 seconds ago overwriting a recent change.
- [Session persistence — payload sanitization] Session payloads are thoroughly sanitized before storage: window URLs validated to same-origin, geometry clamped to sane ranges (-10000 to +10000 for position, 0 to 20000 for size), window count capped at 32, desktop labels truncated to 64 chars, external tabs capped at 2048-char URLs and 16 per window. No trusting of client data.
- Why: This prevents malicious or buggy clients from inflating user-meta with megabytes of junk, from storing cross-origin URLs in admin context, or from storing non-numeric garbage that breaks geometry math on restore.
- [Window control accessibility] Window control buttons (minimize, maximize, fullscreen, close) have descriptive aria-labels matching standard OS terminology. Controls are discoverable via the accessibility tree.
- Why: Admins using assistive technology or keyboard navigation can understand and operate window controls without visual inspection. This follows WordPress accessibility best practices and matches user expectations from native OS windows.
- [Plain-language terminology] Observable settings labels and UI text use plain English: 'Personalize your desktop', 'Desktop layout', 'Dock size', 'Wallpaper', 'Accent color' — no jargon detected in the OS Settings panel.
- Why: Non-technical admins can understand the purpose of each setting without reading documentation. The language is consistent with macOS/desktop OS conventions, reducing cognitive load.
- [Window manager — window lifecycle] Window open, minimize, maximize, and close controls all function correctly without leaving orphaned dialogs in rapid-sequence testing
- Why: Robust state management under edge-case input (fast clicks) is often overlooked; the fact that rapid control sequences complete cleanly indicates solid implementation of these core operations.
- [Window manager — concurrent windows] Multiple windows (4+) can be open simultaneously without cross-window state corruption, content bleed, or frozen state
- Why: Managing multiple concurrent browser contexts (iframes) is notoriously fragile. The clean separation of each window's iframe and content state demonstrates careful architecture.
- [Window manager — iframe parameter accuracy] Every opened window iframe correctly receives the ?desktop_mode_chromeless=1 parameter, resulting in chromeless (no admin bar/menu) content render
- Why: Correct parameter passing and iframe isolation are the foundation of the desktop mode experience. This working correctly is critical.
- [Window manager — complex content support] Block editor (post-new.php) loads correctly inside a window iframe without blank state, frozen state, or postMessage bridge failure
- Why: The block editor is one of the most complex WordPress admin pages. Supporting it in a window proves the postMessage bridge and iframe isolation are solid.
| Session | Status | Turns | Flows | Notes |
|---|---|---|---|---|
activation-and-mode |
complete | 12/20 | 5/6 | Executed 5 of 6 planned flows within 12 turns. Core hypotheses (H1-H4) fully probed and verdicted. H5 (unauthenticated redirect) deferred due to environmental constraints (single browser session with persistent auth state). H6 (mode-affected surface tour) not prioritized due to time budget—the critical activation/disable/isolation probes took precedence. Recon S1 discrepancy resolved: admin bar toggle DOES exist, but is only visible in classic (?desktop_mode_classic=1) context, not in the desktop shell which replaces the traditional admin bar entirely. |
breadth-tour-admin |
complete | 18/18 | 8/9 | (recon S1) admin bar toggle: absent (but admin bar itself is present and visible); (recon S2) chromeless param: desktop_mode_chromeless=1 (confirmed); empty-state probed: Recycle Bin at 0 trashed items → window opens successfully showing admin page |
breadth-tour-lifecycle |
complete | 18/18 | 5/5 | All five hypotheses probed within budget. Virtual Spaces feature exists in code (multiple desktops per user, 16 max, stored in session meta) but browser UI entry point was not discovered despite 3 turns of exploration (recon noted it as unexplored surface). REST API, Site Health, and deactivation tests executed successfully. Session endpoint exists but returns 401 authentication error even when user is authenticated (potential capability check or nonce requirement issue). |
os-settings-persistence |
complete | 14/22 | 5/6 | Flow 6 (user isolation via CLI) deferred due to WP-CLI timeout. Verified via direct user meta inspection before the timeout (user 2 has no desktop_mode_os_settings key). Rest of H1, H3, H4, H6 probed and confirmed via browser and curl. H5 (empty-required-fields) not applicable — OS Settings uses option buttons with auto-save, no text inputs to clear. H2 (dock placement reflow) not fully probed — UI lacks explicit dock placement control; Desktop layout options (Classic/Unified/Spatial) selected instead. Security probes (H3, H4) via curl confirmed endpoint authentication gates. |
session-persistence |
complete | 17/20 | 4/5 | H1 (debounce persistence): CONFIRMED PASS — windows persist across reload; 'updated' timestamp updates on page reload indicating active debounce-save. H2 (multiple windows + payload): CONFIRMED PASS — 5 windows (Dashboard, Posts, Media, Pages, Comments) all saved with correct positions; session meta payload 1,832 bytes, well below bloat threshold. H4 (REST endpoint authentication): CONFIRMED PASS — unauthenticated POST returns 401; endpoint properly gated by is_user_logged_in() && current_user_can('read'). H3 (concurrent-session race) and H5 (closed-state persistence) partially probed but not conclusive; see deviations. |
usability-desktop-mode |
complete | 13/20 | 5/6 | Probed first-activation experience (U-H1), desktop shell entry (Flow 2), mode indicator visibility (U-H2 partial), exit affordance discoverability (U-H4), and user dropdown menu for exit options. Did not probe OS Settings save feedback detailed timing (U-H3) or explicit typography jargon audit (U-H5). Mode indicator tour partially executed — desktop shell structure observed, but standard /wp-admin/ pages are inaccessible when desktop mode is enabled (all requests redirect to portal). This itself is a UX finding. |
window-manager |
complete | 21/25 | 6/7 | Covered H1-H3, H5-H7 with empirical probes. H4 (keyboard close) partially tested - Escape key does not close windows, indicating missing keyboard-close affordance or incomplete keyboard event handling. H5 revealed focus stranding issue. All window control buttons (minimize, maximize, close) verified functional. Multiple concurrent windows open without state corruption. Block editor loads without silent failures or blank state. |
These are signals observed during the run that point at test-environment quirks (Studio + SQLite shim, WP-CLI Phar, WC stack interactions), NOT plugin defects. Apply extra scrutiny to findings in affected areas — some Problems may be false positives caused by the environment, and some real bugs may be masked.
| Session | Warning |
|---|---|
os-settings-persistence |
Studio WP-CLI commands began timing out after turn 14 (message: 'Timeout waiting for response to message wp-cli-command: No activity for 120s'). This is a Studio infrastructure issue, not a plugin defect. User meta inspections before the timeout confirmed H6 (user isolation); later CLI probes were replaced with REST API and browser-based verification. |
Recon and Verifier sessions do not produce report.json files by design — this is expected.
- Recon is a mapping pass (scout role): its output is
recon.md. It does not file PQIP items. - Verifier runs in Phase 5.5 to reproduce Critical/Major Problems on a fresh site: its output is
verification.json. Verifier findings are already reflected in the[VERIFIED ✓]/[REFUTED ✗]labels on problems above.
Computed from Claude Code transcripts at ~/.claude/projects/<proj-hash>/. Rates from config/pricing.json.
Window: 2026-05-06T12:31:16Z → 2026-05-06T12:58:29Z (with ±10min buffer for dispatch drift).
Estimated total cost for this run: $18.52
| Category | Cost | % of total |
|---|---|---|
| Fresh input | $0.02 | 0.1% |
| Output | $2.53 | 13.6% |
| Cache-create (5m) | $3.32 | 17.9% |
| Cache-create (1h) | $0.68 | 3.7% |
| Cache-read | $11.97 | 64.6% |
Total: $5.22
| Model | Messages | Input | Output | Cache-5m | Cache-1h | Cache-read | Cost |
|---|---|---|---|---|---|---|---|
claude-sonnet-4-6 |
115 | 143 | 93,849 | 0 | 113,577 | 10,438,105 | $5.22 |
Total: $13.30
| Model | Messages | Input | Output | Cache-5m | Cache-1h | Cache-read | Cost |
|---|---|---|---|---|---|---|---|
claude-haiku-4-5-20251001 |
1307 | 21,950 | 147,763 | 1,686,755 | 0 | 86,997,670 | $11.57 |
claude-sonnet-4-6 |
18 | 26 | 25,265 | 323,222 | 0 | 465,242 | $1.73 |
Per-subagent breakdown (10 sessions)
| Agent ID | Type | Models | Cost |
|---|---|---|---|
a01d526d6f525b221 |
tester | claude-haiku-4-5-20251001 | $1.28 |
a03951f51e113bf94 |
tester | claude-haiku-4-5-20251001 | $1.34 |
a426fd8aa68b2f315 |
tester | claude-haiku-4-5-20251001 | $2.14 |
a6ff4154d4435e856 |
tester | claude-haiku-4-5-20251001 | $1.73 |
a7e4922d3fa10f435 |
tester | claude-haiku-4-5-20251001 | $0.84 |
a91c2796a9f3fabf6 |
tester | claude-haiku-4-5-20251001 | $0.27 |
aa1971f4cb3a0063d |
tester | claude-haiku-4-5-20251001 | $1.37 |
acd182372e30576c2 |
tester | claude-haiku-4-5-20251001 | $1.13 |
ae1600960ddb2b1e8 |
tester | claude-haiku-4-5-20251001 | $1.46 |
ae2972ec4867b724a |
planner | claude-sonnet-4-6 | $1.73 |
Fix first (Major — verified):
- REST session endpoint 401 — The
/wp-json/desktop-mode/v1/sessionendpoint returnsrest_forbiddeneven for authenticated admins. Check thepermission_callbackin the REST route registration. - JS translation pipeline — Add
wp_set_script_translations()for the JS bundle and wrap hardcoded strings (OS Settings,Recycle Bin,Remove Clock,Add widget) withwp.i18n.__(). - Discoverability — Add a welcome notice or admin bar entry so new admins can discover and enter/exit Desktop Mode without knowing the portal URL.
Fix next (Major — investigation pending):
4. Keyboard focus — Add Escape-key handling on .role='dialog' windows and restore focus to the triggering dock button on close. Both issues share the same missing implementation.
Minor polish:
5. Regenerate the .pot file to include Report a bug and other omitted strings.
6. Pass WordPress locale to toLocaleDateString() in the clock widget (or use wp_date() server-side).
7. Add a register_deactivation_hook to clean up desktop_mode_* user meta on plugin deactivation.











