@bongos/core 1.19.148 → 1.19.150

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/.bongos-core.json CHANGED
@@ -2,11 +2,11 @@
2
2
  "artifact": "bongos-core",
3
3
  "manifest_schema": 1,
4
4
  "generator": "scripts/gds/package-core.js",
5
- "core_version": "1.19.148",
6
- "core_contract": "1.19.148",
7
- "source_commit": "7e339c95065a311ed8124d03557ded14bca9446a",
5
+ "core_version": "1.19.150",
6
+ "core_contract": "1.19.150",
7
+ "source_commit": "ba28eaba03c65c990128c56929d0e30a18a2cb14",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-08-16T14:24:46.637Z",
9
+ "built_at": "2026-08-16T14:43:24.321Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 304,
@@ -17,7 +17,7 @@
17
17
  "gate": "passed"
18
18
  },
19
19
  "file_count": 1579,
20
- "tree_sha256": "8cab3bae4120d167d296a253ea3e0b6064a55de4ea9188fa52a09217898cd086",
20
+ "tree_sha256": "5429ba0237ab985ff79be4a1b9a03dd24cacd6260fdc604247b7eef8a36583dc",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -3162,7 +3162,7 @@
3162
3162
  {
3163
3163
  "path": "modules/hall-ui/public/goals-page.js",
3164
3164
  "mode": "0000644",
3165
- "sha256": "fcfe94b3e0cd00ddc881283d9897121518a7acc00b3d05b0715552911e2a0705"
3165
+ "sha256": "f24cfdca48676d882cfa51754fcf1519ade4646a760cb2be1db71fbc27571bca"
3166
3166
  },
