docs: refresh current project snapshot

This commit is contained in:
Frank Song
2026-05-13 10:57:17 +08:00
parent 86740c425e
commit be32b90cea
7 changed files with 137 additions and 127 deletions
+3
View File
@@ -21,6 +21,9 @@
# Default workspace directory shown on first launch
# HERMES_WEBUI_DEFAULT_WORKSPACE=~/workspace
# Optional model override. Leave unset to use the active Hermes provider default.
# HERMES_WEBUI_DEFAULT_MODEL=
# Base directory for all Hermes state (affects all paths above if set)
# HERMES_HOME=~/.hermes
+87 -90
View File
@@ -7,10 +7,10 @@
>
> Keep this document updated as architecture changes are made.
> Current shipped build: `v0.50.245` (April 30, 2026).
> Automated coverage: 3309 tests via `pytest tests/ --collect-only -q`. CI runs on Python 3.11, 3.12, and 3.13 against every PR.
> Current shipped build: `v0.51.52` (May 12, 2026).
> Automated coverage: 5271 tests via `pytest tests/ --collect-only -q`. CI runs on Python 3.11, 3.12, and 3.13 against every PR.
>
> Notable architecture state as of v0.50.245: workspace panel closed/open state is preloaded via a `documentElement` dataset marker before `style.css` paints to avoid first-load flash; transcript disclosure cards animate via transitionable `max-height`/`opacity` states; thinking cards share rounded bordered card chrome with tool cards (gold palette); incremental streaming-markdown via vendored `streaming-markdown@0.2.15` (no CDN); HTTP byte-range streaming for large media; SSE-driven session sidebar with `pending_user_message` + `active_stream_id` lifecycle tracking; configurable model badges (`primary` / `fallback N`) computed in `_build_configured_model_badges()` and provider-aware in the dropdown picker.
> Notable architecture state as of v0.51.52: the bootstrap and first-run onboarding flow own setup discovery; the default WebUI state directory is `~/.hermes/webui`; `ctl.sh` provides a daemon wrapper for homelab installs; chat streaming is still WebUI-owned SSE with stream-ownership guards, cancellation, async manual compression, and turn-journal audit plumbing; provider/model discovery is profile-aware with live-model cache invalidation and custom-provider scoping.
---
@@ -43,42 +43,42 @@ actions. The topbar remains focused on conversation context and the workspace/fi
## 2. File Inventory
<repo>/
server.py Thin routing shell + HTTP Handler + auth middleware. ~81 lines.
server.py Thin routing shell + HTTP Handler + auth middleware. ~435 lines.
Delegates all route handling to api/routes.py.
bootstrap.py One-shot launcher: optional agent install, deps, health wait, browser open.
start.sh Thin wrapper around bootstrap.py for shell-based startup.
Dockerfile python:3.12-slim container image (~23 lines)
docker-compose.yml Compose config with named volume and optional auth (~22 lines)
Dockerfile python:3.12-slim container image (~89 lines)
docker-compose.yml Compose config with named volume and optional auth (~57 lines)
.dockerignore Excludes .git, tests/, .env* from Docker builds
api/
__init__.py Package marker
auth.py Optional password authentication, signed cookies (~149 lines)
config.py Discovery, globals, model detection, reloadable config (~701 lines)
helpers.py HTTP helpers: j(), bad(), require(), safe_resolve(), security headers (~71 lines)
models.py Session model + CRUD, per-session profile tracking (~137 lines)
profiles.py Profile state management, hermes_cli wrapper (~246 lines)
onboarding.py First-run onboarding status, real provider config writes, and readiness detection.
routes.py All GET + POST route handlers (~1180 lines)
auth.py Optional password authentication, signed cookies (~366 lines)
config.py Discovery, globals, model detection, reloadable config (~4136 lines)
helpers.py HTTP helpers: j(), bad(), require(), safe_resolve(), security headers (~302 lines)
models.py Session model + CRUD, per-session profile tracking (~1927 lines)
profiles.py Profile state management, hermes_cli wrapper (~1056 lines)
onboarding.py First-run onboarding status, real provider config writes, OAuth linking, and readiness detection (~1002 lines)
routes.py All GET + POST route handlers (~9630 lines)
startup.py Startup helpers: auto_install_agent_deps() (~50 lines)
streaming.py SSE engine, run_agent, cancel, HERMES_HOME save/restore (~236 lines)
upload.py Multipart parser, file upload handler (~78 lines)
workspace.py File ops: list_dir, read_file_content, workspace helpers (~77 lines)
streaming.py SSE engine, run_agent, cancel, HERMES_HOME save/restore (~4404 lines)
upload.py Multipart parser, file upload handler (~284 lines)
workspace.py File ops: list_dir, read_file_content, workspace helpers (~810 lines)
static/
index.html HTML template (~364 lines)
style.css All CSS incl. mobile responsive (~670 lines)
ui.js DOM helpers, renderMd, tool cards, model dropdown, file tree (~977 lines)
workspace.js File preview, file ops, loadDir, clearPreview (~185 lines)
sessions.js Session CRUD, list rendering, search, SVG icons, dropdown actions (~533 lines)
messages.js send(), SSE event handlers, approval, transcript (~297 lines)
panels.js Cron, skills, memory, workspace, profiles, todo, settings (~974 lines)
commands.js Slash command registry, parser, autocomplete dropdown (~156 lines)
index.html HTML template (~1323 lines)
style.css All CSS incl. mobile responsive (~3767 lines)
ui.js DOM helpers, renderMd, tool cards, model dropdown, file tree (~7197 lines)
workspace.js File preview, file ops, loadDir, clearPreview (~369 lines)
sessions.js Session CRUD, list rendering, search, SVG icons, dropdown actions (~3433 lines)
messages.js send(), SSE event handlers, approval, transcript (~2301 lines)
panels.js Cron, skills, memory, workspace, profiles, todo, settings (~6480 lines)
commands.js Slash command registry, parser, autocomplete dropdown (~1302 lines)
onboarding.js First-run wizard overlay, provider setup flow, and settings/workspace orchestration.
boot.js Event wiring, mobile sidebar/workspace nav, voice input, boot IIFE (~338 lines)
boot.js Event wiring, mobile sidebar/workspace nav, voice input, boot IIFE (~1607 lines)
tests/
conftest.py Isolated test server (port 8788, separate HERMES_HOME) (~240 lines)
test_sprint{1-20b}.py Feature tests per sprint (21 files, 415 test functions)
test_regressions.py Permanent regression gate (23 tests)
AGENTS.md Instruction file for agents working in this directory.
conftest.py Isolated test server/state fixtures (~630 lines)
483 test files 5271 tests collected via pytest
test_regressions.py Permanent regression gate (~976 lines)
CONTRIBUTING.md Contributor workflow and PR expectations.
ROADMAP.md Feature and product roadmap document.
SPRINTS.md Forward sprint plan with CLI + Claude parity targets.
ARCHITECTURE.md THIS FILE.
@@ -90,7 +90,7 @@ actions. The topbar remains focused on conversation context and the workspace/fi
State directory (runtime data, separate from source):
~/.hermes/webui-mvp/
~/.hermes/webui/
sessions/ One JSON file per session: {session_id}.json
workspaces.json Registered workspaces list
last_workspace.txt Last-used workspace path
@@ -99,7 +99,8 @@ State directory (runtime data, separate from source):
Log file:
/tmp/webui-mvp.log stdout/stderr from the background server process
~/.hermes/webui/bootstrap-8787.log start.sh/bootstrap background server log
~/.hermes/webui.log ctl.sh daemon log
---
@@ -118,15 +119,16 @@ Environment variables controlling behavior:
HERMES_WEBUI_DEFAULT_WORKSPACE Default workspace path for new sessions
HERMES_WEBUI_STATE_DIR Where sessions/ folder lives
HERMES_CONFIG_PATH Path to ~/.hermes/config.yaml
HERMES_WEBUI_DEFAULT_MODEL Default LLM model string
HERMES_WEBUI_DEFAULT_MODEL Optional model override; unset means provider default
HERMES_WEBUI_PASSWORD Optional: enable password auth (off by default)
HERMES_WEBUI_SKIP_ONBOARDING Optional: bypass the first-run onboarding wizard
HERMES_HOME Base directory for Hermes state (~/.hermes by default)
Test isolation environment variables (set by conftest.py):
HERMES_WEBUI_PORT=8788 Isolated test port
HERMES_WEBUI_STATE_DIR=~/.hermes/webui-mvp-test Isolated test state
HERMES_WEBUI_DEFAULT_WORKSPACE=.../test-workspace Isolated test workspace
HERMES_WEBUI_TEST_PORT=... Optional pinned test port
HERMES_WEBUI_TEST_STATE_DIR=~/.hermes/webui-test-* Optional pinned test state
HERMES_WEBUI_DEFAULT_WORKSPACE=.../test-workspace Isolated test workspace
Tests NEVER talk to the production server (port 8787).
The test state dir is wiped before each test session and deleted after.
@@ -363,16 +365,18 @@ read_file_content(workspace, rel):
### 5.1 Structure
The frontend is served from static/ as separate files: one HTML template, one CSS file,
and six JavaScript modules (~2,786 lines total). External dependencies: Prism.js (syntax
highlighting) and Mermaid.js (diagrams) from CDN, both loaded async/deferred with SRI hashes.
and multiple JavaScript modules. External dependencies include Prism.js (syntax
highlighting), Mermaid.js (diagrams), xterm.js, and KaTeX assets loaded with the
current static template's integrity/CSP assumptions.
Six JS modules loaded in order at end of <body>:
1. ui.js (~846 lines) DOM helpers, renderMd, tool card rendering, global state
2. workspace.js (~169 lines) File tree, preview, file operations
3. sessions.js (~532 lines) Session CRUD, list rendering, search, SVG icons, dropdown actions, project picker
4. messages.js (~293 lines) send(), SSE event handlers, approval, transcript
5. panels.js (~771 lines) Cron, skills, memory, workspace, todo, switchPanel
6. boot.js (~175 lines) Event wiring + boot IIFE
Core JS modules loaded by the app include:
1. ui.js (~7197 lines) DOM helpers, renderMd, tool card rendering, global state
2. workspace.js (~369 lines) File tree, preview, file operations
3. sessions.js (~3433 lines) Session CRUD, list rendering, search, SVG icons, dropdown actions, project picker
4. messages.js (~2301 lines) send(), SSE event handlers, approval, transcript
5. panels.js (~6480 lines) Cron, skills, memory, workspace, profiles, todo, settings
6. commands.js (~1302 lines) Slash command registry, parser, autocomplete dropdown
7. boot.js (~1607 lines) Event wiring + boot IIFE
sessions.js defines an `ICONS` constant at module level with hardcoded SVG strings for all
session action buttons (pin, unpin, folder, archive, unarchive, duplicate, trash). All icons
@@ -680,27 +684,28 @@ Split server.py into a proper package. Completed across Sprints 4-10.
Current structure:
<repo>/
server.py Entry point + HTTP Handler dispatch (~76 lines)
server.py Entry point + HTTP Handler dispatch (~435 lines)
api/
__init__.py
routes.py All GET + POST route handlers (~1016 lines)
config.py Configuration, constants, global state, model discovery (~640 lines)
helpers.py HTTP helpers: j(), bad(), require(), safe_resolve() (~57 lines)
models.py Session model + CRUD (~132 lines)
workspace.py File ops, workspace management (~77 lines)
upload.py Multipart parser, file upload handler (~77 lines)
streaming.py SSE engine, run_agent, cancel support (~222 lines)
routes.py All GET + POST route handlers (~9630 lines)
config.py Configuration, constants, global state, model discovery (~4136 lines)
helpers.py HTTP helpers: j(), bad(), require(), safe_resolve() (~302 lines)
models.py Session model + CRUD (~1927 lines)
workspace.py File ops, workspace management (~810 lines)
upload.py Multipart parser, file upload handler (~284 lines)
streaming.py SSE engine, run_agent, cancel support (~4404 lines)
static/
index.html HTML document (served from disk)
style.css All CSS (~560 lines)
ui.js, workspace.js, sessions.js, messages.js, panels.js, boot.js
style.css All CSS (~3767 lines)
ui.js, workspace.js, sessions.js, messages.js, panels.js, commands.js, boot.js
tests/
conftest.py Isolated test server on port 8788
test_sprint1-16.py Feature tests per sprint (14 files)
conftest.py Isolated test server/state fixtures
483 test files 5271 tests collected
test_regressions.py Permanent regression gate
Route extraction to api/routes.py completed in Sprint 11. server.py is now a ~76-line
thin shell: Handler class with structured logging, dispatch to routes, and main().
Route extraction to api/routes.py completed in Sprint 11. server.py remains a
thin shell relative to the rest of the app: Handler class with headers,
structured logging, dispatch to routes, TLS wrapping, and main().
### Phase B: Thread-Safe Request Context (Priority: Critical, Effort: Medium)
@@ -779,7 +784,7 @@ Replacing with marked.js + DOMPurify is a future improvement (not blocking).
### Phase G: Observability -- MOSTLY COMPLETE
1. Structured JSON logging: COMPLETE (Sprint 1). Per-request JSON to /tmp/webui-mvp.log.
1. Structured JSON logging: COMPLETE (Sprint 1). Per-request JSON is printed to the active launcher log (`~/.hermes/webui/bootstrap-8787.log` for `start.sh`, `~/.hermes/webui.log` for `ctl.sh`).
2. Enhanced /health: COMPLETE (Sprint 7). Returns `active_streams`, `uptime_seconds`.
3. GET /api/debug/stats: NOT YET IMPLEMENTED. Low priority.
@@ -795,13 +800,13 @@ Optional password gate for non-SSH-tunnel deployments.
### Phase I: Test Infrastructure -- COMPLETE
289 tests across 14 test files + regression gate. Isolated test server on port 8788
with separate HERMES_HOME, wiped per run. Production data never touched.
5271 tests across 483 test files + regression gates. The pytest fixture derives
an isolated port and state directory from the repo path unless
`HERMES_WEBUI_TEST_PORT` / `HERMES_WEBUI_TEST_STATE_DIR` pin them explicitly.
Production data never touched.
Test files: `test_sprint1.py` through `test_sprint11.py`, `test_sprint16.py`, `test_regressions.py`.
Fixtures in `conftest.py`: auto-cleanup, cron isolation, workspace reset.
Remaining: no CI (GitHub Actions), no frontend tests (browser-based).
Fixtures in `conftest.py`: auto-cleanup, profile/config isolation, cron
isolation, workspace reset, and test-server lifecycle.
### Phase J: Performance (Priority: Low, Effort: High)
@@ -889,7 +894,8 @@ The api() helper:
curl -s http://127.0.0.1:8787/health | python3 -m json.tool
# Tail the server log live
tail -f /tmp/webui-mvp.log
tail -f ~/.hermes/webui/bootstrap-8787.log
tail -f ~/.hermes/webui.log # when launched through ctl.sh
# List all sessions (metadata only)
curl -s http://127.0.0.1:8787/api/sessions | python3 -m json.tool
@@ -899,15 +905,15 @@ The api() helper:
curl -s "http://127.0.0.1:8787/api/session?session_id=$SID" | python3 -m json.tool
# Kill and restart server cleanly
pkill -f "python.*webui-mvp/server.py"
<agent-dir>/webui-mvp/start.sh
pkill -f "python.*server.py"
<repo>/start.sh
# Check if server process is running
ps aux | grep "webui-mvp/server.py"
ps aux | grep "server.py"
# Inspect session files on disk
ls -lt ~/.hermes/webui-mvp/sessions/
cat ~/.hermes/webui-mvp/sessions/SESSION_ID.json | python3 -m json.tool
ls -lt ~/.hermes/webui/sessions/
cat ~/.hermes/webui/sessions/SESSION_ID.json | python3 -m json.tool
# Count messages in a session
python3 -c "import json; d=json.load(open('sessions/SID.json')); print(len(d['messages']))"
@@ -920,9 +926,9 @@ The api() helper:
curl -s http://127.0.0.1:8787/health # streams not exposed yet, add in Phase G
# Find all sessions with messages (not Untitled empty)
ls ~/.hermes/webui-mvp/sessions/ | xargs -I{} python3 -c "
ls ~/.hermes/webui/sessions/ | xargs -I{} python3 -c "
import json, sys
d = json.load(open('~/.hermes/webui-mvp/sessions/{}'))
d = json.load(open('~/.hermes/webui/sessions/{}'))
if d['messages']: print('{}', d['title'][:50])
" 2>/dev/null
@@ -1195,31 +1201,22 @@ will be working on this codebase. Read this before touching any file.
### Before Making Any Change
1. Read this document (ARCHITECTURE.md) fully. Especially sections 4, 5, and the ADRs.
2. Read the relevant section of server.py by searching for the SECTION header.
2. Inspect the relevant module under `api/` or `static/`; `server.py` is only the routing shell.
3. Check the Sprint Log (Section 15) to understand what was recently changed.
4. Run the test suite first to confirm baseline: cd <agent-dir> &&
venv/bin/python -m pytest webui-mvp/tests/test_sprint1.py -v
4. Run the relevant test slice first to confirm baseline, for example:
venv/bin/python -m pytest tests/test_regressions.py -q
5. Check server health: curl -s http://127.0.0.1:8787/health
### Making Changes
Always back up server.py before a non-trivial change:
cp server.py server.py.$(date +%Y%m%d_%H%M).bak
Use exact string matching when patching. The pitfalls are documented in the
hermes-webui-mvp skill. Key ones:
- Never use sed on this file from the shell. Use execute_code with Python string replace.
- Always assert the old string is found before replacing (prevents silent no-op patches).
- Unicode escape sequences in JS (\u2026) exist as literal backslash-u in the file.
Match the file's raw content, not interpreted Python strings.
- The HTML block is a Python raw string (r"""..."""). Standard triple-quote escaping
rules do not apply inside it, but Python escape sequences \n etc. work in JS strings
inside it as literal two-character sequences.
Keep edits scoped to the module that owns the behavior. Use exact string
matching when making mechanical patches and verify that the intended old string
was found before replacing it.
After any change:
venv/bin/python -m py_compile webui-mvp/server.py # syntax check
venv/bin/python -m py_compile server.py # syntax check
curl -s http://127.0.0.1:8787/health # server still alive
venv/bin/python -m pytest webui-mvp/tests/ -v # tests still pass
venv/bin/python -m pytest tests/ -v # tests still pass
### Critical Rules (do NOT regress these)
+4
View File
@@ -48,6 +48,10 @@
- **PR #2150** by @Jordan-SkyLF — "Refresh usage" button on the Provider quota card in Settings → Providers. Calls `/api/provider/quota?refresh=1&ts=<now>` with `cache: 'no-store'` to bypass browser, service worker, and reverse-proxy caches that may have stamped a previous quota response, then re-renders just the quota card from the fresh response and shows a `Last checked ...` timestamp. Disabled `Refreshing…` state during the in-flight request; success toast on completion or failure toast if the refresh fails. Note: the `refresh=1` query param is a no-op at the server today (`get_provider_quota()` has no in-process cache layer), so the win is strictly browser-side cache-bust + the `no-store` fetch option. A future maintainer follow-up may add server-side TTL caching of OAuth account-limit fetches, at which point the `refresh=1` param becomes load-bearing on both sides.
### Documentation
- Refreshed the README / TESTING / ARCHITECTURE current-state snapshots for `v0.51.52`: default model override semantics, test collection counts, file inventory line counts, default state/log paths, and the top-level docs index now match the current code. Also corrected the Docker init banner for `HERMES_WEBUI_STATE_DIR`.
## [v0.51.51] — 2026-05-12 — Release AA (stage-344 — 16-PR contributor batch — i18n + insights bucketing/mobile + manual-compress async + workspace recovery + iOS PWA scroll + Cloudflare login health + fr locale)
### Added
+33 -28
View File
@@ -267,7 +267,7 @@ Full list of environment variables:
| `HERMES_WEBUI_PORT` | `8787` | Port |
| `HERMES_WEBUI_STATE_DIR` | `~/.hermes/webui` | Where sessions and state are stored |
| `HERMES_WEBUI_DEFAULT_WORKSPACE` | `~/workspace` | Default workspace |
| `HERMES_WEBUI_DEFAULT_MODEL` | `openai/gpt-5.4-mini` | Default model |
| `HERMES_WEBUI_DEFAULT_MODEL` | *(provider default)* | Optional model override; leave unset to use the active Hermes provider default |
| `HERMES_WEBUI_PASSWORD` | *(unset)* | Set to enable password authentication |
| `HERMES_WEBUI_EXTENSION_DIR` | *(unset)* | Optional local directory served at `/extensions/`; must point to an existing directory before extension injection is enabled |
| `HERMES_WEBUI_EXTENSION_SCRIPT_URLS` | *(unset)* | Optional comma-separated same-origin script URLs to inject; see [WebUI Extensions](docs/EXTENSIONS.md) |
@@ -367,9 +367,9 @@ Or using the agent venv explicitly:
/path/to/hermes-agent/venv/bin/python -m pytest tests/ -v
```
Tests run against an isolated server on port 8788 with a separate state directory.
Production data and real cron jobs are never touched. Current count: **3309 tests**
across 100+ test files.
Tests run against an isolated server with a separate state directory.
Production data and real cron jobs are never touched. Current snapshot:
**5271 tests collected** across **483 test files**.
---
@@ -491,33 +491,33 @@ across 100+ test files.
## Architecture
```
server.py HTTP routing shell + auth middleware (~154 lines)
server.py HTTP routing shell + auth middleware (~435 lines)
api/
auth.py Optional password authentication, signed cookies (~201 lines)
config.py Discovery, globals, model detection, reloadable config (~1110 lines)
helpers.py HTTP helpers, security headers (~175 lines)
models.py Session model + CRUD + CLI bridge (~377 lines)
onboarding.py First-run onboarding wizard, OAuth provider support (~507 lines)
profiles.py Profile state management, hermes_cli wrapper (~411 lines)
routes.py All GET + POST route handlers (~2250 lines)
state_sync.py /insights sync — message_count to state.db (~113 lines)
streaming.py SSE engine, run_agent, cancel support (~660 lines)
updates.py Self-update check and release notes (~257 lines)
upload.py Multipart parser, file upload handler (~82 lines)
workspace.py File ops, workspace helpers, git detection (~288 lines)
auth.py Optional password authentication, signed cookies (~366 lines)
config.py Discovery, globals, model detection, reloadable config (~4136 lines)
helpers.py HTTP helpers, security headers (~302 lines)
models.py Session model + CRUD + CLI bridge (~1927 lines)
onboarding.py First-run onboarding wizard, OAuth provider support (~1002 lines)
profiles.py Profile state management, hermes_cli wrapper (~1056 lines)
routes.py All GET + POST route handlers (~9630 lines)
state_sync.py /insights sync — message_count to state.db (~118 lines)
streaming.py SSE engine, run_agent, cancel support (~4404 lines)
updates.py Self-update check and release notes (~545 lines)
upload.py Multipart parser, file upload handler (~284 lines)
workspace.py File ops, workspace helpers, git detection (~810 lines)
static/
index.html HTML template (~600 lines)
style.css All CSS incl. mobile responsive, themes (~1050 lines)
ui.js DOM helpers, renderMd, tool cards, context indicator (~1740 lines)
workspace.js File preview, file ops, git badge (~286 lines)
sessions.js Session CRUD, collapsible groups, search, reload recovery (~800 lines)
messages.js send(), SSE handlers, live streaming, session recovery (~655 lines)
panels.js Cron, skills, memory, profiles, settings (~1438 lines)
commands.js Slash command autocomplete (~267 lines)
boot.js Mobile nav, voice input, boot IIFE (~524 lines)
index.html HTML template (~1323 lines)
style.css All CSS incl. mobile responsive, themes (~3767 lines)
ui.js DOM helpers, renderMd, tool cards, context indicator (~7197 lines)
workspace.js File preview, file ops, git badge (~369 lines)
sessions.js Session CRUD, collapsible groups, search, reload recovery (~3433 lines)
messages.js send(), SSE handlers, live streaming, session recovery (~2301 lines)
panels.js Cron, skills, memory, profiles, settings (~6480 lines)
commands.js Slash command autocomplete (~1302 lines)
boot.js Mobile nav, voice input, boot IIFE (~1607 lines)
tests/
conftest.py Isolated test server (port 8788)
61 test files 961 test functions
conftest.py Isolated test server/state fixtures
483 test files 5271 tests collected
Dockerfile python:3.12-slim container image
docker-compose.yml Compose with named volume and optional auth
.github/workflows/ CI: multi-arch Docker build + GitHub Release on tag
@@ -537,8 +537,13 @@ State lives outside the repo at `~/.hermes/webui/` by default
- `CHANGELOG.md` -- release notes per sprint
- `SPRINTS.md` -- forward sprint plan with CLI + Claude parity targets
- `THEMES.md` -- theme system documentation, custom theme guide
- `docs/docker.md` -- Docker compose setup, common failures, and bind-mount migration
- `docs/supervisor.md` -- launchd, systemd, supervisord, runit, and s6 process-supervisor setup
- `docs/onboarding.md` -- first-run wizard, provider setup, local model server Base URLs, and safe re-runs
- `docs/troubleshooting.md` -- diagnostic flows for common failures (e.g. "AIAgent not available")
- `docs/wsl-autostart.md` -- WSL2 auto-start at Windows login
- `docs/EXTENSIONS.md` -- administrator-controlled WebUI extension injection
- `docs/rfcs/README.md` -- RFC index for larger architecture and durability proposals
## Contributors
+6 -5
View File
@@ -8,7 +8,7 @@
> Prerequisites: SSH tunnel is active on port 8787. Open http://localhost:8787 in browser.
> Server health check: curl http://127.0.0.1:8787/health should return {"status":"ok"}.
>
> Automated coverage: 3648 tests collected via `pytest tests/ --collect-only -q`. Tests run on every PR via GitHub Actions on Python 3.11, 3.12, and 3.13. The suite covers the bootstrap/static wizard, real provider config persistence (`config.yaml` + `.env`), the `/api/onboarding/*` backend, the onboarding skip/existing-config guard, CSS regression coverage for thinking/tool card animation, streaming session persistence, mobile layout breakpoints, locale parity across 9 languages, and ~700 issue/PR-pinned regression tests.
> Automated coverage: 5271 tests collected via `pytest tests/ --collect-only -q`. Tests run on every PR via GitHub Actions on Python 3.11, 3.12, and 3.13. The suite covers the bootstrap/static wizard, real provider config persistence (`config.yaml` + `.env`), the `/api/onboarding/*` backend, the onboarding skip/existing-config guard, CSS regression coverage for thinking/tool card animation, streaming session persistence, mobile layout breakpoints, locale parity across 11 languages, and hundreds of issue/PR-pinned regression tests.
> Run: `pytest tests/ -v --timeout=60`
>
> Local regression focus: verify that a previously closed workspace panel stays visually closed from first paint through boot completion on desktop refresh; there should be no brief open-then-close flash.
@@ -533,7 +533,8 @@ FAIL: Sidebar causes layout overflow or blocks chat.
### T11.3: Structured Log Output
SETUP: SSH access to the server.
STEPS:
1. In a terminal: tail -f /tmp/webui-mvp.log
1. In a terminal: tail -f ~/.hermes/webui/bootstrap-8787.log
(or tail -f ~/.hermes/webui.log when launched through `ctl.sh`)
2. In browser: perform any action (load page, send message, click file)
EXPECT:
- Log entries appear in terminal as JSON: {"ts":"...","method":"GET","path":"/health","status":200,"ms":0.1}
@@ -577,7 +578,7 @@ FAIL: Browser freezes, crash, or security issue.
## Automated Test Coverage Reference
These behaviors are verified by pytest (run: venv/bin/python -m pytest webui-mvp/tests/ -v):
These behaviors are verified by pytest (run: venv/bin/python -m pytest tests/ -v):
Sprint 1 tests (test_sprint1.py):
- Server health, session CRUD (create/load/update/delete/sort)
@@ -1835,8 +1836,8 @@ Bridged CLI sessions:
---
*Last updated: v0.51.31, May 9, 2026*
*Total automated tests collected: 4977*
*Last updated: v0.51.52, May 13, 2026*
*Total automated tests collected: 5271*
*Regression gate: tests/test_regressions.py*
*Run: pytest tests/ -v --timeout=60*
*Source: <repo>/*
+1 -1
View File
@@ -277,7 +277,7 @@ rm -f $it || error_exit "Failed to delete test file in /app"
echo ""; echo "== Checking required environment variables for hermes-webui"
echo ""; echo "-- HERMES_WEBUI_VERSION: Where to store sessions, workspaces, and other state (default: ~/.hermes/webui-mvp)"
echo ""; echo "-- HERMES_WEBUI_STATE_DIR: Where to store sessions, workspaces, and other state (default: ~/.hermes/webui)"
if [ -z "${HERMES_WEBUI_STATE_DIR+x}" ]; then error_exit "HERMES_WEBUI_STATE_DIR not set"; fi;
echo "-- HERMES_WEBUI_STATE_DIR: $HERMES_WEBUI_STATE_DIR"
if [ ! -d "$HERMES_WEBUI_STATE_DIR" ]; then mkdir -p $HERMES_WEBUI_STATE_DIR || error_exit "Failed to create state directory at $HERMES_WEBUI_STATE_DIR"; fi
+3 -3
View File
@@ -2,8 +2,8 @@
Shared pytest fixtures for webui-mvp tests.
TEST ISOLATION:
Tests run against a SEPARATE server instance on port 8788 with a
completely separate state directory. Production data is never touched.
Tests run against a SEPARATE server instance on an auto-derived test port
with a completely separate state directory. Production data is never touched.
The test state dir is wiped before each full test run and again on teardown.
PATH DISCOVERY:
@@ -32,7 +32,7 @@ HERMES_HOME = pathlib.Path(os.getenv('HERMES_HOME', str(HOME / '.hermes')))
# ── Test server config ────────────────────────────────────────────────────
# Port and state dir auto-derive from the repo path when no env var is set,
# giving every worktree its own isolated port (8800-8899) and state directory.
# giving every worktree its own isolated port (20000-29999) and state directory.
# Override with HERMES_WEBUI_TEST_PORT / HERMES_WEBUI_TEST_STATE_DIR to pin.
def _auto_test_port(repo_root) -> int: