@bitseek/hermes-webui 0.1.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/README.md +213 -0
  2. package/bin/hermes-webui.mjs +588 -0
  3. package/package.json +25 -0
  4. package/scripts/sync-vendor.mjs +74 -0
  5. package/templates/launchd/com.bitseek.hermes-webui.plist +21 -0
  6. package/templates/systemd/hermes-webui.service +13 -0
  7. package/templates/windows/hermes-webui-task.ps1 +3 -0
  8. package/vendor/agent-frontend-shell/.bitseek-source.json +6 -0
  9. package/vendor/agent-frontend-shell/.dockerignore +7 -0
  10. package/vendor/agent-frontend-shell/.env.docker.example +89 -0
  11. package/vendor/agent-frontend-shell/.env.example +34 -0
  12. package/vendor/agent-frontend-shell/.github/FUNDING.yml +3 -0
  13. package/vendor/agent-frontend-shell/.github/workflows/browser-smoke.yml +42 -0
  14. package/vendor/agent-frontend-shell/.github/workflows/docker-smoke.yml +233 -0
  15. package/vendor/agent-frontend-shell/.github/workflows/native-windows-startup.yml +132 -0
  16. package/vendor/agent-frontend-shell/.github/workflows/release.yml +57 -0
  17. package/vendor/agent-frontend-shell/.github/workflows/tests.yml +88 -0
  18. package/vendor/agent-frontend-shell/.vscode/launch.json +59 -0
  19. package/vendor/agent-frontend-shell/.vscode/settings.json +13 -0
  20. package/vendor/agent-frontend-shell/AGENTS.md +80 -0
  21. package/vendor/agent-frontend-shell/ARCHITECTURE.md +1658 -0
  22. package/vendor/agent-frontend-shell/BUGS.md +52 -0
  23. package/vendor/agent-frontend-shell/CHANGELOG.md +7295 -0
  24. package/vendor/agent-frontend-shell/CONTRIBUTING.md +205 -0
  25. package/vendor/agent-frontend-shell/CONTRIBUTORS.md +107 -0
  26. package/vendor/agent-frontend-shell/DESIGN.md +173 -0
  27. package/vendor/agent-frontend-shell/Dockerfile +91 -0
  28. package/vendor/agent-frontend-shell/LICENSE +21 -0
  29. package/vendor/agent-frontend-shell/README-CUSTOM.md +76 -0
  30. package/vendor/agent-frontend-shell/README.md +705 -0
  31. package/vendor/agent-frontend-shell/ROADMAP.md +351 -0
  32. package/vendor/agent-frontend-shell/SPRINTS.md +147 -0
  33. package/vendor/agent-frontend-shell/TESTING.md +1932 -0
  34. package/vendor/agent-frontend-shell/THEMES.md +170 -0
  35. package/vendor/agent-frontend-shell/api/__init__.py +1 -0
  36. package/vendor/agent-frontend-shell/api/agent_health.py +392 -0
  37. package/vendor/agent-frontend-shell/api/agent_sessions.py +782 -0
  38. package/vendor/agent-frontend-shell/api/auth.py +592 -0
  39. package/vendor/agent-frontend-shell/api/background.py +87 -0
  40. package/vendor/agent-frontend-shell/api/clarify.py +238 -0
  41. package/vendor/agent-frontend-shell/api/commands.py +124 -0
  42. package/vendor/agent-frontend-shell/api/compression_anchor.py +134 -0
  43. package/vendor/agent-frontend-shell/api/config.py +5178 -0
  44. package/vendor/agent-frontend-shell/api/dashboard_probe.py +255 -0
  45. package/vendor/agent-frontend-shell/api/extensions.py +253 -0
  46. package/vendor/agent-frontend-shell/api/gateway_chat.py +435 -0
  47. package/vendor/agent-frontend-shell/api/gateway_watcher.py +230 -0
  48. package/vendor/agent-frontend-shell/api/goals.py +608 -0
  49. package/vendor/agent-frontend-shell/api/helpers.py +474 -0
  50. package/vendor/agent-frontend-shell/api/kanban_bridge.py +1255 -0
  51. package/vendor/agent-frontend-shell/api/metering.py +194 -0
  52. package/vendor/agent-frontend-shell/api/models.py +4210 -0
  53. package/vendor/agent-frontend-shell/api/oauth.py +770 -0
  54. package/vendor/agent-frontend-shell/api/onboarding.py +1046 -0
  55. package/vendor/agent-frontend-shell/api/passkeys.py +365 -0
  56. package/vendor/agent-frontend-shell/api/profiles.py +1499 -0
  57. package/vendor/agent-frontend-shell/api/providers.py +2175 -0
  58. package/vendor/agent-frontend-shell/api/request_diagnostics.py +160 -0
  59. package/vendor/agent-frontend-shell/api/rollback.py +320 -0
  60. package/vendor/agent-frontend-shell/api/routes.py +13990 -0
  61. package/vendor/agent-frontend-shell/api/run_journal.py +284 -0
  62. package/vendor/agent-frontend-shell/api/runner_client.py +156 -0
  63. package/vendor/agent-frontend-shell/api/runtime_adapter.py +431 -0
  64. package/vendor/agent-frontend-shell/api/session_discoverability.py +640 -0
  65. package/vendor/agent-frontend-shell/api/session_events.py +45 -0
  66. package/vendor/agent-frontend-shell/api/session_lifecycle.py +208 -0
  67. package/vendor/agent-frontend-shell/api/session_ops.py +207 -0
  68. package/vendor/agent-frontend-shell/api/session_recovery.py +655 -0
  69. package/vendor/agent-frontend-shell/api/skill_usage.py +32 -0
  70. package/vendor/agent-frontend-shell/api/startup.py +128 -0
  71. package/vendor/agent-frontend-shell/api/state_sync.py +187 -0
  72. package/vendor/agent-frontend-shell/api/streaming.py +7048 -0
  73. package/vendor/agent-frontend-shell/api/system_health.py +167 -0
  74. package/vendor/agent-frontend-shell/api/terminal.py +410 -0
  75. package/vendor/agent-frontend-shell/api/turn_journal.py +214 -0
  76. package/vendor/agent-frontend-shell/api/updates.py +1261 -0
  77. package/vendor/agent-frontend-shell/api/upload.py +322 -0
  78. package/vendor/agent-frontend-shell/api/usage.py +26 -0
  79. package/vendor/agent-frontend-shell/api/workspace.py +867 -0
  80. package/vendor/agent-frontend-shell/api/workspace_git.py +1261 -0
  81. package/vendor/agent-frontend-shell/api/worktrees.py +357 -0
  82. package/vendor/agent-frontend-shell/bootstrap.py +492 -0
  83. package/vendor/agent-frontend-shell/ctl.sh +427 -0
  84. package/vendor/agent-frontend-shell/docker-compose.custom.yml +26 -0
  85. package/vendor/agent-frontend-shell/docker-compose.three-container.yml +168 -0
  86. package/vendor/agent-frontend-shell/docker-compose.two-container.yml +147 -0
  87. package/vendor/agent-frontend-shell/docker-compose.yml +57 -0
  88. package/vendor/agent-frontend-shell/docker_init.bash +459 -0
  89. package/vendor/agent-frontend-shell/docs/CONTRACTS.md +207 -0
  90. package/vendor/agent-frontend-shell/docs/EXTENSIONS.md +212 -0
  91. package/vendor/agent-frontend-shell/docs/ISSUES.md +23 -0
  92. package/vendor/agent-frontend-shell/docs/UIUX-GUIDE.md +196 -0
  93. package/vendor/agent-frontend-shell/docs/advanced-chat-setup.md +83 -0
  94. package/vendor/agent-frontend-shell/docs/docker.md +337 -0
  95. package/vendor/agent-frontend-shell/docs/onboarding-agent-checklist.md +207 -0
  96. package/vendor/agent-frontend-shell/docs/onboarding.md +202 -0
  97. package/vendor/agent-frontend-shell/docs/remote-access.md +75 -0
  98. package/vendor/agent-frontend-shell/docs/rfcs/README.md +53 -0
  99. package/vendor/agent-frontend-shell/docs/rfcs/agent-source-boundary.md +70 -0
  100. package/vendor/agent-frontend-shell/docs/rfcs/canonical-session-resolution.md +124 -0
  101. package/vendor/agent-frontend-shell/docs/rfcs/hermes-run-adapter-contract.md +1079 -0
  102. package/vendor/agent-frontend-shell/docs/rfcs/turn-journal.md +195 -0
  103. package/vendor/agent-frontend-shell/docs/rfcs/webui-run-state-consistency-contract.md +157 -0
  104. package/vendor/agent-frontend-shell/docs/supervisor.md +280 -0
  105. package/vendor/agent-frontend-shell/docs/troubleshooting.md +132 -0
  106. package/vendor/agent-frontend-shell/docs/ui-ux/index.html +863 -0
  107. package/vendor/agent-frontend-shell/docs/ui-ux/two-stage-proposal.html +768 -0
  108. package/vendor/agent-frontend-shell/docs/why-hermes.md +489 -0
  109. package/vendor/agent-frontend-shell/docs/workspace-git.md +92 -0
  110. package/vendor/agent-frontend-shell/docs/wsl-autostart.md +126 -0
  111. package/vendor/agent-frontend-shell/eslint.runtime-guard.config.mjs +35 -0
  112. package/vendor/agent-frontend-shell/extensions/bitseek-design-system.md +330 -0
  113. package/vendor/agent-frontend-shell/extensions/branding/assets/apple-touch-icon.png +0 -0
  114. package/vendor/agent-frontend-shell/extensions/branding/assets/empty-logo.svg +739 -0
  115. package/vendor/agent-frontend-shell/extensions/branding/assets/favicon-192.png +0 -0
  116. package/vendor/agent-frontend-shell/extensions/branding/assets/favicon-32.png +0 -0
  117. package/vendor/agent-frontend-shell/extensions/branding/assets/favicon-512.png +0 -0
  118. package/vendor/agent-frontend-shell/extensions/branding/assets/favicon-512.svg +745 -0
  119. package/vendor/agent-frontend-shell/extensions/branding/assets/favicon.ico +0 -0
  120. package/vendor/agent-frontend-shell/extensions/branding/assets/favicon.svg +745 -0
  121. package/vendor/agent-frontend-shell/extensions/branding/assets/titlebar-icon-v2.svg +751 -0
  122. package/vendor/agent-frontend-shell/extensions/branding/assets/titlebar-icon-v3.svg +739 -0
  123. package/vendor/agent-frontend-shell/extensions/branding/assets/titlebar-icon.svg +745 -0
  124. package/vendor/agent-frontend-shell/extensions/branding/branding.js +112 -0
  125. package/vendor/agent-frontend-shell/extensions/branding/config.json +14 -0
  126. package/vendor/agent-frontend-shell/extensions/branding/manifest.json +53 -0
  127. package/vendor/agent-frontend-shell/extensions/index.js +67 -0
  128. package/vendor/agent-frontend-shell/extensions/loader/hermes-loader.js +77 -0
  129. package/vendor/agent-frontend-shell/extensions/manifest.json +16 -0
  130. package/vendor/agent-frontend-shell/extensions/pages/ai-teammates/page.css +333 -0
  131. package/vendor/agent-frontend-shell/extensions/pages/ai-teammates/page.js +487 -0
  132. package/vendor/agent-frontend-shell/extensions/pages/manifest.json +6 -0
  133. package/vendor/agent-frontend-shell/extensions/pages/registry.css +56 -0
  134. package/vendor/agent-frontend-shell/extensions/pages/registry.js +302 -0
  135. package/vendor/agent-frontend-shell/extensions/themes/bitseek/index.css +93 -0
  136. package/vendor/agent-frontend-shell/extensions/themes/bitseek/index.js +98 -0
  137. package/vendor/agent-frontend-shell/install.sh +63 -0
  138. package/vendor/agent-frontend-shell/mcp_server.py +567 -0
  139. package/vendor/agent-frontend-shell/package.json +12 -0
  140. package/vendor/agent-frontend-shell/pyproject.toml +56 -0
  141. package/vendor/agent-frontend-shell/pytest.ini +3 -0
  142. package/vendor/agent-frontend-shell/requirements.txt +5 -0
  143. package/vendor/agent-frontend-shell/server.py +624 -0
  144. package/vendor/agent-frontend-shell/start.ps1 +210 -0
  145. package/vendor/agent-frontend-shell/start.sh +65 -0
  146. package/vendor/agent-frontend-shell/static/apple-touch-icon.png +0 -0
  147. package/vendor/agent-frontend-shell/static/boot.js +1990 -0
  148. package/vendor/agent-frontend-shell/static/commands.js +1402 -0
  149. package/vendor/agent-frontend-shell/static/favicon-192.png +0 -0
  150. package/vendor/agent-frontend-shell/static/favicon-32.png +0 -0
  151. package/vendor/agent-frontend-shell/static/favicon-512.png +0 -0
  152. package/vendor/agent-frontend-shell/static/favicon-512.svg +18 -0
  153. package/vendor/agent-frontend-shell/static/favicon.ico +0 -0
  154. package/vendor/agent-frontend-shell/static/favicon.svg +20 -0
  155. package/vendor/agent-frontend-shell/static/i18n.js +15389 -0
  156. package/vendor/agent-frontend-shell/static/icons.js +92 -0
  157. package/vendor/agent-frontend-shell/static/index.html +1506 -0
  158. package/vendor/agent-frontend-shell/static/login.js +177 -0
  159. package/vendor/agent-frontend-shell/static/manifest.json +53 -0
  160. package/vendor/agent-frontend-shell/static/messages.js +3521 -0
  161. package/vendor/agent-frontend-shell/static/onboarding.js +800 -0
  162. package/vendor/agent-frontend-shell/static/panels.js +7995 -0
  163. package/vendor/agent-frontend-shell/static/pwa-startup.js +83 -0
  164. package/vendor/agent-frontend-shell/static/sessions.js +5165 -0
  165. package/vendor/agent-frontend-shell/static/style.css +4774 -0
  166. package/vendor/agent-frontend-shell/static/sw.js +173 -0
  167. package/vendor/agent-frontend-shell/static/terminal.js +632 -0
  168. package/vendor/agent-frontend-shell/static/ui.js +8997 -0
  169. package/vendor/agent-frontend-shell/static/vendor/js-yaml/4.1.0/js-yaml.min.js +2 -0
  170. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_AMS-Regular.ttf +0 -0
  171. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_AMS-Regular.woff +0 -0
  172. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_AMS-Regular.woff2 +0 -0
  173. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
  174. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
  175. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
  176. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
  177. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
  178. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
  179. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
  180. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Bold.woff +0 -0
  181. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
  182. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
  183. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Regular.woff +0 -0
  184. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
  185. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Bold.ttf +0 -0
  186. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Bold.woff +0 -0
  187. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Bold.woff2 +0 -0
  188. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
  189. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-BoldItalic.woff +0 -0
  190. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
  191. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Italic.ttf +0 -0
  192. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Italic.woff +0 -0
  193. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Italic.woff2 +0 -0
  194. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Regular.ttf +0 -0
  195. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Regular.woff +0 -0
  196. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Main-Regular.woff2 +0 -0
  197. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
  198. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-BoldItalic.woff +0 -0
  199. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
  200. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-Italic.ttf +0 -0
  201. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-Italic.woff +0 -0
  202. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Math-Italic.woff2 +0 -0
  203. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
  204. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Bold.woff +0 -0
  205. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
  206. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
  207. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Italic.woff +0 -0
  208. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
  209. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
  210. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Regular.woff +0 -0
  211. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
  212. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Script-Regular.ttf +0 -0
  213. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Script-Regular.woff +0 -0
  214. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Script-Regular.woff2 +0 -0
  215. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size1-Regular.ttf +0 -0
  216. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size1-Regular.woff +0 -0
  217. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size1-Regular.woff2 +0 -0
  218. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size2-Regular.ttf +0 -0
  219. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size2-Regular.woff +0 -0
  220. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size2-Regular.woff2 +0 -0
  221. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size3-Regular.ttf +0 -0
  222. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size3-Regular.woff +0 -0
  223. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size3-Regular.woff2 +0 -0
  224. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size4-Regular.ttf +0 -0
  225. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size4-Regular.woff +0 -0
  226. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Size4-Regular.woff2 +0 -0
  227. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
  228. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Typewriter-Regular.woff +0 -0
  229. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
  230. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/katex.min.css +1 -0
  231. package/vendor/agent-frontend-shell/static/vendor/katex/0.16.22/katex.min.js +1 -0
  232. package/vendor/agent-frontend-shell/static/vendor/smd.min.js +29 -0
  233. package/vendor/agent-frontend-shell/static/workspace.js +680 -0
