feat: the card shows a user, not a slot number - #1458
Merged
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## v3 #1458 +/- ##
==========================================
- Coverage 99.05% 99.05% -0.01%
==========================================
Files 60 60
Lines 7501 7477 -24
Branches 489 484 -5
==========================================
- Hits 7430 7406 -24
Misses 71 71
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
The three commands a card sends -- subscribe_code_slot, set_slot_condition, clear_slot_condition -- took a slot number and nothing else. They now take a name as well, which is what every other surface moved to. The slot number stays so existing callers keep working, and is expected to go. Names are matched slugified, the way a config entry title already is: a caller holding only the slug an entity id was built from can still name its user. Slugifying collapses more than the name rules do, though -- "Ada-Lovelace" and "Ada Lovelace" are two users under those rules and one slug under this. Where that happens the command is refused and names both, rather than picking one and writing somebody else's credential. A user the allocator has not numbered yet is refused for the same reason: there is nothing on any lock to address. Resolution happens at the websocket boundary rather than deeper down: a slot number is still what the coordinator, the entities and the provider key on, so this is where the two vocabularies meet. clear_slot_condition gained a failure path it did not have before, so it gained the error handling to match. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XDxiHpQJkRKWctS9BfQmbY Entire-Checkpoint: 0f359e698aca
The slot card was the last place a slot number faced the user. It is registered as custom:lcm-user now, takes `name` instead of `slot`, and the editor asks for a user rather than a number. Not a second card. The old one was 1,450 lines of which `slot` appeared in three -- all message payloads, none of them rendering -- so a separate implementation would have been three payloads' worth of difference and 3,500 lines of duplication to maintain. custom:lcm-slot stays registered as a subclass that warns and defers, so dashboards holding one keep working until it is removed. A card carrying both a name and a slot number would keep showing whoever held that number regardless of the name, so the editor drops the number when a name is set, and the card sends one or the other from a single place rather than deciding per command. The strategy passes the user's name down to the section, which passes it to the card; where a user has no name yet the slot number still goes through, because something has to address them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XDxiHpQJkRKWctS9BfQmbY Entire-Checkpoint: cf120f3d01cf
subscribe_code_slot was the only one of the three commands without the service-error wrapper, so a name that did not resolve escaped as an ERROR log line rather than an answer. The editor dispatches on every keystroke, so typing a name produced one per character. set_slot_condition sniffed its own message text for "not found", which none of the resolver's wordings contain, so the same input came back as an unknown error there and a not-found from its sibling. It now catches the resolver explicitly. clear_slot_condition's try was dead -- the decorator above it already did exactly that. The card picker was broken: the stub config named a real entry with an empty name, and only the literal id "stub" suppressed subscribing, so adding the card subscribed for nobody and rendered an error where the preview goes. A card with no addressee is a stub now, whatever entry it names, and naming nobody is no longer an error at all -- that belongs in the editor, not where a preview belongs. A name that is not a string still is one. _addressee tested `!== undefined` where both other producers test truthiness, so an empty or null name sent an unusable addressee and ignored a perfectly good slot number. Clearing the name in the editor no longer takes the slot number with it, leaving the card addressing nothing. Also drops the lcm-slot-editor registration: the deprecated card inherits getConfigElement from the user card, so nothing ever asked for it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XDxiHpQJkRKWctS9BfQmbY Entire-Checkpoint: f74d3b7f7a74
The card was addressed by the user's name, and the card has an inline name editor. Renaming a user on the card left its own stored config naming somebody who no longer exists: the live subscription survived, having resolved the slot once, so nothing looked wrong until the next page load, when the card came back dead. Strategy-generated cards heal themselves on every render; a hand-placed one does not, and the editor had deliberately dropped the slot number that used to hold it together. That is the mistake this project has made before, one layer up. A stored identifier must not be a value the user can change, and the name is now the most changeable value there is. So a card is addressed by an entity of its user. Entity IDs are unique by construction and this integration's do not move on a rename -- unique IDs keep the slot number, which is the whole reason a rename costs nothing. The commands take user_entity_id, named apart from entity_id because on the condition commands that already means the condition entity itself. It also settles the ambiguity the name matching introduced: two users whose names collapse to one slug broke both their generated cards, since the resolver refuses rather than guess. Entity IDs cannot collide, so the generated dashboard no longer has the problem to have. The name stays for anyone writing a card by hand, where it is far easier to type than an entity id, and the editor still clears the other addressees when one is set -- a card carrying two would show whoever the other one pointed at. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XDxiHpQJkRKWctS9BfQmbY Entire-Checkpoint: 2ef94448e4a4
The loop that picks an entity per slot had no test reaching it: every fixture passed an empty `entities` list, so the map was always empty and sections went out addressed only by name. It dropped this file from 98.6% line coverage to 94.9% and nothing failed, because the fallback is the name and the name still worked. Covers both branches: a slot with entities gets the first one seen, which is what keeps the choice stable across renders rather than dependent on registry order, and a slot with none is left to its name and number -- reachable while a slot is being set up, when the configuration knows the user before any entity for them exists. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XDxiHpQJkRKWctS9BfQmbY Entire-Checkpoint: 709ca09d8ddc
raman325
force-pushed
the
feat/user-centric-card
branch
from
August 20, 2026 14:43
05a3521 to
ecc2813
Compare
* origin/v3: feat: the entity-ID rename is a notification, not a repair (#1460)
The header read "Slot 1 · All Locks", which was the last place a slot number faced the user -- and once the card began addressing itself by an entity the number was not even set, so it rendered "Slot undefined". Taking the title out left an icon and a state chip alone in a bordered bar, saying the same thing twice: the card already tints its background by state and the chip already names it. So the bar is gone. The icon moved to the head of the row that carries the user's name, where it reads as the card's avatar rather than a second state indicator, and the chip sits at the end of that row. The name was already there, already editable, already larger than the title above it. It is the card's heading now, in the literal sense too -- dissolving the bar took the <h2> with it, and a screen reader lost the card's title until it was put back on the name. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XDxiHpQJkRKWctS9BfQmbY Entire-Checkpoint: 344fb26915cd
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Proposed change
The slot card was the last place a slot number faced the user. It is now
custom:lcm-user, takesnameinstead ofslot, and its editor asks for a user.Not a second card
The existing card is 1,450 lines, and
slotappears in three of them — all message payloads, none of them rendering. A separate user card would have been three payloads' worth of difference against ~3,500 lines of card, editor and styles duplicated forever. Solcm-useris the implementation, andcustom:lcm-slotstays registered as a subclass that warns and defers, so existing dashboards keep working until it's removed.Backend
subscribe_code_slot,set_slot_conditionandclear_slot_conditionnow acceptnameas well asslot, matched slugified the way a config entry title already is — a caller holding only the slug an entity ID was built from can still name its user.Two refusals rather than guesses, because this path writes to a lock:
Ada-LovelaceandAda Lovelaceare two users under the name rules and one slug under this. The command fails and names both rather than putting a PIN on the wrong person. (Deliberately stricter than entry-title matching, where first match wins.)assignment.slot()returnsint | None— mypy caught this — because a user can exist before allocation numbers them. There's nothing on any lock to address.Resolution happens at the websocket boundary: a slot number is still what the coordinator, entities and providers key on, so that's where the two vocabularies meet.
One detail worth reviewing
A card carrying both a name and a slot number would keep showing whoever held that number, whatever the name said. The editor therefore drops the number when a name is set, and the card picks one addressee in a single place rather than deciding per command.
Type of change
Additional information
custom:lcm-slotis deprecated here and scheduled for removal in a future release; theslotkey on the websocket commands goes with it.Checklist
🤖 Generated with Claude Code
https://claude.ai/code/session_01XDxiHpQJkRKWctS9BfQmbY