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.
- package/_release/product-identity.json +2 -2
- package/console/dist/server/models/ActivationsConsoleModel.d.ts +3 -0
- package/console/dist/server/models/ActivationsConsoleModel.js +25 -1
- package/console/dist/server/models/ApprovalsConsoleModel.d.ts +5 -0
- package/console/dist/server/models/ApprovalsConsoleModel.js +40 -2
- package/console/dist/server/routes/activations.js +47 -0
- package/console/dist/server/routes/approvals.js +1 -1
- package/console/dist/server/routes/health.js +17 -0
- package/console/dist/server/routes/update-transaction.js +63 -3
- package/console/dist/shared/approved-rule-revision-contract.d.ts +8 -0
- package/console/dist/shared/approved-rule-revision-contract.js +48 -0
- package/console/dist/ui/api.d.ts +3 -0
- package/console/dist/ui/api.js +9 -0
- package/console/dist/ui/i18n/en.json +9 -0
- package/console/dist/ui/i18n/zh-CN.json +9 -0
- package/console/dist/ui/pages/activation/ActivationPage.js +69 -2
- package/console/dist/ui/pages/settings/UpdatePage.js +1 -1
- package/console/dist/ui/utils/validators.d.ts +10 -0
- package/console/dist/ui/utils/validators.js +11 -1
- package/console/dist/web/assets/app.css +3 -0
- package/console/dist/web/assets/app.js +31 -31
- package/core/dist/runtime-v2/activation/activation-dispatcher.d.ts +2 -0
- package/core/dist/runtime-v2/activation/activation-dispatcher.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/activation-dispatcher.js +60 -22
- package/core/dist/runtime-v2/activation/activation-dispatcher.js.map +1 -1
- package/core/dist/runtime-v2/activation/activation-types.d.ts +12 -2
- package/core/dist/runtime-v2/activation/activation-types.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/activation-types.js.map +1 -1
- package/core/dist/runtime-v2/activation/index.d.ts +2 -0
- package/core/dist/runtime-v2/activation/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/index.js +2 -0
- package/core/dist/runtime-v2/activation/index.js.map +1 -1
- package/core/dist/runtime-v2/activation/rule-replacement.d.ts +38 -0
- package/core/dist/runtime-v2/activation/rule-replacement.d.ts.map +1 -0
- package/core/dist/runtime-v2/activation/rule-replacement.js +44 -0
- package/core/dist/runtime-v2/activation/rule-replacement.js.map +1 -0
- package/core/dist/runtime-v2/activation/sqlite-activation-state-store.d.ts +9 -1
- package/core/dist/runtime-v2/activation/sqlite-activation-state-store.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/sqlite-activation-state-store.js +53 -23
- package/core/dist/runtime-v2/activation/sqlite-activation-state-store.js.map +1 -1
- package/core/dist/runtime-v2/activation/writers/rule-host-writer.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/writers/rule-host-writer.js +15 -2
- package/core/dist/runtime-v2/activation/writers/rule-host-writer.js.map +1 -1
- package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.d.ts +2 -0
- package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.d.ts.map +1 -1
- package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.js +5 -1
- package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.js.map +1 -1
- package/core/dist/runtime-v2/index.d.ts +3 -1
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +2 -1
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/internalization/approved-rule-revision.d.ts +51 -0
- package/core/dist/runtime-v2/internalization/approved-rule-revision.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/approved-rule-revision.js +211 -0
- package/core/dist/runtime-v2/internalization/approved-rule-revision.js.map +1 -0
- package/core/dist/runtime-v2/internalization/artificer-prompt-builder.d.ts +1 -1
- package/core/dist/runtime-v2/internalization/artificer-prompt-builder.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js +2 -1
- package/core/dist/runtime-v2/internalization/artificer-prompt-builder.js.map +1 -1
- package/core/dist/runtime-v2/internalization/bash-risk.d.ts +6 -0
- package/core/dist/runtime-v2/internalization/bash-risk.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/bash-risk.js +120 -0
- package/core/dist/runtime-v2/internalization/bash-risk.js.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.js +4 -1
- package/core/dist/runtime-v2/internalization/evaluator-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts +2 -1
- package/core/dist/runtime-v2/internalization/peer-runner-contracts.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/peer-runner-contracts.js.map +1 -1
- package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts +21 -0
- package/core/dist/runtime-v2/internalization/pitask-metadata.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/pitask-metadata.js +30 -0
- package/core/dist/runtime-v2/internalization/pitask-metadata.js.map +1 -1
- package/core/dist/runtime-v2/internalization/rule-host-contracts.d.ts +2 -0
- package/core/dist/runtime-v2/internalization/rule-host-contracts.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/rule-host-input-builder.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/rule-host-input-builder.js +6 -0
- package/core/dist/runtime-v2/internalization/rule-host-input-builder.js.map +1 -1
- package/core/dist/runtime-v2/internalization/rulecode-verification-obligations.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/rulecode-verification-obligations.js +32 -0
- package/core/dist/runtime-v2/internalization/rulecode-verification-obligations.js.map +1 -1
- package/core/dist/runtime-v2/proven-channel-baseline.js +1 -1
- package/core/dist/runtime-v2/proven-channel-baseline.js.map +1 -1
- package/core/dist/runtime-v2/store/task/sqlite-approved-rule-revision.d.ts +15 -0
- package/core/dist/runtime-v2/store/task/sqlite-approved-rule-revision.d.ts.map +1 -0
- package/core/dist/runtime-v2/store/task/sqlite-approved-rule-revision.js +39 -0
- package/core/dist/runtime-v2/store/task/sqlite-approved-rule-revision.js.map +1 -0
- package/core/package.json +1 -1
- package/dist/installer.d.ts +81 -0
- package/dist/installer.d.ts.map +1 -1
- package/dist/installer.js +480 -117
- package/dist/installer.js.map +1 -1
- package/dist/update/console-surface.d.ts +1 -1
- package/dist/update/console-surface.d.ts.map +1 -1
- package/dist/update/console-surface.js +1 -1
- package/dist/update/console-surface.js.map +1 -1
- package/dist/update/transaction-journal.d.ts +47 -0
- package/dist/update/transaction-journal.d.ts.map +1 -1
- package/dist/update/transaction-journal.js +87 -2
- package/dist/update/transaction-journal.js.map +1 -1
- package/host-runtime/dist/production-rulehost-gate.js +11 -2
- package/host-runtime/package.json +1 -1
- package/package.json +2 -2
- package/pd-cli/package.json +1 -1
- package/plugin/dist/bundle.js +477 -472
- package/plugin/dist/openclaw.plugin.json +1 -1
- package/plugin/dist/rulehost-evidence.js +52 -49
- package/plugin/openclaw.plugin.json +1 -1
- package/plugin/package.json +1 -1
- package/release-manager/dist/installer.d.ts +81 -0
- package/release-manager/dist/installer.d.ts.map +1 -1
- package/release-manager/dist/installer.js +480 -117
- package/release-manager/dist/installer.js.map +1 -1
- package/release-manager/dist/update/console-surface.d.ts +1 -1
- package/release-manager/dist/update/console-surface.d.ts.map +1 -1
- package/release-manager/dist/update/console-surface.js +1 -1
- package/release-manager/dist/update/console-surface.js.map +1 -1
- package/release-manager/dist/update/transaction-journal.d.ts +47 -0
- package/release-manager/dist/update/transaction-journal.d.ts.map +1 -1
- package/release-manager/dist/update/transaction-journal.js +87 -2
- package/release-manager/dist/update/transaction-journal.js.map +1 -1
- 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
|
|
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
|
}
|