Skip to content

Fail loudly when the base context is read outside its scope (v1.6.4) - #9

Merged
christophdb merged 1 commit into
mainfrom
fix/contextual-client-scope-guard
Sep 4, 2026
Merged

christophdb merged 1 commit into
mainfrom
fix/contextual-client-scope-guard

Conversation

@christophdb

Copy link
Copy Markdown
Member

Nachtrag zu #5. Der hat die eine Stelle repariert, die den Client nach dem runWithBase()-Scope las. Dieser PR sorgt dafür, dass der nächste solche Fehler sich meldet, statt sich zu verstecken.

Warum der Bug fünf Monate überlebt hat

ContextualClient löste über registry.resolve(baseContext.getStore()) auf. Der Store war der nackte Base-Name, also kam „gar kein Scope" und „im Scope, kein Name angegeben" beide als undefined an. Daraus folgten zwei Verhaltensweisen, die beide falsch sind:

Bases konfiguriert Zugriff außerhalb des Scopes
1 lief still durch (Registry-Default) Fehler bleibt latent
≥ 2 Multiple bases available … Specify "base" parameter beschuldigt den Aufrufer eines Arguments, das er gesetzt hatte

Die erste Zeile ist der Grund, warum es niemandem auffiel: Die Konfiguration, die den Fehler zeigt, fahren wir selbst nicht — managed mode baut gar keinen ContextualClient, Mock und Single-Base auch nicht. Die zweite Zeile ist der Grund, warum es als Bedienfehler durchging.

Änderung

Der Store trägt jetzt { base } statt des nackten Namens. Damit ist ein fehlender Store unterscheidbar, und ein Zugriff außerhalb des Scopes wirft:

ContextualClient was used outside runWithBase(). The base context only exists
for the duration of that scope — read what you need inside it rather than
after it returns.

Gleich, egal wie viele Bases konfiguriert sind. Die berechtigte Meldung (Specify "base" parameter) bleibt erhalten, wo sie hingehört: innerhalb des Scopes ohne Base-Argument.

Verifikation

  • Drei neue Tests in tests/contextualClient.spec.ts, zuerst rot geschrieben. Der aufschlussreichste ist der Single-Base-Fall: der warf vorher gar nicht.
  • Volle Suite 334/334 grün, lint und typecheck sauber. Die Absicherung deckt keinen weiteren Verstoß im Bestand auf — deckungsgleich mit der statischen Prüfung (einziger runWithBase-Aufrufer ist handleCallTool; alle Client-Zugriffe der 21 Tools liegen in der Handler-Closure; keine setTimeout/Event-Callbacks, die den Kontext verlassen; keine lazy konsumierten Rückgaben).
  • Dynamische Gegenprobe über alle 22 Tools gegen zwei Bases: 0 Lecks (auf main vor Fix: every tool call fails in multi-base mode with two or more bases #5 waren es 22 von 22).

🤖 Generated with Claude Code

https://claude.ai/code/session_01Tn4FT2sVqoFz1QdpNDNAy7

#5 fixed the one place that read the client after runWithBase() had returned.
This makes the next such mistake announce itself instead of hiding.

ContextualClient resolved through registry.resolve(getStore()), which could not
tell "no scope at all" from "in a scope, no base named" — both arrived as
undefined. With a single base the out-of-scope read then quietly succeeded via
the registry default; with two or more it threw `Multiple bases available …
Specify "base" parameter`, blaming the caller for omitting an argument they had
supplied. That combination is what let the defect survive five months: the
configuration that would have exposed it is not the one we run.

The store now carries { base } instead of the bare name, so an absent store is
detectable, and reading outside the scope throws a message that names the
actual error — the same way regardless of how many bases are configured.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Tn4FT2sVqoFz1QdpNDNAy7
@christophdb
christophdb merged commit d69d9b6 into main Sep 4, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant