@unson/brainbase-mcp 0.13.0 → 0.14.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.
|
@@ -8,8 +8,9 @@ import type { FoundationDefinition, FoundationScope, FoundationType } from './on
|
|
|
8
8
|
* principal, selected project, and current revision.
|
|
9
9
|
*/
|
|
10
10
|
export declare const FOUNDATION_GRAPH_WRITE_CONTRACT_VERSION: "foundation-graph-draft-write.v1";
|
|
11
|
+
export declare const FOUNDATION_GRAPH_ADOPTION_CONTRACT_VERSION: "foundation-graph-adoption.v1";
|
|
11
12
|
export type FoundationGraphWriteOperation = 'create' | 'update';
|
|
12
|
-
export type FoundationGraphWriteIssueCode = 'INVALID_INPUT' | 'INVALID_IDENTITY' | 'ROW_DEFINITION_MISMATCH' | 'REVISION_CONFLICT' | 'CURRENT_DEFINITION_REQUIRED' | 'DEFINITION_INVALID' | 'DRAFT_ADOPTION_REQUIRED' | 'CANDIDATE_STORAGE_REQUIRED' | 'DRAFT_AUTHORIZATION_REQUIRED' | 'PROVENANCE_REQUIRED' | 'OWNER_MISMATCH' | 'ACL_CHANGE_FORBIDDEN' | 'WRITER_NOT_AUTHORIZED' | 'SCOPE_VIOLATION' | 'PROJECT_MISMATCH';
|
|
13
|
+
export type FoundationGraphWriteIssueCode = 'INVALID_INPUT' | 'INVALID_IDENTITY' | 'ROW_DEFINITION_MISMATCH' | 'REVISION_CONFLICT' | 'CURRENT_DEFINITION_REQUIRED' | 'DEFINITION_INVALID' | 'DRAFT_ADOPTION_REQUIRED' | 'CANDIDATE_STORAGE_REQUIRED' | 'DRAFT_AUTHORIZATION_REQUIRED' | 'PROVENANCE_REQUIRED' | 'OWNER_MISMATCH' | 'ACL_CHANGE_FORBIDDEN' | 'WRITER_NOT_AUTHORIZED' | 'SCOPE_VIOLATION' | 'PROJECT_MISMATCH' | 'ADOPTION_TARGET_CHANGED' | 'ADOPTER_IS_OWNER' | 'ALREADY_ADOPTED' | 'DEFINITION_NOT_READY' | 'AUTHORITY_REQUIRED';
|
|
13
14
|
export interface FoundationGraphWriteIssue {
|
|
14
15
|
readonly code: FoundationGraphWriteIssueCode;
|
|
15
16
|
readonly path: string;
|
|
@@ -88,3 +89,62 @@ export declare function validateFoundationGraphWrite(input: unknown): Foundation
|
|
|
88
89
|
export declare function normalizeFoundationGraphWrite(input: unknown): FoundationGraphWriteResult;
|
|
89
90
|
/** Descriptive alias for callers that use the validation/normalization pair. */
|
|
90
91
|
export declare const validateAndNormalizeFoundationGraphWrite: typeof normalizeFoundationGraphWrite;
|
|
92
|
+
/**
|
|
93
|
+
* ADR-015: adopt the current Foundation draft for judgment and evaluation.
|
|
94
|
+
*
|
|
95
|
+
* The adopter names the revision and digest they reviewed. The contract only
|
|
96
|
+
* succeeds when that is the current Graph revision, the adopter is not the
|
|
97
|
+
* draft's owner, and the unchanged definition passes the judgment and
|
|
98
|
+
* evaluation checks. The adopted definition is the next revision with the same
|
|
99
|
+
* content; only the adoption state, authorized uses, storage class and an
|
|
100
|
+
* appended decision provenance entry change. Whether the adopter holds the
|
|
101
|
+
* required organization authority is decided by the host before this call and
|
|
102
|
+
* passed in as `adoption.authorityRef`.
|
|
103
|
+
*/
|
|
104
|
+
export interface FoundationGraphAdoptionInput {
|
|
105
|
+
readonly context: FoundationGraphWriteContext;
|
|
106
|
+
/** The current Graph row, as read under the adopter's access in the same transaction. */
|
|
107
|
+
readonly row: FoundationGraphWriteRow;
|
|
108
|
+
/** What the adopter reviewed. */
|
|
109
|
+
readonly expected: {
|
|
110
|
+
readonly revision: string;
|
|
111
|
+
readonly digest: string;
|
|
112
|
+
};
|
|
113
|
+
readonly adoption: {
|
|
114
|
+
readonly authorityRef: string;
|
|
115
|
+
readonly adoptedAt: string;
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
export interface FoundationGraphAdoptionRecord {
|
|
119
|
+
readonly adoptedBy: string;
|
|
120
|
+
readonly authorityRef: string;
|
|
121
|
+
readonly adoptedRevision: string;
|
|
122
|
+
readonly adoptedDigest: string;
|
|
123
|
+
readonly adoptedAt: string;
|
|
124
|
+
}
|
|
125
|
+
export interface FoundationGraphAdoptionResult {
|
|
126
|
+
readonly contractVersion: typeof FOUNDATION_GRAPH_ADOPTION_CONTRACT_VERSION;
|
|
127
|
+
readonly operation: 'adopt';
|
|
128
|
+
readonly projectCode: string;
|
|
129
|
+
readonly row: {
|
|
130
|
+
readonly id: string;
|
|
131
|
+
readonly type: FoundationType;
|
|
132
|
+
readonly revision: string;
|
|
133
|
+
readonly projectCode: string;
|
|
134
|
+
readonly payload: {
|
|
135
|
+
readonly foundation: FoundationDefinition;
|
|
136
|
+
readonly adoption: FoundationGraphAdoptionRecord;
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
readonly definition: FoundationDefinition;
|
|
140
|
+
readonly adoption: FoundationGraphAdoptionRecord;
|
|
141
|
+
}
|
|
142
|
+
export interface FoundationGraphAdoptionValidationResult {
|
|
143
|
+
readonly valid: boolean;
|
|
144
|
+
readonly status: 'valid' | 'invalid';
|
|
145
|
+
readonly issues: readonly FoundationGraphWriteIssue[];
|
|
146
|
+
readonly normalized?: FoundationGraphAdoptionResult;
|
|
147
|
+
}
|
|
148
|
+
export declare function validateFoundationGraphAdoption(input: unknown): FoundationGraphAdoptionValidationResult;
|
|
149
|
+
/** Normalize a valid adoption or throw with the complete validation issues. */
|
|
150
|
+
export declare function normalizeFoundationGraphAdoption(input: unknown): FoundationGraphAdoptionResult;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { cloneFoundationDefinition, nextFoundationRevision } from './foundation-catalog.js';
|
|
1
|
+
import { cloneFoundationDefinition, digestFoundationDefinition, nextFoundationRevision } from './foundation-catalog.js';
|
|
2
2
|
import { validateFoundationDefinition } from './ontology-foundation.js';
|
|
3
3
|
/**
|
|
4
4
|
* Contract for preparing a Foundation draft for the canonical Graph writer.
|
|
@@ -12,6 +12,10 @@ export const FOUNDATION_GRAPH_WRITE_CONTRACT_VERSION = 'foundation-graph-draft-w
|
|
|
12
12
|
const FOUNDATION_TYPES = ['objective', 'variable', 'model', 'constraint'];
|
|
13
13
|
const REVISION_PATTERN = /^[1-9]\d*$/u;
|
|
14
14
|
const FOUNDATION_USES_DRAFT = ['draft'];
|
|
15
|
+
/** ADR-015 F2: an adopted definition may be used for judgment and evaluation, never execution. */
|
|
16
|
+
const FOUNDATION_USES_ADOPTED = ['draft', 'judgment', 'evaluation'];
|
|
17
|
+
const DIGEST_PATTERN = /^sha256:[0-9a-f]{64}$/u;
|
|
18
|
+
export const FOUNDATION_GRAPH_ADOPTION_CONTRACT_VERSION = 'foundation-graph-adoption.v1';
|
|
15
19
|
/**
|
|
16
20
|
* Error thrown by the throwing normalizer. The non-throwing validator should
|
|
17
21
|
* be used at HTTP/MCP boundaries when callers need to render all issues.
|
|
@@ -207,6 +211,117 @@ export function normalizeFoundationGraphWrite(input) {
|
|
|
207
211
|
}
|
|
208
212
|
/** Descriptive alias for callers that use the validation/normalization pair. */
|
|
209
213
|
export const validateAndNormalizeFoundationGraphWrite = normalizeFoundationGraphWrite;
|
|
214
|
+
export function validateFoundationGraphAdoption(input) {
|
|
215
|
+
const issues = [];
|
|
216
|
+
const fail = () => ({ valid: false, status: 'invalid', issues });
|
|
217
|
+
if (!isRecord(input) || !isRecord(input.context) || !isRecord(input.row) || !isRecord(input.expected) || !isRecord(input.adoption)) {
|
|
218
|
+
issues.push({ code: 'INVALID_INPUT', path: 'input', message: 'Adoption needs context, row, expected and adoption objects.' });
|
|
219
|
+
return fail();
|
|
220
|
+
}
|
|
221
|
+
const context = input.context;
|
|
222
|
+
const row = input.row;
|
|
223
|
+
const expected = input.expected;
|
|
224
|
+
const adoption = input.adoption;
|
|
225
|
+
validateContext(context, issues);
|
|
226
|
+
validateGraphRow(row, issues);
|
|
227
|
+
if (isNonEmptyString(row.projectCode) && isNonEmptyString(context.projectCode) && row.projectCode !== context.projectCode) {
|
|
228
|
+
issues.push({ code: 'PROJECT_MISMATCH', path: 'row.projectCode', message: 'Graph row projectCode must match the trusted selected project.' });
|
|
229
|
+
}
|
|
230
|
+
if (!isCanonicalRevision(expected.revision) || typeof expected.digest !== 'string' || !DIGEST_PATTERN.test(expected.digest)) {
|
|
231
|
+
issues.push({ code: 'INVALID_INPUT', path: 'expected', message: 'expected needs the reviewed revision and its sha256 digest.' });
|
|
232
|
+
}
|
|
233
|
+
if (!isNonEmptyString(adoption.authorityRef)) {
|
|
234
|
+
issues.push({ code: 'AUTHORITY_REQUIRED', path: 'adoption.authorityRef', message: 'Adoption requires the evidence of the adopter\'s organization authority.' });
|
|
235
|
+
}
|
|
236
|
+
if (typeof adoption.adoptedAt !== 'string' || !isRfc3339(adoption.adoptedAt)) {
|
|
237
|
+
issues.push({ code: 'INVALID_INPUT', path: 'adoption.adoptedAt', message: 'adoption.adoptedAt must be an RFC 3339 timestamp.' });
|
|
238
|
+
}
|
|
239
|
+
if (issues.length > 0)
|
|
240
|
+
return fail();
|
|
241
|
+
const rowType = resolveRowType(row);
|
|
242
|
+
const current = unwrapFoundation(row.payload.foundation);
|
|
243
|
+
const currentResult = validateFoundationDefinition(current, { use: 'draft' });
|
|
244
|
+
appendDefinitionIssues(issues, 'row.payload.foundation', currentResult.issues);
|
|
245
|
+
if (!currentResult.valid || !isFoundationDefinitionRecord(current) || !rowType)
|
|
246
|
+
return fail();
|
|
247
|
+
if (current.id !== row.id || current.type !== rowType || current.revision !== row.revision) {
|
|
248
|
+
issues.push({ code: 'ROW_DEFINITION_MISMATCH', path: 'row.payload.foundation', message: 'The current definition must match the Graph row identity and revision.' });
|
|
249
|
+
return fail();
|
|
250
|
+
}
|
|
251
|
+
const currentDefinition = current;
|
|
252
|
+
const currentDigest = digestFoundationDefinition(currentDefinition);
|
|
253
|
+
if (expected.revision !== current.revision || expected.digest !== currentDigest) {
|
|
254
|
+
issues.push({
|
|
255
|
+
code: 'ADOPTION_TARGET_CHANGED',
|
|
256
|
+
path: 'expected',
|
|
257
|
+
message: `The reviewed revision ${String(expected.revision)} is not the current revision ${current.revision} with the same content.`
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
if (current.adoptionState !== 'draft') {
|
|
261
|
+
issues.push({ code: 'ALREADY_ADOPTED', path: 'row.payload.foundation.adoptionState', message: `Only a draft can be adopted; the current revision is ${String(current.adoptionState)}.` });
|
|
262
|
+
}
|
|
263
|
+
if (current.acl.ownerId === context.principal) {
|
|
264
|
+
issues.push({ code: 'ADOPTER_IS_OWNER', path: 'context.principal', message: 'The owner of a draft cannot adopt it.' });
|
|
265
|
+
}
|
|
266
|
+
validateCandidateScope(current, context, issues);
|
|
267
|
+
if (issues.length > 0)
|
|
268
|
+
return fail();
|
|
269
|
+
let revision;
|
|
270
|
+
try {
|
|
271
|
+
revision = nextFoundationRevision(current.revision);
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
issues.push({ code: 'REVISION_CONFLICT', path: 'row.revision', message: 'The next revision cannot be derived from the current revision.' });
|
|
275
|
+
return fail();
|
|
276
|
+
}
|
|
277
|
+
const authorityRef = String(adoption.authorityRef);
|
|
278
|
+
const adopted = cloneFoundationDefinition(currentDefinition);
|
|
279
|
+
adopted.revision = revision;
|
|
280
|
+
adopted.adoptionState = 'approved';
|
|
281
|
+
adopted.storage = 'ontology';
|
|
282
|
+
adopted.authorizedUses = [...FOUNDATION_USES_ADOPTED];
|
|
283
|
+
adopted.provenance = [
|
|
284
|
+
...adopted.provenance,
|
|
285
|
+
{ sourceId: `foundation-adoption:${current.id}@${current.revision}`, sourceKind: 'decision', evidenceIds: [authorityRef] }
|
|
286
|
+
];
|
|
287
|
+
for (const use of ['judgment', 'evaluation']) {
|
|
288
|
+
const ready = validateFoundationDefinition(adopted, { use });
|
|
289
|
+
if (!ready.valid) {
|
|
290
|
+
for (const issue of ready.issues) {
|
|
291
|
+
issues.push({ code: 'DEFINITION_NOT_READY', path: `row.payload.foundation.${issue.path}`, message: `Not ready for ${use}: ${issue.message}` });
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (issues.length > 0)
|
|
296
|
+
return fail();
|
|
297
|
+
const record = {
|
|
298
|
+
adoptedBy: context.principal,
|
|
299
|
+
authorityRef,
|
|
300
|
+
adoptedRevision: current.revision,
|
|
301
|
+
adoptedDigest: currentDigest,
|
|
302
|
+
adoptedAt: String(adoption.adoptedAt)
|
|
303
|
+
};
|
|
304
|
+
return {
|
|
305
|
+
valid: true,
|
|
306
|
+
status: 'valid',
|
|
307
|
+
issues: [],
|
|
308
|
+
normalized: {
|
|
309
|
+
contractVersion: FOUNDATION_GRAPH_ADOPTION_CONTRACT_VERSION,
|
|
310
|
+
operation: 'adopt',
|
|
311
|
+
projectCode: context.projectCode,
|
|
312
|
+
row: { id: current.id, type: rowType, revision, projectCode: context.projectCode, payload: { foundation: adopted, adoption: record } },
|
|
313
|
+
definition: adopted,
|
|
314
|
+
adoption: record
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
/** Normalize a valid adoption or throw with the complete validation issues. */
|
|
319
|
+
export function normalizeFoundationGraphAdoption(input) {
|
|
320
|
+
const result = validateFoundationGraphAdoption(input);
|
|
321
|
+
if (!result.valid || !result.normalized)
|
|
322
|
+
throw new FoundationGraphWriteError(result.issues);
|
|
323
|
+
return result.normalized;
|
|
324
|
+
}
|
|
210
325
|
function validateContext(context, issues) {
|
|
211
326
|
if (!isNonEmptyString(context.principal)) {
|
|
212
327
|
issues.push({ code: 'INVALID_INPUT', path: 'context.principal', message: 'context.principal must be a non-empty trusted identifier.' });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unson/brainbase-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Local-first Brainbase judgment OS that lets supported AI agents reuse a user's approved goals, priorities, decisions, and judgment criteria.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -352,5 +352,5 @@
|
|
|
352
352
|
"engines": {
|
|
353
353
|
"node": ">=20"
|
|
354
354
|
},
|
|
355
|
-
"gitHead": "
|
|
355
|
+
"gitHead": "4c9f27c5bc49aed6cb9132c442739865a41f8cc5"
|
|
356
356
|
}
|