create-principles-disciple 1.146.1 → 1.146.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/_release/product-identity.json +2 -2
  2. package/console/dist/server/models/ActivationsConsoleModel.d.ts +3 -0
  3. package/console/dist/server/models/ActivationsConsoleModel.js +25 -1
  4. package/console/dist/server/models/ApprovalsConsoleModel.d.ts +5 -0
  5. package/console/dist/server/models/ApprovalsConsoleModel.js +40 -2
  6. package/console/dist/server/routes/activations.js +47 -0
  7. package/console/dist/server/routes/approvals.js +1 -1
  8. package/console/dist/server/routes/health.js +17 -0
  9. package/console/dist/server/routes/update-transaction.js +63 -3
  10. package/console/dist/shared/approved-rule-revision-contract.d.ts +8 -0
  11. package/console/dist/shared/approved-rule-revision-contract.js +48 -0
  12. package/console/dist/ui/api.d.ts +3 -0
  13. package/console/dist/ui/api.js +9 -0
  14. package/console/dist/ui/i18n/en.json +9 -0
  15. package/console/dist/ui/i18n/zh-CN.json +9 -0
  16. package/console/dist/ui/pages/activation/ActivationPage.js +69 -2
  17. package/console/dist/ui/pages/settings/UpdatePage.js +1 -1
  18. package/console/dist/ui/utils/validators.d.ts +10 -0
  19. package/console/dist/ui/utils/validators.js +11 -1
  20. package/console/dist/web/assets/app.css +3 -0
  21. package/console/dist/web/assets/app.js +31 -31
  22. package/core/dist/runtime-v2/activation/activation-dispatcher.d.ts +2 -0
  23. package/core/dist/runtime-v2/activation/activation-dispatcher.d.ts.map +1 -1
  24. package/core/dist/runtime-v2/activation/activation-dispatcher.js +60 -22
  25. package/core/dist/runtime-v2/activation/activation-dispatcher.js.map +1 -1
  26. package/core/dist/runtime-v2/activation/activation-types.d.ts +12 -2
  27. package/core/dist/runtime-v2/activation/activation-types.d.ts.map +1 -1
  28. package/core/dist/runtime-v2/activation/activation-types.js.map +1 -1
  29. package/core/dist/runtime-v2/activation/index.d.ts +2 -0
  30. package/core/dist/runtime-v2/activation/index.d.ts.map +1 -1
  31. package/core/dist/runtime-v2/activation/index.js +2 -0
  32. package/core/dist/runtime-v2/activation/index.js.map +1 -1
  33. package/core/dist/runtime-v2/activation/rule-replacement.d.ts +38 -0
  34. package/core/dist/runtime-v2/activation/rule-replacement.d.ts.map +1 -0
  35. package/core/dist/runtime-v2/activation/rule-replacement.js +44 -0
  36. package/core/dist/runtime-v2/activation/rule-replacement.js.map +1 -0
  37. package/core/dist/runtime-v2/activation/sqlite-activation-state-store.d.ts +9 -1
  38. package/core/dist/runtime-v2/activation/sqlite-activation-state-store.d.ts.map +1 -1
  39. package/core/dist/runtime-v2/activation/sqlite-activation-state-store.js +53 -23
  40. package/core/dist/runtime-v2/activation/sqlite-activation-state-store.js.map +1 -1
  41. package/core/dist/runtime-v2/activation/writers/rule-host-writer.d.ts.map +1 -1
  42. package/core/dist/runtime-v2/activation/writers/rule-host-writer.js +15 -2
  43. package/core/dist/runtime-v2/activation/writers/rule-host-writer.js.map +1 -1
  44. package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.d.ts +2 -0
  45. package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.d.ts.map +1 -1
  46. package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.js +5 -1
  47. package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.js.map +1 -1
  48. package/core/dist/runtime-v2/index.d.ts +3 -1
  49. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  50. package/core/dist/runtime-v2/index.js +2 -1
  51. package/core/dist/runtime-v2/index.js.map +1 -1
  52. package/core/dist/runtime-v2/internalization/approved-rule-revision.d.ts +51 -0
  53. package/core/dist/runtime-v2/internalization/approved-rule-revision.d.ts.map +1 -0
  54. package/core/dist/runtime-v2/internalization/approved-rule-revision.js +211 -0
  55. package/core/dist/runtime-v2/internalization/approved-rule-revision.js.map +1 -0
  56. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.d.ts +1 -1
  57. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.d.ts.map +1 -1
  58. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js +2 -1
  59. package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js.map +1 -1
  60. package/core/dist/runtime-v2/internalization/bash-risk.d.ts +6 -0
  61. package/core/dist/runtime-v2/internalization/bash-risk.d.ts.map +1 -1
  62. package/core/dist/runtime-v2/internalization/bash-risk.js +120 -0
  63. package/core/dist/runtime-v2/internalization/bash-risk.js.map +1 -1
  64. package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
  65. package/core/dist/runtime-v2/internalization/evaluator-runner.js +4 -1
  66. package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
  67. package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts +2 -1
  68. package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts.map +1 -1
  69. package/core/dist/runtime-v2/internalization/peer-runner-contracts.js.map +1 -1
  70. package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts +21 -0
  71. package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts.map +1 -1
  72. package/core/dist/runtime-v2/internalization/pitask-metadata.js +30 -0
  73. package/core/dist/runtime-v2/internalization/pitask-metadata.js.map +1 -1
  74. package/core/dist/runtime-v2/internalization/rule-host-contracts.d.ts +2 -0
  75. package/core/dist/runtime-v2/internalization/rule-host-contracts.d.ts.map +1 -1
  76. package/core/dist/runtime-v2/internalization/rule-host-input-builder.d.ts.map +1 -1
  77. package/core/dist/runtime-v2/internalization/rule-host-input-builder.js +6 -0
  78. package/core/dist/runtime-v2/internalization/rule-host-input-builder.js.map +1 -1
  79. package/core/dist/runtime-v2/internalization/rulecode-verification-obligations.d.ts.map +1 -1
  80. package/core/dist/runtime-v2/internalization/rulecode-verification-obligations.js +32 -0
  81. package/core/dist/runtime-v2/internalization/rulecode-verification-obligations.js.map +1 -1
  82. package/core/dist/runtime-v2/proven-channel-baseline.js +1 -1
  83. package/core/dist/runtime-v2/proven-channel-baseline.js.map +1 -1
  84. package/core/dist/runtime-v2/store/task/sqlite-approved-rule-revision.d.ts +15 -0
  85. package/core/dist/runtime-v2/store/task/sqlite-approved-rule-revision.d.ts.map +1 -0
  86. package/core/dist/runtime-v2/store/task/sqlite-approved-rule-revision.js +39 -0
  87. package/core/dist/runtime-v2/store/task/sqlite-approved-rule-revision.js.map +1 -0
  88. package/core/package.json +1 -1
  89. package/dist/installer.d.ts +81 -0
  90. package/dist/installer.d.ts.map +1 -1
  91. package/dist/installer.js +480 -117
  92. package/dist/installer.js.map +1 -1
  93. package/dist/update/console-surface.d.ts +1 -1
  94. package/dist/update/console-surface.d.ts.map +1 -1
  95. package/dist/update/console-surface.js +1 -1
  96. package/dist/update/console-surface.js.map +1 -1
  97. package/dist/update/transaction-journal.d.ts +47 -0
  98. package/dist/update/transaction-journal.d.ts.map +1 -1
  99. package/dist/update/transaction-journal.js +87 -2
  100. package/dist/update/transaction-journal.js.map +1 -1
  101. package/host-runtime/dist/production-rulehost-gate.js +11 -2
  102. package/host-runtime/package.json +1 -1
  103. package/package.json +2 -2
  104. package/pd-cli/package.json +1 -1
  105. package/plugin/dist/bundle.js +477 -472
  106. package/plugin/dist/openclaw.plugin.json +1 -1
  107. package/plugin/dist/rulehost-evidence.js +52 -49
  108. package/plugin/openclaw.plugin.json +1 -1
  109. package/plugin/package.json +1 -1
  110. package/release-manager/dist/installer.d.ts +81 -0
  111. package/release-manager/dist/installer.d.ts.map +1 -1
  112. package/release-manager/dist/installer.js +480 -117
  113. package/release-manager/dist/installer.js.map +1 -1
  114. package/release-manager/dist/update/console-surface.d.ts +1 -1
  115. package/release-manager/dist/update/console-surface.d.ts.map +1 -1
  116. package/release-manager/dist/update/console-surface.js +1 -1
  117. package/release-manager/dist/update/console-surface.js.map +1 -1
  118. package/release-manager/dist/update/transaction-journal.d.ts +47 -0
  119. package/release-manager/dist/update/transaction-journal.d.ts.map +1 -1
  120. package/release-manager/dist/update/transaction-journal.js +87 -2
  121. package/release-manager/dist/update/transaction-journal.js.map +1 -1
  122. package/release-manager/package.json +2 -2
