@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,731 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Contract for composing versioned Judgment DAGs into one parent judgment.
|
|
3
|
+
*
|
|
4
|
+
* This module intentionally has no persistence or host adapter dependency.
|
|
5
|
+
* It validates the composition, freezes the evaluation boundary, and records
|
|
6
|
+
* the result of each child without changing the J0 single-DAG run artifact.
|
|
7
|
+
*/
|
|
8
|
+
import { JUDGMENT_DAG_SCOPE_TYPES } from './judgment-dag-core.js';
|
|
9
|
+
export const JUDGMENT_DAG_COMPOSITION_VERSION = 'judgment-dag-composition.v1';
|
|
10
|
+
export const JUDGMENT_DAG_COMPOSITION_STATUSES = Object.freeze([
|
|
11
|
+
'completed',
|
|
12
|
+
'failed',
|
|
13
|
+
'held'
|
|
14
|
+
]);
|
|
15
|
+
export const JUDGMENT_DAG_SUBDAG_STATUSES = Object.freeze([
|
|
16
|
+
'completed',
|
|
17
|
+
'failed',
|
|
18
|
+
'held'
|
|
19
|
+
]);
|
|
20
|
+
export class JudgmentDAGCompositionError extends Error {
|
|
21
|
+
code;
|
|
22
|
+
invocation_id;
|
|
23
|
+
dependency_id;
|
|
24
|
+
cycle;
|
|
25
|
+
constructor(code, message, details = {}) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = 'JudgmentDAGCompositionError';
|
|
28
|
+
this.code = code;
|
|
29
|
+
this.invocation_id = details.invocation_id;
|
|
30
|
+
this.dependency_id = details.dependency_id;
|
|
31
|
+
this.cycle = details.cycle;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function isRecord(value) {
|
|
35
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
36
|
+
}
|
|
37
|
+
function isPlainRecord(value) {
|
|
38
|
+
return (isRecord(value) &&
|
|
39
|
+
(Object.getPrototypeOf(value) === Object.prototype || Object.getPrototypeOf(value) === null));
|
|
40
|
+
}
|
|
41
|
+
function isSafeString(value) {
|
|
42
|
+
return typeof value === 'string' && value.trim().length > 0 && !/[\u0000-\u001F\u007F]/u.test(value);
|
|
43
|
+
}
|
|
44
|
+
function invalidContract(message) {
|
|
45
|
+
throw new JudgmentDAGCompositionError('invalid_contract', message);
|
|
46
|
+
}
|
|
47
|
+
function requireRecord(value, path) {
|
|
48
|
+
if (!isPlainRecord(value)) {
|
|
49
|
+
invalidContract(`${path} must be a plain object`);
|
|
50
|
+
}
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
function requireExactKeys(value, allowed, path) {
|
|
54
|
+
for (const key of Object.keys(value)) {
|
|
55
|
+
if (!allowed.includes(key)) {
|
|
56
|
+
invalidContract(`${path}.${key} is not an allowed contract field`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function requireString(value, path) {
|
|
61
|
+
if (!isSafeString(value)) {
|
|
62
|
+
invalidContract(`${path} must be a non-empty string without control characters`);
|
|
63
|
+
}
|
|
64
|
+
return value;
|
|
65
|
+
}
|
|
66
|
+
function requireArray(value, path) {
|
|
67
|
+
if (!Array.isArray(value)) {
|
|
68
|
+
invalidContract(`${path} must be an array`);
|
|
69
|
+
}
|
|
70
|
+
return value;
|
|
71
|
+
}
|
|
72
|
+
function snapshotJSON(value, path, active = new Set()) {
|
|
73
|
+
if (value === null || typeof value === 'string' || typeof value === 'boolean') {
|
|
74
|
+
return value;
|
|
75
|
+
}
|
|
76
|
+
if (typeof value === 'number') {
|
|
77
|
+
if (Number.isFinite(value)) {
|
|
78
|
+
return value;
|
|
79
|
+
}
|
|
80
|
+
invalidContract(`${path} must contain only finite JSON numbers`);
|
|
81
|
+
}
|
|
82
|
+
if (typeof value !== 'object') {
|
|
83
|
+
invalidContract(`${path} must be a JSON value`);
|
|
84
|
+
}
|
|
85
|
+
const objectValue = value;
|
|
86
|
+
if (active.has(objectValue)) {
|
|
87
|
+
invalidContract(`${path} must not contain cyclic values`);
|
|
88
|
+
}
|
|
89
|
+
active.add(objectValue);
|
|
90
|
+
try {
|
|
91
|
+
if (Array.isArray(value)) {
|
|
92
|
+
const result = [];
|
|
93
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
94
|
+
if (!(index in value)) {
|
|
95
|
+
invalidContract(`${path}[${index}] must be defined`);
|
|
96
|
+
}
|
|
97
|
+
result.push(snapshotJSON(value[index], `${path}[${index}]`, active));
|
|
98
|
+
}
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
101
|
+
if (!isPlainRecord(value)) {
|
|
102
|
+
invalidContract(`${path} must contain only plain objects`);
|
|
103
|
+
}
|
|
104
|
+
const result = {};
|
|
105
|
+
for (const key of Object.keys(value)) {
|
|
106
|
+
result[key] = snapshotJSON(value[key], `${path}.${key}`, active);
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
if (error instanceof JudgmentDAGCompositionError) {
|
|
112
|
+
throw error;
|
|
113
|
+
}
|
|
114
|
+
invalidContract(`${path} must be readable`);
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
active.delete(objectValue);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function deepFreeze(value) {
|
|
121
|
+
if (value !== null && typeof value === 'object' && !Object.isFrozen(value)) {
|
|
122
|
+
Object.freeze(value);
|
|
123
|
+
for (const child of Object.values(value)) {
|
|
124
|
+
deepFreeze(child);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
function parseScope(value, path) {
|
|
130
|
+
const scope = requireRecord(value, path);
|
|
131
|
+
requireExactKeys(scope, ['type', 'id'], path);
|
|
132
|
+
if (!JUDGMENT_DAG_SCOPE_TYPES.includes(scope.type)) {
|
|
133
|
+
invalidContract(`${path}.type must be a supported scope type`);
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
type: scope.type,
|
|
137
|
+
id: requireString(scope.id, `${path}.id`)
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function parseVersionReference(value, path) {
|
|
141
|
+
const reference = requireRecord(value, path);
|
|
142
|
+
requireExactKeys(reference, ['id', 'version'], path);
|
|
143
|
+
return {
|
|
144
|
+
id: requireString(reference.id, `${path}.id`),
|
|
145
|
+
version: requireString(reference.version, `${path}.version`)
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const SNAPSHOT_ID_PATTERN = /^sha256:[0-9a-f]{64}$/u;
|
|
149
|
+
function parseProblemSnapshotReference(value, path) {
|
|
150
|
+
const reference = requireRecord(value, path);
|
|
151
|
+
requireExactKeys(reference, ['snapshot_id', 'problem_id', 'revision'], path);
|
|
152
|
+
const snapshotId = requireString(reference.snapshot_id, `${path}.snapshot_id`);
|
|
153
|
+
if (!SNAPSHOT_ID_PATTERN.test(snapshotId)) {
|
|
154
|
+
invalidContract(`${path}.snapshot_id must be sha256:<64 lowercase hex>`);
|
|
155
|
+
}
|
|
156
|
+
const problemId = requireString(reference.problem_id, `${path}.problem_id`);
|
|
157
|
+
const revision = requireString(reference.revision, `${path}.revision`);
|
|
158
|
+
if (!/^[1-9]\d*$/u.test(revision)) {
|
|
159
|
+
invalidContract(`${path}.revision must be a positive decimal revision`);
|
|
160
|
+
}
|
|
161
|
+
return deepFreeze({
|
|
162
|
+
snapshot_id: snapshotId,
|
|
163
|
+
problem_id: problemId,
|
|
164
|
+
revision
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
function parseStringList(value, path) {
|
|
168
|
+
const values = requireArray(value, path).map((entry, index) => requireString(entry, `${path}[${index}]`));
|
|
169
|
+
if (new Set(values).size !== values.length) {
|
|
170
|
+
invalidContract(`${path} must not contain duplicates`);
|
|
171
|
+
}
|
|
172
|
+
return [...values].sort();
|
|
173
|
+
}
|
|
174
|
+
function parseChild(value, index) {
|
|
175
|
+
const path = `children[${index}]`;
|
|
176
|
+
const child = requireRecord(value, path);
|
|
177
|
+
requireExactKeys(child, [
|
|
178
|
+
'invocation_id',
|
|
179
|
+
'dag',
|
|
180
|
+
'question',
|
|
181
|
+
'input_contract',
|
|
182
|
+
'output_contract',
|
|
183
|
+
'required_capabilities',
|
|
184
|
+
'depends_on'
|
|
185
|
+
], path);
|
|
186
|
+
return {
|
|
187
|
+
invocation_id: requireString(child.invocation_id, `${path}.invocation_id`),
|
|
188
|
+
dag: parseVersionReference(child.dag, `${path}.dag`),
|
|
189
|
+
question: requireString(child.question, `${path}.question`),
|
|
190
|
+
input_contract: requireString(child.input_contract, `${path}.input_contract`),
|
|
191
|
+
output_contract: requireString(child.output_contract, `${path}.output_contract`),
|
|
192
|
+
required_capabilities: parseStringList(child.required_capabilities, `${path}.required_capabilities`),
|
|
193
|
+
depends_on: parseStringList(child.depends_on, `${path}.depends_on`)
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
function parseComposition(value) {
|
|
197
|
+
const composition = requireRecord(value, 'composition');
|
|
198
|
+
requireExactKeys(composition, ['composition_id', 'composition_version', 'scope', 'parent_dag', 'children'], 'composition');
|
|
199
|
+
const children = requireArray(composition.children, 'composition.children')
|
|
200
|
+
.map((child, index) => parseChild(child, index))
|
|
201
|
+
.sort((left, right) => left.invocation_id.localeCompare(right.invocation_id));
|
|
202
|
+
return {
|
|
203
|
+
composition_id: requireString(composition.composition_id, 'composition.composition_id'),
|
|
204
|
+
composition_version: requireString(composition.composition_version, 'composition.composition_version'),
|
|
205
|
+
scope: parseScope(composition.scope, 'composition.scope'),
|
|
206
|
+
parent_dag: parseVersionReference(composition.parent_dag, 'composition.parent_dag'),
|
|
207
|
+
children
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
function equalScope(left, right) {
|
|
211
|
+
return left.type === right.type && left.id === right.id;
|
|
212
|
+
}
|
|
213
|
+
function equalVersionReference(left, right) {
|
|
214
|
+
return left.id === right.id && left.version === right.version;
|
|
215
|
+
}
|
|
216
|
+
function equalProblemSnapshotReference(left, right) {
|
|
217
|
+
return left.snapshot_id === right.snapshot_id &&
|
|
218
|
+
left.problem_id === right.problem_id &&
|
|
219
|
+
left.revision === right.revision;
|
|
220
|
+
}
|
|
221
|
+
function validateTopology(composition) {
|
|
222
|
+
const childById = new Map();
|
|
223
|
+
for (const child of composition.children) {
|
|
224
|
+
if (childById.has(child.invocation_id)) {
|
|
225
|
+
throw new JudgmentDAGCompositionError('duplicate_invocation', `composition contains duplicate invocation ${child.invocation_id}`, { invocation_id: child.invocation_id });
|
|
226
|
+
}
|
|
227
|
+
childById.set(child.invocation_id, child);
|
|
228
|
+
}
|
|
229
|
+
const remaining = new Map();
|
|
230
|
+
const dependents = new Map();
|
|
231
|
+
for (const child of composition.children) {
|
|
232
|
+
remaining.set(child.invocation_id, child.depends_on.length);
|
|
233
|
+
dependents.set(child.invocation_id, []);
|
|
234
|
+
}
|
|
235
|
+
for (const child of composition.children) {
|
|
236
|
+
for (const dependencyId of child.depends_on) {
|
|
237
|
+
if (!childById.has(dependencyId)) {
|
|
238
|
+
throw new JudgmentDAGCompositionError('missing_dependency', `invocation ${child.invocation_id} depends on missing invocation ${dependencyId}`, { invocation_id: child.invocation_id, dependency_id: dependencyId });
|
|
239
|
+
}
|
|
240
|
+
dependents.get(dependencyId)?.push(child.invocation_id);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
const ready = composition.children
|
|
244
|
+
.filter((child) => remaining.get(child.invocation_id) === 0)
|
|
245
|
+
.map((child) => child.invocation_id)
|
|
246
|
+
.sort();
|
|
247
|
+
const executionOrder = [];
|
|
248
|
+
while (ready.length > 0) {
|
|
249
|
+
const invocationId = ready.shift();
|
|
250
|
+
if (invocationId === undefined) {
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
executionOrder.push(invocationId);
|
|
254
|
+
for (const dependentId of [...(dependents.get(invocationId) ?? [])].sort()) {
|
|
255
|
+
const next = (remaining.get(dependentId) ?? 0) - 1;
|
|
256
|
+
remaining.set(dependentId, next);
|
|
257
|
+
if (next === 0) {
|
|
258
|
+
ready.push(dependentId);
|
|
259
|
+
ready.sort();
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (executionOrder.length !== composition.children.length) {
|
|
264
|
+
const cycle = composition.children
|
|
265
|
+
.filter((child) => (remaining.get(child.invocation_id) ?? 0) > 0)
|
|
266
|
+
.map((child) => child.invocation_id)
|
|
267
|
+
.sort();
|
|
268
|
+
throw new JudgmentDAGCompositionError('cycle', `composition contains an execution dependency cycle: ${cycle.join(' -> ')}`, { cycle });
|
|
269
|
+
}
|
|
270
|
+
return {
|
|
271
|
+
valid: true,
|
|
272
|
+
composition_id: composition.composition_id,
|
|
273
|
+
composition_version: composition.composition_version,
|
|
274
|
+
execution_order: executionOrder
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
export function validateJudgmentDAGComposition(value) {
|
|
278
|
+
return validateTopology(parseComposition(value));
|
|
279
|
+
}
|
|
280
|
+
export function assertValidJudgmentDAGComposition(value) {
|
|
281
|
+
validateJudgmentDAGComposition(value);
|
|
282
|
+
}
|
|
283
|
+
export function createJudgmentDAGCompositionDefinition(value) {
|
|
284
|
+
const composition = parseComposition(value);
|
|
285
|
+
validateTopology(composition);
|
|
286
|
+
return deepFreeze(composition);
|
|
287
|
+
}
|
|
288
|
+
function parseDelegation(value, expectedScope) {
|
|
289
|
+
const delegation = requireRecord(value, 'request.delegation');
|
|
290
|
+
requireExactKeys(delegation, ['scope', 'capabilities'], 'request.delegation');
|
|
291
|
+
const scope = parseScope(delegation.scope, 'request.delegation.scope');
|
|
292
|
+
if (!equalScope(scope, expectedScope)) {
|
|
293
|
+
throw new JudgmentDAGCompositionError('scope_mismatch', 'request delegation scope must match the composition scope');
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
scope,
|
|
297
|
+
capabilities: parseStringList(delegation.capabilities, 'request.delegation.capabilities')
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
function hasCapabilities(available, required) {
|
|
301
|
+
const availableSet = new Set(available);
|
|
302
|
+
return required.every((capability) => availableSet.has(capability));
|
|
303
|
+
}
|
|
304
|
+
function parseRunReference(value, child) {
|
|
305
|
+
if (value === null) {
|
|
306
|
+
return null;
|
|
307
|
+
}
|
|
308
|
+
const reference = requireRecord(value, 'child.result.run_reference');
|
|
309
|
+
requireExactKeys(reference, ['run_id', 'dag', 'artifact_id'], 'child.result.run_reference');
|
|
310
|
+
const dag = parseVersionReference(reference.dag, 'child.result.run_reference.dag');
|
|
311
|
+
if (dag.id !== child.dag.id || dag.version !== child.dag.version) {
|
|
312
|
+
throw new JudgmentDAGCompositionError('dag_mismatch', `child ${child.invocation_id} returned a run for a different DAG`, { invocation_id: child.invocation_id });
|
|
313
|
+
}
|
|
314
|
+
const runId = requireString(reference.run_id, 'child.result.run_reference.run_id');
|
|
315
|
+
const artifactId = reference.artifact_id === undefined
|
|
316
|
+
? undefined
|
|
317
|
+
: requireString(reference.artifact_id, 'child.result.run_reference.artifact_id');
|
|
318
|
+
if (artifactId !== undefined && !/^sha256:[0-9a-f]{64}$/u.test(artifactId)) {
|
|
319
|
+
invalidContract('child.result.run_reference.artifact_id must be sha256:<64 lowercase hex>');
|
|
320
|
+
}
|
|
321
|
+
return {
|
|
322
|
+
run_id: runId,
|
|
323
|
+
dag,
|
|
324
|
+
...(artifactId === undefined
|
|
325
|
+
? {}
|
|
326
|
+
: { artifact_id: artifactId })
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
function parseEvidence(value) {
|
|
330
|
+
return requireArray(value, 'child.result.evidence').map((entry, index) => {
|
|
331
|
+
const path = `child.result.evidence[${index}]`;
|
|
332
|
+
const evidence = requireRecord(entry, path);
|
|
333
|
+
requireExactKeys(evidence, ['kind', 'id', 'revision'], path);
|
|
334
|
+
const baseResult = {
|
|
335
|
+
kind: requireString(evidence.kind, `${path}.kind`),
|
|
336
|
+
id: requireString(evidence.id, `${path}.id`)
|
|
337
|
+
};
|
|
338
|
+
const revision = evidence.revision === undefined
|
|
339
|
+
? undefined
|
|
340
|
+
: requireString(evidence.revision, `${path}.revision`);
|
|
341
|
+
return revision === undefined ? baseResult : { ...baseResult, revision };
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
function normalizeChildResult(value, child) {
|
|
345
|
+
const result = requireRecord(value, `child ${child.invocation_id} result`);
|
|
346
|
+
requireExactKeys(result, [
|
|
347
|
+
'status',
|
|
348
|
+
'input_contract',
|
|
349
|
+
'output_contract',
|
|
350
|
+
'run_reference',
|
|
351
|
+
'conclusion',
|
|
352
|
+
'evidence',
|
|
353
|
+
'applicability',
|
|
354
|
+
'uncertainty',
|
|
355
|
+
'reason'
|
|
356
|
+
], `child ${child.invocation_id} result`);
|
|
357
|
+
if (!JUDGMENT_DAG_SUBDAG_STATUSES.includes(result.status)) {
|
|
358
|
+
throw new JudgmentDAGCompositionError('invalid_child_result', `child ${child.invocation_id} returned an unsupported status`, { invocation_id: child.invocation_id });
|
|
359
|
+
}
|
|
360
|
+
if (result.input_contract !== child.input_contract || result.output_contract !== child.output_contract) {
|
|
361
|
+
throw new JudgmentDAGCompositionError('contract_mismatch', `child ${child.invocation_id} returned a different input or output contract`, { invocation_id: child.invocation_id });
|
|
362
|
+
}
|
|
363
|
+
const runReference = parseRunReference(result.run_reference, child);
|
|
364
|
+
if (result.status === 'completed' && runReference === null) {
|
|
365
|
+
throw new JudgmentDAGCompositionError('invalid_child_result', `completed child ${child.invocation_id} must include a run reference`, { invocation_id: child.invocation_id });
|
|
366
|
+
}
|
|
367
|
+
const normalized = {
|
|
368
|
+
status: result.status,
|
|
369
|
+
input_contract: child.input_contract,
|
|
370
|
+
output_contract: child.output_contract,
|
|
371
|
+
run_reference: runReference,
|
|
372
|
+
conclusion: snapshotJSON(result.conclusion, `child ${child.invocation_id} result.conclusion`),
|
|
373
|
+
evidence: parseEvidence(result.evidence),
|
|
374
|
+
applicability: snapshotJSON(result.applicability, `child ${child.invocation_id} result.applicability`),
|
|
375
|
+
uncertainty: snapshotJSON(result.uncertainty, `child ${child.invocation_id} result.uncertainty`)
|
|
376
|
+
};
|
|
377
|
+
if (result.reason === undefined) {
|
|
378
|
+
return deepFreeze(normalized);
|
|
379
|
+
}
|
|
380
|
+
return deepFreeze({
|
|
381
|
+
...normalized,
|
|
382
|
+
reason: requireString(result.reason, `child ${child.invocation_id} result.reason`)
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
function incompleteResult(status, child, reason) {
|
|
386
|
+
return deepFreeze({
|
|
387
|
+
status,
|
|
388
|
+
input_contract: child.input_contract,
|
|
389
|
+
output_contract: child.output_contract,
|
|
390
|
+
run_reference: null,
|
|
391
|
+
conclusion: null,
|
|
392
|
+
evidence: [],
|
|
393
|
+
applicability: { status: 'not_run' },
|
|
394
|
+
uncertainty: { status: 'blocked' },
|
|
395
|
+
reason
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
function parsePort(value) {
|
|
399
|
+
const port = requireRecord(value, 'request.port');
|
|
400
|
+
requireExactKeys(port, ['execute'], 'request.port');
|
|
401
|
+
if (typeof port.execute !== 'function') {
|
|
402
|
+
invalidContract('request.port.execute must be a function');
|
|
403
|
+
}
|
|
404
|
+
return port;
|
|
405
|
+
}
|
|
406
|
+
function parseSnapshotReader(value) {
|
|
407
|
+
const reader = requireRecord(value, 'request.snapshot_reader');
|
|
408
|
+
requireExactKeys(reader, ['read'], 'request.snapshot_reader');
|
|
409
|
+
if (typeof reader.read !== 'function') {
|
|
410
|
+
invalidContract('request.snapshot_reader.read must be a function');
|
|
411
|
+
}
|
|
412
|
+
return reader;
|
|
413
|
+
}
|
|
414
|
+
function parseDAGResolver(value) {
|
|
415
|
+
const resolver = requireRecord(value, 'request.dag_resolver');
|
|
416
|
+
requireExactKeys(resolver, ['resolve'], 'request.dag_resolver');
|
|
417
|
+
if (typeof resolver.resolve !== 'function') {
|
|
418
|
+
invalidContract('request.dag_resolver.resolve must be a function');
|
|
419
|
+
}
|
|
420
|
+
return resolver;
|
|
421
|
+
}
|
|
422
|
+
function parseContractValidator(value) {
|
|
423
|
+
const validator = requireRecord(value, 'request.contract_validator');
|
|
424
|
+
requireExactKeys(validator, ['validate'], 'request.contract_validator');
|
|
425
|
+
if (typeof validator.validate !== 'function') {
|
|
426
|
+
invalidContract('request.contract_validator.validate must be a function');
|
|
427
|
+
}
|
|
428
|
+
return validator;
|
|
429
|
+
}
|
|
430
|
+
function parseArtifactReader(value) {
|
|
431
|
+
const reader = requireRecord(value, 'request.artifact_reader');
|
|
432
|
+
requireExactKeys(reader, ['read'], 'request.artifact_reader');
|
|
433
|
+
if (typeof reader.read !== 'function') {
|
|
434
|
+
invalidContract('request.artifact_reader.read must be a function');
|
|
435
|
+
}
|
|
436
|
+
return reader;
|
|
437
|
+
}
|
|
438
|
+
function parseRequest(value) {
|
|
439
|
+
const request = requireRecord(value, 'request');
|
|
440
|
+
requireExactKeys(request, [
|
|
441
|
+
'run_id',
|
|
442
|
+
'composition',
|
|
443
|
+
'question',
|
|
444
|
+
'input',
|
|
445
|
+
'problem_snapshot',
|
|
446
|
+
'fixed_conditions',
|
|
447
|
+
'delegation',
|
|
448
|
+
'snapshot_reader',
|
|
449
|
+
'dag_resolver',
|
|
450
|
+
'contract_validator',
|
|
451
|
+
'artifact_reader',
|
|
452
|
+
'port'
|
|
453
|
+
], 'request');
|
|
454
|
+
const composition = createJudgmentDAGCompositionDefinition(request.composition);
|
|
455
|
+
const validation = validateTopology(composition);
|
|
456
|
+
const artifactReader = request.artifact_reader === undefined
|
|
457
|
+
? undefined
|
|
458
|
+
: parseArtifactReader(request.artifact_reader);
|
|
459
|
+
return {
|
|
460
|
+
run_id: requireString(request.run_id, 'request.run_id'),
|
|
461
|
+
composition,
|
|
462
|
+
question: requireString(request.question, 'request.question'),
|
|
463
|
+
input: snapshotJSON(request.input, 'request.input'),
|
|
464
|
+
problem_snapshot: parseProblemSnapshotReference(request.problem_snapshot, 'request.problem_snapshot'),
|
|
465
|
+
fixed_conditions: snapshotJSON(request.fixed_conditions, 'request.fixed_conditions'),
|
|
466
|
+
delegation: parseDelegation(request.delegation, composition.scope),
|
|
467
|
+
snapshot_reader: parseSnapshotReader(request.snapshot_reader),
|
|
468
|
+
dag_resolver: parseDAGResolver(request.dag_resolver),
|
|
469
|
+
contract_validator: parseContractValidator(request.contract_validator),
|
|
470
|
+
...(artifactReader === undefined ? {} : { artifact_reader: artifactReader }),
|
|
471
|
+
port: parsePort(request.port),
|
|
472
|
+
validation
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
function parseVersionResolution(value, path) {
|
|
476
|
+
const resolution = requireRecord(value, path);
|
|
477
|
+
requireExactKeys(resolution, ['dag', 'input_contract', 'output_contract'], path);
|
|
478
|
+
return deepFreeze({
|
|
479
|
+
dag: parseVersionReference(resolution.dag, `${path}.dag`),
|
|
480
|
+
input_contract: requireString(resolution.input_contract, `${path}.input_contract`),
|
|
481
|
+
output_contract: requireString(resolution.output_contract, `${path}.output_contract`)
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
async function resolveDAGReferences(parsed) {
|
|
485
|
+
const resolve = async (role, dag, invocationId) => {
|
|
486
|
+
let rawResolution;
|
|
487
|
+
try {
|
|
488
|
+
rawResolution = await parsed.dag_resolver.resolve({
|
|
489
|
+
role,
|
|
490
|
+
...(invocationId === undefined ? {} : { invocation_id: invocationId }),
|
|
491
|
+
dag
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
catch {
|
|
495
|
+
throw new JudgmentDAGCompositionError('dag_unavailable', `${role} DAG ${dag.id}@${dag.version} could not be resolved`, invocationId === undefined ? {} : { invocation_id: invocationId });
|
|
496
|
+
}
|
|
497
|
+
try {
|
|
498
|
+
return parseVersionResolution(rawResolution, `${role} DAG resolution`);
|
|
499
|
+
}
|
|
500
|
+
catch {
|
|
501
|
+
throw new JudgmentDAGCompositionError('dag_unavailable', `${role} DAG ${dag.id}@${dag.version} returned an invalid resolution`, invocationId === undefined ? {} : { invocation_id: invocationId });
|
|
502
|
+
}
|
|
503
|
+
};
|
|
504
|
+
const parentResolution = await resolve('parent', parsed.composition.parent_dag);
|
|
505
|
+
if (!equalVersionReference(parentResolution.dag, parsed.composition.parent_dag)) {
|
|
506
|
+
throw new JudgmentDAGCompositionError('dag_mismatch', 'parent DAG resolver returned a different version');
|
|
507
|
+
}
|
|
508
|
+
for (const child of parsed.composition.children) {
|
|
509
|
+
const resolution = await resolve('child', child.dag, child.invocation_id);
|
|
510
|
+
if (!equalVersionReference(resolution.dag, child.dag)) {
|
|
511
|
+
throw new JudgmentDAGCompositionError('dag_mismatch', `child ${child.invocation_id} DAG resolver returned a different version`, { invocation_id: child.invocation_id });
|
|
512
|
+
}
|
|
513
|
+
if (resolution.input_contract !== child.input_contract ||
|
|
514
|
+
resolution.output_contract !== child.output_contract) {
|
|
515
|
+
throw new JudgmentDAGCompositionError('contract_mismatch', `child ${child.invocation_id} DAG version has a different input or output contract`, { invocation_id: child.invocation_id });
|
|
516
|
+
}
|
|
517
|
+
try {
|
|
518
|
+
await parsed.contract_validator.validate({
|
|
519
|
+
role: 'child',
|
|
520
|
+
invocation_id: child.invocation_id,
|
|
521
|
+
dag: child.dag,
|
|
522
|
+
input_contract: resolution.input_contract,
|
|
523
|
+
output_contract: resolution.output_contract
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
catch {
|
|
527
|
+
throw new JudgmentDAGCompositionError('contract_mismatch', `child ${child.invocation_id} input/output contract was rejected`, { invocation_id: child.invocation_id });
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
async function readProblemSnapshot(parsed) {
|
|
532
|
+
let rawResult;
|
|
533
|
+
try {
|
|
534
|
+
rawResult = await parsed.snapshot_reader.read({
|
|
535
|
+
mode: 'read',
|
|
536
|
+
reference_resolution: 'current',
|
|
537
|
+
reference: parsed.problem_snapshot
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
catch {
|
|
541
|
+
throw new JudgmentDAGCompositionError('snapshot_unavailable', 'the selected JudgmentProblem snapshot could not be read');
|
|
542
|
+
}
|
|
543
|
+
try {
|
|
544
|
+
const result = requireRecord(rawResult, 'snapshot_reader result');
|
|
545
|
+
requireExactKeys(result, ['reference_resolution', 'status', 'reference', 'snapshot'], 'snapshot_reader result');
|
|
546
|
+
if (result.status !== 'resolved' || result.reference_resolution !== 'current') {
|
|
547
|
+
throw new Error('snapshot is not resolved');
|
|
548
|
+
}
|
|
549
|
+
const reference = parseProblemSnapshotReference(result.reference, 'snapshot_reader result.reference');
|
|
550
|
+
if (!equalProblemSnapshotReference(reference, parsed.problem_snapshot)) {
|
|
551
|
+
throw new Error('snapshot reference does not match the requested snapshot');
|
|
552
|
+
}
|
|
553
|
+
const snapshot = snapshotJSON(result.snapshot, 'snapshot_reader result.snapshot');
|
|
554
|
+
const snapshotRecord = requireRecord(snapshot, 'snapshot_reader result.snapshot');
|
|
555
|
+
if (snapshotRecord.snapshot_version !== 'judgment-problem-snapshot.v1' ||
|
|
556
|
+
snapshotRecord.problem_id !== parsed.problem_snapshot.problem_id ||
|
|
557
|
+
snapshotRecord.revision !== parsed.problem_snapshot.revision) {
|
|
558
|
+
throw new Error('snapshot identity does not match the requested snapshot');
|
|
559
|
+
}
|
|
560
|
+
return deepFreeze(snapshot);
|
|
561
|
+
}
|
|
562
|
+
catch {
|
|
563
|
+
throw new JudgmentDAGCompositionError('snapshot_unavailable', 'the snapshot reader returned an invalid or mismatched JudgmentProblem snapshot');
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
async function verifyArtifactReadback(result, child, reader) {
|
|
567
|
+
const runReference = result.run_reference;
|
|
568
|
+
if (runReference?.artifact_id === undefined)
|
|
569
|
+
return;
|
|
570
|
+
if (reader === undefined) {
|
|
571
|
+
throw new JudgmentDAGCompositionError('artifact_readback_failed', `child ${child.invocation_id} returned an artifact without a readback adapter`, { invocation_id: child.invocation_id });
|
|
572
|
+
}
|
|
573
|
+
let rawArtifact;
|
|
574
|
+
try {
|
|
575
|
+
rawArtifact = await reader.read({
|
|
576
|
+
artifact_id: runReference.artifact_id,
|
|
577
|
+
expected_run_id: runReference.run_id,
|
|
578
|
+
expected_dag: child.dag
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
catch {
|
|
582
|
+
throw new JudgmentDAGCompositionError('artifact_readback_failed', `child ${child.invocation_id} artifact could not be read back`, { invocation_id: child.invocation_id });
|
|
583
|
+
}
|
|
584
|
+
try {
|
|
585
|
+
const artifact = requireRecord(rawArtifact, `child ${child.invocation_id} artifact`);
|
|
586
|
+
if (requireString(artifact.run_id, `child ${child.invocation_id} artifact.run_id`) !== runReference.run_id) {
|
|
587
|
+
throw new Error('artifact run identity does not match the child result');
|
|
588
|
+
}
|
|
589
|
+
const artifactDAG = requireRecord(artifact.dag, `child ${child.invocation_id} artifact.dag`);
|
|
590
|
+
const artifactDAGReference = {
|
|
591
|
+
id: requireString(artifactDAG.id, `child ${child.invocation_id} artifact.dag.id`),
|
|
592
|
+
version: requireString(artifactDAG.version, `child ${child.invocation_id} artifact.dag.version`)
|
|
593
|
+
};
|
|
594
|
+
if (!equalVersionReference(artifactDAGReference, child.dag)) {
|
|
595
|
+
throw new Error('artifact DAG identity does not match the child result');
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
catch {
|
|
599
|
+
throw new JudgmentDAGCompositionError('artifact_readback_failed', `child ${child.invocation_id} artifact readback did not match the child run`, { invocation_id: child.invocation_id });
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
function childByInvocation(composition) {
|
|
603
|
+
return new Map(composition.children.map((child) => [child.invocation_id, child]));
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Execute only the explicitly declared evaluation port for each child.
|
|
607
|
+
* Resource reservation, deploys, external sends, and arbitrary host-code
|
|
608
|
+
* isolation remain outside this OSS boundary.
|
|
609
|
+
*/
|
|
610
|
+
export async function executeJudgmentDAGComposition(request) {
|
|
611
|
+
const parsed = parseRequest(request);
|
|
612
|
+
const childrenById = childByInvocation(parsed.composition);
|
|
613
|
+
// Preflight every child before the first port call. A later capability
|
|
614
|
+
// violation therefore cannot leave an earlier child partially evaluated.
|
|
615
|
+
for (const child of parsed.composition.children) {
|
|
616
|
+
if (!hasCapabilities(parsed.delegation.capabilities, child.required_capabilities)) {
|
|
617
|
+
const missing = child.required_capabilities.filter((capability) => !parsed.delegation.capabilities.includes(capability));
|
|
618
|
+
throw new JudgmentDAGCompositionError('capability_violation', `child ${child.invocation_id} requires undelegated capabilities: ${missing.join(', ')}`, { invocation_id: child.invocation_id });
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
// Resolve the selected problem snapshot and every declared DAG version before
|
|
622
|
+
// the first evaluation port call. The snapshot adapter owns current lookup
|
|
623
|
+
// and authorization; the coordinator only accepts the exact locator and
|
|
624
|
+
// identity it was given.
|
|
625
|
+
await readProblemSnapshot(parsed);
|
|
626
|
+
await resolveDAGReferences(parsed);
|
|
627
|
+
const childRecords = new Map();
|
|
628
|
+
for (const invocationId of parsed.validation.execution_order) {
|
|
629
|
+
const child = childrenById.get(invocationId);
|
|
630
|
+
if (child === undefined) {
|
|
631
|
+
throw new JudgmentDAGCompositionError('invalid_contract', `execution order references unknown invocation ${invocationId}`, { invocation_id: invocationId });
|
|
632
|
+
}
|
|
633
|
+
const dependencyResults = child.depends_on.map((dependencyId) => {
|
|
634
|
+
const dependency = childRecords.get(dependencyId);
|
|
635
|
+
if (dependency === undefined) {
|
|
636
|
+
throw new JudgmentDAGCompositionError('invalid_contract', `dependency ${dependencyId} has not been recorded before ${invocationId}`, { invocation_id: invocationId, dependency_id: dependencyId });
|
|
637
|
+
}
|
|
638
|
+
return {
|
|
639
|
+
invocation_id: dependency.invocation_id,
|
|
640
|
+
result: dependency.result
|
|
641
|
+
};
|
|
642
|
+
});
|
|
643
|
+
const delegation = {
|
|
644
|
+
scope: parsed.delegation.scope,
|
|
645
|
+
capabilities: child.required_capabilities
|
|
646
|
+
};
|
|
647
|
+
const blockedDependency = dependencyResults.find((dependency) => dependency.result.status !== 'completed');
|
|
648
|
+
let result;
|
|
649
|
+
if (blockedDependency !== undefined) {
|
|
650
|
+
result = incompleteResult('held', child, `dependency ${blockedDependency.invocation_id} is ${blockedDependency.result.status}`);
|
|
651
|
+
}
|
|
652
|
+
else {
|
|
653
|
+
const childRequest = deepFreeze({
|
|
654
|
+
mode: 'evaluation',
|
|
655
|
+
parent_run_id: parsed.run_id,
|
|
656
|
+
composition: {
|
|
657
|
+
id: parsed.composition.composition_id,
|
|
658
|
+
version: parsed.composition.composition_version
|
|
659
|
+
},
|
|
660
|
+
invocation_id: child.invocation_id,
|
|
661
|
+
dag: child.dag,
|
|
662
|
+
question: child.question,
|
|
663
|
+
input: parsed.input,
|
|
664
|
+
problem_snapshot: parsed.problem_snapshot,
|
|
665
|
+
fixed_conditions: parsed.fixed_conditions,
|
|
666
|
+
delegation,
|
|
667
|
+
dependency_results: dependencyResults
|
|
668
|
+
});
|
|
669
|
+
let rawResult;
|
|
670
|
+
let executorFailed = false;
|
|
671
|
+
try {
|
|
672
|
+
rawResult = await parsed.port.execute(childRequest);
|
|
673
|
+
}
|
|
674
|
+
catch {
|
|
675
|
+
executorFailed = true;
|
|
676
|
+
}
|
|
677
|
+
if (executorFailed) {
|
|
678
|
+
result = incompleteResult('failed', child, 'evaluation port failed');
|
|
679
|
+
}
|
|
680
|
+
else {
|
|
681
|
+
// Keep result validation outside the executor catch. A malformed or
|
|
682
|
+
// mismatched result is a contract error, not a successful child.
|
|
683
|
+
result = normalizeChildResult(rawResult, child);
|
|
684
|
+
await verifyArtifactReadback(result, child, parsed.artifact_reader);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
const childRecord = {
|
|
688
|
+
invocation_id: child.invocation_id,
|
|
689
|
+
dag: child.dag,
|
|
690
|
+
question: child.question,
|
|
691
|
+
input_contract: child.input_contract,
|
|
692
|
+
output_contract: child.output_contract,
|
|
693
|
+
input: parsed.input,
|
|
694
|
+
problem_snapshot: parsed.problem_snapshot,
|
|
695
|
+
fixed_conditions: parsed.fixed_conditions,
|
|
696
|
+
delegation,
|
|
697
|
+
dependency_invocation_ids: child.depends_on,
|
|
698
|
+
dependency_results: dependencyResults,
|
|
699
|
+
result
|
|
700
|
+
};
|
|
701
|
+
childRecords.set(invocationId, deepFreeze(childRecord));
|
|
702
|
+
}
|
|
703
|
+
const children = parsed.validation.execution_order.map((invocationId) => {
|
|
704
|
+
const child = childRecords.get(invocationId);
|
|
705
|
+
if (child === undefined) {
|
|
706
|
+
throw new JudgmentDAGCompositionError('invalid_contract', `child ${invocationId} was not recorded`, { invocation_id: invocationId });
|
|
707
|
+
}
|
|
708
|
+
return child;
|
|
709
|
+
});
|
|
710
|
+
const status = children.some((child) => child.result.status === 'failed')
|
|
711
|
+
? 'failed'
|
|
712
|
+
: children.some((child) => child.result.status === 'held')
|
|
713
|
+
? 'held'
|
|
714
|
+
: 'completed';
|
|
715
|
+
return deepFreeze({
|
|
716
|
+
run_id: parsed.run_id,
|
|
717
|
+
composition: {
|
|
718
|
+
id: parsed.composition.composition_id,
|
|
719
|
+
version: parsed.composition.composition_version
|
|
720
|
+
},
|
|
721
|
+
parent_dag: parsed.composition.parent_dag,
|
|
722
|
+
question: parsed.question,
|
|
723
|
+
input: parsed.input,
|
|
724
|
+
problem_snapshot: parsed.problem_snapshot,
|
|
725
|
+
fixed_conditions: parsed.fixed_conditions,
|
|
726
|
+
delegation: parsed.delegation,
|
|
727
|
+
execution_order: parsed.validation.execution_order,
|
|
728
|
+
status,
|
|
729
|
+
children
|
|
730
|
+
});
|
|
731
|
+
}
|