feat(jira-knowledge): complete production capability (UI, Observability, Docs, Regression)
- Phase 9: Extend JiraConnectDialog with Project Knowledge config, mapping, and Sync Now button - Phase 10: Wire JiraSyncService to CanonicalAuditLogger for sync start/complete/fail events - Phase 11: Add retrieval regression suite with synthetic corpus and baseline metrics - Phase 14: Add comprehensive production guide (docs/jira-knowledge-guide.md) - Fix UI status label references (self.status -> self.conn_status) - Implement real sync trigger logic in UI using JiraSyncService
This commit is contained in:
@@ -90,6 +90,21 @@ class JiraSyncService:
|
||||
manifest.mark_attempt()
|
||||
save_manifest(self._index._root, manifest)
|
||||
|
||||
# Emit audit event for sync start
|
||||
try:
|
||||
from ...infrastructure.telemetry.audit_logger import CanonicalAuditLogger
|
||||
from ...config import CONFIG_DIR
|
||||
logger = CanonicalAuditLogger(CONFIG_DIR / "audit")
|
||||
logger.record(
|
||||
kind="jira_knowledge.sync.started",
|
||||
name=f"{target.cowork_project_id}:{target.jira_project_key}",
|
||||
ok=True,
|
||||
detail=f"mode={'incremental' if incremental else 'full'}",
|
||||
agent_role="system"
|
||||
)
|
||||
except Exception:
|
||||
pass # Audit failure must not break sync
|
||||
|
||||
# Fall back to full sync when no cursor exists.
|
||||
if incremental and not manifest.sync_cursor:
|
||||
incremental = False
|
||||
@@ -127,6 +142,22 @@ class JiraSyncService:
|
||||
total_indexed=total_indexed,
|
||||
)
|
||||
save_manifest(self._index._root, manifest)
|
||||
|
||||
# Emit audit event for sync success
|
||||
try:
|
||||
from ...infrastructure.telemetry.audit_logger import CanonicalAuditLogger
|
||||
from ...config import CONFIG_DIR
|
||||
logger = CanonicalAuditLogger(CONFIG_DIR / "audit")
|
||||
logger.record(
|
||||
kind="jira_knowledge.sync.completed",
|
||||
name=f"{target.cowork_project_id}:{target.jira_project_key}",
|
||||
ok=True,
|
||||
detail=f"processed={processed},failed={failed},duration={duration:.2f}s",
|
||||
agent_role="system"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return SyncResult(
|
||||
processed=processed,
|
||||
failed=failed,
|
||||
@@ -138,11 +169,43 @@ class JiraSyncService:
|
||||
duration = time.monotonic() - start
|
||||
manifest.mark_failure(category=exc.code, failed=0)
|
||||
save_manifest(self._index._root, manifest)
|
||||
|
||||
# Emit audit event for sync failure
|
||||
try:
|
||||
from ...infrastructure.telemetry.audit_logger import CanonicalAuditLogger
|
||||
from ...config import CONFIG_DIR
|
||||
logger = CanonicalAuditLogger(CONFIG_DIR / "audit")
|
||||
logger.record(
|
||||
kind="jira_knowledge.sync.failed",
|
||||
name=f"{target.cowork_project_id}:{target.jira_project_key}",
|
||||
ok=False,
|
||||
detail=f"error={exc.code},message={exc.safe_message[:100]}",
|
||||
agent_role="system"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
raise
|
||||
except Exception as exc: # noqa: BLE001
|
||||
duration = time.monotonic() - start
|
||||
manifest.mark_failure(category="UNEXPECTED", failed=0)
|
||||
save_manifest(self._index._root, manifest)
|
||||
|
||||
# Emit audit event for unexpected failure
|
||||
try:
|
||||
from ...infrastructure.telemetry.audit_logger import CanonicalAuditLogger
|
||||
from ...config import CONFIG_DIR
|
||||
logger = CanonicalAuditLogger(CONFIG_DIR / "audit")
|
||||
logger.record(
|
||||
kind="jira_knowledge.sync.failed",
|
||||
name=f"{target.cowork_project_id}:{target.jira_project_key}",
|
||||
ok=False,
|
||||
detail=f"error=UNEXPECTED,type={type(exc).__name__}",
|
||||
agent_role="system"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
raise ProviderError(
|
||||
"SYNC_FAILED",
|
||||
f"Jira sync failed: {type(exc).__name__}",
|
||||
|
||||
Reference in New Issue
Block a user