@bongos/core 1.24.40 → 1.24.42
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 +103 -103
- package/docs/canonical-permissions.md +2 -1
- package/docs/copy-inventory.md +198 -223
- package/docs/copy-registry.json +316 -580
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +11 -69
- package/docs/page-readings.json +348 -728
- package/modules/hall-ui/public/ai-studio.html +2 -2
- package/modules/hall-ui/public/atlas.html +2 -2
- package/modules/hall-ui/public/blockers.html +2 -2
- package/modules/hall-ui/public/board-room.html +2 -2
- package/modules/hall-ui/public/builders.js +17 -262
- package/modules/hall-ui/public/city.html +2 -2
- package/modules/hall-ui/public/collab.html +2 -2
- package/modules/hall-ui/public/commands.html +2 -2
- package/modules/hall-ui/public/copy-desk.html +2 -2
- package/modules/hall-ui/public/craft-mode.js +1 -1
- package/modules/hall-ui/public/deploy.html +2 -2
- package/modules/hall-ui/public/gate.js +12 -5
- package/modules/hall-ui/public/goal-map.html +2 -2
- package/modules/hall-ui/public/goals.html +2 -2
- package/modules/hall-ui/public/government.html +2 -2
- package/modules/hall-ui/public/hall-render.js +3 -248
- package/modules/hall-ui/public/hall-widgets.js +3 -14
- package/modules/hall-ui/public/idea.html +2 -2
- package/modules/hall-ui/public/ideas.html +2 -2
- package/modules/hall-ui/public/index.html +4 -5
- package/modules/hall-ui/public/market-watch-item.html +2 -2
- package/modules/hall-ui/public/market-watch-rules.html +2 -2
- package/modules/hall-ui/public/market-watch.html +2 -2
- package/modules/hall-ui/public/modules.html +2 -2
- package/modules/hall-ui/public/money.html +2 -2
- package/modules/hall-ui/public/my-agents.html +2 -2
- package/modules/hall-ui/public/palette.js +10 -0
- package/modules/hall-ui/public/primer.html +2 -2
- package/modules/hall-ui/public/profile.html +2 -2
- package/modules/hall-ui/public/project-settings.html +2 -2
- package/modules/hall-ui/public/roadmap.html +2 -2
- package/modules/hall-ui/public/roster.html +2 -2
- package/modules/hall-ui/public/sessions.js +21 -10
- package/modules/hall-ui/public/settings.html +2 -2
- package/modules/hall-ui/public/setup-gate.js +156 -0
- package/modules/hall-ui/public/shell.js +17 -3
- package/modules/hall-ui/public/street.html +2 -2
- package/modules/hall-ui/public/studio.html +2 -2
- package/modules/hall-ui/public/style.css +85 -117
- package/modules/hall-ui/public/task.html +2 -2
- package/modules/hall-ui/public/thinking.html +2 -2
- package/modules/hall-ui/public/tweak-editor.html +2 -2
- package/modules/hall-ui/public/watch.html +120 -7
- package/modules/hall-ui/public/watch.js +11 -0
- package/modules/hall-ui/public/watch.states.json +49 -1
- package/modules/hall-ui/public/work.html +2 -2
- package/modules/hall-ui/records/setup-gate.md +43 -0
- package/modules/lifecycle/routes/tasks.js +2 -2
- package/modules/onboarding/onboarding-state.js +41 -20
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/scripts/bongos/gen-hall-sitemap.js +2 -0
- package/src/bongos/routes/me.js +2 -2
- package/src/bongos/serve-internal.js +15 -22
- package/src/module-api.js +1 -1
- package/tests/autonomy_builder_scope.mjs +2 -1
- package/tests/hall_audit.mjs +1 -1
- package/tests/hall_board_world.mjs +1 -1
- package/tests/hall_landing_boot.mjs +2 -3
- package/tests/hall_loading.mjs +13 -2
- package/tests/hall_nav.mjs +5 -2
- package/tests/hall_onboarding_bongos_setup.mjs +14 -14
- package/tests/hall_oversight_tabs.mjs +162 -0
- package/tests/hall_oversight_world.mjs +23 -9
- package/tests/hall_page_gate_map.mjs +7 -2
- package/tests/hall_roster_world.mjs +14 -9
- package/tests/hall_sessions_ledger.mjs +2 -1
- package/tests/module_contributions.mjs +4 -2
- package/tests/nav_permission_atoms.mjs +4 -2
- package/tests/onboarding_state.mjs +33 -0
- package/tests/sessions_kit_pager.mjs +8 -4
- package/tests/setup_flow.mjs +242 -0
- package/tests/welcome_boot.mjs +3 -2
- package/modules/hall-ui/public/gate.html +0 -76
- package/modules/hall-ui/public/gate.states.json +0 -60
- package/modules/hall-ui/public/sessions.html +0 -128
- package/modules/hall-ui/public/sessions.states.json +0 -59
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
// tests/setup_flow.mjs — the setup flow a new builder finishes once (task 1005076).
|
|
2
|
+
//
|
|
3
|
+
// Owner, 2026-11-05: a new builder is held on one setup screen until every step is done,
|
|
4
|
+
// and once set up they are in for good — no setup banner on Home. Three halves, each run
|
|
5
|
+
// for real: the claimable route ticks first_start only for a Claude (CLI) session; the
|
|
6
|
+
// shell loads setup-gate.js and draws nothing else over it while setup is incomplete; and
|
|
7
|
+
// the screen draws the steps, saves a craft, and lets the builder in once /me says done.
|
|
8
|
+
// The rule itself (which steps, who goes straight in) is in tests/onboarding_state.mjs.
|
|
9
|
+
//
|
|
10
|
+
// Run: node tests/setup_flow.mjs
|
|
11
|
+
|
|
12
|
+
import { strict as assert } from 'node:assert';
|
|
13
|
+
import fs from 'node:fs';
|
|
14
|
+
import path from 'node:path';
|
|
15
|
+
import vm from 'node:vm';
|
|
16
|
+
import { fileURLToPath } from 'node:url';
|
|
17
|
+
import { makeEl, realOTB, settle } from './helpers.mjs';
|
|
18
|
+
|
|
19
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
20
|
+
const read = (rel) => fs.readFileSync(path.join(ROOT, rel), 'utf8');
|
|
21
|
+
const ROUTE = read('modules/lifecycle/routes/tasks.js');
|
|
22
|
+
const SHELL = read('modules/hall-ui/public/shell.js');
|
|
23
|
+
const GATE = read('modules/hall-ui/public/setup-gate.js');
|
|
24
|
+
|
|
25
|
+
let passed = 0;
|
|
26
|
+
let failed = 0;
|
|
27
|
+
const t = async (name, fn) => {
|
|
28
|
+
try { await fn(); passed += 1; console.log(` PASS ${name}`); }
|
|
29
|
+
catch (e) { failed += 1; console.log(` FAIL ${name}\n ${e.message}`); }
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const STEPS = (cleared) => ['signed_in', 'cli_setup', 'pick_disciplines', 'first_start']
|
|
33
|
+
.map((id) => ({ id, label: `Label ${id}`, hint: `Hint ${id}`, cleared: cleared.includes(id) }));
|
|
34
|
+
|
|
35
|
+
console.log('the setup flow (task 1005076)');
|
|
36
|
+
|
|
37
|
+
// ---- the claimable route: first_start only from Claude --------------------
|
|
38
|
+
|
|
39
|
+
// The real guarded statement, run against each kind of session.
|
|
40
|
+
const from = ROUTE.indexOf("if (req.gdsSession && req.gdsSession.source === 'cli')");
|
|
41
|
+
const guard = ROUTE.slice(from, ROUTE.indexOf('\n', from));
|
|
42
|
+
const runGuard = (gdsSession) => {
|
|
43
|
+
const marks = [];
|
|
44
|
+
const seams = { resolveOptional: () => ({ safeMarkStage: (_pool, id, stage) => marks.push([id, stage]) }) };
|
|
45
|
+
vm.runInNewContext(guard, { req: { gdsSession, builder: { id: 7 } }, seams, pool: {} });
|
|
46
|
+
return marks;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
await t('GET /tasks/claimable marks first_start for a Claude (CLI) session, never for the browser', () => {
|
|
50
|
+
assert.ok(from > 0, 'the guard is in the claimable handler');
|
|
51
|
+
assert.match(guard, /safeMarkStage\(pool, req\.builder\.id, 'first_start', req\.builder\)/);
|
|
52
|
+
assert.deepEqual(runGuard({ source: 'cli' }), [[7, 'first_start']]);
|
|
53
|
+
assert.deepEqual(runGuard({ source: 'web' }), [], 'the hall pages read this feed from the browser');
|
|
54
|
+
assert.deepEqual(runGuard(null), []);
|
|
55
|
+
assert.equal((ROUTE.match(/'first_start'/g) || []).length, 1, 'no unguarded mark elsewhere in the routes');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// ---- the screen ------------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
function bootGate({ patchOk = true } = {}) {
|
|
61
|
+
const body = makeEl('body');
|
|
62
|
+
const htmlClasses = new Set();
|
|
63
|
+
const requests = [];
|
|
64
|
+
let reloaded = 0;
|
|
65
|
+
let me = null;
|
|
66
|
+
const api = { request: (method, p, opts) => {
|
|
67
|
+
requests.push({ method, p, body: opts && opts.body });
|
|
68
|
+
if (method === 'PATCH') return Promise.resolve({ ok: patchOk, status: patchOk ? 200 : 422, data: {} });
|
|
69
|
+
return Promise.resolve({ ok: true, status: 200, data: me });
|
|
70
|
+
} };
|
|
71
|
+
const intervals = [];
|
|
72
|
+
const document = {
|
|
73
|
+
body,
|
|
74
|
+
documentElement: { classList: { add: (c) => htmlClasses.add(c), remove: (c) => htmlClasses.delete(c) } },
|
|
75
|
+
createElement: (tag) => makeEl(tag),
|
|
76
|
+
getElementById: (id) => body.querySelector('#' + id),
|
|
77
|
+
activeElement: null,
|
|
78
|
+
};
|
|
79
|
+
// the crafts this project offers (dom-utils.js reads them off __MODULES__)
|
|
80
|
+
const window = { OTB: { ...realOTB(), disciplines: () => ['engineer', 'artist', 'ui'] }, location: { reload: () => { reloaded += 1; } } };
|
|
81
|
+
const sandbox = { window, document, setInterval: (fn) => { intervals.push(fn); return intervals.length; }, clearInterval() {} };
|
|
82
|
+
vm.createContext(sandbox);
|
|
83
|
+
vm.runInContext(GATE, sandbox, { filename: 'setup-gate.js' });
|
|
84
|
+
return {
|
|
85
|
+
show: (setup, builder) => window.OTBSetupGate.show(setup, { api, builder }),
|
|
86
|
+
gate: () => body.querySelector('#setup-gate'),
|
|
87
|
+
htmlClasses, requests, intervals,
|
|
88
|
+
setMe: (v) => { me = v; },
|
|
89
|
+
reloaded: () => reloaded,
|
|
90
|
+
click: (target) => body.querySelector('#setup-gate').fire('click', { target }),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
await t('the screen lists every step in order, ticks the cleared ones, and opens the first gap', () => {
|
|
95
|
+
const g = bootGate();
|
|
96
|
+
g.show({ complete: false, steps: STEPS(['signed_in', 'cli_setup']) }, { display_name: 'Sam' });
|
|
97
|
+
const gate = g.gate();
|
|
98
|
+
assert.ok(gate, 'drawn');
|
|
99
|
+
assert.equal(gate.getAttribute('aria-modal'), 'true');
|
|
100
|
+
assert.ok(g.htmlClasses.has('setup-gate-open'), 'the hall behind it does not scroll');
|
|
101
|
+
assert.match(gate.querySelector('#setup-gate-h').textContent, /Welcome, Sam/);
|
|
102
|
+
assert.equal(gate.querySelector('.setup-gate__count').textContent, '2 of 4 done');
|
|
103
|
+
const steps = gate.querySelectorAll('.setup-gate__step');
|
|
104
|
+
assert.deepEqual(steps.map((s) => s.className), ['setup-gate__step is-done', 'setup-gate__step is-done', 'setup-gate__step is-current', 'setup-gate__step']);
|
|
105
|
+
assert.match(steps[2].textContent, /Hint pick_disciplines/, 'only the current step shows its hint');
|
|
106
|
+
assert.doesNotMatch(steps[3].textContent, /Hint first_start/);
|
|
107
|
+
assert.deepEqual(gate.querySelectorAll('[data-craft]').map((b) => b.textContent), ['Engineer', 'Artist', 'UI'],
|
|
108
|
+
'the crafts this project offers');
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
await t('choosing a craft saves it through PATCH /me/disciplines, the route every picker uses', async () => {
|
|
112
|
+
const g = bootGate();
|
|
113
|
+
g.show({ complete: false, steps: STEPS(['signed_in', 'cli_setup']) });
|
|
114
|
+
const save = () => g.gate().querySelector('[data-save-crafts]');
|
|
115
|
+
assert.equal(save().disabled, true, 'nothing picked, nothing to save');
|
|
116
|
+
g.click(g.gate().querySelector('[data-craft="artist"]'));
|
|
117
|
+
assert.equal(g.gate().querySelector('[data-craft="artist"]').getAttribute('aria-pressed'), 'true');
|
|
118
|
+
assert.equal(save().disabled, false);
|
|
119
|
+
g.setMe({ onboarding: { setup: { complete: false, steps: STEPS(['signed_in', 'cli_setup', 'pick_disciplines']) } } });
|
|
120
|
+
g.click(save());
|
|
121
|
+
await settle(); await settle();
|
|
122
|
+
const patch = g.requests.find((r) => r.method === 'PATCH');
|
|
123
|
+
assert.equal(patch.p, '/api/bongos/me/disciplines');
|
|
124
|
+
assert.deepEqual(JSON.parse(JSON.stringify(patch.body)), { preferred_disciplines: ['artist'] });
|
|
125
|
+
assert.equal(g.gate().querySelector('.setup-gate__count').textContent, '3 of 4 done', 're-read /me and ticked');
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
await t('a craft that does not save says so and can be tried again', async () => {
|
|
129
|
+
const g = bootGate({ patchOk: false });
|
|
130
|
+
g.show({ complete: false, steps: STEPS(['signed_in', 'cli_setup']) });
|
|
131
|
+
g.click(g.gate().querySelector('[data-craft="engineer"]'));
|
|
132
|
+
g.click(g.gate().querySelector('[data-save-crafts]'));
|
|
133
|
+
await settle();
|
|
134
|
+
const err = g.gate().querySelector('[data-craft-err]');
|
|
135
|
+
assert.equal(err.hidden, false);
|
|
136
|
+
assert.match(err.textContent, /did not save/);
|
|
137
|
+
assert.equal(g.gate().querySelector('[data-save-crafts]').disabled, false);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
await t('it checks /me while it is up, and lets the builder in the moment setup is complete', async () => {
|
|
141
|
+
const g = bootGate();
|
|
142
|
+
g.show({ complete: false, steps: STEPS(['signed_in', 'cli_setup', 'pick_disciplines']) });
|
|
143
|
+
assert.equal(g.intervals.length, 1, 'it polls');
|
|
144
|
+
g.setMe({ onboarding: { setup: { complete: true, steps: STEPS(['signed_in', 'cli_setup', 'pick_disciplines', 'first_start']) } } });
|
|
145
|
+
g.intervals[0]();
|
|
146
|
+
await settle();
|
|
147
|
+
assert.equal(g.gate(), null, 'the screen is gone');
|
|
148
|
+
assert.ok(!g.htmlClasses.has('setup-gate-open'));
|
|
149
|
+
assert.equal(g.reloaded(), 1, 'the page reloads into the hall');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
await t('a complete setup draws nothing', () => {
|
|
153
|
+
const g = bootGate();
|
|
154
|
+
g.show({ complete: true, steps: STEPS(['signed_in', 'cli_setup', 'pick_disciplines', 'first_start']) });
|
|
155
|
+
assert.equal(g.gate(), null);
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
// ---- the shell: loads the screen, and nothing else is drawn over it ---------
|
|
159
|
+
|
|
160
|
+
function bootShell({ deferLoad = false } = {}) {
|
|
161
|
+
const loaded = [];
|
|
162
|
+
const pending = [];
|
|
163
|
+
const asked = [];
|
|
164
|
+
let sandbox = null;
|
|
165
|
+
const stubEl = () => ({
|
|
166
|
+
dataset: {}, style: {}, textContent: '', hidden: false, innerHTML: '',
|
|
167
|
+
classList: { add() {}, remove() {}, toggle() { return false; }, contains() { return false; } },
|
|
168
|
+
setAttribute() {}, getAttribute() { return null; }, removeAttribute() {}, addEventListener() {},
|
|
169
|
+
querySelector() { return null; }, querySelectorAll() { return []; }, appendChild() {}, focus() {},
|
|
170
|
+
});
|
|
171
|
+
const els = new Map();
|
|
172
|
+
const document = {
|
|
173
|
+
body: { dataset: { page: 'work' }, appendChild() {} },
|
|
174
|
+
documentElement: { dataset: {}, style: {}, classList: { add() {}, remove() {}, toggle() { return false; }, contains() { return false; } } },
|
|
175
|
+
getElementById(id) { if (!els.has(id)) els.set(id, stubEl()); return els.get(id); },
|
|
176
|
+
querySelectorAll() { return []; }, createElement: () => stubEl(),
|
|
177
|
+
addEventListener() {}, dispatchEvent() {}, get cookie() { return ''; }, set cookie(_v) {},
|
|
178
|
+
head: { appendChild(tag) {
|
|
179
|
+
loaded.push(tag.src);
|
|
180
|
+
const land = () => {
|
|
181
|
+
if (/setup-gate/.test(tag.src)) sandbox.window.OTBSetupGate = { show: (setup) => asked.push(['gate', setup.complete]) };
|
|
182
|
+
if (tag.onload) tag.onload();
|
|
183
|
+
};
|
|
184
|
+
if (deferLoad && /setup-gate/.test(tag.src)) pending.push(land); else land();
|
|
185
|
+
} },
|
|
186
|
+
};
|
|
187
|
+
const location = { hostname: 'h', origin: 'https://h', pathname: '/work', hash: '', assign() {}, replace() {} };
|
|
188
|
+
const window = {
|
|
189
|
+
OTB: realOTB(), __BRANDING__: {}, __MODULES__: { enabled: {} },
|
|
190
|
+
BongosClient: { createClient: () => ({ request: () => new Promise(() => {}) }) },
|
|
191
|
+
// the boot pass runs before anyone is signed in, when neither draws anything: count signed-in asks only
|
|
192
|
+
OTBWelcome: { onAccess: (access) => { if (access.authed === true) asked.push(['welcome']); return false; } },
|
|
193
|
+
OTBCraftMode: { onAccess: (access) => { if (access.authed === true) asked.push(['craft']); }, applyNav() {}, mount() {}, setPositions() {}, switchHtml: () => '' },
|
|
194
|
+
matchMedia: () => ({ matches: false, addEventListener() {} }),
|
|
195
|
+
localStorage: { getItem: () => null, setItem() {} },
|
|
196
|
+
requestAnimationFrame() {}, scrollTo() {}, location, addEventListener() {},
|
|
197
|
+
};
|
|
198
|
+
sandbox = { window, document, location, console, setTimeout, clearTimeout, queueMicrotask,
|
|
199
|
+
CustomEvent: class { constructor(tp, i) { this.type = tp; Object.assign(this, i); } } };
|
|
200
|
+
sandbox.globalThis = sandbox;
|
|
201
|
+
vm.createContext(sandbox);
|
|
202
|
+
vm.runInContext(SHELL, sandbox, { filename: 'shell.js' });
|
|
203
|
+
return { apply: (state) => window.OTBShell.applyAccess(state), land: () => pending.splice(0).forEach((f) => f()), loaded, asked };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
await t('the shell holds an unfinished builder on the setup screen, with no welcome or craft picker over it', () => {
|
|
207
|
+
const s = bootShell();
|
|
208
|
+
s.apply({ authed: true, rank: 'xenos', builder: { id: 9 }, setup: { complete: false, steps: STEPS(['signed_in']) } });
|
|
209
|
+
assert.equal(s.loaded.filter((u) => /^\/builders\/setup-gate\.js/.test(u)).length, 1, 'its file is loaded');
|
|
210
|
+
assert.deepEqual(s.asked, [['gate', false]], 'the screen, and nothing over it');
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
await t('the setup screen loads once, however often the shell re-checks access before it lands', () => {
|
|
214
|
+
const s = bootShell({ deferLoad: true });
|
|
215
|
+
const held = { authed: true, rank: 'xenos', builder: { id: 9 }, setup: { complete: false, steps: STEPS(['signed_in']) } };
|
|
216
|
+
s.apply(held);
|
|
217
|
+
s.apply(held);
|
|
218
|
+
s.apply(held);
|
|
219
|
+
assert.equal(s.loaded.filter((u) => /setup-gate\.js/.test(u)).length, 1, 'one script tag, so one screen');
|
|
220
|
+
s.land();
|
|
221
|
+
assert.deepEqual(s.asked, [['gate', false]]);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
await t('a set-up builder loads no setup file and gets the hall as before', () => {
|
|
225
|
+
const s = bootShell();
|
|
226
|
+
s.apply({ authed: true, rank: 'xenos', builder: { id: 9 }, setup: { complete: true, steps: [] } });
|
|
227
|
+
assert.equal(s.loaded.filter((u) => /setup-gate/.test(u)).length, 0);
|
|
228
|
+
assert.ok(!s.asked.some(([k]) => k === 'gate'), 'no setup screen');
|
|
229
|
+
assert.deepEqual(s.asked.slice(0, 2), [['welcome'], ['craft']], 'the daily welcome and craft picker as before');
|
|
230
|
+
const unknown = bootShell();
|
|
231
|
+
unknown.apply({ authed: true, rank: 'xenos', builder: { id: 9 }, setup: null });
|
|
232
|
+
assert.equal(unknown.loaded.filter((u) => /setup-gate/.test(u)).length, 0, 'an older server that sends no setup holds nobody');
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
await t('Home carries no setup banner: the Welcome and Getting started panels are gone', () => {
|
|
236
|
+
const widgets = read('modules/hall-ui/public/hall-widgets.js');
|
|
237
|
+
assert.doesNotMatch(widgets, /w\('welcome-scroll'|w\('onboarding-scroll'/);
|
|
238
|
+
assert.doesNotMatch(read('modules/hall-ui/public/index.html'), /welcome-reopen|Show the setup guide again/);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
console.log(`\n${passed} passed, ${failed} failed`);
|
|
242
|
+
if (failed) process.exit(1);
|
package/tests/welcome_boot.mjs
CHANGED
|
@@ -122,6 +122,7 @@ test('Home loads the welcome before the shell, and the shell asks it before the
|
|
|
122
122
|
'welcome-boot.js, then the shell trio');
|
|
123
123
|
assert.ok(at('welcome-boot.css') > 0, 'and its sheet');
|
|
124
124
|
const shell = fs.readFileSync(path.join(HALL, 'shell.js'), 'utf8');
|
|
125
|
-
|
|
126
|
-
|
|
125
|
+
// task 1005076: and neither is asked while the setup screen holds the builder (tests/setup_flow.mjs).
|
|
126
|
+
assert.match(shell, /if \(!holdForSetup\(state && state\.builder\) && !\(window\.OTBWelcome && window\.OTBWelcome\.onAccess\(access, state && state\.builder\)\) && CRAFT\) CRAFT\.onAccess\(/,
|
|
127
|
+
'the picker is asked only when setup is done and the welcome did not show');
|
|
127
128
|
});
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>Autobongos — {{worldName}}</title>
|
|
7
|
-
<meta name="description" content="The Autobongos runner: switch unattended building on or off, choose the goals it may work, and pause any builder's runner.">
|
|
8
|
-
<link rel="icon" href="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><text x='16' y='24' font-size='24' text-anchor='middle' font-family='sans-serif'>⌂</text></svg>">
|
|
9
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
|
-
<link rel="stylesheet" href="/builders/style.css?v=2026-07-02-shell1">
|
|
12
|
-
<link rel="stylesheet" href="/builders/oversight.css">
|
|
13
|
-
<link rel="stylesheet" href="/builders/gate.css">
|
|
14
|
-
</head>
|
|
15
|
-
<body data-page="gate" data-page-title="Autobongos">
|
|
16
|
-
|
|
17
|
-
<div class="app">
|
|
18
|
-
<aside class="app-sidebar" id="app-sidebar" aria-label="Primary"></aside>
|
|
19
|
-
<div class="app-scrim" id="app-scrim" hidden></div>
|
|
20
|
-
<div class="app-body">
|
|
21
|
-
<header class="app-topbar" id="app-topbar"></header>
|
|
22
|
-
<div class="app-content">
|
|
23
|
-
<!-- The page's one h1, for screen readers: no page shows its head (task
|
|
24
|
-
1004836), so its sub-line went in task 1004829. -->
|
|
25
|
-
<div class="page-head page-head--board">
|
|
26
|
-
<h1 class="page-head__h">Autobongos</h1>
|
|
27
|
-
</div>
|
|
28
|
-
|
|
29
|
-
<main id="hall-main">
|
|
30
|
-
<!-- task 1005043: the page loader, shown until any section below is revealed. -->
|
|
31
|
-
<p class="hall-loading hall-loading--page hall-loading--wait" role="status">Loading…</p>
|
|
32
|
-
<!-- task 1003469: the sealed state is a panel with a heading, not a bare
|
|
33
|
-
line on the ground. gate.js writes the real floor into the lede from
|
|
34
|
-
the pack's rank label, so the copy cannot drift from ADR 0157. -->
|
|
35
|
-
<section class="scroll" id="gate-sealed" aria-labelledby="gate-sealed-h" hidden>
|
|
36
|
-
<h2 class="scroll__h" id="gate-sealed-h">Restricted</h2>
|
|
37
|
-
<p class="scroll__lede" id="gate-sealed-body">This page opens at a higher rank.</p>
|
|
38
|
-
</section>
|
|
39
|
-
|
|
40
|
-
<section id="gate-body" hidden>
|
|
41
|
-
<!-- The Autobongos fence (task 1003908, ADR 0307 §6): what is allowed to
|
|
42
|
-
reach main unattended. This page held the held-PR list too until task
|
|
43
|
-
1004859 removed it; the URL stays /gate so bookmarks keep working.
|
|
44
|
-
Archon-only — a Metic gets the note below instead, and the routes
|
|
45
|
-
behind the panel are system:true/archon server-side regardless. -->
|
|
46
|
-
<p class="scroll__lede" id="gate-archon-only" hidden></p>
|
|
47
|
-
<section class="scroll" id="fence-panel" aria-labelledby="fence-h" hidden>
|
|
48
|
-
<h2 class="scroll__h" id="fence-h">Autobongos runner</h2>
|
|
49
|
-
<!-- One panel (task 1004509): a checklist while anything the runner
|
|
50
|
-
needs is missing, one status line with the switch once it is all
|
|
51
|
-
ticked. Drawn from /autonomy/runners + /autonomy/fence together. -->
|
|
52
|
-
<div id="fence-body"><span class="kit-skel"></span></div>
|
|
53
|
-
<!-- Every builder's runner (task 1004501): what each one picked to
|
|
54
|
-
work, whether you paused it, and whether it is checking in. -->
|
|
55
|
-
<div id="fence-builders"></div>
|
|
56
|
-
</section>
|
|
57
|
-
</section>
|
|
58
|
-
</main>
|
|
59
|
-
|
|
60
|
-
<footer class="app-foot" id="app-foot"></footer>
|
|
61
|
-
</div>
|
|
62
|
-
</div>
|
|
63
|
-
</div>
|
|
64
|
-
|
|
65
|
-
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
66
|
-
|
|
67
|
-
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
68
|
-
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
69
|
-
<script src="/builders/craft-mode.js?v=2026-11-05-sections"></script>
|
|
70
|
-
<script src="/builders/shell.js?v=2026-11-05-sections"></script>
|
|
71
|
-
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
72
|
-
<script src="/builders/palette.js" defer></script>
|
|
73
|
-
<script src="/builders/hall-kit.js"></script>
|
|
74
|
-
<script src="/builders/gate.js?v=2026-10-25-autobongos"></script>
|
|
75
|
-
</body>
|
|
76
|
-
</html>
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"_": "Autobongos — the owner's runner panel behind requireGovernmentPage, still served at /gate (task 1004859 removed the held-PR list). Measured at three widths in both modes rather than looked at once.",
|
|
3
|
-
"page": "/builders/gate",
|
|
4
|
-
"surface": "hall-ui",
|
|
5
|
-
"stub": {
|
|
6
|
-
"prefix": "/builders/"
|
|
7
|
-
},
|
|
8
|
-
"modeQuery": false,
|
|
9
|
-
"_feeds": "The kit's own stub carries no feed for this page, so against it the page renders its EMPTY and SKELETON states — which is the point: those are states the floors must hold in. Against the hall-preview harness the same states render populated. Every declared 404 below is a fixture the harness does not carry, named rather than hidden.",
|
|
10
|
-
"ignoreRequests": [
|
|
11
|
-
"/api/(gds|bongos)/me$",
|
|
12
|
-
"/api/(gds|bongos)/me/",
|
|
13
|
-
"/api/(gds|bongos)/goals",
|
|
14
|
-
"/api/(gds|bongos)/inbox/",
|
|
15
|
-
"/api/(gds|bongos)/versions",
|
|
16
|
-
"/api/(gds|bongos)/builders",
|
|
17
|
-
"/api/(gds|bongos)/access-requests",
|
|
18
|
-
"/api/(gds|bongos)/autonomy/"
|
|
19
|
-
],
|
|
20
|
-
"ignoreConsole": [
|
|
21
|
-
"/api/(gds|bongos)/",
|
|
22
|
-
"\\b401\\b",
|
|
23
|
-
"\\b403\\b",
|
|
24
|
-
"\\b404\\b",
|
|
25
|
-
"\\b501\\b",
|
|
26
|
-
"Unauthorized",
|
|
27
|
-
"Forbidden",
|
|
28
|
-
"Not Found",
|
|
29
|
-
"Not Implemented",
|
|
30
|
-
"load failed"
|
|
31
|
-
],
|
|
32
|
-
"states": {
|
|
33
|
-
"queue": {
|
|
34
|
-
"auth": true,
|
|
35
|
-
"actions": [
|
|
36
|
-
[
|
|
37
|
-
"wait",
|
|
38
|
-
700
|
|
39
|
-
]
|
|
40
|
-
],
|
|
41
|
-
"expect": {
|
|
42
|
-
"visible": [
|
|
43
|
-
"#gate-body"
|
|
44
|
-
]
|
|
45
|
-
}
|
|
46
|
-
},
|
|
47
|
-
"out": {
|
|
48
|
-
"auth": false
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
"reducedMotion": {
|
|
52
|
-
"auth": true,
|
|
53
|
-
"actions": [
|
|
54
|
-
[
|
|
55
|
-
"wait",
|
|
56
|
-
600
|
|
57
|
-
]
|
|
58
|
-
]
|
|
59
|
-
}
|
|
60
|
-
}
|
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>Sessions — {{worldName}}</title>
|
|
7
|
-
<meta name="description" content="The record of every work session — who built, what it touched, what it cost.">
|
|
8
|
-
<link rel="icon" href="data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'><text x='16' y='24' font-size='24' text-anchor='middle' font-family='sans-serif'>⌂</text></svg>">
|
|
9
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
|
-
<link rel="stylesheet" href="/builders/style.css?v=2026-06-04-sessions">
|
|
12
|
-
<!-- task 1003470: the family sheet (the top-of-page strip, the mono fact,
|
|
13
|
-
the chip that is a door), linked before the page's own. -->
|
|
14
|
-
<link rel="stylesheet" href="/builders/oversight.css">
|
|
15
|
-
<link rel="stylesheet" href="/builders/sessions.css">
|
|
16
|
-
</head>
|
|
17
|
-
<body data-page="sessions" data-page-title="Sessions">
|
|
18
|
-
|
|
19
|
-
<div class="app">
|
|
20
|
-
<aside class="app-sidebar" id="app-sidebar" aria-label="Primary"></aside>
|
|
21
|
-
<div class="app-scrim" id="app-scrim" hidden></div>
|
|
22
|
-
<div class="app-body">
|
|
23
|
-
<header class="app-topbar" id="app-topbar"></header>
|
|
24
|
-
<div class="app-content">
|
|
25
|
-
<!-- The page's one h1, for screen readers: no page shows its head (task
|
|
26
|
-
1004836), so its sub-line went in task 1004829. -->
|
|
27
|
-
<div class="page-head page-head--board">
|
|
28
|
-
<h1 class="page-head__h">Sessions</h1>
|
|
29
|
-
</div>
|
|
30
|
-
|
|
31
|
-
<main id="hall-main">
|
|
32
|
-
<!-- task 1005043: the page loader, shown until any section below is revealed. -->
|
|
33
|
-
<p class="hall-loading hall-loading--page hall-loading--wait" role="status">Loading…</p>
|
|
34
|
-
<!-- Access notice — shown by sessions.js when the viewer is below the
|
|
35
|
-
floor. The heading and the line are written from the pack's rank
|
|
36
|
-
label (ADR 0157: metic and up), so the static text never renders. -->
|
|
37
|
-
<section class="scroll" id="gate-scroll" aria-labelledby="gate-h" hidden>
|
|
38
|
-
<h2 id="gate-h" class="scroll__h">Higher ranks only</h2>
|
|
39
|
-
<p class="scroll__lede" id="gate-body">The session log opens from a higher rank.</p>
|
|
40
|
-
</section>
|
|
41
|
-
|
|
42
|
-
<!-- Totals — the shell's glass strip at the top of the page (the way
|
|
43
|
-
Watch and Gate carry theirs), not a strip inside a panel. -->
|
|
44
|
-
<section class="ov-summary" id="stats-scroll" aria-labelledby="stats-h" hidden>
|
|
45
|
-
<h2 id="stats-h" class="sr-only">Totals</h2>
|
|
46
|
-
<div class="stat-row" id="stats-body"></div>
|
|
47
|
-
</section>
|
|
48
|
-
|
|
49
|
-
<!-- Every recorded session — the corpus table. No wrapper: the shell's
|
|
50
|
-
card strip scrolls a wide ledger on a phone, and the old wrapper
|
|
51
|
-
turned the sticky header into a scrollport overlay on desktop. -->
|
|
52
|
-
<section class="scroll" id="sessions-scroll" aria-labelledby="sessions-h" hidden>
|
|
53
|
-
<h2 id="sessions-h" class="scroll__h">Recorded sessions</h2>
|
|
54
|
-
<!-- The count, and a failed load, sit here: the page head that used to carry
|
|
55
|
-
them is hidden (task 1004829). -->
|
|
56
|
-
<p class="scroll__lede" id="ledger-status" role="status" aria-live="polite">Loading sessions…</p>
|
|
57
|
-
<!-- task 1003975: the ledger's first filter. It LOOKS like the pager's
|
|
58
|
-
per-page control on purpose (one control grammar per page) but wears
|
|
59
|
-
its own class — the hall_audit guard reads `class="pager…"` as a
|
|
60
|
-
hand-rolled pager, and it is right to: borrowing that family is how a
|
|
61
|
-
second pager implementation starts. Its options are filled from the
|
|
62
|
-
server's vocabulary in sessions.js, never hardcoded here, where they
|
|
63
|
-
would drift from what the route accepts. -->
|
|
64
|
-
<div class="ledger-filters">
|
|
65
|
-
<label class="ledger-filter">
|
|
66
|
-
<span>Kind</span>
|
|
67
|
-
<select id="session-type-filter" aria-label="Filter sessions by kind"></select>
|
|
68
|
-
</label>
|
|
69
|
-
</div>
|
|
70
|
-
<table class="ledger" id="sessions-table">
|
|
71
|
-
<thead>
|
|
72
|
-
<tr>
|
|
73
|
-
<th>Session</th><th>Builder</th>
|
|
74
|
-
<th title="What the session did, derived from its own transcript: whether it wrote files, used tools, and how many prompts a person actually sent.">Kind</th>
|
|
75
|
-
<th>Tasks</th>
|
|
76
|
-
<th class="ledger__num">Earned</th><th class="ledger__num">Turns</th><th class="ledger__num">Tokens</th><th class="ledger__num">Tool errors</th>
|
|
77
|
-
<th class="ledger__num">Duration</th>
|
|
78
|
-
<th class="ledger__num" title="How the session read to this builder, scored against their own interaction profile. Advisory — it gates nothing.">Plain speech</th>
|
|
79
|
-
<th>Recorded</th>
|
|
80
|
-
</tr>
|
|
81
|
-
</thead>
|
|
82
|
-
<tbody id="sessions-body"></tbody>
|
|
83
|
-
</table>
|
|
84
|
-
<!-- The kit stamps the pager bar here (sessions.js, OTBKit.pagerBarHtml).
|
|
85
|
-
makeLedger is deliberately NOT used: it stamps table + pager into one
|
|
86
|
-
mount, and this page keeps them separate so the bar sits below the
|
|
87
|
-
ledger rather than inside whatever scrollport the table is in. -->
|
|
88
|
-
<div id="sessions-pager-mount"></div>
|
|
89
|
-
<p class="ledger__note" id="ledger-foot"></p>
|
|
90
|
-
</section>
|
|
91
|
-
|
|
92
|
-
<!-- task 1002830: the narrative. Opened from a row's Read door; the SAME page
|
|
93
|
-
and the same gate as the ledger above (ADR 0051 — no second page). The
|
|
94
|
-
server decides who may read a transcript and records an administrator's
|
|
95
|
-
cross-builder read BEFORE serving it; this panel only displays. -->
|
|
96
|
-
<section class="scroll" id="transcript-scroll" aria-labelledby="transcript-h" hidden>
|
|
97
|
-
<h2 id="transcript-h" class="scroll__h">Transcript</h2>
|
|
98
|
-
<p class="scroll__lede" id="transcript-sub"></p>
|
|
99
|
-
<p><button type="button" class="btn" id="transcript-close">Close transcript</button></p>
|
|
100
|
-
<div id="transcript-body" class="transcript"></div>
|
|
101
|
-
<p><button type="button" class="btn" id="transcript-more" hidden>Load more turns</button></p>
|
|
102
|
-
</section>
|
|
103
|
-
</main>
|
|
104
|
-
|
|
105
|
-
<footer class="app-foot" id="app-foot"></footer>
|
|
106
|
-
</div>
|
|
107
|
-
</div>
|
|
108
|
-
</div>
|
|
109
|
-
|
|
110
|
-
<div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
|
|
111
|
-
|
|
112
|
-
<!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
|
|
113
|
-
chrome exists before the page script runs. -->
|
|
114
|
-
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
115
|
-
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
116
|
-
<script src="/builders/craft-mode.js?v=2026-11-05-sections"></script>
|
|
117
|
-
<script src="/builders/shell.js?v=2026-11-05-sections"></script>
|
|
118
|
-
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
119
|
-
<script src="/builders/palette.js" defer></script>
|
|
120
|
-
<script src="/builders/task-detail.js?v=2026-07-03-taskdetail"></script>
|
|
121
|
-
<!-- The shared component kit — the pager contract this page now uses instead of
|
|
122
|
-
its own (task 1003279-sibling follow-up of 1003281). Must load BEFORE
|
|
123
|
-
sessions.js, which reads window.OTBKit at boot. -->
|
|
124
|
-
<script src="/builders/hall-kit.js"></script>
|
|
125
|
-
<script src="/builders/brand-apply.js?v=2026-06-19-r56"></script>
|
|
126
|
-
<script src="/builders/sessions.js?v=2026-10-02-transcript"></script>
|
|
127
|
-
</body>
|
|
128
|
-
</html>
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"_": "Sessions — the session ledger behind requireGovernmentPage. sessions.js pages SERVER-SIDE by offset and is the one sanctioned hand-rolled pager in the hall; this file measures it, it does not ask it to change. Written with the re-skin (task 1003307); the redesign (task 1003470) put the totals on the shell's top-of-page strip, dropped the nowrap + wrapper pair that broke the sticky header, and made every id on a row a door. Populated look: the hall-preview harness (five rows in sessions__search.json).",
|
|
3
|
-
"page": "/builders/sessions",
|
|
4
|
-
"surface": "hall-ui",
|
|
5
|
-
"stub": {
|
|
6
|
-
"prefix": "/builders/"
|
|
7
|
-
},
|
|
8
|
-
"modeQuery": false,
|
|
9
|
-
"_feeds": "The kit's own stub carries no feed for this page, so against it the page renders its EMPTY and SKELETON states — which is the point: those are states the floors must hold in. Against the hall-preview harness the same states render populated. Every declared 404 below is a fixture the harness does not carry, named rather than hidden.",
|
|
10
|
-
"ignoreRequests": [
|
|
11
|
-
"/api/(gds|bongos)/me$",
|
|
12
|
-
"/api/(gds|bongos)/me/",
|
|
13
|
-
"/api/(gds|bongos)/goals",
|
|
14
|
-
"/api/(gds|bongos)/inbox/",
|
|
15
|
-
"/api/(gds|bongos)/versions",
|
|
16
|
-
"/api/(gds|bongos)/sessions"
|
|
17
|
-
],
|
|
18
|
-
"ignoreConsole": [
|
|
19
|
-
"/api/(gds|bongos)/",
|
|
20
|
-
"\\b401\\b",
|
|
21
|
-
"\\b403\\b",
|
|
22
|
-
"\\b404\\b",
|
|
23
|
-
"\\b501\\b",
|
|
24
|
-
"Unauthorized",
|
|
25
|
-
"Forbidden",
|
|
26
|
-
"Not Found",
|
|
27
|
-
"Not Implemented",
|
|
28
|
-
"load failed"
|
|
29
|
-
],
|
|
30
|
-
"states": {
|
|
31
|
-
"ledger": {
|
|
32
|
-
"auth": true,
|
|
33
|
-
"actions": [
|
|
34
|
-
[
|
|
35
|
-
"wait",
|
|
36
|
-
700
|
|
37
|
-
]
|
|
38
|
-
],
|
|
39
|
-
"expect": {
|
|
40
|
-
"visible": [
|
|
41
|
-
"#stats-scroll",
|
|
42
|
-
"#sessions-scroll"
|
|
43
|
-
]
|
|
44
|
-
}
|
|
45
|
-
},
|
|
46
|
-
"out": {
|
|
47
|
-
"auth": false
|
|
48
|
-
}
|
|
49
|
-
},
|
|
50
|
-
"reducedMotion": {
|
|
51
|
-
"auth": true,
|
|
52
|
-
"actions": [
|
|
53
|
-
[
|
|
54
|
-
"wait",
|
|
55
|
-
600
|
|
56
|
-
]
|
|
57
|
-
]
|
|
58
|
-
}
|
|
59
|
-
}
|