@@ -8,7 +8,7 @@ import { PageLoading } from "../../components/layout/page-loading.js";
8
8
  import { SectionTitle } from "../../components/layout/section-title.js";
9
9
  import { ShinyText } from "../../components/ui/shiny-text.js";
10
10
  import { Button } from "../../components/ui/button.js";
11
- import { fetchAllActivations, disableActivation, fetchLifecycleMetrics, fetchReceiptCounts, fetchRuleCodeOwnerReview, proposePromptRevision, ruleCodeDecision, pauseAllRuleCode, releaseRuleCodePause, } from "../../api.js";
11
+ import { fetchAllActivations, disableActivation, fetchLifecycleMetrics, fetchReceiptCounts, fetchRuleCodeOwnerReview, fetchApprovedRuleRevisionPreview, reviseApprovedRuleOnce, proposePromptRevision, ruleCodeDecision, pauseAllRuleCode, releaseRuleCodePause, } from "../../api.js";
12
12
  import { ReceiptCoverageDisclosure, getReceiptSourceStatusLabelKey } from "../../components/receipts/ReceiptCoverageDisclosure.js";
13
13
  import { validateActivationsData, validateLifecycleMetricsData, isReversibleChannel, } from "./ActivationValidators.js";
14
14
  import { enumLabel } from "../../utils/enum-labels.js";
@@ -41,6 +41,73 @@ function CapabilityBoundaryDeclaration() {
41
41
  const { t } = useTranslation();
42
42
  return (_jsx("div", { className: "px-[18px] py-[14px] bg-panel border border-line rounded-[6px] text-ink-3 text-[13px] leading-relaxed", role: "note", "aria-label": t("pages.activation.boundaryLabel"), children: t("pages.activation.boundaryText") }));
43
43
  }
