refactor: address PR #851 review round 3 (ingest transport)

- LocalTransport.run_consumers increments active_consumer_count per worker
  (instead of once after the loop) so the count is accurate if a later
  tg.start() raises mid-pool.
- Add LocalTransport.aclose() to explicitly close its owned send/receive stream
  ends (belt-and-suspenders against unclosed-resource warnings; anyio aclose is
  idempotent, and by shutdown the scanner is already winding down). Reworded the
  base IngestTransport.aclose() docstring to point at the overrides.
- Inline ingest_transport.producer at the scanner/user_manager call sites,
  dropping the single-use task_producer alias in both lifespan paths.
- Annotate DistributedTransport._producer explicitly as ProcrastinateTaskProducer
  so the drain() coupling is visible and ty catches drift.
- Add a unit test for LocalTransport.aclose() (closes the owned streams,
  idempotent).

Refs: Deck #196

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Coutinho
2026-06-04 20:36:58 +02:00
co-authored by Claude Opus 4.8
parent 2179e9ddb0
commit c0c52c1b34
3 changed files with 47 additions and 11 deletions
+4 -6
View File
@@ -1720,7 +1720,6 @@ def get_app(transport: str = "streamable-http", enabled_apps: list[str] | None =
# Named ingest_transport (not transport) to avoid shadowing the
# get_app(transport=...) HTTP-transport parameter.
ingest_transport = await build_transport(settings)
task_producer = ingest_transport.producer
# Publish to app.state (ADR-007), the module singleton (FastMCP
# session lifespans), and the /app browser sub-app in one place.
@@ -1730,10 +1729,10 @@ def get_app(transport: str = "streamable-http", enabled_apps: list[str] | None =
# Start background tasks using anyio TaskGroup
async with anyio.create_task_group() as tg:
# Start scanner task (publishes to task_producer)
# Start scanner task (publishes to the transport's producer)
await tg.start(
scanner_task,
task_producer,
ingest_transport.producer,
shutdown_event,
scanner_wake_event,
client,
@@ -1912,7 +1911,6 @@ def get_app(transport: str = "streamable-http", enabled_apps: list[str] | None =
# Named ingest_transport (not transport) to avoid shadowing the
# get_app(transport=...) HTTP-transport parameter.
ingest_transport = await build_transport(settings)
task_producer = ingest_transport.producer
# Publish to app.state (ADR-007), the module singleton (FastMCP
# session lifespans), and the /app browser sub-app in one place.
@@ -1930,11 +1928,11 @@ def get_app(transport: str = "streamable-http", enabled_apps: list[str] | None =
# management API revoke endpoint (via app.state.oauth_context).
async with anyio.create_task_group() as tg:
# Start user manager task (supervises per-user scanners).
# Each per-user scanner clones task_producer; for the bus
# Each per-user scanner clones the producer; for the bus
# producer clone() returns the shared connection.
await tg.start(
user_manager_task,
task_producer,
ingest_transport.producer,
shutdown_event,
scanner_wake_event,
token_storage,