Problem / Why
The indexer setter currently assigns if the key is present and silently does nothing otherwise; it never inserts. That is a legitimate design, but it is undocumented and is easy to misread as an upsert, which is exactly the misuse feature #464 had to delete from EfcItemController (issue #459). Until the contract is stated, the same misuse will recur. Feature #444, which owns KbdActions.cs, deliberately declined to change Remove's shape and left this question open.
Proposed Behavior
State the intended contract explicitly in XML documentation on the indexer, then make the implementation match it. If assign-if-present is intended, consider making the silent no-op observable (a return value or a documented precondition) so a caller cannot mistake failure for success. If upsert is intended, change the implementation and audit every call site.
Acceptance Criteria
Constraints & Risks
KbdActions.cs is owned by feature #444 and is consumed across several controllers, so a semantic change has a wide blast radius. Two distinct equality paths are load-bearing and must not be collapsed: Add/Remove and the duplicate guard use StoredKeyEquals (EqualityComparer<TKey>.Default), while Find/FindIndex/ContainsKey/FilterKeys use the element-defined KeyEquals. KaStringAsync.KeyEquals is substring-matching and side-effecting, and its early return must not be turned into a fall-through.
Test Conditions
Source
From: docs/features/potential/2026-08-27-kbdactions-indexer-setter-contract-decision.md
Problem / Why
The indexer setter currently assigns if the key is present and silently does nothing otherwise; it never inserts. That is a legitimate design, but it is undocumented and is easy to misread as an upsert, which is exactly the misuse feature #464 had to delete from
EfcItemController(issue #459). Until the contract is stated, the same misuse will recur. Feature #444, which ownsKbdActions.cs, deliberately declined to changeRemove's shape and left this question open.Proposed Behavior
State the intended contract explicitly in XML documentation on the indexer, then make the implementation match it. If assign-if-present is intended, consider making the silent no-op observable (a return value or a documented precondition) so a caller cannot mistake failure for success. If upsert is intended, change the implementation and audit every call site.
Acceptance Criteria
overwriteDuplicatestruth table is documented alongside the indexer contract.Constraints & Risks
KbdActions.csis owned by feature #444 and is consumed across several controllers, so a semantic change has a wide blast radius. Two distinct equality paths are load-bearing and must not be collapsed:Add/Removeand the duplicate guard useStoredKeyEquals(EqualityComparer<TKey>.Default), whileFind/FindIndex/ContainsKey/FilterKeysuse the element-definedKeyEquals.KaStringAsync.KeyEqualsis substring-matching and side-effecting, and its early return must not be turned into a fall-through.Test Conditions
StoredKeyEqualsversusKeyEqualsdistinctionArgumentExceptionon a repeated(SourceId, StoredKey)pairSource
From: docs/features/potential/2026-08-27-kbdactions-indexer-setter-contract-decision.md