feat(ui): enhance console window management with geometry persistence and improved z-index handling - #213
Merged
Conversation
…ate + logout endpoints (#186)
…pi/login, /api/logout (#186)
Run-lock-aware CLI surface per ADR-0036 §6: guard_run_lock refuses each verb under a held run.lock before any git/gh call, otherwise delegates to existing ralphy_core primitives.
Rust helper bin (runlock_test_child) drives the HeldAlive refusal path without a shell-script child; branch_create proves the happy path on an isolated temp repo.
Self-review finding: no test proved require_some_label wins over resolve_toplevel end-to-end. Adds label_set_rejects_empty_labels_before_touching_repo.
Transplant the daemon UI terminal body into the workbench mock: vendor the xterm build set, load it in index.html, replace the faux console in wb-console.js with a live xterm attached to /ws/session (new launch, plain console, and reattach-on-load per live session), and refit on Agents-tab activation. Prove the embedding with workbench_serves_vendored_xterm. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… server close (#190) Run cargo fmt over the workbench_serves_vendored_xterm assert (verify gate runs fmt --check). Also disconnect the per-window ResizeObserver in wb-console.js's server-initiated ws.onclose branch so a dead-ws terminal stops firing fit() until the window is closed (self-review LOW). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rgv (#191) Add EffectClass + Verb::ALL/effect_class and replace the static blessed_args with spawn_argv, composing the run argv from closed-enum params (agent / planAgent via session::Agent, branchMode via BranchMode) — out-of-enum or free-text values are refused. command_ws refuses malformed params with one error frame and no spawn; the test child echoes its argv. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…s are refused (#191) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ns panel (#191) Add the ACTION_TO_VERB adapter that turns run-start/triage/push workbench:action events into a /ws/command Command, streaming status:output chunks into a capped Runs-panel raw feed. Assert the adapter ships and the page loads it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…elpers (#191) The run verb now validates agent+branchMode; the existing spawn-lifecycle and survives-disconnect tests must send them or hit the refusal path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
/api/repos now includes branch, derived by reading <path>/.git/HEAD directly (no git invocation, per ADR-0036 §3): Some(name) for a symbolic ref, None for detached HEAD or an unreadable/missing file.
loadRepos() fetches the daemon's real repo registry at init and overwrites the static projects seed; state maps reachable->idle/offline, remote is inferred from the slug shape. Swallows fetch errors so the file:// standalone shell still falls back to the seed.
…stry (#194) Two new deep modules — `confine` (path confinement, the read path's only security boundary) and `tree` (gitignore-aware `list` + text/binary `read`) — plus `TreeList`/`FileRead` Observe verbs wired through `command_ws`, which answers reads in-daemon on the requesting id and never reaches the spawn path. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…y refusal (#194) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…path (#194) `WBDaemon.observe` drives a one-reply `/ws/command` read; `mountTree` seeds the root and lazy-loads folders from `tree.list`, and `openTab` fetches real bytes via `file.read`, surfacing a refusal reason. Static `file://` fallback kept. These legs are review-only (no JS harness, PRD #185). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
#194) A botched `git add` pathspec dropped confine.rs, tree.rs, lib.rs, and Cargo.toml from 6e93d96 (which shipped only dispatch.rs/Cargo.lock) — commit the actual security-critical modules and the `command_ws` Observe branch here. Self-review fixes folded in: read() decides UTF-8 on the whole file, not the 8 KiB window (a valid multibyte char split at the boundary no longer false-positives as binary); adds tests for the window-boundary case and for escape-masked-as-not-found at both the tree and wire layers. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…derr signal Drain the child line-by-line (read_until) instead of read_to_end, so the run log is teed to disk as it arrives — observable live and crash-surviving, rather than buffered in memory and written once at exit. The file is rewritten once at the end in canonical stdout-then-stderr order, keeping it byte-identical to the returned `log` the detectors scan. Add `run_headless_logged_watched`: a stderr-only early-kill predicate that reaps the child the moment a matching line prints, instead of leaving it to idle in its own silent backoff until the wall timeout. Watches stderr only so the agent's own stdout (which may legitimately mention "rate limit") can't trip it. Existing signatures unchanged; the four adapters keep calling the same runner. Tests: early-kill fires on the marker line; a non-matching predicate still times out normally; the `stderr-then-sleep` child helper drives it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…locks OpenCode fronts many providers whose reset strings can't be trusted: live (FinCal #73) a "5 hour" limit reported a reset ~12h out — impossible, yet it parses cleanly and would park the run 11h on a bogus instant. Collapse every detected OpenCode limit to `Limit(None)` (`unschedulable_opencode_limit`) so the runner uses the ~30-min synthetic cadence (ADR-0030 D1) instead of scheduling on the hint. Claude/Codex are stable and keep theirs — trust is a per-vendor extraction decision (ADR-0023). Drive execute() through `run_headless_logged_watched` with `usage_limit_regex` over stderr, so a provider quota block (which only prints to `--print-logs` stderr and never reaches the JSON stream) is reaped in ~sub-second instead of burning the whole per-issue budget in silent backoff. The predicate matches the same signal the post-run classifier keys on, so an early-killed run classifies identically — only faster. plan() passes a no-op (no limit surfaces there). Killing on the agent's explicit stderr line is the sanctioned interruption (ADR-0003 D1 / ADR-0030), not a clock; the 60-min wall timeout is demoted to the last-resort net for a truly mute hang. Amend ADR-0005 D9 accordingly (D9a reset-discard, D9b early-kill) and correct the stale "stop-on-limit is forced" and module-doc claims. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Localhost daemon: Require-login disabled with loopback explanation, TOTP enroll stays enabled, Log off doesn't strand the operator behind the gate. Smoke script itself lives at .ralphy/wb_auth_205.py (gitignored scratch, mirrors #204's wb_smoke_204.py).
…pped errors, no source guess (#206)
…g.set/unset refusals (#207)
…ved UI (#208) - A2: isolation:isolate on .stage traps floating consoles below .kanban's z-index regardless of console focus counter. - B1: sweep every mock/Mock reference and false-in-daemon claim (settings/security footnotes) from served UI comments and strings. - test served_ui_copy_has_no_mock_or_false_claims asserts absence across all 13 non-vendor served files.
projectQuery + filteredProjects() filter by slug/branch substring; a global / shortcut focuses the search input (reusing consoleShortcutsBlocked so it never hijacks a field/modal/login); Escape clears it. Removes the dead sidebar ellipsis affordance.
…209) One consolidated Playwright pass over two real daemons (7357 loopback, 7358 network-bind Session) asserting none of the five audited symptoms (docs/audit-workbench-2026-07-13.md) reproduce, plus three extras corroborating #202-#208: 30/30 checks, ALL SYMPTOMS NOT REPRODUCIBLE. Includes the network-bind Session TOTP login (C3's hard half, #179/#205) driven through the real login form with a stdlib RFC 6238 TOTP compute.
Reworks the Security panel and its daemon backend across three phases. Phase A — UI polish: Security-modal scrollbar joins the themed design system; Login password requires a matching confirmation (two fields); shortened copy. Phase B — two-phase TOTP enrolment (§C): `enroll` writes a pending seed (`daemon-totp.pending`) and shows the QR; new `POST /api/security/totp/confirm` verifies a code before arming the live seed — no more lockout from enrolling without scanning. Phase C — real session control + OWASP ASVS L1 (§A/§B/§D): - Require-login is a persisted `daemon-require-login` flag that gates the browser UI even on a loopback bind. `auth::AuthState` holds a runtime-swappable policy (RwLock), so toggling promotes Localhost→Session immediately (no restart) and mints the access token so local machine clients use a bearer. - Session epoch (`epoch.rs`, `daemon-session-epoch`) mixed into cookie format v2 gives instant server-side invalidation on logout / re-mint / TOTP revoke / toggle — real logout, still stateless. - Login throttle (lockout after repeated failures), TOTP anti-replay (`daemon-totp-laststep`, step must strictly increase), sliding idle timeout (cookie carries `iat`; 30-min idle under a 12h absolute cap), and PBKDF2-SHA1 raised to OWASP's ~1.3M floor. Green: cargo fmt --check, clippy -D warnings, 201 tests across 28 binaries. Note: does NOT stage the unrelated `wss://` change in wb-console.js/wb-daemon.js. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…he gate When the daemon boots gated (require-login), the SPA's initial loadRepos/ loadIdentity return 401 and record "could not load projects". A successful login lifted the gate but nothing re-ran those one-shot fetches, so the shell stayed empty. submitLogin now calls rehydrateAfterAuth() on success (both the daemon and demo paths); the presence socket already self-reconnects on its 3s backoff, so it is not re-run here. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… the reveal eye - The authenticator-code input carried no autocomplete, so Edge captured the 6-digit TOTP code as a saved "username". Mark it autocomplete="one-time-code" (name one-time-code) so the browser treats it as an OTP, not a credential. - Edge/IE render a native black reveal/clear eye inside password inputs that vanished on the warm-dark theme; invert it to a light, muted glyph. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…shell - Introduced `wb-settings.js` to define a comprehensive settings schema for the workbench shell, including daemon, events, telegram, queue, branch, verify, claude, opencode, remote control, and schedule configurations. - Implemented `wb-translate.js` for on-device translation capabilities using the browser's Translator API and LanguageDetector, supporting multiple languages. - Created `wb-viewer.js` to manage file viewers for source code and Markdown, featuring editing capabilities, in-page search, and Markdown rendering with mermaid support.
… and improved z-index handling
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Testing
Manual testing of console window management and z-index behavior in the workbench shell.
🤖 Generated with Claude Code