{"repo":"NousResearch/hermes-agent","public_id":"18498","display_label":"Kanban assignee case sensitivity and dropdown UI","summary":"These issues and PRs address case sensitivity mismatches in Kanban assignee/profile handling, along with requests to replace the freeform assignee text input with a dropdown populated from existing profiles. Multiple fix attempts and salvage PRs suggest the normalization approach has been revisited more than once.","subsystem":"dashboard","counts":{"members":9,"open_issues":2,"open_issues_distinct":2,"open_prs":0,"closed_issues":3,"merged_prs":2,"attempt_prs":2},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":19700},"resolution":{"state":"partially_resolved","by":"pr-19700,pr-23235"},"freshness":{"state":"verified","last_verified_at":"2026-05-10T14:35:01+00:00","is_stale":false},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.16111111111111112},"fix_edges":[{"issue_number":18498,"pr_number":19700,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true},{"issue_number":21320,"pr_number":23235,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true}],"members":[{"kind":"issue","number":18498,"title":"[Bug]: Kanban dispatcher fails to claim tasks assigned to \"jules\" due to case sensitivity in profile validation","url":null,"state":"closed"},{"kind":"issue","number":20875,"title":"[Feature] Kanban: Add assignee dropdown to task creation form","url":null,"state":"open"},{"kind":"issue","number":21320,"title":"Kanban WebUI silently converts input to uppercase, causing profile mismatch crash loops with no notification","url":null,"state":"closed"},{"kind":"issue","number":21506,"title":"[Feature]: Kanban assignee should be a dropdown select instead of free text input","url":null,"state":"closed"},{"kind":"issue","number":30437,"title":"Kanban dashboard: assignee field should be a dropdown populated from profiles (not freeform text)","url":null,"state":"open"},{"kind":"pr","number":18515,"title":"fix(kanban): normalize profile names to lowercase in assign_task","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#18518","number":18518},{"kind":"pr","value":"#19700","number":19700},{"kind":"pr","value":"#18518","number":18518}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":19700,"title":"fix(profiles,kanban): normalize profile IDs for assignees (salvage of #18518)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["hermes_cli/kanban_db.py","hermes_cli/profiles.py","scripts/release.py","tests/hermes_cli/test_kanban_db.py","tests/hermes_cli/test_profiles.py"]},{"kind":"pr","number":21459,"title":"fix(kanban): preserve assignee casing in dashboard","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#23235","number":23235},{"kind":"pr","value":"#21320","number":21320},{"kind":"pr","value":"#1","number":1}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":23235,"title":"fix(kanban): preserve assignee casing in dashboard (salvage #21459)","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["plugins/kanban/dashboard/dist/index.js","plugins/kanban/dashboard/dist/style.css","scripts/release.py","tests/plugins/test_kanban_dashboard_plugin.py"],"fix_hunks":[{"file":"plugins/kanban/dashboard/dist/index.js","hunks":["@@ -1924,6 +1924,10 @@\n           title: props.columnName === \"triage\"\n             ? \"Hermes profile that will spec this task (default: the dispatcher's configured specifier). Leave blank to let the dispatcher pick.\"\n             : \"Hermes profile to assign. Leave blank and the dispatcher will pick from available profiles when the task is Ready.\",\n+          style: { textTransform: \"none\" },\n+          autoCapitalize: \"none\",\n+          autoCorrect: \"off\",\n+          spellCheck: false,\n         }),\n         h(Input, {\n           type: \"number\",","@@ -2517,6 +2521,10 @@\n         },\n         placeholder: tx(t, \"emptyAssignee\", \"(empty = unassign)\"),\n         className: \"h-7 text-xs flex-1\",\n+        style: { textTransform: \"none\" },\n+        autoCapitalize: \"none\",\n+        autoCorrect: \"off\",\n+        spellCheck: false,\n       }),\n     );\n   }"]},{"file":"plugins/kanban/dashboard/dist/style.css","hunks":["@@ -202,8 +202,10 @@\n   align-items: center;\n   gap: 0.4rem;\n   font-size: 0.65rem;\n-  text-transform: uppercase;\n-  letter-spacing: 0.08em;\n+  /* Assignee/profile names are case-sensitive. Do not visually uppercase\n+   * lane headers, otherwise a valid `analyst` profile appears as `ANALYST`\n+   * in the WebUI and users may copy the wrong casing back into edits. */\n+  letter-spacing: 0.02em;\n   color: var(--color-muted-foreground);\n   padding: 0 0.1rem;\n }"]},{"file":"scripts/release.py","hunks":["@@ -713,6 +713,7 @@\n     \"[redacted:email]\": \"tymrtn\",\n     \"[redacted:email]\": \"kallidean\",\n     \"[redacted:email]\": \"baocin\",\n+    \"[redacted:email]\": \"princepal9120\",\n     \"[redacted:email]\": \"sicnuyudidi\",\n     \"[redacted:email]\": \"haimu0x\",\n     \"[redacted:email]\": \"NIDNASSER-Abdelmajid\","]},{"file":"tests/plugins/test_kanban_dashboard_plugin.py","hunks":["@@ -1723,3 +1723,65 @@ def test_dashboard_requests_default_board_explicitly():\n     assert \"SDK.fetchJSON(withBoard(`${API}/config`, board))\" in dist\n     assert \"SDK.fetchJSON(withBoard(`${API}/boards`, board))\" in dist\n     assert \"}, [loadBoardList, switchBoard, board]);\" in dist\n+\n+\n+def test_dashboard_assignee_inputs_preserve_casing():\n+    \"\"\"Assignee/specifier inputs must disable browser auto-capitalization.\n+\n+    Hermes profile names are case-sensitive — the dispatcher uses the\n+    assignee string as a literal directory/profile lookup. Mobile browsers\n+    (iOS/Android) and some IMEs auto-capitalize the first letter of any\n+    text input by default, so a user typing ``analyst`` ends up submitting\n+    ``Analyst`` and the dispatcher fails to spawn a matching profile,\n+    leading to the crash loop reported in #21320.\n+\n+    The fix sets ``autoCapitalize=\"none\"``, ``autoCorrect=\"off\"``,\n+    ``spellCheck=false``, and ``style={textTransform: \"none\"}`` on the\n+    two assignee ``<Input>`` elements (inline triage/lane create-task\n+    input + task-edit panel \"(empty = unassign)\" input).\n+\n+    This test pins those attributes in the compiled dist bundle so a\n+    future rebuild that loses them fails CI immediately.\n+    \"\"\"\n+    repo_root = Path(__file__).resolve().parents[2]\n+    dist = (repo_root / \"plugins\" / \"kanban\" / \"dashboard\" / \"dist\" / \"index.js\").read_text()\n+\n+    # Both sites should have all four attributes. Count occurrences to\n+    # ensure both inputs got the treatment, not just one.\n+    assert dist.count('autoCapitalize: \"none\"') >= 2, (\n+        \"Expected autoCapitalize=\\\"none\\\" on both assignee inputs (inline \"\n+        \"create + task-edit panel)\"\n+    )\n+    assert dist.count('autoCorrect: \"off\"') >= 2\n+    assert dist.count(\"spellCheck: false\") >= 2\n+    assert dist.count('textTransform: \"none\"') >= 2\n+\n+\n+def test_dashboard_lane_head_preserves_assignee_casing():\n+    \"\"\"Lane headers must not visually uppercase profile names.\n+\n+    The previous CSS rule ``.hermes-kanban-lane-head { text-transform:\n+    uppercase; letter-spacing: 0.08em }`` made a valid ``analyst`` profile\n+    appear as ``ANALYST`` in column headers; users then copied the\n+    uppercase form back into edits, hitting the same crash loop as the\n+    auto-capitalization path. The fix removes ``text-transform: uppercase``\n+    from the rule and tightens letter-spacing.\n+\n+    Static-asset regression test for the rule contents.\n+    \"\"\"\n+    repo_root = Path(__file__).resolve().parents[2]\n+    style = (repo_root / \"plugins\" / \"kanban\" / \"dashboard\" / \"dist\" / \"style.css\").read_text()\n+\n+    # Locate the .hermes-kanban-lane-head block. Use a generous slice to\n+    # keep this resilient to nearby unrelated CSS edits.\n+    marker = \".hermes-kanban-lane-head {\"\n+    idx = style.find(marker)\n+    assert idx != -1, \"could not locate .hermes-kanban-lane-head rule\"\n+    end = style.find(\"}\", idx)\n+    assert end != -1\n+    rule = style[idx:end]\n+\n+    assert \"text-transform: uppercase\" not in rule, (\n+        \"Lane head must not visually uppercase profile names — see #21320 \"\n+        \"and the explanatory comment in the CSS rule.\"\n+    )\n"]}]}],"trust":{"default_tier":"verified","includes_suggested":false}}