# Web Hermes Analysis + Notifications (2026-05) Session learning from extending BOSS's local `xianyu-hunter` fork with Web-visible analysis and notification controls. ## Product shape BOSS expects When adding analysis/notification capability, make it visible in the Web dashboard, not only as backend APIs: - Header button: **通知设置**. - Task controls: **执行分析** beside edit/duplicate/start/rerun/delete. - New task tab: **Hermes 分析** with recommendation/caution/reject summary and item cards. - Product list should show analysis verdict badges and concise reasons inline. - Analysis should persist into `data/tasks.json` and generate a Markdown report under `reports/`. ## Backend API pattern Add a lightweight local analysis layer and notifier: - `lib/analyzer.mjs` - `buildAnalysis(task, { limit })` returns `{ items, summary }`. - `renderAnalysisMarkdown(task, summary)` writes `reports/-analysis.md`. - Include item-level `hermesAnalysis: { score, verdict, reason, flags, positives, analyzedAt }`. - `lib/notifier.mjs` - `GET /api/notify-config` and `PUT /api/notify-config` backed by `data/notify-config.json`. - `sendNotifications(task, summary, { force })` supports: - `hermes-webhook`: POST `{ message, target, source, taskId }`. - `bark`: POST `{ title, body, group }` to `https://api.day.app/` or configured endpoint. - `pushplus`: POST `{ token, title, content, template:'txt' }`. - `webhook`: generic JSON `{ title, message, taskId }`. - `server.mjs` - `POST /api/tasks/:id/analyze` should call `taskManager.analyzeTask`, write report, persist summary, and optionally notify. ## TaskManager persistence details - Add `getTaskFull(id)` for routes that need full in-memory task data. - Add `setTaskAnalysis(id, analysis)` that merges item `hermesAnalysis` into `products.fineFiltered`, stores `task.hermesAnalysis`, persists to disk, and emits `task:updated`. - Include `hermesAnalysis` in `_serialize`, `_serializeFull`, and `_loadFromDisk`. - Clear `task.hermesAnalysis = null` when resetting products/rerunning a task. - New/duplicated tasks should initialize `hermesAnalysis: null`. ## Frontend implementation details - Bump asset query versions in `index.html` after changing CSS/JS to avoid stale browser cache (`style.css?v=11`, `app.js?v=11`, etc.). - `public/app.js` helpers: - `analyzeTaskNow(id)` calls `POST /api/tasks/:id/analyze` with `{ notify: true }`, then refreshes task detail. - `renderAnalysis()` renders empty state, stat cards, report path, and scored item cards. - When rendering raw product rows, map analysis from `products.fineFiltered` by `id`; raw items often do not contain the merged `hermesAnalysis` directly. - Preserve notification channel `id` when saving from the form; otherwise repeated saves create new channel IDs. ## Verification checklist Run syntax checks: ```bash cd /Users/chick/.Hermes/workspace/research/xianyu-hunter node --check server.mjs node --check lib/task.mjs node --check lib/analyzer.mjs node --check lib/notifier.mjs node --check public/app.js ``` Restart exact stale listener if `xianyu_ops.mjs stop` says `no-recorded-server` but port is still occupied: ```bash pid=$(lsof -nP -iTCP:3000 -sTCP:LISTEN -t || true) [ -z "$pid" ] || kill "$pid" node tools/xianyu_ops.mjs start --port 3000 ``` Smoke test APIs: ```bash curl -fsS http://127.0.0.1:3000/api/notify-config | python3 -m json.tool curl -fsS http://127.0.0.1:3000/api/tasks -o /tmp/xh_tasks.json TASK=$(python3 - <<'PY' import json j=json.load(open('/tmp/xh_tasks.json')) print(j[0]['id'] if j else '') PY ) curl -fsS -X POST "http://127.0.0.1:3000/api/tasks/$TASK/analyze" \ -H 'Content-Type: application/json' \ -d '{"notify":false,"limit":10}' | python3 -m json.tool ``` Important pitfall: piping a large `/api/tasks` response directly into `python3 - <<'PY'` consumes stdin for the script, not JSON. Save curl output to a temp file first. ## Model-specific scoring pitfall: E3-1245v3 For CPU searches like `E3-1245v3`, scoring must distinguish true target models from related models used as SEO keywords. Rules added in `lib/analyzer.mjs`: - If the task query indicates `E3-1245v3`, require an exact-ish match: `e3\s*-?\s*1245\s*[_-]?\s*v3`. - Penalize titles whose main model is another E3 v3, e.g. `E3-1275v3 ... 性能超 E3-1245v3`, because the title may only use 1245v3 as a comparison/keyword. - Flag failure terms beyond obvious `坏/不亮/暗病`, including `不能进系统`. This reduced false recommendations in the E3-1245v3 task from related CPUs and broken parts.