create-principles-disciple 1.143.0 → 1.143.1

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 (60) hide show
  1. package/console/dist/server/index.js +19 -0
  2. package/console/dist/server/models/OwnerDecisionViewModel.d.ts +65 -0
  3. package/console/dist/server/models/OwnerDecisionViewModel.js +686 -0
  4. package/console/dist/server/routes/owner-decision.d.ts +40 -0
  5. package/console/dist/server/routes/owner-decision.js +88 -0
  6. package/console/dist/server/routes/principles.d.ts +3 -0
  7. package/console/dist/server/routes/principles.js +3 -0
  8. package/console/dist/ui/api.d.ts +7 -3
  9. package/console/dist/ui/api.js +13 -2
  10. package/console/dist/ui/i18n/en.json +70 -4
  11. package/console/dist/ui/i18n/zh-CN.json +70 -4
  12. package/console/dist/ui/pages/focus/FocusPage.js +25 -2
  13. package/console/dist/ui/pages/principles/PrincipleDetailPage.d.ts +24 -0
  14. package/console/dist/ui/pages/principles/PrincipleDetailPage.js +168 -210
  15. package/console/dist/ui/pages/principles/PrinciplesPage.js +25 -10
  16. package/console/dist/ui/utils/enum-labels.d.ts +1 -1
  17. package/console/dist/ui/utils/enum-labels.js +8 -0
  18. package/console/dist/ui/utils/validators.d.ts +99 -1
  19. package/console/dist/ui/utils/validators.js +115 -0
  20. package/console/dist/web/assets/app.css +3 -0
  21. package/console/dist/web/assets/app.js +590 -349
  22. package/core/dist/runtime-v2/adapter/__tests__/principle-tree-ledger-adapter.test.js +25 -0
  23. package/core/dist/runtime-v2/adapter/__tests__/principle-tree-ledger-adapter.test.js.map +1 -1
  24. package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.d.ts +16 -0
  25. package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.d.ts.map +1 -1
  26. package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.js +22 -0
  27. package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.js.map +1 -1
  28. package/core/dist/runtime-v2/index.d.ts +7 -0
  29. package/core/dist/runtime-v2/index.d.ts.map +1 -1
  30. package/core/dist/runtime-v2/index.js +4 -0
  31. package/core/dist/runtime-v2/index.js.map +1 -1
  32. package/core/dist/runtime-v2/owner-decision/__tests__/owner-decision-view.test.d.ts +2 -0
  33. package/core/dist/runtime-v2/owner-decision/__tests__/owner-decision-view.test.d.ts.map +1 -0
  34. package/core/dist/runtime-v2/owner-decision/__tests__/owner-decision-view.test.js +559 -0
  35. package/core/dist/runtime-v2/owner-decision/__tests__/owner-decision-view.test.js.map +1 -0
  36. package/core/dist/runtime-v2/owner-decision/owner-decision-view-contract.d.ts +7868 -0
  37. package/core/dist/runtime-v2/owner-decision/owner-decision-view-contract.d.ts.map +1 -0
  38. package/core/dist/runtime-v2/owner-decision/owner-decision-view-contract.js +367 -0
  39. package/core/dist/runtime-v2/owner-decision/owner-decision-view-contract.js.map +1 -0
  40. package/core/dist/runtime-v2/owner-decision/owner-decision-view.d.ts +415 -0
  41. package/core/dist/runtime-v2/owner-decision/owner-decision-view.d.ts.map +1 -0
  42. package/core/dist/runtime-v2/owner-decision/owner-decision-view.js +856 -0
  43. package/core/dist/runtime-v2/owner-decision/owner-decision-view.js.map +1 -0
  44. package/core/dist/runtime-v2/owner-decision/semantic-selector.d.ts +72 -0
  45. package/core/dist/runtime-v2/owner-decision/semantic-selector.d.ts.map +1 -0
  46. package/core/dist/runtime-v2/owner-decision/semantic-selector.js +176 -0
  47. package/core/dist/runtime-v2/owner-decision/semantic-selector.js.map +1 -0
  48. package/package.json +1 -1
  49. package/pd-cli/dist/commands/runtime-internalization-run-rulehost.d.ts +14 -0
  50. package/pd-cli/dist/commands/runtime-internalization-run-rulehost.d.ts.map +1 -1
  51. package/pd-cli/dist/commands/runtime-internalization-run-rulehost.js +47 -7
  52. package/pd-cli/dist/commands/runtime-internalization-run-rulehost.js.map +1 -1
  53. package/pd-cli/dist/services/rulehost-pipeline-runner.d.ts +84 -0
  54. package/pd-cli/dist/services/rulehost-pipeline-runner.d.ts.map +1 -1
  55. package/pd-cli/dist/services/rulehost-pipeline-runner.js +215 -38
  56. package/pd-cli/dist/services/rulehost-pipeline-runner.js.map +1 -1
  57. package/plugin/dist/bundle.js +526 -526
  58. package/plugin/dist/governance-audit.js +205 -205
  59. package/plugin/dist/rulehost-evidence.js +246 -246
  60. package/release-manager/package.json +1 -1
