Merge remote-tracking branch 'origin/main' into codex/p0-obs-002-20260715
This commit is contained in:
@@ -64,8 +64,8 @@ from src.api.v1 import incidents as incidents_v1 # Phase 6.4: Decision Proposal
|
||||
from src.api.v1 import iwooos as iwooos_v1 # IwoooS security governance API
|
||||
from src.api.v1 import knowledge as knowledge_v1 # KB Phase 1: Knowledge Base
|
||||
from src.api.v1 import learning as learning_v1 # Phase D-G P0: Learning API
|
||||
from src.api.v1 import metrics as metrics_v1 # Phase 7: Gold Metrics (真實血脈)
|
||||
from src.api.v1 import mcp_control_plane as mcp_control_plane_v1
|
||||
from src.api.v1 import metrics as metrics_v1 # Phase 7: Gold Metrics (真實血脈)
|
||||
from src.api.v1 import monitoring as monitoring_v1 # 2026-04-03: 監控工具狀態
|
||||
from src.api.v1 import notifications as notifications_v1 # 2026-04-10: 通知頻道狀態
|
||||
from src.api.v1 import (
|
||||
@@ -650,22 +650,24 @@ async def lifespan(_app: FastAPI) -> AsyncGenerator[None, None]:
|
||||
logger.warning("approval_timeout_resolver_schedule_failed", error=str(e))
|
||||
|
||||
# T73: 已有完成證據但仍卡在 INVESTIGATING 的舊 incident 小批次收斂。
|
||||
# 僅處理 auto-repair success / approval EXECUTION_SUCCESS / approval EXPIRED,
|
||||
# 不自動關閉 manual_required 或單純 APPROVED 事件。
|
||||
# API production pool 只有 serving 預算;實際 loop 由單一 standalone
|
||||
# signal worker 擁有,避免 coroutine 被 budget gate 關閉後仍假性記錄
|
||||
# scheduled。reconciler 本身只處理強證據或已不再 firing 的事件。
|
||||
try:
|
||||
from src.jobs.incident_lifecycle_reconciler import (
|
||||
INTERVAL_SECONDS as INCIDENT_LIFECYCLE_RECONCILER_INTERVAL,
|
||||
)
|
||||
from src.jobs.incident_lifecycle_reconciler import (
|
||||
run_incident_lifecycle_reconciler_loop,
|
||||
)
|
||||
schedule_api_background_task(run_incident_lifecycle_reconciler_loop())
|
||||
logger.info(
|
||||
"incident_lifecycle_reconciler_scheduled",
|
||||
"incident_lifecycle_reconciler_owner_delegated",
|
||||
interval_sec=INCIDENT_LIFECYCLE_RECONCILER_INTERVAL,
|
||||
scheduled_in_api_process=False,
|
||||
production_process_owner="awoooi-worker",
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning("incident_lifecycle_reconciler_schedule_failed", error=str(e))
|
||||
logger.warning(
|
||||
"incident_lifecycle_reconciler_owner_resolution_failed",
|
||||
error=str(e),
|
||||
)
|
||||
|
||||
# AwoooP Ansible candidate backfill worker.
|
||||
# 把近期已命中 allowlisted PlayBook、但缺 durable candidate row 的事故補進
|
||||
|
||||
@@ -42,7 +42,7 @@ _SOURCE_HOSTS = {
|
||||
}
|
||||
_VERSION_PATTERNS = (
|
||||
re.compile(
|
||||
rb"""(?ix)["'](?:version|latestVersion|packageVersion)["']\s*[:=]\s*["']v?"""
|
||||
rb"""(?ix)["'](?:latestVersion|packageVersion|softwareVersion)["']\s*[:=]\s*["']v?"""
|
||||
rb"""([0-9]+\.[0-9]+\.[0-9]+(?:[-+][0-9A-Za-z.-]+)?)["']"""
|
||||
),
|
||||
re.compile(
|
||||
|
||||
@@ -703,13 +703,14 @@ def _inspect_source_contract(repo_root: Path) -> dict[str, Any]:
|
||||
|
||||
|
||||
def _read_source_contract_text(repo_root: Path, relative_path: str) -> str:
|
||||
candidates = [repo_root / relative_path]
|
||||
source_path = repo_root / relative_path
|
||||
candidates = [source_path]
|
||||
api_prefix = "apps/api/"
|
||||
if relative_path.startswith(api_prefix):
|
||||
candidates.append(repo_root / relative_path.removeprefix(api_prefix))
|
||||
if (
|
||||
relative_path == _AWOOOP_VERIFIED_CONTEXT_SURFACE_SOURCE
|
||||
and not (repo_root / "apps").exists()
|
||||
and not source_path.exists()
|
||||
):
|
||||
candidates.append(repo_root / "src/services/telegram_alert_ai_automation_matrix.py")
|
||||
|
||||
|
||||
@@ -623,7 +623,35 @@ async def _main() -> None:
|
||||
backup_restore_legacy_backfill_task: asyncio.Task[Any] | None = None
|
||||
security_maintenance_tasks: list[asyncio.Task[Any]] = []
|
||||
agent99_controlled_dispatch_reconciler_task: asyncio.Task[Any] | None = None
|
||||
incident_lifecycle_reconciler_task: asyncio.Task[Any] | None = None
|
||||
mcp_version_lifecycle_task: asyncio.Task[Any] | None = None
|
||||
|
||||
# Production API pods reserve their DB pool for serving requests. This
|
||||
# singleton worker therefore owns the existing bounded, fail-closed
|
||||
# lifecycle reconciler. It resolves at most one configured batch per pass
|
||||
# and independently reads current Prometheus firing state before treating
|
||||
# inactive alerts as closure candidates.
|
||||
from src.jobs.incident_lifecycle_reconciler import (
|
||||
BATCH_LIMIT as INCIDENT_LIFECYCLE_RECONCILER_BATCH_LIMIT,
|
||||
)
|
||||
from src.jobs.incident_lifecycle_reconciler import (
|
||||
INTERVAL_SECONDS as INCIDENT_LIFECYCLE_RECONCILER_INTERVAL,
|
||||
)
|
||||
from src.jobs.incident_lifecycle_reconciler import (
|
||||
run_incident_lifecycle_reconciler_loop,
|
||||
)
|
||||
|
||||
incident_lifecycle_reconciler_task = asyncio.create_task(
|
||||
run_incident_lifecycle_reconciler_loop(),
|
||||
name="run_incident_lifecycle_reconciler_loop",
|
||||
)
|
||||
logger.info(
|
||||
"signal_worker_incident_lifecycle_reconciler_started",
|
||||
owner="signal_worker",
|
||||
interval_seconds=INCIDENT_LIFECYCLE_RECONCILER_INTERVAL,
|
||||
batch_limit=INCIDENT_LIFECYCLE_RECONCILER_BATCH_LIMIT,
|
||||
direct_incident_closure_performed=False,
|
||||
)
|
||||
if settings.ENABLE_AWOOOP_ANSIBLE_CANDIDATE_BACKFILL_WORKER:
|
||||
from src.jobs.awooop_ansible_candidate_backfill_job import (
|
||||
run_awooop_ansible_candidate_backfill_loop,
|
||||
@@ -794,6 +822,12 @@ async def _main() -> None:
|
||||
await agent99_controlled_dispatch_reconciler_task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
if incident_lifecycle_reconciler_task is not None:
|
||||
incident_lifecycle_reconciler_task.cancel()
|
||||
try:
|
||||
await incident_lifecycle_reconciler_task
|
||||
except asyncio.CancelledError:
|
||||
pass
|
||||
if mcp_version_lifecycle_task is not None:
|
||||
mcp_version_lifecycle_task.cancel()
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user