@unson/brainbase-mcp 0.15.6 → 0.16.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/dist/cli.js CHANGED
@@ -22,7 +22,6 @@ import { blockedJudgmentOutput, processJudgmentHook } from './judgment-host.js';
22
22
  import { applyCanonicalWrites, buildCanonicalEdge } from './canonical-edge-builder.js';
23
23
  import { defaultJudgmentJournalRoot } from './judgment-value-proof-review.js';
24
24
  import { createWorldExtension, readWorldVocabulary } from './world-extension.js';
25
- import { createOrganizationGraphSource } from './organization-graph-web.js';
26
25
  import { createLocalWebHost, LOCAL_WEB_DEFAULT_PORT } from './local-web-host.js';
27
26
  export async function runCli(argv = process.argv.slice(2), io = process) {
28
27
  const parsed = parseArgs(argv);
@@ -1052,30 +1051,16 @@ async function webServe(parsed, io, command) {
1052
1051
  if (worldVocabularyPath !== undefined && !world)
1053
1052
  throw new Error(`${command} --world-vocabulary requires --world`);
1054
1053
  const worldVocabulary = worldVocabularyPath === undefined ? undefined : await readWorldVocabulary(resolve(worldVocabularyPath));
1055
- // Ledger C1: the Graph screens read the owner's organization Graph read only.
1056
- let organizationGraph = null;
1057
- if (parsed.flags.has('organization-graph')) {
1058
- const source = await createOrganizationGraphSource();
1059
- if ('reason' in source) {
1060
- throw new Error(`${command} --organization-graph cannot read the organization Graph (${source.reason}); sign in with brainbase auth first`);
1054
+ // The organization Graph is shown by the organization web, not by this local host (C1 ended 2026-10-05).
1055
+ for (const removed of ['organization-graph', 'organization-web']) {
1056
+ if (parsed.flags.has(removed) || first(parsed, removed) !== undefined) {
1057
+ throw new Error(`${command} --${removed} was removed; open the organization web to see the organization Graph`);
1061
1058
  }
1062
- organizationGraph = source;
1063
- }
1064
- // Where the organization's records are corrected; the read-only screens link to it.
1065
- const organizationWeb = first(parsed, 'organization-web');
1066
- if (organizationWeb !== undefined) {
1067
- if (!organizationGraph)
1068
- throw new Error(`${command} --organization-web requires --organization-graph`);
1069
- if (!/^https:\/\/[^/?#@\s]+$/u.test(organizationWeb)) {
1070
- throw new Error(`${command} --organization-web must be an https origin such as https://org.example.com`);
1071
- }
1072
- organizationGraph = { ...organizationGraph, webUrl: organizationWeb };
1073
1059
  }
1074
1060
  const { server } = createLocalWebHost({
1075
1061
  dataDir,
1076
1062
  journalRoot,
1077
- extensions: world ? [createWorldExtension(worldVocabulary ? { vocabulary: worldVocabulary } : {})] : [],
1078
- ...(organizationGraph ? { organizationGraph } : {})
1063
+ extensions: world ? [createWorldExtension(worldVocabulary ? { vocabulary: worldVocabulary } : {})] : []
1079
1064
  });
1080
1065
  await new Promise((resolveListen, rejectListen) => {
1081
1066
  server.once('error', rejectListen);
@@ -1091,8 +1076,6 @@ async function webServe(parsed, io, command) {
1091
1076
  `- プロジェクトと関係者: ${origin}/#projects`,
1092
1077
  `- 情報と関係: ${origin}/#graph`,
1093
1078
  ...(world ? [`- 世界: ${origin}/#world`] : []),
1094
- ...(organizationGraph ? [`組織のGraph(読み取りのみ): ${organizationGraph.server}`] : []),
1095
- ...(organizationGraph?.webUrl ? [`組織版(訂正の入口): ${organizationGraph.webUrl}`] : []),
1096
1079
  `データ: ${dataDir}`,
1097
1080
  `判断journal: ${journalRoot}`,
1098
1081
  '終了: Ctrl+C',
@@ -1249,7 +1232,7 @@ function usage() {
1249
1232
  brainbase judgment:install --target codex [--autonomy-mode off|canary|on] [--autonomy-project code] [--dry-run] [--output path]
1250
1233
  brainbase judgment:hook [--autonomy-mode off|canary|on] [--autonomy-project code]
1251
1234
  brainbase doctor [--dir path] [--judgment-hooks path]
1252
- brainbase web:serve [--dir path] [--journal path] [--port n] [--world [--world-vocabulary file]] [--organization-graph [--organization-web https-origin]]
1235
+ brainbase web:serve [--dir path] [--journal path] [--port n] [--world [--world-vocabulary file]]
1253
1236
  brainbase review:serve [--dir path] [--journal path] [--port n] (web:serveの別名)
1254
1237
  `;
1255
1238
  }
@@ -1,5 +1,4 @@
1
1
  import type { IncomingMessage, ServerResponse } from 'node:http';
2
- import { type GraphWebAnySource, type InMemoryGraphReader } from './graph-web.js';
3
2
  export declare const GRAPH_WEB_HTTP_VERSION: "graph-web-http.v1";
4
3
  export interface GraphWebHttpOptions {
5
4
  /** Personal OS data directory chosen by the host. Never taken from the request. */
@@ -12,12 +11,6 @@ export interface GraphWebHttpOptions {
12
11
  */
13
12
  readonly assertWriteAllowed?: (request: IncomingMessage) => void | Promise<void>;
14
13
  readonly now?: () => Date;
15
- /**
16
- * Ledger C1: read every route from a Graph the host holds in memory (the
17
- * owner's organization Graph) instead of the data directory. Corrections
18
- * are refused as read only.
19
- */
20
- readonly readGraph?: () => Promise<InMemoryGraphReader<GraphWebAnySource>>;
21
14
  }
22
15
  export type GraphWebHttpHandler = (request: IncomingMessage, response: ServerResponse) => Promise<boolean>;
23
16
  /**
@@ -52,9 +52,6 @@ export function createGraphWebHttpHandler(options) {
52
52
  if (route.name === 'corrections') {
53
53
  if (request.method !== 'POST')
54
54
  throw methodNotAllowed('POST');
55
- if (options.readGraph) {
56
- throw new HttpError(403, 'organization_graph_read_only', 'This host reads the organization Graph read only; correct it in the organization edition');
57
- }
58
55
  await assertWriteAllowed(options.assertWriteAllowed, request);
59
56
  const body = await readJsonBody(request);
60
57
  const result = await applyGraphCorrection(dataDir, body, { now: now() });
@@ -65,17 +62,6 @@ export function createGraphWebHttpHandler(options) {
65
62
  throw methodNotAllowed('GET');
66
63
  const asOf = queryValue(url, 'as_of');
67
64
  const readOptions = { ...(asOf !== undefined ? { asOf } : {}), now: now() };
68
- if (options.readGraph) {
69
- const reader = await options.readGraph();
70
- const body = route.name === 'status' ? reader.status(readOptions)
71
- : route.name === 'projects' ? reader.listProjects(readOptions)
72
- : route.name === 'project' ? reader.readProject(route.id, readOptions)
73
- : route.name === 'search' ? reader.search({ q: queryValue(url, 'q'), type: queryValue(url, 'type'), asOf, limit: parseLimit(queryValue(url, 'limit')), now: readOptions.now })
74
- : route.name === 'entity' ? reader.readEntity(route.id, readOptions)
75
- : reader.ontology(readOptions);
76
- writeJson(response, 200, body);
77
- return true;
78
- }
79
65
  switch (route.name) {
80
66
  case 'status':
81
67
  writeJson(response, 200, await readGraphWebStatus(dataDir, readOptions));
@@ -1,7 +1,7 @@
1
1
  import { GRAPH_CORRECTIONS_FILE, type GraphCorrectionRecord } from './graph-corrections.js';
2
2
  import { type PortableGraphOwner } from './portable-graph.js';
3
3
  import type { FoundationAdoptionState, FoundationOperator } from './ontology-foundation.js';
4
- import type { CanonicalEdge, CanonicalEntityKind, CoreRelation, GraphFileV2 } from './types.js';
4
+ import type { CanonicalEdge, CanonicalEntityKind, CoreRelation } from './types.js';
5
5
  /**
6
6
  * Read views of the canonical local Graph for the local Web screens
7
7
  * "プロジェクトと関係者" and "情報と関係".
@@ -34,20 +34,7 @@ export interface GraphWebOwnerPrivateSource {
34
34
  graphFormat: 2;
35
35
  authority: 'owner_private';
36
36
  }
37
- /**
38
- * The owner's organization Graph read through the host (ledger C1): read only,
39
- * held in memory, never written to the data directory.
40
- */
41
- export interface GraphWebOrganizationSource {
42
- dataDir: null;
43
- graphFormat: 2;
44
- authority: 'organization_graph';
45
- /** Organization server origin, without credentials. */
46
- server: string;
47
- /** When the host last read the organization Graph. */
48
- readAt: string;
49
- }
50
- export type GraphWebAnySource = GraphWebSource | GraphWebOwnerPrivateSource | GraphWebOrganizationSource;
37
+ export type GraphWebAnySource = GraphWebSource | GraphWebOwnerPrivateSource;
51
38
  export interface GraphMigrationRequired {
52
39
  status: 'migration_required';
53
40
  source: GraphWebSource;
@@ -374,21 +361,4 @@ export interface OwnerPrivateGraph {
374
361
  * machine, so a relation's source ID is shown unresolved.
375
362
  */
376
363
  export declare function openOwnerPrivateGraph(bundle: unknown, owner: PortableGraphOwner): OwnerPrivateGraph;
377
- /** Read views of one in-memory Graph v2 held by the host (no data directory, no corrections). */
378
- export interface InMemoryGraphReader<S extends GraphWebAnySource> {
379
- readonly source: S;
380
- status(options?: GraphWebReadOptions): GraphWebStatus<S>;
381
- listProjects(options?: GraphWebReadOptions): GraphProjectList<S>;
382
- readProject(projectId: string, options?: GraphWebReadOptions): GraphProjectDetail<S>;
383
- search(input?: GraphEntitySearchInput): GraphEntitySearchResult<S>;
384
- readEntity(entityId: string, options?: GraphWebReadOptions): GraphEntityDetail<S>;
385
- ontology(options?: GraphWebReadOptions): GraphOntologySummary<S>;
386
- }
387
- /**
388
- * The same read views as the local Graph routes over a Graph v2 the host
389
- * holds in memory (the organization Graph read under C1). The Graph is
390
- * checked with the canonical validator first; nothing is written, and there
391
- * are no correction records or relation sources to resolve.
392
- */
393
- export declare function openInMemoryGraph<S extends GraphWebAnySource>(graph: GraphFileV2, source: S): InMemoryGraphReader<S>;
394
364
  export {};
package/dist/graph-web.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { access, readdir, readFile } from 'node:fs/promises';
2
2
  import { join } from 'node:path';
3
- import { isActiveAt, validateCanonicalGraph } from './canonical-graph.js';
3
+ import { isActiveAt } from './canonical-graph.js';
4
4
  import { assertFoundationCatalog, findFoundationRecord } from './foundation-catalog.js';
5
5
  import { GRAPH_CORRECTION_EDGE_FIELDS, GRAPH_CORRECTION_ENTITY_FIELDS, GRAPH_CORRECTION_NEW_EDGE_RELATIONS, GRAPH_CORRECTION_PROJECT_FIELDS, GRAPH_CORRECTIONS_FILE, graphRecordDigest, readGraphCorrectionHistory } from './graph-corrections.js';
6
6
  import { ONTOLOGY_VERSION, portableOntology } from './ontology.js';
@@ -305,46 +305,6 @@ export function openOwnerPrivateGraph(bundle, owner) {
305
305
  }
306
306
  });
307
307
  }
308
- /**
309
- * The same read views as the local Graph routes over a Graph v2 the host
310
- * holds in memory (the organization Graph read under C1). The Graph is
311
- * checked with the canonical validator first; nothing is written, and there
312
- * are no correction records or relation sources to resolve.
313
- */
314
- export function openInMemoryGraph(graph, source) {
315
- try {
316
- validateCanonicalGraph(graph);
317
- }
318
- catch (error) {
319
- throw new GraphWebError('unavailable', 'graph_invalid', errorMessage(error));
320
- }
321
- return Object.freeze({
322
- source,
323
- status: (options = {}) => {
324
- const asOf = resolveAsOf(options);
325
- const entities = Object.fromEntries(ENTITY_KINDS.map((kind) => [kind, graph.entities.filter((entity) => entity.type === kind).length]));
326
- return {
327
- status: 'ok',
328
- version: GRAPH_WEB_VERSION,
329
- source,
330
- asOf,
331
- ontology: { ...graph.ontology, currentVersion: ONTOLOGY_VERSION },
332
- counts: {
333
- entities: { ...entities, total: graph.entities.length },
334
- edges: { total: graph.edges.length, active: graph.edges.filter((edge) => isActiveAt(edge, asOf)).length },
335
- relationships: 0,
336
- corrections: 0
337
- },
338
- issues: []
339
- };
340
- },
341
- listProjects: (options = {}) => projectListOf(graph, source, resolveAsOf(options)),
342
- readProject: (projectId, options = {}) => projectDetailOf(graph, source, projectId, resolveAsOf(options), NO_REFERENCES),
343
- search: (input = {}) => entitySearchOf(graph, source, parseSearchInput(input)),
344
- readEntity: (entityId, options = {}) => entityDetailOf(graph, source, entityId, resolveAsOf(options), NO_REFERENCES),
345
- ontology: (options = {}) => ontologyOf(graph, source, resolveAsOf(options))
346
- });
347
- }
348
308
  function decisionRecordView(record) {
349
309
  return {
350
310
  title: record.title,
@@ -1,8 +1,6 @@
1
1
  import { type IncomingMessage, type Server, type ServerResponse } from 'node:http';
2
2
  import { type ObjectiveDefinitionBuilder } from './foundation-http.js';
3
- import type { GraphWebAnySource, InMemoryGraphReader } from './graph-web.js';
4
3
  import { JudgmentValueProofJournalCache } from './judgment-value-proof-review.js';
5
- import type { GraphFileV2 } from './types.js';
6
4
  /**
7
5
  * General local single-owner Web host (`brainbase web:serve`).
8
6
  *
@@ -41,8 +39,6 @@ export interface LocalWebModuleContext {
41
39
  readonly journalRoot: string;
42
40
  readonly token: string;
43
41
  readonly now: () => Date;
44
- /** Present when the host reads the organization Graph (C1). */
45
- readonly organizationGraph?: LocalWebOrganizationGraph;
46
42
  /** The host's one listing cache for the judgment journal, shared by every screen that reads it. */
47
43
  readonly journalCache: JudgmentValueProofJournalCache;
48
44
  }
@@ -80,25 +76,6 @@ export interface LocalWebHostOptions {
80
76
  readonly uiDir?: string;
81
77
  readonly now?: () => Date;
82
78
  readonly extensions?: readonly LocalWebExtension[];
83
- /**
84
- * Ledger C1: the owner's organization Graph, read only. When given, the Graph
85
- * screens read it instead of the data directory and the status reports it.
86
- */
87
- readonly organizationGraph?: LocalWebOrganizationGraph;
88
- }
89
- export interface LocalWebOrganizationGraph {
90
- /** Organization server origin shown as the source, without credentials. */
91
- readonly server: string;
92
- /** The organization's own web (where its records are corrected), when the owner configured it. */
93
- readonly webUrl?: string;
94
- read(): Promise<InMemoryGraphReader<GraphWebAnySource>>;
95
- /** The same Graph as plain records (the world reads project metadata from it). */
96
- readGraphFile?(): Promise<GraphFileV2>;
97
- /**
98
- * The organization's words for field values (glossary terms with `payload.vocabulary`), or null when
99
- * they could not be read. The rest of the Graph is read even when these are not.
100
- */
101
- readVocabularyTerms?(): Promise<readonly GraphVocabularyTerm[] | null>;
102
79
  }
103
80
  /** One organization term that names a field value, e.g. project.kind = product → プロダクト. */
104
81
  export interface GraphVocabularyTerm {
@@ -138,9 +138,7 @@ export function createLocalStatusModule(context) {
138
138
  writeJson(response, 405, { error: { code: 'method_not_allowed', message: 'Use GET' } });
139
139
  return true;
140
140
  }
141
- const local = await readLocalGraphState(context.dataDir);
142
- // Under C1 the Graph screens read the organization Graph, so they are not gated on the local file.
143
- const graph = context.organizationGraph ? { ...local, status: 'ready', format: 'v2', message: undefined } : local;
141
+ const graph = await readLocalGraphState(context.dataDir);
144
142
  writeJson(response, 200, {
145
143
  version: LOCAL_WEB_HOST_VERSION,
146
144
  data_dir: context.dataDir,
@@ -150,15 +148,7 @@ export function createLocalStatusModule(context) {
150
148
  format: graph.format,
151
149
  ...(graph.message ? { message: graph.message } : {}),
152
150
  commands: graphCommands(graph, context.dataDir)
153
- },
154
- organization_graph: context.organizationGraph
155
- ? {
156
- status: 'connected',
157
- server: context.organizationGraph.server,
158
- mode: 'read_only',
159
- ...(context.organizationGraph.webUrl ? { web_url: context.organizationGraph.webUrl } : {})
160
- }
161
- : { status: 'not_connected' }
151
+ }
162
152
  });
163
153
  return true;
164
154
  }
@@ -683,7 +673,6 @@ export function createGraphWebModule(context) {
683
673
  dataDir: context.dataDir,
684
674
  basePath: LOCAL_WEB_GRAPH_PREFIX,
685
675
  now: context.now,
686
- ...(context.organizationGraph ? { readGraph: context.organizationGraph.read } : {}),
687
676
  assertWriteAllowed(request) {
688
677
  if (isTrustedLocalWrite(request, context.token))
689
678
  return;
@@ -782,8 +771,7 @@ export function createLocalWebHost(options = {}) {
782
771
  journalRoot,
783
772
  token,
784
773
  now: options.now ?? (() => new Date()),
785
- journalCache: new JudgmentValueProofJournalCache(),
786
- ...(options.organizationGraph ? { organizationGraph: options.organizationGraph } : {})
774
+ journalCache: new JudgmentValueProofJournalCache()
787
775
  };
788
776
  const modules = defaultLocalWebModules(context);
789
777
  const extensions = [...options.extensions ?? []];
@@ -1,10 +1,10 @@
1
- import type { GraphVocabularyTerm, LocalWebOrganizationGraph } from './local-web-host.js';
1
+ import type { GraphVocabularyTerm } from './local-web-host.js';
2
2
  import type { CanonicalEntityKind, GraphFileV2 } from './types.js';
3
3
  /**
4
- * Ledger C1 (adopted 2026-09-24): while the owner validates the Web, the host
5
- * reads the owner's organization Graph read only. The records are projected
6
- * into an in-memory Graph v2 so the existing Graph screens read them through
7
- * the same views as a local Graph. Nothing is written locally or remotely.
4
+ * Projects organization Graph records (as an organization host reads them for
5
+ * its member) into a Graph v2, so shared parts such as the world draw them the
6
+ * same way as a local Graph. This module never reads a network or a token: the
7
+ * host that owns the records passes them in (`projectOrganizationWorld`).
8
8
  *
9
9
  * Relations come only from what the organization Graph states:
10
10
  * - `member_of` person → project → `participates_in` (role = role_code)
@@ -55,26 +55,4 @@ export declare function projectOrganizationGraph(records: OrganizationGraphRecor
55
55
  id: string;
56
56
  name?: string;
57
57
  }): OrganizationGraphProjection;
58
- interface OrganizationAccess {
59
- readonly server: string;
60
- readonly token: string;
61
- }
62
- /** The owner's Brainbase server and token, read at request time (tokens rotate hourly). Never logged or returned. */
63
- export declare function readOrganizationAccess(home?: string): Promise<OrganizationAccess | {
64
- readonly reason: string;
65
- }>;
66
- export interface OrganizationGraphSourceOptions {
67
- readonly home?: string;
68
- readonly fetch?: typeof fetch;
69
- readonly now?: () => Date;
70
- readonly cacheMs?: number;
71
- }
72
- /**
73
- * The organization Graph for the local host (C1). Reads are cached briefly and
74
- * shared while in flight; a failure is reported as `unavailable`, never as an
75
- * empty Graph.
76
- */
77
- export declare function createOrganizationGraphSource(options?: OrganizationGraphSourceOptions): Promise<LocalWebOrganizationGraph | {
78
- readonly reason: string;
79
- }>;
80
58
  export {};
@@ -1,14 +1,10 @@
1
- import { readFile } from 'node:fs/promises';
2
- import { homedir } from 'node:os';
3
- import { join } from 'node:path';
4
- import { GraphWebError, openInMemoryGraph } from './graph-web.js';
5
1
  import { canonicalEdgeId } from './canonical-graph.js';
6
2
  import { canonicalGraphOntologyRelease } from './templates.js';
7
3
  /**
8
- * Ledger C1 (adopted 2026-09-24): while the owner validates the Web, the host
9
- * reads the owner's organization Graph read only. The records are projected
10
- * into an in-memory Graph v2 so the existing Graph screens read them through
11
- * the same views as a local Graph. Nothing is written locally or remotely.
4
+ * Projects organization Graph records (as an organization host reads them for
5
+ * its member) into a Graph v2, so shared parts such as the world draw them the
6
+ * same way as a local Graph. This module never reads a network or a token: the
7
+ * host that owns the records passes them in (`projectOrganizationWorld`).
12
8
  *
13
9
  * Relations come only from what the organization Graph states:
14
10
  * - `member_of` person → project → `participates_in` (role = role_code)
@@ -19,8 +15,6 @@ import { canonicalGraphOntologyRelease } from './templates.js';
19
15
  */
20
16
  export const ORGANIZATION_GRAPH_WEB_VERSION = 'organization-graph-web.v0';
21
17
  const ENTITY_TYPES = ['project', 'person', 'org', 'decision'];
22
- const FETCH_TIMEOUT_MS = 15_000;
23
- const CACHE_MS = 60_000;
24
18
  function isRecord(value) {
25
19
  return value !== null && typeof value === 'object' && !Array.isArray(value);
26
20
  }
@@ -190,121 +184,3 @@ export function projectOrganizationGraph(records, owner) {
190
184
  excluded: { inactive, unnamed, danglingRelations }
191
185
  };
192
186
  }
193
- /** The owner's Brainbase server and token, read at request time (tokens rotate hourly). Never logged or returned. */
194
- export async function readOrganizationAccess(home = homedir()) {
195
- try {
196
- const config = JSON.parse(await readFile(join(home, '.brainbase', 'config.json'), 'utf8'));
197
- const tokens = JSON.parse(await readFile(join(home, '.brainbase', 'tokens.json'), 'utf8'));
198
- const server = isRecord(config) ? text(config.server_url) : null;
199
- const token = isRecord(tokens) ? text(tokens.access_token) : null;
200
- if (!server || !/^https:\/\/[^/?#@]+$/u.test(server))
201
- return { reason: 'server_url_missing_or_invalid' };
202
- if (!token)
203
- return { reason: 'access_token_missing' };
204
- return { server, token };
205
- }
206
- catch {
207
- return { reason: 'brainbase_auth_files_unreadable' };
208
- }
209
- }
210
- async function getRecords(request, access, path) {
211
- const controller = new AbortController();
212
- const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
213
- try {
214
- const response = await request(`${access.server}/api/info/graph/${path}`, {
215
- method: 'GET',
216
- headers: { Authorization: `Bearer ${access.token}`, Accept: 'application/json' },
217
- redirect: 'error',
218
- signal: controller.signal
219
- });
220
- if (response.status === 401 || response.status === 403) {
221
- throw new GraphWebError('unavailable', 'organization_graph_auth_failed', `The organization Graph refused the owner's token (HTTP ${response.status}); sign in again`);
222
- }
223
- if (!response.ok)
224
- throw new GraphWebError('unavailable', 'organization_graph_unavailable', `The organization Graph answered HTTP ${response.status} for ${path.split('?')[0]}`);
225
- const body = await response.json();
226
- if (!isRecord(body) || !Array.isArray(body.records))
227
- throw new GraphWebError('unavailable', 'organization_graph_unavailable', 'The organization Graph response has no records');
228
- return body.records;
229
- }
230
- catch (error) {
231
- if (error instanceof GraphWebError)
232
- throw error;
233
- const reason = error instanceof Error && error.name === 'AbortError' ? 'timed out' : 'request failed';
234
- throw new GraphWebError('unavailable', 'organization_graph_unavailable', `The organization Graph ${reason}`);
235
- }
236
- finally {
237
- clearTimeout(timer);
238
- }
239
- }
240
- /**
241
- * The organization Graph for the local host (C1). Reads are cached briefly and
242
- * shared while in flight; a failure is reported as `unavailable`, never as an
243
- * empty Graph.
244
- */
245
- export async function createOrganizationGraphSource(options = {}) {
246
- const home = options.home ?? homedir();
247
- const initial = await readOrganizationAccess(home);
248
- if ('reason' in initial)
249
- return initial;
250
- const request = options.fetch ?? fetch;
251
- const now = options.now ?? (() => new Date());
252
- const cacheMs = options.cacheMs ?? CACHE_MS;
253
- let cached = null;
254
- let inFlight = null;
255
- async function load() {
256
- const access = await readOrganizationAccess(home);
257
- if ('reason' in access)
258
- throw new GraphWebError('unavailable', 'organization_graph_not_connected', `The organization Graph cannot be read (${access.reason})`);
259
- // The organization's words are read alongside; failing to read them never fails the Graph.
260
- const termsRead = getRecords(request, access, 'entities?type=glossary_term&limit=500')
261
- .then((records) => projectVocabularyTerms(records))
262
- .catch(() => null);
263
- const [project, person, org, decision, raci, memberOf, assignedTo] = await Promise.all([
264
- getRecords(request, access, 'entities?type=project&limit=500'),
265
- getRecords(request, access, 'entities?type=person&limit=500'),
266
- getRecords(request, access, 'entities?type=org&limit=500'),
267
- getRecords(request, access, 'entities?type=decision&limit=500'),
268
- getRecords(request, access, 'entities?type=raci_assignment&limit=500'),
269
- getRecords(request, access, 'edges?type=member_of'),
270
- getRecords(request, access, 'edges?type=assigned_to')
271
- ]);
272
- const projection = projectOrganizationGraph({
273
- entities: {
274
- project: project,
275
- person: person,
276
- org: org,
277
- decision: decision,
278
- raci_assignment: raci
279
- },
280
- memberOf: memberOf,
281
- assignedTo: assignedTo
282
- });
283
- const source = { dataDir: null, graphFormat: 2, authority: 'organization_graph', server: access.server, readAt: now().toISOString() };
284
- return { reader: openInMemoryGraph(projection.graph, source), graph: projection.graph, terms: await termsRead };
285
- }
286
- async function current() {
287
- const at = now().getTime();
288
- if (cached && at - cached.at < cacheMs)
289
- return cached.loaded;
290
- inFlight ??= load().then((loaded) => {
291
- cached = { at: now().getTime(), loaded };
292
- return loaded;
293
- }).finally(() => {
294
- inFlight = null;
295
- });
296
- return inFlight;
297
- }
298
- return {
299
- server: initial.server,
300
- async read() {
301
- return (await current()).reader;
302
- },
303
- async readGraphFile() {
304
- return (await current()).graph;
305
- },
306
- async readVocabularyTerms() {
307
- return (await current()).terms;
308
- }
309
- };
310
- }
@@ -131,11 +131,11 @@ export declare function resolveWorldVocabulary(config: WorldVocabularyConfig | u
131
131
  /** Reads and checks a vocabulary file; an invalid file is an error, never silently ignored. */
132
132
  export declare function readWorldVocabulary(path: string): Promise<WorldVocabularyConfig>;
133
133
  /**
134
- * The businesses from the same Graph the other screens read: the organization
135
- * Graph when the host reads it (C1), else the local Graph. A Graph that cannot
136
- * be read is a state with its reason, never zero businesses.
134
+ * The businesses from the same Graph the other screens read (this Mac's Graph).
135
+ * An organization host draws its own records with `projectOrganizationWorld`.
136
+ * A Graph that cannot be read is a state with its reason, never zero businesses.
137
137
  */
138
- export declare function readWorldBusinesses(context: Pick<LocalWebModuleContext, 'dataDir' | 'organizationGraph'>, options?: {
138
+ export declare function readWorldBusinesses(context: Pick<LocalWebModuleContext, 'dataDir'>, options?: {
139
139
  readonly vocabulary?: WorldVocabularyConfig;
140
140
  readonly now?: () => Date;
141
141
  }): Promise<WorldBusinessesResponse>;
@@ -174,21 +174,6 @@ export declare function readJudgmentPlaces(journalRoot: string, options?: {
174
174
  readonly places: readonly WorldJudgmentPlace[];
175
175
  readonly reason?: string;
176
176
  }>;
177
- /**
178
- * The judgments of one organization, for a world drawn from that organization's Graph. A judgment
179
- * belongs to the organization only when the repository it was worked in is one of the organization's
180
- * businesses (a registered repository or the business code, the same rule as the world's placement).
181
- * Every other judgment (another company's work, or one whose repository was not recorded) is withheld:
182
- * only their count leaves the host, never their questions or repositories.
183
- */
184
- export declare function readOrganizationJudgments(context: Pick<LocalWebModuleContext, 'journalRoot' | 'journalCache' | 'dataDir' | 'now'>, businesses: readonly Pick<WorldBusiness, 'code' | 'repositories'>[], workspaces?: Map<string, string | null>): Promise<{
185
- readonly status: 'available' | 'unavailable';
186
- readonly scope: 'organization';
187
- readonly withheld: number;
188
- readonly places: readonly WorldJudgmentPlace[];
189
- readonly home: unknown;
190
- readonly reason?: string;
191
- }>;
192
177
  export declare function createWorldExtension(options?: {
193
178
  readonly vocabulary?: WorldVocabularyConfig;
194
179
  }): LocalWebExtension;