feat(governance): bind km archive confirm to dry-run fingerprint
All checks were successful
Code Review / ai-code-review (push) Successful in 14s
Type Sync Check / check-type-sync (push) Successful in 31s
CD Pipeline / tests (push) Successful in 4m8s
CD Pipeline / build-and-deploy (push) Successful in 4m48s
CD Pipeline / post-deploy-checks (push) Successful in 2m13s

This commit is contained in:
Your Name
2026-05-20 09:19:32 +08:00
parent 83ca72e989
commit 584d2a77ff
8 changed files with 230 additions and 2 deletions

View File

@@ -1929,9 +1929,11 @@
"failed": "Archive action failed; refresh and verify the latest dedupe plan.",
"previewFailed": "Dry-run preview failed; refresh and verify the latest dedupe plan.",
"confirmFailed": "Archive confirmation failed; the backend may have detected a changed dedupe plan.",
"missingPreviewFingerprint": "Missing dry-run plan fingerprint; run the preview again first.",
"requiresOwner": "Run the dry-run preview first, then owner-confirm the archive; the backend rechecks the latest plan.",
"previewResult": "Dry run would archive {count}; writes KM: {writesKm}; writes audit: {writesAudit}",
"previewNext": "Next: only after owner confirmation will duplicate KM be soft-archived and audit / stale-ratio recheck rows be written.",
"planFingerprint": "Plan fingerprint: {fingerprint}",
"result": "Archived {archived}; audit dispatch: {audit}",
"recheck": "Stale-ratio recheck: {status}; dispatch: {dispatch}",
"snapshot": "Current stale {stale} / total {total}; ratio {ratio}; threshold {threshold}",

View File

@@ -1930,9 +1930,11 @@
"failed": "封存動作失敗;請重新整理後確認最新 dedupe plan。",
"previewFailed": "乾跑預覽失敗;請重新整理後確認最新 dedupe plan。",
"confirmFailed": "確認封存失敗;後端可能偵測到 dedupe plan 已變更。",
"missingPreviewFingerprint": "缺少乾跑 plan fingerprint請先重新執行乾跑預覽。",
"requiresOwner": "必須先乾跑預覽,再由 owner 確認封存;後端會重新比對最新 plan。",
"previewResult": "乾跑將封存 {count} 份;寫 KM{writesKm};寫稽核:{writesAudit}",
"previewNext": "下一步owner 確認後才會 soft archive duplicate KM 並寫入 audit / stale ratio 回測。",
"planFingerprint": "Plan fingerprint{fingerprint}",
"result": "已封存 {archived} 份;稽核 dispatch{audit}",
"recheck": "Stale ratio 回測:{status}dispatch{dispatch}",
"snapshot": "目前 stale {stale} / total {total}ratio {ratio};門檻 {threshold}",

View File

@@ -303,6 +303,7 @@ type KnowledgeReviewDraftArchiveResponse = {
| "already_active"
| "not_requested";
stale_ratio_recheck_dispatch_id?: string | null;
dry_run_plan_fingerprint?: string | null;
next_action: string;
generated_at?: string | null;
};
@@ -1558,6 +1559,22 @@ function KnowledgeGovernancePanel({
}, [t]);
const confirmArchiveDuplicates = useCallback(async (group: KnowledgeReviewDraftDedupeGroup) => {
const dryRunPlanFingerprint =
archiveActions[group.governance_event_id]?.previewResult?.dry_run_plan_fingerprint;
if (!dryRunPlanFingerprint) {
setArchiveActions((current) => ({
...current,
[group.governance_event_id]: {
previewLoading: false,
confirmLoading: false,
previewResult: current[group.governance_event_id]?.previewResult ?? null,
result: null,
error: t("archiveActions.missingPreviewFingerprint"),
},
}));
return;
}
setArchiveActions((current) => ({
...current,
[group.governance_event_id]: {
@@ -1576,6 +1593,7 @@ function KnowledgeGovernancePanel({
owner: "operator_console",
owner_approved: true,
dry_run: false,
dry_run_plan_fingerprint: dryRunPlanFingerprint,
},
15000
);
@@ -1592,7 +1610,7 @@ function KnowledgeGovernancePanel({
if (result?.status === "archived" || result?.status === "noop_already_archived") {
onArchived();
}
}, [onArchived, t]);
}, [archiveActions, onArchived, t]);
return (
<section className="border border-[#e0ddd4] bg-white">
@@ -1723,7 +1741,10 @@ function KnowledgeGovernancePanel({
const archiveAction = archiveActions[group.governance_event_id];
const previewResult = archiveAction?.previewResult ?? null;
const finalResult = archiveAction?.result ?? null;
const previewReady = previewResult?.status === "dry_run";
const previewReady = (
previewResult?.status === "dry_run" &&
Boolean(previewResult.dry_run_plan_fingerprint)
);
const previewKey = groupArchiveStatusKey(previewResult?.status);
const finalResultKey = groupArchiveStatusKey(finalResult?.status);
return (
@@ -1824,6 +1845,11 @@ function KnowledgeGovernancePanel({
<p className="mt-1 text-[#5f5b52]">
{t("archiveActions.previewNext")}
</p>
<p className="mt-1 break-all font-mono text-[11px] text-[#5f5b52]">
{t("archiveActions.planFingerprint", {
fingerprint: previewResult.dry_run_plan_fingerprint ?? "--",
})}
</p>
{previewResult.stale_ratio_snapshot ? (
<p className="mt-1 text-[#5f5b52]">
{t("archiveActions.snapshot", {