5e801d74d52a08cb30179afa88ba774e41d88519
3
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
c71312b2e8 |
feat(kanban): edit task button, real Run dispatcher, assignee dropdown
Three connected gaps in the Kanban UX, fixed together because they're
load-bearing for the actual work-queue lifecycle:
1. Edit task — the detail view had only status-transition buttons (Triage/
Todo/Ready/Blocked/Done/Archived) plus Block/Unblock and Add comment.
No way to edit title, body, assignee, tenant, or priority once the task
was created. Backend already supported it via PATCH /api/kanban/tasks/<id>
(api/kanban_bridge.py::_patch_task) — purely a UI gap.
Now: an Edit button on the task-detail header opens the existing modal
pre-filled with current values, switches the modal title to 'Edit task'
and the submit button to 'Save', PATCHes instead of POSTing on submit.
2. Run dispatcher — the existing 'Preview dispatcher' button always passed
?dry_run=1 (nudgeKanbanDispatcher), so it was preview-only. There was
literally no UI button anywhere in the WebUI that actually ran the
dispatcher to claim Ready tasks and spawn workers. Users had to drop
to the CLI.
Now: new runKanbanDispatcher() entry point hits /api/kanban/dispatch
without dry_run=1, after a showConfirmDialog confirmation because it
spawns subprocess workers. Two UI surfaces: a lightning-bolt button in
the board header (visually distinct from the dry-run preview ▶), and
a primary 'Run dispatcher' button in the sidebar bulk bar next to a
relabeled 'Preview' button. Toast result shows concrete numbers from
dispatch_once(): 'Dispatched: 1 spawned, 2 skipped (no assignee)' —
not just a generic 'OK'.
3. Assignee dropdown — the previous create modal accepted free-text
assignee with no validation. The dispatcher (kanban_db.py:3567) only
spawns workers when row['assignee'] is a real Hermes profile name; any
typo or blank value made the task sit in Ready forever.
Now: <select> populated from /api/profiles (Hermes profile names) with
historical board assignees grouped under 'Other (CLI lanes / removed
profiles)', plus an explicit '— Unassigned (won't auto-run) —' option.
Default selection is the first profile, not Unassigned. Custom SVG
chevron so the field reads visually as a dropdown. Helper text under
the field explains the dispatcher claim contract. Soft warning if user
explicitly picks Unassigned + Ready ('You picked Unassigned + Ready.
The dispatcher will skip this task. Submit again to confirm, or pick
a profile.'); proceeds on second submit.
Side effect: default new-task status changed from triage to ready, since
'ready' is what users want for tasks they intend to actually run. Triage
is still in the dropdown for tasks that need staging review.
i18n: 19 new keys translated across all 8 supported locales.
Tests: 3 new regression tests in tests/test_kanban_ui_static.py:
- test_kanban_task_detail_has_edit_button_and_modal_supports_edit_mode
- test_kanban_assignee_dropdown_uses_select_not_freetext
- test_kanban_run_dispatcher_button_exists_and_is_distinct_from_preview
Verified end-to-end in browser: created board → opened modal with profile
dropdown → created task with assignee=archivist → clicked Edit → changed
all 5 fields → saved → verified persistence → clicked Run dispatcher →
confirm dialog → confirmed → toast 'Dispatched: 1 spawned' → task moved
Ready → Running.
Test suite: 5042 passed, 11 skipped, 3 xpassed, 0 regressions in 151s.
|
||
|
|
a3af4a3c8f |
fix(profile/mcp): discover MCP tools after per-session HERMES_HOME mutation
Issue #1968: switching to a non-default profile in the WebUI dropdown had no effect on which MCP servers were available. Every chat session, regardless of profile, only saw the default profile's mcp_servers from ~/.hermes/config.yaml. Non-default profile MCP servers (postgres, custom stdio servers, anything in <profile>/config.yaml) never registered. Root cause: api/streaming.py:1922 called discover_mcp_tools() at the TOP of _run_agent_streaming(), about 100 lines BEFORE the per-session 'os.environ["HERMES_HOME"] = _profile_home' mutation at line 2053. discover_mcp_tools() reads ~/.hermes/config.yaml via get_hermes_home(), which uses os.environ['HERMES_HOME']. So at the call site, HERMES_HOME was still whatever the WebUI server process had at startup — the default profile, every time. Fix: relocate the discover_mcp_tools() call past the _ENV_LOCK block so get_hermes_home() resolves to the session's actual profile home. Same try/except wrapping is preserved; same idempotency semantics on already-connected servers; same lazy-import pattern. Caveat (out of scope, agent-side): _servers in tools/mcp_tool.py is a process-global Dict[str, MCPServerTask] keyed only by server name. So once profile A registers a server named e.g. 'postgres', profile B's discovery sees 'postgres' as already connected and skips it — even if B's config points at a different binary or DB. Concurrent multi-profile WebUI processes will still hit 'first profile wins per server name'. Fully fixing that requires keying _servers by (profile_home, name) upstream in hermes-agent. This PR ships layer 1 only — fixes the single-non-default-profile case (the headline symptom). Tests: tests/test_issue1968_mcp_profile_discovery.py — 4 static tests pinning the lexical ordering invariants. Verified mutation-safety: a proof-of-concept revert (re-adding a discover call before the HERMES_HOME mutation) makes the 'only called once' test fail. Test suite: 5047 passed, 4 skipped, 3 xpassed, 0 regressions. Closes #1968 |
||
|
|
10ea2a014f |
fix(kanban): header '+' button opens create-task modal
The Kanban sidebar panel's header '+' button (#kanbanNewTaskBtn) was wired straight to createKanbanTask(), which reads the inline #kanbanNewTaskTitle input and silently returns when empty. The inline input lives below five rows of filters (search, assignee, tenant, archived/mine toggles, stats, bulk-action bar) and is typically off-screen on first panel open, so the header button looked dead — clicking it with no title typed did nothing visible (no modal, no scroll, no focus shift, no toast). Now the header '+' opens #kanbanTaskModal — a centered overlay with the same .kanban-modal-overlay shell the existing create-board modal uses, so the two flows look and behave identically (centered card, dim backdrop, ESC closes, click-on-backdrop closes). The modal exposes the fields the backend already accepts at /api/kanban/tasks: Title, Description, Status (Triage/Todo/Ready), Priority, Assignee (datalist suggestions from the active board), Tenant (datalist). UX details: - Title is required; submit-with-empty shows a properly styled red error - Title field auto-focuses on open - ESC closes the modal; backdrop click closes; Enter on simple inputs submits, Enter in the description textarea inserts a newline - Submit POSTs only the fields the user filled in (no forced empty strings) and auto-opens the new task's detail view - Submit button disables while posting to prevent double-submit - Inline quick-add (Enter on #kanbanNewTaskTitle) is preserved as a power-user shortcut Side effect: .kanban-modal-error styling improved (proper red alert with border + tinted background) so the existing create-board modal benefits from the same polish for free. i18n: 11 new keys added across all 8 supported locales (en, ja, ru, es, de, zh, pt, ko). Tests: tests/test_kanban_ui_static.py::test_kanban_new_task_header_button_opens_modal covers the modal markup, button wiring, ESC/Enter handling, datalist population, submit behavior, and inline-quick-add fallthrough. Verified end-to-end in the browser on an isolated test env (port 8789): created a board from scratch, opened the modal via header '+', submitted with title/description/status/priority/assignee/tenant filled in, moved the task through statuses (Triage → Todo → Ready → Blocked → Archived), added a comment, verified Cancel + ESC + backdrop-click all close cleanly, verified validation error rendering, verified inline quick-add still works. Closes #1964 |