@unson/brainbase-mcp 0.41.7 → 0.41.9
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/dist/foundation-graph-http.js +5 -1
- package/dist/graph-foundation-reader.d.ts +33 -4
- package/dist/graph-foundation-reader.js +107 -10
- package/package.json +2 -2
- package/ui/foundation-overview.css +1 -8
- package/ui/foundation-overview.js +168 -34
- package/ui/graph-own-share.js +28 -9
- package/ui/outcome-knowledge.css +18 -0
- package/ui/outcome-knowledge.js +265 -9
- package/ui/workspace-kit.css +42 -0
- package/ui/workspace-kit.js +165 -0
- package/ui/world/world-canvas-notes.js +2 -1
- package/ui/world/world-canvas-ui.css +48 -3
- package/ui/world/world-canvas-ui.js +17 -4
- package/ui/world/world-district.js +34 -5
- package/ui/world/world-view.css +6 -0
- package/ui/world/world-view.js +109 -19
- package/ui/world/world-work-rail.js +15 -2
|
@@ -55,6 +55,16 @@ const PHILOSOPHY_STATUS_LABELS = Object.freeze({
|
|
|
55
55
|
archived: '終了',
|
|
56
56
|
});
|
|
57
57
|
const OPERATOR_LABELS = Object.freeze({ at_least: '以上', at_most: '以下', equals: '一致' });
|
|
58
|
+
const SOURCE_KIND_LABELS = Object.freeze({
|
|
59
|
+
candidate: '候補からの登録',
|
|
60
|
+
document: '文書',
|
|
61
|
+
observation: '観察',
|
|
62
|
+
decision: '決定',
|
|
63
|
+
import: '取り込み',
|
|
64
|
+
});
|
|
65
|
+
// A record without a name is said to have none; its id stays in the record details (P9).
|
|
66
|
+
const UNNAMED_RECORD = '名前の記録なし';
|
|
67
|
+
const UNNAMED_VARIABLE = '名前の記録がない変数';
|
|
58
68
|
|
|
59
69
|
export class FoundationOverviewError extends Error {
|
|
60
70
|
constructor(code, message) {
|
|
@@ -138,6 +148,22 @@ function validatePhilosophyRecords(value) {
|
|
|
138
148
|
return value;
|
|
139
149
|
}
|
|
140
150
|
|
|
151
|
+
function validateUnscopedPhilosophyRecords(value) {
|
|
152
|
+
if (!Array.isArray(value)) throw error('invalid_schema', '適用範囲が未記録の哲学の形式を確認できません。');
|
|
153
|
+
for (const [index, item] of value.entries()) {
|
|
154
|
+
if (!isObject(item)
|
|
155
|
+
|| item.kind !== 'philosophy'
|
|
156
|
+
|| !nonEmpty(item.id)
|
|
157
|
+
|| !Number.isSafeInteger(item.version) || item.version < 1
|
|
158
|
+
|| item.applicability_state !== 'unrecorded'
|
|
159
|
+
|| !isObject(item.payload)
|
|
160
|
+
|| !nonEmpty(item.payload.statement)) {
|
|
161
|
+
throw error('invalid_schema', `適用範囲が未記録の哲学の${index + 1}件目の形式が不正です。`);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return value;
|
|
165
|
+
}
|
|
166
|
+
|
|
141
167
|
/** Validate the host contract without converting an unreadable response into an empty catalog. */
|
|
142
168
|
export function normalizeFoundationCatalog(value, expectedScopeId) {
|
|
143
169
|
if (!isObject(value) || value.contractVersion !== FOUNDATION_OVERVIEW_CONTRACT_VERSION) {
|
|
@@ -153,7 +179,10 @@ export function normalizeFoundationCatalog(value, expectedScopeId) {
|
|
|
153
179
|
const knownReadableStatuses = new Set(['complete', 'ready', 'resolved', 'empty']);
|
|
154
180
|
const unreadableStatus = (readStatus && !knownReadableStatuses.has(readStatus))
|
|
155
181
|
|| (catalogStatus && !knownReadableStatuses.has(catalogStatus));
|
|
156
|
-
|
|
182
|
+
// Older hosts omit this field; then it stays unknown instead of becoming [].
|
|
183
|
+
const unscopedPhilosophies = value.philosophies_without_applicability === undefined
|
|
184
|
+
? undefined : validateUnscopedPhilosophyRecords(value.philosophies_without_applicability);
|
|
185
|
+
const collections = [value.objectives, value.variables, value.models, value.philosophies, unscopedPhilosophies];
|
|
157
186
|
const hasRecords = collections.some((records) => Array.isArray(records) && records.length > 0);
|
|
158
187
|
const absenceConfirmed = value.absence_confirmed === true;
|
|
159
188
|
if (unreadableStatus || (!hasRecords && ('absence_confirmed' in value || readStatus || catalogStatus) && !absenceConfirmed)) {
|
|
@@ -170,6 +199,7 @@ export function normalizeFoundationCatalog(value, expectedScopeId) {
|
|
|
170
199
|
variables: value.variables,
|
|
171
200
|
models: value.models,
|
|
172
201
|
philosophies: value.philosophies,
|
|
202
|
+
...(unscopedPhilosophies ? { philosophies_without_applicability: unscopedPhilosophies } : {}),
|
|
173
203
|
...(readStatus ? { read_status: readStatus } : {}),
|
|
174
204
|
...(catalogStatus ? { status: catalogStatus } : {}),
|
|
175
205
|
...(typeof value.absence_confirmed === 'boolean' ? { absence_confirmed: value.absence_confirmed } : {}),
|
|
@@ -225,12 +255,52 @@ const SCOPE_LABELS = Object.freeze({
|
|
|
225
255
|
company: '組織',
|
|
226
256
|
});
|
|
227
257
|
|
|
258
|
+
function formatValidity(from, until) {
|
|
259
|
+
if (!from && !until) return null;
|
|
260
|
+
if (!from) return `${formatDate(until)} まで`;
|
|
261
|
+
return `${formatDate(from)} から ${until ? `${formatDate(until)} まで` : '(終了日の記録なし)'}`;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* A scope in words: `{ type, id }` names the project or organization, and a validity scope
|
|
266
|
+
* (`subjectIds`, `validFrom`, `validUntil`) becomes its period and the number of subjects. The
|
|
267
|
+
* subject ids themselves belong to the record details.
|
|
268
|
+
*/
|
|
228
269
|
function formatScope(value) {
|
|
229
270
|
if (!isObject(value)) return formatStructuredValue(value);
|
|
230
271
|
const type = formatAny(value.type);
|
|
231
272
|
const id = formatAny(value.id);
|
|
232
273
|
if (type && id) return `${SCOPE_LABELS[type] ?? type}: ${id}`;
|
|
233
|
-
|
|
274
|
+
const subjects = firstValue(value, 'subjectIds', 'subject_ids');
|
|
275
|
+
const period = formatValidity(firstValue(value, 'validFrom', 'valid_from'), firstValue(value, 'validUntil', 'valid_until'));
|
|
276
|
+
const parts = [
|
|
277
|
+
Array.isArray(subjects) && subjects.length > 0 ? `対象 ${subjects.length}件` : null,
|
|
278
|
+
period ? `適用期間: ${period}` : null,
|
|
279
|
+
].filter(Boolean);
|
|
280
|
+
if (parts.length > 0) return parts.join(' / ');
|
|
281
|
+
return formatAny(firstValue(value, 'text', 'description', 'statement', 'label', 'name'))
|
|
282
|
+
?? '読める形の記録がありません(記録の詳細を参照)';
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** Where a definition came from, as kinds of source; the source and evidence ids stay in the details. */
|
|
286
|
+
function formatProvenance(provenance) {
|
|
287
|
+
if (!Array.isArray(provenance) || provenance.length === 0) return null;
|
|
288
|
+
return provenance.map((item) => {
|
|
289
|
+
const kind = SOURCE_KIND_LABELS[item?.sourceKind] ?? '種類の記録なし';
|
|
290
|
+
const evidence = Array.isArray(item?.evidenceIds) ? item.evidenceIds.length : 0;
|
|
291
|
+
return evidence > 0 ? `${kind}(証拠${evidence}件)` : kind;
|
|
292
|
+
}).join('、');
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/** Ids, revisions and digests of one record, for the "記録の詳細" disclosure only. */
|
|
296
|
+
function recordDetailsElement(doc, rows, summary = '記録の詳細') {
|
|
297
|
+
const details = makeElement(doc, 'details', { className: 'bb-fov-original' });
|
|
298
|
+
details.append(makeElement(doc, 'summary', { text: summary }), workspaceDefinition(doc, rows));
|
|
299
|
+
return details;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function referenceLabels(refs) {
|
|
303
|
+
return Array.isArray(refs) && refs.length > 0 ? refs.map(referenceLabel).join('、') : null;
|
|
234
304
|
}
|
|
235
305
|
|
|
236
306
|
function recordDefinition(record) { return isObject(record?.definition) ? record.definition : {}; }
|
|
@@ -265,6 +335,15 @@ function statusElement(doc, definition, className = '') {
|
|
|
265
335
|
return makeElement(doc, 'p', { className: `bb-fov-status${className ? ` ${className}` : ''}`, text: statusText(definition) });
|
|
266
336
|
}
|
|
267
337
|
|
|
338
|
+
/**
|
|
339
|
+
* The one state a reader needs in front of a model: whether it is held as verified or as a
|
|
340
|
+
* hypothesis. Adoption, validation and authorized uses stay in the record details.
|
|
341
|
+
*/
|
|
342
|
+
function certaintyText(definition) {
|
|
343
|
+
const label = EPISTEMIC_LABELS[definition.epistemicState] ?? VALIDATION_LABELS[definition.validationState];
|
|
344
|
+
return `確からしさ: ${label ?? '記録なし'}`;
|
|
345
|
+
}
|
|
346
|
+
|
|
268
347
|
function detailsFor(record) {
|
|
269
348
|
return isObject(record?.details) ? record.details : {};
|
|
270
349
|
}
|
|
@@ -310,9 +389,7 @@ function criteriaList(doc, definition, variableMap) {
|
|
|
310
389
|
const ref = criterion?.variableRef;
|
|
311
390
|
const exact = variableMap.get(foundationKey(ref));
|
|
312
391
|
const exactDefinition = exact ? recordDefinition(exact) : null;
|
|
313
|
-
const name = exactDefinition
|
|
314
|
-
? (formatAny(exactDefinition.meaning) ?? referenceLabel(ref))
|
|
315
|
-
: referenceLabel(ref);
|
|
392
|
+
const name = (exactDefinition ? formatAny(exactDefinition.meaning) : null) ?? UNNAMED_VARIABLE;
|
|
316
393
|
const variableDetails = exactDefinition
|
|
317
394
|
? [
|
|
318
395
|
['定義', exactDefinition.definition],
|
|
@@ -328,7 +405,7 @@ function criteriaList(doc, definition, variableMap) {
|
|
|
328
405
|
const status = exact ? '参照確認済み' : '参照未確認(基準変数の版が一致しません)';
|
|
329
406
|
list.append(makeElement(doc, 'li', {
|
|
330
407
|
className: exact ? '' : 'is-unrecorded',
|
|
331
|
-
text: `${name}${variableDetails.length > 0 ? `(${variableDetails.join(' / ')})` : ''}
|
|
408
|
+
text: `${name}${variableDetails.length > 0 ? `(${variableDetails.join(' / ')})` : ''} ${operator}${target === null ? '' : ` ${target}`} — ${status}`,
|
|
332
409
|
}));
|
|
333
410
|
}
|
|
334
411
|
return list;
|
|
@@ -395,7 +472,7 @@ function recordedStateElement(doc, record) {
|
|
|
395
472
|
function objectiveCard(doc, record, variableMap) {
|
|
396
473
|
const definition = recordDefinition(record);
|
|
397
474
|
const details = detailsFor(record);
|
|
398
|
-
const title = formatAny(firstValue(details, 'title')) ?? formatAny(definition.meaning) ??
|
|
475
|
+
const title = formatAny(firstValue(details, 'title')) ?? formatAny(definition.meaning) ?? UNNAMED_RECORD;
|
|
399
476
|
const gaps = missingObjectiveFields(record);
|
|
400
477
|
const current = currentStateFor(record);
|
|
401
478
|
const evaluationPeriod = formatAny(firstValue(details, 'evaluation_period_note', 'evaluationPeriodNote'))
|
|
@@ -446,11 +523,16 @@ function objectiveReading(doc, record, variableMap, recordContexts) {
|
|
|
446
523
|
const provenance = Array.isArray(definition.provenance) ? definition.provenance : [];
|
|
447
524
|
section('出典', workspaceDefinition(doc, [
|
|
448
525
|
['記録元', recordContexts?.get(record)?.label ?? formatAny(record.source) ?? '未記録'],
|
|
449
|
-
['根拠',
|
|
526
|
+
['根拠', formatProvenance(provenance) ?? '未記録'],
|
|
450
527
|
]));
|
|
528
|
+
const criteriaRefs = Array.isArray(definition.criteria) ? definition.criteria.map((criterion) => criterion?.variableRef) : [];
|
|
451
529
|
const original = makeElement(doc, 'details', { className: 'bb-fov-original' });
|
|
452
530
|
original.append(makeElement(doc, 'summary', { text: '記録の詳細・参照・原文' }), objectiveCard(doc, record, variableMap),
|
|
453
|
-
workspaceDefinition(doc, [
|
|
531
|
+
workspaceDefinition(doc, [
|
|
532
|
+
['目的ID', definition.id], ['版', definition.revision], ['digest', record.digest],
|
|
533
|
+
['基準変数の参照', referenceLabels(criteriaRefs)],
|
|
534
|
+
['根拠の記録', provenance.length ? formatStructuredValue(provenance) : null],
|
|
535
|
+
]));
|
|
454
536
|
body.append(original); return body;
|
|
455
537
|
}
|
|
456
538
|
|
|
@@ -477,12 +559,12 @@ function renderObjectives(doc, catalog, viewState, callbacks, recordContexts) {
|
|
|
477
559
|
const opener = doc.activeElement;
|
|
478
560
|
viewState.dialog?.close();
|
|
479
561
|
viewState.dialog = workspaceDialog(doc, {
|
|
480
|
-
title: formatAny(details.title) ?? formatAny(definition.meaning) ??
|
|
562
|
+
title: formatAny(details.title) ?? formatAny(definition.meaning) ?? UNNAMED_RECORD, opener,
|
|
481
563
|
content: objectiveReading(doc, record, recordContexts?.get(record)?.variableMap ?? variableMap, recordContexts),
|
|
482
564
|
});
|
|
483
565
|
},
|
|
484
566
|
cells: [
|
|
485
|
-
{ primary: formatAny(details.title) ?? formatAny(definition.meaning) ??
|
|
567
|
+
{ primary: formatAny(details.title) ?? formatAny(definition.meaning) ?? UNNAMED_RECORD },
|
|
486
568
|
current.text ?? current.status ?? '未記録',
|
|
487
569
|
{ text: current.missing.length ? current.missing.join('・') : missingObjectiveFields(record).join('・') || '記録なし', className: 'bb-fov-missing' },
|
|
488
570
|
formatDate(current.asOf),
|
|
@@ -506,13 +588,13 @@ function philosophyApplicability(payload) {
|
|
|
506
588
|
const period = validFrom
|
|
507
589
|
? `${formatDate(validFrom)}から${validUntil ? ` ${formatDate(validUntil)}まで` : ''}`
|
|
508
590
|
: null;
|
|
509
|
-
return [scope, period ? `適用期間: ${period}` : null].filter(Boolean).join(' / ') ||
|
|
591
|
+
return [scope, period ? `適用期間: ${period}` : null].filter(Boolean).join(' / ') || formatScope(legacy);
|
|
510
592
|
}
|
|
511
593
|
|
|
512
594
|
function philosophyCard(doc, record) {
|
|
513
595
|
const payload = record.payload;
|
|
514
596
|
const card = makeElement(doc, 'article', { className: 'bb-fov-philosophy-card' });
|
|
515
|
-
const title = formatAny(payload.title) ??
|
|
597
|
+
const title = formatAny(payload.title) ?? UNNAMED_RECORD;
|
|
516
598
|
card.append(makeElement(doc, 'h3', { text: title }));
|
|
517
599
|
|
|
518
600
|
if (formatAny(payload.statement)) card.append(makeElement(doc, 'p', { className: 'bb-fov-philosophy-statement', text: formatAny(payload.statement) }));
|
|
@@ -527,21 +609,53 @@ function philosophyCard(doc, record) {
|
|
|
527
609
|
return card;
|
|
528
610
|
}
|
|
529
611
|
|
|
612
|
+
function unscopedPhilosophyCard(doc, record, recordContexts) {
|
|
613
|
+
const payload = record.payload;
|
|
614
|
+
const card = makeElement(doc, 'article', { className: 'bb-fov-philosophy-card is-unscoped' });
|
|
615
|
+
card.append(makeElement(doc, 'h3', { text: formatAny(payload.title) ?? UNNAMED_RECORD }));
|
|
616
|
+
card.append(makeElement(doc, 'p', { className: 'bb-fov-philosophy-statement', text: payload.statement }));
|
|
617
|
+
if (formatAny(payload.summary) && payload.summary !== payload.statement) card.append(makeElement(doc, 'p', { text: payload.summary }));
|
|
618
|
+
// The section notice explains what an unrecorded applicability means.
|
|
619
|
+
card.append(workspaceDefinition(doc, [['適用範囲', '未記録']]));
|
|
620
|
+
const refs = makeElement(doc, 'details', { className: 'bb-fov-original' });
|
|
621
|
+
refs.append(makeElement(doc, 'summary', { text: '記録の詳細' }), workspaceDefinition(doc, [['出典', recordContexts?.get(record)?.label ?? '未記録'], ['状態', payload.status], ['Graphの版', record.version], ['記録ID', record.id]]));
|
|
622
|
+
card.append(refs);
|
|
623
|
+
return card;
|
|
624
|
+
}
|
|
625
|
+
|
|
530
626
|
function renderPhilosophies(doc, catalog, recordContexts) {
|
|
531
627
|
const section = makeElement(doc, 'section', { className: 'bb-fov-section', attrs: { 'aria-label': '哲学' } });
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
628
|
+
const unscoped = catalog.philosophies_without_applicability;
|
|
629
|
+
|
|
630
|
+
if (catalog.philosophies.length === 0 && !unscoped?.length) {
|
|
631
|
+
section.append(workspaceNotice(doc, {
|
|
632
|
+
label: '哲学',
|
|
633
|
+
text: unscoped
|
|
634
|
+
? 'この対象範囲には登録された哲学がありません。'
|
|
635
|
+
// Without the unscoped list, only the verified list is known to be empty.
|
|
636
|
+
: '適用範囲が記録された哲学はありません。適用範囲が未記録の哲学があるかは確認できていません。',
|
|
637
|
+
}));
|
|
535
638
|
return section;
|
|
536
639
|
}
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
const
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
640
|
+
if (catalog.philosophies.length) {
|
|
641
|
+
const grid = makeElement(doc, 'div', { className: 'bb-fov-philosophy-grid' });
|
|
642
|
+
for (const record of catalog.philosophies) {
|
|
643
|
+
const card = philosophyCard(doc, record);
|
|
644
|
+
const refs = makeElement(doc, 'details', { className: 'bb-fov-original' });
|
|
645
|
+
refs.append(makeElement(doc, 'summary', { text: '記録の詳細' }), workspaceDefinition(doc, [['出典', recordContexts?.get(record)?.label ?? '未記録'], ['状態', record.payload.status], ['版', record.revision], ['記録ID', record.id], ['digest', record.digest]]));
|
|
646
|
+
card.append(refs); grid.append(card);
|
|
647
|
+
}
|
|
648
|
+
section.append(grid);
|
|
649
|
+
}
|
|
650
|
+
if (unscoped?.length) {
|
|
651
|
+
section.append(workspaceNotice(doc, {
|
|
652
|
+
label: '適用範囲が未記録',
|
|
653
|
+
text: `次の${unscoped.length}件は、判断への適用範囲が記録されていない哲学です。判断では参照されますが、どの範囲に当てはまるかは確かめられていません。`,
|
|
654
|
+
}));
|
|
655
|
+
const grid = makeElement(doc, 'div', { className: 'bb-fov-philosophy-grid' });
|
|
656
|
+
for (const record of unscoped) grid.append(unscopedPhilosophyCard(doc, record, recordContexts));
|
|
657
|
+
section.append(grid);
|
|
543
658
|
}
|
|
544
|
-
section.append(grid);
|
|
545
659
|
return section;
|
|
546
660
|
}
|
|
547
661
|
|
|
@@ -569,10 +683,10 @@ function renderReferenceList(doc, refs, variableMap, label) {
|
|
|
569
683
|
}
|
|
570
684
|
for (const ref of refs) {
|
|
571
685
|
const exact = variableMap.get(foundationKey(ref));
|
|
572
|
-
const name = exact ? formatAny(recordDefinition(exact).meaning)
|
|
686
|
+
const name = (exact ? formatAny(recordDefinition(exact).meaning) : null) ?? UNNAMED_VARIABLE;
|
|
573
687
|
list.append(makeElement(doc, 'li', {
|
|
574
688
|
className: exact ? '' : 'is-unrecorded',
|
|
575
|
-
text: exact ?
|
|
689
|
+
text: exact ? name : `${name} — 未接続(変数の版が一致しません)`,
|
|
576
690
|
}));
|
|
577
691
|
}
|
|
578
692
|
return list;
|
|
@@ -581,16 +695,32 @@ function renderReferenceList(doc, refs, variableMap, label) {
|
|
|
581
695
|
function modelCard(doc, record, variableMap) {
|
|
582
696
|
const definition = recordDefinition(record);
|
|
583
697
|
const card = makeElement(doc, 'article', { className: 'bb-fov-model-card' });
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
698
|
+
// The stored scope is ids and a registration time, not the reader's "where it applies"; the
|
|
699
|
+
// record's own words go in front, and the stored scope waits in the details.
|
|
700
|
+
const applicability = definition.applicability ?? definition.scope;
|
|
701
|
+
const registered = isObject(applicability) ? applicability : {};
|
|
702
|
+
const subjects = firstValue(registered, 'subjectIds', 'subject_ids');
|
|
703
|
+
const applicabilityText = formatAny(firstValue(detailsFor(record), 'applicability_text', 'applicabilityText'))
|
|
704
|
+
?? (isObject(applicability) ? null : formatAny(applicability));
|
|
705
|
+
card.append(makeElement(doc, 'h3', { text: formatAny(definition.meaning) ?? UNNAMED_RECORD }));
|
|
706
|
+
card.append(makeElement(doc, 'p', { className: 'bb-fov-status', text: certaintyText(definition) }));
|
|
587
707
|
card.append(workspaceDefinition(doc, [
|
|
588
708
|
['関係', formatAny(definition.relationship)],
|
|
589
|
-
['適用範囲',
|
|
709
|
+
['適用範囲', applicabilityText ?? { text: '範囲の説明は記録されていません', className: 'is-unrecorded' }],
|
|
590
710
|
['不確かさ', formatAny(definition.uncertainty)],
|
|
591
711
|
['入力変数', renderReferenceList(doc, definition.inputVariableRefs, variableMap, '入力変数')],
|
|
592
712
|
['出力変数', renderReferenceList(doc, definition.outputVariableRefs, variableMap, '出力変数')],
|
|
593
713
|
]));
|
|
714
|
+
card.append(recordDetailsElement(doc, [
|
|
715
|
+
['状態の内訳', statusText(definition)],
|
|
716
|
+
['記録ID', definition.id],
|
|
717
|
+
['版', definition.revision],
|
|
718
|
+
['digest', record.digest],
|
|
719
|
+
['登録上の対象', Array.isArray(subjects) && subjects.length > 0 ? subjects.join('、') : null],
|
|
720
|
+
['登録上の開始日時', formatValidity(firstValue(registered, 'validFrom', 'valid_from'), firstValue(registered, 'validUntil', 'valid_until'))],
|
|
721
|
+
['入力変数の参照', referenceLabels(definition.inputVariableRefs)],
|
|
722
|
+
['出力変数の参照', referenceLabels(definition.outputVariableRefs)],
|
|
723
|
+
]));
|
|
594
724
|
return card;
|
|
595
725
|
}
|
|
596
726
|
|
|
@@ -712,7 +842,7 @@ export function renderFoundationOverview(root, state, {
|
|
|
712
842
|
surface.append(workspacePageHeader(doc, {
|
|
713
843
|
crumbs: ['Brainbase', '目的と現状'],
|
|
714
844
|
title: '目的と現状',
|
|
715
|
-
lead: '
|
|
845
|
+
lead: '選択したプロジェクトの目的、哲学、世界モデルを、記録された内容と未確認の点を分けて読みます。',
|
|
716
846
|
source: `正本: ${scopeId}`,
|
|
717
847
|
}));
|
|
718
848
|
}
|
|
@@ -734,7 +864,7 @@ export function renderFoundationOverview(root, state, {
|
|
|
734
864
|
return workspaceDisclosure(doc, { title, help, content, open: viewState[key] ?? initialOpen, onToggle: open => { viewState[key] = open; } });
|
|
735
865
|
};
|
|
736
866
|
surface.append(fold('哲学', '判断の前提となる考え方です。登録された本文と、判断への適用範囲を分けて表示します。', renderPhilosophies(doc, catalog, recordContexts), true));
|
|
737
|
-
surface.append(fold('世界モデル', '
|
|
867
|
+
surface.append(fold('世界モデル', '何をすると結果が変わるか、という仮説。検証済みか仮説かを先に示し、ほかの状態や記録の番号は各モデルの「記録の詳細」で確認できます。', renderModels(doc, catalog, viewState, callbacks, recordContexts), false));
|
|
738
868
|
root.append(surface);
|
|
739
869
|
renderLoadingState(doc, root, viewState, false, onLoading);
|
|
740
870
|
return surface;
|
|
@@ -751,10 +881,11 @@ export function renderFoundationCollectionOverview(root, sources, {
|
|
|
751
881
|
} = {}) {
|
|
752
882
|
const doc = getDocument(explicitDocument);
|
|
753
883
|
root.replaceChildren();
|
|
754
|
-
const catalog = { objectives: [], variables: [], models: [], philosophies: [] };
|
|
884
|
+
const catalog = { objectives: [], variables: [], models: [], philosophies: [], philosophies_without_applicability: [] };
|
|
755
885
|
const contexts = new WeakMap();
|
|
756
886
|
const seen = new Map();
|
|
757
887
|
let ready = 0;
|
|
888
|
+
let unscopedUnknown = false;
|
|
758
889
|
for (const source of sources) {
|
|
759
890
|
if (source.state.status !== 'ready') {
|
|
760
891
|
if (source.state.status === 'loading') {
|
|
@@ -768,8 +899,9 @@ export function renderFoundationCollectionOverview(root, sources, {
|
|
|
768
899
|
const value = normalizeFoundationCatalog(source.state.catalog, source.scopeId);
|
|
769
900
|
ready++;
|
|
770
901
|
const variableMap = new Map(value.variables.map((record) => [foundationKey(recordDefinition(record)), record]));
|
|
771
|
-
|
|
772
|
-
|
|
902
|
+
if (!value.philosophies_without_applicability) unscopedUnknown = true;
|
|
903
|
+
for (const kind of ['objectives', 'models', 'philosophies', 'philosophies_without_applicability']) {
|
|
904
|
+
for (const original of value[kind] ?? []) {
|
|
773
905
|
const definition = original.definition ?? original;
|
|
774
906
|
const scope = original.scope ?? original.currentScope ?? definition.scope;
|
|
775
907
|
// Scope-less/project records remain tied to their authenticated catalog.
|
|
@@ -790,6 +922,8 @@ export function renderFoundationCollectionOverview(root, sources, {
|
|
|
790
922
|
}
|
|
791
923
|
}
|
|
792
924
|
}
|
|
925
|
+
// A host that did not report unscoped philosophies leaves the absence unknown.
|
|
926
|
+
if (unscopedUnknown && catalog.philosophies_without_applicability.length === 0) delete catalog.philosophies_without_applicability;
|
|
793
927
|
if (ready) {
|
|
794
928
|
const mount = makeElement(doc, 'div');
|
|
795
929
|
renderFoundationOverview(mount, { status: 'ready', catalog }, {
|
package/ui/graph-own-share.js
CHANGED
|
@@ -76,12 +76,25 @@ export function graphOwnShareSource(option, { fetcher } = {}) {
|
|
|
76
76
|
});
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
/** What the owner sent:
|
|
79
|
+
/** What the owner sent, in words: when it was sent. The snapshot ID and digest are in ownShareHandoverRows. */
|
|
80
80
|
export function ownShareHandoverText(handover) {
|
|
81
81
|
if (!isRecord(handover) || !textOrNull(handover.graphId)) return null;
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
82
|
+
return `${formatDateTime(handover.createdAt)} に手元のOSSから送った束`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** The snapshot ID, where it is kept and its digest, for the record details only. */
|
|
86
|
+
export function ownShareHandoverRows(handover) {
|
|
87
|
+
if (!isRecord(handover) || !textOrNull(handover.graphId)) return null;
|
|
88
|
+
return [
|
|
89
|
+
['束ID', handover.graphId],
|
|
90
|
+
['置き場所', textOrNull(handover.projectCode)],
|
|
91
|
+
['digest', textOrNull(handover.digest) ?? '未確認'],
|
|
92
|
+
];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function handoverDetails(doc, handover) {
|
|
96
|
+
const rows = ownShareHandoverRows(handover);
|
|
97
|
+
return rows ? recordDetails(doc, rows) : null;
|
|
85
98
|
}
|
|
86
99
|
|
|
87
100
|
/**
|
|
@@ -113,6 +126,8 @@ export function ownShareSection(doc, { label, lead = OWN_LEAD, handover, childre
|
|
|
113
126
|
const sent = ownShareHandoverText(handover);
|
|
114
127
|
if (sent) facts.append(makeElement(doc, 'span', { text: sent }));
|
|
115
128
|
section.append(facts);
|
|
129
|
+
const sentDetails = handoverDetails(doc, handover);
|
|
130
|
+
if (sentDetails) section.append(sentDetails);
|
|
116
131
|
for (const child of children) if (child) section.append(child);
|
|
117
132
|
return section;
|
|
118
133
|
}
|
|
@@ -250,7 +265,7 @@ function personText(person) {
|
|
|
250
265
|
}
|
|
251
266
|
|
|
252
267
|
function criterionText(criterion) {
|
|
253
|
-
const variable = textOrNull(criterion.variable?.meaning) ??
|
|
268
|
+
const variable = textOrNull(criterion.variable?.meaning) ?? '名前の記録がない変数';
|
|
254
269
|
if (criterion.target === null || criterion.target === undefined) return `${variable}(目標値なし)`;
|
|
255
270
|
const value = `${String(criterion.target)}${textOrNull(criterion.variable?.unit) ? ` ${criterion.variable.unit}` : ''}`;
|
|
256
271
|
if (criterion.operator === 'at_least') return `${variable}: ${value}以上`;
|
|
@@ -295,7 +310,7 @@ export function createOwnObjectivesView({
|
|
|
295
310
|
function detail(objective) {
|
|
296
311
|
const wrap = makeElement(doc, 'div', { className: 'bb-graph-own-detail', attrs: { 'aria-label': '選んだ目的(引き継いだ自分の分)' } });
|
|
297
312
|
wrap.append(
|
|
298
|
-
workspaceRailHead(doc, { kicker: '目的(引き継いだ自分の分)', title: objective.meaning
|
|
313
|
+
workspaceRailHead(doc, { kicker: '目的(引き継いだ自分の分)', title: objective.meaning }),
|
|
299
314
|
closeButton(doc, () => controller.select(null)),
|
|
300
315
|
);
|
|
301
316
|
const criteria = makeElement(doc, 'ul', { className: 'bb-graph-plain-list' });
|
|
@@ -312,7 +327,11 @@ export function createOwnObjectivesView({
|
|
|
312
327
|
['状態', ownLabel(ADOPTION_LABELS, objective.adoptionState) ?? textOrNull(objective.adoptionState)],
|
|
313
328
|
['公開範囲(手元での設定)', ownLabel(VISIBILITY_LABELS, objective.visibility) ?? textOrNull(objective.visibility)],
|
|
314
329
|
]),
|
|
315
|
-
recordDetails(doc, [
|
|
330
|
+
recordDetails(doc, [
|
|
331
|
+
['目的ID', objective.id],
|
|
332
|
+
['版', objective.revision],
|
|
333
|
+
['基準変数のID', objective.criteria.map((criterion) => textOrNull(criterion.variable?.id)).filter(Boolean).join('、') || null],
|
|
334
|
+
]),
|
|
316
335
|
],
|
|
317
336
|
}));
|
|
318
337
|
return wrap;
|
|
@@ -344,7 +363,7 @@ export function createOwnObjectivesView({
|
|
|
344
363
|
selected: objective.id === state.selectedId,
|
|
345
364
|
onSelect: (id) => controller.select(id),
|
|
346
365
|
cells: [
|
|
347
|
-
{ primary: objective.meaning
|
|
366
|
+
{ primary: objective.meaning },
|
|
348
367
|
ownLabel(ADOPTION_LABELS, objective.adoptionState) ?? String(objective.adoptionState ?? ''),
|
|
349
368
|
periodText(objective.evaluationPeriod),
|
|
350
369
|
personText(objective.accountable) ?? { text: '未登録', className: 'is-unresolved' },
|
|
@@ -365,7 +384,7 @@ export function createOwnObjectivesView({
|
|
|
365
384
|
if (compact) {
|
|
366
385
|
const facts = makeElement(doc, 'p', { className: 'bb-graph-own-facts', text: '読み取りのみ' });
|
|
367
386
|
const refs = makeElement(doc, 'details');
|
|
368
|
-
refs.append(makeElement(doc, 'summary', { text: '出典' }), makeElement(doc, 'p', { text: ownShareHandoverText(handover) ?? (state.list.state === 'not_handed_over' ? 'まだ引き継いでいません。' : '出典は確認できません。') }));
|
|
387
|
+
refs.append(makeElement(doc, 'summary', { text: '出典' }), makeElement(doc, 'p', { text: ownShareHandoverText(handover) ?? (state.list.state === 'not_handed_over' ? 'まだ引き継いでいません。' : '出典は確認できません。') }), handoverDetails(doc, handover));
|
|
369
388
|
root.replaceChildren(facts, ...body(), refs);
|
|
370
389
|
}
|
|
371
390
|
else root.replaceChildren(ownShareSection(doc, { label: source.label, lead: OBJECTIVES_LEAD, handover, children: body(), className: 'bb-graph-own-objectives' }));
|
package/ui/outcome-knowledge.css
CHANGED
|
@@ -287,6 +287,7 @@
|
|
|
287
287
|
-webkit-line-clamp: 2;
|
|
288
288
|
}
|
|
289
289
|
.knowledge-outcome .knowledge-row-meta { display: flex; flex-wrap: wrap; gap: .15rem .55rem; min-width: 0; }
|
|
290
|
+
.knowledge-outcome .knowledge-row-project { color: var(--knowledge-ink); font-size: .68rem; font-weight: 600; overflow-wrap: anywhere; }
|
|
290
291
|
.knowledge-outcome .knowledge-row-type,
|
|
291
292
|
.knowledge-outcome .knowledge-row-scope,
|
|
292
293
|
.knowledge-outcome .knowledge-row-state { color: var(--knowledge-muted); font-size: .68rem; white-space: nowrap; }
|
|
@@ -451,3 +452,20 @@
|
|
|
451
452
|
.knowledge-outcome .knowledge-skeleton-line { animation: none; }
|
|
452
453
|
.knowledge-outcome .knowledge-button { transition: none; }
|
|
453
454
|
}
|
|
455
|
+
|
|
456
|
+
.knowledge-outcome .knowledge-project-filter {
|
|
457
|
+
display: grid;
|
|
458
|
+
gap: .25rem;
|
|
459
|
+
min-width: min(16rem, 100%);
|
|
460
|
+
color: var(--knowledge-muted);
|
|
461
|
+
font-size: .72rem;
|
|
462
|
+
}
|
|
463
|
+
.knowledge-outcome .knowledge-list-truncated { margin-top: .25rem; color: var(--knowledge-muted); font-size: .72rem; }
|
|
464
|
+
.knowledge-outcome .knowledge-overview-owner {
|
|
465
|
+
display: grid;
|
|
466
|
+
gap: .5rem;
|
|
467
|
+
margin-top: .75rem;
|
|
468
|
+
border-top: 1px solid var(--knowledge-line);
|
|
469
|
+
padding-top: .75rem;
|
|
470
|
+
}
|
|
471
|
+
.knowledge-outcome .knowledge-overview-project { color: var(--knowledge-muted); font-size: .78rem; }
|