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:
thanhnv
2026-09-07 01:42:45 +09:00
parent 1caded98e9
commit 9b4dc01c1a
4 changed files with 559 additions and 35 deletions
@@ -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__}",