create-principles-disciple 1.145.35 → 1.145.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/_release/product-identity.json +2 -2
  2. package/console/dist/server/index.js +4 -0
  3. package/console/dist/server/models/ApprovalsConsoleModel.d.ts +22 -2
  4. package/console/dist/server/models/ApprovalsConsoleModel.js +56 -16
  5. package/console/dist/server/models/ApprovalsGroupedConsoleModel.d.ts +8 -0
  6. package/console/dist/server/models/ApprovalsGroupedConsoleModel.js +3 -2
  7. package/console/dist/server/models/approval-review-material.d.ts +15 -0
  8. package/console/dist/server/models/approval-review-material.js +32 -0
  9. package/console/dist/server/routes/approvals.js +32 -7
  10. package/console/dist/server/utils/response.js +8 -0
  11. package/console/dist/ui/api.d.ts +8 -2
  12. package/console/dist/ui/api.js +8 -4
  13. package/console/dist/ui/i18n/en.json +98 -7
  14. package/console/dist/ui/i18n/zh-CN.json +98 -7
  15. package/console/dist/ui/pages/focus/ApprovalSubjectReading.d.ts +24 -0
  16. package/console/dist/ui/pages/focus/ApprovalSubjectReading.js +133 -0
  17. package/console/dist/ui/pages/focus/FocusPage.d.ts +5 -26
  18. package/console/dist/ui/pages/focus/FocusPage.js +166 -459
  19. package/console/dist/ui/pages/focus/OwnerWorkbench.d.ts +19 -0
  20. package/console/dist/ui/pages/focus/OwnerWorkbench.js +75 -0
  21. package/console/dist/ui/pages/focus/RuleCandidateBindingPanel.d.ts +4 -1
  22. package/console/dist/ui/pages/focus/RuleCandidateBindingPanel.js +7 -3
  23. package/console/dist/ui/pages/focus/focus-validation.js +31 -0
  24. package/console/dist/ui/pages/principles/PrincipleDetailPage.js +110 -86
  25. package/console/dist/ui/pages/principles/PrinciplesPage.js +39 -30
  26. package/console/dist/ui/utils/approval-review.d.ts +30 -0
  27. package/console/dist/ui/utils/approval-review.js +50 -0
  28. package/console/dist/ui/utils/reading-layout.d.ts +1 -0
  29. package/console/dist/ui/utils/reading-layout.js +1 -1
  30. package/console/dist/ui/utils/validators.d.ts +4 -0
  31. package/console/dist/ui/utils/validators.js +10 -2
  32. package/console/dist/web/assets/app.css +119 -11
  33. package/console/dist/web/assets/app.js +40 -30
  34. package/core/dist/runtime-v2/activation/activation-types.d.ts +12 -2
  35. package/core/dist/runtime-v2/activation/activation-types.d.ts.map +1 -1
  36. package/core/dist/runtime-v2/activation/activation-types.js.map +1 -1
  37. package/core/dist/runtime-v2/activation/approval-queue.d.ts +3 -2
  38. package/core/dist/runtime-v2/activation/approval-queue.d.ts.map +1 -1
  39. package/core/dist/runtime-v2/activation/approval-queue.js +6 -4
  40. package/core/dist/runtime-v2/activation/approval-queue.js.map +1 -1
  41. package/core/dist/runtime-v2/activation/approval-review-guard.d.ts +26 -0
  42. package/core/dist/runtime-v2/activation/approval-review-guard.d.ts.map +1 -0
  43. package/core/dist/runtime-v2/activation/approval-review-guard.js +25 -0
  44. package/core/dist/runtime-v2/activation/approval-review-guard.js.map +1 -0
  45. package/core/dist/runtime-v2/activation/memory-approval-store.d.ts +6 -2
  46. package/core/dist/runtime-v2/activation/memory-approval-store.d.ts.map +1 -1
  47. package/core/dist/runtime-v2/activation/memory-approval-store.js +26 -2
  48. package/core/dist/runtime-v2/activation/memory-approval-store.js.map +1 -1
  49. package/core/dist/runtime-v2/activation/sqlite-approval-store.d.ts +6 -2
  50. package/core/dist/runtime-v2/activation/sqlite-approval-store.d.ts.map +1 -1
  51. package/core/dist/runtime-v2/activation/sqlite-approval-store.js +59 -2
  52. package/core/dist/runtime-v2/activation/sqlite-approval-store.js.map +1 -1
  53. package/core/dist/runtime-v2/diagnostician-association-shadow.d.ts +24 -0
  54. package/core/dist/runtime-v2/diagnostician-association-shadow.d.ts.map +1 -0
  55. package/core/dist/runtime-v2/diagnostician-association-shadow.js +84 -0
  56. package/core/dist/runtime-v2/diagnostician-association-shadow.js.map +1 -0
  57. package/core/dist/runtime-v2/diagnostician-association-validation.d.ts +18 -0
  58. package/core/dist/runtime-v2/diagnostician-association-validation.d.ts.map +1 -0
  59. package/core/dist/runtime-v2/diagnostician-association-validation.js +47 -0
  60. package/core/dist/runtime-v2/diagnostician-association-validation.js.map +1 -0
  61. package/core/dist/runtime-v2/diagnostician-output.d.ts +123 -0
  62. package/core/dist/runtime-v2/diagnostician-output.d.ts.map +1 -1
  63. package/core/dist/runtime-v2/diagnostician-output.js +41 -0
  64. package/core/dist/runtime-v2/diagnostician-output.js.map +1 -1
  65. package/core/dist/runtime-v2/index.d.ts +2 -0
  66. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  67. package/core/dist/runtime-v2/index.js +1 -0
  68. package/core/dist/runtime-v2/index.js.map +1 -1
  69. package/core/dist/runtime-v2/internalization/diag-router-runner.d.ts +12 -1
  70. package/core/dist/runtime-v2/internalization/diag-router-runner.d.ts.map +1 -1
  71. package/core/dist/runtime-v2/internalization/diag-router-runner.js +50 -2
  72. package/core/dist/runtime-v2/internalization/diag-router-runner.js.map +1 -1
  73. package/core/dist/runtime-v2/internalization/router-context-evidence.d.ts +45 -0
  74. package/core/dist/runtime-v2/internalization/router-context-evidence.d.ts.map +1 -0
  75. package/core/dist/runtime-v2/internalization/router-context-evidence.js +64 -0
  76. package/core/dist/runtime-v2/internalization/router-context-evidence.js.map +1 -0
  77. package/core/package.json +1 -1
  78. package/package.json +1 -1
  79. package/pd-cli/package.json +1 -1
  80. package/plugin/dist/bundle.js +395 -395
  81. package/release-manager/package.json +1 -1
