@bongos/core 1.19.902 → 1.19.904
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 +37 -67
- package/bin/bongos.js +7 -13
- package/docs/copy-inventory.md +6 -7
- package/docs/copy-registry.json +9 -18
- package/docs/module-api-changelog.md +4 -0
- package/docs/recipes/bongos-cli-release.md +3 -2
- package/docs/recipes/instance-first-run.md +1 -2
- package/docs/recipes/local-dev.md +0 -2
- package/modules/hall-ui/public/collab-lib.js +22 -0
- package/modules/hall-ui/public/collab.js +17 -2
- package/modules/hall-ui/public/hall-kit.js +11 -5
- package/modules/hall-ui/public/profile-charts.js +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/api.js +1 -25
- package/scripts/gds/build-cli-package.js +17 -35
- package/scripts/gds/claude-materialize.js +1 -1
- package/scripts/gds/cli-lib.js +5 -127
- package/scripts/gds/go-live.js +1 -1
- package/scripts/gds/login.js +1 -1
- package/scripts/gds/plain-cards.js +0 -8
- package/scripts/gds/ship-land.js +2 -18
- package/src/module-api.js +1 -1
- package/tests/bongos_dispatch.mjs +4 -4
- package/tests/claude_materialize.mjs +1 -1
- package/tests/cli_package.mjs +1 -4
- package/tests/cli_surface.mjs +1 -88
- package/tests/collab_page.mjs +44 -0
- package/tests/hall_kit_charts.mjs +38 -0
- package/tests/plain_cards.mjs +2 -2
- package/tests/rank_tier_single_source.mjs +3 -5
- package/scripts/gds/box-connect-lib.js +0 -550
- package/scripts/gds/code.js +0 -105
- package/scripts/gds/shell.js +0 -102
- package/tests/bongos_box_connect.mjs +0 -166
- package/tests/box_scope_predicate.mjs +0 -117
- package/tests/cli_box_scope.mjs +0 -92
package/tests/cli_surface.mjs
CHANGED
|
@@ -205,7 +205,7 @@ function guidanceOutput(fn, env) {
|
|
|
205
205
|
return out;
|
|
206
206
|
}
|
|
207
207
|
|
|
208
|
-
const GUIDANCE = ['printUnauthGuidanceAndExit', '
|
|
208
|
+
const GUIDANCE = ['printUnauthGuidanceAndExit', 'requireSession'];
|
|
209
209
|
|
|
210
210
|
test('no guidance path speaks slash commands to a terminal reader', () => {
|
|
211
211
|
for (const fn of GUIDANCE) {
|
|
@@ -220,7 +220,6 @@ test('the agent still gets its slash commands from those same paths', () => {
|
|
|
220
220
|
// The inverse guard: a blanket rewrite that "fixed" the leak by deleting the slash commands
|
|
221
221
|
// would break the reader they were written for, and nothing else here would notice.
|
|
222
222
|
assert.match(runGuidance('printUnauthGuidanceAndExit', {}), /\/builder-reauth/);
|
|
223
|
-
assert.match(runGuidance('printBoxScopeGuidanceAndExit', {}), /\/builder-reauth/);
|
|
224
223
|
assert.match(runGuidance('requireSession', {}), /\/builder-setup/);
|
|
225
224
|
});
|
|
226
225
|
|
|
@@ -231,7 +230,6 @@ test('every shipped script that names a slash command routes it through the surf
|
|
|
231
230
|
const ALLOWED = new Set([
|
|
232
231
|
'scripts/gds/cli-lib.js', // agent side of the three branches, proven above
|
|
233
232
|
'scripts/gds/setup.js', // re-run + newcomer-path lines, asserted below
|
|
234
|
-
'scripts/gds/box-connect-lib.js',// key-registration hint, asserted below
|
|
235
233
|
'scripts/gds/surface.js', // the switch itself
|
|
236
234
|
'scripts/gds/start.js', // section 3's documented agent-only exemptions
|
|
237
235
|
]);
|
|
@@ -257,88 +255,3 @@ test('setup.js routes its newcomer path and re-run lines through the surface', (
|
|
|
257
255
|
assert.ok(!/console\.log\(' 2\. Survey work: \/builder-start'\)/.test(src), 'hard-coded /builder-start left in setup.js');
|
|
258
256
|
assert.match(src, /re-run \$\{surfaceAction\('setup'\)\}/, 'the re-run lines must use surfaceAction');
|
|
259
257
|
});
|
|
260
|
-
|
|
261
|
-
test('box-connect-lib names the box command for the reader it has', () => {
|
|
262
|
-
const src = fs.readFileSync(path.join(REPO_ROOT, 'scripts', 'gds', 'box-connect-lib.js'), 'utf8');
|
|
263
|
-
assert.ok(!/add it via \/builder-box/.test(src), 'hard-coded /builder-box left in box-connect-lib.js');
|
|
264
|
-
assert.match(src, /add it via \$\{surfaceAction\('box'\)\}/);
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
// ── 5. The dead end: an instance with no dev box (task 1003730) ─────────────────────────────
|
|
268
|
-
//
|
|
269
|
-
// `bongos claim N` refuses and points at `bongos shell`. On an instance whose manifest carries
|
|
270
|
-
// no dev-box module that call 404s, so the public CLI's only offered route to a checkout was a
|
|
271
|
-
// hard error — a newcomer could see the work and not start it. The manifest always said so;
|
|
272
|
-
// nothing read it.
|
|
273
|
-
|
|
274
|
-
const cliLib = require(path.join(REPO_ROOT, 'scripts', 'gds', 'cli-lib.js'));
|
|
275
|
-
|
|
276
|
-
test('the manifest decides, and only an explicit roster can prove absence', () => {
|
|
277
|
-
assert.equal(cliLib.devBoxEnabledInManifest({ modules: { enabled: { 'dev-box': true } } }), true);
|
|
278
|
-
assert.equal(cliLib.devBoxEnabledInManifest({ modules: { enabled: { economy: true } } }), false);
|
|
279
|
-
});
|
|
280
|
-
|
|
281
|
-
test('the check fails OPEN — a hiccup must never block a working box', () => {
|
|
282
|
-
// Every one of these is "I could not tell", and the safe answer to that is to proceed and let
|
|
283
|
-
// the real API call speak. Failing closed here would break `bongos shell` on every instance
|
|
284
|
-
// running a core too old to publish a module roster.
|
|
285
|
-
for (const manifest of [null, undefined, {}, { modules: {} }, { modules: { enabled: null } }, 'nonsense']) {
|
|
286
|
-
assert.equal(cliLib.devBoxEnabledInManifest(manifest), true, `should fail open for ${JSON.stringify(manifest)}`);
|
|
287
|
-
}
|
|
288
|
-
});
|
|
289
|
-
|
|
290
|
-
test('the explanation names a command the reader can actually run', () => {
|
|
291
|
-
// It routes through the same switch as everything else: a terminal reader must not be handed
|
|
292
|
-
// `/builder-claim`, and an agent in a checkout must still get it.
|
|
293
|
-
const prev = process.env.BONGOS_SURFACE;
|
|
294
|
-
try {
|
|
295
|
-
process.env.BONGOS_SURFACE = 'terminal';
|
|
296
|
-
const term = cliLib.devBoxUnavailableMessage();
|
|
297
|
-
assert.ok(!/\/builder-/.test(term), `slash command in the terminal dev-box message: ${term}`);
|
|
298
|
-
assert.match(term, /bongos claim N/);
|
|
299
|
-
assert.match(term, /doesn't offer cloud dev boxes/);
|
|
300
|
-
assert.match(term, /checkout/, 'must name the actual alternative, not just refuse');
|
|
301
|
-
|
|
302
|
-
delete process.env.BONGOS_SURFACE;
|
|
303
|
-
assert.match(cliLib.devBoxUnavailableMessage(), /\/builder-claim N/, 'agents keep the slash command');
|
|
304
|
-
} finally {
|
|
305
|
-
if (prev === undefined) delete process.env.BONGOS_SURFACE;
|
|
306
|
-
else process.env.BONGOS_SURFACE = prev;
|
|
307
|
-
}
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
test('both box entry points check BEFORE they touch the box API', () => {
|
|
311
|
-
// Ordering is the whole point: key registration hits the box API too, so gating only the
|
|
312
|
-
// ensure call still printed "Could not register your key (not_found)" first.
|
|
313
|
-
const src = fs.readFileSync(path.join(REPO_ROOT, 'scripts', 'gds', 'box-connect-lib.js'), 'utf8');
|
|
314
|
-
assert.match(
|
|
315
|
-
src,
|
|
316
|
-
/async function prepareConnection\([^)]*\) \{[\s\S]{0,400}?await assertDevBoxAvailable\(\);[\s\S]{0,200}?ensureSshKey/,
|
|
317
|
-
'prepareConnection must assert before ensureSshKey/key registration',
|
|
318
|
-
);
|
|
319
|
-
assert.match(
|
|
320
|
-
src,
|
|
321
|
-
/async function ensureBoxActive\([^)]*\) \{\s*await assertDevBoxAvailable\(\);/,
|
|
322
|
-
'ensureBoxActive must assert before postBoxEnsure',
|
|
323
|
-
);
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
test('the raw box passthrough explains a 404 too', () => {
|
|
327
|
-
// `bongos box` is api.js aimed at POST /box/ensure, so without this it dumped a bare 404 body.
|
|
328
|
-
const src = fs.readFileSync(path.join(REPO_ROOT, 'scripts', 'gds', 'api.js'), 'utf8');
|
|
329
|
-
assert.match(src, /instanceDevBoxSupport/, 'api.js must consult the dev-box check');
|
|
330
|
-
assert.match(src, /res\.status === 404/, 'only on a 404');
|
|
331
|
-
assert.match(src, /\\\/box\\b/, 'and only for a box path — api.js stays generic');
|
|
332
|
-
});
|
|
333
|
-
|
|
334
|
-
test('the generated dispatcher does not promise a dev box it cannot verify', () => {
|
|
335
|
-
// The dispatcher has no session or manifest yet, so it must present `bongos shell` as
|
|
336
|
-
// conditional. Stating it flatly is what sent newcomers into the 404.
|
|
337
|
-
const gen = fs.readFileSync(path.join(REPO_ROOT, 'scripts', 'gds', 'build-cli-package.js'), 'utf8');
|
|
338
|
-
assert.ok(
|
|
339
|
-
!/'To get a checkout without installing anything locally/.test(gen),
|
|
340
|
-
'the unconditional promise is back in the checkout-only refusal',
|
|
341
|
-
);
|
|
342
|
-
assert.match(gen, /If this instance offers cloud dev boxes/);
|
|
343
|
-
assert.match(gen, /Not every instance runs dev boxes/);
|
|
344
|
-
});
|
package/tests/collab_page.mjs
CHANGED
|
@@ -421,6 +421,50 @@ test('the SIDEBAR badge: the Collab item declares the help_requests count', () =
|
|
|
421
421
|
assert.match(fn, /href: '\/collab'/, 'pointing at the page, not the retired hash view');
|
|
422
422
|
});
|
|
423
423
|
|
|
424
|
+
// ---- THE MINGLE ROW SPEAKS THE ROLE ---------------------------------------
|
|
425
|
+
// task 1003967 (artist review of 1003960). Run, not grepped: the first version of this
|
|
426
|
+
// guard matched identifier names in the source, which would have agreed with a swapped
|
|
427
|
+
// article just as happily as with a correct one. The sentence is pure, so execute it.
|
|
428
|
+
|
|
429
|
+
test('the mingle sub-line reads as English for every pair, and never shows the column', () => {
|
|
430
|
+
const L = lib();
|
|
431
|
+
const OTB = domUtils();
|
|
432
|
+
const word = (d) => (OTB.roleIsUnset(d) ? '' : OTB.roleWord(d));
|
|
433
|
+
const line = (a, b) => L.pairSentence(word(a), word(b));
|
|
434
|
+
|
|
435
|
+
assert.equal(line('engineer', 'artist'), 'An engineer — you are an artist. Mingle pairs across roles.');
|
|
436
|
+
assert.equal(line('artist', 'ideator'), 'An artist — you are an ideator. Mingle pairs across roles.');
|
|
437
|
+
|
|
438
|
+
// 'ui' is the acronym exception roleWord() already makes, and it is why the article
|
|
439
|
+
// cannot be a plain vowel test — "an UI" is how that shortcut fails.
|
|
440
|
+
assert.equal(line('ui', 'engineer'), 'A UI — you are an engineer. Mingle pairs across roles.');
|
|
441
|
+
assert.equal(line('engineer', 'ui'), 'An engineer — you are a UI. Mingle pairs across roles.');
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
test('a pair with an unset role drops the comparison rather than naming the default', () => {
|
|
445
|
+
const L = lib();
|
|
446
|
+
const OTB = domUtils();
|
|
447
|
+
const word = (d) => (OTB.roleIsUnset(d) ? '' : OTB.roleWord(d));
|
|
448
|
+
|
|
449
|
+
// The defect this replaced rendered the database's own word — "A unclassified — you are
|
|
450
|
+
// a engineer" — which is the example V2 names. Nothing a person did not choose may reach
|
|
451
|
+
// the sentence, whichever side is missing.
|
|
452
|
+
for (const pair of [[OTB.ROLE_UNSET, 'engineer'], ['engineer', OTB.ROLE_UNSET], [null, 'artist'], ['', '']]) {
|
|
453
|
+
const out = L.pairSentence(word(pair[0]), word(pair[1]));
|
|
454
|
+
assert.equal(out, 'Mingle pairs across roles.', `pair ${JSON.stringify(pair)}`);
|
|
455
|
+
assert.doesNotMatch(out, /unclassified/);
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
test('aOrAn is the article rule, stated once', () => {
|
|
460
|
+
const L = lib();
|
|
461
|
+
assert.equal(L.aOrAn('engineer'), 'an');
|
|
462
|
+
assert.equal(L.aOrAn('artist'), 'an');
|
|
463
|
+
assert.equal(L.aOrAn('ideator'), 'an');
|
|
464
|
+
assert.equal(L.aOrAn('UI'), 'a', 'the acronym is spoken "you-eye", so it takes "a"');
|
|
465
|
+
assert.equal(L.aOrAn('ui'), 'a');
|
|
466
|
+
});
|
|
467
|
+
|
|
424
468
|
// ---- THE ANSWERABLE CLAUSE ------------------------------------------------
|
|
425
469
|
test('the asker can settle in place, with both outcomes', () => {
|
|
426
470
|
const js = read('modules', 'hall-ui', 'public', 'collab.js');
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
|
|
16
16
|
import { test } from 'node:test';
|
|
17
17
|
import assert from 'node:assert/strict';
|
|
18
|
+
import { readFileSync } from 'node:fs';
|
|
18
19
|
import fs from 'node:fs';
|
|
19
20
|
import path from 'node:path';
|
|
20
21
|
import vm from 'node:vm';
|
|
@@ -642,6 +643,43 @@ test('CAL: a one-week window says "week", and the full window still says "the la
|
|
|
642
643
|
assert.match(full, /over the last year/, 'the 53-week window is a year and should read like one');
|
|
643
644
|
});
|
|
644
645
|
|
|
646
|
+
// --- task 1003621 (artist review of 1003583): ONE name for the drawn window ---------
|
|
647
|
+
|
|
648
|
+
test('CAL: the window carries its own name, and the aria summary uses that name', () => {
|
|
649
|
+
// The half the arithmetic fix left behind. 1003583 made the summary and the caption
|
|
650
|
+
// COUNT the same window; they still DESCRIBED it differently — the aria summary said
|
|
651
|
+
// "the last year" (a rolling 53 weeks, which is what is drawn) while the caption under
|
|
652
|
+
// the same grid said "this year" (a calendar year, which is not). V3 asks the
|
|
653
|
+
// accessible line and the visible one to state the same fact, so the phrase now lives
|
|
654
|
+
// on the window itself and both read it.
|
|
655
|
+
for (const weeks of [1, 6, 53]) {
|
|
656
|
+
const win = kit.calendarWindow([{ local_date: '2026-09-03', count: 1 }], { today: '2026-09-03', weeks });
|
|
657
|
+
assert.ok(win && typeof win.spanLabel === 'string' && win.spanLabel.length,
|
|
658
|
+
`calendarWindow must name its own window (weeks=${weeks})`);
|
|
659
|
+
const html = kit.calendarGridHtml([{ local_date: '2026-09-03', count: 1 }], { today: '2026-09-03', weeks });
|
|
660
|
+
assert.ok(html.includes(`over ${win.spanLabel}:`),
|
|
661
|
+
`the aria summary must use the window's own name, not a second copy of the rule (weeks=${weeks})`);
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
test('CAL: the caption under the grid cannot drift back to "this year"', () => {
|
|
666
|
+
// The caption is rendered by profile-charts.js from win.spanLabel. What is pinned here
|
|
667
|
+
// is the phrase it must never go back to: a calendar year is not the window drawn, and
|
|
668
|
+
// a reader comparing the number to the picture is the person who pays for the gap.
|
|
669
|
+
const win = kit.calendarWindow([{ local_date: '2026-09-03', count: 1 }], { today: '2026-09-03', weeks: 53 });
|
|
670
|
+
assert.equal(win.spanLabel, 'the last year');
|
|
671
|
+
|
|
672
|
+
// Asserted against the SOURCE because the caption is assembled in a browser global the
|
|
673
|
+
// unit lane does not render. The same idiom tests/modules_route_wiring.mjs uses.
|
|
674
|
+
const src = readFileSync(new URL('../modules/hall-ui/public/profile-charts.js', import.meta.url), 'utf8');
|
|
675
|
+
assert.match(src, /spanLabel/, 'the caption must take the window name from the window');
|
|
676
|
+
assert.doesNotMatch(
|
|
677
|
+
src, /shipped this year/,
|
|
678
|
+
'the caption said "this year" while the grid drew a rolling 53 weeks and the aria '
|
|
679
|
+
+ 'summary said "the last year" — one window, two names, which is the V3 break',
|
|
680
|
+
);
|
|
681
|
+
});
|
|
682
|
+
|
|
645
683
|
test('CAL: the summary counts only the days the grid actually draws', () => {
|
|
646
684
|
// 2026-09-03 is a Thursday, so a 2-week grid starts at Sunday 2026-08-23. A ship on
|
|
647
685
|
// 2026-08-01 is real history the caller supplied, but it is off this picture, and it
|
package/tests/plain_cards.mjs
CHANGED
|
@@ -107,7 +107,7 @@ test('every card is complete, and short enough to be read', () => {
|
|
|
107
107
|
|
|
108
108
|
test('the restrictions that must never be worked around are marked by_design', () => {
|
|
109
109
|
for (const code of ['REBASE_REQUIRED', 'INSUFFICIENT_RANK', 'rank_too_low_for_task', 'session_already_has_active_claim',
|
|
110
|
-
'main_checkout_claim', 'ship:protected_branch', 'ship:auth', '
|
|
110
|
+
'main_checkout_claim', 'ship:protected_branch', 'ship:auth', 'no_push_credential', 'grade_refused_authorization']) {
|
|
111
111
|
assert.equal(cardFor(code)?.by_design, true, code);
|
|
112
112
|
}
|
|
113
113
|
assert.match(cardFor('no_push_credential').next, /never ask/i);
|
|
@@ -164,7 +164,7 @@ test('a person at a terminal gets no card — it is written in the agent\'s voic
|
|
|
164
164
|
|
|
165
165
|
test('the sign-in printers, the release refusals and the claim guard each print their card', () => {
|
|
166
166
|
const wired = {
|
|
167
|
-
'scripts/gds/cli-lib.js': ['session_invalid', '
|
|
167
|
+
'scripts/gds/cli-lib.js': ['session_invalid', 'no_session'],
|
|
168
168
|
'scripts/gds/release.js': ['no_active_claim', 'CLAIM_SESSION_MISMATCH'],
|
|
169
169
|
'scripts/gds/claim.js': ['main_checkout_claim'],
|
|
170
170
|
'scripts/gds/ship-land.js': ['land_pending', 'reconcile_blocked'],
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// modules/discord/discord-channels.js): dependency-free substrate
|
|
13
13
|
// (permission-path-check runs in a bare git hook), source-parsed literals
|
|
14
14
|
// (src/bongos/db.js — gate-review.js + diagram-facts.js regex its text),
|
|
15
|
-
// standalone CLIs (sequence
|
|
15
|
+
// standalone CLIs (sequence), the require-free dev-box
|
|
16
16
|
// module template (boxes.js), zero-dep catalog + its text-parsing auditor
|
|
17
17
|
// (route-rank-check), and the hall browser statics.
|
|
18
18
|
// 3. NO NEW parallel map can land: a repo scan flags any rank→number map or
|
|
@@ -38,7 +38,6 @@ const lifecycleDb = require(path.join(ROOT, 'modules', 'lifecycle', 'db.js'));
|
|
|
38
38
|
const ppc = require(path.join(ROOT, 'src', 'bongos', 'permission-path-check.js'));
|
|
39
39
|
const rrc = require(path.join(ROOT, 'src', 'bongos', 'route-rank-check.js'));
|
|
40
40
|
const sequence = require(path.join(ROOT, 'scripts', 'gds', 'sequence.js'));
|
|
41
|
-
const boxConnect = require(path.join(ROOT, 'scripts', 'gds', 'box-connect-lib.js'));
|
|
42
41
|
const boxes = require(path.join(ROOT, 'modules', 'dev-box', 'boxes.js'));
|
|
43
42
|
const catalog = require(path.join(ROOT, 'modules', 'government', 'catalog.js'));
|
|
44
43
|
|
|
@@ -82,8 +81,8 @@ test('1-based local copies match: permission-path-check, sequence CLI', () => {
|
|
|
82
81
|
assert.deepEqual(sequence.RANK_TIER, TIER, 'sequence.js CLI copy drifted');
|
|
83
82
|
});
|
|
84
83
|
|
|
85
|
-
test('0-based local copies match:
|
|
86
|
-
for (const [name, map] of [['
|
|
84
|
+
test('0-based local copies match: dev-box boxes.js', () => {
|
|
85
|
+
for (const [name, map] of [['boxes', boxes.RANK_ORDER]]) {
|
|
87
86
|
assert.deepEqual(Object.keys(map).sort(), [...RANK_KEYS].sort(), `${name} keys drifted`);
|
|
88
87
|
for (const r of RANK_KEYS) {
|
|
89
88
|
assert.equal(map[r], TIER[r] - 1, `${name}.RANK_ORDER.${r} must be tier-1 (0-based)`);
|
|
@@ -193,7 +192,6 @@ const MAP_ALLOWLIST = new Set([
|
|
|
193
192
|
'src/bongos/db.js',
|
|
194
193
|
'src/bongos/permission-path-check.js',
|
|
195
194
|
'scripts/gds/sequence.js',
|
|
196
|
-
'scripts/gds/box-connect-lib.js',
|
|
197
195
|
'modules/dev-box/boxes.js',
|
|
198
196
|
'modules/hall-ui/public/builders.js',
|
|
199
197
|
// task 1003206 carved the hall page scripts up. builders.js KEPT its map (the
|