Story-3404 · internal worker consolidation · local proof

One shell, eighteen product doors

The shared worker is not a generic Lambda router. It is a closed switchboard for the already-approved internal routes: an invocation must identify one exact trigger, and that trigger names one existing handler. Queue and event envelopes pass through unchanged; reserved task wrappers expose only their explicit inner payload.

1 · ClassifyMatch one exact SQS ARN, standard EventBridge rule ARN, domain-event key, or reserved task.
2 · LoadImport only the selected handler from the immutable ROUTES registry.
3 · RunLog the selected route, drive async work on the one process-shared loop and return the handler response verbatim.

The closed surface

SourceRoutesExisting handlers
SQS10Extraction ×2, registration, deep-read, context-enrich, framer, agent-results, clerk ×2, WhatsApp ingress
Standard EventBridge3Ingest relay, registration relay, domain relay; exact rule ARN remains visible in the envelope
Domain event1io.pramaan.functions:agent_run.artifact_observed, classified by exact source and detail-type
Reserved task4Run sweeper, alert digest, storage allocation, context backfill
Isolated drill2One SQS and one EventBridge probe route; neither is a product source
Permission canary1Direct-only task; rejects fixtures outside the configured staging canary firm

The registry contract test requires exactly 21 unique route IDs and 21 unique (source_kind, source_key) pairs: 18 admitted product routes plus two isolated drill routes and one direct permission-canary route. Billing renewal remains absent until its Terraform enable gate is true. The expected handler multiset preserves the legitimate shared-handler pairs: extraction, clerk, and the two-source cutover probe.

Refusal is part of the product

Unknown sources, mixed-record SQS batches and ambiguous task events are rejected before any candidate handler is imported. HTTP and WebSocket APIs, Razorpay, Indian Kanoon, malware results, document rendering, WhatsApp sender and WhatsApp webhook remain separate trust or deployment boundaries and cannot appear in ROUTES.

Why the shared event loop matters

A warm process may dispatch different database-backed workers in sequence. Building and closing a loop inside each handler can strand the cached async engine on the first loop. src/shared/worker_runtime.py owns the only loop runner; a static guard scans every registered handler and rejects asyncio.run, Runner, new_event_loop and run_until_complete. Planted examples prove the guard itself turns red.

The database regression invokes two registered DB-backed routes in one child process. Both execute SELECT 1 through the production cached SQLAlchemy engine and record the same engine ID and running-loop ID. A closed shared loop fails loudly; it is never replaced underneath connections cached on the old loop.

Local package evidence

Clean-source measurement: 2,398,712 compressed bytes and 7,785,094 uncompressed bytes. Immutable-layer receipts add 226,012,056 bytes for pramaan-deps:16 and AWSOpenTelemetryDistroPython:22. The assembled footprint is 233,797,150 / 262,144,000 bytes, leaving 28,346,850 bytes. The test rebuilds the code Zip, reads the exact versioned layer receipt and guards both the 50 MiB direct code-Zip and 250 MiB assembled limits. Oversize planted measurements fail it.

This is package proof only. The exact deployed environment union, collisions and 4,096-byte Lambda environment limit belong to infra Story-3405, where Terraform owns the real values. Story-3404 does not fabricate secret lengths or query live AWS to make a local test green.

Verification boundary

EvidenceWhat it proves
test_shared_worker_handler.pyExact routing, fail-closed ambiguity, lazy selected import, passthrough response, and direct backfill pairing.
test_worker_runtime.pyBack-to-back coroutines reuse one live loop; candidate handlers use the shared seam.
test_worker_runtime_db.pyTwo registered DB routes query through one production cached engine on one worker loop.
test_shared_worker_registry_contract.pyAccepted inventory only, trust-boundary exclusions, no private handler loops, planted negatives.
test_shared_worker_package_limits.pyActual clean-source Zip and uncompressed bytes remain below Lambda limits.
test_shared_worker_conditioned_canary.pyEight governed operations fail closed, return concrete receipts, and keep stateful fixtures inside the configured canary firm.

All evidence on this page is repository-local. No Lambda configuration, event-source mapping, queue, EventBridge target, IAM policy or other AWS resource was mutated by this story slice.

The future context-backfill caller is pinned as one pair: pramaan-workers:live plus the reserved context_backfill wrapper. Its rollback pair is the old pramaan-context-backfill target plus the original unwrapped payload. Fleet tests fail if either half moves alone. The branch remains unmerged until the dormant alias exists.