@@ -7,7 +7,7 @@ import { SectionTitle } from "../../components/layout/section-title.js";
7
7
  import { Button } from "../../components/ui/button.js";
8
8
  import { Skeleton } from "../../components/ui/skeleton.js";
9
9
  import { ShinyText } from "../../components/ui/shiny-text.js";
10
- import { fetchPrinciples, fetchApprovalsGrouped, } from "../../api.js";
10
+ import { fetchPrinciples, fetchApprovalsGrouped, fetchOwnerDecisionLibrary, } from "../../api.js";
11
11
  import { enumLabel } from "../../utils/enum-labels.js";
12
12
  import { formatDate } from "../../utils/format-date.js";
13
13
  import { PrinciplesViewNav } from "./PrinciplesViewNav.js";
@@ -155,15 +155,20 @@ export function PrinciplesPage() {
155
155
  // "只看待决策"不再作为默认视图 (真实决策在治理焦点)。
156
156
  const [filterMode, setFilterMode] = useState('all');
157
157
  const [categories, setCategories] = useState();
158
+ // Owner Decision Experience v1: backend semantic titles + decision states
159
+ // (display-only — the page never re-derives decision qualification).
160
+ const [ownerDecisionLibrary, setOwnerDecisionLibrary] = useState(null);
158
161
  // Fetch data
159
162
  const loadData = useCallback(async () => {
160
163
  setLoading(true);
161
164
  setError(null);
162
165
  try {
163
- const [pResult, aResult] = await Promise.all([
166
+ const [pResult, aResult, dResult] = await Promise.all([
164
167
  fetchPrinciples(filterMode),
165
168
  fetchApprovalsGrouped(),
169
+ fetchOwnerDecisionLibrary(),
166
170
  ]);
171
+ setOwnerDecisionLibrary(dResult.success && dResult.data ? dResult.data : null);
167
172
  if (!pResult.success) {
168
173
  setError(pResult.error ?? "Failed to load principles");
169
174
  return;
@@ -203,18 +208,25 @@ export function PrinciplesPage() {
203
208
  ? ag.records.map((r) => r.channel).filter(isString)
204
209
  : ["prompt"];
205
210
  const uniqueChannels = [...new Set(channels)];
206
- const confidence = p.evaluability === "deterministic"
207
- ? "high"
208
- : p.evaluability === "weak_heuristic"
209
- ? "low"
210
- : "medium";
211
+ // Owner Decision Experience v1 (SPEC §10/§11.2): the card title comes
212
+ // from the backend Semantic Selector; when no readable principle exists
213
+ // the card shows a NEUTRAL placeholder — the technical trigger/original
214
+ // text stays reachable in the collapsed details below (never the title).
215
+ const decision = ownerDecisionLibrary?.entries[p.id];
216
+ const semanticTitle = decision?.learnedPrinciple.status === 'known' ? decision.learnedPrinciple.text : undefined;
211
217
  // PRI-332: Determine display title with bounded fallback for unreadable titles
212
218
  const rawTitle = p.triggerPattern || p.text.slice(0, 80);
213
219
  const detectedLang = p.detectedLanguage ?? 'unknown';
214
220
  const readWarningCode = p.readabilityWarningCode;
215
221
  let displayTitle = rawTitle;
216
222
  let usedFallback = false;
217
- if (readWarningCode) {
223
+ if (semanticTitle !== undefined) {
224
+ displayTitle = semanticTitle;
225
+ usedFallback = rawTitle !== semanticTitle;
226
+ }
227
+ else if (readWarningCode || decision !== undefined) {
228
+ // No qualified semantic text: neutral placeholder instead of a
229
+ // technical trigger as the Owner-facing title.
218
230
  displayTitle = t("principles.readabilityFallbackTitle");
219
231
  usedFallback = true;
220
232
  }
@@ -227,7 +239,8 @@ export function PrinciplesPage() {
227
239
  action: p.action,
228
240
  status: toReviewStatus(p.status, ag?.status),
229
241
  channels: uniqueChannels,
230
- confidence,
242
+ evaluability: p.evaluability,
243
+ decisionState: decision?.decisionState,
231
244
  updatedAt: p.updatedAt,
232
245
  createdAt: p.createdAt,
233
246
  priority: p.priority,
@@ -291,7 +304,9 @@ export function PrinciplesPage() {
291
304
  STATUS_TEXT[card.status] +
292
305
  " border-current/20", children: t("principles.status" + card.status.charAt(0).toUpperCase() + card.status.slice(1)) }), card.channels.map((ch) => (_jsx("span", { className: "font-mono text-[11px] uppercase tracking-[0.02em] border border-line rounded-[2px] px-2 py-0.5 text-ink-3", children: enumLabel('channel', ch, t) }, ch))), card.detectedLanguage && card.detectedLanguage !== 'unknown' && (_jsx("span", { className: "font-mono text-[11px] tracking-[0.02em] border border-line rounded-[2px] px-2 py-0.5 text-ink-4", children: card.detectedLanguage === 'zh'
293
306
  ? t("principles.languageHintZh")
294
- : t("principles.languageHintEn") })), _jsxs("span", { className: "font-mono text-[11px] text-ink-4", children: [t("principles.confidence"), ": ", enumLabel('confidence', card.confidence, t)] })] }), card.readabilityWarningCode && (_jsx("div", { className: "mb-2 px-3 py-1.5 bg-amber/5 border border-amber/20 rounded-[3px] text-ink-3 text-[12px] leading-snug", children: t("principles.readabilityWarning." + card.readabilityWarningCode, { defaultValue: t("principles.readabilityFallbackTitle") }) })), card.detectedLanguage && card.detectedLanguage !== 'unknown' && i18n.language && !i18n.language.startsWith(card.detectedLanguage) && (_jsx("div", { className: "mb-2 px-3 py-1.5 bg-surface/60 border-l-2 border-line rounded-[3px] text-ink-4 text-[12px] leading-snug", children: t("principles.languageMismatchHint", { lang: card.detectedLanguage === 'en' ? 'English' : '中文' }) })), _jsx("h3", { className: "font-semibold text-ink mb-1", children: card.title }), card.titleUsedFallback && card.originalTitle && (_jsxs("details", { className: "mb-1", children: [_jsx("summary", { className: "text-ink-4 text-[11px] font-mono cursor-pointer hover:underline", children: t("principles.readabilityOriginalLabel") }), _jsx("div", { className: "text-ink-4 text-[12px] leading-snug mt-1 font-mono bg-surface/40 px-2 py-1 rounded-[3px] break-all", children: card.originalTitle })] })), _jsx("p", { className: "text-ink-3 text-[13px] line-clamp-3 leading-relaxed", children: card.text }), _jsxs("p", { className: "font-mono text-[11px] text-ink-4 mt-2", children: [t("principles.updatedAt"), ": ", formatDate(card.updatedAt, i18n.language)] }), _jsx(EvolutionBlock, { label: t("principles.blockBasis"), children: ag?.candidateDescription ?? t("principles.blockBasisFallback") }), _jsx(EvolutionBlock, { label: t("principles.blockImpact"), children: t(BLOCK_IMPACT_KEY[card.status]) }), _jsx(EvolutionBlock, { label: t("principles.blockBehavior"), children: card.action ? card.action : t("principles.blockBehaviorFallback") }), _jsx("div", { className: "mt-3 pt-3 border-t border-line/60", children: _jsxs("div", { className: "flex items-center justify-between gap-3 bg-gov/5 border border-line rounded-[3px] px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: "font-mono text-[11px] uppercase tracking-[0.02em] text-ink-4", children: t("principles.govDecision") }), _jsxs("span", { className: "font-mono text-[11px] uppercase tracking-[0.02em] border rounded-[2px] px-2 py-0.5 " +
307
+ : t("principles.languageHintEn") })), _jsxs("span", { className: "font-mono text-[11px] text-ink-4", children: [t("principles.evaluabilityLabel", { defaultValue: "可评价性" }), ": ", enumLabel('evaluability', card.evaluability, t)] }), card.decisionState !== undefined && (_jsx("span", { className: "font-mono text-[11px] tracking-[0.02em] border border-line rounded-[2px] px-2 py-0.5 text-ink-4", children: t(`principles.ownerDecision.state.${card.decisionState}`, { defaultValue: card.decisionState }) }))] }), card.readabilityWarningCode && (_jsx("div", { className: "mb-2 px-3 py-1.5 bg-amber/5 border border-amber/20 rounded-[3px] text-ink-3 text-[12px] leading-snug", children: t("principles.readabilityWarning." + card.readabilityWarningCode, { defaultValue: t("principles.readabilityFallbackTitle") }) })), card.detectedLanguage && card.detectedLanguage !== 'unknown' && i18n.language && !i18n.language.startsWith(card.detectedLanguage) && (_jsx("div", { className: "mb-2 px-3 py-1.5 bg-surface/60 border-l-2 border-line rounded-[3px] text-ink-4 text-[12px] leading-snug", children: t("principles.languageMismatchHint", { lang: card.detectedLanguage === 'en' ? 'English' : '中文' }) })), _jsx("h3", { className: "font-semibold text-ink mb-1", children: card.title }), card.titleUsedFallback && card.originalTitle && (_jsxs("details", { className: "mb-1", children: [_jsx("summary", { className: "text-ink-4 text-[11px] font-mono cursor-pointer hover:underline", children: t("principles.readabilityOriginalLabel") }), _jsx("div", { className: "text-ink-4 text-[12px] leading-snug mt-1 font-mono bg-surface/40 px-2 py-1 rounded-[3px] break-all", children: card.originalTitle })] })), _jsx("p", { className: "text-ink-3 text-[13px] line-clamp-3 leading-relaxed", children: card.text }), _jsxs("p", { className: "font-mono text-[11px] text-ink-4 mt-2", children: [t("principles.updatedAt"), ": ", formatDate(card.updatedAt, i18n.language)] }), _jsx(EvolutionBlock, { label: t("principles.blockBasis"), children: ag?.candidateDescription ?? t("principles.blockBasisFallback") }), _jsx(EvolutionBlock, { label: t("principles.blockImpact"), children: card.decisionState !== undefined
308
+ ? t("principles.blockImpactSeeDecisionView", { defaultValue: "以详情页「当前实际执行」为准(本卡片不据此判断影响)。" })
309
+ : t(BLOCK_IMPACT_KEY[card.status]) }), _jsx(EvolutionBlock, { label: t("principles.blockBehavior"), children: card.action ? card.action : t("principles.blockBehaviorFallback") }), _jsx("div", { className: "mt-3 pt-3 border-t border-line/60", children: _jsxs("div", { className: "flex items-center justify-between gap-3 bg-gov/5 border border-line rounded-[3px] px-3 py-2", children: [_jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [_jsx("span", { className: "font-mono text-[11px] uppercase tracking-[0.02em] text-ink-4", children: t("principles.govDecision") }), _jsxs("span", { className: "font-mono text-[11px] uppercase tracking-[0.02em] border rounded-[2px] px-2 py-0.5 " +
295
310
  STATUS_TEXT[card.status] +
296
311
  " border-current/20", children: [decision.glyph, " ", t(decision.labelKey)] })] }), decisionDate && (_jsx("span", { className: "font-mono text-[11px] text-ink-4 whitespace-nowrap", children: formatDate(decisionDate, i18n.language) }))] }) })] }, card.principleId));
