Skip to content

bug: caller-controlled scope IDs grow Runtime caches without bound #1322

Description

@AlexStocks

Describe the bug

Caller-controlled scope_id values permanently populate several process-level dictionaries in a long-running Server.

Preparing context for a previously unseen scope creates and retains:

  • one BuiltinRuntime scope lock;
  • one RelationalContexts PowerContext object;
  • one source lock;
  • one activation lock.

There is no TTL, LRU, maximum scope count, weak-reference lifecycle, or explicit close operation for a single scope. A valid request does not need to persist any Source or Memory before these objects are retained.

RFC 0019 intentionally caches a scoped Runtime composition, but the remote Server now accepts arbitrary scope IDs at the HTTP/MCP boundary. The original cache assumption therefore becomes an unbounded resource path.

Steps to reproduce

At commit a6e0dfe:

  1. Start a fresh SQLite-backed Server with Dashboard, MCP, metrics, and access logging disabled.
  2. Send 250 successful POST /v1/context/prepare requests.
  3. Use a different valid scope_id for each request and the same short query.
  4. Inspect the Runtime and RelationalContexts dictionaries.

Observed:

runtime_scope_locks=250
provider_contexts=250
provider_source_locks=250
provider_activation_locks=250

No Source or Memory entries were written.

Relevant paths:

  • src/powercontext/builtin/runtime/application.py:1197 and 1383-1396
  • src/powercontext/builtin/runtime/relational.py:340-346
  • src/powercontext/builtin/runtime/relational.py:449-500
  • docs/en/rfcs/0019_local_source_memory_runtime.md:161-167

Expected behavior

A long-running Server should have a concurrency-safe, bounded lifecycle for scope-local objects.

Possible acceptance criteria:

  • Define a maximum or idle lifecycle for cached scopes.
  • Do not evict a scope lock while an operation still holds or waits for it.
  • Allow inactive scope contexts to be reclaimed without weakening per-scope write serialization.
  • Expose a bounded metric for active/cached scopes without using scope_id as a metric label.
  • Add a public-boundary regression test showing that many one-off scope IDs do not cause permanent linear growth.

Authentication and rate limiting can reduce abuse but do not replace cache lifecycle management for normal long-lived multi-project use.

Actual behavior

Each new valid scope_id permanently increases multiple dictionaries until the process exits. An authenticated caller, or an unauthenticated caller when the Server is publicly bound, can force continued process-memory growth with one-off scope IDs.

Environment

  • PowerContext: 0.0.3.dev11+ga6e0dfe8b.d20260823
  • Commit: a6e0dfe
  • Python: 3.14.3
  • Backend: SQLite
  • OS: Windows 11
  • Probe requests: 250 successful public HTTP prepare_context operations

Are you willing to submit a PR to fix this bug?

  • Yes, I would like to submit a PR.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

Status
In progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions