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.
- package/console/dist/server/index.js +19 -0
- package/console/dist/server/models/OwnerDecisionViewModel.d.ts +65 -0
- package/console/dist/server/models/OwnerDecisionViewModel.js +686 -0
- package/console/dist/server/routes/owner-decision.d.ts +40 -0
- package/console/dist/server/routes/owner-decision.js +88 -0
- package/console/dist/server/routes/principles.d.ts +3 -0
- package/console/dist/server/routes/principles.js +3 -0
- package/console/dist/ui/api.d.ts +7 -3
- package/console/dist/ui/api.js +13 -2
- package/console/dist/ui/i18n/en.json +70 -4
- package/console/dist/ui/i18n/zh-CN.json +70 -4
- package/console/dist/ui/pages/focus/FocusPage.js +25 -2
- package/console/dist/ui/pages/principles/PrincipleDetailPage.d.ts +24 -0
- package/console/dist/ui/pages/principles/PrincipleDetailPage.js +168 -210
- package/console/dist/ui/pages/principles/PrinciplesPage.js +25 -10
- package/console/dist/ui/utils/enum-labels.d.ts +1 -1
- package/console/dist/ui/utils/enum-labels.js +8 -0
- package/console/dist/ui/utils/validators.d.ts +99 -1
- package/console/dist/ui/utils/validators.js +115 -0
- package/console/dist/web/assets/app.css +3 -0
- package/console/dist/web/assets/app.js +590 -349
- package/core/dist/runtime-v2/adapter/__tests__/principle-tree-ledger-adapter.test.js +25 -0
- package/core/dist/runtime-v2/adapter/__tests__/principle-tree-ledger-adapter.test.js.map +1 -1
- package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.d.ts +16 -0
- package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.d.ts.map +1 -1
- package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.js +22 -0
- package/core/dist/runtime-v2/adapter/principle-tree-ledger-adapter.js.map +1 -1
- package/core/dist/runtime-v2/index.d.ts +7 -0
- package/core/dist/runtime-v2/index.d.ts.map +1 -1
- package/core/dist/runtime-v2/index.js +4 -0
- package/core/dist/runtime-v2/index.js.map +1 -1
- package/core/dist/runtime-v2/owner-decision/__tests__/owner-decision-view.test.d.ts +2 -0
- package/core/dist/runtime-v2/owner-decision/__tests__/owner-decision-view.test.d.ts.map +1 -0
- package/core/dist/runtime-v2/owner-decision/__tests__/owner-decision-view.test.js +559 -0
- package/core/dist/runtime-v2/owner-decision/__tests__/owner-decision-view.test.js.map +1 -0
- package/core/dist/runtime-v2/owner-decision/owner-decision-view-contract.d.ts +7868 -0
- package/core/dist/runtime-v2/owner-decision/owner-decision-view-contract.d.ts.map +1 -0
- package/core/dist/runtime-v2/owner-decision/owner-decision-view-contract.js +367 -0
- package/core/dist/runtime-v2/owner-decision/owner-decision-view-contract.js.map +1 -0
- package/core/dist/runtime-v2/owner-decision/owner-decision-view.d.ts +415 -0
- package/core/dist/runtime-v2/owner-decision/owner-decision-view.d.ts.map +1 -0
- package/core/dist/runtime-v2/owner-decision/owner-decision-view.js +856 -0
- package/core/dist/runtime-v2/owner-decision/owner-decision-view.js.map +1 -0
- package/core/dist/runtime-v2/owner-decision/semantic-selector.d.ts +72 -0
- package/core/dist/runtime-v2/owner-decision/semantic-selector.d.ts.map +1 -0
- package/core/dist/runtime-v2/owner-decision/semantic-selector.js +176 -0
- package/core/dist/runtime-v2/owner-decision/semantic-selector.js.map +1 -0
- package/package.json +1 -1
- package/pd-cli/dist/commands/runtime-internalization-run-rulehost.d.ts +14 -0
- package/pd-cli/dist/commands/runtime-internalization-run-rulehost.d.ts.map +1 -1
- package/pd-cli/dist/commands/runtime-internalization-run-rulehost.js +47 -7
- package/pd-cli/dist/commands/runtime-internalization-run-rulehost.js.map +1 -1
- package/pd-cli/dist/services/rulehost-pipeline-runner.d.ts +84 -0
- package/pd-cli/dist/services/rulehost-pipeline-runner.d.ts.map +1 -1
- package/pd-cli/dist/services/rulehost-pipeline-runner.js +215 -38
- package/pd-cli/dist/services/rulehost-pipeline-runner.js.map +1 -1
- package/plugin/dist/bundle.js +526 -526
- package/plugin/dist/governance-audit.js +205 -205
- package/plugin/dist/rulehost-evidence.js +246 -246
- 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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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 (
|
|
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
|
-
|
|
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.
|
|
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
|