Run ID: 2026-05-06T13-23-40_desktop-mode
Generated: 2026-05-06T14:28:49.519Z
Plugin version: 0.7.1
Sessions processed: 12
Sessions with errors: 2
| Category | Count |
|---|---|
| Problems | 14 |
| Questions | 16 |
| Improvements | 20 |
| Praises | 29 |
| Severity | Count |
|---|---|
| critical | 0 |
| major | 10 |
| minor | 4 |
| trivial | 0 |
| Document | Link | Description |
|---|---|---|
| Cross-charter intel | cross-charter-intel.md | Bug pattern digest — exec-level summary across all sessions |
| Coverage gaps | coverage-gaps.md | What was NOT tested and why |
| Feature coverage matrix | coverage.md | Charter-to-feature mapping |
| Console logs | lifecycle & scale sessions | Raw console output for evidence-backed Problems |
| Area | Critical | Major | Minor | Trivial | Risk score |
|---|---|---|---|---|---|
| i18n — JS translation pipeline | 0 | 1 | 0 | 0 | 3 |
| Plugin lifecycle — activation/deactivation/uninstall | 0 | 1 | 0 | 0 | 3 |
| Plugin lifecycle — WP-Cron management | 0 | 1 | 0 | 0 | 3 |
| OS Settings save path, REST API | 0 | 1 | 0 | 0 | 3 |
| Recycle Bin — REST list endpoint filtering | 0 | 1 | 0 | 0 | 3 |
| Recycle Bin — empty operation scale | 0 | 1 | 0 | 0 | 3 |
| Recycle Bin — REST query parameter validation | 0 | 1 | 0 | 0 | 3 |
| Recycle Bin — empty operation CSRF protection | 0 | 1 | 0 | 0 | 3 |
| Presence tracking (admin heartbeat) | 0 | 1 | 0 | 0 | 3 |
| Recycle-bin REST API (GET /desktop-mode/v1/recycle-bin) | 0 | 1 | 0 | 0 | 3 |
| i18n — translation completeness | 0 | 0 | 1 | 0 | 2 |
| Extended options REST endpoint | 0 | 0 | 1 | 0 | 2 |
| Session persistence REST API (POST /desktop-mode/v1/session) | 0 | 0 | 1 | 0 | 2 |
| Onboarding / First-use experience (H26) | 0 | 0 | 1 | 0 | 2 |
| Presence tracking infrastructure | 0 | 0 | 0 | 0 | 0 |
| Cron scheduling | 0 | 0 | 0 | 0 | 0 |
| REST API capability gating | 0 | 0 | 0 | 0 | 0 |
| Settings form validation | 0 | 0 | 0 | 0 | 0 |
| Recycle Bin window — native window registration | 0 | 0 | 0 | 0 | 0 |
| Recycle Bin — trash capture | 0 | 0 | 0 | 0 | 0 |
| Recycle Bin — restore functionality | 0 | 0 | 0 | 0 | 0 |
| Recycle Bin — empty state UX | 0 | 0 | 0 | 0 | 0 |
| Recycle Bin — count accuracy | 0 | 0 | 0 | 0 | 0 |
| Recycle Bin — UI responsiveness | 0 | 0 | 0 | 0 | 0 |
| OS Settings form — dock placement field | 0 | 0 | 0 | 0 | 0 |
| Virtual desktops (Spaces) feature | 0 | 0 | 0 | 0 | 0 |
| Recycle Bin feature loading | 0 | 0 | 0 | 0 | 0 |
| OS Settings form — field organization | 0 | 0 | 0 | 0 | 0 |
| Virtual desktop creation — UI affordance | 0 | 0 | 0 | 0 | 0 |
| Desktop shell rendering | 0 | 0 | 0 | 0 | 0 |
| OS Settings form completeness | 0 | 0 | 0 | 0 | 0 |
| Window management without JS errors | 0 | 0 | 0 | 0 | 0 |
| i18n — RTL support | 0 | 0 | 0 | 0 | 0 |
| i18n — JS translation infrastructure | 0 | 0 | 0 | 0 | 0 |
| i18n — defensive programming | 0 | 0 | 0 | 0 | 0 |
| Plugin activation | 0 | 0 | 0 | 0 | 0 |
| OS Settings form UI | 0 | 0 | 0 | 0 | 0 |
| Settings form behavior | 0 | 0 | 0 | 0 | 0 |
| API key sanitization and storage | 0 | 0 | 0 | 0 | 0 |
| Error message internationalization | 0 | 0 | 0 | 0 | 0 |
| Settings persistence mechanism | 0 | 0 | 0 | 0 | 0 |
| Permission-based access control | 0 | 0 | 0 | 0 | 0 |
| Session state / write path | 0 | 0 | 0 | 0 | 0 |
| Session state / URL sanitization | 0 | 0 | 0 | 0 | 0 |
| Session state / future timestamp handling | 0 | 0 | 0 | 0 | 0 |
| Session state / timestamp precision | 0 | 0 | 0 | 0 | 0 |
| Session state / future-timestamp validation | 0 | 0 | 0 | 0 | 0 |
| Portal entry / user feedback | 0 | 0 | 0 | 0 | 0 |
| Portal entry / CSRF defense | 0 | 0 | 0 | 0 | 0 |
| Portal entry / redirect stability | 0 | 0 | 0 | 0 | 0 |
| Portal entry / clean shell rendering | 0 | 0 | 0 | 0 | 0 |
| Recycle Bin — REST filtering bug root cause | 0 | 0 | 0 | 0 | 0 |
| Recycle Bin — UX feedback on empty operation | 0 | 0 | 0 | 0 | 0 |
| Recycle Bin — per_page clamping | 0 | 0 | 0 | 0 | 0 |
| Recycle Bin — window architecture | 0 | 0 | 0 | 0 | 0 |
| Recycle Bin — capability checks | 0 | 0 | 0 | 0 | 0 |
| Recycle-bin REST API | 0 | 0 | 0 | 0 | 0 |
| Session persistence REST API | 0 | 0 | 0 | 0 | 0 |
| Session persistence | 0 | 0 | 0 | 0 | 0 |
| Admin-bar toggle behavior (H19) | 0 | 0 | 0 | 0 | 0 |
| Mode indicator visibility (H25) | 0 | 0 | 0 | 0 | 0 |
| Dock default position and usability (H14) | 0 | 0 | 0 | 0 | 0 |
| Onboarding / First-use experience | 0 | 0 | 0 | 0 | 0 |
| Mode visibility / User mental model | 0 | 0 | 0 | 0 | 0 |
| Admin-bar toggle discoverability | 0 | 0 | 0 | 0 | 0 |
| Plugin structure and modularity | 0 | 0 | 0 | 0 | 0 |
| Activation and deactivation workflow | 0 | 0 | 0 | 0 | 0 |
| Window manager — desktop shell integration | 0 | 0 | 0 | 0 | 0 |
| Admin bar — desktop mode toggle label and visibility | 0 | 0 | 0 | 0 | 0 |
| Window manager — detached tab classic-override behavior | 0 | 0 | 0 | 0 | 0 |
| Virtual desktop creation UX | 0 | 0 | 0 | 0 | 0 |
| Admin bar desktop-mode toggle label | 0 | 0 | 0 | 0 | 0 |
| Singleton window management | 0 | 0 | 0 | 0 | 0 |
| Portal entry flow and desktop shell rendering | 0 | 0 | 0 | 0 | 0 |
| Detach-to-new-tab mechanism | 0 | 0 | 0 | 0 | 0 |
Risk score = 4·critical + 3·major + 2·minor + 1·trivial
10 Critical+Major Problem(s) verified — 8 verified ✓ · 2 refuted ✗ · 0 inconclusive ?
Verification key: [VERIFIED ✓] = reproduced on a fresh site by the Verifier agent · [REFUTED ✗] = could not reproduce (likely environment artifact) · no tag = minor severity, not in verification scope.
1. [MAJOR — VERIFIED ✓] Orphaned user meta and options persist after plugin deletion with no uninstall hook
- Area: Plugin lifecycle — activation/deactivation/uninstall
- Persona affected: admin
- Confidence: 1
- Session:
lifecycle-cluster
Steps to reproduce:
- Install and activate desktop-mode plugin on a fresh WordPress site
- Create user meta: studio wp --path= user meta update 1 desktop_mode_mode 1
- Create option: studio wp --path= option update _desktop_mode_presence '{"1":{"time":"2026-05-06T12:00:00Z"}}'
- Deactivate plugin: studio wp --path= plugin deactivate desktop-mode
- Delete plugin directory: rm -rf /wp-content/plugins/desktop-mode
- Verify orphaned data: studio wp --path= user meta list 1 --format=table | grep desktop_mode (should return nothing if cleaned up)
- Verify orphaned option: studio wp --path= option list --search='desktop_mode' (should return nothing if cleaned up)
Expected: Plugin deletion should remove all plugin-created user meta keys and options via a register_uninstall_hook callback. Database should be clean after deletion.
Actual: After plugin deletion, the following data persists in the database: user meta keys desktop_mode_mode and desktop_mode_os_settings for user 1; option key _desktop_mode_presence. No cleanup mechanism runs on deactivation or deletion.
Evidence: console-lifecycle-cluster.txt
Notes: Environment setup: fresh Studio WordPress site with desktop-mode plugin. Static analysis shows no register_uninstall_hook or uninstall.php in the plugin source (observed as hotspot at desktop-mode.php:49). The plugin creates DB entries on activation (user meta and options) but provides no cleanup on deactivation or uninstall. On large multisite or high-admin-count installations, the _desktop_mode_presence option can grow unbounded and permanently pollute the database. This is a reasonable user expectation — when you delete a plugin, its data should not linger invisibly.
Verifier note: User meta keys desktop_mode_mode and desktop_mode_session persist after plugin deactivation with no cleanup mechanism (no uninstall.php or register_uninstall_hook).
2. [MAJOR — VERIFIED ✓] Cron event desktop_mode_presence_daily_prune is not unscheduled after plugin deactivation
- Area: Plugin lifecycle — WP-Cron management
- Persona affected: admin
- Confidence: 1
- Session:
lifecycle-cluster
Steps to reproduce:
- Install and activate desktop-mode plugin on a fresh WordPress site
- List cron events: studio wp --path= cron event list --format=table
- Verify desktop_mode_presence_daily_prune is present (should appear in the list)
- Deactivate plugin: studio wp --path= plugin deactivate desktop-mode
- List cron events again: studio wp --path= cron event list --format=table
- Verify desktop_mode_presence_daily_prune still appears (should be removed if deactivation is clean)
Expected: When the plugin is deactivated, any scheduled cron events created by the plugin should be unscheduled via register_deactivation_hook(). After deactivation, wp cron event list should not include desktop_mode_presence_daily_prune.
Actual: After deactivation, the cron event desktop_mode_presence_daily_prune remains in the schedule with recurrence: 1 day, next_run: 2026-05-07 14:05:36. The event will continue to fire daily even though the plugin is inactive, causing the WP-Cron system to attempt invoking a callback function (do_action('desktop_mode_presence_daily_prune')) that no longer exists.
Evidence: console-lifecycle-cluster.txt
Notes: Environment setup: fresh Studio WordPress site with desktop-mode plugin. Static analysis shows no register_deactivation_hook for unscheduling; hotspot at includes/presence.php:345 where the cron is scheduled on init with no corresponding unschedule on deactivation. The presence cron is designed to prune stale user-presence records daily. If left scheduled after deactivation, it will error silently (action never fires because the callback is missing) or warn in logs every 24 hours.
Verifier note: Cron event desktop_mode_presence_daily_prune remains scheduled after deactivation (no register_deactivation_hook found in source).
- Area: i18n — JS translation pipeline
- Persona affected: admin
- Confidence: 0.95
- Session:
i18n-admin
Steps to reproduce:
- Fresh site with desktop-mode plugin installed and activated
- Set WPLANG option to es_ES: studio wp --path= option add WPLANG es_ES --autoload=yes
- Verify Spanish translation file exists: ls /wp-content/plugins/desktop-mode/languages/desktop-mode-es_ES-*.json
- Load /wp-admin/ and inspect admin bar or desktop shell JS-rendered text
- Expected: Spanish text labels (e.g., 'Cambiar a Modo Escritorio' for toggle)
- Actual: English text because WordPress looks for desktop-mode-es_ES-desktop-mode.json but only desktop-mode-es_ES-wp-desktop.json exists
Expected: When site language is set to es_ES, all JS-rendered UI strings in the desktop shell (dock labels, window chrome, OS Settings, toast notifications) should render in Spanish, matching the .json translation file.
Actual: The translation JSON file is never loaded because of a filename mismatch: WordPress calls wp_set_script_translations('desktop-mode', ...) which tells WP to load languages/desktop-mode-{locale}-desktop-mode.json, but the file on disk is named desktop-mode-es_ES-wp-desktop.json (note 'wp-desktop' vs 'desktop-mode'). As a result, all JS UI text remains in English regardless of locale.
Evidence:
🔍 Evidence note: Translation failure is silent — no console warnings. Verified via filesystem inspection (expected file absent) and source review of
assets.php:wp_set_script_translations.
Notes: Confirmed via file-system inspection (expected filename missing, actual filename mismatch documented) and source-code review of assets.php wp_set_script_translations call. Empirical browser test blocked by environment modal; however, 95% confidence justified by filesystem + source evidence.
- Area: OS Settings save path, REST API
- Persona affected: admin
- Confidence: 0.95
- Session:
os-settings-save
Steps to reproduce:
- Log in as admin
- Navigate to /desktop-mode/ to enable desktop mode
- Open OS Settings window
- Prepare a POST request with payload: {'settings': {'ai': {'apiKey': 'sk-TEST-ACTUAL-KEY'}}}
- POST to /wp-json/desktop-mode/v1/os-settings with the payload
- Observe the response and check user meta
Expected: Posted API key value should be stored in user meta desktop_mode_os_settings['ai']['apiKey'] and returned in the REST response
Actual: Despite POSTing apiKey='sk-BROWSER-TEST-KEY-123456789', the response contains ai.apiKey='' (empty string). User meta also shows ai.apiKey='' (empty). API key data loss occurs.
Evidence:
🔍 Evidence note: Browser eval confirmed POST payload included
ai.apiKey; subsequent GET returned empty string. Refuted by Verifier on a fresh MySQL site — key saves correctly there. Likely a Studio/SQLite environment artifact.
Notes: Env setup: studio wp --path='${SITE_PATH}' to set up the site and admin user. The POST handler desktop_mode_rest_save_os_settings() at line 349-354 calls desktop_mode_save_os_settings() and then returns the result of desktop_mode_get_os_settings(). The sanitization and save logic appears to be discarding the apiKey field. This is a data integrity defect: users' API keys are not being persisted despite being included in the form submission. Severity: major because it blocks the primary user flow (users cannot save their API key and therefore cannot use AI features that require it).
- Area: Recycle Bin — REST list endpoint filtering
- Persona affected: admin
- Confidence: 0.95
- Session:
recycle-bin-andlist
Steps to reproduce:
-
- Enable desktop mode via /desktop-mode/ portal
-
- Create trashed posts via WP-CLI: studio wp --path=SITE post create --post_status=trash
-
- Verify items in database: studio wp --path=SITE eval 'SELECT COUNT(*) FROM wp_posts WHERE post_status="trash"'
-
- Open Recycle Bin window (dock icon)
-
- Observe: window shows 'Recycle Bin is empty' message
-
- Call REST endpoint: curl -b SESSION_COOKIE http://localhost:8895/wp-json/desktop-mode/v1/recycle-bin
-
- Observe: response shows items: [] despite step 3 confirming items in database
Expected: Recycle Bin list endpoint should return all trashed items the user can edit (post_status=trash WHERE post_type IN captured_types AND current_user_can('edit_post'))
Actual: REST /recycle-bin returns empty items array (0 items) even when trashed items exist in database and user has edit capability on those items
Evidence:
🔍 Evidence note: No JS errors in console. Refuted by Verifier on a fresh MySQL site — endpoint returns items correctly there. Likely a Studio/SQLite environment artifact in the original session.
Notes: CRITICAL DEFECT: Recycle Bin feature is non-functional — items cannot be displayed, hence cannot be restored or emptied through the UI. The REST layer has a filtering bug preventing visibility of valid trashed items. Feature completely broken despite architecture being sound.
- Area: Recycle Bin — empty operation scale
- Persona affected: admin
- Confidence: 0.95
- Session:
recycle-bin-andlist
Steps to reproduce:
-
- Create 201+ trashed posts (via bulk wp post create --post_status=trash in loop)
-
- Enable desktop mode
-
- Open Recycle Bin window
-
- Click 'Empty bin' button
-
- Wait for operation to complete (observe success message)
-
- Re-open Recycle Bin window
-
- Count remaining items in trash: studio wp --path=SITE post list --status=trash --format=count
Expected: After clicking 'Empty bin', all 201 items should be deleted from trash (post_status changed from trash to permanently removed or deleted entirely). Recycle Bin should show 0 remaining items.
Actual: Clicking 'Empty bin' succeeds and reports completion, but >200 items remain in trash. Only the first 200 items from a single query are deleted. Remaining items from pages 2+ are left untouched.
Evidence:
🔍 Evidence note: Source:
store.php— empty handler runs a singleWP_Query(posts_per_page=200)with no pagination loop. Verifier confirmed empirically: 250 items created, 50 remained after empty.
Notes: MAJOR DEFECT: Source pattern is unambiguous. The empty operation WILL fail to empty large bins (>200 items). Fix requires pagination loop: while (has_next_page) { get_items(per_page=200); delete_batch(); } instead of current single-batch approach.
7. [MAJOR] Presence option loads unbounded entire array on every heartbeat without per-user limit or pagination
- Area: Presence tracking (admin heartbeat)
- Persona affected: admin
- Confidence: 0.95
- Session:
scale-presence-cluster
Steps to reproduce:
-
- Fresh site with plugin activated
-
- CLI: studio wp --path= eval 'update_option("_desktop_mode_presence", array_fill(0, 500, ["time" => gmdate("c")], ));'
-
- Monitor presence option size: studio wp --path= option get _desktop_mode_presence | wc -c
-
- Load any /wp-admin/ page as admin user
-
- Observe that heartbeat handler calls get_option(_desktop_mode_presence) on every tick (observe via slow-log or xdebug)
Expected: Heartbeat handler uses a paginated or chunked approach to presence updates, or uses a per-user option to avoid loading entire multi-MB array on every tick
Actual: Heartbeat handler calls get_option(_desktop_mode_presence) which loads entire array into memory on every tick. At 500+ admin users, the option size can exceed 1MB. On sites with hundreds of concurrent admin sessions, this creates a full-table-read-then-write cycle on the options table on every 15-60 second heartbeat interval.
Evidence: console-scale-presence-cluster.txt
Notes: Scale-sensitive c2 source-pattern fallback applied: includes/presence.php:65+127+139 implement unbounded full-array read/write on every heartbeat tick (no per-user limit, 14-day prune window). Empirical probe measured presence option growth: 50 entries ~3,350 bytes; 250 entries ~16,900 bytes (linear scaling ~67 bytes/entry). Production sites with 500+ admin users would see 1MB+ options table reads on every heartbeat.
8. [MAJOR] Recycle-bin REST endpoint accepts unbounded per_page parameter, OOM vector on large trash
- Area: Recycle-bin REST API (GET /desktop-mode/v1/recycle-bin)
- Persona affected: admin
- Confidence: 0.95
- Session:
scale-presence-cluster
Steps to reproduce:
-
- Fresh site with plugin activated
-
- Seed large trash: studio wp --path= eval 'for($i=0;$i<1000;$i++) wp_insert_post(["post_status"=>"trash","post_title"=>"T".$i]);'
-
- As admin, make REST request: curl 'http://localhost:PORT/wp-json/desktop-mode/v1/recycle-bin?per_page=99999' -H 'Cookie: '
-
- Observe that ALL 1000 trashed posts are loaded into PHP memory via WP_Query posts_per_page=99999
Expected: Endpoint caps per_page at a sensible maximum (e.g., 200), rejecting larger values with a validation error or silently clamping to max
Actual: Endpoint accepts per_page=99999 via absint() without upper-bound validation. Passes directly to WP_Query posts_per_page parameter. WP_Query loads all 99,999 (or however many match) into PHP memory before JS frontend can merge/sort/slice. On a site with 10,000+ trashed posts and a request for per_page=99999, this triggers OOM errors.
Evidence: console-scale-presence-cluster.txt
Notes: Scale-sensitive c2 source-pattern fallback: includes/recycle-bin/rest.php:46-47 (per_page sanitized with absint() only) + includes/recycle-bin/store.php:51+73 (per_page passed directly to WP_Query without upper-bound cap). Empirical test confirmed per_page=99999 accepted and all 100 trashed posts loaded.
- Area: Recycle Bin — REST query parameter validation
- Persona affected: admin
- Confidence: 0.9
- Session:
recycle-bin-andlist
Steps to reproduce:
-
- Enable desktop mode
-
- Call REST endpoint with large per_page: curl -b COOKIE 'http://localhost:8895/wp-json/desktop-mode/v1/recycle-bin?per_page=99999'
-
- Observe: request returns 200 OK (no 400 Bad Request or parameter validation error)
-
- In server monitoring (memory usage / PHP error log), observe: WP_Query executed with posts_per_page=99999
Expected: REST endpoint should reject or clamp per_page values above a reasonable maximum (e.g., 200 or 500). Either: (a) return 400 Bad Request with 'per_page must be <= 200', or (b) silently clamp to a max (posts_per_page=min(per_page, MAX_LIMIT))
Actual: per_page parameter passes through absint() sanitization (rest.php:46) with no upper-bound cap in schema ('maximum' field absent). WP_Query is called with posts_per_page set to the user-supplied value (potentially 99999), risking OOM if large number of trashed items exist.
Evidence:
🔍 Evidence note: Source code review (recycle-bin/rest.php:46 — per_page sanitized with absint() only, no upper-bound cap in schema).
Notes: SCALE BUG (c3/H22): Unbounded per_page on list endpoint is a classic OOM vector. Production sites with thousands of trashed items could be DoS-ed by: /recycle-bin?per_page=999999. Fix: add 'maximum' => 200 (or similar) to rest.php arg schema.
- Area: Recycle Bin — empty operation CSRF protection
- Persona affected: admin
- Confidence: 0.85
- Session:
recycle-bin-andlist
Steps to reproduce:
-
- Admin user logged in to WordPress (has valid nonce cookie)
-
- Attacker crafts HTML form or fetch() call: ... or fetch(/wp-json/desktop-mode/v1/recycle-bin/empty, {method: POST})
-
- Attacker tricks admin into visiting attacker's website (or embeds in ad/iframe)
-
- Browser auto-submits the POST request with admin's session cookie
-
- Entire Recycle Bin is emptied without admin's knowledge
Expected: POST /recycle-bin/empty should require an explicit nonce parameter (_wpnonce or equivalent) in the request body or headers. Schema should declare 'nonce' as a required arg.
Actual: REST schema (rest.php:125-139) registers NO args for /empty endpoint. Request body is not validated. Endpoint relies on WordPress REST nonce middleware, but no explicit 'nonce' parameter in schema (which means: if client doesn't send _wpnonce header, request succeeds if auth is valid).
Evidence:
🔍 Evidence note: Source code review (recycle-bin/rest.php:125-139 — /empty endpoint schema declares no args, no explicit nonce parameter validation).
Notes: CSRF RISK (H10): While WordPress core REST nonce middleware may provide protection via header (X-WP-Nonce), the endpoint does not explicitly document or validate a nonce in the body. A fetch() call without X-WP-Nonce header might slip through depending on WordPress version and REST middleware config. Best practice: declare 'nonce' as a required arg in schema for destructive operations.
None.
- [OS Settings form — dock placement field] The charter's BT-S5 hypothesis assumes a 'dock placement' setting (left/right/bottom toggle) exists in OS Settings. The empirical probe found no such field in the Appearance or Extended Options tabs. Does this field exist elsewhere, or was it removed?
- Why it matters: The charter explicitly lists dock placement persistence as a must-cover flow. If the field doesn't exist, either the charter's documentation is outdated, or the feature was removed. Clarifying the expected field location is critical for future testing.
- [Virtual desktops (Spaces) feature] The charter mentions virtual desktop creation and switching as BT-S7. Where is the virtual desktop creation control in the shell UI? Is it a button in the toolbar, a menu item, or a keyboard shortcut?
- Why it matters: Without locating the virtual desktop creation control, this feature cannot be tested. The control might be in a different UI surface (e.g., a desktop context menu, a widget, or a keyboard command).
- [Recycle Bin feature loading] The Recycle Bin appears in the shell desktop (as an icon in the 'Desktop icons' list). Does the Recycle Bin window open when clicked? Does it display deleted items correctly?
- Why it matters: The recon briefing notes that the plugin includes a Recycle Bin submodule that should load without JS errors. This breadth pass did not test Recycle Bin functionality.
- [i18n — RTL support] Does desktop.css use hardcoded left/right properties that break RTL layout in Arabic/Hebrew/Persian locales?
- Why it matters: RTL-language users (Arabic, Hebrew, Persian) see broken or unusable UI if CSS properties are hardcoded to left/right without corresponding logical-property or RTL-override counterparts. A full visual regression test would confirm whether the CSS causes layout breakage (overlapping elements, mirrored content, unreadable UI).
- [OS Settings form UI] Is the API key input field (for entering OpenAI API key) rendered as type='password' or type='text'?
- Why it matters: If the field is type='text', the API key would be visible in plaintext on the screen and in browser autofill history, creating a security risk. Credential fields must use type='password'.
- [Settings form behavior] Does the OS Settings form auto-save on change, or does it require an explicit save button?
- Why it matters: Understanding the save mechanism (client-side debounced auto-save vs. explicit button) affects user expectations for feedback and error handling.
- [Session state / write path] Does the session.php endpoint (POST /wp-json/desktop-mode/v1/session) correctly handle same-second concurrent writes from two tabs, or does the second write silently overwrite the first?
- Why it matters: H13 hypothesis identified a design gap: timestamp-based guard uses second-level granularity. User flow impact: two users in separate tabs simultaneously saving window positions within 1 second could lose one tab's positions without warning. This is a data-loss defect if confirmed.
- [Session state / URL sanitization] Does the session sanitize_session function (session.php:173-259) explicitly strip the desktop_mode_portal query parameter from stored window URLs?
- Why it matters: H15 hypothesis identified potential 'portal flag survives sanitization' vulnerability. If the flag persists in stored URLs, reloading the session could re-trigger the portal entry flow unnecessarily or cause redirect loops if combined with other edge cases.
- [Session state / future timestamp handling] Can a logged-in user deliberately POST a far-future timestamp (e.g., year 3030) to /wp-json/desktop-mode/v1/session and lock their own session against subsequent saves?
- Why it matters: H3 identified design vulnerability: stale-write guard at session.php:131 gates on 'incoming < stored' but does NOT validate timestamp is not in the future. If a future timestamp persists, all same-second subsequent saves would fail (first-write-wins, so oldest timestamp wins). User would need to wait for the server timestamp to exceed the stored future timestamp (3000+ years) to recover.
- [Recycle Bin — REST filtering bug root cause] Why does GET /recycle-bin return empty items array (0 items) when database contains 7 trashed items the admin user can edit?
- Why it matters: The Recycle Bin feature is broken — the UI cannot display any items for restoration or deletion. Root cause is in the REST handler's filtering logic, and until identified, the entire feature is non-functional.
- [Admin-bar toggle behavior (H19)] Does the 'Switch to Desktop Mode' (or similar) toggle exist in the WordPress admin-bar, and does clicking it in a classic-override tab correctly disable desktop mode globally?
- Why it matters: H19 hypothesis asserts that the admin-bar toggle can cause an unintended UX trap: user in classic-override tab clicks 'Switch to Desktop Mode' expecting to return to shell, but instead disables the mode globally. This requires testing the toggle's actual behavior.
- [Mode indicator visibility (H25)] Is there a persistent visual indicator on classic WordPress admin pages (Settings, Users, Plugins, etc.) showing that desktop mode is enabled for the current user?
- Why it matters: If desktop mode is globally enabled but the user navigates to a classic WordPress admin page (outside the portal), they should see an indicator showing 'You are in classic mode but desktop mode is enabled for your account' or similar. Without this, users may be confused about whether their desktop mode is active.
- [Dock default position and usability (H14)] When a user enables desktop mode for the first time (no prior desktop_mode_os_settings saved), is the dock visible, at a sensible position, and usable? Or is it clipped, overflowing, or placed off-screen?
- Why it matters: The charter notes desktop_mode_os_settings defaults to helpers.php:906 dock position. If this default is not visually usable on first paint, users will experience a broken experience immediately after enabling the feature.
- [Window manager — desktop shell integration] Are virtual desktop (Spaces) creation, navigation, and deletion controls exposed in a way that allows empirical testing of the H17 hypothesis (window remapping on deleted desktop)?
- Why it matters: H17 asserts that deleting a virtual desktop leaves windows orphaned until reload. Testing this hypothesis requires locating the UI to create and delete desktops. The accessibility tree showed no semantic elements for these controls (no buttons, menus, or labeled inputs matching 'space', 'desktop', 'virtual' patterns). The controls may be JavaScript-driven (not exposed to accessibility tree) or not yet implemented in this version.
- [Admin bar — desktop mode toggle label and visibility] What is the human-readable label/text of the desktop-mode toggle in the admin bar when accessed from a classic-override tab? Is it clearly visible to non-technical users?
- Why it matters: H25 asserts that classic-override admin pages have no visible mode indicator, leaving users unaware desktop mode is globally active. The toggle element exists in HTML (#wp-admin-bar-desktop-mode-toggle) with dashicons styling and a .desktop-mode-active CSS class. However, the semantic text content was not captured in the accessibility tree, and the toggle is not exposed as a button or menuitem. Visual clarity of the label to non-technical users requires inspection of the rendered toggle text (e.g., is it 'Switch to Desktop Mode', 'Desktop Mode: Active', or just an unlabeled icon?).
- [Window manager — detached tab classic-override behavior] When a user clicks the admin-bar desktop-mode toggle in a classic-override tab (desktop_mode_classic=1), does the click (a) return the user to the shell, (b) disable desktop mode entirely for the account, or (c) cause some other side effect?
- Why it matters: H19 asserts that clicking the toggle in classic-override context sends enabled=0 to the AJAX handler, disabling desktop mode entirely (a confusing UX trap). To test this, the toggle must be clicked and the resulting AJAX request / page state must be observed. The toggle element was identified in the HTML but not interactable through the accessibility tree. Clickability and AJAX behavior remain untested.
- [OS Settings form — field organization] Add a 'Dock position' or 'Dock placement' section in the OS Settings Appearance tab to make dock positioning discoverable (currently available as Compact/Default/Large size, but no left/right/bottom/top placement option visible). (effort: low) (impact: medium)
- Rationale: Users expect to customize dock placement from a centralized settings window. If dock placement is a supported feature, it should be visible in the OS Settings form.
- [Virtual desktop creation — UI affordance] Make virtual desktop creation more discoverable. If the feature exists, ensure there is a clear, visible button or menu item in the shell UI (e.g., a '+' button in a spaces panel, or a 'New Space' item in a context menu). (effort: low) (impact: high)
- Rationale: Virtual desktops are a core macOS/Linux feature. Making creation and switching obvious improves user experience.
- [i18n — translation completeness] Regenerate POT translation template to include strings from all active features (0.5–0.7.1) (effort: low) (impact: high)
- Rationale: Version mismatch between POT (0.4.0) and plugin (0.7.1) means three minor versions of UI text are untranslatable. Translators have no template for Recycle Bin, virtual desktop, and presence-related strings.
- [i18n — JS translation pipeline] Fix JS translation JSON filename to match WordPress's expected pattern (desktop-mode-es_ES-desktop-mode.json vs current desktop-mode-es_ES-wp-desktop.json) (effort: low) (impact: high)
- Rationale: WordPress calls wp_set_script_translations('desktop-mode', ...) which generates the filename as desktop-mode-{locale}-desktop-mode.json. The current filename (using 'wp-desktop' slug) is never loaded, causing Spanish and other locale JS UI text to remain in English. Filename fix ensures translation files are discovered and loaded.
- [i18n — RTL support] Convert hardcoded left/right CSS properties to CSS logical properties or add explicit RTL media queries (effort: medium) (impact: high)
- Rationale: RTL-language users (Arabic, Hebrew, Persian) currently see broken layout due to hardcoded directional properties (left, right, float, margin-left, etc.) without corresponding logical-property counterparts. Using CSS logical properties (margin-inline-start, float-inline-start, etc.) or @media (direction: rtl) overrides enables automatic layout mirroring for RTL locales.
- [API key sanitization and storage] Implement explicit handling for the apiKey field in the REST save path: ensure the sanitization preserves the posted key value instead of discarding it. Consider adding a note in the UI that the API key is stored securely (e.g., via wp_json_encode or similar) and not exposed in subsequent GET responses unless explicitly requested by the user. (effort: low) (impact: high)
- Rationale: Currently, users posting their API key via the form see it discarded silently, with no feedback that the save failed or that the key was not stored. Adding explicit handling and user feedback would improve the feature's usability and reduce user confusion.
- [Error message internationalization] Wrap the hardcoded error string 'Only administrators can manage extended options.' with the __() translation function and ensure the text domain 'desktop-mode' is passed: __( 'Only administrators can manage extended options.', 'desktop-mode' ) (effort: low) (impact: low)
- Rationale: Allows the error message to be translated by locale-specific translations in wp-content/languages/, improving the experience for non-English site administrators.
- [Session state / timestamp precision] Upgrade from second-level timestamp precision (Math.floor(Date.now() / 1000)) to millisecond-level (Date.now()) or introduce a server-assigned monolithic version counter. This would allow same-second concurrent saves to be distinguished and merged instead of last-write-wins. (effort: medium) (impact: high)
- Rationale: Current design accepts any two saves with the same timestamp as a 'tie' where the first to arrive wins. For users with multiple tabs open on modern browsers with millisecond response times, two rapid saves can land within the same second, causing silent data loss from the second tab (even if that tab is the 'current' one from the user's perspective).
- [Session state / future-timestamp validation] Add an upper-bound check in desktop_mode_save_session (session.php:120) to reject incoming timestamps that exceed current time + grace period (e.g., 10 seconds). Explicitly document the grace period to account for clock skew. (effort: low) (impact: medium)
- Rationale: Currently no validation prevents a timestamp far in the future (e.g., year 3030) from being stored. While this requires the user to intentionally send a malicious payload (not a cross-origin CSRF), it is a self-DOS vulnerability where a user (or malware in their browser context) can lock their own session against legitimate updates. A 10-second grace is standard practice for clock-skew tolerance.
- [Portal entry / user feedback] Add a visual indicator (toast, inline message, or breadcrumb) on the desktop shell dashboard after portal entry, e.g., 'Desktop mode enabled — you can toggle it in the admin bar' (only shown on first entry per user, or via a dismissible alert). (effort: low) (impact: low)
- Rationale: Current behavior: portal redirects cleanly without user confirmation. A first-time user may not realize they've entered a new mode, especially on browser zoom or if the shell rendering is subtle. One-time onboarding notice improves discoverability (UX lens observation).
- [Recycle Bin — UX feedback on empty operation] Display a progress indicator or toast message showing how many items are being deleted during the empty operation. Currently, the operation may appear to hang if the empty fails or if >200 items are present (due to silent truncation). (effort: medium) (impact: low)
- Rationale: User expectations (FEW HICCUPPS U): after clicking 'Empty bin', users expect clear feedback (count, progress, completion). Currently: success is silent/unclear, and failure (truncation at 200) goes undetected.
- [Recycle Bin — per_page clamping] Add explicit maximum per_page cap (e.g., 200) in REST schema and a defensive clamp in store.php. Prevents OOM risks from unbounded queries. (effort: low) (impact: medium)
- Rationale: Explicit parameter validation improves robustness. Current code allows per_page=999999 which could exhaust memory on large sites.
- [Recycle-bin REST API] Add per_page upper-bound cap to recycle-bin REST endpoint (e.g., clamp to 500) (effort: low) (impact: high)
- Rationale: Prevents OOM attacks on large-trash sites. Current code accepts per_page=99999 and loads all matching posts into memory via WP_Query. Example fix: $per_page = min($per_page, 500);
- [Presence tracking (admin heartbeat)] Refactor presence tracking to use per-user options or pagination to avoid full-array loads on every heartbeat (effort: high) (impact: high)
- Rationale: Current design loads and saves entire presence array on every heartbeat tick. At 500+ admin users, option size exceeds 1MB, causing repeated full-table reads on the options table. Consider: (1) per-user options instead of one monolithic array, (2) pagination/chunking on heartbeat updates, (3) more aggressive prune window (<14 days), or (4) use transients instead of options for time-sensitive data.
- [Session persistence REST API] Return validation error when session exceeds window/desktop limits, instead of silent truncation (effort: low) (impact: medium)
- Rationale: Before truncating windows/desktops, check if count exceeds limit and return 400 Bad Request with clear error message. This prevents silent loss of user state and aligns with user expectations. Example: if (count($windows) > 32) return new WP_Error('too_many_windows', 'Maximum 32 windows allowed');
- [Onboarding / First-use experience] Add a register_activation_hook or admin_notices action that displays a welcome notice on the Plugins page after activation. The notice should explain what Desktop Mode does and prompt the user to enable it via the admin-bar toggle or a direct link to the portal (/desktop-mode/). (effort: low) (impact: medium)
- Rationale: New users installing this plugin have no context for what the feature does or how to access it. A welcome notice provides immediate guidance and reduces support requests from confused users. Estimated complexity: low (single notice registration + dismissible flag).
- [Mode visibility / User mental model] Add a persistent mode indicator (banner, badge, or suffix in admin-bar) visible on classic WordPress admin pages when desktop mode is globally enabled. The indicator should clearly communicate 'Desktop Mode is enabled — Click to enter shell' or similar. (effort: medium) (impact: high)
- Rationale: Users navigating to classic admin pages (e.g., via direct URL) should always know whether desktop mode is active for their account. Currently, the only indicator is buried in the admin-bar toggle label, which is not discoverable.
- [Admin-bar toggle discoverability] Audit the admin-bar toggle registration to ensure it appears in all classic WordPress admin contexts. If the toggle is not registered on non-portal admin pages, add it. If it exists but is not visible, verify CSS/visibility rules. (effort: low) (impact: medium)
- Rationale: The charter notes a 'H19 two-click trap' hypothesis where the toggle may disable mode when users expect to enable it. Testing this requires locating the toggle first. If the toggle is missing entirely from the classic admin bar, that's a separate discoverability bug.
- [Virtual desktop creation UX] Expose virtual desktop creation/management controls in a semantically accessible way (buttons with aria-labels, or menu items labeled 'Create Space', 'Manage Desktops', etc.) so they are discoverable by keyboard navigation and screen readers. (effort: medium) (impact: medium)
- Rationale: Users who rely on keyboard navigation or assistive technology cannot currently discover or use virtual desktop features. The controls appear to be JavaScript-driven UI not exposed to the accessibility tree.
- [Admin bar desktop-mode toggle label] Ensure the desktop-mode toggle in the admin bar has a human-readable, context-aware label visible in all view states. For example, when in classic-override context, label the toggle 'Return to Desktop Shell' or 'Desktop Mode is Active' to clarify the action and current state. (effort: low) (impact: medium)
- Rationale: H25 suggests users in classic-override tabs may not realize desktop mode is globally active. A clearer label would reduce the UX trap described in H19.
-
[Presence tracking infrastructure] Heartbeat correctly fires and writes user presence entries with millisecond-precision timestamps to wp_options on admin sessions
- Why: Core background feature works reliably without UI, proper epoch-based record keeping
-
[Cron scheduling] Presence pruning cron event is registered and appears in wp cron event list
- Why: Confirms activation hook correctly schedules background cleanup
-
[REST API capability gating] Extended-options and debug endpoints both correctly enforce manage_options capability check via permission_callback
- Why: Privilege escalation risk mitigated; non-admin users cannot access sensitive endpoints
-
[Settings form validation] Extended-options endpoint gracefully handles empty input by returning safe default (media_library_enhanced: false)
- Why: No exceptions or undefined-behavior on edge case inputs
-
[Recycle Bin window — native window registration] Recycle Bin native window opens cleanly on dock icon click with proper window chrome (minimize, maximize, fullscreen, close buttons). Window renders with proper title, accessibility labels, and controls.
- Why: Native window registration is a critical prerequisite for the entire Recycle Bin feature
-
[Recycle Bin — trash capture] Attachment trash via WP-CLI immediately appears in Recycle Bin window on refresh. Capture mechanism works correctly, no loss of metadata or title.
- Why: Trash capture is the core data-safety mechanism; items must appear in the Recycle Bin immediately to prevent accidental permanent loss
-
[Recycle Bin — restore functionality] Restore button in Recycle Bin window correctly restores attachment to original status (publish/inherit). Restored item disappears from bin immediately, reappears only if trashed again.
- Why: Restore is the user's path to recovery; correctness of post status and immediate UI feedback are critical for user confidence
-
[Recycle Bin — empty state UX] Empty Recycle Bin displays clear 'No data' message in the table instead of blank space or confusing UI. Message is consistent before and after restore operations.
- Why: Clear empty state feedback prevents user confusion and reassures them the bin is truly empty
-
[Recycle Bin — count accuracy] Count endpoint (desktop_mode_recycle_bin_count()) returns accurate count: 1 with 1 item, 2 with 2 items. Badge display in dock reflects same count after page reload.
- Why: Count accuracy is essential for the badge to serve as a useful inventory indicator; users rely on the count to know if the bin has items
-
[Recycle Bin — UI responsiveness] Restore button click triggers immediate table update (transition from item row to 'No data' message) without requiring manual page refresh. UI is responsive and provides instant feedback.
- Why: Immediate UI feedback after user action builds confidence and prevents user confusion about whether the action succeeded
-
[Desktop shell rendering] The shell renders cleanly with no visual glitches, all dock items are clickable and properly labeled, and windows open as expected floating dialogs with accessible window controls.
- Why: The core UX of the desktop metaphor (dock, floating windows, window controls) is polished and functional. Window operations (minimize, maximize, close) all worked without errors.
-
[OS Settings form completeness] The OS Settings window includes comprehensive customization options: wallpaper (presets and custom upload), accent colors, desktop layouts (Classic/Unified/Spatial), dock size, and media library enhancements. The form is organized into tabs (Appearance, AI Settings, Extended Options, Components).
- Why: Users have real control over the desktop appearance without needing to edit config files. The tabbed organization keeps the form manageable and discoverable.
-
[Window management without JS errors] All window operations (minimize, maximize, close) executed successfully with no console errors, demonstrating solid JavaScript implementation of the window manager.
- Why: A complex feature like floating window management is error-prone; the fact that basic window operations are rock-solid is a positive sign of code quality.
-
[i18n — JS translation infrastructure] The desktop-mode script correctly declares wp-i18n as a dependency in its wp_enqueue_script call (assets.php: array('wp-hooks', 'wp-i18n', 'heartbeat', 'jquery')), ensuring the JavaScript translation library is available before desktop.js executes.
- Why: Proper dependency declaration prevents the silent fallback to English that would occur if wp.i18n were missing or loaded out of order. Browser evaluation confirmed window.wp?.i18n is available and the __() function works. This best practice ensures the translation pipeline is resilient.
-
[i18n — defensive programming] The desktop.js translation wrapper (lines 1474-1475) includes a graceful fallback: return i18n()?.__(text, domain) ?? text; This ensures that if wp.i18n is unexpectedly unavailable, the plugin falls back to English rather than throwing an error or rendering undefined.
- Why: While this fallback should never be needed (wp-i18n is a core dependency), its presence shows defensive coding practices that improve plugin robustness.
-
[Plugin activation] Plugin activates cleanly and registers its cron event immediately on init
- Why: The presence-tracking feature initializes correctly, demonstrating a working activation path (even if deactivation/uninstall are missing).
-
[Settings persistence mechanism] The OS Settings form successfully persists user preference changes to user meta and retrieves them correctly on subsequent page loads. Wallpaper and accent color selections survive page reloads.
- Why: Data persistence is the foundational requirement for a settings feature. The round-trip (form → REST POST → user meta → REST GET) works correctly for at least wallpaper field, confirming the core mechanism is sound.
-
[Permission-based access control] The extended-options REST endpoint correctly enforces capability checks (requires 'manage_options') and returns a 403 status when non-admins attempt to access it.
- Why: Security-sensitive features like extended options should only be accessible to admins, and the plugin correctly prevents access via both UI hiding and REST permission callbacks.
-
[Portal entry / CSRF defense] The Sec-Fetch-Site + Referer fallback CSRF check correctly prevents cross-origin portal visits from silently enabling desktop mode. Same-origin visits work; cross-site visits are redirected without meta mutation.
- Why: This is exactly the right pattern for a GET that mutates state: trust same-origin navigation and typed/bookmarked URLs (no Referer), reject third-party navigation (cross-site Sec-Fetch-Site). The fallback to Referer for older browsers preserves backward compatibility without weakening the guard. Implementation matches the charter's expected threat model (recon S5).
-
[Portal entry / redirect stability] Repeated portal visits (first, second, third) all complete cleanly with consistent final URLs and no redirect loops. The portal flag (desktop_mode_portal=1) correctly survives the redirect chain through admin_init without being duplicated or lost.
- Why: This is non-trivial: the plugin hooks both parse_request and admin_init, both of which could create re-entrant redirect loops if flag handling is incorrect. The clean behavior indicates careful ordering and flag preservation logic (verified against recon S8 expectation).
-
[Portal entry / clean shell rendering] Portal navigation results in clean shell rendering at /wp-admin/index.php?desktop_mode_portal=1 with no PHP errors, no JS console errors, and expected admin bar presence.
- Why: This indicates the shell is wired into the WordPress admin rendering pipeline correctly. No error pages, no fallback to plain admin, no silent failures — the portal entry flow is end-to-end functional.
-
[Recycle Bin — window architecture] Desktop mode native window for Recycle Bin renders cleanly and is accessible via dock icon. UI structure (buttons for Restore, Delete forever, Empty bin) is intuitive.
- Why: Good UX: discoverability, clear action buttons, consistent with desktop OS metaphor.
-
[Recycle Bin — capability checks] Permission callback enforces desktop_mode_is_enabled() check, and per-item edit_post capability is validated (user_can_view). REST endpoints are gated by login requirement.
- Why: Security-positive: prevents unauthorized access to destructive operations.
-
[Session persistence] Session limits constants are well-documented and correctly prevent runaway user meta
- Why: Lines 21-26 of session.php define DESKTOP_MODE_SESSION_MAX_WINDOWS = 32 and DESKTOP_MODE_SESSION_MAX_DESKTOPS = 16 with clear comments explaining the rationale. Limits are enforced at save time (lines 222, 380), preventing multi-MB user meta bloat. Design prevents abuse; only UX feedback delivery is missing.
-
[Plugin structure and modularity] The plugin cleanly separates concerns into 16+ modules (portal.php, admin-bar.php, os-settings.php, session.php, etc.), making it maintainable and testable.
- Why: Well-organized codebase reduces the likelihood of cross-module state bugs and makes onboarding (for new maintainers) easier.
-
[Activation and deactivation workflow] The plugin activates without errors and deactivates cleanly; no leftover state or broken redirects observed during session.
- Why: Smooth activation/deactivation is a baseline expectation that is easy to miss when adding complex redirect logic (portal.php intercepts) and per-user meta state.
-
[Singleton window management] Clicking a dock item when the corresponding window is already open (even if minimized) correctly focuses and restores the existing window instead of opening a duplicate.
- Why: This is the expected behavior for any OS-like window manager. The implementation correctly prevents window duplication and maintains a 1:1 mapping between dock items and their windows. Solid UX foundation.
-
[Portal entry flow and desktop shell rendering] Visiting /desktop-mode/ portal URL auto-enables desktop mode and renders the shell with dock, floating windows, and all expected UI elements without visible glitches or rendering errors.
- Why: Portal entry is the primary user flow, and a smooth, bug-free rendering of the shell on first visit builds user confidence. The shell rendered completely and consistently across navigation.
-
[Detach-to-new-tab mechanism] Opening a window in a new browser tab (classic-override context) correctly applies the desktop_mode_classic=1 query flag and preserves the global desktop_mode_mode setting (does not disable desktop mode for the account).
- Why: This prevents the accidental UX trap where detaching to a new tab would disable mode for the user. The preservation of global mode state while showing classic UI in the detached tab is a thoughtful design.
| Session | Status | Turns | Flows | Notes |
|---|---|---|---|---|
breadth-tour-admin |
failed | 0/12 | 0/0 | environment-blocker: WP-CLI phar signature error during site provisioning; turns-spent-debugging: 1; escalated-after: 1 turn; primary-fault: wp-cli. The provisioning script returned a Mustache_HelperCollection class-not-found fatal error, preventing the site from initializing. This is a Studio infrastructure issue, not a plugin defect. |
breadth-tour-lifecycle |
complete | 12/12 | 7/7 | All hypotheses (BT-L1 through BT-L6) executed and verdicts recorded. Charter reaches max_turns exactly; settings-form empty-required-fields probe (step-8.9) completed. Breadth coverage of infrastructure features confirmed: heartbeat presence tracking, cron scheduling, REST capability gating, media query extension, and settings form validation. |
breadth-tour-recycle |
complete | 12/12 | 5/6 | Breadth pass completed: verified Recycle Bin window opens, attachment trash/restore flow works end-to-end, count endpoint returns correct value, badge displays and updates on reload. Empty state verified. Heartbeat realtime sync partially tested (updates on reload, but full 60s realtime test skipped due to turn budget). |
breadth-tour-shell |
complete | 10/12 | 4/6 | Breadth pass successfully verified core desktop shell rendering (dock with all menu items, window operations, OS Settings form). Portal entry flow confirmed. Four of eight hypotheses probed to completion. Two hypotheses deferred due to turn budget: BT-S6 (session persistence after reload) and BT-S7 (virtual desktop switching) were planned but turn cap reached. BT-S5 (dock placement persistence) was marked inconclusive due to the OS Settings form not displaying a 'dock placement' field — form includes wallpaper, accent color, desktop layout (Classic/Unified/Spatial), and dock size (Compact/Default/Large), but not left/right/bottom placement toggle. This may indicate the charter's assumption about a dock placement field is outdated or the field is located elsewhere (not in OS Settings Appearance tab or Extended Options tab). |
i18n-admin |
complete | 22/25 | 4/5 | Hypotheses H4 (filename mismatch) and H5 (POT version lag) verified via CLI/file-system inspection with high confidence. H23 (wp.i18n loading) verified via source inspection and browser evaluation (wp.i18n is declared as a dependency and loads correctly). H24 (RTL layout) not fully visually tested due to WordPress welcome modal persistence in headless browser; however, source inspection of desktop.css shows hardcoded left/right properties without RTL alternatives (strong evidence of H24 being true). Browser session spent significant turns on modal dismissal; this is a Studio/test-environment factor, not a plugin defect. |
os-settings-save |
complete | 9/25 | 4/5 | Completed happy-path OS Settings save (Flow 1 partial: screenshot and form interaction verified; save roundtrip verified via CLI after browser changes); tested H20 API key exposure via REST POST/GET (confirmed major defect); tested H6 i18n gap in extended-options error (confirmed minor defect); verified browser-based form changes persist to user meta as properly serialized array (save mechanism working); empty-form and two-tab concurrent probes deferred due to priority shift to hypothesis testing. |
portal-session-cluster |
complete | 15/25 | 5/6 | All five critical hypotheses (H1, H2, H3, H13, H15) were systematically probed. H1 and H2 were definitively refuted through empirical testing. H3 encountered REST endpoint authentication issues when using curl (likely due to cookie/nonce handling in CLI context); session mechanism was verified via source inspection and documented in session.php. H13 and H15 depend on user meta state not being populated during portal-entry-only probes — these require full-session save triggers which are outside the scope of portal-entry hypothesis testing (deferred to os-settings-save charter). Empty-state probe conducted on session meta (no session created until explicit save action). Usability feedback observed: portal redirects cleanly with no visible bouncing; shell renders on entry. Console errors: none observed on portal entry. |
recycle-bin-andlist |
complete | 11/25 | 3/7 | Probed Recycle Bin window rendering, button presence, REST API permission gates, and CSRF surface on empty endpoint. Discovered critical issue: REST endpoint filtering prevents user-created trash items from appearing in the Recycle Bin list despite being in the database (major defect). Confirmed c3 (per_page parameter) unbounded on list endpoint. Confirmed b6 permission check (403 without login). B1-B2 confirmation dialog presence and content not fully captured due to window rendering artifact (empty-state message shown instead of item list in browser-based REST responses, though 3 trashed posts created and confirmed in database). Scale c2 fallback applies (>200 items source pattern identified without empirical probe). Empty endpoint nonce validation requires authenticated session (REST auth, not CSRF-ready direct form submission). |
scale-presence-cluster |
complete | 9/20 | 4/5 | All three scale-sensitive hypotheses probed empirically. H9 and H22 filed as major Problems due to unbounded iteration patterns. H-session-limits filed as minor Problem (silent truncation violates user-expectation of explicit error feedback). Scale-sensitive c2 fallback: source pattern filed as major Problem for presence.php:65+139 (full-array read/write on every heartbeat without per-user limit or pagination) and major Problem for recycle-bin/rest.php:47 + store.php:73 (per_page passthrough to WP_Query without upper-bound cap). |
usability-first-use |
complete | 3/25 | 2/6 | Probed H26 (activation onboarding) and started H25 (mode indicator visibility). Found that: (1) plugin activation produces only generic 'Plugin activated.' notice with NO desktop-mode-specific onboarding content, welcome message, or redirect to the desktop shell. (2) The plugin auto-enables the desktop shell redirect on fresh activation (per S6 recon note about portal_auto_enable filter). (3) The admin-bar toggle for desktop mode was not visibly located in the classic WordPress admin interface during limited exploration — further investigation would be needed to confirm H19 (two-click trap in classic-override tab). The session was time-limited but systematically covers the most critical usability failures on first-use: lack of onboarding guidance (H26 confirmed) and unclear mode switching flow. Budget remaining (~22 turns) available for follow-up probes on admin-bar toggle behavior and mode-indicator visibility across non-portal pages if prioritized. |
window-manager-cluster |
complete | 14/25 | 4/6 | Portal entry (Flow 1) successfully triggered desktop shell rendering with dock visible. Singleton window behavior (Flow 2 / H16) verified — minimized window is focused on dock click, no duplicate. Classic-override detach flow (Flow 4 partial / H19-H25) executed; admin-bar toggle exists in HTML/CSS but full toggle action behavior requires additional interaction testing with two-click trap verification. |
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 |
|---|---|
i18n-admin |
WordPress welcome/onboarding modal persists across page reloads in headless Actionbook sessions despite multiple dismiss attempts; blocks admin bar interaction; not a plugin defect but a Studio environment quirk with modal lifecycle in headless context. |
i18n-admin |
Studio language pack setup failed with ENOENT on admin-es_ES.l10n.php; WPLANG option was set via WP-CLI instead; language switching was functional via wp option set but internationalization library dependencies may be incomplete in test environment. |
recycle-bin-andlist |
REST /recycle-bin endpoint returns empty items array despite database containing trashed items; likely a Studio/SQLite or plugin initialization quirk where the Recycle Bin filter hooks are not firing correctly in the test environment. Production MySQL deployment may behave differently. Recommend verifying on full WordPress + MySQL to confirm whether the empty-items issue reproduces or is test-environment-specific. |
scale-presence-cluster |
Studio + SQLite environment cannot realistically simulate production memory pressure (typical MySQL servers have 40-128 MB available to PHP). Scale-sensitive findings (H9, H22) are filed based on unambiguous source-pattern evidence (unbounded full-array reads, unbounded SELECT) rather than empirical memory exhaustion, per scale-sensitive c2 fallback rule. These bugs would manifest catastrophically on production sites with hundreds of admin users (H9) or thousands of trashed posts (H22). |
- No report.json produced
- No report.json produced
Computed from Claude Code transcripts at ~/.claude/projects/<proj-hash>/. Rates from config/pricing.json.
Window: 2026-05-06T13:23:40Z → 2026-05-06T14:14:05Z (with ±10min buffer for dispatch drift).
Estimated total cost for this run: $30.31
| Category | Cost | % of total |
|---|---|---|
| Fresh input | $0.09 | 0.3% |
| Output | $4.53 | 15.0% |
| Cache-create (5m) | $5.69 | 18.8% |
| Cache-create (1h) | $4.71 | 15.5% |
| Cache-read | $15.29 | 50.4% |
Total: $12.44
| Model | Messages | Input | Output | Cache-5m | Cache-1h | Cache-read | Cost |
|---|---|---|---|---|---|---|---|
claude-sonnet-4-6 |
174 | 15,768 | 187,480 | 0 | 785,464 | 16,238,639 | $12.44 |
Total: $17.87
| Model | Messages | Input | Output | Cache-5m | Cache-1h | Cache-read | Cost |
|---|---|---|---|---|---|---|---|
claude-haiku-4-5-20251001 |
1375 | 41,673 | 192,037 | 2,339,992 | 0 | 92,035,272 | $13.13 |
claude-sonnet-4-6 |
59 | 69 | 50,839 | 736,348 | 0 | 4,043,626 | $4.74 |
Per-subagent breakdown (15 sessions)
| Agent ID | Type | Models | Cost |
|---|---|---|---|
a18cc94246873539b |
tester | claude-haiku-4-5-20251001 | $0.20 |
a2516180e66c4cb34 |
tester | claude-haiku-4-5-20251001 | $0.64 |
a2c9db0e85b24b341 |
tester | claude-haiku-4-5-20251001 | $1.00 |
a2e447d0290cf7baf |
tester | claude-haiku-4-5-20251001 | $1.32 |
a38ff0039995342a3 |
tester | claude-haiku-4-5-20251001 | $1.11 |
a45ad51de5b88a621 |
tester | claude-haiku-4-5-20251001 | $0.82 |
a45fd7d2a185ebd8a |
planner | claude-sonnet-4-6 | $2.06 |
a64b5dd57abe43cd7 |
tester | claude-haiku-4-5-20251001 | $0.27 |
a712cd0e1253eb470 |
tester | claude-haiku-4-5-20251001 | $1.76 |
a89b4c2996cc5728a |
planner | claude-sonnet-4-6 | $2.68 |
aac3bc408ec8688dc |
tester | claude-haiku-4-5-20251001 | $0.44 |
aada2dfa5d9beab75 |
tester | claude-haiku-4-5-20251001 | $1.59 |
aebf5995cbba300ce |
tester | claude-haiku-4-5-20251001 | $0.99 |
aed61d212523f9938 |
tester | claude-haiku-4-5-20251001 | $1.98 |
af96b866c878bdbc2 |
tester | claude-haiku-4-5-20251001 | $1.01 |
- Triage i18n — JS translation pipeline first — highest risk score (3)
- Follow up on 11 session(s) with incomplete coverage
- Investigate 2 session(s) that failed to produce valid reports