3167
3167
  {
3168
3168
  "path": "modules/hall-ui/public/goals.css",
@@ -3197,7 +3197,7 @@
3197
3197
  {
3198
3198
  "path": "modules/hall-ui/public/hall-kit.js",
3199
3199
  "mode": "0000644",
3200
- "sha256": "672ef980513a150a81773ad674750e7d1560535dd08cbf2e1463155fbe00d7f6"
3200
+ "sha256": "4d5ec8f334fc00f23e81fe21283b7ea2fb173b253c638a08ef48b97111cc654f"
3201
3201
  },
3202
3202
  {
3203
3203
  "path": "modules/hall-ui/public/hall-widgets.js",
@@ -3407,7 +3407,7 @@
3407
3407
  {
3408
3408
  "path": "modules/hall-ui/public/task.js",
3409
3409
  "mode": "0000644",
3410
- "sha256": "2430a3b351d80ae55724c1bfd1c514855e9a5c04ceaa1012e4475d2dc09b9d84"
3410
+ "sha256": "ac6bfa459924a43005625e9c2655a5a1397c941dfa0727594bb25f930180d36a"
3411
3411
  },
3412
3412
  {
3413
3413
  "path": "modules/hall-ui/public/watch.css",
@@ -3427,7 +3427,7 @@
3427
3427
  {
3428
3428
  "path": "modules/hall-ui/public/work.css",
3429
3429
  "mode": "0000644",
3430
- "sha256": "8ad8c570a2196aabb46c86e8efcfab2b0d22451b4f23aa10a02b4f52d1415690"
3430
+ "sha256": "e5dd2fd84b31174d10bb6558920cbbc9e34b1776d775db3c69518810a1c7b96c"
3431
3431
  },
3432
3432
  {
3433
3433
  "path": "modules/hall-ui/public/work.html",
@@ -3437,7 +3437,7 @@
3437
3437
  {
3438
3438
  "path": "modules/hall-ui/public/work.js",
3439
3439
  "mode": "0000644",
3440
- "sha256": "eb7e5919af8d60c5ae6f68e05a5d97e4b7ca97fa17941b79086eec39c9af7faf"
3440
+ "sha256": "8ee189def6ded1c8669212d7233f57948ac58bf23ad8277422996ffd8914e537"
3441
3441
  },
3442
3442
  {
3443
3443
  "path": "modules/ideas/CLAUDE.md",
@@ -4187,12 +4187,12 @@
4187
4187
  {
4188
4188
  "path": "package-lock.json",
4189
4189
  "mode": "0000644",
4190
- "sha256": "354de77e768f98c77062278cead6271a3249cfe4ed35d811fb76b497f3028d8b"
4190
+ "sha256": "86e259a725603b58d5659d630124568731893e6497d933327cbc29ab733955b8"
4191
4191
  },
4192
4192
  {
4193
4193
  "path": "package.json",
4194
4194
  "mode": "0000644",
4195
- "sha256": "23c72c88771c78d76a61334a4710e6ce77a146331de749c944f6bf2890602162"
4195
+ "sha256": "cf358f722d2710aa11787645b256c890c6508e5c34ff735044ef8a9b28951d7e"
4196
4196
  },
4197
4197
  {
4198
4198
  "path": "public-docs/index.html",
@@ -5612,7 +5612,7 @@
5612
5612
  {
5613
5613
  "path": "src/bongos/module-scope-map.js",
5614
5614
  "mode": "0000644",
5615
- "sha256": "c42e08fd814524fd022f74ba651393cd8b3e5ac1418b7cb7c5ea647251af3dc2"
5615
+ "sha256": "dde61de24af4bdb3ed0d00267873ee700c959166a4f86ab3a3283e8813f65bc6"
5616
5616
  },
5617
5617
  {
5618
5618
  "path": "src/bongos/module-submissions.js",
@@ -5782,7 +5782,7 @@
5782
5782
  {
5783
5783
  "path": "src/module-api.js",
5784
5784
  "mode": "0000644",
5785
- "sha256": "f6ad6d7407077ad6166646472601151847a96f338de366e0de4daa692957b205"
5785
+ "sha256": "6cdc6cf28bcb58d18a41a575a253e9066e4fc6d1f86b8d1f7be60e12b99784db"
5786
5786
  },
5787
5787
  {
5788
5788
  "path": "src/module-loader/catalog.js",
@@ -7107,7 +7107,7 @@
7107
7107
  {
7108
7108
  "path": "tests/module-scope-map.mjs",
7109
7109
  "mode": "0000644",
7110
- "sha256": "e858ff2ae89fbd1577dc00f988c97b71e91f06ee754f5513ba8c18c214d3e181"
7110
+ "sha256": "9eb05d38e5138e0da16d57d1d4c8768528dbc052acbc1a9d5d5e7cef7d38f06c"
7111
7111
  },
7112
7112
  {
7113
7113
  "path": "tests/module_api.mjs",
@@ -529,6 +529,9 @@
529
529
  const touchesChip = touches.length
530
530
  ? `<span class="task__touches" title="${escapeHtml(touches.join('\n'))}">✎ ${touches.length} file${touches.length === 1 ? '' : 's'}</span>`
531
531
  : '';
532
+ // task 1002955: same age chip the board renders — reviewing a goal for stale
533
+ // work is the case that needed it most.
534
+ const ageChip = window.OTBKit.ageChipHtml(t.created_at);
532
535
  const claim = state.claimsByTask.get(id);
533
536
  const isReady = t.status === 'ready' && !!state.me;
534
537
  const mineActive = t.status === 'active' && claim && sameId(claim.builder_id, selfId());
@@ -569,6 +572,7 @@
569
572
  <span>${minutesText}</span>
570
573
  <span>${cpmText}</span>
571
574
  ${touchesChip}
575
+ ${ageChip}
572
576
  </div>
573
577
  </div>
574
578
  <div class="task__credits">
@@ -417,6 +417,66 @@
417
417
  };
418
418
  }
419
419
 
420
+ // ---- age (task 1002955) ------------------------------------------------------
421
+ // How old is this row? The board, the goal Work card and the task panel all
422
+ // render a task's kind/estimate/credits but never said when it was written, so
423
+ // a queue of 40 ready tasks read as equally fresh whether it was filed
424
+ // yesterday or three months ago. Kit-level because the card markup is
425
+ // duplicated across work.js + goals-page.js (task 1002463) and a third
426
+ // hand-rolled date helper is exactly what this file exists to prevent.
427
+
428
+ // Coarse buckets on purpose: past a week nobody cares about the hour, and the
429
+ // exact day is one hover away (ageChipHtml puts it on title=). Anything in the
430
+ // future — a clock skew, a seeded row — clamps to "new" rather than "-3d".
431
+ const AGE_STALE_DAYS = 30;
432
+ function fmtAge(iso) {
433
+ if (!iso) return '';
434
+ const ms = Date.now() - new Date(iso).getTime();
435
+ if (!Number.isFinite(ms)) return '';
436
+ const days = Math.floor(ms / 86400000);
437
+ if (days < 0) return 'new';
438
+ if (days === 0) return 'today';
439
+ if (days === 1) return '1d';
440
+ if (days < 14) return `${days}d`;
441
+ if (days < 60) return `${Math.floor(days / 7)}w`;
442
+ // Clamped at 11: 30-day months overshoot, so days 330-364 would otherwise
443
+ // print "12mo" immediately before the year bucket takes over at 365.
444
+ if (days < 365) return `${Math.min(11, Math.floor(days / 30))}mo`;
445
+ const years = Math.floor(days / 365);
446
+ const rem = Math.floor((days % 365) / 30);
447
+ return rem ? `${years}y ${rem}mo` : `${years}y`;
448
+ }
449
+
450
+ // Absolute date for the tooltip — locale-formatted, no time (the age chip is
451
+ // about which week something was filed, not which minute).
452
+ function fmtAbsDate(iso) {
453
+ if (!iso) return '';
454
+ const d = new Date(iso);
455
+ if (Number.isNaN(d.getTime())) return '';
456
+ try {
457
+ return d.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });
458
+ } catch (_) { return d.toISOString().slice(0, 10); }
459
+ }
460
+
461
+ function ageDays(iso) {
462
+ if (!iso) return null;
463
+ const ms = Date.now() - new Date(iso).getTime();
464
+ return Number.isFinite(ms) ? Math.floor(ms / 86400000) : null;
465
+ }
466
+
467
+ // ageChipHtml(iso, opts?) → the meta-row chip. Past AGE_STALE_DAYS it gains
468
+ // .task__age--stale, so scanning a goal's queue for work that has been sitting
469
+ // is a glance rather than an API call. `label` names the date in the tooltip
470
+ // ("Created" by default) so the same chip can carry other timestamps.
471
+ function ageChipHtml(iso, { label = 'Created' } = {}) {
472
+ const text = fmtAge(iso);
473
+ if (!text) return '';
474
+ const days = ageDays(iso);
475
+ const stale = days != null && days >= AGE_STALE_DAYS;
476
+ const abs = fmtAbsDate(iso);
477
+ return `<span class="task__age${stale ? ' task__age--stale' : ''}" title="${esc(label)} ${esc(abs)}">${esc(text)}</span>`;
478
+ }
479
+
420
480
  // ---- empty state / skeleton / collapsible ------------------------------------
421
481
 
422
482
  // One standard empty pattern: a short line + at most one action link. An
@@ -465,6 +525,8 @@
465
525
  makeLedger, makeFilterBar, makeTabs, makeSectionRail,
466
526
  // states
467
527
  emptyStateHtml, skeletonRows, collapsibleCard,
528
+ // age (task 1002955)
529
+ fmtAge, fmtAbsDate, ageChipHtml, AGE_STALE_DAYS,
468
530
  };
469
531
 
470
532
  // Node-require-safe (the dom-utils.js pattern): assignment is the only
@@ -184,6 +184,14 @@
184
184
  if (t.est_minutes) meta.push(`${t.est_minutes}m`);
185
185
  parts.push(`<p class="deeplink-card__meta">${meta.join(' · ')}</p>`);
186
186
 
187
+ // task 1002955: when was this written? The panel showed a shipped date but
188
+ // never a created one, so an untouched task carried no age at all. Absolute
189
+ // date first (the panel has the room the card chip doesn't), age after it.
190
+ if (t.created_at) {
191
+ const age = fmtRel(t.created_at);
192
+ parts.push(`<p class="deeplink-card__note">Created ${escapeHtml(fmtDate(t.created_at))}${age ? ` — ${escapeHtml(age)}` : ''}.</p>`);
193
+ }
194
+
187
195
  if (t.goal_id) {
188
196
  parts.push(`<p class="td-goal">Goal: <a href="/goals#/goal/${encodeURIComponent(t.goal_id)}">Goal #${escapeHtml(String(t.goal_id))}</a></p>`);
189
197
  }
@@ -66,6 +66,21 @@
66
66
  cursor: help;
67
67
  }
68
68
 
69
+ /* How old the task is (task 1002955, OTBKit.ageChipHtml). Quiet by default —
70
+ it's orientation, not a call to act — but a task past the stale threshold
71
+ steps up from faint to soft+bold so a queue that has been sitting reads at a
72
+ glance instead of needing the API. The exact date is on the tooltip, hence
73
+ `cursor: help`. Deliberately NOT --warn: that token is a dark amber paired
74
+ with --warn-weak behind it, and as bare text it loses contrast on the dark
75
+ theme's card. --ink-soft steps up legibly in both. */
76
+ .task__age {
77
+ color: var(--ink-faint);
78
+ font-size: 12px;
79
+ white-space: nowrap;
80
+ cursor: help;
81
+ }
82
+ .task__age--stale { color: var(--ink-soft); font-weight: 600; }
83
+
69
84
  .task__tag--newcomer {
70
85
  background: var(--ok-weak);
71
86
  color: var(--ok);
@@ -360,6 +360,9 @@
360
360
  const touchesChip = touches.length
361
361
  ? `<span class="task__touches" title="${escapeHtml(touches.join('\n'))}">✎ ${touches.length} file${touches.length === 1 ? '' : 's'}</span>`
362
362
  : '';
363
+ // task 1002955: how old is this task? A ready queue reads as equally fresh
364
+ // without it; the exact date rides the tooltip.
365
+ const ageChip = window.OTBKit.ageChipHtml(t.created_at);
363
366
  const selected = selection.claim.has(String(t.id));
364
367
  return `
365
368
  <div class="task${selected ? ' task--selected' : ''}" data-task-id="${t.id}">
@@ -380,6 +383,7 @@
380
383
  <span>${minutesText}</span>
381
384
  <span>${cpmText}</span>
382
385
  ${touchesChip}
386
+ ${ageChip}
383
387
  </div>
384
388
  </div>
385
389
  <div class="task__credits">
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.148",
3
+ "version": "1.19.150",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.148",
9
+ "version": "1.19.150",
10
10
  "license": "AGPL-3.0-or-later",
11
11
  "dependencies": {
12
12
  "express": "^4.21.2",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.148",
3
+ "version": "1.19.150",
4
4
  "description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "main": "src/platform-server.js",
@@ -207,6 +207,19 @@ const MODULE_GLOBS = {
207
207
  'modules/game/', 'src/preview-server.js',
208
208
  ],
209
209
  'art-pipeline': ['modules/art-pipeline/'],
210
+ // --- the declarative agent registry (goal 1000038, ADR 0083 default:false):
211
+ // agent definitions reconciled at deploy into agents_definitions + a per-fire
212
+ // agents_runs ledger. Carved to modules/agents/ at foundation time, so the
213
+ // glob is its module home. `.claude/agents/` is the module's SOURCE surface
214
+ // (files-are-source — the committed definitions ARE the input agents-sync
215
+ // upserts), listed alongside it on the autonomy precedent, which likewise
216
+ // owns its '.claude/scheduled-tasks/' definition dir. The sync CLI travels
217
+ // with the domain but stays under scripts/gds/ (the dev-box/provisioning
218
+ // precedent). Without this key the three Agents goals (1000038/39/40) could
219
+ // declare no wall at all (task 1002736).
220
+ agents: [
221
+ 'modules/agents/', '.claude/agents/', 'scripts/gds/agents-sync.js',
222
+ ],
210
223
  // --- the hub identity provider + the PROJECTS CATALOG it owns (ADR 0141): platform
211
224
  // accounts, the SSO client registry, project memberships/invites, and the
212
225
  // platform_identity_projects rows the Explore feed reads. Carved already, so the
package/src/module-api.js CHANGED
@@ -723,8 +723,12 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
723
723
  // landed since 1.19.146 with no explicit bump. run 31950807165. (task 1002620)
724
724
  // 1.19.148 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
725
725
  // landed since 1.19.147 with no explicit bump. run 31952592623. (task 1002620)
726
+ // 1.19.149 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
727
+ // landed since 1.19.148 with no explicit bump. run 31953207683. (task 1002620)
728
+ // 1.19.150 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
729
+ // landed since 1.19.149 with no explicit bump. run 31953504697. (task 1002620)
726
730
  // ---------------------------------------------------------------------------
727
- const CORE_VERSION = '1.19.148'; // CI auto-patch carrier (ADR 0161); see changelog above
731
+ const CORE_VERSION = '1.19.150'; // CI auto-patch carrier (ADR 0161); see changelog above
728
732
 
729
733
  // A namespaced logger so a module's log lines are attributable + consistent.
730
734
  // Usage: const log = api.logger('dev-box'); log.info('mounted');
@@ -136,3 +136,33 @@ test('platform-identity joined the roster (task 1002322) — the catalog territo
136
136
  assert.equal(covers('src/bongos/auth.js'), false, 'the wall must not cover the kernel auth core');
137
137
  assert.equal(scope.scopeIncludesProtected(wall), false, 'goal 1000046 wall stays non-protected (governance widens it later)');
138
138
  });
139
+
140
+ test('agents joined the roster (task 1002736) — the territory goals 1000038/39/40 wall', () => {
141
+ // Before this, modules/agents/ was covered by NO key, so the three Agents goals
142
+ // had to stay wall-less: scope_modules:[] resolves to zero globs, and
143
+ // goal-scope-check then denies EVERY member-authored task (empty_scope).
144
+ assert.ok(scope.isModuleKey('agents'), 'roster missing module "agents"');
145
+ const globs = scope.globsForModules(['agents']);
146
+ assert.ok(globs.includes('modules/agents/'), 'agents owns its module directory');
147
+ assert.ok(globs.includes('.claude/agents/'), 'the committed definitions are the module source surface');
148
+ // Derived, not hand-set: none of the agents globs appear in PROTECTED_GLOBS.
149
+ assert.equal(scope.isProtectedModule('agents'), false);
150
+ assert.equal(
151
+ scope.scopeForModule('agents').protected,
152
+ scope.isProtected(globs),
153
+ 'protected must be the faithful derivation, never a literal',
154
+ );
155
+ // Asserted through the REAL consumer (directional containment), not re-implemented.
156
+ const covers = (touch) => gsc.scopeSubset(['agents'], [touch]).ok;
157
+ for (const touch of [
158
+ 'modules/agents/module.json',
159
+ 'modules/agents/migrations/agents_001_definitions.sql',
160
+ '.claude/agents/reviewer.md',
161
+ 'scripts/gds/agents-sync.js',
162
+ ]) {
163
+ assert.equal(covers(touch), true, `wall does not cover ${touch}`);
164
+ }
165
+ // …and still BOUNDS: the trust core and the sibling definition dir stay outside.
166
+ assert.equal(covers('src/bongos/auth.js'), false, 'the wall must not cover the kernel auth core');
167
+ assert.equal(covers('.claude/scheduled-tasks/bfg/SKILL.md'), false, 'scheduled-tasks belongs to autonomy, not agents');
168
+ });