fix(review): 5 issues found in agent review of PR #40

BUG-1 (critical): CSS cascade — .sidebar{position:relative} and
.rightpanel{position:relative} at line 528/530 appeared after the
@media(max-width:640px) block and silently overrode the position:fixed
overlay behavior needed for the mobile slide-in. Wrapped both in
@media(min-width:641px) so they only apply on desktop.

BUG-2 (medium): mobileSwitchPanel() in boot.js always reopened the
sidebar overlay after closing it, with a stale comment saying 'close
after a moment' but no actual auto-close. For the 'chat' panel, the
content lives in the main area — reopening the sidebar obstructs it.
Fixed: only open sidebar for non-chat panels; chat tap closes sidebar.

BUG-3 (medium): Dockerfile was missing 'pip install -r requirements.txt'.
pyyaml (required by api/config.py) is not in the python:3.12-slim base
image — the container would fail at startup with ImportError.

SEC-2 (medium): No .dockerignore — COPY . /app included .git/, tests/,
and .env* in every image. Added .dockerignore excluding these.

NIT-3: docker-compose.yml used ${HERMES_HOME:-~/.hermes} but Docker
Compose does not shell-expand ~ in default values. Changed to
${HERMES_HOME:-${HOME}/.hermes}.

Tests: 415 passed, 0 failed (same as pre-fix).
This commit is contained in:
Nathan Esquenazi
2026-04-03 17:21:42 +00:00
parent d278563e00
commit 574cd2cf70
5 changed files with 32 additions and 12 deletions

7
.dockerignore Normal file
View File

@@ -0,0 +1,7 @@
.git
.pytest_cache
__pycache__
*.pyc
*.pyo
tests/
.env*

View File

@@ -8,6 +8,9 @@ WORKDIR /app
# Copy source # Copy source
COPY . /app COPY . /app
# Install Python dependencies
RUN pip install --no-cache-dir -r requirements.txt
# Default to binding all interfaces (required for container networking) # Default to binding all interfaces (required for container networking)
ENV HERMES_WEBUI_HOST=0.0.0.0 ENV HERMES_WEBUI_HOST=0.0.0.0
ENV HERMES_WEBUI_PORT=8787 ENV HERMES_WEBUI_PORT=8787

View File

@@ -9,7 +9,7 @@ services:
# Persist session data, settings, and projects across restarts # Persist session data, settings, and projects across restarts
- hermes-data:/data - hermes-data:/data
# Mount hermes-agent for full agent features (optional) # Mount hermes-agent for full agent features (optional)
- ${HERMES_HOME:-~/.hermes}:/root/.hermes:ro - ${HERMES_HOME:-${HOME}/.hermes}:/root/.hermes:ro
environment: environment:
- HERMES_WEBUI_HOST=0.0.0.0 - HERMES_WEBUI_HOST=0.0.0.0
- HERMES_WEBUI_PORT=8787 - HERMES_WEBUI_PORT=8787

View File

@@ -29,16 +29,21 @@ function toggleMobileFiles(){
panel.classList.toggle('mobile-open'); panel.classList.toggle('mobile-open');
} }
function mobileSwitchPanel(name){ function mobileSwitchPanel(name){
// Close sidebar if open, then switch panel // Switch the panel content view
switchPanel(name);
// For non-chat panels (tasks, skills, memory, spaces), open the sidebar
// so the panel is visible. For 'chat', the content is in the main area —
// just close the sidebar so the chat view is unobstructed.
if(name==='chat'){
closeMobileSidebar(); closeMobileSidebar();
// Open sidebar for the selected panel, then close after a moment } else {
const sidebar=document.querySelector('.sidebar'); const sidebar=document.querySelector('.sidebar');
const overlay=$('mobileOverlay'); const overlay=$('mobileOverlay');
if(sidebar){ if(sidebar){
sidebar.classList.add('mobile-open'); sidebar.classList.add('mobile-open');
if(overlay)overlay.classList.add('visible'); if(overlay)overlay.classList.add('visible');
} }
switchPanel(name); }
// Update bottom nav active state // Update bottom nav active state
document.querySelectorAll('.mobile-nav-btn').forEach(btn=>{ document.querySelectorAll('.mobile-nav-btn').forEach(btn=>{
btn.classList.toggle('active',btn.dataset.panel===name); btn.classList.toggle('active',btn.dataset.panel===name);

View File

@@ -525,9 +525,14 @@
transition:background .15s; transition:background .15s;
} }
.resize-handle:hover,.resize-handle.dragging{background:rgba(124,185,255,.35);} .resize-handle:hover,.resize-handle.dragging{background:rgba(124,185,255,.35);}
.sidebar{position:relative;} /* Desktop-only: position:relative for sidebar/rightpanel resize handles.
Must be scoped to min-width:641px so it doesn't override the mobile
position:fixed slide-in overlay set in the max-width:640px @media block above. */
@media(min-width:641px){
.sidebar{position:relative;}
.rightpanel{position:relative;}
}
.sidebar .resize-handle{right:-2px;} .sidebar .resize-handle{right:-2px;}
.rightpanel{position:relative;}
.rightpanel .resize-handle{left:-2px;} .rightpanel .resize-handle{left:-2px;}
/* Prevent text selection during drag */ /* Prevent text selection during drag */
body.resizing{user-select:none;cursor:col-resize;} body.resizing{user-select:none;cursor:col-resize;}