Implement ACTIVITY-WP-0022/0023: safe sink default and gap closures
All checks were successful
CI Smoke / host-smoke (push) Successful in 1s
CI Smoke / container-smoke (push) Successful in 3s
Build and Publish Container Image / build-and-push (push) Successful in 28s

Default ISSUE_SINK_TYPE to state-hub (no silent Forgejo issues), hard-fail
prune apply without live-images protection, refresh-live-images script,
disable TaskExecutor stub by default, and document consumer/sink contracts.
This commit is contained in:
tegwick 2026-07-21 21:40:08 +02:00
parent 5c7a90ce7c
commit 4f5399df84
19 changed files with 525 additions and 155 deletions

View file

@ -2,9 +2,17 @@
IssueSink adapter interface and implementations.
IssueSink is the outbound boundary between activity-core and task backends
(issue-core, etc.). It receives TaskSpec objects and returns TaskRef objects.
(issue-core, State Hub progress, etc.). It receives TaskSpec objects and
returns TaskRef objects.
Active sink is selected by ISSUE_SINK_TYPE env var: "rest" (default) | "null".
Active sink is selected by ISSUE_SINK_TYPE:
state-hub (default) State Hub progress (`activity_task_spawn`); no Forgejo
null dry-run synthetic refs
rest issue-core REST (explicit opt-in; may project to Forgejo)
ACTIVITY-WP-0022: default must not silently create Forgejo issues for internal
findings. Use rest only when intentionally projecting to an external tracker.
"""
from __future__ import annotations
@ -22,7 +30,9 @@ logger = logging.getLogger(__name__)
ISSUE_CORE_URL = os.environ.get("ISSUE_CORE_URL", "http://127.0.0.1:8765")
ISSUE_CORE_API_KEY_ENV = "ISSUE_CORE_API_KEY"
ISSUE_SINK_TYPE = os.environ.get("ISSUE_SINK_TYPE", "rest")
# Safe default for internal fleet findings (ACTIVITY-WP-0022).
DEFAULT_ISSUE_SINK_TYPE = "state-hub"
ISSUE_SINK_TYPE = os.environ.get("ISSUE_SINK_TYPE", DEFAULT_ISSUE_SINK_TYPE)
class IssueSink(ABC):
@ -188,9 +198,18 @@ def get_issue_sink() -> IssueSink:
Re-reads the env on each call so ConfigMap/env patches apply without
requiring a module reload (ACTIVITY-WP-0021).
"""
sink_type = os.environ.get("ISSUE_SINK_TYPE", ISSUE_SINK_TYPE).lower()
sink_type = os.environ.get("ISSUE_SINK_TYPE", DEFAULT_ISSUE_SINK_TYPE).strip().lower()
if not sink_type:
sink_type = DEFAULT_ISSUE_SINK_TYPE
if sink_type == "null":
return NullSink()
if sink_type in {"state-hub", "state_hub", "progress"}:
return StateHubProgressSink()
return IssueCoreRestSink()
if sink_type == "rest":
return IssueCoreRestSink()
logger.warning(
"unknown ISSUE_SINK_TYPE=%r — falling back to %s (safe default)",
sink_type,
DEFAULT_ISSUE_SINK_TYPE,
)
return StateHubProgressSink()