fix(client): contain notification-callback failures and document the source lifecycle

Review follow-ups: the three new notify loops (currentProvideInfo
subscribers, ui-skill lexicon listeners, late-registration controller
setup) now contain per-callback failures so one faulty consumer cannot
starve the rest, abort the list projection pass, or poison the source
roster with no disposer; controller lexicon polling drops a throwing
source with a console record like the candidate path. The ui-slash
README (both languages) now states the late-registration warm and the
subscribeLexicon contract, and the scenario suite drives a typed /name
token gaining its decoration when the roll settles with no further
input.
This commit is contained in:
imccyu
2026-07-28 14:33:37 +08:00
parent f331f248d8
commit d833be412a
8 changed files with 74 additions and 9 deletions

View File

@@ -290,7 +290,16 @@ export class SlashController {
const rolls = new Map<TriggerChar, readonly string[]>()
for (const src of this.deps.roster.all()) {
if (src.lexicon === undefined) continue
const names = src.lexicon(projection)
let names: readonly string[] | undefined
try {
names = src.lexicon(projection)
} catch (error) {
// A faulty source drops silently with a console record (the
// candidate-fetch failure policy); the refresh runs inside
// notification callbacks, where a throw would starve other consumers.
console.error(`[ui-slash] source "${src.name}" lexicon failed:`, error)
continue
}
if (names === undefined) continue
const prev = rolls.get(src.trigger)
rolls.set(src.trigger, prev === undefined ? names : [...prev, ...names])

View File

@@ -50,7 +50,16 @@ export class SlashService extends Service implements SlashServiceContract {
throw new Error(`slash source "${src.trigger}${src.name}" is already registered`)
}
live.sources.push(src)
for (const controller of live.controllers.values()) controller.sourceAdded(src)
for (const controller of live.controllers.values()) {
try {
controller.sourceAdded(src)
} catch (error) {
// Contain faulty source callbacks (warm/subscribeLexicon): the
// registration must stand with a usable disposer and the remaining
// controllers must still be notified.
console.error(`[ui-slash] source "${src.trigger}${src.name}" late-registration setup failed:`, error)
}
}
return () => {
const at = live.sources.indexOf(src)
if (at < 0) return