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.
ROUTES registry.The closed surface
| Source | Routes | Existing handlers |
|---|---|---|
| SQS | 10 | Extraction ×2, registration, deep-read, context-enrich, framer, agent-results, clerk ×2, WhatsApp ingress |
| Standard EventBridge | 3 | Ingest relay, registration relay, domain relay; exact rule ARN remains visible in the envelope |
| Domain event | 1 | io.pramaan.functions:agent_run.artifact_observed, classified by exact source and detail-type |
| Reserved task | 4 | Run sweeper, alert digest, storage allocation, context backfill |
| Isolated drill | 2 | One SQS and one EventBridge probe route; neither is a product source |
| Permission canary | 1 | Direct-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
| Evidence | What it proves |
|---|---|
test_shared_worker_handler.py | Exact routing, fail-closed ambiguity, lazy selected import, passthrough response, and direct backfill pairing. |
test_worker_runtime.py | Back-to-back coroutines reuse one live loop; candidate handlers use the shared seam. |
test_worker_runtime_db.py | Two registered DB routes query through one production cached engine on one worker loop. |
test_shared_worker_registry_contract.py | Accepted inventory only, trust-boundary exclusions, no private handler loops, planted negatives. |
test_shared_worker_package_limits.py | Actual clean-source Zip and uncompressed bytes remain below Lambda limits. |
test_shared_worker_conditioned_canary.py | Eight 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.