@unson/brainbase-mcp 0.10.4 → 0.10.6

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.
@@ -1,6 +1,8 @@
1
- import { FOUNDATION_HTTP_CONTRACT_VERSION, createFoundationHttpRouter } from './foundation-http.js';
1
+ import { FOUNDATION_HTTP_CONTRACT_VERSION, createFoundationHttpRouter, createObjectiveFoundationRoute } from './foundation-http.js';
2
+ import { checkObjectiveReadiness } from './company-os-objectives.js';
2
3
  import { createFoundationPublicProvider, createFoundationPublicRoute } from './foundation-public-provider.js';
3
4
  import { createGraphFoundationReaders } from './graph-foundation-reader.js';
5
+ import { FoundationStoreError } from './foundation-store.js';
4
6
  /**
5
7
  * Readiness probe for the Graph history contract used by the public adapter.
6
8
  *
@@ -217,16 +219,36 @@ export function createFoundationGraphHttpHandler(options) {
217
219
  }
218
220
  const readers = createGraphFoundationReaders({
219
221
  context,
222
+ selectedProjectCode: scopeId,
220
223
  query: async (text, values) => ({
221
224
  rows: (await client.query(text, values ?? [])).rows
222
225
  })
223
226
  });
224
227
  const provider = createFoundationPublicProvider(readers);
225
228
  const route = createFoundationPublicRoute(provider);
229
+ const objectiveRoute = createObjectiveFoundationRoute({
230
+ objectives: {
231
+ async createObjective() {
232
+ throw new FoundationStoreError('unsupported_graph', 'Objective writes are not configured for the Graph read adapter');
233
+ },
234
+ async readObjective(id, objectiveContext, revision) {
235
+ return revision === undefined
236
+ ? readers.store.readLatest('objective', id, objectiveContext)
237
+ : readers.store.read({ id, type: 'objective', revision }, objectiveContext);
238
+ },
239
+ async updateObjective() {
240
+ throw new FoundationStoreError('unsupported_graph', 'Objective writes are not configured for the Graph read adapter');
241
+ },
242
+ async checkObjectiveReadiness(id, objectiveContext, revision) {
243
+ return checkObjectiveReadiness(readers.store, id, objectiveContext, revision);
244
+ },
245
+ list: (type, objectiveContext) => readers.store.list(type, objectiveContext)
246
+ }
247
+ });
226
248
  const handler = createFoundationHttpRouter({
227
249
  resolveContext: () => context,
228
250
  csrf: options.csrf,
229
- routes: [route],
251
+ routes: [route, objectiveRoute],
230
252
  bodyLimitBytes
231
253
  });
232
254
  return handler.handle(request);
@@ -13,6 +13,18 @@ export declare const GRAPH_FOUNDATION_READ_CONTRACT_VERSION: "graph-foundation-h
13
13
  * future providers fail closed when they do not prove current-row visibility.
14
14
  */
15
15
  export declare const GRAPH_FOUNDATION_READ_SQL = "\nSELECT\n history.entity_id,\n history.entity_type,\n history.revision,\n history.payload,\n history.project_id,\n history.role_min,\n history.sensitivity,\n history.lifecycle_status,\n history.storage_digest,\n history.captured_at,\n (\n history.storage_digest = 'sha256:' ||\n encode(sha256(convert_to(history.payload::text, 'UTF8')), 'hex')\n ) AS storage_digest_valid,\n current_entity.id AS current_entity_id,\n current_entity.entity_type AS current_entity_type,\n current_entity.payload AS current_payload,\n current_entity.project_id AS current_project_id,\n current_entity.role_min AS current_role_min,\n current_entity.sensitivity AS current_sensitivity,\n current_entity.lifecycle_status AS current_lifecycle_status,\n projects.code AS current_project_code,\n true AS current_visible\nFROM public.graph_foundation_revisions AS history\nJOIN public.graph_entities AS current_entity\n ON current_entity.id = history.entity_id\n AND current_entity.entity_type = history.entity_type\nJOIN public.projects AS projects\n ON projects.id = current_entity.project_id\nWHERE history.entity_id = $1\n AND history.entity_type = $2\n AND history.revision = $3\n";