297
312
  })] }))] }));
@@ -19,7 +19,7 @@
19
19
  * - Local map keyed by the RAW value (with hyphens) for exact lookup
20
20
  */
21
21
  type TFunc = (key: string) => string;
22
- export type EnumCategory = 'evidenceState' | 'sourceKind' | 'channel' | 'readiness' | 'featureCategory' | 'featureId' | 'confidence' | 'feedbackType' | 'admission';
22
+ export type EnumCategory = 'evidenceState' | 'sourceKind' | 'channel' | 'readiness' | 'featureCategory' | 'featureId' | 'confidence' | 'feedbackType' | 'admission' | 'evaluability';
23
23
  /**
24
24
  * Resolve an enum value to a human-readable label.
25
25
  *
@@ -99,6 +99,14 @@ const LOCAL_LABELS = {
99
99
  'evidence_only': '仅作证据',
100
100
  'owner_confirmation_required': '待确认',
101
101
  },
102
+ // Owner Decision Experience v1 (T10): evaluability is NOT confidence — it
103
+ // describes how the principle's adherence can be evaluated, and must never
104
+ // be displayed as a confidence score.
105
+ evaluability: {
106
+ 'deterministic': '可确定性检查',
107
+ 'weak_heuristic': '弱启发式',
108
+ 'manual_only': '仅人工',
109
+ },
102
110
  };
103
111
  // ── Helpers ──────────────────────────────────────────────────────────────────
104
112
  /**
@@ -8,11 +8,109 @@
8
8
  * - Uses `Object.hasOwn()` for untrusted object keys (Rule 5)
9
9
  * - Graceful degradation includes a reason (Rule 9)
10
10
  */
