create-principles-disciple 1.145.35 → 1.145.36
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/ApprovalsConsoleModel.d.ts +22 -2
- package/console/dist/server/models/ApprovalsConsoleModel.js +56 -16
- package/console/dist/server/models/ApprovalsGroupedConsoleModel.d.ts +8 -0
- package/console/dist/server/models/ApprovalsGroupedConsoleModel.js +3 -2
- package/console/dist/server/models/approval-review-material.d.ts +15 -0
- package/console/dist/server/models/approval-review-material.js +32 -0
- package/console/dist/server/routes/approvals.js +32 -7
- package/console/dist/ui/api.d.ts +8 -2
- package/console/dist/ui/api.js +8 -4
- package/console/dist/ui/i18n/en.json +98 -7
- package/console/dist/ui/i18n/zh-CN.json +98 -7
- package/console/dist/ui/pages/focus/ApprovalSubjectReading.d.ts +24 -0
- package/console/dist/ui/pages/focus/ApprovalSubjectReading.js +133 -0
- package/console/dist/ui/pages/focus/FocusPage.d.ts +5 -26
- package/console/dist/ui/pages/focus/FocusPage.js +166 -459
- package/console/dist/ui/pages/focus/OwnerWorkbench.d.ts +19 -0
- package/console/dist/ui/pages/focus/OwnerWorkbench.js +75 -0
- package/console/dist/ui/pages/focus/RuleCandidateBindingPanel.d.ts +4 -1
- package/console/dist/ui/pages/focus/RuleCandidateBindingPanel.js +7 -3
- package/console/dist/ui/pages/focus/focus-validation.js +31 -0
- package/console/dist/ui/pages/principles/PrincipleDetailPage.js +110 -86
- package/console/dist/ui/pages/principles/PrinciplesPage.js +39 -30
- package/console/dist/ui/utils/approval-review.d.ts +30 -0
- package/console/dist/ui/utils/approval-review.js +50 -0
- package/console/dist/ui/utils/reading-layout.d.ts +1 -0
- package/console/dist/ui/utils/reading-layout.js +1 -1
- package/console/dist/ui/utils/validators.d.ts +4 -0
- package/console/dist/ui/utils/validators.js +10 -2
- package/console/dist/web/assets/app.css +119 -11
- package/console/dist/web/assets/app.js +40 -30
- 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/approval-queue.d.ts +3 -2
- package/core/dist/runtime-v2/activation/approval-queue.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/approval-queue.js +6 -4
- package/core/dist/runtime-v2/activation/approval-queue.js.map +1 -1
- package/core/dist/runtime-v2/activation/approval-review-guard.d.ts +26 -0
- package/core/dist/runtime-v2/activation/approval-review-guard.d.ts.map +1 -0
- package/core/dist/runtime-v2/activation/approval-review-guard.js +25 -0
- package/core/dist/runtime-v2/activation/approval-review-guard.js.map +1 -0
- package/core/dist/runtime-v2/activation/memory-approval-store.d.ts +6 -2
- package/core/dist/runtime-v2/activation/memory-approval-store.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/memory-approval-store.js +26 -2
- package/core/dist/runtime-v2/activation/memory-approval-store.js.map +1 -1
- package/core/dist/runtime-v2/activation/sqlite-approval-store.d.ts +6 -2
- package/core/dist/runtime-v2/activation/sqlite-approval-store.d.ts.map +1 -1
- package/core/dist/runtime-v2/activation/sqlite-approval-store.js +59 -2
- package/core/dist/runtime-v2/activation/sqlite-approval-store.js.map +1 -1
- package/core/dist/runtime-v2/diagnostician-association-shadow.d.ts +24 -0
- package/core/dist/runtime-v2/diagnostician-association-shadow.d.ts.map +1 -0
- package/core/dist/runtime-v2/diagnostician-association-shadow.js +84 -0
- package/core/dist/runtime-v2/diagnostician-association-shadow.js.map +1 -0
- package/core/dist/runtime-v2/diagnostician-association-validation.d.ts +18 -0
- package/core/dist/runtime-v2/diagnostician-association-validation.d.ts.map +1 -0
- package/core/dist/runtime-v2/diagnostician-association-validation.js +47 -0
- package/core/dist/runtime-v2/diagnostician-association-validation.js.map +1 -0
- package/core/dist/runtime-v2/diagnostician-output.d.ts +123 -0
- package/core/dist/runtime-v2/diagnostician-output.d.ts.map +1 -1
- package/core/dist/runtime-v2/diagnostician-output.js +41 -0
- package/core/dist/runtime-v2/diagnostician-output.js.map +1 -1
- package/core/dist/runtime-v2/index.d.ts +2 -0
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +1 -0
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/internalization/diag-router-runner.d.ts +12 -1
- package/core/dist/runtime-v2/internalization/diag-router-runner.d.ts.map +1 -1
- package/core/dist/runtime-v2/internalization/diag-router-runner.js +50 -2
- package/core/dist/runtime-v2/internalization/diag-router-runner.js.map +1 -1
- package/core/dist/runtime-v2/internalization/router-context-evidence.d.ts +45 -0
- package/core/dist/runtime-v2/internalization/router-context-evidence.d.ts.map +1 -0
- package/core/dist/runtime-v2/internalization/router-context-evidence.js +64 -0
- package/core/dist/runtime-v2/internalization/router-context-evidence.js.map +1 -0
- package/core/package.json +1 -1
- package/package.json +1 -1
- package/pd-cli/package.json +1 -1
- package/plugin/dist/bundle.js +395 -395
- package/release-manager/package.json +1 -1
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState, useEffect, useCallback } from "react";
|
|
3
|
-
import { useParams, useNavigate, Link } from "react-router-dom";
|
|
2
|
+
import { useState, useEffect, useCallback, useRef } from "react";
|
|
3
|
+
import { useParams, useNavigate, useLocation, Link } from "react-router-dom";
|
|
4
4
|
import { useTranslation } from "react-i18next";
|
|
5
5
|
import { toast } from "sonner";
|
|
6
6
|
import { PageShell } from "../../components/layout/page-shell.js";
|
|
7
7
|
import { PageLoading } from "../../components/layout/page-loading.js";
|
|
8
8
|
import { SectionTitle } from "../../components/layout/section-title.js";
|
|
9
9
|
import { Button } from "../../components/ui/button.js";
|
|
10
|
-
import { fetchPrincipleDetail, fetchApprovalsGrouped, fetchLifecycleMetrics, fetchPrincipleTrajectory, fetchPrincipleGovernance, fetchPrincipleReceipts, fetchEvidenceAudit, fetchOwnerDecisionView,
|
|
10
|
+
import { fetchPrincipleDetail, fetchApprovalsGrouped, fetchLifecycleMetrics, fetchPrincipleTrajectory, fetchPrincipleGovernance, fetchPrincipleReceipts, fetchEvidenceAudit, fetchOwnerDecisionView, disableActivation, } from "../../api.js";
|
|
11
|
+
import { ApprovalSubjectReading } from "../focus/ApprovalSubjectReading.js";
|
|
11
12
|
import { ReceiptCoverageDisclosure, getReceiptSourceStatusLabelKey } from "../../components/receipts/ReceiptCoverageDisclosure.js";
|
|
12
13
|
import { formatDate } from "../../utils/format-date.js";
|
|
13
14
|
import { localizeAggregatedBlocker } from "../../utils/blocker-localization.js";
|
|
14
15
|
import { groupByReasonCode, formatGroupedReasonLine } from "../../utils/attention-presentation.js";
|
|
15
|
-
import { splitReadingTitleBody } from "../../utils/reading-layout.js";
|
|
16
|
+
import { splitReadingTitleBody, truncateByCodePoints } from "../../utils/reading-layout.js";
|
|
16
17
|
// ── Runtime validation (H section) ──────────────────────────────────────────
|
|
17
18
|
function isString(v) {
|
|
18
19
|
return typeof v === "string";
|
|
@@ -217,8 +218,13 @@ export function PrincipleDetailPage() {
|
|
|
217
218
|
const { t, i18n } = useTranslation("pages");
|
|
218
219
|
const { id } = useParams();
|
|
219
220
|
const navigate = useNavigate();
|
|
221
|
+
const location = useLocation();
|
|
222
|
+
const navigationState = location.state;
|
|
223
|
+
const requestedReturn = navigationState !== null && typeof navigationState === 'object' && Object.hasOwn(navigationState, 'returnTo') ? Reflect.get(navigationState, 'returnTo') : null;
|
|
224
|
+
const returnTo = typeof requestedReturn === 'string' && (requestedReturn === '/principles' || requestedReturn.startsWith('/principles?') || requestedReturn === '/focus' || requestedReturn.startsWith('/focus?')) ? requestedReturn : '/principles';
|
|
220
225
|
const [principle, setPrinciple] = useState(null);
|
|
221
226
|
const [approvalGroup, setApprovalGroup] = useState(null);
|
|
227
|
+
const [approvalGroupUnavailable, setApprovalGroupUnavailable] = useState(false);
|
|
222
228
|
const [lifecycle, setLifecycle] = useState(null);
|
|
223
229
|
const [trajectory, setTrajectory] = useState(null);
|
|
224
230
|
const [governance, setGovernance] = useState(null);
|
|
@@ -232,6 +238,7 @@ export function PrincipleDetailPage() {
|
|
|
232
238
|
const [loadingEvidenceKind, setLoadingEvidenceKind] = useState(null);
|
|
233
239
|
const [loading, setLoading] = useState(true);
|
|
234
240
|
const [error, setError] = useState(null);
|
|
241
|
+
const detailEpoch = useRef(0);
|
|
235
242
|
// Decision action state — driven by backend available_actions only.
|
|
236
243
|
const [pendingAction, setPendingAction] = useState(null);
|
|
237
244
|
const [rejectReason, setRejectReason] = useState("");
|
|
@@ -245,9 +252,12 @@ export function PrincipleDetailPage() {
|
|
|
245
252
|
const loadData = useCallback(async () => {
|
|
246
253
|
if (!id)
|
|
247
254
|
return;
|
|
255
|
+
const epoch = ++detailEpoch.current;
|
|
248
256
|
setLoading(true);
|
|
257
|
+
setLoadingEvidenceKind(null);
|
|
249
258
|
setError(null);
|
|
250
259
|
setApprovalGroup(null); // Clear previous approval group to prevent stale actionability (P1)
|
|
260
|
+
setApprovalGroupUnavailable(false);
|
|
251
261
|
setGovernance(null);
|
|
252
262
|
setGovernanceUnavailable(null);
|
|
253
263
|
setOwnerDecision(null);
|
|
@@ -265,6 +275,8 @@ export function PrincipleDetailPage() {
|
|
|
265
275
|
fetchPrincipleReceipts(id),
|
|
266
276
|
fetchEvidenceAudit('principle', id),
|
|
267
277
|
]);
|
|
278
|
+
if (epoch !== detailEpoch.current)
|
|
279
|
+
return;
|
|
268
280
|
if (!pResult.success) {
|
|
269
281
|
setError(pResult.error ?? "Failed to load principle");
|
|
270
282
|
return;
|
|
@@ -282,6 +294,9 @@ export function PrincipleDetailPage() {
|
|
|
282
294
|
}
|
|
283
295
|
else {
|
|
284
296
|
setApprovalGroup(null);
|
|
297
|
+
// The grouped list is the only source of pending approval records here;
|
|
298
|
+
// swallowing the failure would hide the decision surface without a word.
|
|
299
|
+
setApprovalGroupUnavailable(true);
|
|
285
300
|
}
|
|
286
301
|
const lData = lResult.success ? validateLifecycleMetrics(lResult.data) : null;
|
|
287
302
|
setLifecycle(lData);
|
|
@@ -322,15 +337,25 @@ export function PrincipleDetailPage() {
|
|
|
322
337
|
}
|
|
323
338
|
}
|
|
324
339
|
catch (err) {
|
|
340
|
+
if (epoch !== detailEpoch.current)
|
|
341
|
+
return;
|
|
325
342
|
setError(err instanceof Error ? err.message : "Unknown error");
|
|
326
343
|
setApprovalGroup(null);
|
|
344
|
+
setApprovalGroupUnavailable(true);
|
|
327
345
|
}
|
|
328
346
|
finally {
|
|
329
|
-
|
|
347
|
+
if (epoch === detailEpoch.current)
|
|
348
|
+
setLoading(false);
|
|
330
349
|
}
|
|
331
350
|
}, [id]);
|
|
332
351
|
useEffect(() => {
|
|
333
|
-
|
|
352
|
+
setPendingAction(null);
|
|
353
|
+
setRejectReason('');
|
|
354
|
+
setNewArtifactId('');
|
|
355
|
+
setApproveNote('');
|
|
356
|
+
setActionLoading(false);
|
|
357
|
+
void loadData();
|
|
358
|
+
return () => { detailEpoch.current++; };
|
|
334
359
|
}, [loadData]);
|
|
335
360
|
const loadOlderEvidence = async (kind) => {
|
|
336
361
|
if (!id || !evidenceAudit || loadingEvidenceKind)
|
|
@@ -339,8 +364,11 @@ export function PrincipleDetailPage() {
|
|
|
339
364
|
if (!page.hasMore || !page.nextCursor)
|
|
340
365
|
return;
|
|
341
366
|
setLoadingEvidenceKind(kind);
|
|
367
|
+
const epoch = detailEpoch.current;
|
|
342
368
|
try {
|
|
343
369
|
const result = await fetchEvidenceAudit('principle', id, { kind, after: page.nextCursor });
|
|
370
|
+
if (epoch !== detailEpoch.current)
|
|
371
|
+
return;
|
|
344
372
|
if (!result.success || !result.data)
|
|
345
373
|
return;
|
|
346
374
|
if (result.data.status !== 'ok') {
|
|
@@ -360,20 +388,26 @@ export function PrincipleDetailPage() {
|
|
|
360
388
|
}));
|
|
361
389
|
}
|
|
362
390
|
catch (err) {
|
|
391
|
+
if (epoch !== detailEpoch.current)
|
|
392
|
+
return;
|
|
363
393
|
toast.error(err instanceof Error ? err.message : '读取更早的干预证据失败');
|
|
364
394
|
}
|
|
365
395
|
finally {
|
|
366
|
-
|
|
396
|
+
if (epoch === detailEpoch.current)
|
|
397
|
+
setLoadingEvidenceKind(null);
|
|
367
398
|
}
|
|
368
399
|
};
|
|
369
400
|
// ── Owner decision actions (SPEC §8.4: mutation service is the authority) ─
|
|
370
401
|
// The view's actions are advisory; every submission goes through the real
|
|
371
402
|
// mutation service. On failure: show the service reason, invalidate stale
|
|
372
403
|
// actions, re-GET the view. Never auto-retry a mutation.
|
|
373
|
-
const submitOwnerAction = async (action
|
|
404
|
+
const submitOwnerAction = async (action) => {
|
|
374
405
|
if (actionLoading)
|
|
375
406
|
return;
|
|
407
|
+
if (action.semantic !== 'disable')
|
|
408
|
+
return;
|
|
376
409
|
setActionLoading(true);
|
|
410
|
+
const epoch = detailEpoch.current;
|
|
377
411
|
try {
|
|
378
412
|
let failed = false;
|
|
379
413
|
let failureDetail = '';
|
|
@@ -383,37 +417,7 @@ export function PrincipleDetailPage() {
|
|
|
383
417
|
failureDetail = `${result.error ?? ''}${result.nextAction ? ` ${result.nextAction}` : ''}`.trim();
|
|
384
418
|
}
|
|
385
419
|
};
|
|
386
|
-
if (action.semantic === '
|
|
387
|
-
const approvalId = actionTargetId(action, 'approval');
|
|
388
|
-
if (approvalId === null) {
|
|
389
|
-
toast.error(t("principles.detail.ownerDecision.invalidTarget", { defaultValue: "动作目标缺失,无法提交。" }));
|
|
390
|
-
return;
|
|
391
|
-
}
|
|
392
|
-
const approveResult = await approveApproval(approvalId, note);
|
|
393
|
-
settle(approveResult);
|
|
394
|
-
// PRI-890: non-fatal server warnings (e.g. prompt injection budget
|
|
395
|
-
// exclusion) must reach the Owner on this path too, not vanish.
|
|
396
|
-
if (approveResult.success && typeof approveResult.data?.warning === 'string') {
|
|
397
|
-
toast.warning(approveResult.data.warning, { duration: 15000 });
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
else if (action.semantic === 'reject') {
|
|
401
|
-
const approvalId = actionTargetId(action, 'approval');
|
|
402
|
-
if (approvalId === null) {
|
|
403
|
-
toast.error(t("principles.detail.ownerDecision.invalidTarget", { defaultValue: "动作目标缺失,无法提交。" }));
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
settle(await rejectApproval(approvalId, reason ?? ""));
|
|
407
|
-
}
|
|
408
|
-
else if (action.semantic === 'edit_approval') {
|
|
409
|
-
const approvalId = actionTargetId(action, 'approval');
|
|
410
|
-
if (approvalId === null || reason === undefined || newArtifactId.trim() === '') {
|
|
411
|
-
toast.error(t("principles.detail.ownerDecision.invalidTarget", { defaultValue: "动作目标缺失,无法提交。" }));
|
|
412
|
-
return;
|
|
413
|
-
}
|
|
414
|
-
settle(await editApproval(approvalId, newArtifactId.trim(), reason));
|
|
415
|
-
}
|
|
416
|
-
else if (action.semantic === 'disable') {
|
|
420
|
+
if (action.semantic === 'disable') {
|
|
417
421
|
const activationId = actionTargetId(action, 'activation');
|
|
418
422
|
if (activationId === null) {
|
|
419
423
|
toast.error(t("principles.detail.ownerDecision.invalidTarget", { defaultValue: "动作目标缺失,无法提交。" }));
|
|
@@ -424,6 +428,8 @@ export function PrincipleDetailPage() {
|
|
|
424
428
|
else {
|
|
425
429
|
return; // unknown semantics are never rendered; belt-and-suspenders
|
|
426
430
|
}
|
|
431
|
+
if (epoch !== detailEpoch.current)
|
|
432
|
+
return;
|
|
427
433
|
if (!failed) {
|
|
428
434
|
toast.success(t("principles.detail.ownerDecision.actionDone", { defaultValue: "操作已提交。" }));
|
|
429
435
|
}
|
|
@@ -437,16 +443,20 @@ export function PrincipleDetailPage() {
|
|
|
437
443
|
}
|
|
438
444
|
}
|
|
439
445
|
catch (err) {
|
|
446
|
+
if (epoch !== detailEpoch.current)
|
|
447
|
+
return;
|
|
440
448
|
toast.error(err instanceof Error ? err.message : String(err));
|
|
441
449
|
}
|
|
442
450
|
finally {
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
451
|
+
if (epoch === detailEpoch.current) {
|
|
452
|
+
setActionLoading(false);
|
|
453
|
+
setPendingAction(null);
|
|
454
|
+
setRejectReason("");
|
|
455
|
+
setNewArtifactId("");
|
|
456
|
+
setApproveNote("");
|
|
457
|
+
// Invalidate the stale action list and re-derive from a fresh GET.
|
|
458
|
+
await loadData();
|
|
459
|
+
}
|
|
450
460
|
}
|
|
451
461
|
};
|
|
452
462
|
// ── Render ──────────────────────────────────────────────────────────────
|
|
@@ -454,7 +464,7 @@ export function PrincipleDetailPage() {
|
|
|
454
464
|
return (_jsx(PageShell, { children: _jsx(PageLoading, { cardCount: 1, label: t("principles.loading", { defaultValue: "Loading…" }) }) }));
|
|
455
465
|
}
|
|
456
466
|
if (error || !principle) {
|
|
457
|
-
return (_jsxs(PageShell, { children: [_jsxs(Button, { variant: "ghost", onClick: () => navigate(
|
|
467
|
+
return (_jsxs(PageShell, { children: [_jsxs(Button, { variant: "ghost", onClick: () => navigate(returnTo), className: "mb-4", children: ["\u2190 ", t("principles.detail.backToList")] }), _jsxs("div", { className: "border border-danger/30 rounded-[var(--radius-md)] p-4", children: [_jsx("p", { className: "text-danger text-sm", children: error ?? t("principles.notFound") }), _jsx(Button, { variant: "outline", size: "sm", onClick: loadData, className: "mt-2", children: t("principles.retry", { defaultValue: "Retry" }) })] })] }));
|
|
458
468
|
}
|
|
459
469
|
const hasRules = principle.rules.length > 0;
|
|
460
470
|
const receiptPresentation = receipts?.status === 'ok'
|
|
@@ -466,30 +476,36 @@ export function PrincipleDetailPage() {
|
|
|
466
476
|
? ownerDecision.learnedPrinciple.value.text
|
|
467
477
|
: undefined;
|
|
468
478
|
const learnedKnown = learnedText !== undefined;
|
|
479
|
+
const learnedPreview = learnedText === undefined ? undefined : truncateByCodePoints(learnedText, 240);
|
|
480
|
+
const learnedTruncated = learnedPreview !== learnedText;
|
|
469
481
|
const headlineLayout = splitReadingTitleBody(learnedText ?? principle.text);
|
|
470
|
-
return (_jsx(PageShell, { children: _jsxs("div", { className: "animate-[pdFadeIn_400ms_ease-out]", children: [_jsxs(Button, { variant: "ghost", onClick: () => navigate(
|
|
482
|
+
return (_jsx(PageShell, { children: _jsxs("div", { className: "animate-[pdFadeIn_400ms_ease-out]", children: [_jsxs(Button, { variant: "ghost", onClick: () => navigate(returnTo), className: "mb-4 -ml-2", children: ["\u2190 ", t("principles.detail.backToList")] }), _jsxs("section", { className: "mb-8", children: [_jsx(SectionTitle, { children: t("principles.detail.conclusion") }), _jsx("h1", { className: "text-[22px] font-semibold text-ink leading-snug mb-3", "data-testid": "principle-detail-title", children: headlineLayout.title }), headlineLayout.body !== null && !learnedKnown && (_jsx("div", { className: "mb-3 text-[15px] leading-relaxed text-ink whitespace-pre-wrap break-words", "data-testid": "principle-detail-body", children: headlineLayout.body })), ownerDecision !== null && ownerDecision.learnedPrinciple.status !== 'known' && (_jsx("p", { className: "text-amber text-[13px] leading-relaxed mb-3", "data-testid": "owner-decision-technical-note", children: t("principles.detail.ownerDecision.technicalOriginalNote", {
|
|
471
483
|
defaultValue: "以上为原始技术建议文本;可读的行为准则暂缺(保留原文以保证可追溯)。",
|
|
472
|
-
}) })), _jsx("p", { className: "text-ink-3 text-[14px] leading-relaxed mb-3", children: t("principles.detail.policyNote") }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(
|
|
484
|
+
}) })), _jsx("p", { className: "text-ink-3 text-[14px] leading-relaxed mb-3", children: t("principles.detail.policyNote") }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { className: "text-ink-4 text-[13px]", children: t("principles.detail.modifyWordingNote") }), _jsx(Button, { variant: "ghost", size: "sm", className: "ml-auto", "data-testid": "principle-feedback", onClick: () => {
|
|
473
485
|
const params = new URLSearchParams({ source: "principle_page", principleId: id ?? "" });
|
|
474
486
|
navigate(`/report-problem?${params.toString()}`);
|
|
475
487
|
}, children: t("reportProblem.entryFeedback") })] })] }), ownerDecision !== null && (_jsxs("section", { className: "mb-8", "aria-labelledby": "owner-decision-title", "data-testid": "owner-decision-view", children: [_jsx(SectionTitle, { children: t("principles.detail.ownerDecision.title", { defaultValue: "这条原则的决策视图" }) }), _jsxs("div", { className: "rounded-[var(--radius-md)] border border-gov/25 bg-gov/5 p-4", children: [_jsxs("div", { className: "flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between", children: [_jsxs("div", { children: [_jsx("h2", { id: "owner-decision-title", className: "text-[18px] font-semibold text-ink", children: t(DECISION_STATE_LABEL_KEY[ownerDecision.decisionState]) }), _jsx("p", { className: "mt-1 text-[13px] leading-relaxed text-ink-2", children: localizeNextAction(ownerDecision.nextAction.code, ownerDecision.nextAction.ownerText, t) })] }), _jsx("span", { "data-testid": "owner-decision-state", className: "w-fit rounded-full border border-line px-2 py-1 font-mono text-[11px] text-ink-3", children: ownerDecision.sourceReadStatus === 'complete'
|
|
476
488
|
? t("principles.detail.ownerDecision.readsComplete", { defaultValue: "本次所需数据已读取" })
|
|
477
|
-
: t("principles.detail.ownerDecision.readsPartial", { defaultValue: "部分数据源读取失败" }) })] }),
|
|
489
|
+
: t("principles.detail.ownerDecision.readsPartial", { defaultValue: "部分数据源读取失败" }) })] }), _jsxs("div", { className: "mt-4", "data-testid": "owner-decision-learned", children: [_jsx("p", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4", children: t("principles.detail.ownerDecision.learned", { defaultValue: "Agent 学到的行为准则" }) }), ownerDecision.learnedPrinciple.status === 'known' ? (_jsxs(_Fragment, { children: [_jsx("p", { className: "mt-1 text-ink-2 text-[15px] leading-relaxed whitespace-pre-wrap break-words", children: learnedPreview }), learnedTruncated && _jsxs("details", { className: "mt-3 text-sm text-ink-2", children: [_jsx("summary", { className: "cursor-pointer text-gov", children: t('principles.detail.fullRule') }), _jsx("p", { className: "mt-3 whitespace-pre-wrap break-words leading-relaxed", children: learnedText })] }), _jsx("p", { className: "mt-1 text-ink-4 text-[12px]", children: t("principles.detail.ownerDecision.sourceTierLabel", {
|
|
478
490
|
defaultValue: "来源:{{tier}}(未经改写)",
|
|
479
491
|
tier: t(`principles.detail.ownerDecision.tier.${ownerDecision.learnedPrinciple.value.sourceTier}`, { defaultValue: ownerDecision.learnedPrinciple.value.sourceTier }),
|
|
480
|
-
}) })] })) : (_jsx("p", { className: "mt-1 text-amber text-[13px] leading-relaxed", children: ownerDecision.learnedPrinciple.reason.ownerText }))] }),
|
|
481
|
-
? t("principles.detail.ownerDecision.enforcement.active", { defaultValue: "当前有正在执行的生效方式(见下方明细)。" })
|
|
482
|
-
: ownerDecision.currentEnforcement.value?.state === 'deactivated'
|
|
483
|
-
? t("principles.detail.ownerDecision.enforcement.deactivated", { defaultValue: "此前的执行方式已停用;当前没有正在执行的生效方式。" })
|
|
484
|
-
: ownerDecision.currentEnforcement.value?.state === 'partially_active'
|
|
485
|
-
? t("principles.detail.ownerDecision.enforcement.partial", { defaultValue: "部分执行方式仍在运行(见下方明细)。" })
|
|
486
|
-
: t("principles.detail.ownerDecision.enforcement.none", { defaultValue: "这条原则当前没有生效中的执行方式。" }) }), ownerDecision.currentEnforcement.value?.items.map((item) => (_jsxs("p", { className: "mt-1 text-ink-3 text-[12px]", children: [item.mode.status === 'known' ? item.mode.value : item.channel, " · ", item.active
|
|
487
|
-
? t("principles.detail.ownerDecision.enforcement.since", { defaultValue: "生效中(自 {{since}})", since: item.since.status === 'known' ? item.since.value.slice(0, 10) : '?' })
|
|
488
|
-
: t("principles.detail.ownerDecision.enforcement.stopped", { defaultValue: "已停用" })] }, item.activationRef.id)))] })), ownerDecision.currentEnforcement.status === 'unknown' && (_jsx("p", { className: "mt-4 text-amber text-[13px]", "data-testid": "owner-decision-enforcement-unknown", children: ownerDecision.currentEnforcement.reason?.ownerText })), ownerDecision.evidenceSummary.status === 'known' && (_jsxs("div", { className: "mt-4", "data-testid": "owner-decision-evidence", children: [_jsx("p", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4", children: t("principles.detail.ownerDecision.evidence", { defaultValue: "行为证据" }) }), _jsx("p", { className: "mt-1 text-ink-2 text-[13px] leading-relaxed", children: ownerDecision.evidenceSummary.value?.ownerExplanation })] })), _jsxs("div", { className: "mt-4", "data-testid": "owner-decision-uncertainty", children: [_jsx("p", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4", children: t("principles.detail.ownerDecision.uncertainty", { defaultValue: "风险与不确定性" }) }), ownerDecision.risk.status === 'known' && (ownerDecision.risk.value?.items.length ?? 0) > 0 && (_jsx("ul", { className: "mt-1 space-y-1 text-[13px] text-ink-2", children: ownerDecision.risk.value?.items.map((item, index) => (_jsx("li", { children: item.text }, `risk-${index}`))) })), ownerDecision.risk.status === 'known' && (ownerDecision.risk.value?.items.length ?? 0) === 0 && (_jsx("p", { className: "mt-1 text-ink-3 text-[13px]", children: t("principles.detail.ownerDecision.riskNotAssessed", { defaultValue: "尚未做过风险评估;这不代表没有风险。" }) })), ownerDecision.risk.status === 'unknown' && (_jsx("p", { className: "mt-1 text-ink-3 text-[13px]", children: ownerDecision.risk.reason?.ownerText })), ownerDecision.uncertainty.status === 'known' && ownerDecision.uncertainty.value.length > 0 && (_jsx("ul", { className: "mt-2 space-y-1 text-[12px] text-ink-3", children: ownerDecision.uncertainty.value.map((item, index) => (_jsx("li", { children: item.text }, `unc-${index}`))) }))] }), _jsxs("div", { className: "mt-4", "data-testid": "owner-decision-rollback", children: [_jsx("p", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4", children: t("principles.detail.ownerDecision.rollback", { defaultValue: "如何停止影响" }) }), ownerDecision.rollback.status === 'known' && (_jsxs(_Fragment, { children: [_jsx("p", { className: "mt-1 text-ink-2 text-[13px] leading-relaxed", children: ownerDecision.rollback.value?.ownerText }), ownerDecision.rollback.value?.stopFuture.status === 'known' && (_jsx("ul", { className: "mt-1 space-y-1 text-[12px] text-ink-3", children: ownerDecision.rollback.value.stopFuture.value.map((item, index) => (_jsx("li", { children: item.text }, `rb-${index}`))) })), ownerDecision.rollback.value?.stopFuture.status === 'not_applicable' && (_jsx("p", { className: "mt-1 text-ink-3 text-[12px]", children: ownerDecision.rollback.value.stopFuture.reason.ownerText }))] })), ownerDecision.rollback.status === 'unknown' && (_jsx("p", { className: "mt-1 text-amber text-[13px]", children: ownerDecision.rollback.reason?.ownerText }))] }), ownerDecision.decisionSubjects.some((subject) => subject.state !== 'pending') && (_jsxs("div", { className: "mt-3", "data-testid": "owner-decision-history", children: [_jsx("p", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4", children: t("principles.detail.ownerDecision.history", { defaultValue: "决定历史" }) }), _jsx("ul", { className: "mt-1 space-y-1 text-[13px] text-ink-2", children: ownerDecision.decisionSubjects.filter((subject) => subject.state !== 'pending').map((subject) => {
|
|
492
|
+
}) })] })) : (_jsx("p", { className: "mt-1 text-amber text-[13px] leading-relaxed", children: ownerDecision.learnedPrinciple.reason.ownerText }))] }), ownerDecision.decisionSubjects.some((subject) => subject.state !== 'pending') && (_jsxs("div", { className: "mt-3", "data-testid": "owner-decision-history", children: [_jsx("p", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4", children: t("principles.detail.ownerDecision.history", { defaultValue: "决定历史" }) }), _jsx("ul", { className: "mt-1 space-y-1 text-[13px] text-ink-2", children: ownerDecision.decisionSubjects.filter((subject) => subject.state !== 'pending').map((subject) => {
|
|
489
493
|
const decidedAt = subject.targetRefs.find((target) => target.kind === 'approval')?.recordedAt;
|
|
490
494
|
const stateKey = `principles.detail.ownerDecision.historyState.${subject.state}`;
|
|
491
495
|
return (_jsxs("li", { children: [t(stateKey, { defaultValue: subject.state }), decidedAt !== undefined ? ` · ${decidedAt.slice(0, 10)}` : '', ' · ', t(`principles.detail.ownerDecision.channel.${subject.channel}`, { defaultValue: subject.channel })] }, `hist-${subject.key}`));
|
|
492
|
-
}) })] })), _jsxs("div", { className: "mt-5 border-t border-line pt-4", "data-testid": "owner-decision-actions", children: [_jsx("p", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4", children: t("principles.detail.ownerDecision.decision", { defaultValue: "你的决定" }) }), ownerDecision.availableActions.length === 0 && (_jsx("p", { className: "mt-1 text-ink-3 text-[13px]", children: t("principles.detail.ownerDecision.noActions", { defaultValue: "当前没有可以执行的操作。" }) })),
|
|
496
|
+
}) })] })), _jsxs("div", { className: "mt-5 border-t border-line pt-4", "data-testid": "owner-decision-actions", children: [_jsx("p", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4", children: t("principles.detail.ownerDecision.decision", { defaultValue: "你的决定" }) }), ownerDecision.availableActions.length === 0 && (_jsx("p", { className: "mt-1 text-ink-3 text-[13px]", children: t("principles.detail.ownerDecision.noActions", { defaultValue: "当前没有可以执行的操作。" }) })), approvalGroupUnavailable && ownerDecision.availableActions.length > 0 && (_jsxs("div", { role: "alert", className: "mt-2 text-[13px] leading-relaxed text-amber", "data-testid": "detail-approvals-unavailable", children: [_jsx("p", { children: t('focus.workbench.approvalsUnavailable') }), _jsx("button", { type: "button", onClick: () => { void loadData(); }, className: "mt-1 text-gov underline", children: t('focus.workbench.retry') })] })), approvalGroup?.records.filter(record => record.status === 'pending').map(record => {
|
|
497
|
+
const allowedActions = ownerDecision.availableActions
|
|
498
|
+
.filter(action => actionTargetId(action, 'approval') === record.id)
|
|
499
|
+
.map(action => action.semantic)
|
|
500
|
+
.filter((semantic) => semantic === 'approve' || semantic === 'reject' || semantic === 'edit_approval');
|
|
501
|
+
// A sibling's broken revision review must not block this record;
|
|
502
|
+
// without any per-record defect the group flag still covers the
|
|
503
|
+
// "multiple versions pending" ambiguity.
|
|
504
|
+
const revisionReviewUnavailable = record.revisionReviewUnavailable
|
|
505
|
+
?? (approvalGroup.records.some(entry => entry.status === 'pending' && entry.revisionReviewUnavailable === true)
|
|
506
|
+
? false : approvalGroup.revisionReviewUnavailable);
|
|
507
|
+
return _jsxs("details", { className: "mt-4 rounded border border-line p-4", "data-testid": "detail-approval-review", children: [_jsxs("summary", { className: "cursor-pointer text-sm text-gov", children: [t('focus.workbench.proposal'), " \u00B7 ", t(`principles.detail.ownerDecision.channel.${record.channel}`, { defaultValue: record.channel })] }), _jsx("div", { className: "mt-5", children: _jsx(ApprovalSubjectReading, { principleId: principle.id, approvalId: record.id, artifactId: record.artifactId, channel: record.channel, showPrincipleLink: false, revisionReview: new Set(approvalGroup.records.filter(entry => entry.status === 'pending').map(entry => entry.artifactId)).size === 1 ? approvalGroup.revisionReview : undefined, revisionReviewUnavailable: revisionReviewUnavailable, allowedActions: allowedActions, lockedReason: ownerDecision.availableActions.some(action => actionTargetId(action, 'approval') === record.id) ? undefined : t('focus.workbench.permissionsUnknown'), onResolved: message => { toast.success(message); void loadData(); } }) })] }, record.id);
|
|
508
|
+
}), _jsx("div", { className: "mt-2 flex flex-wrap gap-3", children: ownerDecision.availableActions.filter(action => action.semantic === 'disable').map((action) => (_jsx(Button, { variant: action.semantic === 'reject' ? 'destructive' : action.semantic === 'disable' ? 'outline' : 'default', size: "sm", disabled: actionLoading, "data-action-semantic": action.semantic, onClick: () => {
|
|
493
509
|
setRejectReason("");
|
|
494
510
|
setApproveNote("");
|
|
495
511
|
setPendingAction(action);
|
|
@@ -498,8 +514,16 @@ export function PrincipleDetailPage() {
|
|
|
498
514
|
|| (pendingAction.requirements.some((req) => req.name === 'reason') && !rejectReason.trim())
|
|
499
515
|
|| (pendingAction.requirements.some((req) => req.name === 'newArtifactId') && !newArtifactId.trim()), onClick: () => {
|
|
500
516
|
const action = pendingAction;
|
|
501
|
-
void submitOwnerAction(action
|
|
502
|
-
}, children: t("principles.detail.confirm", { defaultValue: "确认" }) }), _jsx(Button, { variant: "outline", size: "sm", onClick: () => { setPendingAction(null); setRejectReason(""); setNewArtifactId(""); setApproveNote(""); }, children: t("principles.detail.cancel") })] })] })] }))] })
|
|
517
|
+
void submitOwnerAction(action);
|
|
518
|
+
}, children: t("principles.detail.confirm", { defaultValue: "确认" }) }), _jsx(Button, { variant: "outline", size: "sm", onClick: () => { setPendingAction(null); setRejectReason(""); setNewArtifactId(""); setApproveNote(""); }, children: t("principles.detail.cancel") })] })] })] }))] }), _jsxs("details", { className: "mt-5 border-t border-line pt-4", "data-testid": "principle-formation-disclosure", children: [_jsx("summary", { className: "cursor-pointer text-sm text-gov", children: t('principles.detail.formationDisclosure') }), _jsxs("div", { className: "mt-4", children: [_jsx(OwnerNarrativeBlock, { testId: "owner-decision-incident", label: t("principles.detail.ownerDecision.incident", { defaultValue: "发生了什么" }), field: ownerDecision.incidentSummary }), _jsx(OwnerNarrativeBlock, { testId: "owner-decision-rationale", label: t("principles.detail.ownerDecision.rationale", { defaultValue: "为什么这么判断" }), field: ownerDecision.rationale })] })] }), _jsx(OwnerNarrativeBlock, { testId: "owner-decision-applicability", label: t("principles.detail.ownerDecision.applicability", { defaultValue: "什么时候适用" }), field: ownerDecision.applicability }), _jsx(OwnerNarrativeBlock, { testId: "owner-decision-expected", label: t("principles.detail.ownerDecision.expected", { defaultValue: "批准后会怎样(拟议行为,未部署)" }), field: ownerDecision.expectedBehavior }), ownerDecision.currentEnforcement.status === 'known' && (_jsxs("div", { className: "mt-4", "data-testid": "owner-decision-enforcement", children: [_jsx("p", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4", children: t("principles.detail.ownerDecision.enforcementLabel", { defaultValue: "当前实际执行" }) }), _jsx("p", { className: "mt-1 text-ink-2 text-[14px] leading-relaxed", children: ownerDecision.currentEnforcement.value?.state === 'active'
|
|
519
|
+
? t("principles.detail.ownerDecision.enforcement.active", { defaultValue: "当前有正在执行的生效方式(见下方明细)。" })
|
|
520
|
+
: ownerDecision.currentEnforcement.value?.state === 'deactivated'
|
|
521
|
+
? t("principles.detail.ownerDecision.enforcement.deactivated", { defaultValue: "此前的执行方式已停用;当前没有正在执行的生效方式。" })
|
|
522
|
+
: ownerDecision.currentEnforcement.value?.state === 'partially_active'
|
|
523
|
+
? t("principles.detail.ownerDecision.enforcement.partial", { defaultValue: "部分执行方式仍在运行(见下方明细)。" })
|
|
524
|
+
: t("principles.detail.ownerDecision.enforcement.none", { defaultValue: "这条原则当前没有生效中的执行方式。" }) }), ownerDecision.currentEnforcement.value?.items.map((item) => (_jsxs("p", { className: "mt-1 text-ink-3 text-[12px]", children: [item.mode.status === 'known' ? item.mode.value : item.channel, " · ", item.active
|
|
525
|
+
? t("principles.detail.ownerDecision.enforcement.since", { defaultValue: "生效中(自 {{since}})", since: item.since.status === 'known' ? item.since.value.slice(0, 10) : '?' })
|
|
526
|
+
: t("principles.detail.ownerDecision.enforcement.stopped", { defaultValue: "已停用" })] }, item.activationRef.id)))] })), ownerDecision.currentEnforcement.status === 'unknown' && (_jsx("p", { className: "mt-4 text-amber text-[13px]", "data-testid": "owner-decision-enforcement-unknown", children: ownerDecision.currentEnforcement.reason?.ownerText })), ownerDecision.evidenceSummary.status === 'known' && (_jsxs("div", { className: "mt-4", "data-testid": "owner-decision-evidence", children: [_jsx("p", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4", children: t("principles.detail.ownerDecision.evidence", { defaultValue: "行为证据" }) }), _jsx("p", { className: "mt-1 text-ink-2 text-[13px] leading-relaxed", children: ownerDecision.evidenceSummary.value?.ownerExplanation })] })), _jsxs("div", { className: "mt-4", "data-testid": "owner-decision-uncertainty", children: [_jsx("p", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4", children: t("principles.detail.ownerDecision.uncertainty", { defaultValue: "风险与不确定性" }) }), ownerDecision.risk.status === 'known' && (ownerDecision.risk.value?.items.length ?? 0) > 0 && (_jsx("ul", { className: "mt-1 space-y-1 text-[13px] text-ink-2", children: ownerDecision.risk.value?.items.map((item, index) => (_jsx("li", { children: item.text }, `risk-${index}`))) })), ownerDecision.risk.status === 'known' && (ownerDecision.risk.value?.items.length ?? 0) === 0 && (_jsx("p", { className: "mt-1 text-ink-3 text-[13px]", children: t("principles.detail.ownerDecision.riskNotAssessed", { defaultValue: "尚未做过风险评估;这不代表没有风险。" }) })), ownerDecision.risk.status === 'unknown' && (_jsx("p", { className: "mt-1 text-ink-3 text-[13px]", children: ownerDecision.risk.reason?.ownerText })), ownerDecision.uncertainty.status === 'known' && ownerDecision.uncertainty.value.length > 0 && (_jsx("ul", { className: "mt-2 space-y-1 text-[12px] text-ink-3", children: ownerDecision.uncertainty.value.map((item, index) => (_jsx("li", { children: item.text }, `unc-${index}`))) }))] }), _jsxs("div", { className: "mt-4", "data-testid": "owner-decision-rollback", children: [_jsx("p", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4", children: t("principles.detail.ownerDecision.rollback", { defaultValue: "如何停止影响" }) }), ownerDecision.rollback.status === 'known' && (_jsxs(_Fragment, { children: [_jsx("p", { className: "mt-1 text-ink-2 text-[13px] leading-relaxed", children: ownerDecision.rollback.value?.ownerText }), ownerDecision.rollback.value?.stopFuture.status === 'known' && (_jsx("ul", { className: "mt-1 space-y-1 text-[12px] text-ink-3", children: ownerDecision.rollback.value.stopFuture.value.map((item, index) => (_jsx("li", { children: item.text }, `rb-${index}`))) })), ownerDecision.rollback.value?.stopFuture.status === 'not_applicable' && (_jsx("p", { className: "mt-1 text-ink-3 text-[12px]", children: ownerDecision.rollback.value.stopFuture.reason.ownerText }))] })), ownerDecision.rollback.status === 'unknown' && (_jsx("p", { className: "mt-1 text-amber text-[13px]", children: ownerDecision.rollback.reason?.ownerText }))] })] })] })), ownerDecision === null && ownerDecisionUnavailable !== null && (_jsxs("section", { className: "mb-8", "data-testid": "owner-decision-unavailable", children: [_jsx(SectionTitle, { children: t("principles.detail.ownerDecision.title", { defaultValue: "这条原则的决策视图" }) }), _jsxs("div", { className: "rounded-[var(--radius-md)] border border-amber/30 bg-amber/5 p-4", role: "status", children: [_jsx("p", { className: "text-ink-2 text-[13px]", children: t("principles.detail.ownerDecision.unavailable", { defaultValue: "决策视图暂时不可用,无法据此做决定。" }) }), _jsx("p", { className: "mt-1 text-ink-4 text-[12px] font-mono", children: ownerDecisionUnavailable.reason })] })] })), (governance !== null || governanceUnavailable !== null) && (_jsxs("section", { className: "mb-8", "aria-labelledby": "governance-summary-title", children: [_jsx(SectionTitle, { children: t('principles.detail.governance.title') }), governance !== null ? (_jsxs("div", { "data-testid": "governance-summary", className: `rounded-[var(--radius-md)] border p-4 ${governance.dataQuality.degraded ? 'border-amber/30 bg-amber/5' : 'border-gov/25 bg-gov/5'}`, children: [_jsxs("div", { className: "flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between", children: [_jsxs("div", { children: [_jsx("h2", { id: "governance-summary-title", className: "text-[18px] font-semibold text-ink", children: t(`principles.detail.${governance.summary.headlineCode}`, { defaultValue: governance.summary.headlineCode }) }), _jsx("p", { className: "mt-1 text-[13px] leading-relaxed text-ink-2", children: t(`principles.detail.${governance.summary.reasonCode}`, { defaultValue: governance.summary.reasonCode }) })] }), _jsx("span", { className: "w-fit rounded-full border border-line px-2 py-1 font-mono text-[11px] text-ink-3", children: t(`principles.detail.governance.confidence.${governance.dataQuality.degraded ? 'degraded' : 'strong'}`) })] }), _jsxs("dl", { className: "mt-4 grid grid-cols-1 gap-3 sm:grid-cols-3", children: [_jsxs("div", { children: [_jsx("dt", { className: "font-mono text-[11px] text-ink-4", children: t('principles.detail.governance.principleState') }), _jsx("dd", { className: "text-[13px] text-ink-2", children: t(`principles.detail.governance.state.${governance.principleState.value}`) })] }), _jsxs("div", { children: [_jsx("dt", { className: "font-mono text-[11px] text-ink-4", children: t('principles.detail.governance.process') }), _jsx("dd", { className: "text-[13px] text-ink-2", children: governance.process.stage === undefined ? t('principles.detail.governance.none') : t(`principles.detail.governance.stage.${governance.process.stage}`) })] }), _jsxs("div", { children: [_jsx("dt", { className: "font-mono text-[11px] text-ink-4", children: t('principles.detail.governance.automation') }), _jsx("dd", { className: "text-[13px] text-ink-2", children: t(`principles.detail.governance.automationState.${governance.automation.state}`) })] })] }), _jsxs("div", { "data-testid": "governance-next-action", className: "mt-4 border-l-2 border-l-gov pl-3", children: [_jsx("p", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4", children: t('principles.detail.governance.nextAction') }), _jsx("p", { className: "text-[13px] text-ink-2", children: t(`principles.detail.${governance.summary.nextActionCode}`, { defaultValue: governance.summary.nextActionCode }) })] }), governance.attention.items.length > 0 && (_jsxs("div", { className: "mt-4", children: [_jsx("p", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-4", children: t('principles.detail.governance.blockers') }), _jsx("ul", { className: "mt-1 space-y-1 text-[13px] text-ink-2", children: groupByReasonCode(governance.attention.items).map(group => _jsx("li", { children: formatGroupedReasonLine(t, 'principles.detail.governance.attention', group) }, group.reasonCode)) })] })), governance.dataQuality.degraded && (_jsxs("div", { "data-testid": "governance-data-quality", className: "mt-4 rounded-[var(--radius-sm)] border border-amber/20 p-3", children: [_jsx("p", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-amber", children: t('principles.detail.governance.uncertainty') }), _jsx("ul", { className: "mt-1 space-y-1 text-[12px] text-ink-3", children: groupByReasonCode(governance.dataQuality.issues).map(group => _jsx("li", { children: formatGroupedReasonLine(t, 'principles.detail.governance.issue', group) }, group.reasonCode)) })] }))] })) : (_jsxs("div", { "data-testid": "governance-data-quality", className: "rounded-[var(--radius-md)] border border-amber/30 bg-amber/5 p-4", role: "status", children: [_jsx("h2", { id: "governance-summary-title", className: "text-[16px] font-semibold text-ink", children: t('principles.detail.governance.unavailable') }), _jsx("p", { className: "mt-1 text-[13px] text-ink-2", children: t('principles.detail.governance.unavailableReason') }), _jsx("p", { className: "mt-2 text-[12px] text-ink-3", children: t('principles.detail.governance.unavailableNextAction') })] }))] })), receipts !== null && (_jsxs("section", { className: "mb-8", "aria-labelledby": "receipt-history-title", children: [_jsx(SectionTitle, { children: t('principles.detail.receipts.title') }), receipts.status === 'ok' ? (_jsxs("div", { "data-testid": "receipt-history", className: "rounded-[var(--radius-md)] border border-line p-4", children: [_jsxs("div", { className: "flex flex-wrap items-baseline justify-between gap-2", children: [_jsx("h2", { id: "receipt-history-title", className: "text-[15px] font-semibold text-ink", children: receiptPresentation?.headlineKey === 'principles.detail.receipts.headline'
|
|
503
527
|
? t(receiptPresentation.headlineKey, {
|
|
504
528
|
defaultValue: '',
|
|
505
529
|
effectCount: receipts.effectCount,
|
|
@@ -538,30 +562,30 @@ export function PrincipleDetailPage() {
|
|
|
538
562
|
activationId: record.activationReference.activationId,
|
|
539
563
|
activatedAt: record.activationReference.activatedAt ? ` · ${record.activationReference.activatedAt}` : '',
|
|
540
564
|
snapshotDigest: ` · ${t('principles.detail.evidenceAudit.snapshotDigestLabel')}: ${record.activationReference.sourceSnapshotDigest}`,
|
|
541
|
-
})] })), record.nativeLineage.length > 0 && _jsxs(_Fragment, { children: [" \u00B7 ", t('principles.detail.evidenceAudit.nativeLineage', { lineage: record.nativeLineage.join(' · ') })] })] }, record.evidenceId)))) })] }), _jsx("p", { className: "mt-3 text-[11px] leading-relaxed text-ink-4", children: t('principles.detail.evidenceAudit.note') })] })) : (_jsxs("div", { "data-testid": "evidence-audit-degraded", className: "rounded-[var(--radius-md)] border border-amber/30 bg-amber/5 p-4", role: "status", children: [_jsx("h3", { className: "text-[13px] font-semibold text-ink", children: t('principles.detail.evidenceAudit.unavailable') }), _jsx("p", { className: "mt-1 text-[13px] text-ink-2", children: evidenceAudit.reason ?? '—' }), evidenceAudit.nextAction && _jsx("p", { className: "mt-1 text-[12px] text-ink-3", children: evidenceAudit.nextAction })] }))] })), _jsxs("section", { className: "mb-8", children: [_jsx(SectionTitle, { children: t("principles.detail.whyExists") }), _jsxs("div", { className: "space-y-3 mb-6", children: [_jsxs("div", { children: [_jsx("span", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3", children: t("principles.detail.applicable", { defaultValue: "适用场景(原始触发配置)" }) }), _jsx("p", { className: "text-ink-2 text-[14px] leading-relaxed font-mono break-all", children: principle.triggerPattern || t("principles.detail.notSpecified", { defaultValue: "未指定" }) })] }), _jsxs("div", { children: [_jsx("span", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3", children: t("principles.detail.expectedBehavior", { defaultValue: "预期行为(原始建议)" }) }), _jsx("p", { className: "text-ink-2 text-[14px] leading-relaxed", children: principle.action || t("principles.detail.notSpecified", { defaultValue: "未指定" }) })] })] }), principle.derivedFromPainIds.length > 0 && (_jsxs("div", { children: [_jsx("span", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3", children: t("principles.detail.evidence") }), _jsxs("div", { className: "mt-2 flex items-center gap-3 flex-wrap", children: [_jsx("p", { className: "text-ink-2 text-[14px] leading-relaxed", children: t("principles.detail.evidenceCount", {
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
+
})] })), record.nativeLineage.length > 0 && _jsxs(_Fragment, { children: [" \u00B7 ", t('principles.detail.evidenceAudit.nativeLineage', { lineage: record.nativeLineage.join(' · ') })] })] }, record.evidenceId)))) })] }), _jsx("p", { className: "mt-3 text-[11px] leading-relaxed text-ink-4", children: t('principles.detail.evidenceAudit.note') })] })) : (_jsxs("div", { "data-testid": "evidence-audit-degraded", className: "rounded-[var(--radius-md)] border border-amber/30 bg-amber/5 p-4", role: "status", children: [_jsx("h3", { className: "text-[13px] font-semibold text-ink", children: t('principles.detail.evidenceAudit.unavailable') }), _jsx("p", { className: "mt-1 text-[13px] text-ink-2", children: evidenceAudit.reason ?? '—' }), evidenceAudit.nextAction && _jsx("p", { className: "mt-1 text-[12px] text-ink-3", children: evidenceAudit.nextAction })] }))] })), _jsxs("details", { className: "mb-8 border-t border-line pt-4", "data-testid": "principle-extra-context", children: [_jsx("summary", { className: "cursor-pointer text-sm text-gov", children: t('principles.detail.extraContext') }), _jsxs("div", { className: "mt-5", children: [_jsxs("section", { className: "mb-8", children: [_jsx(SectionTitle, { children: t("principles.detail.whyExists") }), _jsxs("div", { className: "space-y-3 mb-6", children: [_jsxs("div", { children: [_jsx("span", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3", children: t("principles.detail.applicable", { defaultValue: "适用场景(原始触发配置)" }) }), _jsx("p", { className: "text-ink-2 text-[14px] leading-relaxed font-mono break-all", children: principle.triggerPattern || t("principles.detail.notSpecified", { defaultValue: "未指定" }) })] }), _jsxs("div", { children: [_jsx("span", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3", children: t("principles.detail.expectedBehavior", { defaultValue: "预期行为(原始建议)" }) }), _jsx("p", { className: "text-ink-2 text-[14px] leading-relaxed", children: principle.action || t("principles.detail.notSpecified", { defaultValue: "未指定" }) })] })] }), principle.derivedFromPainIds.length > 0 && (_jsxs("div", { children: [_jsx("span", { className: "font-mono text-[11px] uppercase tracking-[0.08em] text-ink-3", children: t("principles.detail.evidence") }), _jsxs("div", { className: "mt-2 flex items-center gap-3 flex-wrap", children: [_jsx("p", { className: "text-ink-2 text-[14px] leading-relaxed", children: t("principles.detail.evidenceCount", {
|
|
566
|
+
defaultValue: "已关联 {{count}} 条证据记录",
|
|
567
|
+
count: principle.derivedFromPainIds.length,
|
|
568
|
+
}) }), _jsx(Button, { variant: "ghost", size: "sm", onClick: async () => {
|
|
569
|
+
try {
|
|
570
|
+
const summary = principle.derivedFromPainIds
|
|
571
|
+
.map((pid, i) => `pain_${i + 1}_id: ${pid}`)
|
|
572
|
+
.join("\n");
|
|
573
|
+
await navigator.clipboard.writeText(summary);
|
|
574
|
+
setEvidenceCopied(true);
|
|
575
|
+
setTimeout(() => setEvidenceCopied(false), 2000);
|
|
576
|
+
}
|
|
577
|
+
catch (error) {
|
|
578
|
+
// clipboard unavailable — expand as fallback
|
|
579
|
+
console.warn("Evidence ID copy failed; expanding technical details.", error);
|
|
580
|
+
if (!evidenceExpanded)
|
|
581
|
+
setEvidenceExpanded(true);
|
|
582
|
+
}
|
|
583
|
+
}, className: "font-mono text-[11px] h-7", children: evidenceCopied
|
|
584
|
+
? t("principles.detail.evidenceCopied", { defaultValue: "已复制" })
|
|
585
|
+
: t("principles.detail.copyEvidenceId", { defaultValue: "复制证据 ID" }) }), _jsx("button", { type: "button", onClick: () => setEvidenceExpanded((v) => !v), className: "font-mono text-[11px] text-ink-4 hover:text-ink-3 transition-colors underline-offset-2 hover:underline", children: t("principles.detail.expandTechDetails", { defaultValue: "展开技术细节" }) }), _jsx(Link, { to: "/pain", className: "font-mono text-[11px] text-gov hover:underline transition-colors", children: t("principles.detail.viewEvidenceChain", { defaultValue: "在证据链页查看 →" }) })] }), evidenceExpanded && (_jsx("div", { className: "mt-3 p-3 bg-paper-2 border border-line rounded-[var(--radius-sm)]", children: _jsx("div", { className: "grid grid-cols-[auto 1fr] gap-x-4 gap-y-2 text-[13px]", children: principle.derivedFromPainIds.map((painId, idx) => (_jsxs("div", { className: "contents", children: [_jsx("span", { className: "font-mono text-ink-4", children: t("principles.detail.evidenceIdLabel", {
|
|
586
|
+
defaultValue: "证据 {{n}} ID",
|
|
587
|
+
n: idx + 1,
|
|
588
|
+
}) }), _jsx("span", { className: "font-mono text-ink-2 break-all", children: painId })] }, `${painId}-${idx}`))) }) }))] }))] }), _jsxs("section", { className: "mb-8 border-t border-line pt-6", children: [_jsx(SectionTitle, { children: t("principles.detail.ownerReflection") }), _jsxs("div", { className: "space-y-3", children: [_jsx("div", { className: "border-l-2 border-l-gov pl-3 text-ink-2 text-[13px] leading-relaxed", children: t("principles.detail.reflectionQ1") }), _jsx("div", { className: "border-l-2 border-l-gov pl-3 text-ink-2 text-[13px] leading-relaxed", children: t("principles.detail.reflectionQ2") }), _jsx("div", { className: "border-l-2 border-l-gov pl-3 text-ink-2 text-[13px] leading-relaxed", children: t("principles.detail.reflectionQ3") })] })] })] })] }), _jsxs("details", { className: "mb-8 border border-line rounded-[var(--radius-md)]", children: [_jsx("summary", { className: "px-4 py-3 cursor-pointer font-mono text-[11px] uppercase tracking-[0.1em] text-ink-3 hover:text-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-gov", children: t("principles.detail.trajectory") }), _jsxs("div", { className: "px-4 pb-4 border-t border-line pt-4", children: [governance !== null && (_jsxs("section", { className: "mb-6", "data-testid": "governance-timeline", children: [_jsx(SectionTitle, { children: t('principles.detail.governance.timeline') }), governance.timeline.length === 0 ? _jsx("p", { className: "text-[13px] text-ink-4", children: t('principles.detail.governance.timelineEmpty') }) : (_jsx("ol", { className: "space-y-3 border-l border-line pl-4", children: governance.timeline.map((event, index) => {
|
|
565
589
|
const derived = event.code === 'revision_requested' || event.code === 'revision_reopened';
|
|
566
590
|
return _jsxs("li", { children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: "text-[13px] text-ink-2", children: t(`principles.detail.governance.timelineCode.${event.code}`, { defaultValue: event.code }) }), _jsx("span", { className: "rounded border border-line px-1.5 py-0.5 font-mono text-[10px] text-ink-4", children: t(`principles.detail.governance.${derived ? 'derived' : 'fact'}`) })] }), _jsx("p", { className: "mt-0.5 font-mono text-[11px] text-ink-4", children: event.occurredAt ?? event.recordedAt })] }, `${event.sourceRef.type}-${event.sourceRef.id}-${event.code}-${index}`);
|
|
567
591
|
}) }))] })), trajectory?.degraded && (_jsxs("div", { className: "mb-4 p-3 bg-amber/5 border border-amber/20 rounded-[var(--radius-sm)]", children: [_jsx("p", { className: "text-amber text-[12px] font-mono", children: trajectory.degraded.reason }), _jsx("p", { className: "text-ink-4 text-[11px] mt-1", children: trajectory.degraded.nextAction })] })), _jsx("div", { className: "space-y-4", children: (trajectory?.stages ?? []).length > 0 ? (trajectory.stages.map((stage) => (_jsxs("div", { className: "flex gap-4", children: [_jsx("span", { className: "font-mono text-[12px] text-ink-3 min-w-[80px]", children: t(STAGE_LABEL_KEYS[stage.key] ?? `principles.detail.stage${stage.key}`, { defaultValue: stage.key }) }), _jsx("div", { className: "flex-1", children: stage.status === 'available' ? (_jsxs(_Fragment, { children: [_jsx("p", { className: "text-ink-2 text-[13px]", children: stage.summary }), stage.detail && (_jsx("p", { className: "text-ink-3 text-[12px] mt-0.5", children: stage.detail })), stage.timestamp && (_jsx("p", { className: "text-ink-4 text-[11px] mt-0.5 font-mono", children: stage.timestamp }))] })) : (_jsxs("p", { className: "text-ink-4 text-[13px]", children: [stage.unavailableReason ?? '—', stage.nextAction && (_jsx("span", { className: "block text-ink-4 text-[11px] mt-0.5", children: stage.nextAction }))] })) })] }, stage.key)))) : (
|