44
+ function ApprovedRuleRevisionPanel({ activationId, onChanged }) {
45
+ const { t } = useTranslation();
46
+ const [preview, setPreview] = useState(null);
47
+ const [feedback, setFeedback] = useState('');
48
+ const [error, setError] = useState(null);
49
+ const [busy, setBusy] = useState(false);
50
+ const [submitted, setSubmitted] = useState(false);
51
+ // A concurrent/earlier revision already holds this artifact's budget. That is
52
+ // not this request succeeding, and the feedback just typed was NOT recorded.
53
+ const [alreadyStarted, setAlreadyStarted] = useState(false);
54
+ // Preserve the exact request across a transport retry; a new ID is not a new
55
+ // budget (the server's budget is anchored on the artifact digest + receipt,
56
+ // so editing the feedback and resending cannot spend a second round).
57
+ const [pendingRequestId, setPendingRequestId] = useState(null);
58
+ const load = useCallback(async () => {
59
+ const result = await fetchApprovedRuleRevisionPreview(activationId);
60
+ if (result.success) {
61
+ setPreview(result.data);
62
+ setError(null);
63
+ }
64
+ else
65
+ setError(result.error);
66
+ }, [activationId]);
67
+ useEffect(() => { void load(); }, [load]);
68
+ const revise = async () => {
69
+ if (preview?.status !== 'ready')
70
+ return;
71
+ const input = {
72
+ expectedArtifactId: preview.expectedArtifactId, expectedArtifactDigest: preview.expectedArtifactDigest,
73
+ expectedBindingRevision: preview.expectedBindingRevision, expectedControlVersion: preview.expectedControlVersion,
74
+ expectedTaskRevisionEpoch: preview.expectedTaskRevisionEpoch,
75
+ requestId: pendingRequestId ?? crypto.randomUUID(), feedback: feedback.trim(),
76
+ };
77
+ setPendingRequestId(input.requestId);
78
+ setBusy(true);
79
+ setError(null);
80
+ const result = await reviseApprovedRuleOnce(activationId, input);
81
+ setBusy(false);
82
+ if (!result.success) {
83
+ setError(result.error);
84
+ return;
85
+ }
86
+ if (result.data.status === 'refused') {
87
+ setPendingRequestId(null);
88
+ // Take the server verdict as the current view: if the refresh below also
89
+ // fails, the stale `ready` form must not stay submittable.
90
+ setPreview(result.data);
91
+ setError(result.data.nextAction);
92
+ await load();
93
+ return;
94
+ }
95
+ if (result.data.status === 'already_started') {
96
+ toast.info(t('pages.activation.ruleRevisionAlreadyExists'));
97
+ setAlreadyStarted(true);
98
+ return;
99
+ }
100
+ // Announce before onChanged(): the parent reload unmounts this panel, and a
101
+ // panel-local confirmation would disappear with it.
102
+ toast.success(t('pages.activation.ruleRevisionStarted'));
103
+ setSubmitted(true);
104
+ onChanged();
105
+ };
106
+ return _jsxs("div", { className: "mt-3 rounded border border-line p-3 text-[12px] text-ink-3", "data-testid": `approved-rule-revision-${activationId}`, children: [_jsx("div", { className: "font-medium text-ink", children: t('pages.activation.ruleRevisionTitle') }), _jsx("p", { className: "mt-1", children: t('pages.activation.ruleRevisionBoundary') }), error && _jsx("p", { role: "alert", className: "mt-2 text-danger", children: error }), alreadyStarted ? _jsxs("div", { className: "mt-2", children: [_jsx("p", { role: "status", children: t('pages.activation.ruleRevisionAlreadyExists') }), _jsx(Link, { to: "/focus", className: "mt-1 inline-block text-gov underline", children: t('pages.activation.ruleRevisionReviewQueue') })] })
107
+ : submitted ? _jsxs("div", { className: "mt-2", children: [_jsx("p", { role: "status", className: "text-green", children: t('pages.activation.ruleRevisionStarted') }), _jsx(Link, { to: "/focus", className: "mt-1 inline-block text-gov underline", children: t('pages.activation.ruleRevisionReviewQueue') })] })
108
+ : preview?.status === 'ready' ? _jsxs(_Fragment, { children: [_jsxs("label", { className: "mt-2 block", children: [t('pages.activation.ruleRevisionFeedback'), _jsx("textarea", { value: feedback, maxLength: 4000, disabled: busy, onChange: event => { setFeedback(event.target.value); setPendingRequestId(null); }, className: "mt-1 min-h-20 w-full rounded border border-line bg-panel px-3 py-2 text-ink" })] }), _jsx(Button, { disabled: busy || feedback.trim().length === 0, onClick: () => void revise(), children: t('pages.activation.ruleRevisionOnce') })] }) : preview?.status === 'refused' ? _jsx("p", { className: "mt-2", children: preview.nextAction })
109
+ : _jsx("p", { className: "mt-2", children: t('pages.activation.ownerDecisionLoading') })] });
110
+ }
44
111
  function RuleCodeOwnerPanel({ record, onChanged }) {
45
112
  const { t } = useTranslation();
46
113
  const [review, setReview] = useState(null);
@@ -96,7 +163,7 @@ function RuleCodeOwnerPanel({ record, onChanged }) {
96
163
  const value = (number) => number === null ? t('pages.activation.notCollected') : String(number);
97
164
  const affectedTools = Array.isArray(review.artifact.content?.affectedTools) ? review.artifact.content.affectedTools.filter((item) => typeof item === 'string') : [];
98
165
  const renderWindow = (title, window) => _jsxs("div", { className: "rounded border border-line p-2", children: [_jsx("div", { className: "font-medium text-ink", children: title }), _jsxs("div", { className: "mt-1 grid grid-cols-2 gap-1 md:grid-cols-5", children: [_jsxs("span", { children: [t('pages.activation.eligibleEvaluations'), ": ", value(window.eligible)] }), _jsxs("span", { children: [t('pages.activation.matched'), ": ", value(window.matched)] }), _jsxs("span", { children: [t('pages.activation.blocked'), ": ", value(window.blocked)] }), _jsxs("span", { children: [t('pages.activation.unhealthy'), ": ", value(window.unhealthy)] }), _jsxs("span", { children: [t('pages.activation.circuitTrips'), ": ", window.circuitTrips] })] }), _jsxs("div", { className: "mt-1 text-ink-4", children: [t('pages.activation.toolDistribution'), ": ", window.toolDistribution === null ? t('pages.activation.notCollected') : Object.entries(window.toolDistribution).map(([tool, count]) => `${tool} ${count}`).join(', ') || '—'] })] });
99
- return _jsxs("div", { className: "mt-4 rounded-[5px] border border-gov/25 bg-surface/70 p-4", "data-testid": `owner-review-${record.activationId}`, children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("strong", { className: "text-[13px] text-ink", children: t('pages.activation.ownerDecisionTitle') }), _jsx("span", { className: "font-mono text-[11px] text-gov", children: review.readiness.status }), review.controlState?.enforcement === 'safety_isolated' && _jsx("span", { className: "text-danger text-[11px]", children: t('pages.activation.safetyIsolation') })] }), _jsxs("div", { className: "mt-2 grid grid-cols-2 gap-2 text-[12px] text-ink-3 md:grid-cols-4", children: [_jsxs("span", { children: [t('pages.activation.eligibleEvaluations'), ": ", value(summary.observed)] }), _jsxs("span", { children: [t('pages.activation.matched'), ": ", value(summary.matched)] }), _jsxs("span", { children: [t('pages.activation.wouldBlock'), ": ", value(summary.wouldBlock)] }), _jsxs("span", { children: [t('pages.activation.wouldAllow'), ": ", value(summary.wouldAllow)] }), _jsxs("span", { children: [t('pages.activation.requireApproval'), ": ", value(summary.requireApproval)] }), _jsxs("span", { children: [t('pages.activation.autoCorrect'), ": ", value(summary.autoCorrect)] }), _jsxs("span", { children: [t('pages.activation.neutralControl'), ": ", value(summary.neutralControl)] }), _jsxs("span", { children: [t('pages.activation.evaluationErrors'), ": ", value(summary.errors)] })] }), _jsx("div", { className: "mt-2 text-[11px] text-ink-4", children: t('pages.activation.shadowWindow', { first: summary.firstObservedAt ?? t('pages.activation.notCollected'), last: summary.lastObservedAt ?? t('pages.activation.notCollected') }) }), _jsx("div", { className: "mt-2 text-[12px] text-ink-3", children: t('pages.activation.runtimeCapability', { version: review.runtimeCapability.hostRuntimeVersion, supported: t(review.runtimeCapability.shadowEvidence ? 'pages.activation.supported' : 'pages.activation.unsupported') }) }), _jsx("div", { className: "mt-2 text-[12px] text-amber", children: t('pages.activation.worstImpact', { tools: affectedTools.join(', ') || t('pages.activation.notCollected') }) }), _jsxs("div", { className: "mt-3 text-[12px] text-ink-3", children: [_jsx("div", { className: "mb-1 font-medium text-ink", children: t('pages.activation.hardSafetyChecks') }), review.readiness.evidenceSnapshot.safetyGateResults.map(check => _jsxs("div", { className: check.status === 'passed' ? 'text-green' : 'text-danger', children: [t(`pages.activation.checks.${check.checkId}`, { defaultValue: check.checkId }), ": ", t(`pages.activation.checkStatus.${check.status}`, { defaultValue: check.status }), check.reasonCode ? ` · ${t(`pages.activation.reasonCodes.${check.reasonCode}`, { defaultValue: check.reasonCode })}` : ''] }, check.checkId))] }), review.readiness.failedChecks.length > 0 && _jsx("ul", { className: "mt-2 text-[12px] text-danger", children: review.readiness.failedChecks.map(check => _jsxs("li", { children: [t(`pages.activation.checks.${check.checkId}`, { defaultValue: check.checkId }), ": ", t(`pages.activation.reasonCodes.${check.reasonCode}`, { defaultValue: check.reasonCode })] }, check.checkId)) }), _jsxs("details", { className: "mt-2 text-[12px]", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.activation.scopeArtifactCode') }), _jsx("pre", { className: "mt-2 max-h-64 overflow-auto whitespace-pre-wrap text-ink-3", children: JSON.stringify(review.artifact.content, null, 2) })] }), _jsxs("div", { className: "mt-3 grid gap-2 text-[11px] text-ink-3 md:grid-cols-2", children: [renderWindow(t('pages.activation.live24Hours'), review.liveMetrics.last24Hours), renderWindow(t('pages.activation.live7Days'), review.liveMetrics.last7Days)] }), _jsx("div", { className: "mt-2 text-[11px] text-ink-3", children: t('pages.activation.behaviorDrift', { approved: value(review.behaviorDrift.approvedBlockRate), live: value(review.behaviorDrift.liveBlockRate), delta: value(review.behaviorDrift.delta) }) }), review.liveMetrics.representativeSamples.length > 0 && _jsxs("details", { className: "mt-2 text-[12px]", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.activation.representativeSamples') }), review.liveMetrics.representativeSamples.map((sample, index) => _jsxs("div", { className: "font-mono text-ink-3", children: [sample.toolName, " \u00B7 ", sample.decision, " \u00B7 ", sample.pathCategory] }, `${sample.toolName}-${index}`))] }), review.decisions.length > 0 && _jsxs("details", { className: "mt-2 text-[12px]", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.activation.ownerDecisionHistory') }), review.decisions.map(decision => _jsxs("div", { className: "mt-1 border-l border-line pl-2 text-ink-3", children: [_jsx("span", { className: "font-mono", children: decision.decidedAt }), " \u00B7 ", decision.decision, " \u00B7 ", decision.principalKind, ":", decision.actorId, " \u00B7 ", decision.authenticationMethod, " \u00B7 ", decision.reasonCode, decision.note ? ` · ${decision.note}` : ''] }, decision.decisionId))] }), _jsx("div", { className: "mt-2 text-[11px] text-ink-4", children: t('pages.activation.circuitConditions') }), review.readiness.status === 'evidence_insufficient' && _jsxs("label", { className: "mt-3 block text-[12px] text-ink-3", children: [t('pages.activation.overrideReason'), _jsxs("select", { value: overrideReason, onChange: event => setOverrideReason(event.target.value), className: "mt-1 w-full rounded border border-line bg-panel px-3 py-2 text-ink", children: [_jsx("option", { value: "rare_behavior", children: t('pages.activation.overrideRareBehavior') }), _jsx("option", { value: "controlled_rollout", children: t('pages.activation.overrideControlledRollout') }), _jsx("option", { value: "owner_accepts_limited_evidence", children: t('pages.activation.overrideOwnerAccepts') })] })] }), _jsx("input", { value: note, onChange: event => setNote(event.target.value), placeholder: t('pages.activation.ownerReviewNote'), className: "mt-3 w-full rounded border border-line bg-panel px-3 py-2 text-[12px] text-ink" }), _jsxs("div", { className: "mt-3 flex flex-wrap gap-2", children: [review.globalPause?.status === 'paused' && _jsx(Button, { disabled: busy || !review.ownerDecisionEnabled, variant: "outline", onClick: () => void releasePause(), children: t('pages.activation.releaseGlobalPause') }), record.action === 'code_tool_hook_shadow_activate' && _jsxs(_Fragment, { children: [_jsx(Button, { disabled: busy || !review.ownerDecisionEnabled, onClick: () => void decide('continue-observing'), children: t('pages.activation.continueObserving') }), _jsx(Button, { disabled: busy || !review.ownerDecisionEnabled, variant: "outline", onClick: () => void decide('reject-after-shadow'), children: t('pages.activation.rejectAfterShadow') }), _jsx(Button, { disabled: busy || !review.ownerDecisionEnabled || (review.readiness.status !== 'ready' && review.readiness.status !== 'evidence_insufficient') || (review.readiness.status === 'evidence_insufficient' && note.trim().length === 0), onClick: () => void decide('promote'), children: t('pages.activation.confirmLive') })] }), (record.action === 'code_tool_hook_live_activate' || review.controlState?.enforcement === 'safety_isolated') && _jsx(Button, { disabled: busy, variant: "destructive", onClick: () => void decide('emergency-deactivate'), children: t('pages.activation.emergencyDeactivate') }), review.controlState?.enforcement === 'safety_isolated' && _jsx(Button, { disabled: busy || !review.ownerDecisionEnabled, variant: "outline", onClick: () => void decide('recover-to-shadow'), children: t('pages.activation.recoverToShadow') })] }), !review.ownerDecisionEnabled && _jsx("div", { className: "mt-2 text-[11px] text-amber", children: t('pages.activation.rolloutDisabled') })] });
166
+ return _jsxs("div", { className: "mt-4 rounded-[5px] border border-gov/25 bg-surface/70 p-4", "data-testid": `owner-review-${record.activationId}`, children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("strong", { className: "text-[13px] text-ink", children: t('pages.activation.ownerDecisionTitle') }), _jsx("span", { className: "font-mono text-[11px] text-gov", children: review.readiness.status }), review.controlState?.enforcement === 'safety_isolated' && _jsx("span", { className: "text-danger text-[11px]", children: t('pages.activation.safetyIsolation') })] }), _jsxs("div", { className: "mt-2 grid grid-cols-2 gap-2 text-[12px] text-ink-3 md:grid-cols-4", children: [_jsxs("span", { children: [t('pages.activation.eligibleEvaluations'), ": ", value(summary.observed)] }), _jsxs("span", { children: [t('pages.activation.matched'), ": ", value(summary.matched)] }), _jsxs("span", { children: [t('pages.activation.wouldBlock'), ": ", value(summary.wouldBlock)] }), _jsxs("span", { children: [t('pages.activation.wouldAllow'), ": ", value(summary.wouldAllow)] }), _jsxs("span", { children: [t('pages.activation.requireApproval'), ": ", value(summary.requireApproval)] }), _jsxs("span", { children: [t('pages.activation.autoCorrect'), ": ", value(summary.autoCorrect)] }), _jsxs("span", { children: [t('pages.activation.neutralControl'), ": ", value(summary.neutralControl)] }), _jsxs("span", { children: [t('pages.activation.evaluationErrors'), ": ", value(summary.errors)] })] }), _jsx("div", { className: "mt-2 text-[11px] text-ink-4", children: t('pages.activation.shadowWindow', { first: summary.firstObservedAt ?? t('pages.activation.notCollected'), last: summary.lastObservedAt ?? t('pages.activation.notCollected') }) }), _jsx("div", { className: "mt-2 text-[12px] text-ink-3", children: t('pages.activation.runtimeCapability', { version: review.runtimeCapability.hostRuntimeVersion, supported: t(review.runtimeCapability.shadowEvidence ? 'pages.activation.supported' : 'pages.activation.unsupported') }) }), _jsx("div", { className: "mt-2 text-[12px] text-amber", children: t('pages.activation.worstImpact', { tools: affectedTools.join(', ') || t('pages.activation.notCollected') }) }), _jsxs("div", { className: "mt-3 text-[12px] text-ink-3", children: [_jsx("div", { className: "mb-1 font-medium text-ink", children: t('pages.activation.hardSafetyChecks') }), review.readiness.evidenceSnapshot.safetyGateResults.map(check => _jsxs("div", { className: check.status === 'passed' ? 'text-green' : 'text-danger', children: [t(`pages.activation.checks.${check.checkId}`, { defaultValue: check.checkId }), ": ", t(`pages.activation.checkStatus.${check.status}`, { defaultValue: check.status }), check.reasonCode ? ` · ${t(`pages.activation.reasonCodes.${check.reasonCode}`, { defaultValue: check.reasonCode })}` : ''] }, check.checkId))] }), review.readiness.failedChecks.length > 0 && _jsx("ul", { className: "mt-2 text-[12px] text-danger", children: review.readiness.failedChecks.map(check => _jsxs("li", { children: [t(`pages.activation.checks.${check.checkId}`, { defaultValue: check.checkId }), ": ", t(`pages.activation.reasonCodes.${check.reasonCode}`, { defaultValue: check.reasonCode })] }, check.checkId)) }), _jsxs("details", { className: "mt-2 text-[12px]", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.activation.scopeArtifactCode') }), _jsx("pre", { className: "mt-2 max-h-64 overflow-auto whitespace-pre-wrap text-ink-3", children: JSON.stringify(review.artifact.content, null, 2) })] }), _jsxs("div", { className: "mt-3 grid gap-2 text-[11px] text-ink-3 md:grid-cols-2", children: [renderWindow(t('pages.activation.live24Hours'), review.liveMetrics.last24Hours), renderWindow(t('pages.activation.live7Days'), review.liveMetrics.last7Days)] }), _jsx("div", { className: "mt-2 text-[11px] text-ink-3", children: t('pages.activation.behaviorDrift', { approved: value(review.behaviorDrift.approvedBlockRate), live: value(review.behaviorDrift.liveBlockRate), delta: value(review.behaviorDrift.delta) }) }), review.liveMetrics.representativeSamples.length > 0 && _jsxs("details", { className: "mt-2 text-[12px]", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.activation.representativeSamples') }), review.liveMetrics.representativeSamples.map((sample, index) => _jsxs("div", { className: "font-mono text-ink-3", children: [sample.toolName, " \u00B7 ", sample.decision, " \u00B7 ", sample.pathCategory] }, `${sample.toolName}-${index}`))] }), review.decisions.length > 0 && _jsxs("details", { className: "mt-2 text-[12px]", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('pages.activation.ownerDecisionHistory') }), review.decisions.map(decision => _jsxs("div", { className: "mt-1 border-l border-line pl-2 text-ink-3", children: [_jsx("span", { className: "font-mono", children: decision.decidedAt }), " \u00B7 ", decision.decision, " \u00B7 ", decision.principalKind, ":", decision.actorId, " \u00B7 ", decision.authenticationMethod, " \u00B7 ", decision.reasonCode, decision.note ? ` · ${decision.note}` : ''] }, decision.decisionId))] }), _jsx("div", { className: "mt-2 text-[11px] text-ink-4", children: t('pages.activation.circuitConditions') }), record.action === 'code_tool_hook_shadow_activate' && _jsx(ApprovedRuleRevisionPanel, { activationId: record.activationId, onChanged: onChanged }), review.readiness.status === 'evidence_insufficient' && _jsxs("label", { className: "mt-3 block text-[12px] text-ink-3", children: [t('pages.activation.overrideReason'), _jsxs("select", { value: overrideReason, onChange: event => setOverrideReason(event.target.value), className: "mt-1 w-full rounded border border-line bg-panel px-3 py-2 text-ink", children: [_jsx("option", { value: "rare_behavior", children: t('pages.activation.overrideRareBehavior') }), _jsx("option", { value: "controlled_rollout", children: t('pages.activation.overrideControlledRollout') }), _jsx("option", { value: "owner_accepts_limited_evidence", children: t('pages.activation.overrideOwnerAccepts') })] })] }), _jsx("input", { value: note, onChange: event => setNote(event.target.value), placeholder: t('pages.activation.ownerReviewNote'), className: "mt-3 w-full rounded border border-line bg-panel px-3 py-2 text-[12px] text-ink" }), _jsxs("div", { className: "mt-3 flex flex-wrap gap-2", children: [review.globalPause?.status === 'paused' && _jsx(Button, { disabled: busy || !review.ownerDecisionEnabled, variant: "outline", onClick: () => void releasePause(), children: t('pages.activation.releaseGlobalPause') }), record.action === 'code_tool_hook_shadow_activate' && _jsxs(_Fragment, { children: [_jsx(Button, { disabled: busy || !review.ownerDecisionEnabled, onClick: () => void decide('continue-observing'), children: t('pages.activation.continueObserving') }), _jsx(Button, { disabled: busy || !review.ownerDecisionEnabled, variant: "outline", onClick: () => void decide('reject-after-shadow'), children: t('pages.activation.rejectAfterShadow') }), _jsx(Button, { disabled: busy || !review.ownerDecisionEnabled || (review.readiness.status !== 'ready' && review.readiness.status !== 'evidence_insufficient') || (review.readiness.status === 'evidence_insufficient' && note.trim().length === 0), onClick: () => void decide('promote'), children: t('pages.activation.confirmLive') })] }), (record.action === 'code_tool_hook_live_activate' || review.controlState?.enforcement === 'safety_isolated') && _jsx(Button, { disabled: busy, variant: "destructive", onClick: () => void decide('emergency-deactivate'), children: t('pages.activation.emergencyDeactivate') }), review.controlState?.enforcement === 'safety_isolated' && _jsx(Button, { disabled: busy || !review.ownerDecisionEnabled, variant: "outline", onClick: () => void decide('recover-to-shadow'), children: t('pages.activation.recoverToShadow') })] }), !review.ownerDecisionEnabled && _jsx("div", { className: "mt-2 text-[11px] text-amber", children: t('pages.activation.rolloutDisabled') })] });
100
167
  }
