@bongos/core 1.19.1012 → 1.19.1014
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 +113 -78
- package/docs/adr/0331-the-sky-is-the-ideators-home.md +6 -1
- package/docs/copy-inventory.md +120 -66
- package/docs/copy-registry.json +984 -498
- package/docs/file-map.md +1 -1
- package/docs/module-api-changelog.md +4 -0
- package/modules/hall-ui/public/atlas.html +1 -1
- package/modules/hall-ui/public/blockers.html +1 -1
- package/modules/hall-ui/public/board-room.html +1 -1
- package/modules/hall-ui/public/collab.html +1 -1
- package/modules/hall-ui/public/copy-desk.html +1 -1
- package/modules/hall-ui/public/deploy.html +1 -1
- package/modules/hall-ui/public/diagrams.html +1 -1
- package/modules/hall-ui/public/drachmae.html +1 -1
- package/modules/hall-ui/public/fleet-costs.js +80 -0
- package/modules/hall-ui/public/fleet.html +64 -0
- package/modules/hall-ui/public/fleet.js +45 -0
- package/modules/hall-ui/public/fleet.states.json +14 -0
- package/modules/hall-ui/public/gate.html +1 -1
- package/modules/hall-ui/public/goal-map.html +1 -1
- package/modules/hall-ui/public/goals.html +1 -1
- package/modules/hall-ui/public/government.html +1 -1
- package/modules/hall-ui/public/idea-objects.js +4 -1
- package/modules/hall-ui/public/idea.html +1 -1
- package/modules/hall-ui/public/ideas.html +1 -1
- package/modules/hall-ui/public/index.html +1 -1
- package/modules/hall-ui/public/modules.html +1 -1
- package/modules/hall-ui/public/people.html +1 -1
- package/modules/hall-ui/public/primer.html +1 -1
- package/modules/hall-ui/public/profile.html +1 -1
- package/modules/hall-ui/public/project-settings.html +1 -1
- package/modules/hall-ui/public/ranks.html +1 -1
- package/modules/hall-ui/public/roadmap.html +1 -1
- package/modules/hall-ui/public/roster.html +1 -1
- package/modules/hall-ui/public/sessions.html +1 -1
- package/modules/hall-ui/public/settings.html +1 -1
- package/modules/hall-ui/public/shell.js +4 -0
- package/modules/hall-ui/public/sky-composer.js +155 -0
- package/modules/hall-ui/public/sky-data.js +287 -0
- package/modules/hall-ui/public/sky-draw.js +244 -0
- package/modules/hall-ui/public/sky-interaction.js +137 -0
- package/modules/hall-ui/public/sky-layout.js +81 -0
- package/modules/hall-ui/public/sky-panel.js +141 -0
- package/modules/hall-ui/public/sky-tour.js +80 -0
- package/modules/hall-ui/public/studio.html +1 -1
- package/modules/hall-ui/public/task.html +1 -1
- package/modules/hall-ui/public/thinking.css +203 -267
- package/modules/hall-ui/public/thinking.html +119 -58
- package/modules/hall-ui/public/thinking.states.json +33 -9
- package/modules/hall-ui/public/watch.html +1 -1
- package/modules/hall-ui/public/work.html +1 -1
- package/modules/provisioning/routes/provisioning.js +4 -4
- package/modules/public-landing/public/projects.html +7 -0
- package/modules/ui-design/kit/fixtures/provisioning-instances.json +1 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/bongos/auth.js +8 -0
- package/src/bongos/routes/me.js +4 -1
- package/src/bongos/serve-internal.js +25 -6
- package/src/module-api.js +1 -1
- package/tests/auth_resolve_failure.mjs +3 -2
- package/tests/fleet_cost_view.mjs +189 -0
- package/tests/hall_audit.mjs +27 -0
- package/tests/hall_nav.mjs +3 -1
- package/tests/hall_page_gate_map.mjs +13 -5
- package/tests/hall_sky.mjs +393 -405
- package/tests/hall_thinking_room.mjs +274 -296
- package/tests/helpers.mjs +185 -217
- package/tests/ideator_large_dead_thought_proof.mjs +33 -25
- package/tests/ideator_traceable_proof.mjs +61 -39
- package/tests/nav_permission_atoms.mjs +1 -1
- package/modules/hall-ui/public/sky.css +0 -365
- package/modules/hall-ui/public/sky.html +0 -79
- package/modules/hall-ui/public/sky.js +0 -580
- package/modules/hall-ui/public/sky.states.json +0 -20
- package/modules/hall-ui/public/thinking.js +0 -418
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// tests/fleet_cost_view.mjs — Cost view v1 (task 1001949, ADR 0327): the fleet cost page
|
|
2
|
+
// for the platform owner, and the one estimate line on a cloud-host owner's project page.
|
|
3
|
+
//
|
|
4
|
+
// The rule every case below leans on: an UNPRICED cost (null) renders as unpriced and never
|
|
5
|
+
// as $0, and never moves a total. A $0 there would be a price nobody quoted.
|
|
6
|
+
import assert from 'node:assert/strict';
|
|
7
|
+
import { test } from 'node:test';
|
|
8
|
+
import http from 'node:http';
|
|
9
|
+
import vm from 'node:vm';
|
|
10
|
+
import { readFileSync } from 'node:fs';
|
|
11
|
+
import { createRequire } from 'node:module';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
|
|
15
|
+
const require = createRequire(import.meta.url);
|
|
16
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
17
|
+
const HALL = path.join(ROOT, 'modules', 'hall-ui', 'public');
|
|
18
|
+
const read = (p) => readFileSync(p, 'utf8').replace(/\r\n/g, '\n');
|
|
19
|
+
const costs = require(path.join(HALL, 'fleet-costs.js'));
|
|
20
|
+
const FIXTURE = JSON.parse(read(path.join(ROOT, 'scripts', 'hall-preview', 'fixtures', 'provisioning__fleet__cost-ledger.json')));
|
|
21
|
+
|
|
22
|
+
// ── the fleet page's builders ────────────────────────────────────────────────
|
|
23
|
+
const row = (over) => ({ id: '1', slug: 'p', owner: 'o', hosting_shape: 'cloud-host', tier: null, status: 'active', platform_monthly_usd: 0, owner_monthly_usd: null, ...over });
|
|
24
|
+
const cells = (html) => [...html.matchAll(/<td[^>]*>([\s\S]*?)<\/td>/g)].map((m) => m[1].replace(/<[^>]+>/g, '').trim());
|
|
25
|
+
const foot = (html) => html.slice(html.indexOf('<tfoot>'));
|
|
26
|
+
|
|
27
|
+
test('money: whole dollars stay whole, cents show two places, and an unpriced value has no price', () => {
|
|
28
|
+
assert.equal(costs.money(13), '$13');
|
|
29
|
+
assert.equal(costs.money(12.5), '$12.50');
|
|
30
|
+
assert.equal(costs.money(0), '$0');
|
|
31
|
+
assert.equal(costs.money(null), null);
|
|
32
|
+
assert.equal(costs.money(undefined), null);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('an unpriced row reads "not priced yet", never $0 — and a real $0 still reads $0', () => {
|
|
36
|
+
const html = costs.ledgerHtml({
|
|
37
|
+
instances: [row({ slug: 'a', platform_monthly_usd: 0, owner_monthly_usd: null })],
|
|
38
|
+
totals: { platform: { monthly_usd: 0, unpriced: 0 }, owner: { monthly_usd: 0, unpriced: 1 } },
|
|
39
|
+
});
|
|
40
|
+
const [, , , , us, owner] = cells(html.slice(0, html.indexOf('<tfoot>')));
|
|
41
|
+
assert.equal(us, '$0', 'a cloud-host costs us a real, measured $0');
|
|
42
|
+
assert.equal(owner, 'not priced yet');
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('a total sums only priced rows and names how many it left out', () => {
|
|
46
|
+
const html = costs.ledgerHtml({
|
|
47
|
+
instances: [
|
|
48
|
+
row({ slug: 'cp', hosting_shape: 'control-plane', tier: 's-1vcpu-2gb', platform_monthly_usd: 12, owner_monthly_usd: 0 }),
|
|
49
|
+
row({ slug: 'priced', tier: 'starter+basic-256mb', owner_monthly_usd: 13 }),
|
|
50
|
+
row({ slug: 'bare', owner_monthly_usd: null }),
|
|
51
|
+
],
|
|
52
|
+
totals: { platform: { monthly_usd: 12, unpriced: 0 }, owner: { monthly_usd: 13, unpriced: 1 } },
|
|
53
|
+
});
|
|
54
|
+
const [us, owner] = cells(foot(html));
|
|
55
|
+
assert.equal(us, '$12');
|
|
56
|
+
assert.equal(owner, '$13 + 1 not priced', 'the null row is counted, not summed');
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('a column with NOTHING priced has no total — the sum of no prices is not $0', () => {
|
|
60
|
+
const html = costs.ledgerHtml({
|
|
61
|
+
instances: [row({ slug: 'a' }), row({ slug: 'b' })],
|
|
62
|
+
totals: { platform: { monthly_usd: 0, unpriced: 0 }, owner: { monthly_usd: 0, unpriced: 2 } },
|
|
63
|
+
});
|
|
64
|
+
const [us, owner] = cells(foot(html));
|
|
65
|
+
assert.equal(us, '$0', 'every row prices at a real $0 for us');
|
|
66
|
+
assert.equal(owner, 'not priced yet', 'the owner total is not printed as $0');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('the harness fixture reads as live does today: our box priced, every owner bill but ours unpriced', () => {
|
|
70
|
+
const [us, owner] = cells(foot(costs.ledgerHtml(FIXTURE)));
|
|
71
|
+
assert.equal(us, '$12');
|
|
72
|
+
assert.equal(owner, '$0 + 2 not priced, 1 not visible');
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test('the total says what its cells say: a self-hosted bill is "not visible", not "not priced"', () => {
|
|
76
|
+
const byo = (slug) => row({ slug, hosting_shape: 'byo-host' });
|
|
77
|
+
const mixed = costs.ledgerHtml({
|
|
78
|
+
instances: [row({ slug: 'p', tier: 'starter+basic-256mb', owner_monthly_usd: 13 }), byo('a'), byo('b')],
|
|
79
|
+
totals: { platform: { monthly_usd: 0, unpriced: 0 }, owner: { monthly_usd: 13, unpriced: 2 } },
|
|
80
|
+
});
|
|
81
|
+
assert.equal(cells(foot(mixed))[1], '$13 + 2 not visible');
|
|
82
|
+
const allByo = costs.ledgerHtml({ instances: [byo('a'), byo('b')], totals: { platform: { monthly_usd: 0, unpriced: 0 }, owner: { monthly_usd: 0, unpriced: 2 } } });
|
|
83
|
+
assert.equal(cells(foot(allByo))[1], 'not visible to us');
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('a self-hosted owner\'s bill is not ours to see — a different absence from an unrecorded plan', () => {
|
|
87
|
+
const html = costs.ledgerHtml({ instances: [row({ hosting_shape: 'byo-host' })], totals: { platform: { monthly_usd: 0, unpriced: 0 }, owner: { monthly_usd: 0, unpriced: 1 } } });
|
|
88
|
+
assert.equal(cells(html)[5], 'not visible to us');
|
|
89
|
+
assert.equal(cells(html)[2], 'Self-hosted');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test('every row of the fixture is listed, a missing plan says so, and row text is escaped', () => {
|
|
93
|
+
const html = costs.ledgerHtml(FIXTURE);
|
|
94
|
+
for (const r of FIXTURE.instances) assert.ok(html.includes(`>${r.slug}<`), `${r.slug} is a row`);
|
|
95
|
+
assert.match(html, /none recorded/);
|
|
96
|
+
const hostile = costs.ledgerHtml({ instances: [row({ slug: '<img src=x onerror=1>', owner: '"q"' })], totals: {} });
|
|
97
|
+
assert.doesNotMatch(hostile, /<img/);
|
|
98
|
+
assert.match(hostile, /<img src=x onerror=1>/);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('no projects renders a sentence, not an empty table', () => {
|
|
102
|
+
assert.match(costs.ledgerHtml({ instances: [], totals: {} }), /No projects are hosted here yet/);
|
|
103
|
+
assert.match(costs.ledgerHtml(null), /No projects are hosted here yet/);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// ── the page: its wiring and its gate ────────────────────────────────────────
|
|
107
|
+
test('the page loads the builders before its chrome and reads the ledger route', () => {
|
|
108
|
+
const html = read(path.join(HALL, 'fleet.html'));
|
|
109
|
+
assert.ok(html.indexOf('fleet-costs.js') !== -1 && html.indexOf('fleet-costs.js') < html.indexOf('/fleet.js'), 'fleet-costs.js loads first');
|
|
110
|
+
const js = read(path.join(HALL, 'fleet.js'));
|
|
111
|
+
assert.match(js, /\/provisioning\/fleet\/cost-ledger/);
|
|
112
|
+
assert.match(js, /status === 403[\s\S]{0,200}fl-sealed/, 'a refusal shows the sealed state');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test('the page gate is the atom the ledger route checks, and the nav mirrors its floor', () => {
|
|
116
|
+
const route = read(path.join(ROOT, 'modules', 'provisioning', 'routes', 'provisioning.js'));
|
|
117
|
+
assert.match(route, /router\.get\('\/provisioning\/fleet\/cost-ledger', auth\.requireBuilder, auth\.requirePermission\('provisioning\.fleet\.manage'\)/);
|
|
118
|
+
assert.match(read(path.join(ROOT, 'src', 'bongos', 'auth.js')), /requireFleetPage: Object\.freeze\(\{ permission: 'provisioning\.fleet\.manage'/);
|
|
119
|
+
// The nav is cosmetic (the page gate is the wall); 'government' is the metic floor.
|
|
120
|
+
const { byKey } = require(path.join(ROOT, 'modules', 'government', 'catalog.js'));
|
|
121
|
+
assert.equal(byKey('provisioning.fleet.manage').floor, 'metic');
|
|
122
|
+
assert.match(read(path.join(HALL, 'shell.js')), /href: '\/fleet', gate: 'government', module: 'provisioning'/);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
// ── the owner's line on their own project page ───────────────────────────────
|
|
126
|
+
const HUB = read(path.join(ROOT, 'modules', 'public-landing', 'public', 'projects.html'));
|
|
127
|
+
function hubFn(name) {
|
|
128
|
+
const start = HUB.indexOf(`function ${name}(`);
|
|
129
|
+
assert.ok(start !== -1, `${name} is defined in projects.html`);
|
|
130
|
+
const end = HUB.indexOf('\n function ', start + 1);
|
|
131
|
+
const box = {};
|
|
132
|
+
vm.runInNewContext(`${HUB.slice(start, end)}\nbox.fn = ${name};`, { box });
|
|
133
|
+
return box.fn;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
test('the owner\'s hosting-bill line: a price when the plan is known, "not priced yet" when not', () => {
|
|
137
|
+
const hostingBill = hubFn('hostingBill');
|
|
138
|
+
assert.equal(hostingBill(null), 'not priced yet');
|
|
139
|
+
assert.equal(hostingBill(undefined), 'not priced yet');
|
|
140
|
+
assert.equal(hostingBill(13), '$13/month, estimated. Render bills you directly.');
|
|
141
|
+
assert.equal(hostingBill(12.5), '$12.50/month, estimated. Render bills you directly.');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('the line is shown only on a live cloud-host — a self-hosted owner\'s bill is theirs', () => {
|
|
145
|
+
const body = HUB.slice(HUB.indexOf('function renderManage('), HUB.indexOf("$('mMeta').innerHTML", HUB.indexOf('function renderManage(')));
|
|
146
|
+
assert.match(body, /if \(inst\.hosting_shape === 'cloud-host' && inst\.status !== 'torn_down'\) rows\.push\(\['hosting bill', hostingBill\(inst\.owner_monthly_usd\)\]\);/);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
// ── the payload the owner's page reads ───────────────────────────────────────
|
|
150
|
+
const provisioning = require(path.join(ROOT, 'modules', 'provisioning', 'provisioning.js'));
|
|
151
|
+
const api = require(path.join(ROOT, 'src', 'module-api.js'));
|
|
152
|
+
let detailRow = null;
|
|
153
|
+
provisioning.getInstanceById = async () => detailRow;
|
|
154
|
+
provisioning.getOpenIntent = async () => null;
|
|
155
|
+
provisioning.getLatestOAuthManifestForInstance = async () => null;
|
|
156
|
+
require(path.join(ROOT, 'modules', 'provisioning', 'catalog-bridge.js')).healInstanceCatalog = async () => {};
|
|
157
|
+
api.requireBuilder = (req, _res, next) => { req.builder = { id: 7, github_login: 'owner' }; next(); };
|
|
158
|
+
const express = require('express');
|
|
159
|
+
const app = express();
|
|
160
|
+
app.use((_req, res, next) => {
|
|
161
|
+
res.fail = (code, opts) => res.status(typeof opts === 'number' ? opts : (opts && opts.status) || 400).json({ error: { code } });
|
|
162
|
+
next();
|
|
163
|
+
});
|
|
164
|
+
app.use('/api/bongos', require(path.join(ROOT, 'modules', 'provisioning', 'routes', 'provisioning.js'))());
|
|
165
|
+
const getJson = (url) => new Promise((resolve, reject) => {
|
|
166
|
+
http.get(url, (res) => { let d = ''; res.on('data', (c) => { d += c; }); res.on('end', () => resolve({ status: res.statusCode, body: JSON.parse(d || 'null') })); }).on('error', reject);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test('GET /provisioning/instances/:id carries the owner\'s estimate — null until a plan is recorded', async () => {
|
|
170
|
+
const server = app.listen(0);
|
|
171
|
+
await new Promise((r) => server.once('listening', r));
|
|
172
|
+
const url = `http://127.0.0.1:${server.address().port}/api/bongos/provisioning/instances/41`;
|
|
173
|
+
const base = { id: 41, owner_builder_id: 7, slug: 'p', status: 'active', hosting_shape: 'cloud-host', description: 'd' };
|
|
174
|
+
try {
|
|
175
|
+
detailRow = { ...base, tier: null };
|
|
176
|
+
let r = await getJson(url);
|
|
177
|
+
assert.equal(r.status, 200);
|
|
178
|
+
assert.ok('owner_monthly_usd' in r.body.instance, 'the field is present');
|
|
179
|
+
assert.equal(r.body.instance.owner_monthly_usd, null, 'no plan: unpriced, not 0');
|
|
180
|
+
detailRow = { ...base, tier: 'starter+basic-256mb' };
|
|
181
|
+
r = await getJson(url);
|
|
182
|
+
assert.equal(r.body.instance.owner_monthly_usd, 13, 'the ledger\'s own price for the named plan');
|
|
183
|
+
detailRow = { ...base, owner_builder_id: 8, tier: 'starter+basic-256mb' };
|
|
184
|
+
r = await getJson(url);
|
|
185
|
+
assert.equal(r.status, 404, 'another builder\'s project answers nothing');
|
|
186
|
+
} finally {
|
|
187
|
+
server.close();
|
|
188
|
+
}
|
|
189
|
+
});
|
package/tests/hall_audit.mjs
CHANGED
|
@@ -354,9 +354,22 @@ test('the module doc still records where the 2179 density pass landed', () => {
|
|
|
354
354
|
// no shell.js, a private hardcoded palette that silently kept the WCAG-failing
|
|
355
355
|
// accent when the pack moved (idea 1000600). Task 1002868 adopted them, so the
|
|
356
356
|
// rule is now universal and exception-free.
|
|
357
|
+
// THE SKY is the one page that is not the hall's chrome, by the owner's decision
|
|
358
|
+
// (task 1004231, ADR 0331): modules/hall-ui/public/thinking.html is the
|
|
359
|
+
// prototype adopted as the design of record — a full-screen black surface with
|
|
360
|
+
// its own header, composer and legend — and its <style> is the pixel contract.
|
|
361
|
+
// It is not the fork this rule was written against: its palette is not a stale
|
|
362
|
+
// private copy of the hall's, it is a different surface's own, pinned by
|
|
363
|
+
// tests/hall_sky.mjs against the template. It still stamps the branding token
|
|
364
|
+
// and the pack's font links like every hall page (the server injects them).
|
|
365
|
+
const STANDALONE_EXEMPT = {
|
|
366
|
+
'thinking.html': 'the sky (task 1004231, ADR 0331) — the design of record is its own surface, not the hall chrome',
|
|
367
|
+
};
|
|
368
|
+
|
|
357
369
|
test('every hall page loads style.css and shell.js', () => {
|
|
358
370
|
const missing = [];
|
|
359
371
|
for (const f of pages()) {
|
|
372
|
+
if (STANDALONE_EXEMPT[f]) continue;
|
|
360
373
|
const html = read(f);
|
|
361
374
|
if (!/<link[^>]+href="[^"]*\bstyle\.css[^"]*"/.test(html)) missing.push(`${f}: no style.css link`);
|
|
362
375
|
if (!/<script[^>]+src="[^"]*\bshell\.js[^"]*"/.test(html)) missing.push(`${f}: no shell.js`);
|
|
@@ -400,6 +413,15 @@ test('no page declares a private copy of a pack color var', () => {
|
|
|
400
413
|
const LITERAL_EXEMPT = {
|
|
401
414
|
'style.css': 'the token tier — this is the ONE file whose job is to hold the literals',
|
|
402
415
|
'settings.css': 'the Discord button wears Discord\'s brand colour (#5865F2); a third-party mark is not a theme colour',
|
|
416
|
+
// The sky (task 1004231, ADR 0331) is the prototype the owner adopted as the
|
|
417
|
+
// design of record, verbatim: its sheet IS its token tier (a black void, one
|
|
418
|
+
// accent, the chart yellow, and the alpha-white ladder the canvas shares),
|
|
419
|
+
// and the two scripts that write inline colour do so with the template's own
|
|
420
|
+
// values. The atlas precedent: a page-scoped palette, pinned rather than
|
|
421
|
+
// re-tokened.
|
|
422
|
+
'thinking.css': 'the sky\'s own sheet is its token tier (task 1004231, ADR 0331 — the design of record)',
|
|
423
|
+
'sky-interaction.js': 'the sky\'s hover card writes the template\'s ring colour inline (task 1004231)',
|
|
424
|
+
'sky-panel.js': 'the sky\'s panel writes the template\'s dead-bar colour inline (task 1004231)',
|
|
403
425
|
};
|
|
404
426
|
const COLOUR_PROPS = 'color|background|background-color|border|border-color|border-top-color|border-bottom-color|fill|stroke|outline|outline-color|box-shadow|text-shadow';
|
|
405
427
|
const colourLiteralRe = () =>
|
|
@@ -465,6 +487,11 @@ test('the verdict classes and the scrim exist in the token tier, in both modes',
|
|
|
465
487
|
// exceptions are selectors that are not keyboard stops at all.
|
|
466
488
|
const OUTLINE_NONE_EXEMPT = {
|
|
467
489
|
main: 'the skip link\'s programmatic target — main takes tabindex="-1" focus so the reader lands past the nav, and ringing the whole page would be noise, not information',
|
|
490
|
+
// The sky's composer (task 1004231, ADR 0331): the line sits inside a pill
|
|
491
|
+
// form, and the indication is the FORM's ring — `.composer form:focus-within`
|
|
492
|
+
// brightens its inset hairline — which is the template's own design. A ring
|
|
493
|
+
// on the input inside the pill would draw two.
|
|
494
|
+
'.composer input': 'the sky\'s composer line — the indication is the pill form\'s focus-within ring (thinking.css), the template\'s own',
|
|
468
495
|
};
|
|
469
496
|
|
|
470
497
|
// Innermost rule blocks only, so a rule inside @media is read as itself and the
|
package/tests/hall_nav.mjs
CHANGED
|
@@ -287,7 +287,9 @@ test('sidebar groups read by audience, in order', () => {
|
|
|
287
287
|
// 'deploy' (task 1003159, ADR 0293) is the SECOND archon item, beside 'government':
|
|
288
288
|
// its gate is the `core.pin.move` atom, archon-floor and system:true. It sits with
|
|
289
289
|
// the steering surfaces because that is what it is — the owner's third release gate.
|
|
290
|
-
|
|
290
|
+
// 'fleet' (task 1001949) follows it at the metic floor of `provisioning.fleet.manage`:
|
|
291
|
+
// what each hosted project costs is an operator surface, like the rest of this group.
|
|
292
|
+
assert.deepEqual(nav[4].items.map((i) => i.id), ['watch', 'gate', 'sessions', 'project-settings', 'deploy', 'fleet', 'government']);
|
|
291
293
|
});
|
|
292
294
|
|
|
293
295
|
test('Economy lives in Docs and points at the reward-schedule page', () => {
|
|
@@ -68,12 +68,15 @@ const ORACLE = {
|
|
|
68
68
|
// having a thought, and the page reads the VIEWER's own trail (the id comes
|
|
69
69
|
// from GET /me, never from the URL), so a rank could protect nothing the
|
|
70
70
|
// session does not already.
|
|
71
|
+
// Since task 1004231 (sky part 3, ADR 0331) that page IS the sky — the
|
|
72
|
+
// prototype the owner adopted, over GET /sky + GET /sky/mine, with the room
|
|
73
|
+
// inside it as the list drawer. Both reads are any-builder and the trail is
|
|
74
|
+
// the viewer's own, so sign-in is still the whole gate.
|
|
71
75
|
'/builders/thinking': 'requireBuilderPage',
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
'/builders/sky': 'requireBuilderPage',
|
|
76
|
+
// Gone: the old ambient sky (task 1004105) was folded into /thinking by task
|
|
77
|
+
// 1004231, and /builders/sky now 301s there (serve-internal.js). A redirect is
|
|
78
|
+
// not a page, so it carries no gate; GONE_PAGES keeps the row honest.
|
|
79
|
+
'/builders/sky': null,
|
|
77
80
|
'/builders/collab': 'requireBuilderPage',
|
|
78
81
|
// task 1002710: the orientation reading rooms moved DOWN to this tier from a
|
|
79
82
|
// thetes+ gate. They are the onboarding documents, so gating them above xenos
|
|
@@ -100,6 +103,8 @@ const ORACLE = {
|
|
|
100
103
|
// its write on purpose: the preview discloses the exact core version the box serves
|
|
101
104
|
// and how far behind it is, which is a vulnerability window.
|
|
102
105
|
'/builders/deploy': 'requireCorePinPage',
|
|
106
|
+
// The fleet cost page (task 1001949), on the atom its one read checks.
|
|
107
|
+
'/builders/fleet': 'requireFleetPage',
|
|
103
108
|
|
|
104
109
|
// Metic+ via the `project.curate` atom — the exact permission GET /scouting
|
|
105
110
|
// checks, so the page can never drift wider than the API it fronts. Its
|
|
@@ -301,6 +306,9 @@ const GONE_PAGES = {
|
|
|
301
306
|
// Task 1003890 — the dev-box deprecation removed the fleet these two drove.
|
|
302
307
|
'/builders/harbor': 'task 1003890 — the dev box is gone, so its Archon view is too',
|
|
303
308
|
'/builders/pair': 'task 1003890 — the dev box is gone, so its pairing page is too',
|
|
309
|
+
// Task 1004231 — the ideator's sky replaced both the room and the old sky with
|
|
310
|
+
// one page at /thinking; /sky is a 301 to it, not a page.
|
|
311
|
+
'/builders/sky': 'task 1004231 — the sky lives at /thinking now; /sky redirects there',
|
|
304
312
|
};
|
|
305
313
|
|
|
306
314
|
test('a row that is null because the page left the hall serves no page', () => {
|