fix(automation): enforce durable alert closure
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 0s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 2m32s
CD Pipeline / build-and-deploy (push) Successful in 8m2s
CD Pipeline / post-deploy-checks (push) Successful in 2m22s

Restore D037 durable incident readback without weakening database failure semantics.

Fence Agent99 dispatch and terminal learning to canonical identities, durable leases, verifier receipts, and reconciler-owned checkpoints.

Drain backup and restore legacy receipts in bounded cohorts with strict transport, claim, projection, and no-false-green controls.

Keep SSH service refusal visible while separating it from broker network-policy reachability.
This commit is contained in:
ogt
2026-07-14 09:40:53 +08:00
parent 72a167a322
commit 6cf8429d17
56 changed files with 15100 additions and 227 deletions

View File

@@ -660,9 +660,20 @@ class KnowledgeService:
# I1: 持有背景 Task 引用,防止 GC 提前回收
self._pending_tasks: set[asyncio.Task] = set() # type: ignore[type-arg]
async def create_entry(self, data: KnowledgeEntryCreate) -> KnowledgeEntry:
async def create_entry(
self,
data: KnowledgeEntryCreate,
*,
project_id: str | None = None,
schedule_embedding: bool = True,
) -> KnowledgeEntry:
"""建立知識條目,建立後背景自動產生 embedding"""
async with get_db_context() as db:
context = (
get_db_context(project_id)
if project_id is not None
else get_db_context()
)
async with context as db:
repo: IKnowledgeRepository = KnowledgeDBRepository(db)
entry = await repo.create(data)
logger.info(
@@ -673,17 +684,33 @@ class KnowledgeService:
)
# 背景產生 embedding (不阻塞回應);持有引用防 GC 回收
task = asyncio.create_task(self._embed_entry(entry.id, data.title, data.content))
self._pending_tasks.add(task)
task.add_done_callback(self._pending_tasks.discard)
if schedule_embedding:
task = asyncio.create_task(
self._embed_entry(
entry.id,
data.title,
data.content,
project_id=project_id,
)
)
self._pending_tasks.add(task)
task.add_done_callback(self._pending_tasks.discard)
return entry
async def _embed_entry(self, entry_id: str, title: str, content: str) -> None:
async def _embed_entry(
self,
entry_id: str,
title: str,
content: str,
*,
project_id: str | None = None,
) -> None:
"""背景任務:產生並儲存 embedding"""
await self.ensure_entry_embedding(
entry_id,
title,
content,
project_id=project_id,
)
async def ensure_entry_embedding(