11
- import type { GovernanceExperienceSnapshot, OwnerGovernanceView } from '@principles/core/runtime-v2';
11
+ import type { GovernanceExperienceSnapshot, OwnerGovernanceView, OwnerDecisionViewCore } from '@principles/core/runtime-v2';
12
+ /** Browser-local structural type for the compact inbox response (SPEC §11.1). */
13
+ export interface OwnerDecisionInboxData {
14
+ schemaVersion: '1';
15
+ generatedAt: string;
16
+ totalPrinciples: number;
17
+ groups: {
18
+ decision: {
19
+ principleId: string;
20
+ decisionState: string;
21
+ learnedPrinciple: {
22
+ status: 'known';
23
+ text: string;
24
+ } | {
25
+ status: 'unknown';
26
+ reasonText: string;
27
+ };
28
+ inbox: {
29
+ group: string;
30
+ attention: string;
31
+ };
32
+ blockerCodes: string[];
33
+ nextAction: {
34
+ ownerText: string;
35
+ };
36
+ }[];
37
+ blocked: {
38
+ principleId: string;
39
+ decisionState: string;
40
+ learnedPrinciple: {
41
+ status: 'known';
42
+ text: string;
43
+ } | {
44
+ status: 'unknown';
45
+ reasonText: string;
46
+ };
47
+ inbox: {
48
+ group: string;
49
+ attention: string;
50
+ };
51
+ blockerCodes: string[];
52
+ nextAction: {
53
+ ownerText: string;
54
+ };
55
+ }[];
56
+ recovery: {
57
+ principleId: string;
58
+ decisionState: string;
59
+ learnedPrinciple: {
60
+ status: 'known';
61
+ text: string;
62
+ } | {
63
+ status: 'unknown';
64
+ reasonText: string;
65
+ };
66
+ inbox: {
67
+ group: string;
68
+ attention: string;
69
+ };
70
+ blockerCodes: string[];
71
+ nextAction: {
72
+ ownerText: string;
73
+ };
74
+ }[];
75
+ };
76
+ historicalUnknown: {
77
+ count: number;
78
+ reasonText: string;
79
+ };
80
+ degraded?: {
81
+ reason: string;
82
+ nextAction: string;
83
+ };
84
+ }
12
85
  import type { FeedbackChannelId } from '../../shared/feedback-channel-ids.js';