16
+ /**
17
+ * Read the newest immutable revision for one Foundation entity. The current
18
+ * Graph row remains part of the query so RLS and the adapter's current-row
19
+ * checks apply identically to latest and historical reads.
20
+ */
21
+ export declare const GRAPH_FOUNDATION_LATEST_SQL = "\nSELECT\n history.entity_id,\n history.entity_type,\n history.revision,\n history.payload,\n history.project_id,\n history.role_min,\n history.sensitivity,\n history.lifecycle_status,\n history.storage_digest,\n history.captured_at,\n (\n history.storage_digest = 'sha256:' ||\n encode(sha256(convert_to(history.payload::text, 'UTF8')), 'hex')\n ) AS storage_digest_valid,\n current_entity.id AS current_entity_id,\n current_entity.entity_type AS current_entity_type,\n current_entity.payload AS current_payload,\n current_entity.project_id AS current_project_id,\n current_entity.role_min AS current_role_min,\n current_entity.sensitivity AS current_sensitivity,\n current_entity.lifecycle_status AS current_lifecycle_status,\n projects.code AS current_project_code,\n true AS current_visible\nFROM public.graph_entities AS current_entity\nLEFT JOIN public.projects AS projects\n ON projects.id = current_entity.project_id\nLEFT JOIN LATERAL (\n SELECT history.*\n FROM public.graph_foundation_revisions AS history\n WHERE history.entity_id = current_entity.id\n AND history.entity_type = current_entity.entity_type\n ORDER BY history.revision::numeric DESC\n LIMIT 1\n) AS history ON true\nWHERE current_entity.id = $1\n AND current_entity.entity_type = $2\nORDER BY history.revision::numeric DESC NULLS LAST\nLIMIT 1\n";
22
+ /**
23
+ * List the newest revision of each Foundation entity visible in the selected
24
+ * project. The project filter is by canonical project code, which is the
25
+ * scope identifier selected by the organization BFF.
26
+ */
27
+ export declare const GRAPH_FOUNDATION_LIST_SQL = "\nSELECT\n history.entity_id,\n history.entity_type,\n history.revision,\n history.payload,\n history.project_id,\n history.role_min,\n history.sensitivity,\n history.lifecycle_status,\n history.storage_digest,\n history.captured_at,\n (\n history.storage_digest = 'sha256:' ||\n encode(sha256(convert_to(history.payload::text, 'UTF8')), 'hex')\n ) AS storage_digest_valid,\n current_entity.id AS current_entity_id,\n current_entity.entity_type AS current_entity_type,\n current_entity.payload AS current_payload,\n current_entity.project_id AS current_project_id,\n current_entity.role_min AS current_role_min,\n current_entity.sensitivity AS current_sensitivity,\n current_entity.lifecycle_status AS current_lifecycle_status,\n projects.code AS current_project_code,\n true AS current_visible\nFROM public.graph_entities AS current_entity\nLEFT JOIN public.projects AS projects\n ON projects.id = current_entity.project_id\nLEFT JOIN LATERAL (\n SELECT history.*\n FROM public.graph_foundation_revisions AS history\n WHERE history.entity_id = current_entity.id\n AND history.entity_type = current_entity.entity_type\n ORDER BY history.revision::numeric DESC\n LIMIT 1\n) AS history ON true\nWHERE ($1::text IS NULL OR current_entity.entity_type = $1)\n AND ($2::text IS NULL OR projects.code = $2 OR projects.id IS NULL)\n AND current_entity.entity_type IN ('objective', 'variable', 'model', 'constraint')\nORDER BY current_entity.id, current_entity.entity_type\n";
16
28
  /** Flat row shape returned by GRAPH_FOUNDATION_READ_SQL. */
