Production crash on the hub panel (recurring, restart-masked), hit live during #955's cutover smoke:
TypeError: Cannot read properties of undefined (reading 'startsWith') — packages/session-ui/src/components/tool-count-label.tsx.
Mechanism: the label renders via split(i18n.t(pluralKey(props.plural, "one"))). When the UI i18n dict hasn't loaded yet (async resource) or the key is missing, i18n.t(...) returns undefined. split(undefined) coerces through /{{\s*count\s*}}/.exec(undefined) → no match → returns {before: "", after: undefined} → the memos' one().after.startsWith(...) throws inside a Solid effect. Cold page load throws; warm reload (cached dict) masks it — matching the observed "restarting fixes it, it comes back."
Fix: guard split() against falsy text — return {before: "", after: ""} (honest empty render until the translation exists, the stock language.tsx fallback discipline). Implemented as an app-bundle overlay twin (the #848/#862 pattern): overlay/packages/session-ui/src/components/tool-count-label.tsx, manifest re-registered via refresh_manifest, drift gate green.
Context: #955 (hub cutover), #964 (headless contract-test precedent).
Production crash on the hub panel (recurring, restart-masked), hit live during #955's cutover smoke:
TypeError: Cannot read properties of undefined (reading 'startsWith')—packages/session-ui/src/components/tool-count-label.tsx.Mechanism: the label renders via
split(i18n.t(pluralKey(props.plural, "one"))). When the UI i18n dict hasn't loaded yet (async resource) or the key is missing,i18n.t(...)returnsundefined.split(undefined)coerces through/{{\s*count\s*}}/.exec(undefined)→ no match → returns{before: "", after: undefined}→ the memos'one().after.startsWith(...)throws inside a Solid effect. Cold page load throws; warm reload (cached dict) masks it — matching the observed "restarting fixes it, it comes back."Fix: guard
split()against falsy text — return{before: "", after: ""}(honest empty render until the translation exists, the stock language.tsx fallback discipline). Implemented as an app-bundle overlay twin (the #848/#862 pattern):overlay/packages/session-ui/src/components/tool-count-label.tsx, manifest re-registered via refresh_manifest, drift gate green.Context: #955 (hub cutover), #964 (headless contract-test precedent).