@unson/brainbase-mcp 0.7.0 → 0.9.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.
Files changed (91) hide show
  1. package/README.md +1 -1
  2. package/contracts/foundation/graph-history.sql +634 -0
  3. package/contracts/judgment-dag/digest.json +2 -2
  4. package/contracts/judgment-value-proof/schema.json +43 -1
  5. package/dist/canonical-task-service.js +16 -5
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +107 -0
  8. package/dist/company-os-objectives.d.ts +11 -0
  9. package/dist/company-os-objectives.js +84 -47
  10. package/dist/foundation-authenticated-tools.d.ts +56 -0
  11. package/dist/foundation-authenticated-tools.js +347 -0
  12. package/dist/foundation-graph-http.d.ts +43 -0
  13. package/dist/foundation-graph-http.js +243 -0
  14. package/dist/foundation-graph-write.d.ts +90 -0
  15. package/dist/foundation-graph-write.js +447 -0
  16. package/dist/foundation-public-provider.d.ts +140 -0
  17. package/dist/foundation-public-provider.js +192 -0
  18. package/dist/graph-corrections.d.ts +157 -0
  19. package/dist/graph-corrections.js +620 -0
  20. package/dist/graph-foundation-reader.d.ts +60 -0
  21. package/dist/graph-foundation-reader.js +494 -0
  22. package/dist/graph-retrieval.d.ts +6 -1
  23. package/dist/graph-revision-reader.d.ts +80 -0
  24. package/dist/graph-revision-reader.js +246 -0
  25. package/dist/graph-web-http.d.ts +26 -0
  26. package/dist/graph-web-http.js +215 -0
  27. package/dist/graph-web.d.ts +364 -0
  28. package/dist/graph-web.js +639 -0
  29. package/dist/judgment-problem-snapshot.d.ts +30 -2
  30. package/dist/judgment-problem-snapshot.js +302 -6
  31. package/dist/judgment-value-proof-review.d.ts +166 -0
  32. package/dist/judgment-value-proof-review.js +437 -0
  33. package/dist/judgment-value-proof.d.ts +28 -1
  34. package/dist/judgment-value-proof.js +53 -8
  35. package/dist/knowledge-continuation.d.ts +127 -0
  36. package/dist/knowledge-continuation.js +446 -0
  37. package/dist/knowledge-lookup.d.ts +178 -0
  38. package/dist/knowledge-lookup.js +758 -0
  39. package/dist/local-web-fetch-bridge.d.ts +5 -0
  40. package/dist/local-web-fetch-bridge.js +37 -0
  41. package/dist/local-web-host.d.ts +109 -0
  42. package/dist/local-web-host.js +688 -0
  43. package/dist/local-web-security.d.ts +25 -0
  44. package/dist/local-web-security.js +95 -0
  45. package/dist/organization-graph.d.ts +15 -0
  46. package/dist/organization-graph.js +24 -4
  47. package/dist/personal-knowledge.d.ts +12 -0
  48. package/dist/personal-knowledge.js +29 -0
  49. package/dist/personal-memory-handover.d.ts +108 -0
  50. package/dist/personal-memory-handover.js +369 -0
  51. package/dist/philosophy-revision-reader.d.ts +85 -0
  52. package/dist/philosophy-revision-reader.js +241 -0
  53. package/dist/portable-graph.d.ts +24 -0
  54. package/dist/portable-graph.js +77 -0
  55. package/dist/server.d.ts +7 -2
  56. package/dist/server.js +9 -6
  57. package/dist/value-proof-review-http.d.ts +25 -0
  58. package/dist/value-proof-review-http.js +185 -0
  59. package/dist/world-model.d.ts +21 -3
  60. package/dist/world-model.js +94 -12
  61. package/package.json +94 -6
  62. package/ui/brainbase-tokens.css +56 -0
  63. package/ui/graph-own-share.js +367 -0
  64. package/ui/graph-projects-view.css +23 -0
  65. package/ui/graph-projects-view.js +730 -0
  66. package/ui/graph-registry-view.css +101 -0
  67. package/ui/graph-registry-view.js +804 -0
  68. package/ui/graph-view-shared.css +522 -0
  69. package/ui/graph-view-shared.js +1227 -0
  70. package/ui/judgment-view.css +119 -45
  71. package/ui/local-web-shell.css +396 -0
  72. package/ui/local-web-shell.js +378 -0
  73. package/ui/objective-editor-http-port.js +154 -0
  74. package/ui/objective-editor.css +180 -74
  75. package/ui/objective-editor.js +490 -43
  76. package/ui/outcome-knowledge.css +50 -14
  77. package/ui/outcome-knowledge.js +15 -1
  78. package/ui/value-proof-review.css +504 -0
  79. package/ui/value-proof-review.js +1104 -0
  80. package/ui/workspace-kit.css +485 -0
  81. package/ui/workspace-kit.js +229 -0
  82. package/ui/world-model-view.css +188 -0
  83. package/ui/world-model-view.js +486 -0
  84. package/ui/icons/mana/LICENSE.tabler.txt +0 -21
  85. package/ui/icons/mana/affiliate.svg +0 -20
  86. package/ui/icons/mana/brand-github.svg +0 -15
  87. package/ui/icons/mana/brand-google-drive.svg +0 -17
  88. package/ui/icons/mana/brand-slack.svg +0 -18
  89. package/ui/icons/mana/cpu.svg +0 -24
  90. package/ui/outcome-mana.css +0 -255
  91. package/ui/outcome-mana.js +0 -2325
@@ -1,4 +1,4 @@
1
- import { type FoundationDefinition, type FoundationAcl, type FoundationRevision } from './ontology-foundation.js';
1
+ import { type FoundationDefinition, type FoundationAcl, type FoundationEvaluationDescriptor, type FoundationRevision } from './ontology-foundation.js';
2
2
  /**
3
3
  * An immutable input bundle for one judgment.
4
4
  *
@@ -8,7 +8,7 @@ import { type FoundationDefinition, type FoundationAcl, type FoundationRevision
8
8
  */
9
9
  export declare const JUDGMENT_PROBLEM_SNAPSHOT_VERSION: "judgment-problem-snapshot.v1";
10
10
  export type JudgmentProblemSnapshotId = `sha256:${string}`;
11
- export type JudgmentProblemReferenceKind = 'objective' | 'criterion' | 'variable' | 'observation' | 'model' | 'constraint' | 'authority' | 'resource' | 'deadline' | 'dag' | 'evidence';
11
+ export type JudgmentProblemReferenceKind = 'entity' | 'edge' | 'objective' | 'criterion' | 'variable' | 'observation' | 'model' | 'constraint' | 'philosophy' | 'authority' | 'resource' | 'deadline' | 'dag' | 'evidence';
12
12
  export type JudgmentProblemScopeType = 'personal' | 'project' | 'organization';
13
13
  export interface JudgmentProblemScope {
14
14
  readonly type: JudgmentProblemScopeType;
@@ -35,8 +35,15 @@ export interface JudgmentProblemReference {
35
35
  readonly scope: JudgmentProblemScope;
36
36
  readonly valid_from: string;
37
37
  readonly valid_to?: string | null;
38
+ /**
39
+ * Source-owned measurement conditions for an Observation reference. The
40
+ * descriptor is checked against the canonical Variable and Objective
41
+ * before a current judgment can use the observation.
42
+ */
43
+ readonly measurement?: JudgmentProblemObservationMeasurementDescriptor;
38
44
  readonly evidence?: JudgmentProblemEvidenceBinding;
39
45
  }
46
+ export type JudgmentProblemObservationMeasurementDescriptor = FoundationEvaluationDescriptor;
40
47
  /**
41
48
  * Stable name shared by sub-DAG and reservation consumers. A ProblemRef is
42
49
  * intentionally only a versioned, scoped reference; it never carries an
@@ -90,6 +97,10 @@ export interface JudgmentProblemReferenceResolution {
90
97
  /** The provider must return the digest of the exact revision it resolved. */
91
98
  readonly digest?: JudgmentProblemSnapshotId;
92
99
  readonly message?: string;
100
+ /** Canonical Observation metadata returned by the source-owned resolver. */
101
+ readonly canonical?: {
102
+ readonly measurement?: JudgmentProblemObservationMeasurementDescriptor;
103
+ };
93
104
  }
94
105
  /**
95
106
  * A provider is the only place that knows how an Observation, authority,
@@ -104,6 +115,8 @@ export interface JudgmentProblemReferenceProvider {
104
115
  readonly reference: JudgmentProblemReference;
105
116
  readonly phase: JudgmentProblemReferenceResolutionPhase;
106
117
  readonly context: JudgmentProblemSnapshotAccessContext;
118
+ /** The full bundle enables cross-reference coherence checks. */
119
+ readonly snapshot?: JudgmentProblemSnapshot;
107
120
  }): JudgmentProblemReferenceResolution | Promise<JudgmentProblemReferenceResolution>;
108
121
  }
109
122
  export interface SaveJudgmentProblemSnapshotRequest {
@@ -124,6 +137,19 @@ export interface LoadJudgmentProblemSnapshotRequest {
124
137
  readonly reference_resolution?: JudgmentProblemReferenceResolutionMode;
125
138
  readonly referenceProvider?: JudgmentProblemReferenceProvider;
126
139
  }
140
+ /**
141
+ * Validate a not-yet-persisted snapshot through the same read and reference
142
+ * boundaries used by load. This is intentionally read-only: callers cannot
143
+ * turn validation into publication by passing an artifact root.
144
+ */
145
+ export interface ValidateJudgmentProblemSnapshotRequest {
146
+ readonly snapshot: JudgmentProblemSnapshot;
147
+ readonly access: JudgmentProblemSnapshotAccessContext;
148
+ readonly accessProvider?: JudgmentProblemSnapshotAccessProvider;
149
+ /** Defaults to `current`; historical validation only rechecks ACL/digest. */
150
+ readonly reference_resolution?: JudgmentProblemReferenceResolutionMode;
151
+ readonly referenceProvider: JudgmentProblemReferenceProvider;
152
+ }
127
153
  export interface JudgmentProblemSnapshotReceipt {
128
154
  readonly snapshot_id: JudgmentProblemSnapshotId;
129
155
  readonly snapshot_version: typeof JUDGMENT_PROBLEM_SNAPSHOT_VERSION;
@@ -147,6 +173,7 @@ export declare const defaultJudgmentProblemSnapshotAccessProvider: JudgmentProbl
147
173
  export declare function computeJudgmentProblemSnapshotId(value: JudgmentProblemSnapshot): JudgmentProblemSnapshotId;
148
174
  export declare function saveJudgmentProblemSnapshot(request: SaveJudgmentProblemSnapshotRequest): Promise<JudgmentProblemSnapshotReceipt>;
149
175
  export declare function loadJudgmentProblemSnapshot(request: LoadJudgmentProblemSnapshotRequest): Promise<JudgmentProblemSnapshot>;
176
+ export declare function validateJudgmentProblemSnapshot(request: ValidateJudgmentProblemSnapshotRequest): Promise<void>;
150
177
  /** Structural adapter for FoundationRevisionStore-like readers. */
151
178
  export interface JudgmentProblemFoundationReferenceReader {
152
179
  read(reference: FoundationRevision, context: {
@@ -181,5 +208,6 @@ export declare function createJudgmentProblemFoundationReferenceProvider(options
181
208
  readonly reference: JudgmentProblemReference;
182
209
  readonly phase: JudgmentProblemReferenceResolutionPhase;
183
210
  readonly context: JudgmentProblemSnapshotAccessContext;
211
+ readonly snapshot?: JudgmentProblemSnapshot;
184
212
  }) => JudgmentProblemReferenceResolution | Promise<JudgmentProblemReferenceResolution>;
185
213
  }): JudgmentProblemReferenceProvider;
@@ -1,7 +1,8 @@
1
1
  import { createHash, randomUUID } from 'node:crypto';
2
2
  import { link, open, lstat, mkdir, readdir, readFile, unlink } from 'node:fs/promises';
3
3
  import path from 'node:path';
4
- import { validateFoundationDefinition } from './ontology-foundation.js';
4
+ import { validateFoundationDefinition, validateEvaluationCompatibility } from './ontology-foundation.js';
5
+ import { checkObjectiveReadiness } from './company-os-objectives.js';
5
6
  /**
6
7
  * An immutable input bundle for one judgment.
7
8
  *
@@ -24,12 +25,15 @@ const SNAPSHOT_ID_PATTERN = /^sha256:([0-9a-f]{64})$/u;
24
25
  const POSITIVE_REVISION_PATTERN = /^[1-9]\d*$/u;
25
26
  const SCOPE_TYPES = ['personal', 'project', 'organization'];
26
27
  const REFERENCE_KINDS = [
28
+ 'entity',
29
+ 'edge',
27
30
  'objective',
28
31
  'criterion',
29
32
  'variable',
30
33
  'observation',
31
34
  'model',
32
35
  'constraint',
36
+ 'philosophy',
33
37
  'authority',
34
38
  'resource',
35
39
  'deadline',
@@ -48,7 +52,7 @@ const SNAPSHOT_KEYS = [
48
52
  'snapshot_version'
49
53
  ];
50
54
  const ENVELOPE_KEYS = ['snapshot', 'snapshot_id', 'snapshot_version'];
51
- const REFERENCE_KEYS = ['digest', 'evidence', 'id', 'kind', 'revision', 'scope', 'valid_from', 'valid_to'];
55
+ const REFERENCE_KEYS = ['digest', 'evidence', 'id', 'kind', 'measurement', 'revision', 'scope', 'valid_from', 'valid_to'];
52
56
  const EVIDENCE_KEYS = ['access', 'content', 'digest', 'mode', 'reference_id', 'reference_revision'];
53
57
  const SCOPE_KEYS = ['id', 'type'];
54
58
  const ACL_KEYS = ['ownerId', 'readerIds', 'visibility', 'writerIds'];
@@ -254,6 +258,9 @@ function validateReference(value, index) {
254
258
  if (validTo !== undefined && Date.parse(validTo) < Date.parse(validFrom)) {
255
259
  fail('invalid_request', `${label}.valid_to precedes valid_from`);
256
260
  }
261
+ const measurement = value.measurement === undefined
262
+ ? undefined
263
+ : validateMeasurementDescriptor(value.measurement, `${label}.measurement`);
257
264
  const evidence = value.evidence === undefined ? undefined : validateEvidence(value.evidence, `${label}.evidence`);
258
265
  return deepFreeze({
259
266
  kind: value.kind,
@@ -263,9 +270,73 @@ function validateReference(value, index) {
263
270
  scope: { type: value.scope.type, id: value.scope.id },
264
271
  valid_from: validFrom,
265
272
  ...(validTo === undefined ? {} : { valid_to: validTo }),
273
+ ...(measurement === undefined ? {} : { measurement }),
266
274
  ...(evidence === undefined ? {} : { evidence })
267
275
  });
268
276
  }
277
+ function validateMeasurementDescriptor(value, label) {
278
+ const aggregations = [
279
+ 'none',
280
+ 'sum',
281
+ 'average',
282
+ 'count',
283
+ 'min',
284
+ 'max',
285
+ 'last',
286
+ 'custom'
287
+ ];
288
+ const keys = ['aggregation', 'granularity', 'period', 'scope', 'unit', 'variableRef'];
289
+ if (!isPlainRecord(value) || !hasOnlyKeys(value, keys)
290
+ || !Object.prototype.hasOwnProperty.call(value, 'variableRef')
291
+ || !Object.prototype.hasOwnProperty.call(value, 'aggregation')
292
+ || !Object.prototype.hasOwnProperty.call(value, 'granularity')
293
+ || !Object.prototype.hasOwnProperty.call(value, 'scope')
294
+ || !Object.prototype.hasOwnProperty.call(value, 'period')) {
295
+ fail('invalid_request', `${label} is invalid`);
296
+ }
297
+ if (!isPlainRecord(value.variableRef)
298
+ || !hasExactKeys(value.variableRef, ['id', 'revision', 'type'])
299
+ || value.variableRef.type !== 'variable') {
300
+ fail('invalid_request', `${label}.variableRef must reference a Variable revision`);
301
+ }
302
+ const variableId = requireString(value.variableRef.id, `${label}.variableRef.id`);
303
+ const variableRevision = requireRevision(value.variableRef.revision, `${label}.variableRef.revision`);
304
+ if (typeof value.aggregation !== 'string' || !aggregations.includes(value.aggregation)) {
305
+ fail('invalid_request', `${label}.aggregation is invalid`);
306
+ }
307
+ const granularity = requireString(value.granularity, `${label}.granularity`);
308
+ if (value.unit !== undefined && typeof value.unit !== 'string') {
309
+ fail('invalid_request', `${label}.unit must be a string when provided`);
310
+ }
311
+ if (!isPlainRecord(value.scope)
312
+ || !Array.isArray(value.scope.subjectIds)
313
+ || value.scope.subjectIds.some((subjectId) => typeof subjectId !== 'string' || subjectId.trim().length === 0)
314
+ || typeof value.scope.validFrom !== 'string'
315
+ || Number.isNaN(Date.parse(value.scope.validFrom))
316
+ || (value.scope.validUntil !== undefined && (typeof value.scope.validUntil !== 'string' || Number.isNaN(Date.parse(value.scope.validUntil))))) {
317
+ fail('invalid_request', `${label}.scope is invalid`);
318
+ }
319
+ if (!isPlainRecord(value.period)
320
+ || typeof value.period.from !== 'string'
321
+ || typeof value.period.until !== 'string'
322
+ || Number.isNaN(Date.parse(value.period.from))
323
+ || Number.isNaN(Date.parse(value.period.until))
324
+ || Date.parse(value.period.until) < Date.parse(value.period.from)) {
325
+ fail('invalid_request', `${label}.period is invalid`);
326
+ }
327
+ return deepFreeze({
328
+ variableRef: { id: variableId, type: 'variable', revision: variableRevision },
329
+ ...(value.unit === undefined ? {} : { unit: value.unit }),
330
+ aggregation: value.aggregation,
331
+ granularity,
332
+ scope: {
333
+ subjectIds: [...value.scope.subjectIds],
334
+ validFrom: value.scope.validFrom,
335
+ ...(value.scope.validUntil === undefined ? {} : { validUntil: value.scope.validUntil })
336
+ },
337
+ period: { from: value.period.from, until: value.period.until }
338
+ });
339
+ }
269
340
  function validateSnapshot(value) {
270
341
  if (!isPlainRecord(value) || !hasExactKeys(value, SNAPSHOT_KEYS) ||
271
342
  value.snapshot_version !== JUDGMENT_PROBLEM_SNAPSHOT_VERSION ||
@@ -299,6 +370,10 @@ function requiredKinds(references) {
299
370
  }
300
371
  function validateRequiredReferences(snapshot) {
301
372
  const kinds = requiredKinds(snapshot.references);
373
+ const objectiveCount = snapshot.references.filter((reference) => reference.kind === 'objective').length;
374
+ if (objectiveCount !== 1) {
375
+ fail('invalid_request', `snapshot must contain exactly one objective reference; found ${objectiveCount}`);
376
+ }
302
377
  const required = [
303
378
  'objective',
304
379
  'criterion',
@@ -353,10 +428,10 @@ function evidenceReference(owner, evidence) {
353
428
  ...(owner.valid_to === undefined ? {} : { valid_to: owner.valid_to })
354
429
  };
355
430
  }
356
- async function resolveOneReference(reference, phase, context, provider, errorReference = reference) {
431
+ async function resolveOneReference(reference, phase, context, provider, snapshot, errorReference = reference) {
357
432
  let result;
358
433
  try {
359
- result = await provider.resolve({ reference, phase, context });
434
+ result = await provider.resolve({ reference, phase, context, snapshot });
360
435
  }
361
436
  catch (error) {
362
437
  if (error instanceof JudgmentProblemSnapshotError)
@@ -383,9 +458,9 @@ async function resolveOneReference(reference, phase, context, provider, errorRef
383
458
  }
384
459
  async function resolveReferences(snapshot, phase, context, provider) {
385
460
  for (const reference of snapshot.references) {
386
- await resolveOneReference(reference, phase, context, provider);
461
+ await resolveOneReference(reference, phase, context, provider, snapshot);
387
462
  if (reference.evidence?.mode === 'immutable_reference') {
388
- await resolveOneReference(evidenceReference(reference, reference.evidence), phase, context, provider, reference);
463
+ await resolveOneReference(evidenceReference(reference, reference.evidence), phase, context, provider, snapshot, reference);
389
464
  }
390
465
  }
391
466
  }
@@ -682,6 +757,43 @@ export async function loadJudgmentProblemSnapshot(request) {
682
757
  await resolveReferences(envelope.snapshot, referenceResolution === 'current' ? 'read' : 'historical_read', context, request.referenceProvider);
683
758
  return envelope.snapshot;
684
759
  }
760
+ export async function validateJudgmentProblemSnapshot(request) {
761
+ if (!isPlainRecord(request))
762
+ fail('invalid_request', 'validation request must be a plain object');
763
+ const snapshot = validateSnapshot(request.snapshot);
764
+ validateRequiredReferences(snapshot);
765
+ const context = validateAccessContext(request.access);
766
+ const referenceResolution = request.reference_resolution ?? 'current';
767
+ if (referenceResolution !== 'current' && referenceResolution !== 'historical') {
768
+ fail('invalid_request', 'reference_resolution must be current or historical');
769
+ }
770
+ if (!request.referenceProvider || typeof request.referenceProvider.resolve !== 'function') {
771
+ fail('invalid_request', `referenceProvider.resolve is required for ${referenceResolution} reference resolution`);
772
+ }
773
+ const accessProvider = request.accessProvider ?? defaultJudgmentProblemSnapshotAccessProvider;
774
+ await authorize('read', snapshot, context, accessProvider);
775
+ for (const reference of snapshot.references) {
776
+ if (reference.evidence === undefined)
777
+ continue;
778
+ try {
779
+ const allowed = await accessProvider.authorize({
780
+ action: 'read',
781
+ context,
782
+ acl: reference.evidence.access,
783
+ snapshot,
784
+ reference
785
+ });
786
+ if (allowed === false)
787
+ fail('unauthorized', `evidence access was denied for ${reference.kind}/${reference.id}@${reference.revision}`, reference);
788
+ }
789
+ catch (error) {
790
+ if (error instanceof JudgmentProblemSnapshotError)
791
+ throw error;
792
+ fail('unauthorized', `evidence access was denied for ${reference.kind}/${reference.id}@${reference.revision}`, reference);
793
+ }
794
+ }
795
+ await resolveReferences(snapshot, referenceResolution === 'current' ? 'read' : 'historical_read', context, request.referenceProvider);
796
+ }
685
797
  function validateAccessContext(value) {
686
798
  if (!isPlainRecord(value) || typeof value.principal !== 'string' || value.principal.trim().length === 0 || value.principal.includes('\0')) {
687
799
  fail('unauthorized', 'a trusted principal is required');
@@ -699,8 +811,16 @@ function validateAccessContext(value) {
699
811
  * bypass the canonical store or digest check.
700
812
  */
701
813
  export function createJudgmentProblemFoundationReferenceProvider(options) {
814
+ const objectiveReader = {
815
+ async read(reference, context) {
816
+ return await options.store.read(reference, { principal: context.principal });
817
+ }
818
+ };
702
819
  return {
703
820
  async resolve(input) {
821
+ if (input.reference.kind === 'observation') {
822
+ return resolveObservationReference(input, options, objectiveReader);
823
+ }
704
824
  if (!['objective', 'variable', 'model', 'constraint'].includes(input.reference.kind)) {
705
825
  if (options.resolveOther)
706
826
  return options.resolveOther(input);
@@ -733,6 +853,26 @@ export function createJudgmentProblemFoundationReferenceProvider(options) {
733
853
  return { status: 'not_applicable', message: applicability.message };
734
854
  }
735
855
  }
856
+ if (definition.type === 'objective') {
857
+ if (input.phase === 'historical_read') {
858
+ const dependencyResult = await validateObjectiveDependencies(definition, input, options);
859
+ if (dependencyResult)
860
+ return dependencyResult;
861
+ }
862
+ else {
863
+ const readiness = await checkObjectiveReadiness(objectiveReader, input.reference.id, { principal: input.context.principal }, input.reference.revision);
864
+ if (!readiness.ready)
865
+ return objectiveReadinessResult(readiness.issues);
866
+ const dependencyResult = await validateObjectiveDependencies(definition, input, options);
867
+ if (dependencyResult)
868
+ return dependencyResult;
869
+ }
870
+ }
871
+ if (definition.type === 'model') {
872
+ const dependencyResult = await validateModelDependencies(definition, input, options);
873
+ if (dependencyResult)
874
+ return dependencyResult;
875
+ }
736
876
  if (options.validate) {
737
877
  return options.validate({
738
878
  ...input,
@@ -743,6 +883,162 @@ export function createJudgmentProblemFoundationReferenceProvider(options) {
743
883
  }
744
884
  };
745
885
  }
886
+ function objectiveReadinessResult(issues) {
887
+ const notApplicable = issues.some((issue) => (issue.code === 'UNAUTHORIZED_USE' || issue.code === 'CRITERION_PERIOD_MISMATCH'));
888
+ return {
889
+ status: notApplicable ? 'not_applicable' : 'unresolved',
890
+ message: issues.map((issue) => issue.message).join('; ')
891
+ };
892
+ }
893
+ async function validateObjectiveDependencies(definition, input, options) {
894
+ for (const criterion of definition.criteria) {
895
+ const dependency = criterion?.variableRef;
896
+ if (!isVariableRevision(dependency)) {
897
+ return { status: 'unresolved', message: 'Objective criterion does not reference a valid Variable revision' };
898
+ }
899
+ const resolved = await options.store.read(dependency, { principal: input.context.principal });
900
+ if (!resolved)
901
+ return { status: 'missing', message: `Objective Variable ${dependency.id}@${dependency.revision} was not found` };
902
+ if (!matchingFoundationRecord(resolved, dependency, 'variable')) {
903
+ return { status: 'unresolved', message: `Objective Variable ${dependency.id}@${dependency.revision} is not the requested revision` };
904
+ }
905
+ const pinned = findSnapshotReference(input.snapshot, dependency);
906
+ if (pinned && pinned.digest !== resolved.digest) {
907
+ return { status: 'unresolved', message: `Objective Variable ${dependency.id}@${dependency.revision} digest changed` };
908
+ }
909
+ }
910
+ return undefined;
911
+ }
912
+ async function validateModelDependencies(definition, input, options) {
913
+ const dependencies = [...definition.inputVariableRefs, ...definition.outputVariableRefs];
914
+ const seen = new Set();
915
+ for (const dependency of dependencies) {
916
+ if (!isVariableRevision(dependency)) {
917
+ return { status: 'unresolved', message: 'Model input/output must reference a valid Variable revision' };
918
+ }
919
+ const key = `${dependency.id}@${dependency.revision}`;
920
+ if (seen.has(key))
921
+ continue;
922
+ seen.add(key);
923
+ const resolved = await options.store.read(dependency, { principal: input.context.principal });
924
+ if (!resolved)
925
+ return { status: 'missing', message: `Model Variable ${dependency.id}@${dependency.revision} was not found` };
926
+ if (!matchingFoundationRecord(resolved, dependency, 'variable')) {
927
+ return { status: 'unresolved', message: `Model Variable ${dependency.id}@${dependency.revision} is not the requested revision` };
928
+ }
929
+ const pinned = findSnapshotReference(input.snapshot, dependency);
930
+ if (pinned && pinned.digest !== resolved.digest) {
931
+ return { status: 'unresolved', message: `Model Variable ${dependency.id}@${dependency.revision} digest changed` };
932
+ }
933
+ if (input.phase === 'historical_read')
934
+ continue;
935
+ const definitionResult = validateFoundationDefinition(resolved.definition, { use: 'judgment' });
936
+ if (!definitionResult.valid) {
937
+ const notApplicable = definitionResult.issues.some((issue) => issue.code === 'UNAUTHORIZED_USE');
938
+ return {
939
+ status: notApplicable ? 'not_applicable' : 'unresolved',
940
+ message: definitionResult.issues.map((issue) => issue.message).join('; ')
941
+ };
942
+ }
943
+ const applicability = validateFoundationReferenceApplicability(input.reference, resolved.definition);
944
+ if (!applicability.valid)
945
+ return { status: 'not_applicable', message: applicability.message };
946
+ }
947
+ return undefined;
948
+ }
949
+ function isVariableRevision(value) {
950
+ return isPlainRecord(value)
951
+ && value.type === 'variable'
952
+ && typeof value.id === 'string'
953
+ && value.id.length > 0
954
+ && typeof value.revision === 'string'
955
+ && /^[1-9]\d*$/u.test(value.revision);
956
+ }
957
+ function matchingFoundationRecord(record, reference, type) {
958
+ return typeof record.digest === 'string'
959
+ && isPlainRecord(record.definition)
960
+ && record.definition.type === type
961
+ && record.definition.id === reference.id
962
+ && record.definition.revision === reference.revision;
963
+ }
964
+ function findSnapshotReference(snapshot, reference) {
965
+ return snapshot?.references.find((candidate) => (candidate.kind === reference.type
966
+ && candidate.id === reference.id
967
+ && candidate.revision === reference.revision));
968
+ }
969
+ async function resolveObservationReference(input, options, objectiveReader) {
970
+ if (!options.resolveOther)
971
+ return { status: 'unresolved', message: 'No canonical Observation provider is registered' };
972
+ const resolved = await options.resolveOther(input);
973
+ if (resolved.status !== 'resolved')
974
+ return resolved;
975
+ const descriptor = input.reference.measurement;
976
+ const canonical = resolved.canonical?.measurement;
977
+ if (input.phase === 'historical_read') {
978
+ if (descriptor !== undefined && canonical !== undefined && !sameMeasurementDescriptor(descriptor, canonical)) {
979
+ return { status: 'unresolved', message: 'Historical Observation measurement metadata changed' };
980
+ }
981
+ return resolved;
982
+ }
983
+ if (descriptor === undefined || canonical === undefined) {
984
+ return { status: 'unresolved', message: 'Current Observation requires canonical measurement metadata and a pinned descriptor' };
985
+ }
986
+ if (!sameMeasurementDescriptor(descriptor, canonical)) {
987
+ return { status: 'unresolved', message: 'Observation measurement descriptor does not match canonical metadata' };
988
+ }
989
+ const snapshot = input.snapshot;
990
+ if (!snapshot)
991
+ return { status: 'unresolved', message: 'Current Observation validation requires the complete snapshot bundle' };
992
+ const objectiveReference = snapshot.references.find((candidate) => candidate.kind === 'objective');
993
+ if (!objectiveReference)
994
+ return { status: 'unresolved', message: 'Current Observation requires an Objective reference' };
995
+ const objectiveRecord = await options.store.read({ id: objectiveReference.id, type: 'objective', revision: objectiveReference.revision }, { principal: input.context.principal });
996
+ if (!objectiveRecord)
997
+ return { status: 'missing', message: 'Objective for Observation measurement was not found' };
998
+ const objectiveRevision = {
999
+ id: objectiveReference.id,
1000
+ type: 'objective',
1001
+ revision: objectiveReference.revision
1002
+ };
1003
+ if (!matchingFoundationRecord(objectiveRecord, objectiveRevision, 'objective')) {
1004
+ return { status: 'unresolved', message: 'Objective for Observation measurement is not the requested revision' };
1005
+ }
1006
+ if (objectiveRecord.digest !== objectiveReference.digest) {
1007
+ return { status: 'unresolved', message: 'Objective for Observation measurement digest changed' };
1008
+ }
1009
+ const objectiveDefinition = objectiveRecord.definition;
1010
+ const objectiveApplicability = validateFoundationReferenceApplicability(objectiveReference, objectiveDefinition);
1011
+ if (!objectiveApplicability.valid)
1012
+ return { status: 'not_applicable', message: objectiveApplicability.message };
1013
+ const readiness = await checkObjectiveReadiness(objectiveReader, objectiveReference.id, { principal: input.context.principal }, objectiveReference.revision);
1014
+ if (!readiness.ready)
1015
+ return objectiveReadinessResult(readiness.issues);
1016
+ const variableRecord = await options.store.read(descriptor.variableRef, { principal: input.context.principal });
1017
+ if (!variableRecord)
1018
+ return { status: 'missing', message: 'Observation measurement Variable was not found' };
1019
+ if (!matchingFoundationRecord(variableRecord, descriptor.variableRef, 'variable')) {
1020
+ return { status: 'unresolved', message: 'Observation measurement Variable is not the requested revision' };
1021
+ }
1022
+ const pinnedVariable = findSnapshotReference(snapshot, descriptor.variableRef);
1023
+ if (pinnedVariable && pinnedVariable.digest !== variableRecord.digest) {
1024
+ return { status: 'unresolved', message: 'Observation measurement Variable digest changed' };
1025
+ }
1026
+ const compatibility = validateEvaluationCompatibility(variableRecord.definition, descriptor, { period: objectiveDefinition.evaluationPeriod });
1027
+ if (!compatibility.valid) {
1028
+ const notApplicable = compatibility.issues.some((issue) => issue.code === 'UNAUTHORIZED_USE');
1029
+ return {
1030
+ status: notApplicable ? 'not_applicable' : 'unresolved',
1031
+ message: compatibility.issues.map((issue) => issue.message).join('; ')
1032
+ };
1033
+ }
1034
+ if (!scopeContainsReference(descriptor.scope, input.reference)) {
1035
+ return { status: 'not_applicable', message: 'Observation measurement scope does not cover the Observation reference' };
1036
+ }
1037
+ return resolved;
1038
+ }
1039
+ function sameMeasurementDescriptor(left, right) {
1040
+ return canonicalJson(left) === canonicalJson(right);
1041
+ }
746
1042
  function validateFoundationReferenceApplicability(reference, definition) {
747
1043
  const scopes = definition.type === 'model'
748
1044
  ? [definition.scope, definition.applicability]
@@ -0,0 +1,166 @@
1
+ import { type JudgmentValueProof } from './judgment-value-proof.js';
2
+ export declare const JUDGMENT_VALUE_PROOF_FILE_SUFFIX = ".value-proof.json";
3
+ export declare const JUDGMENT_VALUE_PROOF_FEEDBACK_SCHEMA = "brainbase-judgment-value-proof-feedback-v1";
4
+ export declare const JUDGMENT_VALUE_PROOF_FEEDBACK_FILE = "judgment-value-proof-feedback.jsonl";
5
+ export type JudgmentValueProofReviewSection = 'needs_human' | 'blocked' | 'continued' | 'other';
6
+ export type JudgmentValueProofFeedbackStatus = 'accepted' | 'corrected' | 'next_time_ask' | 'reverted';
7
+ /** What the owner's feedback asks to change. `delegation` is the ask/continue boundary. */
8
+ export type JudgmentValueProofFeedbackLayer = 'delegation' | 'method' | 'objective' | 'world_model' | 'philosophy' | 'other';
9
+ export declare const JUDGMENT_VALUE_PROOF_FEEDBACK_LAYERS: readonly JudgmentValueProofFeedbackLayer[];
10
+ export interface JudgmentValueProofJournalEntry {
11
+ readonly file: string;
12
+ readonly proof: JudgmentValueProof;
13
+ }
14
+ export interface JudgmentValueProofJournalRejection {
15
+ readonly file: string;
16
+ readonly reason: string;
17
+ }
18
+ export type JudgmentValueProofJournalRead = {
19
+ readonly status: 'unavailable';
20
+ readonly root: string;
21
+ readonly reason: string;
22
+ } | {
23
+ readonly status: 'available';
24
+ readonly root: string;
25
+ readonly entries: readonly JudgmentValueProofJournalEntry[];
26
+ readonly rejected: readonly JudgmentValueProofJournalRejection[];
27
+ readonly latest_recorded_at: string | null;
28
+ };
29
+ export interface JudgmentValueProofFeedbackRecord {
30
+ readonly schema_version: typeof JUDGMENT_VALUE_PROOF_FEEDBACK_SCHEMA;
31
+ readonly feedback_id: string;
32
+ readonly intent_id: string;
33
+ readonly decision_attempt_id: string;
34
+ readonly status: JudgmentValueProofFeedbackStatus;
35
+ readonly summary: string | null;
36
+ /** Absent on records written before the layer was introduced. */
37
+ readonly target_layer?: JudgmentValueProofFeedbackLayer | null;
38
+ readonly recorded_at: string;
39
+ }
40
+ export interface JudgmentValueProofFeedbackInput {
41
+ readonly dataDir?: string;
42
+ readonly intent_id: string;
43
+ readonly decision_attempt_id: string;
44
+ readonly status: JudgmentValueProofFeedbackStatus;
45
+ readonly summary?: string | null;
46
+ readonly target_layer?: JudgmentValueProofFeedbackLayer | null;
47
+ readonly now?: Date;
48
+ }
49
+ export interface JudgmentValueProofReviewItem {
50
+ readonly file: string;
51
+ readonly section: JudgmentValueProofReviewSection;
52
+ readonly proof: JudgmentValueProof;
53
+ readonly feedback_history: readonly JudgmentValueProofFeedbackRecord[];
54
+ }
55
+ export type JudgmentValueProofReviewHome = {
56
+ readonly status: 'unavailable';
57
+ readonly root: string;
58
+ readonly reason: string;
59
+ } | {
60
+ readonly status: 'available';
61
+ readonly root: string;
62
+ readonly coverage: {
63
+ readonly saved: number;
64
+ readonly rejected: number;
65
+ readonly latest_recorded_at: string | null;
66
+ readonly possibly_stalled: boolean;
67
+ };
68
+ readonly sections: Readonly<Record<JudgmentValueProofReviewSection, readonly JudgmentValueProofReviewItem[]>>;
69
+ readonly delegation_map: JudgmentDelegationMap;
70
+ readonly rejected: readonly JudgmentValueProofJournalRejection[];
71
+ };
72
+ /** Whether Brainbase currently proceeds on a kind of judgment, is being checked, or asks the owner. */
73
+ export type JudgmentDelegationState = 'delegated' | 'verifying' | 'returned';
74
+ /** Rows are kinds when recorded; otherwise the reason code, which is not a kind and is shown apart. */
75
+ export type JudgmentDelegationRowSource = 'judgment_kind' | 'reason_code' | 'unrecorded';
76
+ export type JudgmentDelegationStateBasis = {
77
+ readonly reason: 'latest_judgment_returned';
78
+ readonly at: string;
79
+ } | {
80
+ readonly reason: 'latest_feedback';
81
+ readonly status: JudgmentValueProofFeedbackStatus;
82
+ readonly target_layer: JudgmentValueProofFeedbackLayer | null;
83
+ readonly at: string;
84
+ } | {
85
+ readonly reason: 'no_feedback';
86
+ };
87
+ export interface JudgmentDelegationItemRef {
88
+ readonly intent_id: string;
89
+ readonly decision_attempt_id: string;
90
+ readonly row_key: string;
91
+ }
92
+ export interface JudgmentDelegationRow {
93
+ readonly key: string;
94
+ readonly source: JudgmentDelegationRowSource;
95
+ /** Kind label or reason code; null for judgments that recorded neither. */
96
+ readonly label: string | null;
97
+ readonly state: JudgmentDelegationState;
98
+ readonly state_basis: JudgmentDelegationStateBasis;
99
+ readonly counts: {
100
+ readonly continued: number;
101
+ readonly returned: number;
102
+ readonly rated: number;
103
+ readonly by_feedback: Readonly<Record<JudgmentValueProofFeedbackStatus, number>>;
104
+ readonly corrected_or_reverted: number;
105
+ readonly inherited: number;
106
+ };
107
+ readonly latest_recorded_at: string;
108
+ /** Newest first. */
109
+ readonly items: readonly JudgmentDelegationItemRef[];
110
+ }
111
+ export interface JudgmentDelegationMap {
112
+ readonly judged: number;
113
+ readonly kind_recorded: number;
114
+ readonly rated: number;
115
+ /** Kind rows first, then reason-code rows, then the unrecorded row. */
116
+ readonly rows: readonly JudgmentDelegationRow[];
117
+ /** Continued without asking, then corrected or reverted by the owner (P14). */
118
+ readonly corrected_after_continue: readonly JudgmentDelegationItemRef[];
119
+ /** Continued without asking after the owner asked to be asked for that row. */
120
+ readonly continued_after_ask: readonly JudgmentDelegationItemRef[];
121
+ }
122
+ export declare function defaultJudgmentJournalRoot(dataDir?: string): string;
123
+ /**
124
+ * Reads saved judgment value proofs from `<root>/*.value-proof.json` and
125
+ * `<root>/<session>/*.value-proof.json`. The journal is read-only here.
126
+ */
127
+ export declare function readJudgmentValueProofJournal(options?: {
128
+ readonly root?: string;
129
+ }): Promise<JudgmentValueProofJournalRead>;
130
+ export declare function classifyJudgmentValueProof(proof: JudgmentValueProof): JudgmentValueProofReviewSection;
131
+ /** Reads all recorded feedback in append order. A missing file means no feedback has been recorded. */
132
+ export declare function readJudgmentValueProofFeedback(options?: {
133
+ readonly dataDir?: string;
134
+ }): Promise<readonly JudgmentValueProofFeedbackRecord[]>;
135
+ /**
136
+ * Appends one owner feedback record without touching the judgment journal.
137
+ * Re-sending the same attempt, status and summary returns the existing record.
138
+ */
139
+ export declare function recordJudgmentValueProofFeedback(input: JudgmentValueProofFeedbackInput): Promise<{
140
+ readonly record: JudgmentValueProofFeedbackRecord;
141
+ readonly created: boolean;
142
+ }>;
143
+ /** Returns the proof with its latest owner feedback applied, plus the feedback history for that attempt. */
144
+ export declare function applyJudgmentValueProofFeedback(proof: JudgmentValueProof, records: readonly JudgmentValueProofFeedbackRecord[]): {
145
+ readonly proof: JudgmentValueProof;
146
+ readonly history: readonly JudgmentValueProofFeedbackRecord[];
147
+ };
148
+ export declare function buildJudgmentValueProofReviewHome(journal: JudgmentValueProofJournalRead, feedback: readonly JudgmentValueProofFeedbackRecord[], options?: {
149
+ readonly now?: Date;
150
+ readonly staleAfterMs?: number;
151
+ }): JudgmentValueProofReviewHome;
152
+ /**
153
+ * Groups reviewable judgments by kind and decides, per kind, whether Brainbase is trusted to
154
+ * proceed, is still being checked, or returns the judgment to the owner. Deterministic: the state
155
+ * follows only the newest judgment and the newest owner feedback in the row.
156
+ */
157
+ export declare function buildJudgmentDelegationMap(items: readonly JudgmentValueProofReviewItem[], feedback: readonly JudgmentValueProofFeedbackRecord[]): JudgmentDelegationMap;
158
+ export interface JudgmentValueProofFeedbackSummary {
159
+ /** Decisions with at least one feedback record; each counts once, by its latest feedback. */
160
+ readonly rated: number;
161
+ readonly by_status: Readonly<Record<JudgmentValueProofFeedbackStatus, number>>;
162
+ /** Latest `corrected` and `next_time_ask` feedback by the layer they ask to change. */
163
+ readonly by_layer: Readonly<Record<JudgmentValueProofFeedbackLayer | 'unrecorded', number>>;
164
+ }
165
+ /** Counts the latest feedback per decision, for deciding where revisions should live. */
166
+ export declare function summarizeJudgmentValueProofFeedback(records: readonly JudgmentValueProofFeedbackRecord[]): JudgmentValueProofFeedbackSummary;