create-principles-disciple 1.120.6 → 1.121.0
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/console/dist/server/index.js +4 -0
- package/console/dist/server/models/GovernanceConsoleModel.d.ts +10 -2
- package/console/dist/server/models/GovernanceConsoleModel.js +28 -3
- package/console/dist/server/routes/failed-tasks.d.ts +9 -5
- package/console/dist/server/routes/failed-tasks.js +220 -8
- package/console/dist/ui/api.d.ts +4 -3
- package/console/dist/ui/api.js +14 -2
- package/console/dist/ui/i18n/en.json +12 -0
- package/console/dist/ui/i18n/zh-CN.json +12 -0
- package/console/dist/ui/pages/failed-tasks/FailedTasksPage.js +78 -5
- package/console/dist/ui/pages/focus/FocusPage.js +5 -1
- package/console/dist/ui/utils/validators.d.ts +11 -0
- package/console/dist/ui/utils/validators.js +32 -0
- package/console/dist/web/assets/app.js +169 -6
- package/core/dist/runtime-v2/__tests__/owner-retry.test.d.ts +2 -0
- package/core/dist/runtime-v2/__tests__/owner-retry.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/__tests__/owner-retry.test.js +144 -0
- package/core/dist/runtime-v2/__tests__/owner-retry.test.js.map +1 -0
- package/core/dist/runtime-v2/__tests__/pain-diagnosis-persistence.test.d.ts +2 -0
- package/core/dist/runtime-v2/__tests__/pain-diagnosis-persistence.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/__tests__/pain-diagnosis-persistence.test.js +293 -0
- package/core/dist/runtime-v2/__tests__/pain-diagnosis-persistence.test.js.map +1 -0
- package/core/dist/runtime-v2/__tests__/recovery-actions-log.test.d.ts +2 -0
- package/core/dist/runtime-v2/__tests__/recovery-actions-log.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/__tests__/recovery-actions-log.test.js +112 -0
- package/core/dist/runtime-v2/__tests__/recovery-actions-log.test.js.map +1 -0
- package/core/dist/runtime-v2/diagnostician/rootcause-prompt-builder.d.ts +10 -0
- package/core/dist/runtime-v2/diagnostician/rootcause-prompt-builder.d.ts.map +1 -1
- package/core/dist/runtime-v2/diagnostician/rootcause-prompt-builder.js +40 -4
- package/core/dist/runtime-v2/diagnostician/rootcause-prompt-builder.js.map +1 -1
- package/core/dist/runtime-v2/diagnostician-prompt-builder.d.ts +6 -0
- package/core/dist/runtime-v2/diagnostician-prompt-builder.d.ts.map +1 -1
- package/core/dist/runtime-v2/diagnostician-prompt-builder.js.map +1 -1
- package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js +28 -0
- package/core/dist/runtime-v2/feature-flags/__tests__/feature-flag-contract.test.js.map +1 -1
- package/core/dist/runtime-v2/feature-flags/__tests__/pain-diagnosis-persistence-flag.test.d.ts +2 -0
- package/core/dist/runtime-v2/feature-flags/__tests__/pain-diagnosis-persistence-flag.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/feature-flags/__tests__/pain-diagnosis-persistence-flag.test.js +44 -0
- package/core/dist/runtime-v2/feature-flags/__tests__/pain-diagnosis-persistence-flag.test.js.map +1 -0
- package/core/dist/runtime-v2/feature-flags/feature-flag-contract.d.ts.map +1 -1
- package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js +18 -0
- package/core/dist/runtime-v2/feature-flags/feature-flag-contract.js.map +1 -1
- package/core/dist/runtime-v2/index.d.ts +5 -0
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +5 -0
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/internalization/diag-rootcause-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/diag-rootcause-runner.js +5 -0
- package/core/dist/runtime-v2/internalization/diag-rootcause-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/owner-retry.d.ts +49 -0
- package/core/dist/runtime-v2/internalization/owner-retry.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/owner-retry.js +51 -0
- package/core/dist/runtime-v2/internalization/owner-retry.js.map +1 -0
- package/core/dist/runtime-v2/pain-signal-bridge.d.ts +19 -0
- package/core/dist/runtime-v2/pain-signal-bridge.d.ts.map +1 -1
- package/core/dist/runtime-v2/pain-signal-bridge.js +61 -0
- package/core/dist/runtime-v2/pain-signal-bridge.js.map +1 -1
- package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts +12 -0
- package/core/dist/runtime-v2/pain-signal-runtime-factory.d.ts.map +1 -1
- package/core/dist/runtime-v2/pain-signal-runtime-factory.js +48 -2
- package/core/dist/runtime-v2/pain-signal-runtime-factory.js.map +1 -1
- package/core/dist/runtime-v2/recovery-actions-log.d.ts +46 -0
- package/core/dist/runtime-v2/recovery-actions-log.d.ts.map +1 -0
- package/core/dist/runtime-v2/recovery-actions-log.js +116 -0
- package/core/dist/runtime-v2/recovery-actions-log.js.map +1 -0
- package/core/dist/runtime-v2/recovery-sweep-service.d.ts +8 -0
- package/core/dist/runtime-v2/recovery-sweep-service.d.ts.map +1 -1
- package/core/dist/runtime-v2/recovery-sweep-service.js +4 -0
- package/core/dist/runtime-v2/recovery-sweep-service.js.map +1 -1
- package/core/dist/runtime-v2/store/pain-diagnosis/pain-diagnosis-store.d.ts +77 -0
- package/core/dist/runtime-v2/store/pain-diagnosis/pain-diagnosis-store.d.ts.map +1 -0
- package/core/dist/runtime-v2/store/pain-diagnosis/pain-diagnosis-store.js +40 -0
- package/core/dist/runtime-v2/store/pain-diagnosis/pain-diagnosis-store.js.map +1 -0
- package/core/dist/runtime-v2/store/pain-diagnosis/sqlite-pain-diagnosis-store.d.ts +9 -0
- package/core/dist/runtime-v2/store/pain-diagnosis/sqlite-pain-diagnosis-store.d.ts.map +1 -0
- package/core/dist/runtime-v2/store/pain-diagnosis/sqlite-pain-diagnosis-store.js +118 -0
- package/core/dist/runtime-v2/store/pain-diagnosis/sqlite-pain-diagnosis-store.js.map +1 -0
- package/core/dist/runtime-v2/store/runtime-state-manager.d.ts +11 -0
- package/core/dist/runtime-v2/store/runtime-state-manager.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/runtime-state-manager.js +18 -0
- package/core/dist/runtime-v2/store/runtime-state-manager.js.map +1 -1
- package/core/dist/runtime-v2/store/sqlite-connection.d.ts.map +1 -1
- package/core/dist/runtime-v2/store/sqlite-connection.js +23 -0
- package/core/dist/runtime-v2/store/sqlite-connection.js.map +1 -1
- package/host-runtime/dist/production-pain-evidence.js +9 -2
- package/package.json +1 -1
- package/pd-cli/dist/commands/runtime-internalization-retry.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-internalization-retry.js +32 -19
- package/pd-cli/dist/commands/runtime-internalization-retry.js.map +1 -1
- package/plugin/dist/bundle.js +438 -415
|
@@ -11,6 +11,13 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
11
11
|
* Each row exposes a "Create Feedback Draft" button that deep-links into the
|
|
12
12
|
* Report Problem page with the task/pain context pre-filled.
|
|
13
13
|
*
|
|
14
|
+
* Governance Recovery Actions v1: when the `failed_task_recovery_console`
|
|
15
|
+
* feature flag is enabled, each row also exposes a "Resume Evolution" button.
|
|
16
|
+
* Recovery is an Owner governance action (never auto-triggered on load): it
|
|
17
|
+
* always goes through an explicit confirmation dialog before POSTing
|
|
18
|
+
* /api/v1/failed-tasks/:id/recover. The success state means "Recovery
|
|
19
|
+
* Accepted" — the task is pending again; execution is asynchronous.
|
|
20
|
+
*
|
|
14
21
|
* Runtime Contract (rc-*):
|
|
15
22
|
* - rc-1: API response is treated as `unknown` and validated before use
|
|
16
23
|
* - rc-2: no `as` type assertions — runtime type guards only
|
|
@@ -20,13 +27,16 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
20
27
|
import { useState, useEffect, useCallback } from "react";
|
|
21
28
|
import { useNavigate } from "react-router-dom";
|
|
22
29
|
import { useTranslation } from "react-i18next";
|
|
30
|
+
import { toast } from "sonner";
|
|
23
31
|
import { PageShell } from "../../components/layout/page-shell.js";
|
|
24
32
|
import { PageLoading } from "../../components/layout/page-loading.js";
|
|
25
33
|
import { Badge } from "../../components/ui/badge.js";
|
|
26
34
|
import { Card, CardContent, CardHeader, CardTitle } from "../../components/ui/card.js";
|
|
27
35
|
import { Button } from "../../components/ui/button.js";
|
|
36
|
+
import { AlertDialog, AlertDialogContent, AlertDialogHeader, AlertDialogFooter, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel, } from "../../components/ui/alert-dialog.js";
|
|
28
37
|
import { ShinyText } from "../../components/ui/shiny-text.js";
|
|
29
38
|
import { formatDate } from "../../utils/format.js";
|
|
39
|
+
import { recoverFailedTask, fetchConfigSummary } from "../../api.js";
|
|
30
40
|
// ── Runtime validators (rc-1, rc-2, rc-5) ───────────────────────────────────
|
|
31
41
|
//
|
|
32
42
|
// The API response is `unknown` until these guards run. No `as` casts.
|
|
@@ -133,6 +143,30 @@ export function FailedTasksPage() {
|
|
|
133
143
|
const { t } = useTranslation();
|
|
134
144
|
const navigate = useNavigate();
|
|
135
145
|
const [state, setState] = useState({ status: "loading" });
|
|
146
|
+
// Governance Recovery Actions v1 — flag-gated recovery UI (fail-closed:
|
|
147
|
+
// absent/false → no recovery entry, Console stays read-only).
|
|
148
|
+
const [recoveryEnabled, setRecoveryEnabled] = useState(false);
|
|
149
|
+
const [recoverTarget, setRecoverTarget] = useState(null);
|
|
150
|
+
const [recoverLoading, setRecoverLoading] = useState(false);
|
|
151
|
+
useEffect(() => {
|
|
152
|
+
let cancelled = false;
|
|
153
|
+
fetchConfigSummary()
|
|
154
|
+
.then((result) => {
|
|
155
|
+
if (cancelled)
|
|
156
|
+
return;
|
|
157
|
+
// Fail-closed: only an explicitly enabled flag shows the action
|
|
158
|
+
if (result.success && result.data) {
|
|
159
|
+
const flag = result.data.features.find((f) => f.id === "failed_task_recovery_console");
|
|
160
|
+
setRecoveryEnabled(flag?.enabled === true);
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
.catch(() => {
|
|
164
|
+
// rc-9: flag probe failure keeps recovery hidden (fail-closed)
|
|
165
|
+
});
|
|
166
|
+
return () => {
|
|
167
|
+
cancelled = true;
|
|
168
|
+
};
|
|
169
|
+
}, []);
|
|
136
170
|
const loadData = useCallback(async () => {
|
|
137
171
|
setState({ status: "loading" });
|
|
138
172
|
try {
|
|
@@ -205,9 +239,48 @@ export function FailedTasksPage() {
|
|
|
205
239
|
}
|
|
206
240
|
navigate(`/report-problem?${params.toString()}`);
|
|
207
241
|
};
|
|
208
|
-
|
|
242
|
+
// Owner-confirmed recovery (SPEC §6.3: explicit confirmation only — this
|
|
243
|
+
// handler runs exclusively from the dialog's action button, never on load).
|
|
244
|
+
const handleRecoverConfirmed = async () => {
|
|
245
|
+
if (!recoverTarget || recoverLoading)
|
|
246
|
+
return;
|
|
247
|
+
setRecoverLoading(true);
|
|
248
|
+
try {
|
|
249
|
+
const result = await recoverFailedTask(recoverTarget.taskId);
|
|
250
|
+
if (result.success) {
|
|
251
|
+
toast.success(t("pages.failedTasks.recoverSuccess"));
|
|
252
|
+
setRecoverTarget(null);
|
|
253
|
+
await loadData();
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
// rc-9: server-provided reason + next action accompany the failure
|
|
257
|
+
const detail = result.nextAction
|
|
258
|
+
? `${result.error} — ${result.nextAction}`
|
|
259
|
+
: result.error;
|
|
260
|
+
toast.error(`${t("pages.failedTasks.recoverFailed")}: ${detail}`);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
catch (err) {
|
|
264
|
+
toast.error(`${t("pages.failedTasks.recoverFailed")}: ${err instanceof Error ? err.message : "Network error"}`);
|
|
265
|
+
}
|
|
266
|
+
finally {
|
|
267
|
+
setRecoverLoading(false);
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
return (_jsxs(PageShell, { children: [_jsxs("div", { className: "mb-8", children: [_jsx("div", { className: "font-mono text-[11px] uppercase tracking-[0.14em] text-ink-3", children: t("pages.failedTasks.title") }), _jsx(ShinyText, { as: "h1", className: "text-[29px] font-semibold tracking-tight text-ink mt-3 mb-2", duration: 4.5, brightness: 0.5, disabled: state.status !== "loaded" || state.data.tasks.length === 0, children: t("pages.failedTasks.title") }), _jsx("p", { className: "text-ink-3 text-sm leading-relaxed max-w-[712px]", children: t("pages.failedTasks.subtitle") })] }), state.status === "loading" && (_jsx(PageLoading, { cardCount: 3, label: t("common.loading") })), state.status === "disabled" && (_jsxs("div", { className: "py-8", children: [_jsx("div", { className: "p-5 bg-panel border border-amber/20 rounded-[var(--radius-md)] text-amber text-[13px] leading-relaxed", children: t("pages.failedTasks.disabled") }), _jsx("div", { className: "mt-4", children: _jsx(Button, { variant: "outline", size: "sm", onClick: loadData, children: t("common.refresh") }) })] })), state.status === "error" && (_jsxs("div", { className: "py-8", children: [_jsxs("div", { className: "text-danger text-sm mb-2", children: [t("pages.failedTasks.error"), ": ", state.message] }), state.nextAction && (_jsx("div", { className: "text-ink-3 text-[13px] mb-3", children: state.nextAction })), _jsx(Button, { variant: "outline", size: "sm", onClick: loadData, children: t("common.refresh") })] })), state.status === "loaded" && (_jsx("div", { className: "animate-[pdFadeIn_400ms_ease-out]", children: _jsx(LoadedContent, { data: state.data, onCreateDraft: handleCreateDraft, onRecover: recoveryEnabled ? (task) => setRecoverTarget(task) : undefined, t: t }) })), _jsx(AlertDialog, { open: recoverTarget !== null, onOpenChange: (open) => {
|
|
271
|
+
if (!open && !recoverLoading) {
|
|
272
|
+
setRecoverTarget(null);
|
|
273
|
+
}
|
|
274
|
+
}, children: _jsxs(AlertDialogContent, { children: [_jsxs(AlertDialogHeader, { children: [_jsx(AlertDialogTitle, { children: t("pages.failedTasks.recoverConfirmTitle") }), _jsx(AlertDialogDescription, { asChild: true, children: _jsxs("div", { className: "space-y-2 text-[13px] leading-relaxed", children: [_jsxs("div", { children: [_jsxs("span", { className: "font-medium", children: [t("pages.failedTasks.recoverConfirmStatus"), ":"] }), " ", _jsx("span", { className: "font-mono", children: recoverTarget?.status ?? "—" })] }), _jsxs("div", { children: [_jsxs("span", { className: "font-medium", children: [t("pages.failedTasks.recoverConfirmActionLabel"), ":"] }), " ", t("pages.failedTasks.recoverConfirmActionDesc")] }), _jsxs("div", { children: [_jsxs("span", { className: "font-medium", children: [t("pages.failedTasks.recoverConfirmImpactLabel"), ":"] }), " ", t("pages.failedTasks.recoverConfirmImpactDesc")] })] }) })] }), _jsxs(AlertDialogFooter, { children: [_jsx(AlertDialogCancel, { disabled: recoverLoading, children: t("common.cancel") }), _jsx(AlertDialogAction, { disabled: recoverLoading, onClick: (event) => {
|
|
275
|
+
// Prevent Radix from auto-closing before the async POST
|
|
276
|
+
// resolves; handleRecoverConfirmed closes on success.
|
|
277
|
+
event.preventDefault();
|
|
278
|
+
void handleRecoverConfirmed();
|
|
279
|
+
}, children: recoverLoading
|
|
280
|
+
? t("common.loading")
|
|
281
|
+
: t("pages.failedTasks.recoverConfirmButton") })] })] }) })] }));
|
|
209
282
|
}
|
|
210
|
-
function LoadedContent({ data, onCreateDraft, t }) {
|
|
283
|
+
function LoadedContent({ data, onCreateDraft, onRecover, t }) {
|
|
211
284
|
const hasTasks = data.tasks.length > 0;
|
|
212
285
|
// Empty state — surface nextAction from the API (rc-9: no silent fallback)
|
|
213
286
|
if (!hasTasks) {
|
|
@@ -219,11 +292,11 @@ function LoadedContent({ data, onCreateDraft, t }) {
|
|
|
219
292
|
const tasks = grouped.get(kind);
|
|
220
293
|
if (!tasks || tasks.length === 0)
|
|
221
294
|
return null;
|
|
222
|
-
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Badge, { variant: "outline", children: t("pages.failedTasks.kind") }), _jsxs(CardTitle, { className: "text-[15px] font-mono", children: [kind, " (", tasks.length, ")"] })] }) }), _jsx(CardContent, { children: _jsx(TaskTable, { tasks: tasks, onCreateDraft: onCreateDraft, t: t }) })] }, kind));
|
|
295
|
+
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Badge, { variant: "outline", children: t("pages.failedTasks.kind") }), _jsxs(CardTitle, { className: "text-[15px] font-mono", children: [kind, " (", tasks.length, ")"] })] }) }), _jsx(CardContent, { children: _jsx(TaskTable, { tasks: tasks, onCreateDraft: onCreateDraft, onRecover: onRecover, t: t }) })] }, kind));
|
|
223
296
|
}) }), _jsxs("div", { className: "mt-8 font-mono text-[11px] text-ink-4 space-y-1", children: [_jsxs("div", { children: [t("common.total"), ": ", data.total] }), data.total > data.tasks.length && (_jsx("div", { className: "text-amber/80 leading-relaxed", children: t("pages.failedTasks.partialResults")
|
|
224
297
|
.replace("{shown}", String(data.tasks.length))
|
|
225
298
|
.replace("{total}", String(data.total)) }))] })] }));
|
|
226
299
|
}
|
|
227
|
-
function TaskTable({ tasks, onCreateDraft, t }) {
|
|
228
|
-
return (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto_minmax(0,1fr)_auto_minmax(0,1fr)_auto] gap-3 pb-2 border-b border-line font-mono text-[10px] uppercase tracking-[0.08em] text-ink-4", children: [_jsx("span", { children: t("pages.failedTasks.taskId") }), _jsx("span", { children: t("pages.failedTasks.painId") }), _jsx("span", { children: t("pages.failedTasks.status") }), _jsx("span", { children: t("pages.failedTasks.lastError") }), _jsx("span", { children: t("pages.failedTasks.attempts") }), _jsx("span", { children: t("pages.failedTasks.lastAttempt") }), _jsx("span", { "aria-hidden": "true" })] }), tasks.map((task) => (_jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto_minmax(0,1fr)_auto_minmax(0,1fr)_auto] gap-3 py-2 items-center text-[13px] border-b border-line/50 last:border-b-0", children: [_jsx("span", { className: "font-mono text-ink-2 truncate", title: task.taskId, children: truncateId(task.taskId) }), _jsx("span", { className: "font-mono text-ink-3 truncate", title: task.painId ?? "", children: task.painId ? truncateId(task.painId) : t("pages.failedTasks.noPainId") }), _jsx(Badge, { variant: statusToVariant(task.status), children: task.status }), _jsx("span", { className: "text-ink-3 truncate", title: task.lastError ?? "", children: task.lastError ?? "—" }), _jsx("span", { className: "font-mono text-ink-2 text-center", children: task.attemptCount }), _jsx("span", { className: "font-mono text-ink-3 text-[12px]", children: task.lastAttemptAt ? formatDate(task.lastAttemptAt) : "—" }), _jsx(Button, { variant: "outline", size: "sm", onClick: () => onCreateDraft(task), className: "whitespace-nowrap", children: t("pages.failedTasks.createDraft") })] }, task.taskId)))] }));
|
|
300
|
+
function TaskTable({ tasks, onCreateDraft, onRecover, t }) {
|
|
301
|
+
return (_jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto_minmax(0,1fr)_auto_minmax(0,1fr)_auto] gap-3 pb-2 border-b border-line font-mono text-[10px] uppercase tracking-[0.08em] text-ink-4", children: [_jsx("span", { children: t("pages.failedTasks.taskId") }), _jsx("span", { children: t("pages.failedTasks.painId") }), _jsx("span", { children: t("pages.failedTasks.status") }), _jsx("span", { children: t("pages.failedTasks.lastError") }), _jsx("span", { children: t("pages.failedTasks.attempts") }), _jsx("span", { children: t("pages.failedTasks.lastAttempt") }), _jsx("span", { "aria-hidden": "true" })] }), tasks.map((task) => (_jsxs("div", { className: "grid grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto_minmax(0,1fr)_auto_minmax(0,1fr)_auto] gap-3 py-2 items-center text-[13px] border-b border-line/50 last:border-b-0", children: [_jsx("span", { className: "font-mono text-ink-2 truncate", title: task.taskId, children: truncateId(task.taskId) }), _jsx("span", { className: "font-mono text-ink-3 truncate", title: task.painId ?? "", children: task.painId ? truncateId(task.painId) : t("pages.failedTasks.noPainId") }), _jsx(Badge, { variant: statusToVariant(task.status), children: task.status }), _jsx("span", { className: "text-ink-3 truncate", title: task.lastError ?? "", children: task.lastError ?? "—" }), _jsx("span", { className: "font-mono text-ink-2 text-center", children: task.attemptCount }), _jsx("span", { className: "font-mono text-ink-3 text-[12px]", children: task.lastAttemptAt ? formatDate(task.lastAttemptAt) : "—" }), onRecover && (_jsx(Button, { variant: "default", size: "sm", onClick: () => onRecover(task), className: "whitespace-nowrap", children: t("pages.failedTasks.recover") })), _jsx(Button, { variant: "outline", size: "sm", onClick: () => onCreateDraft(task), className: "whitespace-nowrap", children: t("pages.failedTasks.createDraft") })] }, task.taskId)))] }));
|
|
229
302
|
}
|
|
@@ -426,7 +426,11 @@ export function FocusPage() {
|
|
|
426
426
|
const ruleCodePending = ruleCodeItems.filter(item => item.action === 'code_tool_hook_shadow_activate' && item.enforcement !== 'safety_isolated');
|
|
427
427
|
const ruleCodeAlerts = ruleCodeItems.filter(item => item.enforcement === 'safety_isolated');
|
|
428
428
|
// Map codes to i18n text
|
|
429
|
-
|
|
429
|
+
// Governance Recovery Actions v1: tasks_need_human_review interpolates the
|
|
430
|
+
// needs_human_review task count, not the approvals count.
|
|
431
|
+
const humanReviewCount = queueData?.pendingHumanReviewCount ?? 0;
|
|
432
|
+
const stateReasonCount = stateReasonCode === "tasks_need_human_review" ? humanReviewCount : pendingCount;
|
|
433
|
+
const stateReason = getStateReasonText(stateReasonCode, t, stateReasonCount);
|
|
430
434
|
const nextAction = getNextActionText(nextActionCode, t);
|
|
431
435
|
return (_jsx(PageShell, { children: _jsxs("div", { className: "animate-[pdFadeIn_400ms_ease-out]", children: [_jsx("div", { className: "font-mono text-[12px] tracking-[0.14em] text-ink-3 uppercase mb-3", children: t("pages.focus.eyebrow") }), _jsx(ShinyText, { as: "h1", className: "text-[29px] font-semibold tracking-tight text-ink mb-2", duration: 4.5, brightness: 0.5, disabled: pendingCount === 0, children: t("pages.focus.title") }), _jsx("p", { className: "text-ink-3 text-[14px] max-w-[760px] leading-relaxed mb-7", children: t("pages.focus.subtitle") }), _jsxs("div", { className: "mb-7 px-[18px] py-[14px] bg-panel border border-line rounded-[6px]", children: [_jsx("div", { className: "flex items-center gap-2 mb-1", children: _jsx("span", { className: `inline-flex items-center rounded-[2px] px-[7px] py-1 font-mono text-[11px] uppercase ${governanceState === "owner_review_ready"
|
|
432
436
|
? "bg-gov/10 text-gov border border-gov/20"
|
|
@@ -219,6 +219,8 @@ export interface DegradedSignalData {
|
|
|
219
219
|
export interface GovernanceQueueData {
|
|
220
220
|
pendingReviewCount: number;
|
|
221
221
|
behaviorDeviationCount: number;
|
|
222
|
+
/** Governance Recovery Actions v1: needs_human_review internalization tasks (owner-attention items) */
|
|
223
|
+
pendingHumanReviewCount?: number;
|
|
222
224
|
stagnationSignals: StagnationSignalData[];
|
|
223
225
|
governanceState: 'none' | 'in_progress' | 'owner_review_ready' | 'degraded';
|
|
224
226
|
stateReasonCode: string;
|
|
@@ -232,6 +234,15 @@ export interface GovernanceQueueData {
|
|
|
232
234
|
note?: string;
|
|
233
235
|
generatedAt?: string;
|
|
234
236
|
}
|
|
237
|
+
export interface RecoveryResultData {
|
|
238
|
+
taskId: string;
|
|
239
|
+
previousStatus: string;
|
|
240
|
+
newStatus: string;
|
|
241
|
+
/** 'recovered' (failed→pending) | 'requeued' (needs_human_review→pending) */
|
|
242
|
+
result: string;
|
|
243
|
+
nextAction?: string;
|
|
244
|
+
}
|
|
245
|
+
export declare function validateRecoveryResult(v: unknown): RecoveryResultData | null;
|
|
235
246
|
export declare function validateGovernanceQueue(v: unknown): GovernanceQueueData | null;
|
|
236
247
|
export interface ActivationRecordData {
|
|
237
248
|
activationId: string;
|
|
@@ -596,10 +596,12 @@ function validateStagnationSignal(v) {
|
|
|
596
596
|
const VALID_GOVERNANCE_STATES = new Set(['none', 'in_progress', 'owner_review_ready', 'degraded']);
|
|
597
597
|
const VALID_STATE_REASON_CODES = new Set([
|
|
598
598
|
'state_db_missing', 'no_pipeline_activity', 'pending_approvals',
|
|
599
|
+
'tasks_need_human_review',
|
|
599
600
|
'pipeline_active', 'consumed_candidates', 'degraded_state',
|
|
600
601
|
]);
|
|
601
602
|
const VALID_NEXT_ACTION_CODES = new Set([
|
|
602
603
|
'run_config_doctor', 'wait_for_pipeline', 'review_approvals',
|
|
604
|
+
'review_failed_tasks',
|
|
603
605
|
'check_degraded_signals', 'check_pipeline_status',
|
|
604
606
|
]);
|
|
605
607
|
const VALID_DEGRADED_REASON_CODES = new Set(['task_retry_wait', 'task_failed', 'approval_table_missing', 'trajectory_db_unavailable']);
|
|
@@ -629,6 +631,30 @@ function validateDegradedSignal(v) {
|
|
|
629
631
|
source: v.source,
|
|
630
632
|
};
|
|
631
633
|
}
|
|
634
|
+
export function validateRecoveryResult(v) {
|
|
635
|
+
if (!isObject(v))
|
|
636
|
+
return null;
|
|
637
|
+
if (!Object.hasOwn(v, 'taskId') || !isString(v.taskId))
|
|
638
|
+
return null;
|
|
639
|
+
if (!Object.hasOwn(v, 'previousStatus') || !isString(v.previousStatus))
|
|
640
|
+
return null;
|
|
641
|
+
if (!Object.hasOwn(v, 'newStatus') || !isString(v.newStatus))
|
|
642
|
+
return null;
|
|
643
|
+
if (!Object.hasOwn(v, 'result') || !isString(v.result))
|
|
644
|
+
return null;
|
|
645
|
+
const result = {
|
|
646
|
+
taskId: v.taskId,
|
|
647
|
+
previousStatus: v.previousStatus,
|
|
648
|
+
newStatus: v.newStatus,
|
|
649
|
+
result: v.result,
|
|
650
|
+
};
|
|
651
|
+
if (Object.hasOwn(v, 'nextAction')) {
|
|
652
|
+
if (!isString(v.nextAction))
|
|
653
|
+
return null;
|
|
654
|
+
result.nextAction = v.nextAction;
|
|
655
|
+
}
|
|
656
|
+
return result;
|
|
657
|
+
}
|
|
632
658
|
export function validateGovernanceQueue(v) {
|
|
633
659
|
if (!isObject(v))
|
|
634
660
|
return null;
|
|
@@ -699,6 +725,12 @@ export function validateGovernanceQueue(v) {
|
|
|
699
725
|
return null;
|
|
700
726
|
result.evidenceInProgressCount = v.evidenceInProgressCount;
|
|
701
727
|
}
|
|
728
|
+
// Governance Recovery Actions v1: needs_human_review task count
|
|
729
|
+
if (Object.hasOwn(v, 'pendingHumanReviewCount')) {
|
|
730
|
+
if (!isNumber(v.pendingHumanReviewCount))
|
|
731
|
+
return null;
|
|
732
|
+
result.pendingHumanReviewCount = v.pendingHumanReviewCount;
|
|
733
|
+
}
|
|
702
734
|
// Wave 4: gate blocks today (seconds-level auto-blocks)
|
|
703
735
|
if (Object.hasOwn(v, 'gateBlocksToday')) {
|
|
704
736
|
if (!isNumber(v.gateBlocksToday))
|