Kanban assignee case sensitivity and dropdown UI

NousResearch/hermes-agent · cluster 18498 · dashboard

Data as of

2 open items. Fix merged in #19700. Check it covers your case.

The short answer

2open issues
0open PRs
9items grouped
Known?
Yes. 9 related items grouped here, 3 issues already closed.
Fix event
A fix was merged to main in PR #19700. Check it covers your case. No release data is tracked.
Tracker
The maintainers' tracker marks this partially resolved, deciding PRs #19700, #23235. That is separate from the fix event.
Still open
2 issues and 0 PRs.

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.

Open issues2

  • Issue#20875[Feature] Kanban: Add assignee dropdown to task creation form
  • Issue#30437Kanban dashboard: assignee field should be a dropdown populated from profiles (not freeform text)

Open PRs0

No open pull requests.

Fixes on record

Merged to main. A stamp counts the issues a fix is linked to.

  • #19700 fix(profiles,kanban): normalize profile IDs for assignees (salvage of #18518)

    merged to main

    files: 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

  • #23235 fix(kanban): preserve assignee casing in dashboard (salvage #21459)

    merged to main

    files: plugins/kanban/dashboard/dist/index.js, plugins/kanban/dashboard/dist/style.css, scripts/release.py, tests/plugins/test_kanban_dashboard_plugin.py

    Show the diff
    • plugins/kanban/dashboard/dist/index.js
      @@ -1924,6 +1924,10 @@
                 title: props.columnName === "triage"
                   ? "Hermes profile that will spec this task (default: the dispatcher's configured specifier). Leave blank to let the dispatcher pick."
                   : "Hermes profile to assign. Leave blank and the dispatcher will pick from available profiles when the task is Ready.",
      +          style: { textTransform: "none" },
      +          autoCapitalize: "none",
      +          autoCorrect: "off",
      +          spellCheck: false,
               }),
               h(Input, {
                 type: "number",
      @@ -2517,6 +2521,10 @@
               },
               placeholder: tx(t, "emptyAssignee", "(empty = unassign)"),
               className: "h-7 text-xs flex-1",
      +        style: { textTransform: "none" },
      +        autoCapitalize: "none",
      +        autoCorrect: "off",
      +        spellCheck: false,
             }),
           );
         }
    • plugins/kanban/dashboard/dist/style.css
      @@ -202,8 +202,10 @@
         align-items: center;
         gap: 0.4rem;
         font-size: 0.65rem;
      -  text-transform: uppercase;
      -  letter-spacing: 0.08em;
      +  /* Assignee/profile names are case-sensitive. Do not visually uppercase
      +   * lane headers, otherwise a valid `analyst` profile appears as `ANALYST`
      +   * in the WebUI and users may copy the wrong casing back into edits. */
      +  letter-spacing: 0.02em;
         color: var(--color-muted-foreground);
         padding: 0 0.1rem;
       }
    • scripts/release.py
      @@ -713,6 +713,7 @@
           "[redacted:email]": "tymrtn",
           "[redacted:email]": "kallidean",
           "[redacted:email]": "baocin",
      +    "[redacted:email]": "princepal9120",
           "[redacted:email]": "sicnuyudidi",
           "[redacted:email]": "haimu0x",
           "[redacted:email]": "NIDNASSER-Abdelmajid",
    • tests/plugins/test_kanban_dashboard_plugin.py
      @@ -1723,3 +1723,65 @@ def test_dashboard_requests_default_board_explicitly():
           assert "SDK.fetchJSON(withBoard(`${API}/config`, board))" in dist
           assert "SDK.fetchJSON(withBoard(`${API}/boards`, board))" in dist
           assert "}, [loadBoardList, switchBoard, board]);" in dist
      +
      +
      +def test_dashboard_assignee_inputs_preserve_casing():
      +    """Assignee/specifier inputs must disable browser auto-capitalization.
      +
      +    Hermes profile names are case-sensitive — the dispatcher uses the
      +    assignee string as a literal directory/profile lookup. Mobile browsers
      +    (iOS/Android) and some IMEs auto-capitalize the first letter of any
      +    text input by default, so a user typing ``analyst`` ends up submitting
      +    ``Analyst`` and the dispatcher fails to spawn a matching profile,
      +    leading to the crash loop reported in #21320.
      +
      +    The fix sets ``autoCapitalize="none"``, ``autoCorrect="off"``,
      +    ``spellCheck=false``, and ``style={textTransform: "none"}`` on the
      +    two assignee ``<Input>`` elements (inline triage/lane create-task
      +    input + task-edit panel "(empty = unassign)" input).
      +
      +    This test pins those attributes in the compiled dist bundle so a
      +    future rebuild that loses them fails CI immediately.
      +    """
      +    repo_root = Path(__file__).resolve().parents[2]
      +    dist = (repo_root / "plugins" / "kanban" / "dashboard" / "dist" / "index.js").read_text()
      +
      +    # Both sites should have all four attributes. Count occurrences to
      +    # ensure both inputs got the treatment, not just one.
      +    assert dist.count('autoCapitalize: "none"') >= 2, (
      +        "Expected autoCapitalize=\"none\" on both assignee inputs (inline "
      +        "create + task-edit panel)"
      +    )
      +    assert dist.count('autoCorrect: "off"') >= 2
      +    assert dist.count("spellCheck: false") >= 2
      +    assert dist.count('textTransform: "none"') >= 2
      +
      +
      +def test_dashboard_lane_head_preserves_assignee_casing():
      +    """Lane headers must not visually uppercase profile names.
      +
      +    The previous CSS rule ``.hermes-kanban-lane-head { text-transform:
      +    uppercase; letter-spacing: 0.08em }`` made a valid ``analyst`` profile
      +    appear as ``ANALYST`` in column headers; users then copied the
      +    uppercase form back into edits, hitting the same crash loop as the
      +    auto-capitalization path. The fix removes ``text-transform: uppercase``
      +    from the rule and tightens letter-spacing.
      +
      +    Static-asset regression test for the rule contents.
      +    """
      +    repo_root = Path(__file__).resolve().parents[2]
      +    style = (repo_root / "plugins" / "kanban" / "dashboard" / "dist" / "style.css").read_text()
      +
      +    # Locate the .hermes-kanban-lane-head block. Use a generous slice to
      +    # keep this resilient to nearby unrelated CSS edits.
      +    marker = ".hermes-kanban-lane-head {"
      +    idx = style.find(marker)
      +    assert idx != -1, "could not locate .hermes-kanban-lane-head rule"
      +    end = style.find("}", idx)
      +    assert end != -1
      +    rule = style[idx:end]
      +
      +    assert "text-transform: uppercase" not in rule, (
      +        "Lane head must not visually uppercase profile names — see #21320 "
      +        "and the explanatory comment in the CSS rule."
      +    )

2 PRs closed without merging

From the close comments: merged via another change 2

  • #18515 fix(kanban): normalize profile names to lowercase in assign_task · merged via another change → #18518, #19700
  • #21459 fix(kanban): preserve assignee casing in dashboard · merged via another change → #23235, #21320, #1
Closed issues · 3
  • Closed#18498[Bug]: Kanban dispatcher fails to claim tasks assigned to "jules" due to case sensitivity in profile validation
  • Closed#21320Kanban WebUI silently converts input to uppercase, causing profile mismatch crash loops with no notification
  • Closed#21506[Feature]: Kanban assignee should be a dropdown select instead of free text input
How this cluster was grouped
  • Link evidence (partly structural links): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
  • Members outside that structural core may belong to a different underlying problem.
  • Pairwise link density 0.16: the share of item pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. 1.00 means every pair is linked.

Pick up a Lab task

Check whether a fix covers the open issues here, with your own agent. Bring back evidence for maintainers.

Open the Lab

At a glance

Subsystem
dashboard
Merged PRs
#19700, #23235
Closed issues
3
Attempts
2 PRs closed without merging
Fix link checked
10 May 2026

For your agent

The same facts, typed.