refactor(backend): base owns the gateway-attach flow, fail hard (0081 review)
Addresses review on #519 (@didericis 6143 + the fail-hard direction over the codex skip). Base owns the flow (6143 / ADR 0006). `attach_bottled_agents_to_gateway()` is now a concrete method on `BottleBackend` that delegates to `gateway_attach.reconcile_running_bottles`; backends override only three primitives — `_gateway_attach_resources()`, `_running_bottles()`, `_attach_bottle_to_gateway()`. The shared control flow + error policy live in one place so a backend can't drift onto a bespoke loop or a silent skip. Keeps the reconcile flow + `GatewayAttachResources` out of `backend/base.py` (the size guardrail) in a dedicated `backend/gateway_attach.py`. New ADR 0006 records the "shared behaviour in the base backend, subclasses override primitives" theme. Fail hard, never skip (the maintainer's direction over the codex review's skip). Any attach failure now aborts bring-up instead of being logged and skipped: a bottle that silently can't reach the fresh gateway (its egress just starts failing TLS) is worse than a loud failure. Every bottle is attempted and the failures are raised together (aggregate `InfraLaunchError`) so one bring-up surfaces the full blast radius. Resource gathering (the CA fetch) also fails hard. PRD 0081 goal + design updated to match (reversed from the earlier "tolerate per-bottle failures" draft). Bumps the base.py guardrail cap 580->600 for the new contract surface (the delegator + 3 abstract primitives); the flow itself lives in gateway_attach.py. Refs #516, #519. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -26,10 +26,10 @@ from .base import ActiveAgent, BackendStatus, BottleBackend
|
||||
# Tests may replace _backends with a {name: fake} dict via patch.object;
|
||||
# _get_backends() returns the current module-level value as-is when it
|
||||
# is not None, so test fakes take effect without triggering real imports.
|
||||
_backends: dict[str, BottleBackend[Any, Any]] | None = None
|
||||
_backends: dict[str, BottleBackend[Any, Any, Any]] | None = None
|
||||
|
||||
|
||||
def _get_backends() -> dict[str, BottleBackend[Any, Any]]:
|
||||
def _get_backends() -> dict[str, BottleBackend[Any, Any, Any]]:
|
||||
"""Return the registry of all backend instances, loading lazily on first call."""
|
||||
global _backends # pylint: disable=global-statement
|
||||
if _backends is None:
|
||||
@@ -48,7 +48,7 @@ def get_bottle_backend(
|
||||
name: str | None = None,
|
||||
*,
|
||||
prompt: bool = True,
|
||||
) -> BottleBackend[Any, Any]:
|
||||
) -> BottleBackend[Any, Any, Any]:
|
||||
"""Resolve the bottle backend.
|
||||
|
||||
`name` precedence:
|
||||
|
||||
Reference in New Issue
Block a user