@@ -0,0 +1,19 @@
1
+ import { type ReactNode } from 'react';
2
+ export type OwnerWorkItemKind = 'prompt' | 'rule' | 'promotion' | 'revision' | 'validation' | 'binding' | 'archive' | 'approval' | 'unknown';
3
+ export interface OwnerWorkItem {
4
+ key: string;
5
+ title: string;
6
+ kind: OwnerWorkItemKind;
7
+ summary: string;
8
+ reference?: string;
9
+ content: ReactNode;
10
+ }
11
+ /** Presentation only: list grouping never changes an action's identity or authority. */
12
+ export declare function OwnerWorkbench({ items, completed, uncertain }: {
13
+ items: OwnerWorkItem[];
14
+ completed: {
15
+ key: string;
16
+ message: string;
17
+ } | null;
18
+ uncertain: boolean;
19
+ }): import("react").JSX.Element;
@@ -0,0 +1,75 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef } from 'react';
3
+ import { useSearchParams } from 'react-router-dom';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { Archive, CircleHelp, ClipboardCheck, Code2, FilePenLine, FlaskConical, Link2, MessageSquareText, ShieldCheck } from 'lucide-react';
6
+ // Type describes the decision, not an inferred urgency or permission.
7
+ const KIND_PRESENTATION = {
8
+ prompt: { icon: MessageSquareText, label: 'promptReview', style: 'border-gov/30 bg-gov/10 text-gov' },
9
+ rule: { icon: Code2, label: 'ruleReview', style: 'border-ink-3/30 bg-ink-3/10 text-ink-2' },
10
+ promotion: { icon: ShieldCheck, label: 'rulePromotion', style: 'border-amber/30 bg-amber/10 text-ink-2' },
11
+ revision: { icon: FilePenLine, label: 'revisionDecision', style: 'border-amber/30 text-ink-2' },
12
+ validation: { icon: FlaskConical, label: 'validationReview', style: 'border-gov/30 text-gov' },
13
+ binding: { icon: Link2, label: 'bindingReview', style: 'border-gov/30 text-gov' },
14
+ archive: { icon: Archive, label: 'archiveReview', style: 'border-line bg-surface text-ink-3' },
15
+ approval: { icon: ClipboardCheck, label: 'approvalMaterialReview', style: 'border-line text-ink-2' },
16
+ unknown: { icon: CircleHelp, label: 'unknownReview', style: 'border-line text-ink-3' },
17
+ };
18
+ function WorkItemType({ kind }) {
19
+ const { t } = useTranslation();
20
+ const { icon: Icon, label, style } = KIND_PRESENTATION[kind];
21
+ return _jsxs("span", { "data-testid": "owner-work-type", "data-kind": kind, className: `inline-flex max-w-full items-center gap-1.5 rounded-[4px] border px-2 py-1 text-[11px] font-medium leading-relaxed ${style}`, children: [_jsx(Icon, { "aria-hidden": "true", className: "h-3.5 w-3.5 shrink-0", strokeWidth: 1.75 }), t(`pages.focus.workbench.${label}`)] });
22
+ }
23
+ /** Presentation only: list grouping never changes an action's identity or authority. */
24
+ export function OwnerWorkbench({ items, completed, uncertain }) {
25
+ const { t } = useTranslation();
26
+ const [params, setParams] = useSearchParams();
27
+ const selectedKey = params.get('review') ?? '';
28
+ const selected = items.find(item => item.key === selectedKey);
29
+ const heading = useRef(null);
30
+ const list = useRef(null);
31
+ const lastSelected = useRef('');
32
+ const listScroll = useRef(0);
33
+ // A deep link can arrive before the items do. `focusKey` only becomes the
34
+ // selection once the item exists, so the effect re-runs when it appears
35
+ // without stealing focus on every data refresh.
36
+ const focusKey = selected ? selectedKey : '';
37
+ useEffect(() => {
38
+ if (focusKey) {
39
+ heading.current?.focus({ preventScroll: true });
40
+ const reading = heading.current?.closest('[data-testid="owner-work-reading"]');
41
+ if (reading && (reading.getBoundingClientRect().top < 0 || reading.getBoundingClientRect().top > window.innerHeight - 100))
42
+ reading.scrollIntoView({ block: 'start' });
43
+ const current = list.current?.querySelector('[aria-current="true"]');
44
+ if (current && list.current && list.current.scrollHeight > list.current.clientHeight) {
45
+ list.current.scrollTop = Math.max(0, current.offsetTop - list.current.offsetTop - 24);
46
+ }
47
+ }
48
+ else if (selectedKey) {
49
+ heading.current?.focus({ preventScroll: true });
50
+ }
51
+ else if (lastSelected.current && list.current) {
52
+ list.current.scrollTop = listScroll.current;
53
+ const previous = Array.from(list.current.querySelectorAll('[data-item-key]')).find(button => button.dataset.itemKey === lastSelected.current);
54
+ previous?.focus({ preventScroll: true });
55
+ if (previous && (previous.getBoundingClientRect().top < 0 || previous.getBoundingClientRect().bottom > window.innerHeight))
56
+ previous.scrollIntoView({ block: 'nearest' });
57
+ }
58
+ }, [focusKey, selectedKey]);
59
+ function choose(key) {
60
+ if (!key && selectedKey) {
61
+ lastSelected.current = selectedKey;
62
+ listScroll.current = list.current?.scrollTop ?? 0;
63
+ }
64
+ const next = new URLSearchParams(params);
65
+ if (key)
66
+ next.set('review', key);
67
+ else
68
+ next.delete('review');
69
+ setParams(next, { preventScrollReset: true });
70
+ }
71
+ const completedHere = completed?.key === selectedKey ? completed : null;
72
+ const selectedIndex = items.findIndex(item => item.key === selectedKey);
73
+ const nextItem = selectedIndex >= 0 ? items[selectedIndex + 1] : undefined;
74
+ return _jsxs("section", { className: "owner-workbench", "data-testid": "owner-workbench", children: [_jsxs("nav", { ref: list, tabIndex: -1, className: `owner-work-list ${selected ? 'owner-work-list-selected' : ''}`, "aria-label": t('pages.focus.workbench.list'), children: [_jsxs("div", { className: "mb-3 flex items-baseline justify-between gap-3", children: [_jsx("h2", { className: "text-[15px] font-medium text-ink", children: t('pages.focus.workbench.list') }), _jsx("span", { className: "text-[12px] text-ink-4", children: t('pages.focus.workbench.count', { count: items.length }) })] }), items.length === 0 && _jsx("p", { className: "text-sm leading-relaxed text-ink-3", children: t(uncertain ? 'pages.focus.workbench.uncertain' : 'pages.focus.workbench.empty') }), _jsx("ol", { className: "space-y-2", children: items.map(item => _jsx("li", { children: _jsxs("button", { type: "button", onClick: () => choose(item.key), "aria-current": selected?.key === item.key ? 'true' : undefined, "data-testid": "owner-work-item", "data-item-key": item.key, className: `w-full rounded-[6px] border px-4 py-4 text-left transition-colors focus-visible:outline-2 focus-visible:outline-gov ${selected?.key === item.key ? 'border-gov bg-gov/5' : 'border-line bg-panel hover:border-gov/50'}`, children: [_jsx("span", { className: "mb-3 block", children: _jsx(WorkItemType, { kind: item.kind }) }), _jsx("span", { className: "block text-[14px] font-medium leading-relaxed text-ink break-words", children: item.title }), _jsx("span", { className: "mt-2 block text-[12.5px] leading-relaxed text-ink-3 line-clamp-2", children: item.summary }), item.reference && items.some(other => other.key !== item.key && other.title === item.title && other.summary === item.summary) && _jsx("span", { className: "mt-2 block break-all font-mono text-[11px] text-ink-4", children: item.reference })] }) }, item.key)) })] }), _jsx("div", { className: "owner-work-reading min-w-0", "data-testid": "owner-work-reading", children: selected ? _jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "mb-5 text-[13px] text-gov underline underline-offset-4", onClick: () => choose(''), children: t('pages.focus.workbench.back') }), _jsx("div", { className: "mb-3", children: _jsx(WorkItemType, { kind: selected.kind }) }), _jsx("h2", { ref: heading, tabIndex: -1, className: "mb-6 text-[22px] font-semibold leading-snug text-ink break-words", children: selected.title }), _jsx("div", { children: selected.content }, selected.key), _jsxs("footer", { className: "mt-7 border-t border-line pt-5", children: [_jsx("p", { className: "mb-3 text-[12.5px] text-ink-3", children: t('pages.focus.workbench.readingEnd') }), nextItem && _jsx("button", { type: "button", className: "text-[13px] text-gov underline underline-offset-4", onClick: () => choose(nextItem.key), children: t('pages.focus.workbench.next') })] })] }) : _jsxs("div", { className: "py-10 text-ink-3", children: [_jsx("h2", { ref: heading, tabIndex: -1, className: "mb-3 text-[22px] font-medium text-ink", children: t(completedHere ? 'pages.focus.workbench.completed' : selectedKey ? 'pages.focus.workbench.notCurrent' : 'pages.focus.workbench.startTitle') }), _jsx("p", { role: completedHere ? 'status' : undefined, className: "max-w-[440px] text-[14px] leading-relaxed", children: completedHere?.message ?? t(selectedKey ? 'pages.focus.workbench.notCurrentBody' : 'pages.focus.workbench.startBody') }), items[0] && _jsx("button", { type: "button", className: "mt-6 rounded-[4px] border border-gov bg-gov px-4 py-2.5 text-sm text-paper", onClick: () => choose(items[0].key), children: t(completedHere ? 'pages.focus.workbench.next' : 'pages.focus.workbench.start') })] }) })] });
75
+ }
@@ -28,4 +28,7 @@ export declare function canOfferSourceCorrection(item: Pick<RuleBindingQueueItem
28
28
  * `unknown` previously fell through to raw English in the zh-CN group header). */