@@ -0,0 +1,1932 @@
1
+ # Hermes Web UI: Browser Testing Plan
2
+
3
+ > This document is for manual browser testing by you or by a Claude browser agent.
4
+ > It covers user-facing features of the UI across current releases.
5
+ > Each section is written as a step-by-step test procedure with expected outcomes.
6
+ > A browser agent (e.g. Claude with Chrome access) can execute this plan directly.
7
+ >
8
+ > Prerequisites: SSH tunnel is active on port 8787. Open http://localhost:8787 in browser.
9
+ > Server health check: curl http://127.0.0.1:8787/health should return {"status":"ok"}.
10
+ >
11
+ > Automated coverage: ~7,150 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 (3 parallel shards each), alongside a ruff lint gate, a headless browser smoke test, and a Docker smoke test. 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.
12
+ > Run: `pytest tests/ -v --timeout=60`
13
+ >
14
+ > 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.
15
+
16
+ ---
17
+
18
+ ## Static JS runtime lint (brick-class regression guard)
19
+
20
+ Some JS bugs throw a `TypeError`/`ReferenceError` only when a specific function
21
+ actually runs in the browser — `node --check` (lazy syntax check), source-presence
22
+ tests, and even executing the file all miss them. Issue **#3162** was exactly this:
23
+ a `const` binding reassigned inside `_ensureMessagesLoaded` bricked "load conversation
24
+ messages" on every mobile message (v0.51.161–166).
25
+
26
+ The guard is a curated, zero-false-positive ESLint config (`eslint.runtime-guard.config.mjs`)
27
+ that runs ONLY runtime-error rules (`no-const-assign`, `no-import-assign`) over
28
+ `static/**/*.js`. It is NOT a style linter and has no formatting rules.
29
+
30
+ ```bash
31
+ # one-time dev setup (ESLint is a dev-only tool; the app stays pure Python + vanilla JS):
32
+ npm install --no-save --before=<a-date-≥48h-ago> eslint # package-age guard
33
+ # run the guard:
34
+ npm run lint:runtime
35
+ # or directly:
36
+ npx eslint --no-config-lookup -c eslint.runtime-guard.config.mjs "static/**/*.js"
37
+ ```
38
+
39
+ ## Python lint gate (ruff) — forward-looking, new-code-only
40
+
41
+ The Python twin of the ESLint runtime guard. A curated `ruff` ruleset
42
+ (`[tool.ruff]` in `pyproject.toml`) catches latent-bug shapes — unused imports
43
+ (F401), undefined/unused names (F841/F821), redefinitions (F811), mutable default
44
+ args (B006), raise-without-from (B904), loop-variable capture in closures (B023) —
45
+ **plus** real syntax/runtime errors (E9). It is **not** a style/formatting linter:
46
+ the pure-style families (line-length, whitespace) are intentionally OFF so the gate
47
+ never demands a reformat of existing code.
48
+
49
+ The existing tree carries a cosmetic backlog (mostly unused-import F401) that is
50
+ deliberately **not** reformatted. So the gate is enforced **only on the lines a
51
+ change adds or modifies** (`scripts/ruff_lint.py --diff`), which keeps new code
52
+ clean without touching the backlog. Cleaning the backlog is a separate,
53
+ maintainer-run, safe-fixes-only decision (tracked in #3273).
54
+
55
+ ```bash
56
+ # one-time dev setup (ruff is a dev-only tool):
57
+ pip install ruff # or: uv tool install ruff / uvx ruff ...
58
+ # the gate (only flags violations on lines you added/changed vs origin/master):
59
+ python3 scripts/ruff_lint.py --diff origin/master
60
+ # whole-tree backlog report (informational — never blocks):
61
+ python3 scripts/ruff_lint.py --all
62
+ ```
63
+
64
+ `tests/test_ruff_forward_lint.py` holds the **whole tree** free of E9 (real
65
+ syntax/runtime) findings and verifies the curated config shape; it runs in-suite
66
+ when ruff is present and **skips gracefully** when it isn't — so environments
67
+ without ruff aren't blocked, while CI (which installs ruff) enforces it. The
68
+ diff-scoped gate runs as the `lint` job in `.github/workflows/tests.yml` and is
69
+ also part of the maintainer pre-release pre-gate.
70
+
71
+ ## Automated browser smoke (runtime brick-class gate)
72
+
73
+ The ESLint guard above catches `const`-reassign / import-assign statically. The
74
+ **browser smoke** catches the same brick class *dynamically* — plus anything else
75
+ that throws only when a real browser executes the page (e.g. a `function X(){}` /
76
+ `window.X = {}` name collision like #2715/#2771, which ESLint can't see).
77
+
78
+ `tests/browser_smoke.py` boots the real `server.py` (agent-free, on an ephemeral
79
+ port, with an isolated temp state dir) and loads the key pages in headless
80
+ Chromium, failing if **any** console error or uncaught JS exception fires on load.
81
+ It runs in CI (`.github/workflows/browser-smoke.yml`) on every PR and push to
82
+ master, and locally:
83
+
84
+ ```bash
85
+ pip install playwright && python -m playwright install chromium
86
+ python tests/browser_smoke.py
87
+ ```
88
+
89
+ It is intentionally **credential-free**: it strips every `*_API_KEY` from the
90
+ environment before launching the server, needs no secrets, and does not drive a
91
+ real model (it verifies the app *loads and initializes* cleanly — the brick class
92
+ that breaks the page for everyone). A full chat golden-path E2E (send → stream →
93
+ render → switch → reload) lives in the maintainer's private QA harness, which has
94
+ the agent + a mock LLM provider available.
95
+
96
+
97
+ `tests/test_static_js_runtime_lint.py` runs this automatically when eslint is present
98
+ and **skips gracefully** (clear message) when it isn't — so environments without the
99
+ node toolchain aren't blocked, while the release gate (which installs eslint) enforces it.
100
+
101
+ To widen the guard, fix the pre-existing intentional hits first (as of 2026-05-30:
102
+ `no-dupe-keys` ×92 i18n locale-fallback, `no-func-assign` ×2 panel override,
103
+ `no-redeclare` ×1) then promote the rule into the config.
104
+
105
+ ---
106
+
107
+ ## How to Use This Document
108
+
109
+ Each test has:
110
+ - SETUP: what to do before the test
111
+ - STEPS: numbered actions to perform
112
+ - EXPECT: what you should see (pass criteria)
113
+ - FAIL: what a failure looks like
114
+
115
+ Work through sections in order. Each section builds on the previous.
116
+
117
+ ---
118
+
119
+ ## Section 1: Initial Load and Empty State
120
+
121
+ ### T1.1: Fresh Load Shows Empty State
122
+ SETUP: Clear localStorage (DevTools > Application > Local Storage > delete hermes-webui-session) or open in incognito.
123
+ STEPS:
124
+ 1. Navigate to http://localhost:8787
125
+ EXPECT:
126
+ - Dark background
127
+ - Sidebar begins directly with the icon tab row; there is no dedicated branding header
128
+ - Center area shows "What can I help with?" heading with suggestion buttons
129
+ - Session list in sidebar is empty or shows existing sessions
130
+ - Sidebar footer shows a single "Hermes WebUI" control-center button
131
+ - No session is highlighted active
132
+ - Send button is present but there is no input focus by default
133
+ FAIL: Page shows error, blank white screen, or auto-creates a new session without user action.
134
+
135
+ ### T1.2: Suggestion Buttons Work
136
+ SETUP: T1.1 complete, no active session.
137
+ STEPS:
138
+ 1. Click "What files are in this workspace?" suggestion button
139
+ EXPECT:
140
+ - A new session is created automatically (since none existed)
141
+ - The text "What files are in this workspace?" appears as the user message
142
+ - Thinking dots appear below the user message
143
+ - After a few seconds, Hermes responds
144
+ FAIL: Button does nothing, error appears, or page crashes.
145
+
146
+ ---
147
+
148
+ ## Section 2: Session Management
149
+
150
+ ### T2.1: Create New Session via + Button
151
+ SETUP: Any state.
152
+ STEPS:
153
+ 1. Click the "+ New conversation" button in the sidebar
154
+ EXPECT:
155
+ - A new session named "Untitled" appears highlighted in the session list
156
+ - The center area shows the empty state ("What can I help with?")
157
+ - The + button is the ONLY way to create a session (no auto-create on load)
158
+ FAIL: Multiple sessions created, error thrown, or empty state not shown.
159
+
160
+ ### T2.2: Send a Message and See Response
161
+ SETUP: Active session exists.
162
+ STEPS:
163
+ 1. Click in the message input at the bottom
164
+ 2. Type: "Say hello in exactly three words"
165
+ 3. Press Enter (not Shift+Enter)
166
+ EXPECT:
167
+ - User message appears immediately in chat
168
+ - Thinking dots (three animated dots) appear below
169
+ - Send button becomes disabled (grayed out)
170
+ - A red stop button appears in the composer footer while the turn is running
171
+ - Within 10-30 seconds, Hermes responds with a three-word greeting
172
+ - Thinking dots disappear
173
+ - Send button re-enables and the stop button disappears
174
+ - Session title in sidebar updates to reflect the first message
175
+ FAIL: Message never appears, thinking dots never go away, Send button stays disabled forever.
176
+
177
+ ### T2.3: Shift+Enter Creates Newline (Does Not Send)
178
+ SETUP: Active session, input focused.
179
+ STEPS:
180
+ 1. Click the message input
181
+ 2. Type "Line one"
182
+ 3. Press Shift+Enter
183
+ 4. Type "Line two"
184
+ EXPECT:
185
+ - Two lines of text appear in the input box
186
+ - No message is sent (no user message appears in chat)
187
+ FAIL: Message sent on Shift+Enter.
188
+
189
+ ### T2.4: Reload Restores Session
190
+ SETUP: A session exists with at least one exchange (user + assistant).
191
+ STEPS:
192
+ 1. Note the session title and last message content
193
+ 2. Reload the page (Cmd+R or F5)
194
+ EXPECT:
195
+ - The same session loads automatically (no empty state)
196
+ - All messages from before the reload are visible
197
+ - Session title in topbar and sidebar matches what it was before
198
+ FAIL: Session lost on reload, empty state shown, or messages missing.
199
+
200
+ ### T2.5: Delete Active Session
201
+ SETUP: At least two sessions exist. One is active.
202
+ STEPS:
203
+ 1. Hover over the active session in the sidebar (trash icon appears)
204
+ 2. Click the trash icon on the active session
205
+ EXPECT:
206
+ - A "Conversation deleted" toast appears at the bottom for ~3 seconds
207
+ - The deleted session disappears from the sidebar list
208
+ - The next most recent session automatically loads (or empty state if none remain)
209
+ - NO new session is auto-created
210
+ FAIL: Session not removed, new session auto-created, error shown, or wrong session loaded.
211
+
212
+ ### T2.6: Delete Non-Active Session
213
+ SETUP: At least two sessions exist. Session B is not active.
214
+ STEPS:
215
+ 1. Hover over a session that is NOT currently active
216
+ 2. Click its trash icon
217
+ EXPECT:
218
+ - Toast "Conversation deleted" appears
219
+ - That session disappears from list
220
+ - Currently active session remains active and unchanged
221
+ FAIL: Active session changes, multiple sessions deleted, or error.
222
+
223
+ ### T2.7: Delete Last Session Shows Empty State
224
+ SETUP: Exactly one session exists.
225
+ STEPS:
226
+ 1. Delete that session via the trash icon
227
+ EXPECT:
228
+ - Session list is empty
229
+ - Center area shows "What can I help with?" empty state
230
+ - No session is auto-created
231
+ FAIL: New session created, error thrown, or UI breaks.
232
+
233
+ ---
234
+
235
+ ## Section 3: Model Selection
236
+
237
+ ### T3.1: Model Dropdown Shows All Options
238
+ SETUP: Any active session.
239
+ STEPS:
240
+ 1. Look at the composer footer: to the right of the attach/mic controls there is a model dropdown
241
+ 2. Click the dropdown to expand it
242
+ EXPECT:
243
+ - Provider groups visible: OpenAI, Anthropic, Other
244
+ - OpenAI group: GPT-5.4 Mini, GPT-4o, o3, o4-mini
245
+ - Anthropic group: Claude Sonnet 4.6, Claude Sonnet 4.5, Claude Haiku 3.5
246
+ - Other group: Gemini 2.5 Pro, DeepSeek V3, Llama 4 Scout
247
+ FAIL: Only 2 options visible, no groups, or missing models.
248
+
249
+ ### T3.2: Model Dropdown Reflects Active Conversation
250
+ SETUP: Active session.
251
+ STEPS:
252
+ 1. Change model dropdown to "Claude Sonnet 4.6"
253
+ EXPECT:
254
+ - The composer footer dropdown stays on "Claude Sonnet 4.6"
255
+ - Sending the next message uses that session model rather than an older one from another conversation
256
+ STEPS (continued):
257
+ 2. Change model to "Gemini 2.5 Pro"
258
+ EXPECT:
259
+ - The dropdown updates to "Gemini 2.5 Pro"
260
+ - Switching away and back to the conversation restores the same model in the footer selector
261
+ FAIL: Dropdown shows the wrong active model after a session switch, or sending uses a stale model.
262
+
263
+ ### T3.3: Context Badge Shares Footer Space Cleanly
264
+ SETUP: Active session with at least one completed response.
265
+ STEPS:
266
+ 1. Look at the right side of the composer footer
267
+ EXPECT:
268
+ - A compact circular context badge appears next to the send button when usage data is available
269
+ - The number in the center shows the used percentage
270
+ - Hovering or focusing the badge shows a tooltip with percent used, token count, auto-compress threshold, and estimated cost when available
271
+ - The model dropdown remains usable without overlapping the send button or pushing controls out of view
272
+ FAIL: Linear meter still shown, tooltip missing/incomplete, controls overlap, or footer wraps in a broken way.
273
+
274
+ ---
275
+
276
+ ## Section 4: File Upload
277
+
278
+ ### T4.1: Click-to-Attach Opens File Picker
279
+ SETUP: Active session.
280
+ STEPS:
281
+ 1. Click the paperclip icon in the composer footer
282
+ EXPECT:
283
+ - OS file picker dialog opens
284
+ - Accepted types filter visible (images, text, PDF, common code files)
285
+ FAIL: Nothing happens, error thrown.
286
+
287
+ ### T4.2: Attach a Text File and Send
288
+ SETUP: Have a small .txt or .py file ready to upload.
289
+ STEPS:
290
+ 1. Click the paperclip, select the file
291
+ 2. File chip appears in the composer tray above the input
292
+ 3. Type "What is in this file?" in the input
293
+ 4. Press Enter
294
+ EXPECT:
295
+ - Upload progress bar briefly appears
296
+ - User message shows the message text plus a file badge with the filename
297
+ - Hermes responds describing or reading the file content
298
+ FAIL: Upload fails, file badge never appears, Hermes does not mention the file.
299
+
300
+ ### T4.3: Drag and Drop a File
301
+ SETUP: Active session, a file ready on your desktop.
302
+ STEPS:
303
+ 1. Drag a file from Finder/Explorer over the composer area
304
+ EXPECT:
305
+ - Blue dashed border and "Drop files to upload to workspace" overlay appear
306
+ STEPS (continued):
307
+ 2. Drop the file
308
+ EXPECT:
309
+ - File chip appears in the tray
310
+ - Overlay disappears
311
+ FAIL: No drag visual feedback, file not accepted, error on drop.
312
+
313
+ ### T4.4: Paste Screenshot from Clipboard
314
+ SETUP: Take a screenshot (Cmd+Shift+4 on Mac, saves to clipboard).
315
+ STEPS:
316
+ 1. Click in the message input
317
+ 2. Press Cmd+V (paste)
318
+ EXPECT:
319
+ - An image file chip appears in the tray: "screenshot-{timestamp}.png"
320
+ - Status bar briefly shows "Image pasted: screenshot-..."
321
+ FAIL: Nothing pasted, error, or raw binary data appears in input.
322
+
323
+ ### T4.5: Remove a File from Tray
324
+ SETUP: At least one file in the attach tray.
325
+ STEPS:
326
+ 1. Click the X button on a file chip in the tray
327
+ EXPECT:
328
+ - That file is removed from the tray
329
+ - If it was the only file, tray collapses
330
+ FAIL: File not removed, error.
331
+
332
+ ### T4.6: Inline Audio Attachment Editor with Variable Speed
333
+ SETUP: Active session, an audio file ready locally (`.mp3`, `.wav`, `.m4a`, `.ogg`, or `.flac`).
334
+ STEPS:
335
+ 1. Attach the audio file with the paperclip or drag/drop
336
+ 2. Confirm the tray shows an audio media chip, then send the message
337
+ 3. In the sent user message, press Play on the inline audio player
338
+ 4. Click 0.5×, 1.25×, 1.5×, and 2× speed buttons
339
+ EXPECT:
340
+ - The audio renders inline in the chat instead of only as a download/file badge
341
+ - Native audio controls are visible and usable
342
+ - The clicked speed button becomes active and playback speed changes immediately
343
+ - Download/open behavior for non-media files is unchanged
344
+ FAIL: Audio only downloads, no speed buttons appear, or speed buttons do not affect playback.
345
+
346
+ ### T4.7: Inline Video Attachment Editor with Variable Speed
347
+ SETUP: Active session, a video file ready locally (`.mp4`, `.mov`, `.webm`, or `.m4v`).
348
+ STEPS:
349
+ 1. Attach and send the video file
350
+ 2. In the sent user message, play the inline video
351
+ 3. Switch among 0.75×, 1×, 1.5×, and 2× speed controls
352
+ EXPECT:
353
+ - The video renders inline, contained within the message width
354
+ - Native video controls are visible and usable
355
+ - Speed selection updates the video `playbackRate` without reloading the media
356
+ FAIL: Video only shows a generic badge, overflows the chat column, or speed controls fail.
357
+
358
+ ---
359
+
360
+ ## Section 5: Workspace File Browser
361
+
362
+ ### T5.0: Panel Is Closed By Default
363
+ SETUP: Active session with workspace set.
364
+ EXPECT:
365
+ - Right workspace panel is hidden on initial load
366
+ - Center chat column uses the freed width
367
+ - "Files" toggle is visible in the topbar
368
+ FAIL: Right panel starts open without any browsing or preview action.
369
+
370
+ ### T5.1: File Tree Loads When Files Panel Is Opened
371
+ SETUP: Active session with workspace set.
372
+ STEPS:
373
+ 1. Click the "Files" toggle in the topbar
374
+ EXPECT:
375
+ - Right panel shows "WORKSPACE" header
376
+ - File tree lists files and directories in the workspace
377
+ - Directories have folder icons
378
+ - Files have type-appropriate icons (camera for images, notepad for markdown, etc.)
379
+ FAIL: Right panel is blank, error, or all files show generic icon regardless of type.
380
+
381
+ ### T5.2: Navigate Into a Directory
382
+ SETUP: Workspace has at least one subdirectory.
383
+ STEPS:
384
+ 1. Click a directory name in the file tree
385
+ EXPECT:
386
+ - File tree updates to show contents of that directory
387
+ - A ".." or breadcrumb is NOT shown (current behavior: flat navigation)
388
+ FAIL: Click does nothing, error, or entire page breaks.
389
+
390
+ ### T5.3: Preview a Code File
391
+ SETUP: Workspace has a .py or .js or .txt file.
392
+ STEPS:
393
+ 1. Click the file name in the tree
394
+ EXPECT:
395
+ - Right panel switches from file tree to preview area
396
+ - File path shown at top with file extension badge (gray)
397
+ - File contents shown as monospace text (raw code)
398
+ - File tree is hidden, preview is visible
399
+ FAIL: Nothing happens, binary gibberish shown, crash.
400
+
401
+ ### T5.4: Close Preview Returns to File Tree
402
+ SETUP: T5.3 complete, preview is showing.
403
+ STEPS:
404
+ 1. Click the X button in the panel header
405
+ EXPECT:
406
+ - Preview closes
407
+ - If the panel auto-opened for that preview, the entire right panel closes again
408
+ - If the panel was manually opened for browsing first, the file tree is visible again
409
+ - Preview area is hidden
410
+ - Reopening the same file shows fresh content (no stale cached text)
411
+ FAIL: X button does nothing, panel stays stuck open, or the file tree does not reappear after manual browse mode.
412
+
413
+ ### T5.5: Preview an Image File (Sprint 2)
414
+ SETUP: Upload a PNG, JPG, or any image file to the workspace, OR the workspace already contains one.
415
+ STEPS:
416
+ 1. Click an image file (e.g. .png or .jpg) in the file tree
417
+ EXPECT:
418
+ - Preview area shows the actual image rendered inline (NOT a blob of bytes or placeholder)
419
+ - Image is centered, fits within the panel width
420
+ - Path bar shows "image" badge in blue
421
+ - Image maintains aspect ratio
422
+ FAIL: Raw binary text displayed, broken image icon, error message, or nothing happens.
423
+
424
+ ### T5.5b: Preview Audio/Video Files Inline
425
+ SETUP: Workspace contains at least one audio file (`.mp3`, `.wav`, `.m4a`) and one video file (`.mp4`, `.mov`, `.webm`).
426
+ STEPS:
427
+ 1. Click the audio file in the workspace file tree
428
+ 2. Play it and select 1.5× or 2× speed
429
+ 3. Close preview, then click the video file
430
+ 4. Play it and select 0.75× or 1.25× speed
431
+ EXPECT:
432
+ - Audio/video open in the workspace preview panel instead of downloading immediately
433
+ - Path badge shows `audio` or `video`
434
+ - Native media controls and the variable-speed buttons are visible
435
+ - Video scales to the preview panel without overflowing
436
+ FAIL: Browser downloads the media immediately, raw binary appears, or speed controls are missing/broken.
437
+
438
+ ### T5.5c: Preview PDF Files Inline
439
+ SETUP: Workspace contains at least one `.pdf` file.
440
+ STEPS:
441
+ 1. Click the PDF file in the workspace file tree
442
+ 2. Use the browser/PDF viewer scroll and zoom controls if available
443
+ 3. Click "Open in browser" as a fallback
444
+ EXPECT:
445
+ - PDF opens in the workspace preview panel instead of downloading immediately
446
+ - Path badge shows `pdf`
447
+ - PDF iframe fills the preview area
448
+ - "Open in browser" opens the same raw file endpoint in a new tab
449
+ FAIL: Browser downloads the PDF immediately, raw binary appears, or the preview panel is blank without an open fallback.
450
+
451
+ ### T5.6: Preview a Markdown File (Sprint 2)
452
+ SETUP: Workspace has a .md file (or create one: upload a file named README.md with some markdown content).
453
+ STEPS:
454
+ 1. Click the .md file in the file tree
455
+ EXPECT:
456
+ - Preview shows formatted, rendered markdown (NOT raw text with asterisks)
457
+ - Headings render as large bold text
458
+ - **bold** renders as bold, *italic* as italic
459
+ - Bullet lists render as actual list items
460
+ - Code blocks render in monospace with dark background
461
+ - Path bar shows "md" badge in gold
462
+ FAIL: Raw markdown text with asterisks/hashes shown, or no preview at all.
463
+
464
+ ### T5.7: Markdown Preview Renders Tables (Sprint 2)
465
+ SETUP: Upload or create a .md file with a table like:
466
+ | Name | Value |
467
+ |------|-------|
468
+ | foo | bar |
469
+ STEPS:
470
+ 1. Click the file in the file tree
471
+ EXPECT:
472
+ - Table renders as an actual HTML table with borders
473
+ - Column headers (Name, Value) are bold/highlighted
474
+ - Data rows alternate subtle background
475
+ FAIL: Table displayed as raw pipe-separated text.
476
+
477
+ ### T5.8: Refresh Files Button
478
+ SETUP: Active session, workspace has files.
479
+ STEPS:
480
+ 1. Click the "Files" refresh button in the sidebar bottom actions
481
+ EXPECT:
482
+ - File tree reloads
483
+ - If a file was added externally, it now appears
484
+ FAIL: Error, spinner never stops, tree clears without reloading.
485
+
486
+ ---
487
+
488
+ ## Section 6: Workspace Path
489
+
490
+ ### T6.1: Change Workspace Path
491
+ SETUP: Active session.
492
+ STEPS:
493
+ 1. Click the workspace path display in the sidebar bottom (shows current path)
494
+ 2. A prompt dialog appears
495
+ 3. Enter a new valid path (e.g. /tmp)
496
+ 4. Click OK
497
+ EXPECT:
498
+ - Workspace chip in topbar updates to show the last segment of the new path
499
+ - File tree refreshes to show files at the new path
500
+ - Next message sent uses the new workspace
501
+ FAIL: Dialog does not appear, path not saved, error on invalid path.
502
+
503
+ ---
504
+
505
+ ## Section 7: Tool Approval
506
+
507
+ ### T7.1: Dangerous Command Shows Approval Card
508
+ SETUP: Active session with a test-workspace (NOT a production directory).
509
+ STEPS:
510
+ 1. Type: "Run the command: rm -rf /tmp/hermes_test_delete_me"
511
+ 2. Send the message
512
+ EXPECT:
513
+ - Thinking dots appear
514
+ - An orange/red approval card appears above the composer:
515
+ "Dangerous command - approval required"
516
+ - The card shows the command text
517
+ - The card shows the pattern description (e.g. "recursive delete [recursive_delete]")
518
+ - Four buttons: Allow once, Allow this session, Always allow, Deny
519
+ FAIL: No card appears, agent executes without asking, page crashes.
520
+
521
+ ### T7.2: Deny Approval Blocks the Command
522
+ SETUP: T7.1 complete, card is showing.
523
+ STEPS:
524
+ 1. Click "Deny"
525
+ EXPECT:
526
+ - Approval card disappears
527
+ - Agent responds with a message indicating the command was denied/blocked
528
+ - No file was deleted
529
+ FAIL: Command executes despite deny, card stays up, error.
530
+
531
+ ### T7.3: Allow Once Executes the Command
532
+ SETUP: Create a safe test: type "Run: touch /tmp/hermes_approval_test.txt"
533
+ STEPS:
534
+ 1. Send the message
535
+ 2. When approval card appears, click "Allow once"
536
+ EXPECT:
537
+ - Approval card disappears
538
+ - Agent continues and reports the command ran successfully
539
+ - Verify: open a terminal and run: ls /tmp/hermes_approval_test.txt
540
+ FAIL: Command blocked after Allow once, card stays, error.
541
+
542
+ ---
543
+
544
+ ## Section 8: Transcript Download
545
+
546
+ ### T8.1: Download Conversation as Markdown
547
+ SETUP: A session with at least 2 messages (1 user + 1 assistant).
548
+ STEPS:
549
+ 1. Click the "Hermes" button in the sidebar footer
550
+ 2. In the Control Center modal, click "Transcript"
551
+ EXPECT:
552
+ - Browser downloads a .md file named hermes-{session_id}.md
553
+ - Opening the file shows the conversation in markdown format:
554
+ ## user
555
+ (message text)
556
+ ## assistant
557
+ (response text)
558
+ FAIL: No download triggered, file is empty, file is corrupted JSON instead of markdown.
559
+
560
+ ---
561
+
562
+ ## Section 9: Reconnect Banner (Sprint 1 - B4/B5)
563
+
564
+ ### T9.1: Reconnect Banner After Mid-Stream Reload
565
+ NOTE: This test requires deliberate timing. Best done with a slow/long agent request.
566
+ SETUP: Active session.
567
+ STEPS:
568
+ 1. Send a message that will take a while (e.g. "Write me a 500-word short story")
569
+ 2. While thinking dots are showing (within the first 5 seconds), reload the page (Cmd+R)
570
+ EXPECT:
571
+ - Page reloads and restores the session
572
+ - A gold/amber banner appears near the top: "A response may have been in progress..."
573
+ - Two buttons on the banner: "Dismiss" and "Reload"
574
+ - Clicking "Reload" fetches fresh messages from server
575
+ - Clicking "Dismiss" removes the banner
576
+ FAIL: No banner shown, page crashes, banner appears on normal reloads with no in-flight request.
577
+
578
+ ---
579
+
580
+ ## Section 10: Multi-Session and Concurrent Behavior
581
+
582
+ ### T10.1: Switch Sessions While Response Is Loading
583
+ SETUP: Active session, agent running (thinking dots visible from a previous message).
584
+ STEPS:
585
+ 1. While thinking dots are showing, click a DIFFERENT session in the sidebar
586
+ EXPECT:
587
+ - The new session loads cleanly (its messages show)
588
+ - The Send button for the NEW session is NOT disabled (it's not busy)
589
+ - The original session's response is still being generated in the background
590
+ - Clicking back to the original session shows the thinking dots still running
591
+ - When the original request finishes, its messages update correctly
592
+ FAIL: New session shows busy state, switching breaks messages, response lands in wrong session.
593
+
594
+ ### T10.2: Multiple Sessions in List (Up to 30)
595
+ SETUP: Create enough sessions to have at least 5 in the sidebar.
596
+ EXPECT:
597
+ - Sessions listed most-recently-updated first
598
+ - Long titles truncate with "..." and do not overflow the sidebar width
599
+ - Hover shows the trash icon on any session
600
+ FAIL: Titles overflow sidebar, order is wrong, trash icon never appears.
601
+
602
+ ---
603
+
604
+ ## Section 11: Visual and Layout Checks
605
+
606
+ ### T11.1: Right Panel Hidden on Small Screens
607
+ STEPS:
608
+ 1. Resize browser window to below 900px width
609
+ EXPECT:
610
+ - Right panel (workspace) disappears
611
+ - Chat area expands to fill the full width
612
+ FAIL: Right panel overlaps chat or causes horizontal scroll.
613
+
614
+ ### T11.2: Sidebar Hidden on Very Small Screens
615
+ STEPS:
616
+ 1. Resize browser window to below 640px width
617
+ EXPECT:
618
+ - Left sidebar disappears
619
+ - Chat area takes full width
620
+ FAIL: Sidebar causes layout overflow or blocks chat.
621
+
622
+ ### T11.3: Structured Log Output
623
+ SETUP: SSH access to the server.
624
+ STEPS:
625
+ 1. In a terminal: tail -f ~/.hermes/webui/bootstrap-8787.log
626
+ (or tail -f ~/.hermes/webui.log when launched through `ctl.sh`)
627
+ 2. In browser: perform any action (load page, send message, click file)
628
+ EXPECT:
629
+ - Log entries appear in terminal as JSON: {"ts":"...","method":"GET","path":"/health","status":200,"ms":0.1}
630
+ - Every request produces one log line
631
+ - Status codes are correct (200 for success, 400 for bad requests)
632
+ FAIL: No log output, log shows Apache-style text instead of JSON, log file not created.
633
+
634
+ ---
635
+
636
+ ## Section 12: Error Handling
637
+
638
+ ### T12.1: Send Button Disabled When Busy
639
+ SETUP: Message is sending (thinking dots visible).
640
+ EXPECT:
641
+ - Send button is visually grayed out
642
+ - Stop button is visible in the composer footer
643
+ - Pressing Enter does NOT send another message
644
+ - Clicking Send button does nothing
645
+ FAIL: Multiple messages sent while one is in flight.
646
+
647
+ ### T12.2: Upload Failure Shows Status
648
+ SETUP: Active session.
649
+ STEPS:
650
+ 1. Try to attach a file larger than the configured upload limit (20MB by default; overridden by `HERMES_WEBUI_MAX_UPLOAD_MB` if set)
651
+ EXPECT:
652
+ - Status bar shows an error message about file size or the upload is rejected
653
+ - The chat is not broken (can still send messages)
654
+ FAIL: Uncaught error, page crashes, or no feedback given.
655
+
656
+ ### T12.3: File Preview for Binary Non-Image
657
+ SETUP: Workspace has a .zip or .bin file.
658
+ STEPS:
659
+ 1. Click the binary file in the file tree
660
+ EXPECT:
661
+ - Code preview shows some text (may be replacement characters for binary content)
662
+ - OR a "File too large" or "Could not open file" error in the status bar
663
+ - Page does NOT crash
664
+ FAIL: Browser freezes, crash, or security issue.
665
+
666
+ ---
667
+
668
+ ## Automated Test Coverage Reference
669
+
670
+ These behaviors are verified by pytest (run: venv/bin/python -m pytest tests/ -v):
671
+
672
+ Sprint 1 tests (test_sprint1.py):
673
+ - Server health, session CRUD (create/load/update/delete/sort)
674
+ - B11 footgun fix (/api/session 400 on missing ID)
675
+ - Multipart parser: text file, binary PNG
676
+ - HTTP upload: success, too large, no file field, bad session
677
+ - Approval API: pending, inject+deny, inject+session-approve
678
+ - Stream status endpoint
679
+ - File listing, path traversal block
680
+
681
+ Sprint 2 tests (test_sprint2.py):
682
+ - GET /api/file/raw: PNG, JPEG, SVG content-types
683
+ - GET /api/file/raw: path traversal blocked, missing file 404
684
+ - GET /api/file: markdown content returned as text
685
+ - GET /api/file: markdown with table content preserved
686
+ - GET /api/list: image and markdown files appear in directory listing
687
+
688
+ Manual-only tests (not covered by automation):
689
+ - All browser rendering tests (T5.5, T5.6, T5.7 -- visual verification)
690
+ - T7.1-T7.3 -- tool approval card (requires live agent execution)
691
+ - T9.1 -- reconnect banner timing
692
+ - T10.1 -- concurrent session switching
693
+ - T11.1, T11.2 -- responsive layout
694
+ - T11.3 -- log output verification
695
+ - All visual/CSS checks
696
+
697
+ ---
698
+
699
+ ## Claude Browser Agent Instructions
700
+
701
+ If you are a Claude agent with browser access, follow these instructions:
702
+
703
+ 1. Navigate to http://localhost:8787 (assumes SSH tunnel is active from your Mac).
704
+ 2. Execute tests in order. For each test:
705
+ a. Follow SETUP instructions.
706
+ b. Perform each STEP using browser tools (click, type, navigate).
707
+ c. Use browser_vision or browser_snapshot to verify EXPECT conditions.
708
+ d. Record PASS or FAIL with a brief note on what you observed.
709
+ 3. For tests requiring file uploads: use the browser's file picker; you may need to
710
+ create test files in /tmp first via terminal.
711
+ 4. For T7.x (approval tests): the agent running inside Hermes needs to detect a
712
+ dangerous command. Ask Hermes to "run: rm -rf /tmp/test_hermes_approval" and watch
713
+ for the card. The actual rm will not run in a safe test workspace.
714
+ 5. Skip T9.1 (reconnect banner) unless you can precisely time a page reload during an
715
+ active SSE stream.
716
+ 6. Report results as a checklist at the end.
717
+
718
+ ---
719
+
720
+ *Last updated: Sprint 2, March 30, 2026*
721
+ *Server version: v0.4 (server.py in webui-mvp/)*
722
+ *Run automated tests: python -m pytest tests/ -v*
723
+
724
+ ---
725
+
726
+ ## Section 13: Sidebar Panel Navigation (Sprint 3)
727
+
728
+ ### T13.1: Four Tabs Visible in Sidebar
729
+ EXPECT:
730
+ - Four tabs at top of sidebar: Chat, Tasks, Skills, Memory (with icons)
731
+ - Chat tab is active/highlighted by default on load
732
+ FAIL: Only one section visible, no tabs shown.
733
+
734
+ ### T13.2: Tab Switching Preserves Chat State
735
+ STEPS:
736
+ 1. Send a message, get a response (so chat has content)
737
+ 2. Click the "Tasks" tab
738
+ 3. Click the "Chat" tab
739
+ EXPECT:
740
+ - When on Tasks tab: session list disappears, cron list appears
741
+ - When back on Chat: full conversation is still there, session list returns
742
+ - Send button still works after switching back
743
+ FAIL: Messages lost, session list blank, Send button broken.
744
+
745
+ ---
746
+
747
+ ## Section 14: Tasks Panel (Cron Viewer) (Sprint 3)
748
+
749
+ ### T14.1: Tasks Tab Shows Cron Jobs
750
+ STEPS:
751
+ 1. Click the "Tasks" tab in the sidebar
752
+ EXPECT:
753
+ - A list of scheduled jobs appears
754
+ - Each job shows its name and a status badge (active/paused/error/off)
755
+ - At least one job visible ("Morning Crypto Update" or similar)
756
+ FAIL: Empty list, loading spinner forever, error message.
757
+
758
+ ### T14.2: Expand a Job Row
759
+ STEPS:
760
+ 1. Click a job name/row in the Tasks panel
761
+ EXPECT:
762
+ - Row expands to show: schedule string, prompt text (truncated), action buttons
763
+ - "Run now" button (blue), "Pause" or "Resume" button
764
+ - Last output section showing timestamp and content from the last run
765
+ FAIL: Click does nothing, no expansion, buttons missing.
766
+
767
+ ### T14.3: Pause and Resume a Job
768
+ STEPS:
769
+ 1. Expand an active job
770
+ 2. Click "Pause"
771
+ EXPECT:
772
+ - Toast: job paused
773
+ - Job list reloads, status badge changes to "paused"
774
+ - Button changes to "Resume"
775
+ STEPS (continued):
776
+ 3. Click "Resume"
777
+ EXPECT:
778
+ - Toast: job resumed
779
+ - Status badge returns to "active"
780
+ FAIL: Status doesn't change, error toast.
781
+
782
+ ### T14.4: Last Output Shows Real Content
783
+ SETUP: At least one job has run at least once.
784
+ STEPS:
785
+ 1. Expand a job that has run
786
+ EXPECT:
787
+ - Last output section shows a timestamp (e.g. "2026-03-30_16-00-37")
788
+ - Content preview of the last run output (first ~600 chars)
789
+ FAIL: "(no runs yet)" shown even though job has run, or content is garbled.
790
+
791
+ ---
792
+
793
+ ## Section 15: Skills Panel (Sprint 3)
794
+
795
+ ### T15.1: Skills Tab Shows Categorized List
796
+ STEPS:
797
+ 1. Click the "Skills" tab
798
+ EXPECT:
799
+ - A search box at the top
800
+ - Skills grouped by category with folder icons and count (e.g. "autonomous-ai-agents (4)")
801
+ - Individual skill items with name and short description excerpt
802
+ FAIL: Loading forever, empty list, uncategorized blob.
803
+
804
+ ### T15.2: Search Filters Skills
805
+ STEPS:
806
+ 1. Click the Skills tab
807
+ 2. Type "github" in the search box
808
+ EXPECT:
809
+ - List narrows to only skills matching "github" in name, description, or category
810
+ - Categories with no matches are hidden
811
+ STEPS (continued):
812
+ 3. Clear the search box
813
+ EXPECT:
814
+ - Full list returns
815
+ FAIL: Search does nothing, list disappears entirely.
816
+
817
+ ### T15.3: Click Skill Opens SKILL.md in Right Panel
818
+ STEPS:
819
+ 1. Click the Skills tab
820
+ 2. Click any skill name (e.g. "dogfood")
821
+ EXPECT:
822
+ - The right workspace panel switches to preview mode
823
+ - The skill's SKILL.md content renders as formatted markdown
824
+ - Path bar shows the skill name with gold "skill" badge
825
+ - Headings, bold, lists all render correctly (not raw asterisks)
826
+ FAIL: Nothing happens in right panel, raw markdown text shown, error.
827
+
828
+ ---
829
+
830
+ ## Section 16: Memory Panel (Sprint 3)
831
+
832
+ ### T16.1: Memory Tab Shows Personal Notes
833
+ STEPS:
834
+ 1. Click the "Memory" tab
835
+ EXPECT:
836
+ - Two sections: "My Notes" and "User Profile"
837
+ - Each section has a last-modified timestamp in the header
838
+ - Content renders as formatted markdown (not raw text)
839
+ FAIL: Blank, loading forever, error, raw text with § symbols.
840
+
841
+ ### T16.2: Memory Content is Accurate
842
+ STEPS:
843
+ 1. Open Memory tab
844
+ 2. Check a fact you know is in memory (e.g. your Pacific Time preference)
845
+ EXPECT:
846
+ - The fact appears in the correct section (User Profile or My Notes)
847
+ FAIL: Wrong content shown, different user's data, empty even though memory exists.
848
+
849
+ ---
850
+
851
+ ## Section 17: Bug Fix Verification (Sprint 3)
852
+
853
+ ### T17.1: B6 - New Session Inherits Workspace
854
+ SETUP: Active session with workspace changed to a non-default path (e.g. /tmp).
855
+ STEPS:
856
+ 1. Click + New conversation while workspace is set to /tmp
857
+ EXPECT:
858
+ - New session shows /tmp in the workspace chip and path display
859
+ - File tree shows files from /tmp
860
+ FAIL: New session resets to default test-workspace.
861
+
862
+ ### T17.2: B10 - Tool Events Replace Thinking Dots
863
+ SETUP: Send a message that will trigger tool use (e.g. "What files are in /tmp?").
864
+ STEPS:
865
+ 1. Watch the chat area carefully after sending
866
+ EXPECT:
867
+ - Thinking dots appear immediately
868
+ - When first tool fires: dots disappear, replaced by a compact "Running terminal..." row
869
+ - When first token arrives: tool row disappears, streaming text begins
870
+ - NOT: thinking dots AND "Running..." AND streaming text all visible at once
871
+ FAIL: Thinking dots stay while tool runs, or multiple rows stacked confusingly.
872
+
873
+ ### T17.3: B14 - Cmd/Ctrl+K Creates New Chat
874
+ STEPS:
875
+ 1. Press Cmd+K (Mac) or Ctrl+K (Windows/Linux) while on the Chat tab
876
+ EXPECT:
877
+ - A new "Untitled" session is created
878
+ - Empty state shown, input focused
879
+ - Does NOT work if a request is in-flight (Send is disabled)
880
+ FAIL: Nothing happens, browser intercepts shortcut for its own purpose, crash.
881
+
882
+ ---
883
+
884
+ ## Automated Test Coverage (Updated Sprint 3)
885
+
886
+ Sprint 3 tests (test_sprint3.py) - 21 tests:
887
+ - Cron API: list, required fields, output, pause/resume validation, run nonexistent
888
+ - Skills API: list, required fields, content for known skill, name required
889
+ - Memory API: both files returned, string types, mtime keys present
890
+ - Input validation: session/update, session/delete, chat/start require fields;
891
+ chat/start requires message; session/update unknown ID returns 404
892
+ - B6: new session with workspace param sets it correctly
893
+
894
+ Manual-only (not covered by automation):
895
+ - T13.x - T16.x: all visual panel rendering tests
896
+ - T17.1-T17.3: bug fix verification (B6/B10/B14)
897
+ - All previous manual tests from Sections 1-12
898
+
899
+ ---
900
+
901
+ *Last updated: Sprint 3, March 30, 2026*
902
+ *Total automated tests: 48/48*
903
+ *Run: python -m pytest tests/ -v*
904
+
905
+ ---
906
+
907
+ ## Section 18: Source Relocation Verification (Sprint 4)
908
+
909
+ ### T18.1: UI Loads and CSS Renders Correctly After Relocation
910
+ STEPS:
911
+ 1. Open http://localhost:8787
912
+ EXPECT:
913
+ - Dark background, correct colors, sidebar visible
914
+ - Open DevTools Network tab: verify style.css loads from /static/style.css (not inline)
915
+ - No CSS errors in console
916
+ FAIL: Plain unstyled HTML, white background, console errors about missing stylesheet.
917
+
918
+ ---
919
+
920
+ ## Section 19: Session Rename (Sprint 4)
921
+
922
+ ### T19.1: Double-Click to Rename a Session
923
+ SETUP: At least one session in the sidebar.
924
+ STEPS:
925
+ 1. Double-click on a session title in the sidebar
926
+ EXPECT:
927
+ - The title text is replaced by an editable input field, pre-selected
928
+ - Input has a blue border style
929
+ - Sidebar click does NOT navigate away (click is stopped)
930
+ FAIL: Nothing happens, navigation fires, input not focused.
931
+
932
+ ### T19.2: Enter Commits the Rename
933
+ STEPS (continued from T19.1):
934
+ 1. Clear the input and type "My Renamed Session"
935
+ 2. Press Enter
936
+ EXPECT:
937
+ - Input disappears, replaced by the new title text
938
+ - Sidebar shows "My Renamed Session"
939
+ - If this was the active session, topbar title also updates immediately
940
+ - Page reload: title persists
941
+ FAIL: Title reverts, topbar doesn't update, enter not handled.
942
+
943
+ ### T19.3: Escape Cancels the Rename
944
+ STEPS:
945
+ 1. Double-click a session to start editing
946
+ 2. Type some changes
947
+ 3. Press Escape
948
+ EXPECT:
949
+ - Input disappears, original title restored (no change)
950
+ FAIL: Title changed despite Escape, crash.
951
+
952
+ ### T19.4: Blur (Click Away) Commits the Rename
953
+ STEPS:
954
+ 1. Double-click a session, type a new name
955
+ 2. Click somewhere else on the page (not the input)
956
+ EXPECT:
957
+ - New title is committed (same as pressing Enter)
958
+ FAIL: Title reverts on blur.
959
+
960
+ ---
961
+
962
+ ## Section 20: Session Search (Sprint 4)
963
+
964
+ ### T20.1: Search Box Filters Sessions
965
+ SETUP: Multiple sessions with different titles (including at least one you renamed in T19).
966
+ STEPS:
967
+ 1. Verify the Chat tab is active in the sidebar
968
+ 2. Type part of a known session title in the "Filter conversations..." box
969
+ EXPECT:
970
+ - List updates live as you type, showing only matching sessions
971
+ - Non-matching sessions disappear
972
+ - No network request (filtering is instant, client-side)
973
+ FAIL: All sessions shown regardless, search causes page reload, error.
974
+
975
+ ### T20.2: Clear Search Restores Full List
976
+ STEPS (continued):
977
+ 1. Clear the search input
978
+ EXPECT:
979
+ - Full session list returns immediately
980
+ FAIL: List stays filtered after clearing.
981
+
982
+ ### T20.3: No Match Shows Empty List
983
+ STEPS:
984
+ 1. Type "zzznomatch9999" in the search box
985
+ EXPECT:
986
+ - Session list is empty (no items)
987
+ - No error message, just empty
988
+ FAIL: Error shown, crash, or unfiltered list still displayed.
989
+
990
+ ---
991
+
992
+ ## Section 21: Workspace File Operations (Sprint 4)
993
+
994
+ ### T21.1: Delete Button Appears on File Hover
995
+ SETUP: Workspace has at least one file.
996
+ STEPS:
997
+ 1. Hover over a file in the right panel file tree
998
+ EXPECT:
999
+ - A small trash icon appears on the right side of the file row
1000
+ - Hovering away hides it again
1001
+ FAIL: No icon ever appears, icon always visible (not hover-only).
1002
+
1003
+ ### T21.2: Delete a File with Confirmation
1004
+ STEPS:
1005
+ 1. Hover over a file and click its trash icon
1006
+ 2. An in-app confirmation modal appears: "Delete [filename]?"
1007
+ 3. Click OK
1008
+ EXPECT:
1009
+ - Toast: "Deleted [filename]"
1010
+ - File disappears from the tree
1011
+ - If the file was open in the preview panel, preview closes
1012
+ FAIL: File not deleted, no confirmation dialog, error.
1013
+
1014
+ ### T21.3: Cancel Delete Does Nothing
1015
+ STEPS:
1016
+ 1. Hover over a file and click its trash icon
1017
+ 2. Click Cancel on the confirmation modal
1018
+ EXPECT:
1019
+ - File remains in the tree
1020
+ - No toast, no error
1021
+ FAIL: File deleted despite cancel.
1022
+
1023
+ ### T21.4: Create a New File
1024
+ STEPS:
1025
+ 1. Click the + button in the workspace panel header
1026
+ 2. An in-app input modal appears: "New file name (e.g. notes.md):"
1027
+ 3. Type "test-sprint4.md" and click OK
1028
+ EXPECT:
1029
+ - Toast: "Created test-sprint4.md"
1030
+ - File appears in the tree
1031
+ - File opens immediately in the preview panel
1032
+ - File is empty (no content)
1033
+ FAIL: Nothing happens, error, file not created.
1034
+
1035
+ ### T21.5: Create File Validates Name
1036
+ STEPS:
1037
+ 1. Click + for a new file
1038
+ 2. Press Cancel or leave name empty
1039
+ EXPECT:
1040
+ - Nothing created, no error
1041
+ FAIL: Empty-named file created, crash.
1042
+
1043
+ ---
1044
+
1045
+ ## Automated Test Coverage (Updated Sprint 4)
1046
+
1047
+ Sprint 4 tests (test_sprint4.py) - 20 tests:
1048
+ - Relocation: server health, static CSS served, unknown static 404
1049
+ - Session rename: success, persistence, truncation, missing fields, unknown ID
1050
+ - Session search: matches found, empty query, no results
1051
+ - File create: success, missing fields, duplicate rejected
1052
+ - File delete: success, missing file 404, path traversal blocked
1053
+ - Validation: /api/list and /api/file require session_id and path
1054
+
1055
+ Total automated: 68/68 passing.
1056
+
1057
+ Manual-only for Sprint 4:
1058
+ - T18.1: visual CSS verification
1059
+ - T19.1-T19.4: inline rename UX
1060
+ - T20.1-T20.3: live search UX
1061
+ - T21.1-T21.5: file operations UX
1062
+
1063
+ ---
1064
+
1065
+
1066
+
1067
+ ---
1068
+
1069
+ ## Section 22: Workspace Management (Sprint 5)
1070
+
1071
+ ### T22.1: Spaces Tab Shows Configured Workspaces
1072
+ STEPS:
1073
+ 1. Click the "Spaces" tab in the sidebar (folder icon)
1074
+ EXPECT:
1075
+ - A list of workspaces with name and path
1076
+ - At least the default workspace (test-workspace) listed
1077
+ - An "Add workspace path" input at the bottom
1078
+ - Each workspace has "Use" and X (remove) buttons
1079
+ FAIL: Empty panel, loading forever, error.
1080
+
1081
+ ### T22.2: Add a Valid Workspace Path
1082
+ STEPS:
1083
+ 1. Type "/tmp" in the add input and click + Add
1084
+ EXPECT:
1085
+ - "/tmp" appears in the list with name "tmp"
1086
+ - Toast: "Workspace added"
1087
+ FAIL: Error shown, path not saved, no feedback.
1088
+
1089
+ ### T22.3: Add an Invalid Path is Rejected
1090
+ STEPS:
1091
+ 1. Type "/this/path/does/not/exist" in the add input and click + Add
1092
+ EXPECT:
1093
+ - Status bar shows an error (e.g. "Path does not exist")
1094
+ - Path is NOT added to the list
1095
+ FAIL: Invalid path added, no error.
1096
+
1097
+ ### T22.4: Remove a Workspace
1098
+ STEPS:
1099
+ 1. Click the X button next to any non-default workspace
1100
+ 2. Confirm the modal
1101
+ EXPECT:
1102
+ - Workspace disappears from the list
1103
+ - Toast: "Workspace removed"
1104
+ FAIL: Workspace stays, no confirmation, error.
1105
+
1106
+ ### T22.5: Topbar Workspace Chip is a Dropdown
1107
+ STEPS:
1108
+ 1. In any active chat session, look at the topbar right side
1109
+ 2. Click the workspace chip (shows folder icon + workspace name + arrow)
1110
+ EXPECT:
1111
+ - A dropdown appears listing all configured workspaces
1112
+ - Current workspace is highlighted
1113
+ - A "Manage workspaces" link at the bottom
1114
+ FAIL: Nothing happens, no dropdown, error.
1115
+
1116
+ ### T22.6: Quick-Switch Workspace via Topbar
1117
+ SETUP: At least two workspaces configured (add /tmp if needed via Spaces tab).
1118
+ STEPS:
1119
+ 1. Click the workspace chip to open the dropdown
1120
+ 2. Click a different workspace than the current one
1121
+ EXPECT:
1122
+ - Dropdown closes
1123
+ - Toast: "Switched to [name]"
1124
+ - Workspace chip in topbar updates to the new workspace name
1125
+ - File tree in right panel refreshes to show files in the new workspace
1126
+ - Current session is now using the new workspace
1127
+ FAIL: Workspace chip doesn't update, file tree unchanged, error.
1128
+
1129
+ ### T22.7: New Session Inherits Last Used Workspace
1130
+ SETUP: Switch to a non-default workspace (e.g. /tmp) via the topbar dropdown.
1131
+ STEPS:
1132
+ 1. Click + New conversation
1133
+ EXPECT:
1134
+ - New session's workspace chip shows the same workspace you last switched to (/tmp)
1135
+ - File tree shows files from /tmp
1136
+ FAIL: New session defaults back to test-workspace.
1137
+
1138
+ ---
1139
+
1140
+ ## Section 23: Copy Message to Clipboard (Sprint 5)
1141
+
1142
+ ### T23.1: Copy Icon Appears on Hover
1143
+ SETUP: A chat session with at least one message.
1144
+ STEPS:
1145
+ 1. Hover over any chat message (user or assistant)
1146
+ EXPECT:
1147
+ - A small clipboard icon appears in the message header row (right side of "You" or "Hermes")
1148
+ - Icon is not visible when not hovering
1149
+ FAIL: No icon ever appears, always visible.
1150
+
1151
+ ### T23.2: Copy Puts Text in Clipboard
1152
+ STEPS:
1153
+ 1. Hover over an assistant message
1154
+ 2. Click the clipboard icon
1155
+ EXPECT:
1156
+ - Icon briefly shows a check icon, then reverts to the copy icon
1157
+ - Paste (Cmd+V) elsewhere shows the full text of that message
1158
+ FAIL: No visual feedback, clipboard empty or wrong content.
1159
+
1160
+ ---
1161
+
1162
+ ## Section 24: Inline File Editor (Sprint 5)
1163
+
1164
+ ### T24.1: Code File Opens in Read-Only Mode
1165
+ STEPS:
1166
+ 1. Click any .py, .js, or .txt file in the workspace file tree
1167
+ EXPECT:
1168
+ - File content shows in read-only monospace view
1169
+ - An Edit button with a pencil icon is visible in the preview path bar
1170
+ - Content is NOT editable (clicking in it does nothing)
1171
+ FAIL: Content immediately editable, no Edit button.
1172
+
1173
+ ### T24.2: Edit Button Enters Edit Mode
1174
+ STEPS:
1175
+ 1. Click the Edit button on a code file preview
1176
+ EXPECT:
1177
+ - Read-only view replaced by an editable textarea
1178
+ - Content of the file is pre-populated in the textarea
1179
+ - Button changes to "Save" with a disk icon
1180
+ FAIL: Nothing changes, button doesn't change.
1181
+
1182
+ ### T24.3: Save Writes Changes to Disk
1183
+ STEPS:
1184
+ 1. In edit mode, change some text
1185
+ 2. Click the Save button
1186
+ EXPECT:
1187
+ - Read-only view returns, showing the updated content
1188
+ - Toast: "Saved"
1189
+ - Verify: refreshing the file tree and reopening the file shows the new content
1190
+ FAIL: Save does nothing, content reverts, error.
1191
+
1192
+ ### T24.4: Dirty Indicator Shows Unsaved Changes
1193
+ STEPS:
1194
+ 1. Enter edit mode on a file
1195
+ 2. Make any change (type a character)
1196
+ EXPECT:
1197
+ - Button shows "Save*" with the disk icon still visible (asterisk indicates unsaved changes)
1198
+ FAIL: No asterisk, button stays as "Save".
1199
+
1200
+ ### T24.5: Markdown File Edit-Save Roundtrip
1201
+ STEPS:
1202
+ 1. Click a .md file in the workspace
1203
+ 2. Click Edit
1204
+ 3. Add a new line: "## Added by Sprint 5 test"
1205
+ 4. Click Save
1206
+ EXPECT:
1207
+ - Save succeeds (toast)
1208
+ - Markdown view re-renders showing the new heading
1209
+ FAIL: Save fails, markdown not re-rendered.
1210
+
1211
+ ---
1212
+
1213
+ ## Automated Test Coverage (Updated Sprint 5)
1214
+
1215
+ Sprint 5 tests (test_sprint5.py) - 18 tests:
1216
+ - Phase A: app.js served correctly
1217
+ - Workspace CRUD: list, add valid, add invalid path, add invalid dir, add duplicate, requires path, remove, rename, rename unknown
1218
+ - Last workspace tracking: updates on session/update, new session inherits last
1219
+ - File save: success, missing fields, nonexistent file 404, path traversal blocked
1220
+ - Session index: created after save, sessions sorted correctly
1221
+
1222
+ Total automated: 86/86 passing.
1223
+
1224
+ Manual-only for Sprint 5:
1225
+ - T22.1-T22.7: workspace UI (tabs, dropdown, switching, inheritance)
1226
+ - T23.1-T23.2: copy to clipboard UX
1227
+ - T24.1-T24.5: inline file editor UX
1228
+
1229
+ ---
1230
+
1231
+ *Last updated: Sprint 5, March 30, 2026*
1232
+ *Total automated tests: 86/86*
1233
+ *Run: python -m pytest tests/ -v*
1234
+ *Source: <repo>/ | Static: static/style.css + static/app.js*
1235
+
1236
+ ---
1237
+
1238
+ ## Section 25: UI Polish Pass (Post-Sprint 5 Visual Audit)
1239
+
1240
+ These are visual regression checks. Take a screenshot after loading the UI and compare
1241
+ against each criterion below. A Claude browser agent can verify these with browser_vision.
1242
+
1243
+ ### T25.1: Sidebar Nav Tabs are Icon-Only
1244
+ EXPECT:
1245
+ - Five icon-only tabs in the sidebar nav row: message, clock, book, brain, folder
1246
+ - No text labels visible by default (text removed to prevent overflow)
1247
+ - Hovering a tab shows a tooltip with the label (Chat/Tasks/Skills/Memory/Spaces)
1248
+ - Active tab has a blue underline, icon brighter blue
1249
+ - All five icons fit in the sidebar width without clipping
1250
+ FAIL: Text labels showing alongside icons causing overflow, "Spaces" tab cut off.
1251
+
1252
+ ### T25.2: Message Role Labels are Softer
1253
+ EXPECT:
1254
+ - "You" and "Hermes" labels appear in slightly muted blue/gold (not full-brightness)
1255
+ - Labels use Title Case not ALL CAPS
1256
+ - Role icons are circles (not squares) with a subtle border
1257
+ - The role label area does not visually overpower the message content below
1258
+ FAIL: Bright gold "HERMES" and blue "YOU" in caps drawing eye away from content.
1259
+
1260
+ ### T25.3: Code Blocks Have a Connected Language Header
1261
+ EXPECT:
1262
+ - When a code block has a language, the header bar is connected (no gap) to the code
1263
+ - Header has a small colored dot on the left and the language name
1264
+ - Background: slightly lighter than the code body to distinguish it
1265
+ FAIL: Header floats above the code block with visible gap.
1266
+
1267
+ ### T25.4: Send Button Has Depth
1268
+ EXPECT:
1269
+ - Send button has a subtle gradient (lighter at top, slightly darker at bottom)
1270
+ - Hover: button shifts very slightly upward (1px transform)
1271
+ - Visual distinction from the blue link/chip color
1272
+ FAIL: Send button is same flat blue as all other blue elements.
1273
+
1274
+ ### T25.5: Session List Shows Date Groups
1275
+ EXPECT:
1276
+ - Sessions grouped under "Today", "Yesterday", "Earlier" headers
1277
+ - Headers are small, all-caps, muted gray
1278
+ - Active session has a blue left border accent
1279
+ FAIL: No grouping, flat list, no visual hierarchy.
1280
+
1281
+ ### T25.6: Empty State Logo is Distinct
1282
+ EXPECT:
1283
+ - The "H" logo on the empty state is a frosted-glass circle outline style (blue tint)
1284
+ - NOT the same orange/red gradient as the sidebar header logo
1285
+ - They should look different so it's clear they're two different things
1286
+ FAIL: Both logos look identical.
1287
+
1288
+ ### T25.7: New Conversation Button is Blue-Tinted
1289
+ EXPECT:
1290
+ - The "New conversation" button has a subtle blue background tint
1291
+ - Blue-colored text (not plain white/muted)
1292
+ - Feels clickable and primary (not the same style as the secondary sm-btn buttons)
1293
+ FAIL: Button looks same as other secondary buttons.
1294
+
1295
+ ### T25.8: Suggestion Buttons Slide on Hover
1296
+ EXPECT:
1297
+ - On the empty state, hovering a suggestion button shifts it slightly right (2px)
1298
+ - Border turns blue on hover
1299
+ - Subtle background tint on hover
1300
+ FAIL: No hover movement, generic hover state.
1301
+
1302
+ ### T25.9: Toast Notification is Premium
1303
+ EXPECT:
1304
+ - Toast appears at bottom center with blur/frosted-glass background
1305
+ - Subtle shadow behind the toast
1306
+ - Toast slightly floats up (translateY) when appearing
1307
+ FAIL: Plain flat dark box, no blur, no movement.
1308
+
1309
+ ### T25.10: Composer Input Has Glow on Focus
1310
+ EXPECT:
1311
+ - Clicking in the composer/message input shows a blue glow ring around the box
1312
+ - (box-shadow: 0 0 0 3px rgba(124,185,255,0.08))
1313
+ - Border also brightens to a stronger blue
1314
+ FAIL: Only border color change, no glow ring.
1315
+
1316
+ ---
1317
+
1318
+ ## Section 26: Resizable Panels (Sprint 6)
1319
+
1320
+ ### T26.1: Sidebar Can Be Resized by Dragging
1321
+ STEPS:
1322
+ 1. Hover over the right edge of the left sidebar (between sidebar and chat area)
1323
+ EXPECT:
1324
+ - Cursor changes to col-resize (double-headed horizontal arrow)
1325
+ - A subtle blue glow appears on the edge
1326
+ STEPS (continued):
1327
+ 2. Click and drag the edge to the right
1328
+ EXPECT:
1329
+ - Sidebar widens in real time as you drag
1330
+ - Chat area compresses to compensate
1331
+ - Minimum width ~180px, maximum ~420px
1332
+ - Releasing the mouse commits the new width
1333
+ - Hard-refreshing the page restores the saved width
1334
+ FAIL: Cursor doesn't change, panel doesn't resize, width not saved.
1335
+
1336
+ ### T26.2: Workspace Panel Can Be Resized by Dragging
1337
+ STEPS:
1338
+ 1. Hover over the left edge of the right workspace panel
1339
+ 2. Drag left to narrow, drag right to widen
1340
+ EXPECT:
1341
+ - Panel resizes within 180-500px range
1342
+ - Width persists across page reload (stored in localStorage)
1343
+ FAIL: Panel not resizable.
1344
+
1345
+ ---
1346
+
1347
+ ## Section 27: Cron Job Create (Sprint 6)
1348
+
1349
+ ### T27.1: New Job Button Opens Form
1350
+ STEPS:
1351
+ 1. Click the Tasks tab in the sidebar
1352
+ 2. Click the "+ New job" button at the top of the Tasks panel
1353
+ EXPECT:
1354
+ - A form slides in below the header with:
1355
+ - Name input (optional)
1356
+ - Schedule input (cron expression or natural language)
1357
+ - Prompt textarea
1358
+ - Delivery target dropdown
1359
+ - Create job and Cancel buttons
1360
+ FAIL: Nothing happens, no form appears.
1361
+
1362
+ ### T27.2: Create a Job Successfully
1363
+ STEPS:
1364
+ 1. Open the create form (T27.1)
1365
+ 2. Fill in: Name "Test Job", Schedule "every 999h", Prompt "Say hello"
1366
+ 3. Click Create job
1367
+ EXPECT:
1368
+ - Form closes
1369
+ - Toast: "Job created"
1370
+ - New job appears in the cron list with status "active"
1371
+ FAIL: Error shown, job not created, form stays open.
1372
+
1373
+ ### T27.3: Invalid Schedule Shows Error
1374
+ STEPS:
1375
+ 1. Open the create form
1376
+ 2. Leave schedule empty or type "not_a_schedule"
1377
+ 3. Click Create job
1378
+ EXPECT:
1379
+ - Error message appears below the form: "Schedule is required" or parse error
1380
+ - Form stays open (not dismissed)
1381
+ FAIL: Form closes without feedback, generic error.
1382
+
1383
+ ### T27.4: Cancel Closes Form Without Creating
1384
+ STEPS:
1385
+ 1. Open the create form, fill in some fields
1386
+ 2. Click Cancel
1387
+ EXPECT:
1388
+ - Form closes
1389
+ - No new job created
1390
+ - No toast
1391
+ FAIL: Job created, form doesn't close.
1392
+
1393
+ ---
1394
+
1395
+ ## Section 28: Session JSON Export (Sprint 6)
1396
+
1397
+ ### T28.1: JSON Export Button Downloads File
1398
+ SETUP: Active session with at least a few messages.
1399
+ STEPS:
1400
+ 1. Click the "Hermes" button in the sidebar footer
1401
+ 2. In the Control Center modal, click "JSON"
1402
+ EXPECT:
1403
+ - Browser downloads a file named hermes-{session_id}.json
1404
+ - Opening the file shows valid JSON with: session_id, title, messages array,
1405
+ workspace, model, created_at, updated_at
1406
+ - messages array contains objects with role and content fields
1407
+ FAIL: No download triggered, file is empty, file is not valid JSON.
1408
+
1409
+ ### T28.2: Escape Cancels File Editor
1410
+ SETUP: Open a text file in the workspace right panel (click any .py or .md file).
1411
+ STEPS:
1412
+ 1. Click "Edit" button in the preview path bar
1413
+ 2. Make some changes in the textarea
1414
+ 3. Press Escape
1415
+ EXPECT:
1416
+ - Textarea disappears, read-only view returns
1417
+ - Original content is shown (changes discarded)
1418
+ - File on disk is unchanged (verify by closing and reopening the file)
1419
+ FAIL: Escape does nothing, changes are saved, crash.
1420
+
1421
+ ---
1422
+
1423
+ ## Automated Test Coverage (Updated Sprint 6)
1424
+
1425
+ Sprint 6 tests (test_sprint6.py) - 16 tests:
1426
+ - Phase E: HTML served from static/index.html, server.py has no inline HTML
1427
+ - Phase D: approval/respond requires session_id and valid choice; file/raw validates session
1428
+ - Cron create: requires prompt, requires schedule, invalid schedule returns 400, success
1429
+ - Session export: requires session_id, unknown session 404, valid JSON with session_id
1430
+ - Resize: static files contain resize handles and resize JS logic
1431
+
1432
+ Total automated: 106/106 passing.
1433
+
1434
+ Manual-only for Sprint 6:
1435
+ - T26.1-T26.2: resize drag UX
1436
+ - T27.1-T27.4: cron create form UX
1437
+ - T28.1: JSON export download
1438
+ - T28.2: Escape from file editor
1439
+
1440
+ ---
1441
+
1442
+
1443
+ *Static: static/index.html + static/style.css + static/app.js*
1444
+
1445
+
1446
+ ---
1447
+
1448
+ ## Section 29: Cron Edit and Delete (Sprint 7)
1449
+
1450
+ ### T29.1: Edit Button Opens Inline Form
1451
+ SETUP: At least one cron job exists. Tasks tab open.
1452
+ STEPS:
1453
+ 1. Click a cron job to expand it
1454
+ 2. Click the "Edit" (pencil) button
1455
+ EXPECT:
1456
+ - An inline form appears inside the expanded cron body
1457
+ - Name, schedule, and prompt fields are pre-filled with current values
1458
+ FAIL: Nothing happens, new form not shown.
1459
+
1460
+ ### T29.2: Save Edit Updates the Job
1461
+ STEPS (continued from T29.1):
1462
+ 1. Change the name field to "Renamed Job"
1463
+ 2. Click Save
1464
+ EXPECT:
1465
+ - Form closes, toast "Job updated"
1466
+ - Job header shows new name
1467
+ FAIL: Save fails, name unchanged.
1468
+
1469
+ ### T29.3: Delete Button Removes the Job
1470
+ SETUP: A cron job you can safely delete (or a test job created for this).
1471
+ STEPS:
1472
+ 1. Expand the job, click "Delete"
1473
+ 2. Confirm the modal
1474
+ EXPECT:
1475
+ - Toast: "Job deleted"
1476
+ - Job disappears from the list
1477
+ FAIL: Job stays, no confirmation dialog.
1478
+
1479
+ ---
1480
+
1481
+ ## Section 30: Skill Create and Edit (Sprint 7)
1482
+
1483
+ ### T30.1: New Skill Button Opens Form
1484
+ STEPS:
1485
+ 1. Click the Skills tab
1486
+ 2. Click "+ New skill" button
1487
+ EXPECT:
1488
+ - A form appears with name, category, and content textarea fields
1489
+ FAIL: Nothing happens.
1490
+
1491
+ ### T30.2: Create a Skill and See it in List
1492
+ STEPS:
1493
+ 1. Fill in: Name "test-ui-skill", Content "---
1494
+ name: test-ui-skill
1495
+ description: UI test.
1496
+ tags: [test]
1497
+ ---
1498
+
1499
+ # Test"
1500
+ 2. Click Save skill
1501
+ EXPECT:
1502
+ - Toast "Skill created", form closes
1503
+ - Skill appears in the skills list
1504
+ FAIL: Error, skill not in list.
1505
+
1506
+ ### T30.3: Cancel Closes Form Without Creating
1507
+ STEPS:
1508
+ 1. Open new skill form, fill some fields, click Cancel
1509
+ EXPECT:
1510
+ - Form closes, no skill created
1511
+ FAIL: Skill created, form stays.
1512
+
1513
+ ---
1514
+
1515
+ ## Section 31: Memory Inline Edit (Sprint 7)
1516
+
1517
+ ### T31.1: Edit Button Opens Memory Edit Form
1518
+ STEPS:
1519
+ 1. Click the Memory tab
1520
+ 2. Click the "Edit" (pencil) button in the header
1521
+ EXPECT:
1522
+ - An edit form appears below the memory panel with a textarea pre-filled with MEMORY.md content
1523
+ FAIL: Nothing happens, no form.
1524
+
1525
+ ### T31.2: Save Writes Changes
1526
+ STEPS:
1527
+ 1. In edit mode, add a line to the textarea
1528
+ 2. Click Save
1529
+ EXPECT:
1530
+ - Toast "Memory saved", form closes
1531
+ - Memory panel reloads showing the updated content
1532
+ FAIL: Save fails, content unchanged.
1533
+
1534
+ ### T31.3: Cancel Discards Changes
1535
+ STEPS:
1536
+ 1. Open edit form, change content, click Cancel
1537
+ EXPECT:
1538
+ - Form closes, original content unchanged
1539
+ FAIL: Changes saved despite cancel.
1540
+
1541
+ ---
1542
+
1543
+ ## Automated Test Coverage (Updated Sprint 7)
1544
+
1545
+ Sprint 7 tests (test_sprint7.py) - 19 tests:
1546
+ - Health: active_streams field, uptime_seconds field
1547
+ - Session search: empty query, content+depth params accepted, count returned
1548
+ - Cron update: requires job_id, unknown job 404
1549
+ - Cron delete: requires job_id, unknown job 404
1550
+ - Skill save: requires name, requires content, invalid name rejected, create+delete roundtrip
1551
+ - Skill delete: requires name, unknown skill 404
1552
+ - Memory write: requires section, requires content, invalid section, write+read roundtrip
1553
+
1554
+ Total automated: 125/125 passing.
1555
+
1556
+ Manual-only for Sprint 7:
1557
+ - T29.1-T29.3: cron edit/delete UX
1558
+ - T30.1-T30.3: skill create UX
1559
+ - T31.1-T31.3: memory edit UX
1560
+
1561
+ ---
1562
+
1563
+
1564
+
1565
+ ---
1566
+
1567
+ ## Section 32: Edit User Message + Regenerate (Sprint 8)
1568
+
1569
+ ### T32.1: Edit Icon Appears on Hover
1570
+ SETUP: Active session with at least one user message.
1571
+ STEPS:
1572
+ 1. Hover over any user message bubble
1573
+ EXPECT:
1574
+ - A pencil (edit) icon appears in the message header row, right side
1575
+ - Icon not visible when not hovering
1576
+ FAIL: No icon, always visible, wrong position.
1577
+
1578
+ ### T32.2: Click Edit Opens Textarea
1579
+ STEPS:
1580
+ 1. Hover over a user message and click the pencil icon
1581
+ EXPECT:
1582
+ - Message body is replaced by an editable textarea pre-filled with the original text
1583
+ - "Send edit" and "Cancel" buttons appear below
1584
+ - Textarea has a blue border glow (focused style)
1585
+ FAIL: Nothing happens, empty textarea, crash.
1586
+
1587
+ ### T32.3: Cancel Restores Original
1588
+ STEPS (continued from T32.2):
1589
+ 1. Make a change in the textarea
1590
+ 2. Click Cancel
1591
+ EXPECT:
1592
+ - Original message text restored exactly
1593
+ - No messages sent, no API call
1594
+ FAIL: Original text lost, message sent.
1595
+
1596
+ ### T32.4: Escape Also Cancels
1597
+ STEPS:
1598
+ 1. Enter edit mode on a user message
1599
+ 2. Press Escape
1600
+ EXPECT:
1601
+ - Textarea dismissed, original restored
1602
+ FAIL: Escape does nothing.
1603
+
1604
+ ### T32.5: Send Edit Truncates and Regenerates
1605
+ STEPS:
1606
+ 1. Click edit on a user message that has a response after it
1607
+ 2. Change the text
1608
+ 3. Click "Send edit" (or press Enter)
1609
+ EXPECT:
1610
+ - All messages after the edited message are removed
1611
+ - The edited text is sent as a new user message
1612
+ - Hermes streams a fresh response
1613
+ FAIL: Old messages remain, double messages, crash.
1614
+
1615
+ ---
1616
+
1617
+ ## Section 33: Regenerate Last Response (Sprint 8)
1618
+
1619
+ ### T33.1: Retry Icon on Last Assistant Bubble Only
1620
+ SETUP: Session with at least one complete exchange.
1621
+ EXPECT:
1622
+ - A retry (↻) icon appears on hover over the LAST assistant message only
1623
+ - Not on user messages
1624
+ - Not on older assistant messages
1625
+ FAIL: Icon on every message, or not on last.
1626
+
1627
+ ### T33.2: Regenerate Re-Runs Last User Message
1628
+ STEPS:
1629
+ 1. Hover the last assistant bubble and click the retry icon
1630
+ EXPECT:
1631
+ - The last assistant message is removed
1632
+ - The previous user message is re-sent
1633
+ - Hermes streams a new response
1634
+ FAIL: Both messages removed, wrong message sent, crash.
1635
+
1636
+ ---
1637
+
1638
+ ## Section 34: Clear Conversation (Sprint 8)
1639
+
1640
+ ### T34.1: Clear Button Appears When Session Has Messages
1641
+ SETUP: Session with at least one message.
1642
+ EXPECT:
1643
+ - The "Hermes" button is visible in the sidebar footer
1644
+ - Opening the Control Center shows a "Clear" action in the Conversation section
1645
+ - The Clear action is disabled when there is no active session or no messages
1646
+ FAIL: Button always visible, never visible.
1647
+
1648
+ ### T34.2: Clear Wipes Messages and Resets Title
1649
+ STEPS:
1650
+ 1. Click the "Hermes" button in the sidebar footer
1651
+ 2. Click "Clear" in the Conversation section
1652
+ 3. Confirm the modal
1653
+ EXPECT:
1654
+ - All messages disappear from the chat area
1655
+ - Empty state ("What can I help with?") reappears
1656
+ - Session title in sidebar resets to "Untitled"
1657
+ - Toast: "Conversation cleared"
1658
+ - Session still in the sidebar (not deleted)
1659
+ FAIL: Session deleted, messages remain, title not reset.
1660
+
1661
+ ### T34.3: Cancel Clear Does Nothing
1662
+ STEPS:
1663
+ 1. Click Clear, then click Cancel in the confirmation modal
1664
+ EXPECT:
1665
+ - All messages still present
1666
+ - No toast, no change
1667
+ FAIL: Messages cleared despite cancel.
1668
+
1669
+ ---
1670
+
1671
+ ## Section 35: Syntax Highlighting (Sprint 8)
1672
+
1673
+ ### T35.1: Code Blocks Have Syntax Colors
1674
+ SETUP: Ask Hermes something that produces a code response (e.g. "Show me a Python hello world").
1675
+ EXPECT:
1676
+ - The code block has syntax-colored tokens (keywords in one color, strings in another)
1677
+ - NOT all plain white/gray monospace text
1678
+ - Dark background with Prism Tomorrow theme colors
1679
+ FAIL: All plain text, no colors, broken layout.
1680
+
1681
+ ### T35.2: Code in Workspace Preview Also Highlighted
1682
+ SETUP: Open a .py or .js file in the workspace panel.
1683
+ EXPECT:
1684
+ - File content has syntax highlighting (Prism autoloader)
1685
+ FAIL: Plain monospace text only.
1686
+
1687
+ ---
1688
+
1689
+ ## Automated Test Coverage (Updated Sprint 8)
1690
+
1691
+ Sprint 8 tests (test_sprint8.py) - 14 tests:
1692
+ - session/clear: requires session_id, unknown 404, wipes messages+resets title, returns compact
1693
+ - session/truncate: requires session_id, requires keep_count, unknown 404, returns messages array
1694
+ - Static file checks: app.js has editMessage, regenerateResponse, clearConversation, highlightCode
1695
+ - index.html: contains Prism CDN link, contains btnClearConv + clearConversation
1696
+
1697
+ Total automated: 139/139 passing.
1698
+
1699
+ Manual-only for Sprint 8:
1700
+ - T32.1-T32.5: edit message UX
1701
+ - T33.1-T33.2: regenerate UX
1702
+ - T34.1-T34.3: clear conversation UX
1703
+ - T35.1-T35.2: syntax highlighting visual
1704
+
1705
+ ---
1706
+
1707
+ *Last updated: Sprint 10 complete, March 31, 2026*
1708
+ *Total automated tests: 177/177*
1709
+ *Regression gate: tests/test_regressions.py (10 tests, one per introduced bug)*
1710
+ *Run: python -m pytest tests/ -v*
1711
+ *Source: <repo>/*
1712
+ *Modules: ui.js, workspace.js, sessions.js, messages.js, panels.js, boot.js (app.js deleted)*
1713
+
1714
+ ---
1715
+
1716
+ ## Section 36: Message Queue (Sprint 8 hotfix)
1717
+
1718
+ ### T36.1: Typing While Busy Queues the Message
1719
+ SETUP: Active session, a response is currently streaming (thinking dots visible).
1720
+ STEPS:
1721
+ 1. While Hermes is responding, type a new message and press Enter
1722
+ EXPECT:
1723
+ - Input clears immediately
1724
+ - A small toast appears: "Queued: [your message]"
1725
+ - A badge appears in the bottom-right: "1 message queued"
1726
+ - The current response continues uninterrupted
1727
+ FAIL: Message dropped silently, duplicate send triggered, error.
1728
+
1729
+ ### T36.2: Queued Message Sends Automatically After Response
1730
+ STEPS (continued from T36.1):
1731
+ 1. Wait for the current response to finish
1732
+ EXPECT:
1733
+ - As soon as the response completes, the queued message is automatically sent
1734
+ - Badge disappears
1735
+ - New thinking dots appear for the queued message
1736
+ FAIL: Queued message never sends, badge stays, double-send.
1737
+
1738
+ ### T36.3: Queue Badge Shows Count for Multiple Messages
1739
+ STEPS:
1740
+ 1. While busy, type and send two separate messages
1741
+ EXPECT:
1742
+ - Badge reads "2 messages queued"
1743
+ - They drain one at a time, each waiting for the previous response
1744
+ FAIL: Only first message queued, count wrong.
1745
+
1746
+ ### T36.4: Switch Session Clears Queue
1747
+ STEPS:
1748
+ 1. Queue a message in session A
1749
+ 2. Click to session B before it drains
1750
+ EXPECT:
1751
+ - Queue badge disappears
1752
+ - The queued message does NOT fire in session B
1753
+ FAIL: Queued message fires in session B.
1754
+
1755
+ ---
1756
+
1757
+ ## Section 37: Message Persists on Switch-Away (Sprint 8 hotfix)
1758
+
1759
+ ### T37.1: Sent Message Stays Visible After Switch-Away and Back
1760
+ SETUP: Active session.
1761
+ STEPS:
1762
+ 1. Send a message (thinking dots appear)
1763
+ 2. Immediately click to a different session
1764
+ 3. Click back to the original session
1765
+ EXPECT:
1766
+ - The user message you sent is still visible in the chat
1767
+ - Thinking dots are still animating
1768
+ - Session is still in busy state (Send button disabled)
1769
+ - When response arrives, it appears normally
1770
+ FAIL: User message gone, blank chat, response lands in wrong session.
1771
+
1772
+ ---
1773
+
1774
+ ---
1775
+
1776
+ ## Sections Added Post-Sprint 10 (Sprints 11-19)
1777
+
1778
+ The following features were added in Sprints 11-19 and need manual browser testing.
1779
+ Each has automated API-level tests in `tests/test_sprint{N}.py`.
1780
+
1781
+ ### Sprint 11: Multi-Provider Models
1782
+ - Open model dropdown. Verify models grouped by provider (OpenAI, Anthropic, Google, etc.)
1783
+ - If custom `base_url` configured in config.yaml, verify local models appear in dropdown.
1784
+ - Switch model. Send a message. Verify response uses selected model.
1785
+
1786
+ ### Sprint 12: Settings + Pin + Import
1787
+ - Click the "Hermes WebUI" button in the sidebar footer. Control Center overlay opens with vertical section tabs on the left.
1788
+ - Change default model, save. Restart server. Verify setting persisted.
1789
+ - Pin a session (star icon in hover overlay). Verify it floats to top of list.
1790
+ - Export session as JSON. Import it back. Verify messages restored.
1791
+
1792
+ ### Sprint 13: Alerts + Session QoL
1793
+ - Duplicate a session (copy icon in hover overlay). Verify "(copy)" title.
1794
+ - Browser tab title updates to active session name. Switch sessions — title changes.
1795
+
1796
+ ### Sprint 14: Visual Polish + Workspace Ops
1797
+ - Create a mermaid code block in a response. Verify diagram renders inline.
1798
+ - Message timestamps visible next to role labels (hover for full date).
1799
+ - Double-click a file in workspace panel to rename. Enter saves, Escape cancels.
1800
+ - Create a folder via folder icon in workspace header.
1801
+ - Add `#tag` to session title. Verify tag chip appears in sidebar. Click to filter.
1802
+ - Archive a session. Verify it disappears. Toggle "Show archived" to see it.
1803
+
1804
+ ### Sprint 15: Session Projects
1805
+ - Click "+" in project bar to create a project. Type name, Enter.
1806
+ - Click a project chip to filter sessions.
1807
+ - Hover a session → click folder icon → assign to project via picker.
1808
+ - Verify colored left border appears on assigned session.
1809
+ - Double-click project chip to rename. Right-click to delete.
1810
+ - Code blocks have a "Copy" button. Click → "Copied!" feedback.
1811
+ - Messages with 2+ tool cards show "Expand all / Collapse all" toggle.
1812
+
1813
+ ### Sprint 16: Sidebar Visual Polish
1814
+ - Session titles use full sidebar width (no truncated space for hidden icons).
1815
+ - Hover a session → a dotted actions trigger appears on the right.
1816
+ - Click the dotted trigger → a dropdown opens with pin, project, archive, duplicate, and delete actions.
1817
+ - All icons are monochrome SVGs (not emoji). Consistent across platforms.
1818
+ - Pinned sessions show small gold star inline. Unpinned = no star, full title width.
1819
+ - Active session has gold highlight (not blue).
1820
+ - Double-click to rename → session actions hide during rename.
1821
+
1822
+ ### Sprint 17: Workspace + Slash Commands + Send Key
1823
+ - Navigate into a subdirectory. Breadcrumb bar appears with clickable segments.
1824
+ - Up button in panel header navigates to parent. Hidden at root.
1825
+ - Type `/` in composer → autocomplete dropdown appears. Arrow keys navigate.
1826
+ - Type `/help` → lists all commands. `/clear` clears conversation. `/model` switches.
1827
+ - Settings panel: change send key to Ctrl+Enter. Verify Enter inserts newline.
1828
+
1829
+ ### Sprint 18: Thinking + Tree View + Preview Fix
1830
+ - View a file in workspace. Click a breadcrumb or folder → preview closes automatically.
1831
+ - Click a directory toggle arrow (▸) → expands in-place showing children.
1832
+ - Click again (▾) → collapses. Double-click navigates into it (breadcrumb view).
1833
+ - If model returns thinking blocks (Claude extended thinking), verify collapsible gold card appears above response.
1834
+ - Verify the thinking card has a tinted background, visible border, and rounded corners like a tool card, but in the gold thinking palette.
1835
+ - Open and close a thinking card. Verify the caret rotation and the content reveal both animate smoothly instead of snapping open.
1836
+
1837
+ ### UI Polish: Tool Card Disclosure Animation
1838
+ - Trigger a response with at least one completed tool call card.
1839
+ - Open and close the tool call card. Verify the caret rotates smoothly and the args/result section animates open and closed instead of appearing instantly.
1840
+ - If a turn has 2+ tool cards, use "Expand all / Collapse all" and verify the same smooth animation applies to every card in the group.
1841
+
1842
+ ### Sprint 19: Auth + Security
1843
+ - No password set: everything works as normal. No login page.
1844
+ - Set `HERMES_WEBUI_PASSWORD=test` env var. Restart. All pages redirect to `/login`.
1845
+ - Login page: minimal card, password field, "Sign in" button.
1846
+ - Enter correct password → redirected to `/`. Cookie set (24h).
1847
+ - Enter wrong password → error message, stay on login page.
1848
+ - Settings panel: set password via "Access Password" field. Auth activates.
1849
+ - "Sign Out" button visible when auth active. Click → redirected to /login.
1850
+ - API calls without auth cookie → 401 JSON response.
1851
+ - Check response headers: `X-Content-Type-Options: nosniff`, `X-Frame-Options: DENY`.
1852
+
1853
+ ### Sprint 20: Voice Input + Send Button
1854
+ - Mic button visible in composer (Chrome/Edge). Hidden in Firefox.
1855
+ - Tap mic → button turns red with pulse, "Listening..." indicator appears.
1856
+ - Speak → live transcription appears in textarea.
1857
+ - Stop speaking → auto-stops after ~2s silence. Text stays editable.
1858
+ - Tap mic again or Send → stops recording, sends text.
1859
+ - Type text, then tap mic → spoken text appends to existing text (doesn't replace).
1860
+ - Send button hidden when textarea is empty. Appears with pop-in animation when typing.
1861
+ - Send button is icon-only circle (no "Send" text label). Blue with glow.
1862
+ - Attach a file with no text → send button appears.
1863
+ - Send message → button disappears after textarea clears.
1864
+ - While agent is responding → send button hidden.
1865
+
1866
+ ### Sprint 21: Mobile Responsive + Docker
1867
+ - Open on mobile viewport (<640px): hamburger icon visible in topbar.
1868
+ - Tap hamburger → sidebar slides in from left with backdrop overlay.
1869
+ - Tap outside sidebar → closes. Tap a session → closes and loads session.
1870
+ - Sidebar top nav remains visible inside the mobile drawer; includes Chat/Tasks/Skills/Memory/Spaces/Profile tabs.
1871
+ - Tap "Tasks" in the drawer nav → Tasks panel opens in the sidebar drawer.
1872
+ - Tap "Chat" in the drawer nav → sidebar closes and chat is unobstructed in the main area.
1873
+ - Files button in topbar → right panel slides in from right.
1874
+ - No fixed mobile bottom nav; chat transcript and composer use the reclaimed vertical space.
1875
+ - All touch targets are at least 44px (session items, buttons, icons).
1876
+ - Desktop viewport (>640px): no hamburger or mobile overlay; desktop layout unchanged.
1877
+ - Docker: `docker compose up -d` starts server on port 8787.
1878
+ - Docker: session data persists across container restarts (named volume).
1879
+
1880
+ ### Sprint 22: Multi-Profile Support
1881
+ - Profile chip in topbar (purple accent). Click → dropdown with all profiles.
1882
+ - Dropdown shows gateway status dots, model info, skill count per profile.
1883
+ - Click a profile → switches; model dropdown, skills, memory, cron refresh.
1884
+ - "Manage profiles" link opens Profiles sidebar panel.
1885
+ - Profiles panel: cards with name, model, provider, skill count, API key status.
1886
+ - "Use" button switches profile. Delete button removes non-default profiles.
1887
+ - "+ New profile" form: name validation (lowercase + hyphens), clone config checkbox.
1888
+ - Create profile → appears in list and dropdown.
1889
+ - Delete profile → confirmation modal. Auto-switches to default if deleting active.
1890
+ - Attempt switch while agent busy → blocked with toast message.
1891
+ - With hermes-agent not installed → only default profile shown, graceful fallback.
1892
+
1893
+ ---
1894
+
1895
+ ## Slash command parity (manual checklist)
1896
+
1897
+ For each batch-1 command, run via webui slash menu AND via `hermes` CLI in the
1898
+ same `HERMES_HOME` (when applicable) and verify identical effect.
1899
+
1900
+ - [ ] `/help` — dropdown lists 25+ commands; selecting `/help` posts an assistant message listing them.
1901
+ - [ ] `/new` (and alias `/reset`) — starts fresh session.
1902
+ - [ ] `/clear` — clears current transcript display (webui-only meaning, distinct from CLI's "clear screen").
1903
+ - [ ] `/title <name>` — renames active session, topbar + sidebar update; `/title` alone shows current title.
1904
+ - [ ] `/status` — assistant message shows session_id, model, workspace, message count.
1905
+ - [ ] `/usage` — assistant message shows token counts; the "show token usage" setting is unchanged (toggle still in Settings panel).
1906
+ - [ ] `/stop` — interrupts a running stream; with no active stream toasts "No active task to stop."
1907
+ - [ ] `/retry` — removes last user+assistant exchange, refills composer with last user text, resends. Final transcript has only ONE copy of the resent message.
1908
+ - [ ] `/undo` — removes last user+assistant exchange; toast confirms; repeated until empty toasts "Nothing to undo."
1909
+ - [ ] `/model <name>` — switches model dropdown.
1910
+ - [ ] `/personality` — lists personalities; `/personality <name>` switches.
1911
+ - [ ] `/skills [query]` — lists matching skills.
1912
+ - [ ] `/theme <name>` — switches webui theme.
1913
+ - [ ] `/workspace <name>` — switches workspace.
1914
+
1915
+ Unknown / deferred:
1916
+
1917
+ - [ ] `/yolo`, `/reasoning`, `/voice`, `/branch`, `/insights`, `/debug`, `/reload`, etc. — toast "Web UI 暂未实现该命令: /<name>". MUST NOT be sent as plain text to the LLM.
1918
+ - [ ] `/compact` — toast "/compress is not available in the web UI yet — use the CLI for now." (was sending free text to LLM before this batch.)
1919
+ - [ ] Made-up command (e.g. `/fhfajl`) — fall through to send as text (existing behavior preserved for typos vs. real commands).
1920
+
1921
+ Bridged CLI sessions:
1922
+
1923
+ - [ ] Open a CLI-bridged session in webui sidebar (if `show_cli_sessions` setting enabled).
1924
+ - [ ] `/retry`, `/undo` toast "该命令仅支持 Web UI 原生会话…" and do nothing.
1925
+
1926
+ ---
1927
+
1928
+ *Last updated: v0.51.192, May 31, 2026*
1929
+ *Total automated tests collected: ~7,150 (run `pytest tests/ --collect-only -q` for the exact current count)*
1930
+ *Regression gate: tests/test_regressions.py*
1931
+ *Run: pytest tests/ -v --timeout=60*
1932
+ *Source: <repo>/*