101
168
  function ActivationFactCard({ record, lifecycleData, receiptCount, onDisable, disabling, onChanged, }) {
102
169
  const { t, i18n } = useTranslation();
@@ -202,7 +202,7 @@ export function UpdatePage() {
202
202
  return (_jsxs(PageShell, { children: [_jsxs("div", { className: "animate-[pdFadeIn_400ms_ease-out]", children: [recoveryData?.needsRecovery && recoveryData.unfinished.length > 0 && (_jsxs("div", { className: "mb-6 p-4 bg-amber/5 border border-amber/35 rounded-[6px] text-[13px] leading-relaxed text-ink-2", role: "alert", children: [_jsx("p", { className: "font-medium text-amber", children: t("pages.update.recoveryBanner") }), recoveryData.unfinished.slice(0, 3).map((item) => (_jsx("p", { className: "mt-1 font-mono text-[12px] text-ink-3", children: t("pages.update.recoveryItem", {
203
203
  id: item.transactionId,
204
204
  phase: item.lastState ?? "—",
205
- }) }, item.transactionId))), recoveryData.nextAction && (_jsx("p", { className: "mt-2 text-[12px] text-ink-3", children: recoveryData.nextAction }))] })), _jsx("div", { className: "font-mono text-[12px] tracking-[0.14em] text-ink-3 uppercase mb-3", children: t("pages.update.eyebrow") }), _jsx("h1", { className: "text-[29px] font-semibold tracking-tight text-ink mb-2", children: t("pages.update.title") }), _jsx("p", { className: "text-ink-3 text-[14px] max-w-[760px] leading-relaxed mb-7", children: t("pages.update.subtitle") }), _jsxs("section", { className: "mb-8", "aria-labelledby": "section-update-status", children: [_jsx(SectionTitle, { id: "section-update-status", children: t("pages.update.currentVersion") }), _jsxs("div", { className: "bg-panel border border-line rounded-[6px] p-5", children: [_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-ink-3 text-[13px]", children: t("pages.update.currentVersion") }), _jsx("span", { className: "font-mono text-[13px] text-ink", children: statusData?.currentVersion ?? "—" })] }), statusData?.identityDivergence && (_jsx("div", { className: "border border-amber/35 rounded-[4px] px-3 py-2 text-[12px] leading-relaxed text-ink-2", children: t("pages.update.identityDivergence", {
205
+ }) }, item.transactionId))), recoveryData.nextAction && (_jsx("p", { className: "mt-2 text-[12px] text-ink-3", children: recoveryData.nextAction }))] })), recoveryData?.needsRecovery && recoveryData.recoveryRequired.length > 0 && (_jsxs("div", { className: "mb-6 p-4 bg-amber/5 border border-amber/35 rounded-[6px] text-[13px] leading-relaxed text-ink-2", role: "alert", children: [_jsx("p", { className: "font-medium text-amber", children: t("pages.update.recoveryRequiredBanner") }), recoveryData.recoveryRequired.slice(0, 3).map((item) => (_jsx("p", { className: "mt-1 font-mono text-[12px] text-ink-3", children: t("pages.update.recoveryRequiredItem", { id: item.transactionId }) }, item.transactionId))), recoveryData.nextAction && (_jsx("p", { className: "mt-2 text-[12px] text-ink-3", children: recoveryData.nextAction }))] })), _jsx("div", { className: "font-mono text-[12px] tracking-[0.14em] text-ink-3 uppercase mb-3", children: t("pages.update.eyebrow") }), _jsx("h1", { className: "text-[29px] font-semibold tracking-tight text-ink mb-2", children: t("pages.update.title") }), _jsx("p", { className: "text-ink-3 text-[14px] max-w-[760px] leading-relaxed mb-7", children: t("pages.update.subtitle") }), _jsxs("section", { className: "mb-8", "aria-labelledby": "section-update-status", children: [_jsx(SectionTitle, { id: "section-update-status", children: t("pages.update.currentVersion") }), _jsxs("div", { className: "bg-panel border border-line rounded-[6px] p-5", children: [_jsxs("div", { className: "space-y-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-ink-3 text-[13px]", children: t("pages.update.currentVersion") }), _jsx("span", { className: "font-mono text-[13px] text-ink", children: statusData?.currentVersion ?? "—" })] }), statusData?.identityDivergence && (_jsx("div", { className: "border border-amber/35 rounded-[4px] px-3 py-2 text-[12px] leading-relaxed text-ink-2", children: t("pages.update.identityDivergence", {
206
206
  active: statusData.identityDivergence.activeVersion,
207
207
  plugin: statusData.identityDivergence.pluginVersion,
208
208
  }) })), _jsxs("div", { className: "flex items-center justify-between", children: [_jsx("span", { className: "text-ink-3 text-[13px]", children: t("pages.update.latestVersion") }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "font-mono text-[13px] text-ink", children: updateState === 'check_failed' ? "—" : (statusData?.latestVersion || "—") }), updateState === 'check_failed' && (_jsx("span", { className: "inline-flex items-center border border-amber/35 text-amber rounded-[2px] px-[7px] py-1 font-mono text-[11px] uppercase", children: t("pages.update.checkFailed") })), updateState === 'update_blocked' && (_jsx("span", { className: "inline-flex items-center border border-amber/35 text-amber rounded-[2px] px-[7px] py-1 font-mono text-[11px] uppercase", children: t("pages.update.blockedBadge") })), updateState === 'up_to_date' && (_jsx("span", { className: "inline-flex items-center border border-green/35 text-green rounded-[2px] px-[7px] py-1 font-mono text-[11px] uppercase", children: t("pages.update.upToDate") })), updateState === 'update_available' && (_jsx("span", { className: "inline-flex items-center border border-amber/35 text-amber rounded-[2px] px-[7px] py-1 font-mono text-[11px] uppercase", children: t("pages.update.updateAvailable") }))] })] })] }), (updateState === 'update_blocked' || updateState === 'check_failed') && (_jsxs("div", { className: "mt-4 pt-3 border-t border-line text-[12px] leading-relaxed", children: [blockedReason && (_jsxs("p", { className: "text-ink-2", children: [t("pages.update.reasonLabel"), blockedReason] })), statusData?.message && updateState === 'update_blocked' && (_jsx("p", { className: "mt-1 text-ink-3", children: statusData.message })), statusData?.nextAction && (_jsx("p", { className: "mt-1 text-ink-3 font-mono", children: statusData.nextAction }))] })), statusData?.error && (_jsxs("div", { className: "mt-4 pt-3 border-t border-line text-[12px] text-ink-4 font-mono", children: [t("pages.update.checkError"), ": ", statusData.error] })), inProgress && (_jsxs("div", { className: "mt-4 p-4 rounded-[6px] border border-gov/30 bg-surface text-[13px] leading-relaxed", role: "status", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Loader2, { className: "h-4 w-4 animate-spin text-gov" }), _jsx("span", { className: "font-medium text-ink", children: t("pages.update.inProgressPanel") })] }), _jsx("p", { className: "mt-1 font-mono text-[12px] text-ink-3", children: t("pages.update.inProgressPhase", { phase: inProgress.phase ?? "…" }) }), _jsx("p", { className: "mt-1 font-mono text-[12px] text-ink-4", children: inProgress.transactionId })] })), _jsxs("div", { className: "mt-5 pt-4 border-t border-line flex items-center gap-3 flex-wrap", children: [_jsx("button", { type: "button", onClick: handleCheckForUpdates, disabled: checking || fullUpdating || inProgress !== null, className: "border border-line bg-surface text-ink rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:border-line-2 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: checking ? t("pages.update.checking") : t("pages.update.checkForUpdates") }), !isUpToDate && updateState === 'update_available' && (_jsx("button", { type: "button", onClick: () => setShowFullUpdateDialog(true), disabled: checking || fullUpdating || inProgress !== null, className: "bg-gov text-white rounded-[3px] px-[14px] py-[6px] text-[12.5px] hover:bg-gov/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-2 focus-visible:outline-gov focus-visible:outline-offset-2", children: fullUpdating ? (_jsxs("span", { className: "flex items-center gap-1.5", children: [_jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }), t("pages.update.updating")] })) : (t("pages.update.applyUpdate")) }))] }), updateResult && (_jsx("div", { className: `mt-4 p-4 rounded-[6px] border animate-[pdFadeIn_400ms_ease-out] ${updateResult.success ? 'bg-green/5 border-green/20' : updateResult.refusal ? 'bg-amber/5 border-amber/25' : 'bg-danger/5 border-danger/20'}`, children: _jsxs("div", { className: "flex items-start gap-3", children: [updateResult.success ? (_jsx(CheckCircle2, { className: "h-6 w-6 text-green shrink-0 mt-0.5 animate-[pdFadeIn_600ms_ease-out]", style: { transform: 'scale(1)', animationFillMode: 'both' } })) : updateResult.refusal ? (_jsx(AlertTriangle, { className: "h-6 w-6 text-amber shrink-0 mt-0.5" })) : (_jsx(XCircle, { className: "h-6 w-6 text-danger shrink-0 mt-0.5" })), _jsxs("div", { className: "flex-1 min-w-0", children: [updateResult.refusal && (_jsx("p", { className: "text-[12px] font-mono uppercase text-amber mb-1", children: t("pages.update.blockedBadge") })), updateResult.success && updateResult.fromVersion && updateResult.newVersion && (_jsxs("div", { className: "flex items-center gap-2 mb-1.5", children: [_jsx("span", { className: "font-mono text-[13px] text-ink-3 line-through", children: updateResult.fromVersion }), _jsx(ArrowRight, { className: "h-3.5 w-3.5 text-green" }), _jsx("span", { className: "font-mono text-[13px] text-green font-medium", children: updateResult.newVersion })] })), _jsx("p", { className: `text-[13px] ${updateResult.success ? 'text-green' : updateResult.refusal ? 'text-ink-2' : 'text-danger'}`, children: updateResult.message }), updateResult.success && updateResult.requiresRestart && (_jsx("p", { className: "mt-2 p-2 rounded-[3px] bg-amber/5 border border-amber/20 text-[12px] text-amber leading-relaxed", children: t("pages.update.fullUpdateRestartPrompt") })), updateResult.gatewayNotice && (_jsx("p", { className: "mt-2 p-2 rounded-[3px] bg-amber/5 border border-amber/20 text-[12px] text-amber leading-relaxed font-mono", children: updateResult.gatewayNotice })), !updateResult.success && updateResult.nextAction && (_jsx("p", { className: "mt-1 text-[12px] text-ink-4 font-mono leading-relaxed", children: updateResult.nextAction })), !updateResult.success && (_jsxs("div", { className: "mt-2 flex items-center gap-2", children: [/network|fetch|timeout|ECONNREFUSED|ENOTFOUND/i.test(updateResult.message) && (_jsx("p", { className: "text-[12px] text-ink-4 font-mono", children: t("pages.update.networkErrorHint") })), _jsx("button", { type: "button", onClick: () => setShowFullUpdateDialog(true), disabled: fullUpdating, className: "text-[12px] text-danger underline hover:text-danger/80 disabled:opacity-50", children: t("pages.update.retry") })] }))] })] }) }))] })] }), _jsxs("section", { "aria-labelledby": "section-update-history", children: [_jsx(SectionTitle, { id: "section-update-history", children: t("pages.update.history") }), historyErrorReason ? (_jsxs("div", { className: "text-ink-3 text-[13px] leading-relaxed py-3", children: [t("pages.update.loadError"), " (", historyErrorReason, ")"] })) : historyData && historyData.updates.length > 0 ? (_jsx("div", { className: "space-y-[10px]", children: historyData.updates.map((entry) => (_jsxs("article", { className: "bg-panel border border-line rounded-[6px] px-5 py-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "inline-flex items-center border border-line rounded-[2px] px-[7px] py-1 font-mono text-[11px] text-ink-3 bg-surface/80 uppercase", children: t("pages.update.version") }), _jsxs("span", { className: "font-mono text-[13px] text-ink", children: [entry.fromVersion, " \u2192 ", entry.toVersion === "failed" ? "—" : entry.toVersion] }), entry.kind !== "failure" && (_jsx("span", { className: "inline-flex items-center border border-line rounded-[2px] px-[7px] py-1 font-mono text-[11px] text-ink-3 bg-surface/80 uppercase", children: t(`pages.update.historyKind.${entry.kind}`) })), !entry.success && entry.kind !== 'refusal' && (_jsx("span", { className: "inline-flex items-center border border-danger/35 text-danger rounded-[2px] px-[7px] py-1 font-mono text-[11px] uppercase", children: t("pages.update.failed") }))] }), _jsx("div", { className: "flex items-center gap-3", children: _jsx("span", { className: "text-ink-3 text-[13px]", children: formatDate(entry.timestamp, i18n.language, {
@@ -904,6 +904,16 @@ export interface UpdateRecoveryData {
904
904
  * Reported separately (rc-9: never a silent drop) but never hold the alarm.
905
905
  */
906
906
  superseded: UpdateRecoverySuperseded[];
907
+ /**
908
+ * PRI-1069/1070 (G1 P0-4): terminal `failed` transactions whose detail
909
+ * carries a restore-inconsistency marker — the physical install is NOT what
910
+ * the terminal state implies. They stay alarmed until a later completed
911
+ * install records their recovery.
912
+ */
913
+ recoveryRequired: {
914
+ transactionId: string;
915
+ reason: string;
916
+ }[];
907
917
  /**
908
918
  * rc-9 (PRI-896/CR-2): active.json could not be read, so recovery had no
909
919
  * live-release evidence and every non-terminal journal is conservatively an
@@ -2536,7 +2536,17 @@ export function validateUpdateRecovery(v) {
2536
2536
  superseded.push({ transactionId: item.transactionId, reason: item.reason });
2537
2537
  }
2538
2538
  }
2539
- const result = { needsRecovery: v.needsRecovery, unfinished, broken, superseded };
2539
+ const recoveryRequired = [];
2540
+ if (Object.hasOwn(v, 'recoveryRequired') && Array.isArray(v.recoveryRequired)) {
2541
+ for (const item of v.recoveryRequired) {
2542
+ if (!isObject(item) || !Object.hasOwn(item, 'transactionId') || !isString(item.transactionId))
2543
+ continue;
2544
+ if (!Object.hasOwn(item, 'reason') || !isString(item.reason))
2545
+ continue;
2546
+ recoveryRequired.push({ transactionId: item.transactionId, reason: item.reason });
2547
+ }
2548
+ }
2549
+ const result = { needsRecovery: v.needsRecovery, unfinished, broken, superseded, recoveryRequired };
2540
2550
  if (Object.hasOwn(v, 'activeRecordUnreadable') && typeof v.activeRecordUnreadable === 'boolean') {
2541
2551
  result.activeRecordUnreadable = v.activeRecordUnreadable;
2542
2552
  }
@@ -678,6 +678,9 @@
678
678
  .min-h-0 {
679
679
  min-height: 0px;
680
680
  }
681
+ .min-h-20 {
682
+ min-height: calc(var(--spacing) * 20);
683
+ }
681
684
  .min-h-\[44px\] {
682
685
  min-height: 44px;
683
686
  }