17
29
  export interface GraphFoundationHistoryRow {
18
30
  readonly entity_id?: unknown;
@@ -46,9 +58,11 @@ export interface GraphFoundationReaderOptions {
46
58
  readonly context: FoundationStoreContext;
47
59
  /** Query callback bound to the host's already-authenticated transaction. */
48
60
  readonly query: GraphFoundationQuery;
61
+ /** Canonical project code selected by the organization boundary. */
62
+ readonly selectedProjectCode?: string;
49
63
  }
50
64
  export interface GraphFoundationReaders {
51
- readonly store: Pick<FoundationRevisionStore, 'read'>;
65
+ readonly store: Pick<FoundationRevisionStore, 'read' | 'readLatest' | 'list'>;
52
66
  readonly philosophyReader: PhilosophyRevisionReader;
53
67
  }
54
68
  /**
@@ -50,6 +50,98 @@ WHERE history.entity_id = $1
50
50
  AND history.entity_type = $2
51
51
  AND history.revision = $3
52
52
  `;
53
+ /**
54
+ * Read the newest immutable revision for one Foundation entity. The current
55
+ * Graph row remains part of the query so RLS and the adapter's current-row
56
+ * checks apply identically to latest and historical reads.
57
+ */
58
+ export const GRAPH_FOUNDATION_LATEST_SQL = `
59
+ SELECT
60
+ history.entity_id,
61
+ history.entity_type,
62
+ history.revision,
63
+ history.payload,
64
+ history.project_id,
65
+ history.role_min,
66
+ history.sensitivity,
67
+ history.lifecycle_status,
68
+ history.storage_digest,
69
+ history.captured_at,
70
+ (
71
+ history.storage_digest = 'sha256:' ||
72
+ encode(sha256(convert_to(history.payload::text, 'UTF8')), 'hex')
73
+ ) AS storage_digest_valid,
74
+ current_entity.id AS current_entity_id,
75
+ current_entity.entity_type AS current_entity_type,
76
+ current_entity.payload AS current_payload,
77
+ current_entity.project_id AS current_project_id,
78
+ current_entity.role_min AS current_role_min,
79
+ current_entity.sensitivity AS current_sensitivity,
80
+ current_entity.lifecycle_status AS current_lifecycle_status,
81
+ projects.code AS current_project_code,
82
+ true AS current_visible
83
+ FROM public.graph_entities AS current_entity
84
+ LEFT JOIN public.projects AS projects
85
+ ON projects.id = current_entity.project_id
86
+ LEFT JOIN LATERAL (
87
+ SELECT history.*
88
+ FROM public.graph_foundation_revisions AS history
89
+ WHERE history.entity_id = current_entity.id
90
+ AND history.entity_type = current_entity.entity_type
91
+ ORDER BY history.revision::numeric DESC
92
+ LIMIT 1
93
+ ) AS history ON true
94
+ WHERE current_entity.id = $1
95
+ AND current_entity.entity_type = $2
96
+ ORDER BY history.revision::numeric DESC NULLS LAST
97
+ LIMIT 1
98
+ `;
99
+ /**
100
+ * List the newest revision of each Foundation entity visible in the selected
101
+ * project. The project filter is by canonical project code, which is the
102
+ * scope identifier selected by the organization BFF.
103
+ */
104
+ export const GRAPH_FOUNDATION_LIST_SQL = `
105
+ SELECT
106
+ history.entity_id,
107
+ history.entity_type,
108
+ history.revision,
109
+ history.payload,
110
+ history.project_id,
111
+ history.role_min,
112
+ history.sensitivity,
113
+ history.lifecycle_status,
114
+ history.storage_digest,
115
+ history.captured_at,
116
+ (
117
+ history.storage_digest = 'sha256:' ||
118
+ encode(sha256(convert_to(history.payload::text, 'UTF8')), 'hex')
119
+ ) AS storage_digest_valid,
120
+ current_entity.id AS current_entity_id,
121
+ current_entity.entity_type AS current_entity_type,
122
+ current_entity.payload AS current_payload,
123
+ current_entity.project_id AS current_project_id,
124
+ current_entity.role_min AS current_role_min,
125
+ current_entity.sensitivity AS current_sensitivity,
126
+ current_entity.lifecycle_status AS current_lifecycle_status,
127
+ projects.code AS current_project_code,
128
+ true AS current_visible
129
+ FROM public.graph_entities AS current_entity
130
+ LEFT JOIN public.projects AS projects
131
+ ON projects.id = current_entity.project_id
132
+ LEFT JOIN LATERAL (
133
+ SELECT history.*
134
+ FROM public.graph_foundation_revisions AS history
135
+ WHERE history.entity_id = current_entity.id
136
+ AND history.entity_type = current_entity.entity_type
137
+ ORDER BY history.revision::numeric DESC
138
+ LIMIT 1
139
+ ) AS history ON true
140
+ WHERE ($1::text IS NULL OR current_entity.entity_type = $1)
141
+ AND ($2::text IS NULL OR projects.code = $2 OR projects.id IS NULL)
142
+ AND current_entity.entity_type IN ('objective', 'variable', 'model', 'constraint')
143
+ ORDER BY current_entity.id, current_entity.entity_type
144
+ `;
53
145
  const FOUNDATION_TYPES = ['objective', 'variable', 'model', 'constraint'];
54
146
  const REVISION_PATTERN = /^[1-9]\d*$/u;
55
147
  const DIGEST_PATTERN = /^sha256:[0-9a-f]{64}$/u;
@@ -65,6 +157,7 @@ export function createGraphFoundationReaders(options) {
65
157
  assertReaderOptions(options);
66
158
  const context = cloneContext(options.context);
67
159
  const query = options.query;
160
+ const selectedProjectCode = options.selectedProjectCode;
68
161
  const store = {
69
162
  async read(reference, suppliedContext) {
70
163
  assertTrustedPrincipal(context, suppliedContext);
@@ -75,20 +168,42 @@ export function createGraphFoundationReaders(options) {
75
168
  if (rows.length !== 1) {
76
169
  throw corrupt('Graph foundation history returned multiple rows for one revision');
77
170
  }
171
+ return validateFoundationRecord(rows[0], reference, context, selectedProjectCode);
172
+ },
173
+ async readLatest(type, id, suppliedContext) {
174
+ assertTrustedPrincipal(context, suppliedContext);
175
+ assertFoundationType(type);
176
+ assertEntityId(id);
177
+ const rows = await executeLatestRead(query, id, type);
178
+ if (rows.length === 0)
179
+ return null;
180
+ if (rows.length !== 1) {
181
+ throw corrupt('Graph foundation latest query returned multiple rows');
182
+ }
78
183
  const row = rows[0];
79
- const history = validateHistoryRow(row, reference.id, reference.type, reference.revision);
80
- const current = validateCurrentRow(row, reference.id, reference.type);
81
- authorizeCurrentFoundation(current, context);
82
- // A history row remains readable only through the current Graph row,
83
- // including its current project and current scope. The old payload is
84
- // returned unchanged after these current-row checks.
85
- if (history.projectId !== current.projectId) {
86
- throw scopeViolation('Historical foundation project differs from current Graph project');
184
+ const reference = rowReference(row);
185
+ if (reference.id !== id || reference.type !== type) {
186
+ throw corrupt('Graph foundation latest row identity does not match the request');
87
187
  }
88
- return {
89
- definition: cloneFoundationDefinition(history.definition),
90
- digest: digestFoundationDefinition(history.definition)
91
- };
188
+ return validateFoundationRecord(row, reference, context, selectedProjectCode, true);
189
+ },
190
+ async list(type, suppliedContext) {
191
+ assertTrustedPrincipal(context, suppliedContext);
192
+ if (type !== undefined)
193
+ assertFoundationType(type);
194
+ const rows = await executeList(query, type, selectedProjectCode);
195
+ const seen = new Set();
196
+ return rows.map((row) => {
197
+ const reference = rowReference(row);
198
+ if (type !== undefined && reference.type !== type) {
199
+ throw corrupt('Graph foundation list returned an unexpected entity type');
200
+ }
201
+ const key = `${reference.type}:${reference.id}`;
202
+ if (seen.has(key))
203
+ throw corrupt('Graph foundation list returned duplicate entity rows');
204
+ seen.add(key);
205
+ return validateFoundationRecord(row, reference, context, selectedProjectCode, true);
206
+ });
92
207
  }
93
208
  };
94
209
  const philosophyReader = {
@@ -254,6 +369,66 @@ async function executeRead(query, id, type, revision) {
254
369
  }
255
370
  throw corrupt('Graph foundation history query returned an invalid result');
256
371
  }
372
+ async function executeLatestRead(query, id, type) {
373
+ let result;
374
+ try {
375
+ result = await query(GRAPH_FOUNDATION_LATEST_SQL, [id, type]);
376
+ }
377
+ catch {
378
+ throw corrupt('Graph foundation latest query failed');
379
+ }
380
+ if (Array.isArray(result))
381
+ return result;
382
+ if (!Array.isArray(result) && 'rows' in result && Array.isArray(result.rows)) {
383
+ return result.rows;
384
+ }
385
+ throw corrupt('Graph foundation latest query returned an invalid result');
386
+ }
387
+ async function executeList(query, type, selectedProjectCode) {
388
+ let result;
389
+ try {
390
+ result = await query(GRAPH_FOUNDATION_LIST_SQL, [type ?? null, selectedProjectCode ?? null]);
391
+ }
392
+ catch {
393
+ throw corrupt('Graph foundation list query failed');
394
+ }
395
+ if (Array.isArray(result))
396
+ return result;
397
+ if (!Array.isArray(result) && 'rows' in result && Array.isArray(result.rows)) {
398
+ return result.rows;
399
+ }
400
+ throw corrupt('Graph foundation list query returned an invalid result');
401
+ }
402
+ function validateFoundationRecord(row, reference, context, selectedProjectCode, requireCurrentRevision = false) {
403
+ const history = validateHistoryRow(row, reference.id, reference.type, reference.revision);
404
+ const current = validateCurrentRow(row, reference.id, reference.type);
405
+ if (requireCurrentRevision && current.projectCode === undefined) {
406
+ throw corrupt('Current Graph project code is missing');
407
+ }
408
+ authorizeCurrentFoundation(current, context, selectedProjectCode);
409
+ // Latest reads must prove that the current Graph payload points at the
410
+ // revision returned by the history query. Otherwise a missing newer history
411
+ // row could silently fall back to an older immutable revision.
412
+ if (requireCurrentRevision && current.foundation?.revision !== reference.revision) {
413
+ throw corrupt('Current Graph foundation revision does not match the latest history revision');
414
+ }
415
+ // A history row remains readable only through the current Graph row,
416
+ // including its current project and current scope. The old payload is
417
+ // returned unchanged after these current-row checks.
418
+ if (history.projectId !== current.projectId) {
419
+ throw scopeViolation('Historical foundation project differs from current Graph project');
420
+ }
421
+ return {
422
+ definition: cloneFoundationDefinition(history.definition),
423
+ digest: digestFoundationDefinition(history.definition)
424
+ };
425
+ }
426
+ function rowReference(row) {
427
+ const id = requireString(row.entity_id, 'Foundation history entity_id');
428
+ const type = parseFoundationType(row.entity_type);
429
+ const revision = requireRevision(row.revision, 'Foundation history revision');
430
+ return { id, type, revision };
431
+ }
257
432
  function validateHistoryRow(row, id, type, revision) {
258
433
  assertRowIdentity(row, id, type, revision);
259
434
  assertStorageDigest(row);
@@ -301,21 +476,27 @@ function validateCurrentRow(row, id, type) {
301
476
  throw new FoundationStoreError('authorization_denied', 'The current Graph row is not active');
302
477
  }
303
478
  const projectId = requireString(row.current_project_id, 'Current Graph project_id');
479
+ const projectCode = typeof row.current_project_code === 'string' && row.current_project_code.length > 0
480
+ ? row.current_project_code
481
+ : undefined;
304
482
  const payload = requireObject(row.current_payload, 'Current Graph payload');
305
483
  if (type === 'philosophy') {
306
484
  const applicability = parseJudgmentApplicability(payload);
307
- return { payload, projectId, philosophy: { applicability } };
485
+ return { payload, projectId, projectCode, philosophy: { applicability } };
308
486
  }
309
487
  const definition = requireObject(payload.foundation, 'Current Graph payload.foundation');
310
488
  const result = validateFoundationDefinition(definition, { use: 'draft' });
311
489
  if (!result.valid || definition.id !== id || definition.type !== type) {
312
490
  throw corrupt('Current Graph foundation definition is invalid');
313
491
  }
314
- return { payload, projectId, foundation: definition };
492
+ return { payload, projectId, projectCode, foundation: definition };
315
493
  }
316
- function authorizeCurrentFoundation(current, context) {
494
+ function authorizeCurrentFoundation(current, context, selectedProjectCode) {
317
495
  if (!current.foundation)
318
496
  throw corrupt('Current Graph foundation definition is missing');
497
+ if (selectedProjectCode !== undefined && current.projectCode !== selectedProjectCode) {
498
+ throw scopeViolation('The current Graph project is outside the selected project scope');
499
+ }
319
500
  const definition = current.foundation;
320
501
  if (!canRead(definition.acl, context.principal)) {
321
502
  throw new FoundationStoreError('authorization_denied', 'The current foundation ACL does not permit this principal');
@@ -334,6 +515,28 @@ function assertRowIdentity(row, id, type, revision) {
334
515
  throw corrupt('Foundation history identity does not match the requested revision');
335
516
  }
336
517
  }
518
+ function parseFoundationType(value) {
519
+ if (typeof value === 'string' && FOUNDATION_TYPES.includes(value)) {
520
+ return value;
521
+ }
522
+ throw corrupt('Foundation history entity_type is invalid');
523
+ }
524
+ function requireRevision(value, label) {
525
+ if (typeof value !== 'string' || !REVISION_PATTERN.test(value)) {
526
+ throw corrupt(`${label} must be a positive revision`);
527
+ }
528
+ return value;
529
+ }
530
+ function assertEntityId(value) {
531
+ if (typeof value !== 'string' || value.trim().length === 0 || value.includes('\0')) {
532
+ throw new FoundationStoreError('invalid_input', 'A foundation entity id is required');
533
+ }
534
+ }
535
+ function assertFoundationType(value) {
536
+ if (!FOUNDATION_TYPES.includes(value)) {
537
+ throw new FoundationStoreError('invalid_input', 'A supported foundation type is required');
538
+ }
539
+ }
337
540
  function assertFoundationReference(reference) {
338
541
  if (!isPlainRecord(reference) || typeof reference.id !== 'string' || reference.id.length === 0 ||
339
542
  !FOUNDATION_TYPES.includes(reference.type) ||
@@ -365,6 +568,10 @@ function assertReaderOptions(options) {
365
568
  if (!isValidFoundationScope(scope))
366
569
  throw new TypeError('A trusted Graph foundation scope is invalid');
367
570
  }
571
+ if (options.selectedProjectCode !== undefined
572
+ && (typeof options.selectedProjectCode !== 'string' || options.selectedProjectCode.trim().length === 0)) {
573
+ throw new TypeError('A selected Graph project code is invalid');
574
+ }
368
575
  }
369
576
  function assertTrustedPrincipal(trusted, supplied) {
370
577
  if (!supplied || supplied.principal !== trusted.principal) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unson/brainbase-mcp",
3
- "version": "0.10.4",
3
+ "version": "0.10.6",
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",
@@ -336,5 +336,5 @@
336
336
  "engines": {
337
337
  "node": ">=20"
338
338
  },
339
- "gitHead": "84863f09b8274b738b16e15d2550ece19ac51124"
339
+ "gitHead": "c59d4421803804f285d34ae6dd1d766f9c0f8ee9"
340
340
  }
@@ -27,6 +27,12 @@
27
27
  --bb-color-action-hover: #2a5540;
28
28
  --bb-color-action-text: #ffffff;
29
29
  --bb-color-focus: #20a37e;
30
+ --bb-color-project-ink: #172d4f;
31
+ --bb-color-project-muted: #566b87;
32
+ --bb-color-project-border: #dce4ee;
33
+ --bb-color-project-border-strong: #c8d4e2;
34
+ --bb-color-project-accent: #1261ad;
35
+ --bb-color-project-accent-strong: #194f8a;
30
36
  --bb-color-success: #087d62;
31
37
  --bb-color-success-soft: #e6f2ec;
32
38
  --bb-color-warning: #8a631d;
@@ -250,6 +250,7 @@ export function createGraphProjectsView({
250
250
  emptyNotice,
251
251
  pageActions,
252
252
  extraMetrics,
253
+ showProjectLedger = true,
253
254
  renderRailExtensions,
254
255
  selectedId: initialSelectedId,
255
256
  onSelect,
@@ -595,7 +596,7 @@ export function createGraphProjectsView({
595
596
  return children;
596
597
  }
597
598
 
598
- children.push(workspaceLedger(doc, {
599
+ if (showProjectLedger) children.push(workspaceLedger(doc, {
599
600
  className: 'bb-graph-project-ledger',
600
601
  ariaLabel: 'プロジェクトの一覧',
601
602
  columns: GRAPH_PROJECT_LEDGER_COLUMNS,