refactor(supervise): fold Supervise into the facade as Supervisor; drop dead flat-fallbacks
tracker-policy-pr / check-pr (pull_request) Successful in 8s
test / integration-docker (pull_request) Successful in 12s
test / unit (pull_request) Successful in 43s
lint / lint (push) Failing after 59s
test / integration-firecracker (pull_request) Successful in 3m16s
test / coverage (pull_request) Successful in 16s
test / publish-infra (pull_request) Has been skipped
tracker-policy-pr / check-pr (pull_request) Successful in 8s
test / integration-docker (pull_request) Successful in 12s
test / unit (pull_request) Successful in 43s
lint / lint (push) Failing after 59s
test / integration-firecracker (pull_request) Successful in 3m16s
test / coverage (pull_request) Successful in 16s
test / publish-infra (pull_request) Has been skipped
Move the `Supervise` lifecycle out of its own `orchestrator/supervisor/ supervise.py` and into the package `__init__`, renaming the class to `Supervisor`. Callers now import it from `bot_bottle.orchestrator.supervisor` alongside the queue surface it belongs with. Remove the dead `try/except ImportError` flat-import fallbacks from the package-only store modules (db_store, audit_store, config_store, queue_store) and image_cache. Those fallbacks existed for when the store files were flat-copied into the gateway; post-PRD-0070 the data plane never opens the DB, so these modules are only ever imported as part of the package. The two gateway data-plane files that may still be loaded flat (egress_addon_core, git_gate_render) keep their fallbacks. Full unit suite green (2251). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -29,7 +29,7 @@ from ..git_gate import GitGate, GitGatePlan
|
|||||||
from ..log import die
|
from ..log import die
|
||||||
from ..manifest import Manifest, ManifestBottle
|
from ..manifest import Manifest, ManifestBottle
|
||||||
from ..supervisor.plan import SupervisePlan
|
from ..supervisor.plan import SupervisePlan
|
||||||
from ..orchestrator.supervisor.supervise import Supervise
|
from ..orchestrator.supervisor import Supervisor
|
||||||
from . import BottleSpec
|
from . import BottleSpec
|
||||||
|
|
||||||
|
|
||||||
@@ -102,7 +102,7 @@ def prepare_supervise(bottle: ManifestBottle, slug: str) -> SupervisePlan | None
|
|||||||
return None
|
return None
|
||||||
supervise_dir = supervise_state_dir(slug)
|
supervise_dir = supervise_state_dir(slug)
|
||||||
supervise_dir.mkdir(parents=True, exist_ok=True)
|
supervise_dir.mkdir(parents=True, exist_ok=True)
|
||||||
return Supervise().prepare(slug, supervise_dir)
|
return Supervisor().prepare(slug, supervise_dir)
|
||||||
|
|
||||||
|
|
||||||
def merge_provision_env_vars(provision: AgentProvisionPlan) -> AgentProvisionPlan:
|
def merge_provision_env_vars(provision: AgentProvisionPlan) -> AgentProvisionPlan:
|
||||||
|
|||||||
@@ -5,10 +5,7 @@ from __future__ import annotations
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
try:
|
from .store.config_store import ConfigStore
|
||||||
from .store.config_store import ConfigStore
|
|
||||||
except ImportError:
|
|
||||||
from config_store import ConfigStore # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
|
||||||
|
|
||||||
|
|
||||||
class StaleImageError(Exception):
|
class StaleImageError(Exception):
|
||||||
|
|||||||
@@ -6,16 +6,10 @@ import os
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
try:
|
from ...supervisor.types import Proposal, Response
|
||||||
from ...supervisor.types import Proposal, Response
|
from ...paths import host_db_path
|
||||||
from ...paths import host_db_path
|
from ...store.db_store import DbStore
|
||||||
from ...store.db_store import DbStore
|
from ...store.migrations import TableMigrations
|
||||||
from ...store.migrations import TableMigrations
|
|
||||||
except ImportError:
|
|
||||||
from supervisor.types import Proposal, Response # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
|
||||||
from paths import host_db_path # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
|
||||||
from db_store import DbStore # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
|
||||||
from migrations import TableMigrations # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
|
||||||
|
|
||||||
|
|
||||||
class QueueStore(DbStore):
|
class QueueStore(DbStore):
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
"""The orchestrator's view of the supervise plane.
|
"""The orchestrator's view of the supervise plane.
|
||||||
|
|
||||||
Bundles the orchestrator-owned supervise surface: the queue/audit I/O and diff
|
Bundles the orchestrator-owned supervise surface: the queue/audit I/O and diff
|
||||||
rendering (`queue`) and the `Supervise` lifecycle (`supervise`). For
|
rendering (`queue`), and the `Supervisor` lifecycle (host-side database
|
||||||
convenience it re-exports the neutral vocabulary (`bot_bottle.supervisor`'s
|
staging). For convenience it re-exports the neutral vocabulary
|
||||||
types + `SupervisePlan`) so orchestrator-side callers — service, CLI, tests —
|
(`bot_bottle.supervisor`'s types + `SupervisePlan`) so orchestrator-side
|
||||||
import from one place.
|
callers — service, CLI, tests — import from one place.
|
||||||
|
|
||||||
The data plane must NOT import this package; it imports `bot_bottle.supervisor`
|
The data plane must NOT import this package; it imports `bot_bottle.supervisor`
|
||||||
(neutral) directly and reaches the queue over the control-plane RPC.
|
(neutral) directly and reaches the queue over the control-plane RPC.
|
||||||
@@ -12,8 +12,12 @@ The data plane must NOT import this package; it imports `bot_bottle.supervisor`
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from abc import ABC
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from ...supervisor.types import * # noqa: F401,F403 — re-export the neutral vocabulary
|
from ...supervisor.types import * # noqa: F401,F403 — re-export the neutral vocabulary
|
||||||
from ...supervisor.plan import SupervisePlan
|
from ...supervisor.plan import SupervisePlan
|
||||||
|
from ..store.store_manager import StoreManager
|
||||||
from .queue import (
|
from .queue import (
|
||||||
archive_all_proposals,
|
archive_all_proposals,
|
||||||
archive_proposal,
|
archive_proposal,
|
||||||
@@ -31,4 +35,22 @@ from .queue import (
|
|||||||
write_proposal,
|
write_proposal,
|
||||||
write_response,
|
write_response,
|
||||||
)
|
)
|
||||||
from .supervise import Supervise
|
|
||||||
|
|
||||||
|
class Supervisor(ABC):
|
||||||
|
"""Per-bottle supervise lifecycle. Encapsulates host-side database staging;
|
||||||
|
the gateway's start/stop lifecycle is backend-specific.
|
||||||
|
|
||||||
|
`prepare` migrates the orchestrator's `bot-bottle.db` and returns the
|
||||||
|
neutral `SupervisePlan`. It touches the orchestrator store manager, so it
|
||||||
|
lives here rather than in the neutral `bot_bottle.supervisor` package; the
|
||||||
|
backend (which drives launch) may import it — backend → orchestrator is an
|
||||||
|
allowed direction, unlike gateway → orchestrator."""
|
||||||
|
|
||||||
|
def prepare(self, slug: str, stage_dir: Path) -> SupervisePlan:
|
||||||
|
"""Stage the host database. Returns the plan; `internal_network` must be
|
||||||
|
set by the launch step before .start runs."""
|
||||||
|
del stage_dir
|
||||||
|
mgr = StoreManager.instance()
|
||||||
|
mgr.migrate()
|
||||||
|
return SupervisePlan(slug=slug, db_path=mgr.db_path)
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
"""The `Supervise` lifecycle — host-side database staging (PRD 0013 / 0070).
|
|
||||||
|
|
||||||
`Supervise.prepare` migrates the orchestrator's `bot-bottle.db` and returns the
|
|
||||||
neutral `SupervisePlan`. It touches the orchestrator store manager, so it lives
|
|
||||||
here rather than in the neutral `bot_bottle.supervisor` package; the backend
|
|
||||||
(which drives launch) may import it — backend → orchestrator is an allowed
|
|
||||||
direction, unlike gateway → orchestrator.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from abc import ABC
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
from ..store.store_manager import StoreManager
|
|
||||||
from ...supervisor.plan import SupervisePlan
|
|
||||||
|
|
||||||
|
|
||||||
class Supervise(ABC):
|
|
||||||
"""Per-bottle supervise daemon. Encapsulates host-side database staging;
|
|
||||||
the gateway's start/stop lifecycle is backend-specific."""
|
|
||||||
|
|
||||||
def prepare(
|
|
||||||
self,
|
|
||||||
slug: str,
|
|
||||||
stage_dir: Path,
|
|
||||||
) -> SupervisePlan:
|
|
||||||
"""Stage the host database. Returns the plan; `internal_network` must be
|
|
||||||
set by the launch step before .start runs."""
|
|
||||||
del stage_dir
|
|
||||||
mgr = StoreManager.instance()
|
|
||||||
mgr.migrate()
|
|
||||||
return SupervisePlan(
|
|
||||||
slug=slug,
|
|
||||||
db_path=mgr.db_path,
|
|
||||||
)
|
|
||||||
@@ -5,16 +5,10 @@ from __future__ import annotations
|
|||||||
import sqlite3
|
import sqlite3
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
try:
|
from ..supervisor.types import AuditEntry
|
||||||
from ..supervisor.types import AuditEntry
|
from ..paths import host_db_path
|
||||||
from ..paths import host_db_path
|
from .db_store import DbStore
|
||||||
from .db_store import DbStore
|
from .migrations import TableMigrations
|
||||||
from .migrations import TableMigrations
|
|
||||||
except ImportError:
|
|
||||||
from supervisor.types import AuditEntry # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
|
||||||
from paths import host_db_path # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
|
||||||
from db_store import DbStore # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
|
||||||
from migrations import TableMigrations # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
|
||||||
|
|
||||||
|
|
||||||
class AuditStore(DbStore):
|
class AuditStore(DbStore):
|
||||||
|
|||||||
@@ -4,14 +4,9 @@ from __future__ import annotations
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
try:
|
from .db_store import DbStore
|
||||||
from .db_store import DbStore
|
from .migrations import TableMigrations
|
||||||
from .migrations import TableMigrations
|
from ..paths import host_db_path
|
||||||
from ..paths import host_db_path
|
|
||||||
except ImportError:
|
|
||||||
from db_store import DbStore # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
|
||||||
from migrations import TableMigrations # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
|
||||||
from paths import host_db_path # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_CACHED_IMAGE_STALE_WARNING_DAYS = 1
|
DEFAULT_CACHED_IMAGE_STALE_WARNING_DAYS = 1
|
||||||
|
|||||||
@@ -6,10 +6,7 @@ import sqlite3
|
|||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
try:
|
from .migrations import TableMigrations
|
||||||
from .migrations import TableMigrations
|
|
||||||
except ImportError:
|
|
||||||
from migrations import TableMigrations # type: ignore[import-not-found] # pylint: disable=import-error,no-name-in-module
|
|
||||||
|
|
||||||
|
|
||||||
class DbVersionError(Exception):
|
class DbVersionError(Exception):
|
||||||
|
|||||||
@@ -11,6 +11,6 @@ tier's private code, importable by `bot_bottle.gateway` **and**
|
|||||||
status / poll-status constants, and the shared daemon constants.
|
status / poll-status constants, and the shared daemon constants.
|
||||||
* `plan` — `SupervisePlan`, the launch-time staging DTO.
|
* `plan` — `SupervisePlan`, the launch-time staging DTO.
|
||||||
|
|
||||||
The orchestrator-only half (queue I/O, diff rendering, the `Supervise`
|
The orchestrator-only half (queue I/O, diff rendering, the `Supervisor`
|
||||||
lifecycle) lives under `bot_bottle.orchestrator.supervisor`.
|
lifecycle) lives under `bot_bottle.orchestrator.supervisor`.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
"""`SupervisePlan` — the launch-time supervise staging DTO.
|
"""`SupervisePlan` — the launch-time supervise staging DTO.
|
||||||
|
|
||||||
A pure value type (no store access), so it lives in the neutral package: the
|
A pure value type (no store access), so it lives in the neutral package: the
|
||||||
backend builds it at launch and the orchestrator's `Supervise.prepare` returns
|
backend builds it at launch and the orchestrator's `Supervisor.prepare` returns
|
||||||
it. The behaviour that fills it in — staging the host database — is the
|
it. The behaviour that fills it in — staging the host database — is the
|
||||||
orchestrator's, in `bot_bottle.orchestrator.supervisor.supervise`.
|
orchestrator's, in `bot_bottle.orchestrator.supervisor.Supervisor`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
@@ -14,7 +14,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass(frozen=True)
|
||||||
class SupervisePlan:
|
class SupervisePlan:
|
||||||
"""Output of `Supervise.prepare`; consumed by `.start`.
|
"""Output of `Supervisor.prepare`; consumed by `.start`.
|
||||||
|
|
||||||
`db_path` is the host database bind-mounted into the gateway at
|
`db_path` is the host database bind-mounted into the gateway at
|
||||||
/run/supervise/bot-bottle.db. `internal_network` is empty at prepare time;
|
/run/supervise/bot-bottle.db. `internal_network` is empty at prepare time;
|
||||||
|
|||||||
@@ -352,7 +352,7 @@ class TestToolConstants(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class _StubSupervise(supervise.Supervise):
|
class _StubSupervise(supervise.Supervisor):
|
||||||
"""Concrete Supervise subclass for testing the prepare template."""
|
"""Concrete Supervise subclass for testing the prepare template."""
|
||||||
|
|
||||||
def start(self, plan): # type: ignore
|
def start(self, plan): # type: ignore
|
||||||
|
|||||||
Reference in New Issue
Block a user