feat(governance): surface km archive audit history
All checks were successful
Code Review / ai-code-review (push) Successful in 12s
Type Sync Check / check-type-sync (push) Successful in 27s
CD Pipeline / tests (push) Successful in 4m9s
CD Pipeline / build-and-deploy (push) Successful in 3m57s
CD Pipeline / post-deploy-checks (push) Successful in 1m45s
All checks were successful
Code Review / ai-code-review (push) Successful in 12s
Type Sync Check / check-type-sync (push) Successful in 27s
CD Pipeline / tests (push) Successful in 4m9s
CD Pipeline / build-and-deploy (push) Successful in 3m57s
CD Pipeline / post-deploy-checks (push) Successful in 1m45s
This commit is contained in:
@@ -74,6 +74,15 @@ type GovernanceQueueItem = {
|
||||
human_owner?: string | null;
|
||||
kb_draft_entry_id?: string | null;
|
||||
worker_status?: string | null;
|
||||
dry_run_plan_fingerprint?: string | null;
|
||||
archived_count?: number | null;
|
||||
stale_ratio_snapshot?: {
|
||||
stale_count?: number | null;
|
||||
total_count?: number | null;
|
||||
stale_ratio?: number | null;
|
||||
threshold?: number | null;
|
||||
stale_days?: number | null;
|
||||
} | null;
|
||||
};
|
||||
|
||||
type GovernanceQueueResponse = {
|
||||
@@ -776,6 +785,36 @@ function draftDedupeGroupForQueueItem(
|
||||
return groups.find((group) => group.governance_event_id === item.governance_event_id) ?? null;
|
||||
}
|
||||
|
||||
function kmArchiveTraceForDedupeGroup(
|
||||
group: KnowledgeReviewDraftDedupeGroup,
|
||||
queueItems: GovernanceQueueItem[]
|
||||
) {
|
||||
return queueItems
|
||||
.filter((item) =>
|
||||
item.governance_event_id === group.governance_event_id &&
|
||||
(
|
||||
item.executor_type === "hermes_km_review_dedupe_owner_archive" ||
|
||||
item.executor_type === "hermes_km_stale_ratio_recheck"
|
||||
)
|
||||
)
|
||||
.sort((a, b) =>
|
||||
String(b.completed_at ?? b.started_at ?? b.created_at ?? "").localeCompare(
|
||||
String(a.completed_at ?? a.started_at ?? a.created_at ?? "")
|
||||
)
|
||||
)
|
||||
.slice(0, 3);
|
||||
}
|
||||
|
||||
function kmArchiveExecutorKey(executorType?: string | null) {
|
||||
if (
|
||||
executorType === "hermes_km_review_dedupe_owner_archive" ||
|
||||
executorType === "hermes_km_stale_ratio_recheck"
|
||||
) {
|
||||
return executorType;
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
function kmDedupeActionKey(action?: string | null) {
|
||||
if (
|
||||
action === "owner_review_canonical_then_archive_duplicates" ||
|
||||
@@ -1747,6 +1786,7 @@ function KnowledgeGovernancePanel({
|
||||
);
|
||||
const previewKey = groupArchiveStatusKey(previewResult?.status);
|
||||
const finalResultKey = groupArchiveStatusKey(finalResult?.status);
|
||||
const archiveTrace = kmArchiveTraceForDedupeGroup(group, items);
|
||||
return (
|
||||
<div
|
||||
key={group.governance_event_id}
|
||||
@@ -1789,6 +1829,61 @@ function KnowledgeGovernancePanel({
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-2 border border-[#e0ddd4] bg-[#faf9f3] px-2 py-1.5">
|
||||
<p className="font-semibold text-[#2e2b26]">
|
||||
{t("archiveHistory.title")}
|
||||
</p>
|
||||
{archiveTrace.length === 0 ? (
|
||||
<p className="mt-1 text-[#77736a]">
|
||||
{t("archiveHistory.empty")}
|
||||
</p>
|
||||
) : (
|
||||
<div className="mt-1 grid gap-1">
|
||||
{archiveTrace.map((trace) => (
|
||||
<div
|
||||
key={trace.id}
|
||||
className="border border-[#d8d3c7] bg-white px-2 py-1 text-[#5f5b52]"
|
||||
>
|
||||
<p>
|
||||
{t("archiveHistory.item", {
|
||||
executor: t(
|
||||
`archiveHistory.executors.${kmArchiveExecutorKey(trace.executor_type)}` as never
|
||||
),
|
||||
status: t(
|
||||
`statuses.${governanceKmDispatchStatusKey(trace.dispatch_status)}` as never
|
||||
),
|
||||
stage: t(
|
||||
`stages.${governanceKmStageKey(trace.workflow_stage)}` as never
|
||||
),
|
||||
archived: trace.archived_count ?? "--",
|
||||
})}
|
||||
</p>
|
||||
{trace.dry_run_plan_fingerprint ? (
|
||||
<p className="mt-1 break-all font-mono text-[11px]">
|
||||
{t("archiveHistory.fingerprint", {
|
||||
fingerprint: trace.dry_run_plan_fingerprint,
|
||||
})}
|
||||
</p>
|
||||
) : null}
|
||||
{trace.stale_ratio_snapshot ? (
|
||||
<p className="mt-1">
|
||||
{t("archiveHistory.snapshot", {
|
||||
stale: trace.stale_ratio_snapshot.stale_count ?? "--",
|
||||
total: trace.stale_ratio_snapshot.total_count ?? "--",
|
||||
ratio: typeof trace.stale_ratio_snapshot.stale_ratio === "number"
|
||||
? formatStaleRatio(trace.stale_ratio_snapshot.stale_ratio)
|
||||
: "--",
|
||||
threshold: typeof trace.stale_ratio_snapshot.threshold === "number"
|
||||
? formatStaleRatio(trace.stale_ratio_snapshot.threshold)
|
||||
: "--",
|
||||
})}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-3 flex flex-wrap items-center gap-2">
|
||||
<button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user