@unson/brainbase-mcp 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/contracts/judgment-dag/digest.json +2 -2
- package/dist/canonical-task-contract.d.ts +9 -0
- package/dist/canonical-task-contract.js +39 -0
- package/dist/canonical-task-principal.d.ts +16 -0
- package/dist/canonical-task-principal.js +36 -0
- package/dist/canonical-task-service.d.ts +251 -0
- package/dist/canonical-task-service.js +809 -0
- package/dist/company-os-evaluation.d.ts +193 -0
- package/dist/company-os-evaluation.js +1150 -0
- package/dist/company-os-impact-review.d.ts +248 -0
- package/dist/company-os-impact-review.js +663 -0
- package/dist/company-os-learning-adoption.d.ts +288 -0
- package/dist/company-os-learning-adoption.js +944 -0
- package/dist/company-os-objectives.d.ts +52 -0
- package/dist/company-os-objectives.js +197 -0
- package/dist/company-os-receipt-adapter.d.ts +109 -0
- package/dist/company-os-receipt-adapter.js +636 -0
- package/dist/constraint-exception-store.d.ts +23 -0
- package/dist/constraint-exception-store.js +108 -0
- package/dist/constraint-resolution.d.ts +175 -0
- package/dist/constraint-resolution.js +922 -0
- package/dist/decision-adapter.d.ts +145 -0
- package/dist/decision-adapter.js +586 -0
- package/dist/durable-wait-http.d.ts +51 -0
- package/dist/durable-wait-http.js +497 -0
- package/dist/durable-waits.d.ts +234 -0
- package/dist/durable-waits.js +944 -0
- package/dist/execution-authority-http.d.ts +28 -0
- package/dist/execution-authority-http.js +359 -0
- package/dist/execution-authority.d.ts +201 -0
- package/dist/execution-authority.js +634 -0
- package/dist/foundation-catalog.d.ts +26 -0
- package/dist/foundation-catalog.js +206 -0
- package/dist/foundation-constraint-store.d.ts +50 -0
- package/dist/foundation-constraint-store.js +215 -0
- package/dist/foundation-http.d.ts +90 -0
- package/dist/foundation-http.js +439 -0
- package/dist/foundation-store.d.ts +132 -0
- package/dist/foundation-store.js +654 -0
- package/dist/judgment-dag-composition.d.ts +224 -0
- package/dist/judgment-dag-composition.js +731 -0
- package/dist/judgment-dag.d.ts +2 -0
- package/dist/judgment-dag.js +1 -0
- package/dist/judgment-problem-snapshot.d.ts +185 -0
- package/dist/judgment-problem-snapshot.js +777 -0
- package/dist/judgment-view-http.d.ts +22 -0
- package/dist/judgment-view-http.js +128 -0
- package/dist/judgment-view.d.ts +222 -0
- package/dist/judgment-view.js +627 -0
- package/dist/knowledge-adapter.d.ts +112 -0
- package/dist/knowledge-adapter.js +691 -0
- package/dist/ontology-foundation.d.ts +209 -0
- package/dist/ontology-foundation.js +1000 -0
- package/dist/ontology.d.ts +13 -0
- package/dist/ontology.js +14 -0
- package/dist/organization-connection.d.ts +164 -0
- package/dist/organization-connection.js +428 -0
- package/dist/personal-knowledge.d.ts +120 -0
- package/dist/personal-knowledge.js +750 -0
- package/dist/problem-candidates.d.ts +149 -0
- package/dist/problem-candidates.js +898 -0
- package/dist/problem-selection.d.ts +238 -0
- package/dist/problem-selection.js +995 -0
- package/dist/resource-reservation-http.d.ts +31 -0
- package/dist/resource-reservation-http.js +371 -0
- package/dist/resource-reservations.d.ts +221 -0
- package/dist/resource-reservations.js +577 -0
- package/dist/server.d.ts +97 -0
- package/dist/server.js +180 -1
- package/dist/ssot.d.ts +6 -1
- package/dist/ssot.js +63 -5
- package/dist/streamable-http.d.ts +42 -0
- package/dist/streamable-http.js +288 -0
- package/dist/types.d.ts +20 -0
- package/dist/world-model.d.ts +181 -0
- package/dist/world-model.js +873 -0
- package/package.json +145 -3
- package/ui/icons/mana/LICENSE.tabler.txt +21 -0
- package/ui/icons/mana/affiliate.svg +20 -0
- package/ui/icons/mana/brand-github.svg +15 -0
- package/ui/icons/mana/brand-google-drive.svg +17 -0
- package/ui/icons/mana/brand-slack.svg +18 -0
- package/ui/icons/mana/cpu.svg +24 -0
- package/ui/judgment-view.css +238 -0
- package/ui/judgment-view.js +508 -0
- package/ui/objective-editor.css +180 -0
- package/ui/objective-editor.js +1139 -0
- package/ui/outcome-knowledge.css +365 -0
- package/ui/outcome-knowledge.js +1570 -0
- package/ui/outcome-mana.css +255 -0
- package/ui/outcome-mana.js +2325 -0
|
@@ -0,0 +1,508 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Shared read-only judgment trace surface.
|
|
3
|
+
*
|
|
4
|
+
* The host supplies the historical judgment-view response and, optionally, a
|
|
5
|
+
* same-origin request function. This module owns presentation only: it does
|
|
6
|
+
* not select a tenant, copy member/RACI data, or turn unresolved data into an
|
|
7
|
+
* empty state.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const JUDGMENT_VIEW_UI_CONTRACT_VERSION = 'judgment-view-ui.v1';
|
|
11
|
+
|
|
12
|
+
const STATUSES = Object.freeze(['resolved', 'unknown', 'permission_denied', 'unavailable', 'undecidable', 'invalid']);
|
|
13
|
+
const STATUS_LABELS = Object.freeze({
|
|
14
|
+
resolved: '確認済み',
|
|
15
|
+
unknown: '未確認',
|
|
16
|
+
permission_denied: '権限不足',
|
|
17
|
+
unavailable: '取得できません',
|
|
18
|
+
undecidable: '判定不能',
|
|
19
|
+
invalid: '記録不正',
|
|
20
|
+
});
|
|
21
|
+
const CHILD_STATUS_LABELS = Object.freeze({ completed: '完了', failed: '失敗', held: '保留' });
|
|
22
|
+
const ACHIEVEMENT_LABELS = Object.freeze({ achieved: '達成', not_achieved: '未達成', indeterminate: '判定不能' });
|
|
23
|
+
const VALIDITY_LABELS = Object.freeze({ valid: '妥当', invalid: '不妥当', indeterminate: '判定不能' });
|
|
24
|
+
const STATUS_PRECEDENCE = Object.freeze(['invalid', 'permission_denied', 'unavailable', 'unknown', 'undecidable', 'resolved']);
|
|
25
|
+
|
|
26
|
+
function getDocument(explicit) {
|
|
27
|
+
const value = explicit ?? (typeof document === 'undefined' ? null : document);
|
|
28
|
+
if (!value || typeof value.createElement !== 'function') throw new Error('document_unavailable');
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function makeElement(doc, tag, { className, text, attrs = {} } = {}) {
|
|
33
|
+
const element = doc.createElement(tag);
|
|
34
|
+
if (className) element.className = className;
|
|
35
|
+
if (text !== undefined && text !== null) element.textContent = String(text);
|
|
36
|
+
for (const [name, value] of Object.entries(attrs)) {
|
|
37
|
+
if (value === undefined || value === null || value === false) continue;
|
|
38
|
+
element.setAttribute(name, value === true ? '' : String(value));
|
|
39
|
+
}
|
|
40
|
+
return element;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function append(parent, ...children) {
|
|
44
|
+
parent.append(...children.filter((child) => child !== null && child !== undefined));
|
|
45
|
+
return parent;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function clear(parent) {
|
|
49
|
+
parent.replaceChildren();
|
|
50
|
+
return parent;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isRecord(value) {
|
|
54
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function nonEmpty(value) {
|
|
58
|
+
return typeof value === 'string' && value.trim().length > 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function status(value) {
|
|
62
|
+
return STATUSES.includes(value) ? value : 'unknown';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function aggregateStatus(sections) {
|
|
66
|
+
for (const candidate of STATUS_PRECEDENCE) {
|
|
67
|
+
if (sections.some((section) => status(section?.status) === candidate)) return candidate;
|
|
68
|
+
}
|
|
69
|
+
return 'unknown';
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function label(value, fallback = '未確認') {
|
|
73
|
+
if (value === null || value === undefined) return fallback;
|
|
74
|
+
if (typeof value === 'string') return value.trim() || fallback;
|
|
75
|
+
if (typeof value === 'number' || typeof value === 'boolean') return String(value);
|
|
76
|
+
return fallback;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function jsonValue(value) {
|
|
80
|
+
if (value === undefined) return '未確認';
|
|
81
|
+
try {
|
|
82
|
+
const serialized = JSON.stringify(value);
|
|
83
|
+
return serialized === undefined ? '未確認' : serialized;
|
|
84
|
+
} catch {
|
|
85
|
+
return '未確認';
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function statusText(value) {
|
|
90
|
+
const normalized = status(value);
|
|
91
|
+
return STATUS_LABELS[normalized] ?? '未確認';
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function normalizeSection(value) {
|
|
95
|
+
if (!isRecord(value) || !STATUSES.includes(value.status)) return { status: 'unknown', reason: '表示データが不正です' };
|
|
96
|
+
if (value.status === 'resolved' && !Object.hasOwn(value, 'value')) return { status: 'unknown', reason: '表示データが不足しています' };
|
|
97
|
+
return value.status === 'resolved'
|
|
98
|
+
? { status: 'resolved', value: value.value }
|
|
99
|
+
: { status: value.status, ...(nonEmpty(value.reason) ? { reason: value.reason } : {}) };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function normalizedResolvedSection(section, validator, reason) {
|
|
103
|
+
if (section.status !== 'resolved') return section;
|
|
104
|
+
return validator(section.value)
|
|
105
|
+
? section
|
|
106
|
+
: { status: 'unknown', reason };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function normalizeCollection(value, itemValidator = () => true) {
|
|
110
|
+
if (!isRecord(value) || !STATUSES.includes(value.status)) {
|
|
111
|
+
return { status: 'unknown', items: null, absence_confirmed: false, reason: '一覧データが不正です' };
|
|
112
|
+
}
|
|
113
|
+
if (value.status === 'resolved') {
|
|
114
|
+
if (!Array.isArray(value.items) || typeof value.absence_confirmed !== 'boolean' || value.items.some((item) => !itemValidator(item))) {
|
|
115
|
+
return { status: 'unknown', items: null, absence_confirmed: false, reason: '一覧データが不正です' };
|
|
116
|
+
}
|
|
117
|
+
if (value.items.length === 0 && value.absence_confirmed !== true) {
|
|
118
|
+
return { status: 'unknown', items: null, absence_confirmed: false, reason: '空であることを確認できません' };
|
|
119
|
+
}
|
|
120
|
+
return { status: 'resolved', items: value.items, absence_confirmed: value.items.length === 0 };
|
|
121
|
+
}
|
|
122
|
+
if (value.items !== null || value.absence_confirmed !== false) {
|
|
123
|
+
return { status: 'unknown', items: null, absence_confirmed: false, reason: '未確認の一覧を空として扱えません' };
|
|
124
|
+
}
|
|
125
|
+
return { status: value.status, items: null, absence_confirmed: false, ...(nonEmpty(value.reason) ? { reason: value.reason } : {}) };
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function validRef(value) {
|
|
129
|
+
return isRecord(value) && nonEmpty(value.id) && nonEmpty(value.type) && nonEmpty(value.revision) && nonEmpty(value.digest);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function validReferenceItem(value) {
|
|
133
|
+
return isRecord(value) && nonEmpty(value.kind) && nonEmpty(value.id) && nonEmpty(value.revision) && nonEmpty(value.digest);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function validChild(value) {
|
|
137
|
+
return isRecord(value) && nonEmpty(value.invocationId) && isRecord(value.dag) && nonEmpty(value.dag.id)
|
|
138
|
+
&& nonEmpty(value.dag.version) && ['completed', 'failed', 'held'].includes(value.status);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function validEvidence(value) {
|
|
142
|
+
return isRecord(value) && value.source === 'subdag' && nonEmpty(value.invocationId) && nonEmpty(value.kind) && nonEmpty(value.id) && nonEmpty(value.revision);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function validCriterion(value) {
|
|
146
|
+
return isRecord(value)
|
|
147
|
+
&& validRef(value.variableRef)
|
|
148
|
+
&& value.variableRef.type === 'variable'
|
|
149
|
+
&& isRecord(value.variable)
|
|
150
|
+
&& nonEmpty(value.variable.meaning)
|
|
151
|
+
&& nonEmpty(value.variable.subject)
|
|
152
|
+
&& nonEmpty(value.variable.valueKind)
|
|
153
|
+
&& nonEmpty(value.variable.aggregation)
|
|
154
|
+
&& nonEmpty(value.variable.granularity)
|
|
155
|
+
&& ['at_least', 'at_most', 'equals'].includes(value.operator)
|
|
156
|
+
&& (value.target === undefined || ['string', 'number', 'boolean'].includes(typeof value.target));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function validRun(value) {
|
|
160
|
+
return isRecord(value)
|
|
161
|
+
&& nonEmpty(value.runId)
|
|
162
|
+
&& ['completed', 'failed', 'held'].includes(value.status)
|
|
163
|
+
&& nonEmpty(value.question)
|
|
164
|
+
&& isRecord(value.composition)
|
|
165
|
+
&& nonEmpty(value.composition.id)
|
|
166
|
+
&& nonEmpty(value.composition.version)
|
|
167
|
+
&& isRecord(value.parentDag)
|
|
168
|
+
&& nonEmpty(value.parentDag.id)
|
|
169
|
+
&& nonEmpty(value.parentDag.version)
|
|
170
|
+
&& isRecord(value.problemSnapshot)
|
|
171
|
+
&& nonEmpty(value.problemSnapshot.snapshot_id)
|
|
172
|
+
&& nonEmpty(value.problemSnapshot.problem_id)
|
|
173
|
+
&& nonEmpty(value.problemSnapshot.revision);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function validConclusion(value) {
|
|
177
|
+
return isRecord(value)
|
|
178
|
+
&& value.source === 'parent-run-artifact'
|
|
179
|
+
&& nonEmpty(value.runId)
|
|
180
|
+
&& isRecord(value.dag)
|
|
181
|
+
&& nonEmpty(value.dag.id)
|
|
182
|
+
&& nonEmpty(value.dag.version)
|
|
183
|
+
&& Object.hasOwn(value, 'value');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function validProblem(value) {
|
|
187
|
+
return isRecord(value)
|
|
188
|
+
&& nonEmpty(value.snapshotId)
|
|
189
|
+
&& nonEmpty(value.problemId)
|
|
190
|
+
&& nonEmpty(value.revision)
|
|
191
|
+
&& nonEmpty(value.question)
|
|
192
|
+
&& Array.isArray(value.references)
|
|
193
|
+
&& value.references.every(validReferenceItem);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function validObjective(value) {
|
|
197
|
+
return isRecord(value)
|
|
198
|
+
&& validRef(value.ref)
|
|
199
|
+
&& value.ref.type === 'objective'
|
|
200
|
+
&& nonEmpty(value.meaning)
|
|
201
|
+
&& nonEmpty(value.desiredState)
|
|
202
|
+
&& nonEmpty(value.adoptionState)
|
|
203
|
+
&& Array.isArray(value.beneficiaryIds)
|
|
204
|
+
&& value.beneficiaryIds.every(nonEmpty)
|
|
205
|
+
&& isRecord(value.criteria);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function validResultEvaluation(value) {
|
|
209
|
+
return isRecord(value) && nonEmpty(value.evaluationId)
|
|
210
|
+
&& ['achieved', 'not_achieved', 'indeterminate'].includes(value.achievement)
|
|
211
|
+
&& Array.isArray(value.criteria) && value.criteria.every(isRecord)
|
|
212
|
+
&& Array.isArray(value.predictionComparisons) && value.predictionComparisons.every(isRecord)
|
|
213
|
+
&& isRecord(value.outcomeCaseRef)
|
|
214
|
+
&& nonEmpty(value.outcomeCaseRef.id)
|
|
215
|
+
&& nonEmpty(value.outcomeCaseRef.revision)
|
|
216
|
+
&& nonEmpty(value.outcomeCaseRef.digest)
|
|
217
|
+
&& nonEmpty(value.evaluatedAt);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function validJudgmentValidity(value) {
|
|
221
|
+
return isRecord(value) && ['valid', 'invalid', 'indeterminate'].includes(value.status)
|
|
222
|
+
&& nonEmpty(value.basis) && Array.isArray(value.evidenceRefs) && value.evidenceRefs.every(nonEmpty);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Normalize the boundary without dropping malformed collection members. A
|
|
227
|
+
* single malformed member makes the collection unknown so the UI cannot claim
|
|
228
|
+
* a complete list from a filtered response.
|
|
229
|
+
*/
|
|
230
|
+
export function normalizeJudgmentView(input) {
|
|
231
|
+
if (!isRecord(input) || input.mode !== 'historical') {
|
|
232
|
+
return { contract_version: JUDGMENT_VIEW_UI_CONTRACT_VERSION, mode: 'historical', status: 'unknown', reason: '判断記録を確認できません' };
|
|
233
|
+
}
|
|
234
|
+
const run = normalizedResolvedSection(normalizeSection(input.run), validRun, 'Runの表示データが不正です');
|
|
235
|
+
const conclusion = normalizedResolvedSection(normalizeSection(input.conclusion), validConclusion, '結論の表示データが不正です');
|
|
236
|
+
const problem = normalizedResolvedSection(normalizeSection(input.problem), validProblem, 'Problemの表示データが不正です');
|
|
237
|
+
const objective = normalizedResolvedSection(normalizeSection(input.objective), validObjective, 'Objectiveの表示データが不正です');
|
|
238
|
+
const evidence = normalizeCollection(input.evidence, validEvidence);
|
|
239
|
+
const childRuns = normalizeCollection(input.childRuns, validChild);
|
|
240
|
+
const resultEvaluation = normalizeSection(input.resultEvaluation);
|
|
241
|
+
const judgmentValidity = normalizeSection(input.judgmentValidity);
|
|
242
|
+
let normalizedObjective = objective;
|
|
243
|
+
if (objective.status === 'resolved' && isRecord(objective.value)) {
|
|
244
|
+
const criteria = normalizeCollection(objective.value.criteria, validCriterion);
|
|
245
|
+
normalizedObjective = criteria.status === 'resolved'
|
|
246
|
+
? {
|
|
247
|
+
...objective,
|
|
248
|
+
value: {
|
|
249
|
+
...objective.value,
|
|
250
|
+
criteria,
|
|
251
|
+
},
|
|
252
|
+
}
|
|
253
|
+
: { status: criteria.status, ...(nonEmpty(criteria.reason) ? { reason: criteria.reason } : {}) };
|
|
254
|
+
}
|
|
255
|
+
const normalizedEvaluation = resultEvaluation.status === 'resolved' && validResultEvaluation(resultEvaluation.value)
|
|
256
|
+
? resultEvaluation
|
|
257
|
+
: resultEvaluation.status === 'resolved'
|
|
258
|
+
? { status: 'unknown', reason: '成果評価の表示データが不正です' }
|
|
259
|
+
: resultEvaluation;
|
|
260
|
+
const normalizedValidity = judgmentValidity.status === 'resolved' && validJudgmentValidity(judgmentValidity.value)
|
|
261
|
+
? judgmentValidity
|
|
262
|
+
: judgmentValidity.status === 'resolved'
|
|
263
|
+
? { status: 'unknown', reason: '判断妥当性の表示データが不正です' }
|
|
264
|
+
: judgmentValidity;
|
|
265
|
+
const normalizedSections = [
|
|
266
|
+
run,
|
|
267
|
+
conclusion,
|
|
268
|
+
problem,
|
|
269
|
+
normalizedObjective,
|
|
270
|
+
evidence,
|
|
271
|
+
childRuns,
|
|
272
|
+
normalizedEvaluation,
|
|
273
|
+
normalizedValidity,
|
|
274
|
+
];
|
|
275
|
+
const aggregate = aggregateStatus([{ status: input.status }, ...normalizedSections]);
|
|
276
|
+
const reason = normalizedSections.find((section) => section.status !== 'resolved' && nonEmpty(section.reason))?.reason;
|
|
277
|
+
return {
|
|
278
|
+
...input,
|
|
279
|
+
contract_version: JUDGMENT_VIEW_UI_CONTRACT_VERSION,
|
|
280
|
+
mode: 'historical',
|
|
281
|
+
status: aggregate,
|
|
282
|
+
...(reason ? { reason } : {}),
|
|
283
|
+
run,
|
|
284
|
+
conclusion,
|
|
285
|
+
problem,
|
|
286
|
+
objective: normalizedObjective,
|
|
287
|
+
evidence,
|
|
288
|
+
childRuns,
|
|
289
|
+
resultEvaluation: normalizedEvaluation,
|
|
290
|
+
judgmentValidity: normalizedValidity,
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function statusNotice(doc, section, title) {
|
|
295
|
+
const state = status(section?.status);
|
|
296
|
+
const notice = makeElement(doc, 'p', { className: `judgment-view-notice is-${state}`, attrs: { role: 'status' } });
|
|
297
|
+
notice.textContent = `${title}: ${statusText(state)}${nonEmpty(section?.reason) ? `(${section.reason})` : ''}`;
|
|
298
|
+
return notice;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function refText(ref) {
|
|
302
|
+
if (!isRecord(ref)) return '未確認';
|
|
303
|
+
return `${label(ref.type)} / ${label(ref.id)} @ ${label(ref.revision)}${ref.digest ? ` / ${label(ref.digest)}` : ''}`;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function renderReferenceList(doc, parent, references) {
|
|
307
|
+
const list = makeElement(doc, 'ul', { className: 'judgment-view-reference-list' });
|
|
308
|
+
for (const reference of references) {
|
|
309
|
+
list.append(makeElement(doc, 'li', { text: `${label(reference.kind)}: ${refText(reference)}` }));
|
|
310
|
+
}
|
|
311
|
+
parent.append(list);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function renderConclusion(doc, root, section) {
|
|
315
|
+
const panel = makeElement(doc, 'section', { className: 'judgment-view-panel judgment-view-conclusion', attrs: { 'aria-labelledby': 'judgment-view-conclusion-heading' } });
|
|
316
|
+
panel.append(makeElement(doc, 'h2', { text: '結論', attrs: { id: 'judgment-view-conclusion-heading' } }));
|
|
317
|
+
if (section.status !== 'resolved') {
|
|
318
|
+
panel.append(statusNotice(doc, section, '結論'));
|
|
319
|
+
} else {
|
|
320
|
+
panel.append(makeElement(doc, 'pre', { className: 'judgment-view-conclusion-value', text: jsonValue(section.value?.value) }));
|
|
321
|
+
const linkRow = makeElement(doc, 'p', { className: 'judgment-view-link-row' });
|
|
322
|
+
panel.append(linkRow);
|
|
323
|
+
const link = makeElement(doc, 'a', { text: '根拠へ移動', attrs: { href: '#judgment-view-evidence' } });
|
|
324
|
+
linkRow.append(link);
|
|
325
|
+
}
|
|
326
|
+
root.append(panel);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function renderProblem(doc, root, section) {
|
|
330
|
+
const panel = makeElement(doc, 'section', { className: 'judgment-view-panel', attrs: { 'aria-labelledby': 'judgment-view-problem-heading' } });
|
|
331
|
+
panel.append(makeElement(doc, 'h2', { text: '判断時点のProblem', attrs: { id: 'judgment-view-problem-heading' } }));
|
|
332
|
+
if (section.status !== 'resolved') {
|
|
333
|
+
panel.append(statusNotice(doc, section, 'Problem'));
|
|
334
|
+
} else {
|
|
335
|
+
const value = section.value;
|
|
336
|
+
panel.append(makeElement(doc, 'p', { className: 'judgment-view-question', text: label(value?.question) }));
|
|
337
|
+
const meta = makeElement(doc, 'dl', { className: 'judgment-view-meta' });
|
|
338
|
+
for (const [term, detail] of [
|
|
339
|
+
['Snapshot', `${label(value?.snapshotId)} / ${label(value?.problemId)} @ ${label(value?.revision)}`],
|
|
340
|
+
['参照モード', '当時版(historical)'],
|
|
341
|
+
]) {
|
|
342
|
+
meta.append(makeElement(doc, 'dt', { text: term }), makeElement(doc, 'dd', { text: detail }));
|
|
343
|
+
}
|
|
344
|
+
panel.append(meta);
|
|
345
|
+
const references = Array.isArray(value?.references) ? value.references : null;
|
|
346
|
+
if (references) {
|
|
347
|
+
panel.append(makeElement(doc, 'h3', { text: '固定された参照' }));
|
|
348
|
+
renderReferenceList(doc, panel, references);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
root.append(panel);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function renderObjective(doc, root, section) {
|
|
355
|
+
const panel = makeElement(doc, 'section', { className: 'judgment-view-panel', attrs: { 'aria-labelledby': 'judgment-view-objective-heading' } });
|
|
356
|
+
panel.append(makeElement(doc, 'h2', { text: '当時のObjective', attrs: { id: 'judgment-view-objective-heading' } }));
|
|
357
|
+
if (section.status !== 'resolved') {
|
|
358
|
+
panel.append(statusNotice(doc, section, 'Objective'));
|
|
359
|
+
} else {
|
|
360
|
+
const value = section.value;
|
|
361
|
+
panel.append(makeElement(doc, 'p', { className: 'judgment-view-objective-state', text: label(value?.desiredState) }));
|
|
362
|
+
panel.append(makeElement(doc, 'p', { text: label(value?.meaning) }));
|
|
363
|
+
panel.append(makeElement(doc, 'p', { className: 'judgment-view-muted', text: `定義版: ${refText(value?.ref)} / 採用状態: ${label(value?.adoptionState)} / 認識状態: ${label(value?.epistemicState)}` }));
|
|
364
|
+
const criteria = value?.criteria;
|
|
365
|
+
panel.append(makeElement(doc, 'h3', { text: '達成基準' }));
|
|
366
|
+
if (!criteria || criteria.status !== 'resolved') {
|
|
367
|
+
panel.append(statusNotice(doc, criteria, '達成基準'));
|
|
368
|
+
} else if (criteria.items.length === 0) {
|
|
369
|
+
panel.append(makeElement(doc, 'p', { className: 'judgment-view-muted', text: '達成基準は記録されていません。' }));
|
|
370
|
+
} else {
|
|
371
|
+
const list = makeElement(doc, 'ul', { className: 'judgment-view-criteria' });
|
|
372
|
+
for (const criterion of criteria.items) {
|
|
373
|
+
list.append(makeElement(doc, 'li', { text: `${refText(criterion.variableRef)} / ${label(criterion.operator)} / 目標: ${label(criterion.target)}` }));
|
|
374
|
+
}
|
|
375
|
+
panel.append(list);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
root.append(panel);
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
function renderEvidence(doc, root, collection) {
|
|
382
|
+
const panel = makeElement(doc, 'section', { className: 'judgment-view-panel', attrs: { id: 'judgment-view-evidence', 'aria-labelledby': 'judgment-view-evidence-heading' } });
|
|
383
|
+
panel.append(makeElement(doc, 'h2', { text: '根拠の参照記録', attrs: { id: 'judgment-view-evidence-heading' } }));
|
|
384
|
+
if (collection.status !== 'resolved') {
|
|
385
|
+
panel.append(statusNotice(doc, collection, '根拠'));
|
|
386
|
+
} else if (collection.items.length === 0) {
|
|
387
|
+
panel.append(makeElement(doc, 'p', { className: 'judgment-view-notice is-unknown', text: '根拠の参照記録はありません(空成功ではありません)。', attrs: { role: 'status' } }));
|
|
388
|
+
} else {
|
|
389
|
+
const list = makeElement(doc, 'ul', { className: 'judgment-view-evidence-list' });
|
|
390
|
+
for (const item of collection.items) {
|
|
391
|
+
list.append(makeElement(doc, 'li', { text: `${label(item.kind)} / ${label(item.id)}${item.revision ? ` @ ${label(item.revision)}` : ''}(${label(item.invocationId)})` }));
|
|
392
|
+
}
|
|
393
|
+
panel.append(list);
|
|
394
|
+
}
|
|
395
|
+
root.append(panel);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function renderChildRuns(doc, root, collection) {
|
|
399
|
+
const panel = makeElement(doc, 'section', { className: 'judgment-view-panel', attrs: { 'aria-labelledby': 'judgment-view-child-heading' } });
|
|
400
|
+
panel.append(makeElement(doc, 'h2', { text: '下位判断', attrs: { id: 'judgment-view-child-heading' } }));
|
|
401
|
+
if (collection.status !== 'resolved') {
|
|
402
|
+
panel.append(statusNotice(doc, collection, '下位判断'));
|
|
403
|
+
} else if (collection.items.length === 0) {
|
|
404
|
+
panel.append(makeElement(doc, 'p', { className: 'judgment-view-muted', text: '下位判断はありません。' }));
|
|
405
|
+
} else {
|
|
406
|
+
const list = makeElement(doc, 'ul', { className: 'judgment-view-child-list' });
|
|
407
|
+
for (const child of collection.items) {
|
|
408
|
+
const item = makeElement(doc, 'li', { className: `judgment-view-child is-${child.status}` });
|
|
409
|
+
item.append(makeElement(doc, 'strong', { text: `${label(child.invocationId)} — ${CHILD_STATUS_LABELS[child.status] ?? child.status}` }));
|
|
410
|
+
item.append(makeElement(doc, 'span', { text: `${label(child.dag.id)} @ ${label(child.dag.version)}: ${label(child.question)}` }));
|
|
411
|
+
if (child.status !== 'completed' && nonEmpty(child.reason)) item.append(makeElement(doc, 'span', { className: 'judgment-view-muted', text: child.reason }));
|
|
412
|
+
list.append(item);
|
|
413
|
+
}
|
|
414
|
+
panel.append(list);
|
|
415
|
+
}
|
|
416
|
+
root.append(panel);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function renderEvaluation(doc, root, resultEvaluation, judgmentValidity) {
|
|
420
|
+
const panel = makeElement(doc, 'section', { className: 'judgment-view-panel', attrs: { 'aria-labelledby': 'judgment-view-evaluation-heading' } });
|
|
421
|
+
panel.append(makeElement(doc, 'h2', { text: '結果評価と判断妥当性', attrs: { id: 'judgment-view-evaluation-heading' } }));
|
|
422
|
+
const result = makeElement(doc, 'div', { className: 'judgment-view-evaluation-block' });
|
|
423
|
+
result.append(makeElement(doc, 'h3', { text: '結果の達成度' }));
|
|
424
|
+
if (resultEvaluation.status !== 'resolved') result.append(statusNotice(doc, resultEvaluation, '結果評価'));
|
|
425
|
+
else result.append(makeElement(doc, 'p', { text: `${ACHIEVEMENT_LABELS[resultEvaluation.value.achievement] ?? '判定不能'}(評価ID: ${label(resultEvaluation.value.evaluationId)})` }));
|
|
426
|
+
const validity = makeElement(doc, 'div', { className: 'judgment-view-evaluation-block' });
|
|
427
|
+
validity.append(makeElement(doc, 'h3', { text: '判断時点の妥当性' }));
|
|
428
|
+
if (judgmentValidity.status !== 'resolved') validity.append(statusNotice(doc, judgmentValidity, '判断妥当性'));
|
|
429
|
+
else {
|
|
430
|
+
validity.append(makeElement(doc, 'p', { text: `${VALIDITY_LABELS[judgmentValidity.value.status] ?? '判定不能'}: ${label(judgmentValidity.value.basis)}` }));
|
|
431
|
+
if (judgmentValidity.value.evidenceRefs.length > 0) validity.append(makeElement(doc, 'p', { className: 'judgment-view-muted', text: `妥当性の根拠参照: ${judgmentValidity.value.evidenceRefs.join(', ')}` }));
|
|
432
|
+
}
|
|
433
|
+
panel.append(result, validity);
|
|
434
|
+
root.append(panel);
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/** Render one normalized document into a host-owned root. */
|
|
438
|
+
export function renderJudgmentView(root, input, options = {}) {
|
|
439
|
+
const doc = getDocument(options.document);
|
|
440
|
+
const model = normalizeJudgmentView(input);
|
|
441
|
+
clear(root);
|
|
442
|
+
const surface = makeElement(doc, 'section', { className: 'judgment-view', attrs: { 'data-contract-version': JUDGMENT_VIEW_UI_CONTRACT_VERSION } });
|
|
443
|
+
const header = makeElement(doc, 'header', { className: 'judgment-view-header' });
|
|
444
|
+
header.append(makeElement(doc, 'div', { className: 'judgment-view-kicker', text: 'BRAINBASE / JUDGMENT TRACE' }));
|
|
445
|
+
header.append(makeElement(doc, 'h1', { text: '判断の追跡' }));
|
|
446
|
+
header.append(makeElement(doc, 'p', { className: 'judgment-view-mode', text: '当時版(historical)を表示しています。現在版で補完しません。' }));
|
|
447
|
+
header.append(makeElement(doc, 'span', { className: `judgment-view-status is-${model.status}`, text: statusText(model.status), attrs: { role: 'status' } }));
|
|
448
|
+
surface.append(header);
|
|
449
|
+
if (nonEmpty(model.reason)) surface.append(makeElement(doc, 'p', { className: 'judgment-view-notice is-unknown', text: model.reason, attrs: { role: 'status' } }));
|
|
450
|
+
if (model.run.status === 'resolved' && isRecord(model.run.value)) {
|
|
451
|
+
const run = makeElement(doc, 'p', { className: 'judgment-view-run-meta', text: `Run: ${label(model.run.value.runId)} / 構成版: ${label(model.run.value.composition?.id)} @ ${label(model.run.value.composition?.version)} / 状態: ${CHILD_STATUS_LABELS[model.run.value.status] ?? label(model.run.value.status)}` });
|
|
452
|
+
surface.append(run);
|
|
453
|
+
} else surface.append(statusNotice(doc, model.run, 'Run'));
|
|
454
|
+
renderConclusion(doc, surface, model.conclusion);
|
|
455
|
+
renderProblem(doc, surface, model.problem);
|
|
456
|
+
renderObjective(doc, surface, model.objective);
|
|
457
|
+
renderEvidence(doc, surface, model.evidence);
|
|
458
|
+
renderChildRuns(doc, surface, model.childRuns);
|
|
459
|
+
renderEvaluation(doc, surface, model.resultEvaluation, model.judgmentValidity);
|
|
460
|
+
root.append(surface);
|
|
461
|
+
return surface;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function buildPath(runId, evaluationId, basePath = '/judgment-views') {
|
|
465
|
+
const path = `${String(basePath).replace(/\/+$/u, '')}/${encodeURIComponent(runId)}`;
|
|
466
|
+
return evaluationId ? `${path}?evaluationId=${encodeURIComponent(evaluationId)}` : path;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
export function createJudgmentViewUI({ root, document: explicitDocument, api, fetcher, runId, evaluationId, basePath, autoLoad = true } = {}) {
|
|
470
|
+
if (!root) throw new TypeError('root is required');
|
|
471
|
+
if (!nonEmpty(runId)) throw new TypeError('runId is required');
|
|
472
|
+
const doc = getDocument(explicitDocument);
|
|
473
|
+
const read = typeof api === 'function' ? api : api && typeof api.read === 'function' ? (path) => api.read(path) : null;
|
|
474
|
+
const request = read ?? (typeof fetcher === 'function' ? fetcher : typeof globalThis.fetch === 'function' ? (path) => globalThis.fetch(path) : null);
|
|
475
|
+
const state = { model: normalizeJudgmentView({ mode: 'historical', status: 'unknown' }), loading: false, error: null };
|
|
476
|
+
const controller = {
|
|
477
|
+
get state() { return state; },
|
|
478
|
+
render() { renderJudgmentView(root, state.model, { document: doc }); return controller; },
|
|
479
|
+
async load() {
|
|
480
|
+
if (!request) {
|
|
481
|
+
state.model = normalizeJudgmentView({ mode: 'historical', status: 'unavailable', reason: '判断ビューのAPIが未設定です' });
|
|
482
|
+
state.error = 'api_unavailable';
|
|
483
|
+
controller.render();
|
|
484
|
+
return state.model;
|
|
485
|
+
}
|
|
486
|
+
state.loading = true;
|
|
487
|
+
controller.render();
|
|
488
|
+
try {
|
|
489
|
+
const response = await request(buildPath(runId, evaluationId, basePath));
|
|
490
|
+
const body = response && typeof response.json === 'function' ? await response.json() : response;
|
|
491
|
+
state.model = normalizeJudgmentView(body);
|
|
492
|
+
state.error = null;
|
|
493
|
+
} catch (error) {
|
|
494
|
+
state.error = error instanceof Error ? error.message : 'request_failed';
|
|
495
|
+
state.model = normalizeJudgmentView({ mode: 'historical', status: 'unavailable', reason: '判断ビューを取得できません' });
|
|
496
|
+
} finally {
|
|
497
|
+
state.loading = false;
|
|
498
|
+
}
|
|
499
|
+
controller.render();
|
|
500
|
+
return state.model;
|
|
501
|
+
},
|
|
502
|
+
};
|
|
503
|
+
controller.render();
|
|
504
|
+
if (autoLoad) void controller.load();
|
|
505
|
+
return controller;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
export default renderJudgmentView;
|