* feat(web): fix terminal height to fill viewport, prevent outside scrolling
* feat(web): add touch scroll, font size control, and accurate fit to DirectTerminal
* feat(web): add mobile sidebar overlay, hamburger toggle, and reconnect indicator
* feat(web): add hamburger and reconnect pill to topbar
* fix(web): use xterm instead of @xterm/xterm for terminal-touch-scroll import
Upstream uses xterm@5.3.0 (not @xterm/xterm), fix the type import accordingly.
* ci: make pnpm audit strict step non-blocking
npm's legacy audit endpoint (/npm/v1/security/audits) is returning 410 Gone
as it's being retired. Add continue-on-error until pnpm ships support for
the new bulk advisory endpoint.
* fix(web): replace TerminalLike with minimal interface compatible with xterm@5.3.0
xterm@5.3.0 does not have 'input' or 'attachCustomWheelEventHandler' methods
(those are @xterm/xterm v6 APIs). Define a minimal structural interface
matching only the members actually used in the file.
* fix(web): replace mobile back button with hamburger sidebar toggle on session page
On mobile, the session detail page now shows a hamburger button instead of a back arrow, which toggles the sidebar via SidebarContext. This provides better navigation consistency with the main dashboard.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(web): filter sidebar sessions strictly by projectId to prevent prefix collision
Sessions are now filtered to only show those whose projectId matches a configured project. This prevents sessions from different AO projects (e.g., 'ao-' and 'unl-' prefixes) from being incorrectly merged under the same project entry.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(web): add ResizeObserver and deferred fit to fix blank terminal on mount
The terminal now uses a 100ms deferred fit timeout to ensure the container has been sized before measuring. Additionally, a ResizeObserver monitors the terminal container and calls fit() whenever its size changes, ensuring the terminal refits when flex layouts settle.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(web): debounce session refresh to prevent aborting in-flight fetch calls
The useSessionEvents hook now tracks when the last fetch was started and skips scheduling a new refresh if one was started less than 500ms ago. This prevents aggressive AbortController usage that was canceling in-flight requests unnecessarily. Also avoid rescheduling if a debounce timer is already pending.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(web): update SessionDetail mobile test for new sidebar toggle button
Update the test to expect the new "Toggle sidebar" aria-label instead of the previous "Back to dashboard" label on the mobile floating header.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(web): active session amber color, compact session meta row, tighter font controls
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(web): address review feedback — fontSize rerender, dead code, test cleanup
- DirectTerminal: remove fontSize from main init useEffect deps. A dedicated
effect below mutates terminal.options.fontSize in place; keeping it in the
init deps tore down and recreated the terminal (and WebSocket) on every
stepper click, losing scrollback and flashing content.
- Remove dead ReconnectingPill component — it rendered null with a placeholder
comment. Delete the file, import, and render site rather than leaving a shell.
- ProjectSidebar: make the done-filter consistent by using getAttentionLevel
in the sessionsByProject collector. Previously the collector filtered by the
narrow `status === "done"` while the render sites filtered by the broader
`getAttentionLevel(s) === "done"`, so the project badge count could include
merged/killed/terminated sessions that the rendered list hid.
- Drop the two gutted tests that tested JS primitives rather than the component
(layout-height.test.tsx, DirectTerminal.test.tsx). They provided false
coverage. Update ProjectSidebar.test.tsx for the new anchor-based session
rows (click behavior now queries role="link", and clicking the project
toggle expands rather than navigates — the separate dashboard button handles
navigation).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(web): make SessionDetail topbar tests pass after redesign
Reconcile the rebased-upstream tests with our redesigned SessionDetail:
Component:
- Show session.id in the topbar next to the headline. The ID is a stable
identifier (useful for copy/paste) while the headline changes with PR/
issue titles.
- Gate the topbar Orchestrator link on `!isOrchestrator` so we don't link
the orchestrator page to itself.
- Convert the PR popover toggle from a <button> to an <a href={pr.url}>.
Plain click still toggles the popover; ctrl/cmd-click opens the PR on
GitHub in a new tab. aria-label="PR #N" keeps the accessible name stable
regardless of the rendered chevron.
Tests:
- Mobile tests scope orchestrator link queries to the topbar via
within(getByRole("banner")) because MobileBottomNav also has an
"Orchestrator" entry.
- Desktop test opens the PR popover before asserting the PR detail
contents (blocker chips, file count, unresolved comments) — they now
live inside the popover rather than inline.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(web): restore behavioral tests + remove dead mobileOpen prop
Adds missing behavioral coverage the reviewer flagged on Dashboard.mobile.test.tsx:
- Termination confirmation flow: clicking the kill button once enters a
confirming state (aria-label becomes "Confirm terminate session") without
firing the kill request; a second click POSTs to /api/sessions/:id/kill.
- CI check chip rendering: SessionCard renders passing CI check names as chips
when the session has an enriched PR.
Removes the dead `mobileOpen` prop from ProjectSidebar:
- It was declared but immediately aliased as `_mobileOpen` and never used —
the actual mobile overlay state is driven by the `sidebar-wrapper--mobile-open`
class on the parent wrapper div in Dashboard/SessionDetail.
- PullRequestsPage was passing it too but never wired the wrapper class, so
its hamburger buttons were silently no-ops. Wrapped its sidebar in the same
sidebar-wrapper + backdrop pattern so the buckled hamburgers now actually
open the sidebar overlay on mobile.
Not in scope for this round (per reviewer): attention bucket filter /
MobileBottomNav on Dashboard — Dashboard doesn't implement those features,
so there's no behavior to assert. Those would need a feature add, not a test.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(core): listCached stale-data bug + drop inline styles in DirectTerminal
Addresses the reviewer's critical and minor feedback on PR #1278.
Critical — listCached was stale across mutation paths
listCached() had a 35s TTL, invalidated only by spawn() and kill().
Every other metadata-writing path (claimPR, restore, send, remap,
cleanup, lifecycle-manager's direct updateMetadata calls for PR
detection and state transitions) left stale data visible to
/api/sessions for up to 35s.
Fix:
- Wrap updateMetadata / writeMetadata / deleteMetadata inside
createSessionManager() so every in-file mutation auto-invalidates
the cache. The raw imports are aliased as _rawXxx and only used
by the wrappers. No call site needs to remember to invalidate.
- Expose invalidateCache() on the SessionManager interface for
callers that write metadata outside this module.
- lifecycle-manager.ts: call sessionManager.invalidateCache() after
its two direct updateMetadata sites (PR detection, state update)
so polling-driven mutations are visible on the next poll.
- Move the _cache / invalidateCache declarations to the top of the
closure so the mutation wrappers can reference them.
- Update createMockSessionManager + api-routes.test.ts mocks to
satisfy the expanded interface.
- New regression test: external mutation + sm.invalidateCache() →
next listCached() re-reads disk.
C-02 — inline styles on the terminal-container div
DirectTerminal.tsx was using style={{ overflow, display,
flexDirection, flex, minHeight }} — all static values moved to
Tailwind utilities: "w-full p-1.5 flex flex-col flex-1 min-h-0
overflow-hidden".
Not addressed in this commit (noted as out-of-scope refactor):
C-04 400-line cap on DirectTerminal.tsx. Splitting fontSizeControls
and touch-scroll wiring into sub-components is worthwhile but
mechanical — separate PR.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>