13
86
  import type { FeedbackChannelStatus, FeedbackChannelsData, FeedbackSubmitResult } from '../../shared/feedback-contract.js';
14
87
  export type { FeedbackChannelId, FeedbackChannelStatus, FeedbackChannelsData, FeedbackSubmitResult };
15
88
  export declare function validateOwnerGovernanceView(value: unknown): OwnerGovernanceView | null;
89
+ export declare function isOwnerDecisionViewCore(value: unknown): value is OwnerDecisionViewCore;
90
+ export declare function validateOwnerDecisionViewCore(value: unknown): OwnerDecisionViewCore | null;
91
+ export declare function isOwnerDecisionInbox(value: unknown): value is OwnerDecisionInboxData;
92
+ export declare function validateOwnerDecisionInbox(value: unknown): OwnerDecisionInboxData | null;
93
+ /** Browser-local structural type for the compact library projection (SPEC §11.2). */
94
+ export interface OwnerDecisionLibraryData {
95
+ schemaVersion: '1';
96
+ generatedAt: string;
97
+ entries: Record<string, {
98
+ decisionState: string;
99
+ learnedPrinciple: {
100
+ status: 'known';
101
+ text: string;
102
+ sourceTier: string;
103
+ } | {
104
+ status: 'unknown';
105
+ reasonText: string;
106
+ };
107
+ }>;
108
+ degraded?: {
109
+ reason: string;
110
+ };
111
+ }
112
+ export declare function isOwnerDecisionLibrary(value: unknown): value is OwnerDecisionLibraryData;
113
+ export declare function validateOwnerDecisionLibrary(value: unknown): OwnerDecisionLibraryData | null;
16
114
  export declare function validateGovernanceExperienceSnapshot(value: unknown): GovernanceExperienceSnapshot | null;
