@bongos/core 1.21.19 → 1.21.20
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 +35 -25
- package/docs/module-api-changelog.md +2 -0
- package/docs/page-readings.json +195 -196
- package/modules/autonomy/governor-docket.js +118 -4
- package/modules/economy/cost.js +36 -0
- package/modules/economy/governor-docket.js +81 -0
- package/modules/economy/routes/cost.js +2 -0
- package/modules/government/docket-floors.js +222 -0
- package/modules/government/docket-vocab.js +20 -0
- package/modules/government/docket.js +186 -5
- package/modules/hall-ui/public/street-inside.js +99 -43
- package/modules/ideas/governor-docket.js +42 -1
- package/modules/lifecycle/governor-docket.js +197 -2
- package/modules/security/routes/reports.js +16 -0
- package/modules/security/security.js +5 -2
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/src/module-api.js +1 -1
- package/tests/governor_docket.mjs +186 -2
- package/tests/hall_street_inside.mjs +97 -3
|
@@ -68,6 +68,8 @@ function docket(matters = [], over = {}) {
|
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
70
|
const goal = (id, status = 'open', title = `Goal ${id}`) => ({ id: String(id), title, status });
|
|
71
|
+
// a docket floor row in the route's shape (docket.js buildDocket `floors`)
|
|
72
|
+
const floorRow = (key, over = {}) => ({ key, building: key.split('.')[0], status: 'live', count: '3', line: 'three things', lines: [['One thing.', 'today']], hot: false, reason: null, ...over });
|
|
71
73
|
|
|
72
74
|
// ── the drawing is the prototype's ───────────────────────────────────────────
|
|
73
75
|
|
|
@@ -176,10 +178,12 @@ test('each building keeps the prototype\'s capacity, words and floor order', ()
|
|
|
176
178
|
|
|
177
179
|
// ── what a floor says comes from the docket, and only from it ───────────────
|
|
178
180
|
|
|
179
|
-
test('a floor
|
|
180
|
-
const
|
|
181
|
+
test('a floor whose engine is coming stands as not recorded yet: faint, no number, and says so', () => {
|
|
182
|
+
const coming = docket([], { floors: I.PLAN.charter.floors.map((f) => floorRow(f[2].floor, { status: 'coming', count: '', lines: [], reason: 'Not yet.' })) });
|
|
183
|
+
const b = I.civicInfo('charter', { docket: coming, goals: [], now: NOW });
|
|
181
184
|
assert.ok(b.floors.every((f) => f.status === 'coming' && f.count === ''), 'nothing on the charter is read yet');
|
|
182
|
-
|
|
185
|
+
assert.deepEqual(b.floors[1].lines, [['Not yet.', 'coming']], 'it says why, in the docket\'s words');
|
|
186
|
+
const bv = I.buildingModel({ key: 'charter', docket: coming, mode: 'dark', now: NOW });
|
|
183
187
|
const quiet = bv.texts.filter((t) => t.v === 'not recorded yet');
|
|
184
188
|
assert.equal(quiet.length, 8, 'every used floor says it on the wall');
|
|
185
189
|
assert.ok(bv.texts.filter((t) => t.s === 14.5).every((t) => t.c === '#8e8e93'), 'its name lettered faint, like a proposed floor');
|
|
@@ -265,6 +269,96 @@ test('who holds it: the docket\'s holders when it names them, else an empty seat
|
|
|
265
269
|
assert.deepEqual([held.seats, held.who, held.how], [['M'], 'Mira, treasurer', 'more than half must say yes']);
|
|
266
270
|
});
|
|
267
271
|
|
|
272
|
+
// ── real data on every floor (task 1004527) ─────────────────────────────────
|
|
273
|
+
|
|
274
|
+
test('every floor of every building is bound to a docket feed: none is left unwired', () => {
|
|
275
|
+
for (const key of S.CIVIC_KEYS) {
|
|
276
|
+
for (const [name, , bind] of I.PLAN[key].floors) {
|
|
277
|
+
assert.ok(bind && (bind.floor || bind.source || bind.reading || bind.goals), `${key} · ${name} reads something`);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test('a live floor shows exactly what the docket says: its count, line and lines; hot is being decided', () => {
|
|
283
|
+
const d = docket([], { floors: [
|
|
284
|
+
floorRow('treasury.rewards', { count: '$2,400', line: '$2,400 this month · 6 builders', lines: [['Paid to builders for shipped work.', 'each ship']] }),
|
|
285
|
+
floorRow('automation.allowed_goals', { count: '2', line: '2 goals it may build', lines: [['goal 7', 'first in line'], ['goal 99', 'since sep 2']] }),
|
|
286
|
+
floorRow('charter.founding', { count: '', line: '1 of 2 founding steps passed', lines: [['The charter.', 'passed']], hot: true }),
|
|
287
|
+
] });
|
|
288
|
+
const t = I.civicInfo('treasury', { docket: d, now: NOW }).floors.find((f) => f.name === 'Builder rewards');
|
|
289
|
+
assert.deepEqual([t.count, t.line, t.status, t.lines], ['$2,400', '$2,400 this month · 6 builders', 'ok', [['Paid to builders for shipped work.', 'each ship']]]);
|
|
290
|
+
const a = I.civicInfo('automation', { docket: d, goals: [goal(7, 'open', 'Onboarding')], now: NOW }).floors.find((f) => f.name === 'Allowed goals');
|
|
291
|
+
assert.deepEqual(a.lines, [['Onboarding', 'first in line'], ['goal 99', 'since sep 2']], 'a goal it knows is lettered by title; one it does not keeps its id');
|
|
292
|
+
const c = I.buildingModel({ key: 'charter', docket: d, mode: 'dark', now: NOW });
|
|
293
|
+
assert.equal(c.selFloor, 0, 'a founding step being decided is the floor chosen first');
|
|
294
|
+
assert.equal(c.openKicker, 'floor 1 · being decided');
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
test('Rules in force: each rule and what it settled, from its floor; the reading when the floor is absent', () => {
|
|
298
|
+
const d = docket([], { floors: [floorRow('rules.in_force', { count: '1', line: '1 rule · settled 18 this month', lines: [['Every filing lands in its goal without a vote.', 'settled 18']] })] });
|
|
299
|
+
const f = I.civicInfo('rules', { docket: d, now: NOW }).floors[0];
|
|
300
|
+
assert.deepEqual([f.count, f.line, f.lines[0]], ['1', '1 rule · settled 18 this month', ['Every filing lands in its goal without a vote.', 'settled 18']]);
|
|
301
|
+
const old = docket();
|
|
302
|
+
Object.assign(old.readings.find((r) => r.key === 'rules'), { status: 'live', figure: '1 rule', line: 'the landing rule settled 4 filings this month' });
|
|
303
|
+
assert.deepEqual(I.civicInfo('rules', { docket: old, now: NOW }).floors[0].count, '1', 'an older docket still reads the reading');
|
|
304
|
+
const h = I.holderOf('people', { decision: { who: 'metic', rule: 'consent' }, holders: [], holder_count: 3 }, []);
|
|
305
|
+
assert.equal(h.who, 'metic', 'a count with no names never letters undefined');
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
test('a floor the docket cannot answer never shows a number; Goals falls back to the open goals', () => {
|
|
309
|
+
const d = docket([], { floors: [floorRow('people.ranks', { status: 'not_yours', count: '9' }), floorRow('people.council', { status: 'off', count: '5', reason: 'Off here.' }), floorRow('people.newcomers', { status: 'unavailable', count: '2' })] });
|
|
310
|
+
const by = Object.fromEntries(I.civicInfo('people', { docket: d, now: NOW }).floors.map((f) => [f.name, f]));
|
|
311
|
+
for (const [n, st] of [['Ranks', 'not_yours'], ['The council', 'off'], ['Newcomers', 'unavailable'], ['Testers', 'unavailable']]) {
|
|
312
|
+
assert.deepEqual([by[n].status, by[n].count], [st, ''], `${n} says ${st} and no number`);
|
|
313
|
+
}
|
|
314
|
+
const g = I.civicInfo('planning', { docket: docket(), goals: [goal(1), goal(2)], now: NOW }).floors[0];
|
|
315
|
+
assert.deepEqual([g.count, g.line], ['2', '2 goals open'], 'an older docket without floors still counts the open goals');
|
|
316
|
+
const live = I.civicInfo('planning', { docket: docket([], { floors: [floorRow('planning.goals', { count: '1', line: '1 goal open', lines: [['Hall', '5 of 5 kept']] })] }), goals: [goal(1), goal(2)], now: NOW }).floors[0];
|
|
317
|
+
assert.deepEqual([live.count, live.lines], ['1', [['Hall', '5 of 5 kept']]], 'with the floor, its promises kept of total');
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
test('no made-up number: every count on every wall is one the docket sent', () => {
|
|
321
|
+
const ms = [matter({ id: 'access_requests:1', source: 'access_requests', building: 'people' })];
|
|
322
|
+
const floors = [floorRow('people.ranks', { count: '4' }), floorRow('treasury.hosting', { count: '$120' }), floorRow('release.live_month', { count: '7' })];
|
|
323
|
+
const d = docket(ms, { floors });
|
|
324
|
+
Object.assign(d.readings.find((r) => r.key === 'money'), { status: 'live', figure: '$1,100 left', line: 'left' });
|
|
325
|
+
const sent = new Set(['', ...floors.map((f) => f.count), '1', '0', '$1,100']);
|
|
326
|
+
for (const key of S.CIVIC_KEYS) {
|
|
327
|
+
for (const f of I.civicInfo(key, { docket: d, goals: [], now: NOW }).floors) assert.ok(sent.has(f.count), `${key} · ${f.name} shows ${f.count}`);
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test('who holds it reads the decision table: the people it seats, its rule, you, and a vacant seat', () => {
|
|
332
|
+
const info = { key: 'release', decision: { who: 'whoever may put a release live', rule: 'decide', rule_words: 'decides alone', as_today: true }, holders: [{ id: '95', name: 'Ana' }, { id: '7', login: 'theo' }], holder_count: 2 };
|
|
333
|
+
const h = I.holderOf('release', info, [], '95');
|
|
334
|
+
assert.deepEqual([h.seats, h.who, h.rule, h.how], [['you', 'T'], 'Ana and theo · whoever may put a release live', 'decide', 'one person decides']);
|
|
335
|
+
const many = I.holderOf('people', { decision: { who: 'metic and above', rule: 'consent' }, holders: [{ name: 'A' }, { name: 'B' }, { name: 'C' }], holder_count: 7 }, []);
|
|
336
|
+
assert.equal(many.who, 'A, B, C and 4 more · metic and above');
|
|
337
|
+
const vacant = I.holderOf('review', { decision: { who: 'no one yet', rule: 'vacant' }, holders: [] }, []);
|
|
338
|
+
assert.deepEqual([vacant.seats, vacant.who, vacant.how], [['—'], 'no one holds it yet', 'its matters wait until a seat is filled']);
|
|
339
|
+
const d = docket();
|
|
340
|
+
d.buildings.find((b) => b.key === 'review').decision = { who: 'no one yet', rule: 'vacant' };
|
|
341
|
+
assert.equal(I.civicInfo('review', { docket: d, now: NOW }).vacant, true, 'a vacant row is an unheld Review');
|
|
342
|
+
const held = I.civicInfo('review', { docket: docket(), now: NOW });
|
|
343
|
+
assert.deepEqual(held.floors.map((f) => f.name), ['Testers', 'Being tested', 'Signed off'], 'held, its queue is the prototype\'s Being tested');
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
test('the record drawers read the real history by month, file a sitting once, and cap a full drawer', () => {
|
|
347
|
+
const history = [
|
|
348
|
+
{ id: 'government:amendment:12', source: 'government', building: 'charter', title: '§6 Review: testers hold the seat', at: '2026-10-01T09:00:00.000Z', outcome: 'passed' },
|
|
349
|
+
{ id: 'government:founding:3', source: 'government', building: 'charter', title: 'Founding: the charter', at: '2026-07-04T09:00:00.000Z', outcome: 'passed' },
|
|
350
|
+
{ id: 'releases:9', source: 'releases', building: 'release', title: 'Invite flow', at: '2026-09-26T09:00:00.000Z', outcome: 'live' },
|
|
351
|
+
];
|
|
352
|
+
const ms = [matter({ id: 'board:12', source: 'board', building: 'charter', state: 'settled', title: '§6 Review: testers hold the seat', ref: { type: 'board_item', id: '12' }, opened_at: '2026-09-28T00:00:00.000Z' })];
|
|
353
|
+
const m = I.buildingModel({ key: 'charter', docket: docket(ms, { history }), mode: 'dark', now: NOW });
|
|
354
|
+
assert.deepEqual(m.drawers.map((x) => [x.label, x.count]), [['oct', 1], ['jul', 1]], 'the settled sitting is filed once, by when it closed');
|
|
355
|
+
assert.deepEqual(m.drawerItems.map((x) => [x.text, x.meta]), [['§6 Review: testers hold the seat', 'passed']]);
|
|
356
|
+
assert.equal(m.onFile, '1 matter on the docket · 2 on the record');
|
|
357
|
+
const lots = Array.from({ length: 15 }, (_, i) => ({ id: `releases:${i}`, building: 'release', title: `R${i}`, at: '2026-09-02T00:00:00.000Z', outcome: 'live' }));
|
|
358
|
+
const r = I.recordOf([], NOW, lots);
|
|
359
|
+
assert.deepEqual([r[0].count, r[0].items.length, r[0].items[12]], [15, 13, ['and 3 more', '']]);
|
|
360
|
+
});
|
|
361
|
+
|
|
268
362
|
test('the annex lot stands only when the building is full; an unheld Review is the dashed ghost', () => {
|
|
269
363
|
assert.equal(I.buildingModel({ key: 'charter', docket: docket(), mode: 'dark', now: NOW }).hasAnnex, false, '8 of 9');
|
|
270
364
|
const save = I.PLAN.rules.cap;
|