29
29
  export declare const PAIN_SOURCE_KEY: Record<string, string>;
30
30
  /** Inline in Governance Focus; assignment, rule approval and live promotion remain separate. */
31
- export declare function RuleCandidateBindingPanel(): import("react").JSX.Element | null;
31
+ export declare function RuleCandidateBindingPanel({ candidateId, onChanged }?: {
32
+ candidateId?: string;
33
+ onChanged?: () => void;
34
+ }): import("react").JSX.Element | null;
@@ -139,7 +139,7 @@ function stageKindLabel(t, stageKind) {
139
139
  return key ? t(key) : stageKind;
140
140
  }
141
141
  /** Inline in Governance Focus; assignment, rule approval and live promotion remain separate. */
142
- export function RuleCandidateBindingPanel() {
142
+ export function RuleCandidateBindingPanel({ candidateId, onChanged } = {}) {
143
143
  const { t } = useTranslation();
144
144
  const [queue, setQueue] = useState(null);
145
145
  const [review, setReview] = useState(null);
@@ -176,11 +176,15 @@ export function RuleCandidateBindingPanel() {
176
176
  setError([result.error, result.nextAction].filter(Boolean).join(' ') || t('pages.focus.ruleBinding.unavailable'));
177
177
  setBusy(false);
178
178
  }
179
+ useEffect(() => { if (candidateId)
180
+ void load(candidateId); }, [candidateId]);
179
181
  function closeReview() { setOpenCandidateId(''); setReview(null); setSearch(''); setReason(''); }
180
182
  async function refreshQueue() {
181
183
  const result = await fetchRuleBindingQueue();
182
- if (result.success)
184
+ if (result.success) {
183
185
  setQueue(result.data);
186
+ onChanged?.();
187
+ }
184
188
  }
185
189
  async function decide(action) {
186
190
  if (!review)
@@ -262,7 +266,7 @@ export function RuleCandidateBindingPanel() {
262
266
  }
263
267
  if ((queue === null || queue.items.length === 0) && !error)
264
268
  return null;
265
- const groups = groupRuleBindingQueue(queue?.items ?? []);
269
+ const groups = groupRuleBindingQueue((queue?.items ?? []).filter(item => candidateId === undefined || item.candidateId === candidateId));
266
270
  const visibleGroups = showAllGroups ? groups : groups.slice(0, COLLAPSED_GROUP_LIMIT);
267
271
  const bound = review?.currentBinding?.action === 'bind';
268
272
  return _jsxs("section", { className: "mb-7 rounded border border-line p-4", "data-testid": "rule-binding-panel", "aria-label": t('pages.focus.ruleBinding.title'), children: [_jsx("h2", { className: "text-sm font-medium", children: t('pages.focus.ruleBinding.title') }), _jsx("p", { className: "mt-2 text-sm text-ink-3", children: t('pages.focus.ruleBinding.separate') }), queue && _jsx("p", { className: "mt-2 text-[13px] text-ink-3", role: "status", "data-testid": "rule-binding-counts", children: t('pages.focus.ruleBinding.countsLine', { candidates: queue.summary.candidates, painGroups: queue.summary.painGroups, bound: queue.summary.bound, unlinked: queue.summary.unlinked, blocked: queue.summary.blocked }) }), queue?.truncated && _jsx("p", { className: "mt-1 text-[12.5px] text-amber", children: t('pages.focus.ruleBinding.truncatedNote', { count: queue.items.length }) }), error && _jsx("p", { role: "alert", className: "my-2 text-amber", children: error }), message && _jsx("p", { role: "status", className: "my-2", children: message }), visibleGroups.map(group => _jsxs("div", { className: "mt-4", "data-testid": "rule-binding-pain-group", children: [_jsxs("div", { className: "border-l-2 border-line pl-3", children: [_jsxs("div", { className: "text-[13px] text-ink-2 leading-relaxed", children: [_jsx("span", { className: "font-medium text-ink", children: t('pages.focus.ruleBinding.groupPrefix') }), ' ', group.pain.text
@@ -36,12 +36,19 @@ function validateApprovalGroup(raw) {
36
36
  typeof r.status !== "string") {
37
37
  return null;
38
38
  }
39
+ let recordRevisionReviewUnavailable;
40
+ if (Object.hasOwn(r, "revisionReviewUnavailable")) {
41
+ if (typeof r.revisionReviewUnavailable !== "boolean")
42
+ return null;
43
+ recordRevisionReviewUnavailable = r.revisionReviewUnavailable;
44
+ }
39
45
  validRecords.push({
40
46
  id: r.id,
41
47
  artifactId: r.artifactId,
42
48
  channel: r.channel,
43
49
  createdAt: r.createdAt,
44
50
  status: r.status,
51
+ ...(recordRevisionReviewUnavailable !== undefined ? { revisionReviewUnavailable: recordRevisionReviewUnavailable } : {}),
45
52
  });
46
53
  }
47
54
  // Wave 7: candidateDescription is optional — present when backend could
@@ -85,6 +92,28 @@ function validateApprovalGroup(raw) {
85
92
  return null;
86
93
  artifactUnavailable = unavailable;
87
94
  }
95
+ let revisionReviewUnavailable;
96
+ if (Object.hasOwn(raw, 'revisionReviewUnavailable')) {
97
+ const { revisionReviewUnavailable: unavailable } = raw;
98
+ if (typeof unavailable !== 'boolean')
99
+ return null;
100
+ revisionReviewUnavailable = unavailable;
101
+ }
102
+ let revisionReview;
103
+ if (Object.hasOwn(raw, 'revisionReview')) {
104
+ const review = raw.revisionReview;
105
+ if (!isRecord(review) || typeof review.oldStatement !== 'string' || typeof review.newStatement !== 'string' || !isRecord(review.intentFields))
106
+ return null;
107
+ const intentFields = {};
108
+ for (const [key, value] of Object.entries(review.intentFields)) {
109
+ if (typeof value !== 'string')
110
+ return null;
111
+ intentFields[key] = value;
112
+ }
113
+ revisionReview = { oldStatement: review.oldStatement, newStatement: review.newStatement, intentFields,
114
+ ...(typeof review.artifactId === 'string' ? { artifactId: review.artifactId } : {}),
115
+ ...(typeof review.artifactDigest === 'string' && /^sha256:[a-f0-9]{64}$/.test(review.artifactDigest) ? { artifactDigest: review.artifactDigest } : {}) };
116
+ }
88
117
  return {
89
118
  principleId,
90
119
  principleTitle,
@@ -93,6 +122,8 @@ function validateApprovalGroup(raw) {
93
122
  fitsPromptBudget,
94
123
  promptBudgetTierFit,
95
124
  artifactUnavailable,
125
+ revisionReviewUnavailable,
126
+ revisionReview,
96
127
  records: validRecords,
97
128
  };
98
129
  }