fix(llm,settings): refuse post-disposal route replacement and teardown notifications

Two lifecycle holes the registry and the consumer helper left open.

`AdapterRegistrationHandle.replace` had no liveness guard: after the handle's
disposer ran, a replace put routes back into the registry with nothing left to
release them, so the adapter leaked permanently. `owned` being empty cannot
carry that fact, because `replace([])` is the legal empty-section state, so the
disposer records it explicitly.

`installSettingsSection`'s watcher lacked the guard its own disposer carries:
a stored change landing while the consumer unloads reached `onChange`, which
re-registers routes against a fiber whose resources are being released.

Also documents `withFileLock` in the atomic-write README (it claimed one
export), records the age-based lock takeover as a known limitation, and lists
ctx.settings and ctx.credentials in the architecture capability table.
This commit is contained in:
Yichen Jiang
2026-07-30 23:23:18 +08:00
parent 80c36ff0d3
commit 54c60f4079
14 changed files with 119 additions and 11 deletions
+5
View File
@@ -612,6 +612,11 @@ export function installSettingsSection<T>(
})
hooks.onChange()
scope.watch(() => {
// A stored change landing while the consumer unloads reaches the watcher
// before the registration is released, and `onChange` is exactly as
// harmful here as in the disposer above: it re-registers routes against
// a fiber whose resources are being let go.
if (isUnloading(ctx)) return
hooks.onChange()
})
})