Skip to content

Bug Hunt 2026 09 04

Nick Hamze edited this page Sep 4, 2026 · 1 revision

Bug hunt — September 4, 2026

Follow-up status: all four findings below are fixed in 0.9.0-rc.1. The original bug-hunt evidence below describes the pre-fix build and is retained for traceability. The four formerly failing edge cases now pass; implementation and expanded verification are recorded in Modern Core and publishing. See the release record for publication status.

Original investigation (before fixes)

This investigation originally added regression tests and notes, not runtime fixes. The earlier ten-test baseline passed again; the nine new edge cases produced five passes and four failures.

The final combined run completed in 87.75 seconds: 19 tests, 15 passed, 4 failed, 0 skipped, 0 flaky. The same four findings reproduced. After the close assertion completed, the browser recorded zero remaining site windows, confirming destruction rather than an in-progress close animation. Cleanup verification found the original two demo connections, zero leftover fleet_edge_* users, and the managed site's unchanged original timezone.

Tested candidate

  • Fleet 0.9.0-rc.1, local working-tree candidate, runtime ZIP SHA-256 34044d17d21ea827d3a8318ba62688629f12171edb30cbc36d9bff94403d6eef.
  • OpenStation 9bac9176b26a6228e3e57c9a7c8f11f586063afd; no OpenStation source changes or PRs.
  • WordPress 7.1, Studio HTTPS fixtures, server PHP 8.3.33; CLI fault/validation checks on PHP 8.5.10.
  • Cedar Street Studio hub with Harbor Arts Journal and Harbor Arts Center connected. These are disposable local fixtures, not agency production sites.

Original findings, in recommended fix order

P1 — Closing a site window loses unsaved content without confirmation

Open a connected site's Content tab, choose New post, enter a title, observe Unsaved changes, and press the native window Close button. No discard confirmation appears and the editor window disappears. The lost text in this test was synthetic and had never been submitted to the remote site.

In assets/fleet-app.js:162, windowElement() finds the nearest ID starting with wp-window-. A form lives inside wp-window-fleet-site-panel-content, so that panel ID enters the dirty set. The framework close hook checks the actual window ID, wp-window-fleet-site, which does not match. Browser attachments record both IDs. This is a Fleet integration bug, not a reason to change OpenStation.

Proposed acceptance criteria: resolve the actual managed-window root; Cancel keeps the exact source and focus; confirmed discard closes only that window; save clears the right dirty state; two dirty windows do not interfere. Retest close, close-all, navigation, refresh, save, and browser unload. Refresh and tab round-trip preserved text in this run; missing confirmation alone is not classified as data loss.

Regression: canceling a dirty window close retains its source text.

P1 — Failed health checks erase the last known critical finding

The isolated CLI test copies a connected site's record into memory, seeds one critical finding, makes health checks due, and injects transport errors for the health endpoints. The refreshed record has health: [], an empty connection error, and a newly advanced health timestamp. No stored connection is modified by the injected fault.

includes/class-openstation-fleet.php:1723 skips failed responses. The caller at line 1452 overwrites the old findings with the resulting empty array, advances health_checked, and takes the success path. An unavailable check is therefore indistinguishable from absence of findings in that model.

Proposed acceptance criteria: retain last-known findings on failure, visibly mark stale/unknown results, distinguish attempt time from successful-check time, and preserve valid partial results without claiming failed checks succeeded. Test complete failure, partial failure, deadline exhaustion, recovery, and a genuinely resolved critical finding.

Regression: a failed Site Health fetch cannot erase the last critical finding.

P2 — Timezone Save reports success but does not change WordPress

In the managed site's Settings tab, enter America/New_York and save. Fleet displays Remote site settings updated. Reading the target's actual timezone_string option still returns the original empty value.

Core exposes this option as the REST field timezone, not timezone_string. Fleet renders/reads timezone_string in includes/class-openstation-fleet-app.php:811, requests it in a compact settings field list, and posts it in includes/class-openstation-fleet.php:1027. The REST request can succeed while ignoring that unrecognized field. Core settings schema

Proposed acceptance criteria: use Core's REST field consistently; show the existing zone; validate a real zone; verify the returned value before reporting success. Test a named zone, UTC/offset representation supported by Core, and invalid input. Preserve other settings.

The test explicitly opts into a disposable target, verifies that its URL matches a discovered connection, and restores the original timezone only if it equals the value written by this run. On the failing candidate no timezone change occurred. The normal settings-save activity entry remains.

Regression: saving a timezone updates the actual WordPress timezone.

P2 — Malformed scheduling input escapes validation as an exception

Call OpenStation_Fleet_Content::body() with otherwise valid draft fields and date_gmt set to an ISO date followed by a NUL character. On the tested CLI runtime it throws ValueError instead of returning WP_Error.

includes/class-openstation-fleet-content.php:37 passes the string to DateTimeImmutable::createFromFormat() without guarding against that exception. The normal UI is unlikely to produce this value, but malformed requests should fail cleanly. This test proves a validator exception; it does not claim a separately observed HTTP 500.

Proposed acceptance criteria: reject malformed dates before parsing or handle parser exceptions, return a useful validation error, and make zero remote writes. Add malformed/control-character, oversized, impossible, and boundary scheduling inputs across the supported PHP matrix.

Regression: malformed date input returns a validation error rather than crashing.

What passed

The ten existing browser/integration checks passed: connection approval/setup/repair/revocation/disconnect; real post/page source round trips; stale-edit protection; uncertain-create duplicate suppression; independent site windows; native assets, accessibility assertions, and diagnostics.

Five additional cases passed: draft retention after Refresh; retention across a tab round-trip; empty-search recovery; denial of unauthenticated access; and denial of another hub administrator's access to the original user's connections. Both access checks stopped before issuing any remote HTTP request. The temporary administrator was deleted.

Build, smoke, and incremental search checks passed. They do not override the four failing edge regressions. Test code is excluded from the runtime ZIP, whose checksum stayed unchanged.

Reproduction

Run against a disposable local hub with two connected sites; explicitly identify the one managed fixture that may receive test writes:

FLEET_E2E_WRITES=1 \
FLEET_E2E_USER_ID=1 \
FLEET_E2E_HUB_PATH=/absolute/Studio/fleet-hub \
FLEET_E2E_MANAGED_PATH=/absolute/Studio/fleet-adopt \
npx playwright test --config=tests/e2e/playwright.config.js

The new tests are in tests/e2e/edge-cases.e2e.spec.js. They deliberately assert the safe expected behavior and failed for the four findings in this original investigation; they were not skipped or marked expected failures. They pass on the follow-up candidate. Browser cookies stay in memory, traces are off, and fault injection is process-local. Never publish raw environment/process diagnostics or authentication artifacts.

Remaining coverage

This was not another 30-site load run or a cross-host certification. Next expand to concurrent writes to the same post, two dirty windows, keyboard/close-all paths, capability changes during a session, pagination after deletion, partial health failure, timezone/DST boundaries, and mobile-width/keyboard access. Then run the real-host and PHP/WordPress matrix in Launch checklist. Keep confirmed findings separate from these untested risks.

Clone this wiki locally