17
115
  /**
18
116
  * Best-effort error envelope validator.
@@ -130,6 +130,121 @@ function isOwnerGovernanceView(value) {
130
130
  export function validateOwnerGovernanceView(value) {
131
131
  return isOwnerGovernanceView(value) ? value : null;
132
132
  }
133
+ // ── Owner Decision Experience v1 — browser-local structural validator ───────
134
+ //
135
+ // Mirrors the authoritative TypeBox contract in principles-core
136
+ // (owner-decision-view-contract.ts). Type-only import keeps the browser bundle
137
+ // free of the Node-oriented core barrel (same reason as the governance
138
+ // validator above, ERR-100).
139
+ const ownerDecisionStates = new Set(['needs_owner_decision', 'processing', 'blocked', 'recovery_needed', 'decided', 'no_action']);
140
+ const ownerInboxGroups = new Set(['decision', 'blocked', 'recovery', 'none']);
141
+ const ownerInboxAttention = new Set(['individual', 'aggregate', 'none']);
142
+ function isOwnerInboxItem(value) {
143
+ // Codex review P2 fix: the page dereferences item.inbox.attention,
144
+ // item.principleId and item.nextAction.ownerText on every entry — a
145
+ // malformed entry (e.g. `[null]` from a version-skewed server) must be
146
+ // rejected here instead of crashing the render.
147
+ if (!isObject(value) || !hasOwnFields(value, ['principleId', 'decisionState', 'learnedPrinciple', 'inbox', 'blockerCodes', 'nextAction']))
148
+ return false;
149
+ if (!isString(value.principleId) || value.principleId.length === 0)
150
+ return false;
151
+ if (!isString(value.decisionState) || !ownerDecisionStates.has(value.decisionState))
152
+ return false;
153
+ const principle = value.learnedPrinciple;
154
+ if (!isObject(principle))
155
+ return false;
156
+ if (principle.status === 'known') {
157
+ if (!hasOwnFields(principle, ['text', 'sourceTier'])
158
+ || !isString(principle.text) || principle.text.length === 0
159
+ || !isString(principle.sourceTier))
160
+ return false;
161
+ }
162
+ else if (principle.status === 'unknown') {
163
+ if (!hasOwnFields(principle, ['reasonText']) || !isString(principle.reasonText))
164
+ return false;
165
+ }
166
+ else
167
+ return false;
168
+ if (!isObject(value.inbox) || !hasOwnFields(value.inbox, ['group', 'attention'])
169
+ || !isStringEnum(value.inbox.group, ownerInboxGroups)
170
+ || !isStringEnum(value.inbox.attention, ownerInboxAttention))
171
+ return false;
172
+ if (!Array.isArray(value.blockerCodes) || !value.blockerCodes.every(isString))
173
+ return false;
174
+ return isObject(value.nextAction) && hasOwnFields(value.nextAction, ['ownerText'])
175
+ && isString(value.nextAction.ownerText);
176
+ }
177
+ function isOwnerReason(value) {
178
+ return isObject(value) && hasOwnFields(value, ['code', 'ownerText', 'sourceRefs'])
179
+ && isString(value.code) && value.code.length > 0
180
+ && isString(value.ownerText) && value.ownerText.length > 0
181
+ && Array.isArray(value.sourceRefs);
182
+ }
183
+ function isOwnerAction(value) {
184
+ return isObject(value) && hasOwnFields(value, ['key', 'semantic', 'label', 'channel', 'confirmation', 'expectedConsequence'])
185
+ && isString(value.key) && value.key.length > 0
186
+ && isString(value.semantic) && value.semantic.length > 0
187
+ && isString(value.label)
188
+ && isObject(value.confirmation) && typeof value.confirmation.required === 'boolean' && isString(value.confirmation.text);
189
+ }
190
+ export function isOwnerDecisionViewCore(value) {
191
+ if (!isObject(value) || !hasOwnFields(value, ['schemaVersion', 'principleId', 'asOf', 'sourceReadStatus', 'sourceReads',
192
+ 'incidentSummary', 'learnedPrinciple', 'rationale', 'applicability', 'nonApplicabilityOrUnknown', 'expectedBehavior',
193
+ 'currentEnforcement', 'evidenceSummary', 'uncertainty', 'risk', 'rollback', 'decisionState', 'decisionSubjects',
194
+ 'availableActions', 'blockers', 'nextAction', 'inbox', 'technicalDetails']))
195
+ return false;
196
+ if (value.schemaVersion !== '1' || !isString(value.principleId) || !isString(value.asOf))
197
+ return false;
198
+ if (value.sourceReadStatus !== 'complete' && value.sourceReadStatus !== 'partial')
199
+ return false;
200
+ if (!isStringEnum(value.decisionState, ownerDecisionStates))
201
+ return false;
202
+ if (!Array.isArray(value.availableActions) || !value.availableActions.every(isOwnerAction))
203
+ return false;
204
+ if (!Array.isArray(value.blockers) || !value.blockers.every(item => isObject(item) && hasOwnFields(item, ['subjectKey', 'kind', 'reason']) && isOwnerReason(item.reason)))
205
+ return false;
206
+ return isObject(value.nextAction) && hasOwnFields(value.nextAction, ['code', 'ownerText'])
207
+ && isString(value.nextAction.ownerText)
208
+ && isObject(value.inbox) && hasOwnFields(value.inbox, ['group', 'attention', 'reason']) && isOwnerReason(value.inbox.reason);
209
+ }
210
+ export function validateOwnerDecisionViewCore(value) {
211
+ return isOwnerDecisionViewCore(value) ? value : null;
212
+ }
213
+ export function isOwnerDecisionInbox(value) {
214
+ return isObject(value)
215
+ && hasOwnFields(value, ['schemaVersion', 'generatedAt', 'totalPrinciples', 'groups', 'historicalUnknown'])
216
+ && value.schemaVersion === '1'
217
+ && isString(value.generatedAt)
218
+ && typeof value.totalPrinciples === 'number'
219
+ && isObject(value.groups) && hasOwnFields(value.groups, ['decision', 'blocked', 'recovery'])
220
+ && ['decision', 'blocked', 'recovery'].every((key) => {
221
+ const group = value.groups[key];
222
+ return Array.isArray(group) && group.every(isOwnerInboxItem);
223
+ })
224
+ && isObject(value.historicalUnknown) && typeof (value.historicalUnknown).count === 'number';
225
+ }
226
+ export function validateOwnerDecisionInbox(value) {
227
+ return isOwnerDecisionInbox(value) ? value : null;
228
+ }
229
+ function isOwnerDecisionLibraryEntry(value) {
230
+ return isObject(value) && hasOwnFields(value, ['decisionState', 'learnedPrinciple'])
231
+ && isString(value.decisionState)
232
+ && isObject(value.learnedPrinciple)
233
+ && (value.learnedPrinciple.status === 'known'
234
+ ? isString(value.learnedPrinciple.text) && isString(value.learnedPrinciple.sourceTier)
235
+ : value.learnedPrinciple.status === 'unknown' && isString(value.learnedPrinciple.reasonText));
236
+ }
237
+ export function isOwnerDecisionLibrary(value) {
238
+ return isObject(value)
239
+ && hasOwnFields(value, ['schemaVersion', 'generatedAt', 'entries'])
240
+ && value.schemaVersion === '1'
241
+ && isString(value.generatedAt)
242
+ && isObject(value.entries)
243
+ && Object.values(value.entries).every(isOwnerDecisionLibraryEntry);
244
+ }
245
+ export function validateOwnerDecisionLibrary(value) {
246
+ return isOwnerDecisionLibrary(value) ? value : null;
247
+ }
133
248
  // ── PRI-586: Governance Experience Snapshot v1.5.1 (browser-local validator) ──
134
249
  // Mirrors the authoritative TypeBox contract in principles-core
135
250
  // (governance-experience-contract.ts). Type-only import above keeps the client
@@ -1890,6 +1890,9 @@
1890
1890
  .text-\[12px\] {
1891
1891
  font-size: 12px;
1892
1892
  }
1893
+ .text-\[13\.5px\] {
1894
+ font-size: 13.5px;
1895
+ }
1893
1896
  .text-\[13px\] {
1894
1897
  font-size: 13px;
1895
1898
  }