**BREAKING** for operators using `COMPOSE_PROFILES=full` or custom Compose overrides that referenced these stanzas — they're gone in docker-compose.yml and docker-compose.prod.yml. The scheduler-v2 model (previous commit) is now the sole driver: every cadence is a job in services/scheduler/__main__.py:JOBS hitting an admin HTTP endpoint. Why drop instead of keep behind `profiles: [full]`: - The previous stanzas were tight `restart: unless-stopped` boot loops. When the scheduled run ended (every cycle), Docker re-spawned the container, defeating any cadence the service intended. - The whole point of #176 is that there's now exactly one driver. Two drivers (scheduler HTTP + standalone container loop) would race on the same /data/user_sessions and knowledge_items writes. - Removing the stanzas is a louder signal than commenting them out — operators upgrading get a clean failure mode (no stale containers), not a silently double-driven pipeline. The Python entry points (services/{corporate_memory, session_collector, verification_detector}/__main__.py) stay — they're still callable from the CLI for manual one-shot runs and from the new admin endpoints. docs/architecture.md updated to reflect the new schedule table. tests/test_docker_compose.py pins the contract: the two services must not reappear under either Compose file.
66 lines
2.5 KiB
Python
66 lines
2.5 KiB
Python
"""Static contract tests for docker-compose.yml.
|
|
|
|
The corporate-memory and session-collector side-car services were dropped
|
|
in #176 — the scheduler container now drives them through HTTP. These
|
|
tests pin that contract so the services can't quietly come back.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
import yaml
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def compose() -> dict:
|
|
root = Path(__file__).resolve().parent.parent
|
|
return yaml.safe_load((root / "docker-compose.yml").read_text())
|
|
|
|
|
|
class TestComposeServicesRemoved:
|
|
"""The two side-car services must not exist in docker-compose.yml."""
|
|
|
|
def test_corporate_memory_service_removed(self, compose):
|
|
assert "corporate-memory" not in compose["services"], (
|
|
"corporate-memory was dropped in #176 — scheduler drives it via HTTP. "
|
|
"Do not re-add the service stanza."
|
|
)
|
|
|
|
def test_session_collector_service_removed(self, compose):
|
|
assert "session-collector" not in compose["services"], (
|
|
"session-collector was dropped in #176 — scheduler drives it via HTTP. "
|
|
"Do not re-add the service stanza."
|
|
)
|
|
|
|
|
|
class TestComposeSchedulerWires:
|
|
"""The scheduler service must remain — it's the sole driver now."""
|
|
|
|
def test_scheduler_service_present(self, compose):
|
|
assert "scheduler" in compose["services"]
|
|
scheduler = compose["services"]["scheduler"]
|
|
assert scheduler["command"] == "python -m services.scheduler"
|
|
|
|
def test_app_service_present(self, compose):
|
|
assert "app" in compose["services"]
|
|
|
|
|
|
class TestComposeNoBootLoopProfile:
|
|
"""No service that imports anthropic / openai should ship as a tight
|
|
`restart: unless-stopped` boot loop. The previous corporate-memory and
|
|
session-collector stanzas were exactly this footgun."""
|
|
|
|
def test_only_scheduler_is_unconditional_long_running(self, compose):
|
|
# Services WITHOUT a `profiles:` key run on default `docker compose up`.
|
|
always_running = [
|
|
name
|
|
for name, svc in compose["services"].items()
|
|
if "profiles" not in svc
|
|
]
|
|
# Expected always-running set on a default deploy: app + scheduler.
|
|
# extract is one-shot so it has profiles=[extract]; caddy/telegram-bot/
|
|
# ws-gateway are all behind profiles too.
|
|
for boot_loop_offender in ("corporate-memory", "session-collector"):
|
|
assert boot_loop_offender not in always_running
|