@bongos/core 1.24.44 → 1.24.46
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 +179 -144
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +4 -0
- package/clients/bongos-client/index.cjs +4 -0
- package/clients/bongos-client/index.d.ts +8 -0
- package/clients/bongos-client/index.mjs +4 -0
- package/docs/adr/0307-autobongos-a-deterministic-supervisor-and-agentic-workers.md +15 -0
- package/docs/api/openapi.json +152 -3
- package/docs/api-reference.md +4 -2
- package/docs/copy-inventory.md +1111 -1111
- package/docs/copy-registry.json +1547 -1547
- package/docs/module-api-changelog.md +4 -0
- package/docs/onboarding/diagrams/assertions.json +2 -2
- package/docs/page-inventory.json +11 -0
- package/docs/page-readings.json +823 -823
- package/migrations/core_295_runner_logins.sql +50 -0
- package/modules/government/session-scopes.json +5 -0
- package/modules/hall-ui/public/ai-studio.html +1 -1
- package/modules/hall-ui/public/ai-studio.states.json +2 -0
- package/modules/hall-ui/public/atlas.html +1 -1
- package/modules/hall-ui/public/atlas.states.json +2 -0
- package/modules/hall-ui/public/blockers.html +1 -1
- package/modules/hall-ui/public/board-room.html +1 -1
- package/modules/hall-ui/public/board-room.states.json +2 -0
- package/modules/hall-ui/public/city.html +1 -1
- package/modules/hall-ui/public/city.states.json +2 -0
- package/modules/hall-ui/public/collab.html +1 -1
- package/modules/hall-ui/public/collab.states.json +2 -0
- package/modules/hall-ui/public/commands.html +1 -1
- package/modules/hall-ui/public/commands.states.json +2 -0
- package/modules/hall-ui/public/copy-desk.html +1 -1
- package/modules/hall-ui/public/copy-desk.states.json +2 -0
- package/modules/hall-ui/public/craft-mode.js +4 -1
- package/modules/hall-ui/public/deploy.html +1 -1
- package/modules/hall-ui/public/deploy.states.json +2 -0
- package/modules/hall-ui/public/goal-map.html +1 -1
- package/modules/hall-ui/public/goal-map.states.json +2 -0
- package/modules/hall-ui/public/goals.css +1 -0
- package/modules/hall-ui/public/goals.html +1 -1
- package/modules/hall-ui/public/goals.states.json +2 -0
- package/modules/hall-ui/public/government.html +1 -1
- package/modules/hall-ui/public/government.states.json +2 -0
- package/modules/hall-ui/public/idea.html +1 -1
- package/modules/hall-ui/public/idea.states.json +2 -0
- package/modules/hall-ui/public/ideas.html +1 -1
- package/modules/hall-ui/public/ideas.states.json +2 -0
- package/modules/hall-ui/public/index-founding.states.json +2 -0
- package/modules/hall-ui/public/index-genesis-decide.states.json +2 -0
- package/modules/hall-ui/public/index-genesis-demo.states.json +2 -0
- package/modules/hall-ui/public/index-genesis.states.json +2 -0
- package/modules/hall-ui/public/index.html +1 -1
- package/modules/hall-ui/public/index.states.json +5 -0
- package/modules/hall-ui/public/market-watch-item.html +1 -1
- package/modules/hall-ui/public/market-watch-item.states.json +2 -0
- package/modules/hall-ui/public/market-watch-rules.html +1 -1
- package/modules/hall-ui/public/market-watch-rules.states.json +2 -0
- package/modules/hall-ui/public/market-watch.html +1 -1
- package/modules/hall-ui/public/market-watch.states.json +2 -0
- package/modules/hall-ui/public/modules.html +1 -1
- package/modules/hall-ui/public/modules.states.json +2 -0
- package/modules/hall-ui/public/money.html +1 -1
- package/modules/hall-ui/public/money.states.json +2 -0
- package/modules/hall-ui/public/my-agents.html +1 -1
- package/modules/hall-ui/public/my-agents.states.json +2 -0
- package/modules/hall-ui/public/notes-panel.css +1 -1
- package/modules/hall-ui/public/primer.html +1 -1
- package/modules/hall-ui/public/primer.states.json +2 -0
- package/modules/hall-ui/public/profile.html +1 -1
- package/modules/hall-ui/public/profile.states.json +2 -0
- package/modules/hall-ui/public/project-settings.html +1 -1
- package/modules/hall-ui/public/roster.html +1 -1
- package/modules/hall-ui/public/roster.states.json +2 -0
- package/modules/hall-ui/public/settings.html +1 -1
- package/modules/hall-ui/public/settings.states.json +2 -0
- package/modules/hall-ui/public/shell.js +21 -1
- package/modules/hall-ui/public/street-card.states.json +2 -0
- package/modules/hall-ui/public/street.html +1 -1
- package/modules/hall-ui/public/street.states.json +2 -0
- package/modules/hall-ui/public/studio.html +1 -1
- package/modules/hall-ui/public/studio.states.json +2 -0
- package/modules/hall-ui/public/style.css +27 -24
- package/modules/hall-ui/public/task.html +1 -1
- package/modules/hall-ui/public/task.states.json +2 -0
- package/modules/hall-ui/public/thinking.html +1 -1
- package/modules/hall-ui/public/thinking.states.json +2 -0
- package/modules/hall-ui/public/tweak-editor.html +1 -1
- package/modules/hall-ui/public/tweak-editor.states.json +2 -0
- package/modules/hall-ui/public/watch.html +1 -1
- package/modules/hall-ui/public/watch.states.json +2 -0
- package/modules/hall-ui/public/welcome-boot.js +6 -2
- package/modules/hall-ui/public/work-founding.states.json +2 -0
- package/modules/hall-ui/public/work.html +1 -1
- package/modules/hall-ui/public/work.states.json +3 -1
- package/modules/hall-ui/records/hall-navigation.md +5 -0
- package/modules/lifecycle/db-claims.js +6 -3
- package/modules/lifecycle/routes/claims.js +10 -0
- package/modules/lifecycle/routes/tasks.js +1 -1
- package/modules/lifecycle/runner-self-guard.js +19 -10
- package/modules/platform-identity/platform-identity.js +1 -1
- package/modules/platform-identity/tests/platform-identity.mjs +13 -2
- package/modules/provisioning/core-upgrade-authz.js +8 -0
- package/modules/public-landing/public/account.html +1 -0
- package/modules/public-landing/public/community.html +1 -0
- package/modules/public-landing/public/connections.html +1 -0
- package/modules/public-landing/public/contact.html +1 -0
- package/modules/public-landing/public/guild.html +1 -0
- package/modules/public-landing/public/index.html +1 -0
- package/modules/public-landing/public/invitations.html +1 -0
- package/modules/public-landing/public/privacy.html +1 -0
- package/modules/public-landing/public/projects.html +1 -0
- package/modules/public-landing/public/terms.html +1 -0
- package/modules/raise/views/gate.html +1 -0
- package/modules/raise/views/nda.html +1 -0
- package/modules/raise/views/page.html +1 -0
- package/modules/status-ui/public/index.html +1 -0
- package/modules/ui-design/kit/README.md +1 -1
- package/modules/ui-design/kit/fixtures/versions.json +1 -0
- package/modules/ui-design/kit/lib.js +48 -3
- package/modules/ui-design/kit/probe.js +1 -0
- package/modules/ui-design/kit/render.js +2 -0
- package/modules/ui-design/kit/serve.js +1 -0
- package/modules/ui-design/module.json +1 -1
- package/modules/ui-design/public/tokens.css +125 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +20 -0
- package/scripts/bongos/fitness.js +1 -1
- package/scripts/bongos/gen-hall-sitemap.js +3 -1
- package/scripts/bongos/run-unit-tests.js +3 -0
- package/src/bongos/auth.js +25 -4
- package/src/bongos/route-rank-check.js +5 -0
- package/src/bongos/routes/auth.js +48 -0
- package/src/bongos/runner-login.js +151 -0
- package/src/bongos/serve-internal.js +6 -1
- package/src/module-api.js +1 -1
- package/tests/autobongos_runner_login.mjs +296 -0
- package/tests/autobongos_runner_login_db.mjs +105 -0
- package/tests/craft_mode_nav.mjs +51 -3
- package/tests/design_tokens_shared.mjs +183 -0
- package/tests/government_session_scope.mjs +2 -1
- package/tests/hall_reading_world.mjs +5 -1
- package/tests/hall_tokens.mjs +22 -3
- package/tests/nav_permission_atoms.mjs +2 -1
- package/tests/ui_design_kit.mjs +30 -0
- package/tests/welcome_boot_browser.mjs +4 -0
|
@@ -26,11 +26,18 @@
|
|
|
26
26
|
// paths the registry has always protected, and one that does not is treated as
|
|
27
27
|
// unreadable, which refuses.
|
|
28
28
|
//
|
|
29
|
-
// WHICH
|
|
30
|
-
//
|
|
31
|
-
// runner's
|
|
32
|
-
//
|
|
33
|
-
//
|
|
29
|
+
// WHICH PUBLISH IS A RUNNER'S (task 1004547). Any one of three says so:
|
|
30
|
+
// - the claim row's `autonomous` mark, which the claims route sets from the
|
|
31
|
+
// claiming SESSION (a runner's own login, src/bongos/runner-login.js) and which
|
|
32
|
+
// no request body can set or clear. This is the reliable one: a worker that
|
|
33
|
+
// claims under a hand-picked worktree name is still recognised;
|
|
34
|
+
// - the publishing session is itself a runner's (`publisherIsRunner`), whoever
|
|
35
|
+
// made the claim;
|
|
36
|
+
// - the claim's worktree name (runner-self.js RUNNER_TREE_RE), kept for runners
|
|
37
|
+
// still running under their builder's CLI session until they are re-set-up.
|
|
38
|
+
// If the claim read FAILS, the branch name stands in (the runner's branch is its
|
|
39
|
+
// worktree name), so a database hiccup does not switch the wall off for a runner
|
|
40
|
+
// while an ordinary publish still never depends on the read.
|
|
34
41
|
//
|
|
35
42
|
// FAILS CLOSED on the files: a read that throws, a list at GitHub's 300-file cap,
|
|
36
43
|
// or a matcher that fails its check all refuse. An unattended change that cannot
|
|
@@ -49,7 +56,7 @@ const runnerSelf = require('./runner-self');
|
|
|
49
56
|
// resolved it. Both names are read because the server may store a disambiguated
|
|
50
57
|
// worktree_name beside the one the runner asked for.
|
|
51
58
|
const CLAIM_SQL = `
|
|
52
|
-
SELECT c.worktree_name, c.requested_worktree_name
|
|
59
|
+
SELECT c.worktree_name, c.requested_worktree_name, c.autonomous
|
|
53
60
|
FROM claims c WHERE c.task_id = $1
|
|
54
61
|
ORDER BY c.claimed_at DESC, c.id DESC LIMIT 1`;
|
|
55
62
|
|
|
@@ -66,9 +73,11 @@ async function readLatestClaim(taskId, exec = api.pool) {
|
|
|
66
73
|
}
|
|
67
74
|
}
|
|
68
75
|
|
|
69
|
-
function isRunnerPublish(claim, branch) {
|
|
76
|
+
function isRunnerPublish(claim, branch, publisherIsRunner = false) {
|
|
77
|
+
if (publisherIsRunner === true) return true;
|
|
70
78
|
if (claim === CLAIM_UNREADABLE) return runnerSelf.isRunnerTree(branch);
|
|
71
|
-
return !!claim && (
|
|
79
|
+
return !!claim && (claim.autonomous === true
|
|
80
|
+
|| runnerSelf.isRunnerTree(claim.worktree_name) || runnerSelf.isRunnerTree(claim.requested_worktree_name));
|
|
72
81
|
}
|
|
73
82
|
|
|
74
83
|
// Paths the registry has protected since ADR 0043; a matcher that misses either
|
|
@@ -86,9 +95,9 @@ const unreadable = () => ({ code: 'self_modification_unreadable', files: [], pro
|
|
|
86
95
|
// The route's one call. Returns null (publish as usual) or the refusal
|
|
87
96
|
// { code, files, protected }. `deps` is the test seam, and the route passes a
|
|
88
97
|
// shared getPullRequestFiles so this and the auto-pickup check read GitHub once.
|
|
89
|
-
async function runnerSelfEditRefusal({ taskId, prNumber, branch = null }, deps = {}) {
|
|
98
|
+
async function runnerSelfEditRefusal({ taskId, prNumber, branch = null, publisherIsRunner = false }, deps = {}) {
|
|
90
99
|
const claim = await (deps.readLatestClaim || readLatestClaim)(taskId);
|
|
91
|
-
if (!isRunnerPublish(claim, branch)) return null;
|
|
100
|
+
if (!isRunnerPublish(claim, branch, publisherIsRunner)) return null;
|
|
92
101
|
const match = verifiedMatcher(deps.matchProtected || ((files) => api.permissionPathCheck.matchProtected(files)));
|
|
93
102
|
if (!match) return unreadable();
|
|
94
103
|
let rows;
|
|
@@ -96,7 +96,7 @@ function clientSecretValid(client, presentedSecret) {
|
|
|
96
96
|
// FEDERATION_SOURCES mirrors modules/government/session-scopes.json — a module may
|
|
97
97
|
// not read another module's files, and the doorway exports no lookup for it — and
|
|
98
98
|
// tests/platform-identity.mjs pins the two lists equal, so a source declared or
|
|
99
|
-
// retired there without the same edit here fails the build instead of drifting.
|
|
99
|
+
// retired there without the same edit here fails the build instead of drifting ('runner' is left out: task 1004547).
|
|
100
100
|
const FEDERATION_SOURCES = Object.freeze(['cli', 'web', null]);
|
|
101
101
|
function sessionUsableForFederation(session) {
|
|
102
102
|
if (!session) return { ok: false, reason: 'no_session' };
|
|
@@ -130,12 +130,23 @@ t('a session of an undeclared source (a leftover dev-box token) may NOT federate
|
|
|
130
130
|
}
|
|
131
131
|
});
|
|
132
132
|
|
|
133
|
-
|
|
133
|
+
// Sources the registry declares that must NOT federate. An Autobongos runner's own
|
|
134
|
+
// login (task 1004547) is a machine working for its builder on ONE project; it has no
|
|
135
|
+
// business signing that person in to other projects through the hub.
|
|
136
|
+
const NEVER_FEDERATES = ['runner'];
|
|
137
|
+
|
|
138
|
+
t('FEDERATION_SOURCES is exactly the kernel session-source registry, less the machine logins', () => {
|
|
134
139
|
// platform-identity may not read the government module, so it mirrors the list;
|
|
135
140
|
// this pin is what stops the copy drifting from modules/government/session-scopes.json.
|
|
136
141
|
const reg = JSON.parse(readFileSync(new URL('../../government/session-scopes.json', import.meta.url), 'utf8'));
|
|
137
142
|
const declared = reg.scopes.map((r) => (r.source === undefined ? null : r.source));
|
|
138
|
-
assert.
|
|
143
|
+
for (const s of NEVER_FEDERATES) assert.ok(declared.includes(s), `${s} is still declared, so the exclusion is still needed`);
|
|
144
|
+
assert.deepEqual([...P.FEDERATION_SOURCES].sort(), declared.filter((s) => !NEVER_FEDERATES.includes(s)).sort());
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
t('an Autobongos runner login may NOT federate', () => {
|
|
148
|
+
const r = P.sessionUsableForFederation({ github_id: 1, status: 'active', source: 'runner' });
|
|
149
|
+
assert.deepEqual(r, { ok: false, reason: 'session_scope' });
|
|
139
150
|
});
|
|
140
151
|
|
|
141
152
|
t('a service principal (system_role set) may NOT federate', () => {
|
|
@@ -54,6 +54,14 @@ function ownsUpgradableProject(inst, builder) {
|
|
|
54
54
|
function requireOwnProjectOrPin({ pinGate, provisioning, pool, log }) {
|
|
55
55
|
return async function ownProjectOrPinGate(req, res, next) {
|
|
56
56
|
const raw = req.params ? req.params.id : undefined;
|
|
57
|
+
// An Autobongos runner's own login never takes the owner door (task 1004547):
|
|
58
|
+
// an unattended machine does not move a live version, even its owner's. It falls
|
|
59
|
+
// to the permission gate, which refuses it, because a runner session holds no
|
|
60
|
+
// system permission (src/bongos/auth.js resolveHeldPermissions).
|
|
61
|
+
if (req.builder && req.builder.runner) {
|
|
62
|
+
req.coreUpgradeAuthority = 'pin';
|
|
63
|
+
return pinGate(req, res, next);
|
|
64
|
+
}
|
|
57
65
|
if (typeof raw === 'string' && ID_RE.test(raw)) {
|
|
58
66
|
try {
|
|
59
67
|
const inst = await provisioning.getInstanceById(pool, Number(raw));
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
20
20
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
21
21
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400&family=Manrope:wght@300;400;500;600&display=swap">
|
|
22
|
+
<link rel="stylesheet" href="/modules/ui-design/public/tokens.css">
|
|
22
23
|
<link rel="stylesheet" href="/assets/cosmos.css">
|
|
23
24
|
<!-- Dark only, like every apex page (ADR 0204): data-mode="dark" is fixed on <html>. -->
|
|
24
25
|
<style>
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
37
37
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
38
38
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400&family=Manrope:wght@300;400;500;600&display=swap">
|
|
39
|
+
<link rel="stylesheet" href="/modules/ui-design/public/tokens.css">
|
|
39
40
|
<link rel="stylesheet" href="/assets/cosmos.css">
|
|
40
41
|
<!-- Dark only, like every apex page (ADR 0204): data-mode="dark" is fixed on <html>. -->
|
|
41
42
|
<style>
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
23
23
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
24
24
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400&family=Manrope:wght@300;400;500;600&display=swap">
|
|
25
|
+
<link rel="stylesheet" href="/modules/ui-design/public/tokens.css">
|
|
25
26
|
<link rel="stylesheet" href="/assets/cosmos.css">
|
|
26
27
|
<!-- Dark only, like every apex page (ADR 0204): data-mode="dark" is fixed on <html>. -->
|
|
27
28
|
<style>
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
17
17
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
18
18
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400&family=Manrope:wght@300;400;500;600&display=swap">
|
|
19
|
+
<link rel="stylesheet" href="/modules/ui-design/public/tokens.css">
|
|
19
20
|
<link rel="stylesheet" href="/assets/cosmos.css">
|
|
20
21
|
<!-- Dark only, like every apex page (task 1003336, ADR 0204): data-mode="dark" is fixed on <html>. -->
|
|
21
22
|
<style>
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
30
30
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
31
31
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400&family=Manrope:wght@300;400;500;600&display=swap">
|
|
32
|
+
<link rel="stylesheet" href="/modules/ui-design/public/tokens.css">
|
|
32
33
|
<link rel="stylesheet" href="/assets/cosmos.css">
|
|
33
34
|
<!-- Dark only, like every apex page (ADR 0204): data-mode="dark" is fixed on <html>. -->
|
|
34
35
|
<style>
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
<!-- The world's token layer. Root-absolute so the server's ?v= content stamp
|
|
42
42
|
applies (a relative href is never stamped, and a four-hour browser cache
|
|
43
43
|
then styles new markup with an old sheet). -->
|
|
44
|
+
<link rel="stylesheet" href="/modules/ui-design/public/tokens.css">
|
|
44
45
|
<link rel="stylesheet" href="/assets/world.css">
|
|
45
46
|
<style>
|
|
46
47
|
/* ── PAGE-LOCAL SHEET ────────────────────────────────────────────────────
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
24
24
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
25
25
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400&family=Manrope:wght@300;400;500;600&display=swap">
|
|
26
|
+
<link rel="stylesheet" href="/modules/ui-design/public/tokens.css">
|
|
26
27
|
<link rel="stylesheet" href="/assets/cosmos.css">
|
|
27
28
|
<!-- Dark only, like every apex page (ADR 0204): data-mode="dark" is fixed on <html>. -->
|
|
28
29
|
<style>
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
15
15
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
16
16
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400&family=Manrope:wght@300;400;500;600&display=swap">
|
|
17
|
+
<link rel="stylesheet" href="/modules/ui-design/public/tokens.css">
|
|
17
18
|
<link rel="stylesheet" href="/assets/cosmos.css">
|
|
18
19
|
<!-- Dark only, like every apex page (task 1003336, ADR 0204): data-mode="dark" is fixed on <html>. -->
|
|
19
20
|
<style>
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
space sky both modes ride on, light/dark governing the cards. The server
|
|
20
20
|
re-injects the branding :root AFTER stylesheets, so the pack stays
|
|
21
21
|
authoritative. Design contract: docs/design/projects-surfaces-redesign-direction.md -->
|
|
22
|
+
<link rel="stylesheet" href="/modules/ui-design/public/tokens.css">
|
|
22
23
|
<link rel="stylesheet" href="/assets/cosmos.css">
|
|
23
24
|
<!-- The apex is dark only (task 1003336, ADR 0204): data-mode="dark" is fixed on
|
|
24
25
|
<html> above — no pre-paint script, no stored choice, no OS preference. Light
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
15
15
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
16
16
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400&family=Manrope:wght@300;400;500;600&display=swap">
|
|
17
|
+
<link rel="stylesheet" href="/modules/ui-design/public/tokens.css">
|
|
17
18
|
<link rel="stylesheet" href="/assets/cosmos.css">
|
|
18
19
|
<!-- Dark only, like every apex page (task 1003336, ADR 0204): data-mode="dark" is fixed on <html>. -->
|
|
19
20
|
<style>
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
11
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400&family=Manrope:wght@300;400;500;600&display=swap">
|
|
12
12
|
<script src="{{base}}/assets/mode.js"></script>
|
|
13
|
+
<link rel="stylesheet" href="/modules/ui-design/public/tokens.css">
|
|
13
14
|
<link rel="stylesheet" href="{{base}}/assets/raise.css">
|
|
14
15
|
</head>
|
|
15
16
|
<body class="gate-body">
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
11
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400&family=Manrope:wght@300;400;500;600&display=swap">
|
|
12
12
|
<script src="{{base}}/assets/mode.js"></script>
|
|
13
|
+
<link rel="stylesheet" href="/modules/ui-design/public/tokens.css">
|
|
13
14
|
<link rel="stylesheet" href="{{base}}/assets/raise.css">
|
|
14
15
|
</head>
|
|
15
16
|
<body>
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
11
|
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400&family=Manrope:wght@300;400;500;600&display=swap">
|
|
12
12
|
<script src="{{base}}/assets/mode.js"></script>
|
|
13
|
+
<link rel="stylesheet" href="/modules/ui-design/public/tokens.css">
|
|
13
14
|
<link rel="stylesheet" href="{{base}}/assets/raise.css?v={{assetVersion}}">
|
|
14
15
|
<script src="{{base}}/assets/raise.js?v={{assetVersion}}" defer></script>
|
|
15
16
|
{{editorAssets}}
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
10
10
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
11
11
|
<link href="https://fonts.googleapis.com/css2?family=Cinzel:wght@500;700&family=EB+Garamond:ital,wght@0,400;0,500;0,600;1,400;1,500&display=swap" rel="stylesheet">
|
|
12
|
+
<link rel="stylesheet" href="/modules/ui-design/public/tokens.css">
|
|
12
13
|
<link rel="stylesheet" href="/style.css?v=2026-06-12-wip3">
|
|
13
14
|
</head>
|
|
14
15
|
<body>
|
|
@@ -14,7 +14,7 @@ A **look** is a branding pack from the module's style library (`modules/ui-desig
|
|
|
14
14
|
|
|
15
15
|
Two of the floors are about fit ([task 1004203](https://cloudbongos.com/builders#/task/1004203), [ADR 0329](../../../docs/adr/0329-the-rendered-fit-check-is-a-kit-floor-and-an-advisory-ship-check-row.md)): `text-fit` (text past its box, SVG text past its card) and `sibling-overlap` (text-bearing siblings on top of each other). `scripts/bongos/render-check.js` runs just those two over the pages a branch changed, and `/ship-check` shows them as an advisory row.
|
|
16
16
|
|
|
17
|
-
A surface describes itself in two JSON files beside it — `<page>.states.json` and `<page>.probes.json` — in the vocabularies `lib.js` defines and validates. The full loop, the file shapes and the failure table: [`docs/recipes/ui-look-before-you-ship.md`](../../../docs/recipes/ui-look-before-you-ship.md). Exit codes everywhere: 0 clean · 1 problems · 2 a broken file or usage · 3 no browser.
|
|
17
|
+
A surface describes itself in two JSON files beside it — `<page>.states.json` and `<page>.probes.json` — in the vocabularies `lib.js` defines and validates. A page served at the surface root (the hall's Home, `/builders/`) reads `index.states.json`. A file (or one state) may carry `"storage": { "session": {…}, "local": {…} }`, written into the browser before the page's own scripts run, so a first-visit question renders as a returning visitor sees it: every hall file seeds `hall-craft-asked` so the mode picker stays out of the shots ([task 1005080](https://cloudbongos.com/builders#/task/1005080)). A hall state that needs real data (a goal's detail, the Settings sections) passes against the hall-preview harness with `--base-in`/`--base-out`, one render at a time. The full loop, the file shapes and the failure table: [`docs/recipes/ui-look-before-you-ship.md`](../../../docs/recipes/ui-look-before-you-ship.md). Exit codes everywhere: 0 clean · 1 problems · 2 a broken file or usage · 3 no browser.
|
|
18
18
|
|
|
19
19
|
## The anti-pattern detector (`tells.js`, and the rendered twins in `lib.js`)
|
|
20
20
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"versions":[{"id":"BONGOS-V1","name":"Cloud Bongos — platform (V1)","status":"shipped","scope_doc_path":null,"started_at":"2026-06-01T00:00:00.000Z","shipped_at":"2026-09-01T00:00:00.000Z","done_when":"The platform builds itself."},{"id":"BONGOS-V2","name":"The ten working areas","status":"building","scope_doc_path":null,"started_at":"2026-09-01T00:00:00.000Z","shipped_at":null,"done_when":"Every working area has a goal."}],"page":{"limit":50,"offset":0,"total":2}}
|
|
@@ -79,7 +79,16 @@ function resolveSurfaceDir(surface, root = REPO_ROOT) {
|
|
|
79
79
|
return null;
|
|
80
80
|
}
|
|
81
81
|
// The states/probes file for a page lives BESIDE the surface: <dir>/<page>.states.json.
|
|
82
|
-
function sidecarPath(surfaceDir, urlPath, kind) {
|
|
82
|
+
function sidecarPath(surfaceDir, urlPath, kind) {
|
|
83
|
+
const named = path.join(surfaceDir, `${pageName(urlPath)}.${kind}.json`);
|
|
84
|
+
// A page served at the surface ROOT ("/builders/") has no last segment of its own — the
|
|
85
|
+
// segment is the surface's prefix — so its sidecar is index.<kind>.json (task 1005080).
|
|
86
|
+
if (/\/$/.test(String(urlPath || '').split('?')[0]) && !fs.existsSync(named)) {
|
|
87
|
+
const index = path.join(surfaceDir, `index.${kind}.json`);
|
|
88
|
+
if (fs.existsSync(index)) return index;
|
|
89
|
+
}
|
|
90
|
+
return named;
|
|
91
|
+
}
|
|
83
92
|
function readJson(file) { return JSON.parse(fs.readFileSync(file, 'utf8')); }
|
|
84
93
|
|
|
85
94
|
// ── the action vocabulary (states + probes) ───────────────────────────────────
|
|
@@ -332,6 +341,37 @@ function reducedMotionProblems({ running = [], frames = 0 } = {}) {
|
|
|
332
341
|
// floor and not proof, and a reviewer verifies each tell in context (task 1003329, ADR 0223).
|
|
333
342
|
function craftTells(facts) { return (facts && facts.tells ? facts.tells : []).map((t) => `tell: ${t}`); }
|
|
334
343
|
|
|
344
|
+
// ── storage seeded before a page's own scripts run (task 1005080) ─────────────
|
|
345
|
+
// { "storage"?: { "session"?: { key: "value" }, "local"?: { key: "value" } } } on a states or
|
|
346
|
+
// probes file — and the same object on one state — is written into the browser's storage by
|
|
347
|
+
// an init script, so a page that asks a one-time question on first visit (the hall's mode
|
|
348
|
+
// picker) renders as a returning visitor sees it. A state that MEANS the first visit leaves
|
|
349
|
+
// it out. A state's own storage is merged over the file's.
|
|
350
|
+
function validateStorage(st, at) {
|
|
351
|
+
if (st === undefined) return [];
|
|
352
|
+
if (!st || typeof st !== 'object' || Array.isArray(st)) return [`${at}: an object { session?, local? }`];
|
|
353
|
+
const errors = [];
|
|
354
|
+
for (const k of Object.keys(st)) {
|
|
355
|
+
if (k !== 'session' && k !== 'local') { errors.push(`${at}.${k}: unknown store (session | local)`); continue; }
|
|
356
|
+
const m = st[k];
|
|
357
|
+
if (!m || typeof m !== 'object' || Array.isArray(m) || !Object.values(m).every((v) => typeof v === 'string')) errors.push(`${at}.${k}: an object of string → string`);
|
|
358
|
+
}
|
|
359
|
+
return errors;
|
|
360
|
+
}
|
|
361
|
+
function mergeStorage(...layers) {
|
|
362
|
+
const out = { session: {}, local: {} };
|
|
363
|
+
for (const s of layers) if (s) for (const k of ['session', 'local']) Object.assign(out[k], s[k] || {});
|
|
364
|
+
return out;
|
|
365
|
+
}
|
|
366
|
+
async function seedStorage(ctx, ...layers) {
|
|
367
|
+
const st = mergeStorage(...layers);
|
|
368
|
+
if (!Object.keys(st.session).length && !Object.keys(st.local).length) return;
|
|
369
|
+
await ctx.addInitScript((s) => {
|
|
370
|
+
try { for (const [k, v] of Object.entries(s.session)) window.sessionStorage.setItem(k, v); } catch (_) { /* storage refused */ }
|
|
371
|
+
try { for (const [k, v] of Object.entries(s.local)) window.localStorage.setItem(k, v); } catch (_) { /* storage refused */ }
|
|
372
|
+
}, st);
|
|
373
|
+
}
|
|
374
|
+
|
|
335
375
|
// ── the states file ───────────────────────────────────────────────────────────
|
|
336
376
|
// { "page": "/projects", "surface"?: "public-landing" (a module key), "stub"?: { prefix?, child? },
|
|
337
377
|
// "modes"?: ["dark" | "light", …] (the modes the page renders; default both — a page that pins
|
|
@@ -339,7 +379,8 @@ function craftTells(facts) { return (facts && facts.tells ? facts.tells : []).ma
|
|
|
339
379
|
// stub.fixtures?: { "<fixture name>": "<other fixture file>" } (serve another file under a name — an empty list, a
|
|
340
380
|
// failure shape — for the states in THIS file; task 1003345),
|
|
341
381
|
// "ignoreControls"?: [selectors], "states": { name: { url?, auth?, actions?, expect? } },
|
|
342
|
-
// "reducedMotion"?: { auth?, url?, actions? }
|
|
382
|
+
// "reducedMotion"?: { auth?, url?, actions? },
|
|
383
|
+
// "storage"?: { session?, local? } (seeded before load; a state may carry its own — see seedStorage) }
|
|
343
384
|
function validateStatesFile(json) {
|
|
344
385
|
if (!json || typeof json !== 'object' || Array.isArray(json)) return { ok: false, errors: ['a states file is a JSON object'] };
|
|
345
386
|
const errors = [];
|
|
@@ -349,6 +390,7 @@ function validateStatesFile(json) {
|
|
|
349
390
|
if (json.modeQuery !== undefined && json.modeQuery !== false && typeof json.modeQuery !== 'string') errors.push('modeQuery: the query param the page reads for its colour mode (default "mode"), or false');
|
|
350
391
|
if (json.modes !== undefined && !(Array.isArray(json.modes) && json.modes.length && json.modes.every((m) => MODES.includes(m)))) errors.push('modes: a non-empty array of "dark" | "light" — the modes the page renders (default both); a page that pins one mode declares it here');
|
|
351
392
|
for (const k of ['ignoreRequests', 'ignoreConsole', 'ignoreControls']) if (json[k] !== undefined && !(Array.isArray(json[k]) && json[k].every((s) => typeof s === 'string'))) errors.push(`${k}: an array of strings`);
|
|
393
|
+
errors.push(...validateStorage(json.storage, 'storage'));
|
|
352
394
|
const states = json.states;
|
|
353
395
|
if (!states || typeof states !== 'object' || Array.isArray(states) || !Object.keys(states).length) errors.push('states: required — a non-empty object of name → { url?, auth?, actions?, expect? }');
|
|
354
396
|
else {
|
|
@@ -359,6 +401,7 @@ function validateStatesFile(json) {
|
|
|
359
401
|
if (s.auth !== undefined && typeof s.auth !== 'boolean') errors.push(`states.${name}.auth: true (signed-in stub) or false`);
|
|
360
402
|
errors.push(...validateActions(s.actions, `states.${name}.actions`));
|
|
361
403
|
errors.push(...validateExpect(s.expect, `states.${name}.expect`));
|
|
404
|
+
errors.push(...validateStorage(s.storage, `states.${name}.storage`));
|
|
362
405
|
}
|
|
363
406
|
}
|
|
364
407
|
if (json.reducedMotion !== undefined) {
|
|
@@ -386,6 +429,7 @@ function validateProbesFile(json) {
|
|
|
386
429
|
const errors = [];
|
|
387
430
|
if (typeof json.page !== 'string' || !json.page.startsWith('/')) errors.push('page: required — the root-relative path the probes open');
|
|
388
431
|
errors.push(...validateStub(json.stub, 'stub'));
|
|
432
|
+
errors.push(...validateStorage(json.storage, 'storage'));
|
|
389
433
|
if (!Array.isArray(json.probes) || !json.probes.length) errors.push('probes: required — a non-empty array');
|
|
390
434
|
else json.probes.forEach((pr, i) => {
|
|
391
435
|
const at = `probes[${i}]`;
|
|
@@ -396,6 +440,7 @@ function validateProbesFile(json) {
|
|
|
396
440
|
if (pr.url !== undefined && (typeof pr.url !== 'string' || !pr.url.startsWith('/'))) errors.push(`${at}.url: a root-relative path`);
|
|
397
441
|
if (pr.continue !== undefined && typeof pr.continue !== 'boolean') errors.push(`${at}.continue: boolean (reuse the previous probe's page)`);
|
|
398
442
|
if (pr.continue && i === 0) errors.push(`${at}.continue: the first probe has nothing to continue`);
|
|
443
|
+
errors.push(...validateStorage(pr.storage, `${at}.storage`));
|
|
399
444
|
errors.push(...validateActions(pr.steps, `${at}.steps`));
|
|
400
445
|
if (!pr.expect || typeof pr.expect !== 'object' || !Object.keys(pr.expect).length) errors.push(`${at}.expect: required — a probe with nothing to expect proves nothing`);
|
|
401
446
|
else errors.push(...validateExpect(pr.expect, `${at}.expect`));
|
|
@@ -527,7 +572,7 @@ module.exports = {
|
|
|
527
572
|
REPO_ROOT, KIT_DIR, STYLES_DIR, EXIT, FLOORS, SIZES, DEFAULT_SIZES, MODES, CHANNELS, SETTLE_MS, ACTIONS,
|
|
528
573
|
argOf, hasFlag, listArg, pageName, normalizePageArg, resolveSurfaceDir, sidecarPath, readJson,
|
|
529
574
|
runActions, factsInPage, expectProblems, auditFloors, reducedMotionProblems, craftTells,
|
|
530
|
-
validateStatesFile, validateProbesFile, NoBrowserError, launchBrowser, attachCollectors, withMode,
|
|
575
|
+
validateStatesFile, validateProbesFile, mergeStorage, seedStorage, NoBrowserError, launchBrowser, attachCollectors, withMode,
|
|
531
576
|
freePort, startStub, parseColor, composite, luminance, contrastRatio, instancePack, packColors,
|
|
532
577
|
listStyles, resolveStyle, styleBrandingFile,
|
|
533
578
|
};
|
|
@@ -72,6 +72,7 @@ async function main() {
|
|
|
72
72
|
if (ctx) await ctx.close();
|
|
73
73
|
const [W, H] = lib.SIZES[pr.width || 1440];
|
|
74
74
|
ctx = await browser.newContext({ viewport: { width: W, height: H }, deviceScaleFactor: 1 });
|
|
75
|
+
await lib.seedStorage(ctx, json.storage, pr.storage);
|
|
75
76
|
page = await ctx.newPage();
|
|
76
77
|
errors = [];
|
|
77
78
|
page.on('pageerror', (e) => errors.push(String(e.message).split('\n')[0]));
|
|
@@ -97,6 +97,7 @@ async function main() {
|
|
|
97
97
|
const st = json.states[name];
|
|
98
98
|
const [W, H] = lib.SIZES[w];
|
|
99
99
|
const ctx = await browser.newContext({ viewport: { width: W, height: H }, deviceScaleFactor: 1, colorScheme: mode });
|
|
100
|
+
await lib.seedStorage(ctx, json.storage, st.storage);
|
|
100
101
|
const page = await ctx.newPage();
|
|
101
102
|
const logs = lib.attachCollectors(page, collectorOpts);
|
|
102
103
|
const base = st.auth ? baseIn : baseOut;
|
|
@@ -121,6 +122,7 @@ async function main() {
|
|
|
121
122
|
const rm = json.reducedMotion || {};
|
|
122
123
|
for (const mode of lib.hasFlag(args, '--no-reduced-motion') ? [] : modes) {
|
|
123
124
|
const ctx = await browser.newContext({ viewport: { width: 1440, height: 900 }, colorScheme: mode, reducedMotion: 'reduce' });
|
|
125
|
+
await lib.seedStorage(ctx, json.storage, rm.storage);
|
|
124
126
|
const page = await ctx.newPage();
|
|
125
127
|
await page.goto((rm.auth === false ? baseOut : baseIn) + lib.withMode(rm.url || json.page, mode, json.modeQuery), { waitUntil: 'load' });
|
|
126
128
|
await page.waitForTimeout(lib.SETTLE_MS);
|
|
@@ -106,6 +106,7 @@ const FEEDS = {
|
|
|
106
106
|
'/public/cost-summary': 'cost-summary',
|
|
107
107
|
'/public/recent-shipped': 'recent-shipped',
|
|
108
108
|
'/public/leaderboard': 'leaderboard',
|
|
109
|
+
'/public/versions': 'versions', // the roadmap and goals pages' version dates (task 1005080)
|
|
109
110
|
'/projects/featured': 'featured',
|
|
110
111
|
};
|
|
111
112
|
// /founding/builders (task 1004424) answers a never-founded project's empty list by default,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"key": "ui-design",
|
|
3
3
|
"title": "UI design (the design contract, its adapters and sync, the /design playbook, the kit, the styles)",
|
|
4
|
-
"description": "The platform capability that lets any builder on any instance create and design good-looking, functional UIs in line with that instance's world (scoped with the owner 2026-08-28; task 1003324, ADR 0197). It ships METHOD and never a world: the neutral design contract of ADR 0081 with its tool adapters (Claude Design, Figma), their sync CLIs and the validator; the /design playbook for ui-discipline work; the look-before-you-ship kit (kit/) and the style library (styles/). The thirteen design-style skills that lived in skills/ are the opt-in design-styles module since task 1004470; /design reaches them when that module is on. The instance's world — its branding pack, config/design-tokens.*, config/design-sources.json and its DESIGN.md — stays in config/ and at the repo root as the host boundary this module reads and never owns (ADR 0062 §7). Cloud Bongos's own front door is this module's first customer, not its owner. default-on because every instance has a face. No routes, no migrations, no port:
|
|
4
|
+
"description": "The platform capability that lets any builder on any instance create and design good-looking, functional UIs in line with that instance's world (scoped with the owner 2026-08-28; task 1003324, ADR 0197). It ships METHOD and never a world: the neutral design contract of ADR 0081 with its tool adapters (Claude Design, Figma), their sync CLIs and the validator; the /design playbook for ui-discipline work; the look-before-you-ship kit (kit/) and the style library (styles/). The thirteen design-style skills that lived in skills/ are the opt-in design-styles module since task 1004470; /design reaches them when that module is on. The instance's world — its branding pack, config/design-tokens.*, config/design-sources.json and its DESIGN.md — stays in config/ and at the repo root as the host boundary this module reads and never owns (ADR 0062 §7). Cloud Bongos's own front door is this module's first customer, not its owner. default-on because every instance has a face. No routes, no migrations, no port. Since task 1005081 it also ships public/tokens.css, the shared token sheet (the scales: type, spacing, corners, shadows, motion, stacking) every surface loads at /modules/ui-design/public/tokens.css, so disabling it now also withdraws that sheet and the hall's scale aliases lose their values.",
|
|
5
5
|
"version": "1.0.0",
|
|
6
6
|
"coreVersion": "^1.19.0",
|
|
7
7
|
"category": { "parent": "pages" },
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/* modules/ui-design/public/tokens.css — THE SHARED TOKEN SHEET (task 1005081,
|
|
2
|
+
goal 1000138 "One Cloud Bongos design system").
|
|
3
|
+
|
|
4
|
+
One sheet every Cloud Bongos surface loads — the hall, the landing + the
|
|
5
|
+
projects hub, the status page and the raise page — served at the one stable
|
|
6
|
+
path /modules/ui-design/public/tokens.css on every host (serve-internal.js's
|
|
7
|
+
module-asset route; the kit's stub serves the same path).
|
|
8
|
+
|
|
9
|
+
WHAT IS HERE: the named SCALES — type sizes, spacing, corners, shadows,
|
|
10
|
+
motion and stacking layers. Every value was already in use (the hall's token
|
|
11
|
+
block, DESIGN.md's rhythm/shapes/motion), so loading this sheet moves nothing.
|
|
12
|
+
|
|
13
|
+
WHAT IS NOT HERE: the fifteen brand tokens (thirteen colours + two font
|
|
14
|
+
stacks). Those are the instance pack's API — serve-internal.js themeMarkup()
|
|
15
|
+
splices config/branding.*.json theme.ui as a plain :root at the END of <head>,
|
|
16
|
+
after every sheet, so the pack always wins. This sheet declares none of the
|
|
17
|
+
fifteen, so there is nothing for it to win against. No colour literal lives
|
|
18
|
+
here except black with alpha (the shadows) — the Fifteen Rule's one exception.
|
|
19
|
+
|
|
20
|
+
WHY --ds-*: the surfaces grew their own scale names, and some collide with a
|
|
21
|
+
DIFFERENT meaning (cosmos.css spends --line as a colour; raise.css sets
|
|
22
|
+
--radius to 10px). A shared name would silently change whichever surface did
|
|
23
|
+
not define it, so every scale is namespaced and each surface keeps its own
|
|
24
|
+
names as aliases until a later task migrates it (the hall's aliases live in
|
|
25
|
+
modules/hall-ui/public/style.css: --radius → --ds-radius-panel, …).
|
|
26
|
+
|
|
27
|
+
DARK TWINS: the one mode-dependent scale (the shadow) is written twice,
|
|
28
|
+
byte-identical — the switch rule (the hall's html[data-theme], the apex's
|
|
29
|
+
:root[data-mode]) and the prefers-color-scheme twin for first paint.
|
|
30
|
+
|
|
31
|
+
KILL SWITCH: each surface keeps its own `animation/transition: none
|
|
32
|
+
!important` rule; this sheet additionally zeroes its durations under
|
|
33
|
+
prefers-reduced-motion so a rule built on them stays still too. */
|
|
34
|
+
|
|
35
|
+
:root {
|
|
36
|
+
/* ---- type sizes: the steps the hall's 113 literal sizes cluster on, small
|
|
37
|
+
to large. The "-plus" half steps are the ones a later migration
|
|
38
|
+
should collapse into their neighbours. The four fluid roles are
|
|
39
|
+
DESIGN.md's display/headline/figure/title. ---- */
|
|
40
|
+
--ds-text-3xs: 10.5px;
|
|
41
|
+
--ds-text-2xs: 11px;
|
|
42
|
+
--ds-text-2xs-plus: 11.5px;
|
|
43
|
+
--ds-text-xs: 12px;
|
|
44
|
+
--ds-text-xs-plus: 12.5px;
|
|
45
|
+
--ds-text-sm: 13px;
|
|
46
|
+
--ds-text-sm-plus: 13.5px;
|
|
47
|
+
--ds-text-md: 14px;
|
|
48
|
+
--ds-text-md-plus: 14.5px;
|
|
49
|
+
--ds-text-lg: 15px;
|
|
50
|
+
--ds-text-xl: 16px;
|
|
51
|
+
--ds-text-2xl: 17px;
|
|
52
|
+
--ds-text-3xl: 20px;
|
|
53
|
+
--ds-text-4xl: 22px;
|
|
54
|
+
--ds-text-5xl: 26px;
|
|
55
|
+
--ds-text-title: clamp(23px, 2.4vw, 32px);
|
|
56
|
+
--ds-text-headline: clamp(26px, 3.1vw, 42px);
|
|
57
|
+
--ds-text-figure: clamp(30px, 3.7vw, 52px);
|
|
58
|
+
--ds-text-display: clamp(34px, 6.7vw, 96px);
|
|
59
|
+
|
|
60
|
+
/* ---- spacing: DESIGN.md's rhythm (6/10/14/22/34/52) + the band gutters ---- */
|
|
61
|
+
--ds-space-hair: 6px;
|
|
62
|
+
--ds-space-xs: 10px;
|
|
63
|
+
--ds-space-sm: 14px;
|
|
64
|
+
--ds-space-md: 22px;
|
|
65
|
+
--ds-space-lg: 34px;
|
|
66
|
+
--ds-space-xl: 52px;
|
|
67
|
+
--ds-space-gutter: 26px;
|
|
68
|
+
--ds-space-gutter-narrow: 20px;
|
|
69
|
+
|
|
70
|
+
/* ---- the hairline: a WIDTH, never a colour ---- */
|
|
71
|
+
--ds-line: 1px;
|
|
72
|
+
|
|
73
|
+
/* ---- corners: pill or nothing for controls; panels and tiles ---- */
|
|
74
|
+
--ds-radius-pill: 999px;
|
|
75
|
+
--ds-radius-panel: 16px;
|
|
76
|
+
--ds-radius-tile: 10px;
|
|
77
|
+
|
|
78
|
+
/* ---- shadows: the world has no UI shadow vocabulary; this is spent only
|
|
79
|
+
where an element floats over a scrim. Black with alpha. ---- */
|
|
80
|
+
--ds-shadow-pop: 0 18px 40px rgba(0, 0, 0, .28);
|
|
81
|
+
|
|
82
|
+
/* ---- motion: state changes take the one easing; an authored entrance
|
|
83
|
+
takes the exponential ease-out (DESIGN.md, Motion). ---- */
|
|
84
|
+
--ds-dur-fast: 150ms;
|
|
85
|
+
--ds-dur-base: 200ms;
|
|
86
|
+
--ds-dur-slow: 320ms;
|
|
87
|
+
--ds-dur-slower: 420ms;
|
|
88
|
+
--ds-dur-arrive: 700ms;
|
|
89
|
+
--ds-stagger: 70ms;
|
|
90
|
+
--ds-ease: cubic-bezier(.2, .7, .3, 1);
|
|
91
|
+
--ds-ease-arrive: cubic-bezier(.16, 1, .3, 1);
|
|
92
|
+
|
|
93
|
+
/* ---- stacking layers, low to high: the reading hairline under the top
|
|
94
|
+
bar, the chrome, the phone drawer over its scrim, a confirm dialog
|
|
95
|
+
over the drawer, the fullscreen diagram over everything. ---- */
|
|
96
|
+
--ds-z-progress: 25;
|
|
97
|
+
--ds-z-topbar: 30;
|
|
98
|
+
--ds-z-sidebar: 40;
|
|
99
|
+
--ds-z-scrim: 140;
|
|
100
|
+
--ds-z-drawer: 150;
|
|
101
|
+
--ds-z-modal: 160;
|
|
102
|
+
--ds-z-diagram-full: 9999;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* ---------- dark: written twice, byte-identical ---------- */
|
|
106
|
+
html[data-theme="dark"], :root[data-mode="dark"] {
|
|
107
|
+
--ds-shadow-pop: 0 18px 40px rgba(0, 0, 0, .62);
|
|
108
|
+
}
|
|
109
|
+
@media (prefers-color-scheme: dark) {
|
|
110
|
+
html:not([data-theme]):not([data-mode="light"]) {
|
|
111
|
+
--ds-shadow-pop: 0 18px 40px rgba(0, 0, 0, .62);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/* ---------- the kill switch, for the motion scale ---------- */
|
|
116
|
+
@media (prefers-reduced-motion: reduce) {
|
|
117
|
+
:root {
|
|
118
|
+
--ds-dur-fast: 0ms;
|
|
119
|
+
--ds-dur-base: 0ms;
|
|
120
|
+
--ds-dur-slow: 0ms;
|
|
121
|
+
--ds-dur-slower: 0ms;
|
|
122
|
+
--ds-dur-arrive: 0ms;
|
|
123
|
+
--ds-stagger: 0ms;
|
|
124
|
+
}
|
|
125
|
+
}
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.46",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.24.
|
|
9
|
+
"version": "1.24.46",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.24.
|
|
3
|
+
"version": "1.24.46",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/release-notes.json
CHANGED
|
@@ -11227,5 +11227,25 @@
|
|
|
11227
11227
|
"id": "1005051",
|
|
11228
11228
|
"text": "Goals now opens with the version's progress and the timeline of every version, so the separate Roadmap page is gone from the sidebar. Old Roadmap links open Goals. The Tasks board's 'see the version' line points at Goals too."
|
|
11229
11229
|
}
|
|
11230
|
+
],
|
|
11231
|
+
"1.24.45": [
|
|
11232
|
+
{
|
|
11233
|
+
"id": "1005070",
|
|
11234
|
+
"text": "The hall's sidebar now appears instantly on every page instead of popping in late, and Building mode shows a single Home button."
|
|
11235
|
+
}
|
|
11236
|
+
],
|
|
11237
|
+
"1.24.46": [
|
|
11238
|
+
{
|
|
11239
|
+
"id": "1005081",
|
|
11240
|
+
"text": "Cloud Bongos now has one shared set of design measurements (text sizes, spacing, corner shapes, shadows, animation timing and layering) that the builders hall, the landing page, the projects map, the status page and the invest"
|
|
11241
|
+
},
|
|
11242
|
+
{
|
|
11243
|
+
"id": "1004547",
|
|
11244
|
+
"text": "An Autobongos PC can now sign in with a login of its own, so the server always knows its work is automatic and it can never move a live version or flip its own off switch."
|
|
11245
|
+
},
|
|
11246
|
+
{
|
|
11247
|
+
"id": "1005080",
|
|
11248
|
+
"text": "The screenshot checker builders use before shipping a design change shows the real hall pages again, instead of a pop-up covering every page. Two small accessibility fixes came with it: a few tiny links are now big enough to t"
|
|
11249
|
+
}
|
|
11230
11250
|
]
|
|
11231
11251
|
}
|
|
@@ -540,7 +540,7 @@ const KERNEL_FILES = [
|
|
|
540
540
|
'src/bongos/auth-admission.js', // membership kind, admission gate, first-admin seating
|
|
541
541
|
'src/bongos/auth-github.js', // GitHub device + web OAuth flows
|
|
542
542
|
'src/bongos/auth-idp.js', // hub federation, assertion + logout-token verification
|
|
543
|
-
'src/bongos/auth-discord.js',
|
|
543
|
+
'src/bongos/auth-discord.js', 'src/bongos/runner-login.js', // Discord OAuth link + guild membership; an Autobongos PC's own login + its limits (task 1004547; requires only pool/db-kernel)
|
|
544
544
|
'src/bongos/route-rank-check.js', // deterministic route→rank classification
|
|
545
545
|
'src/bongos/permission-path-check.js', // protected-path matcher (PROTECTED_GLOBS, ADR 0043)
|
|
546
546
|
'src/bongos/goal-scope-check.js', // goal create-gate scope wall (composes the 3 matchers above; ADR 0086 §3, BV1.R60)
|
|
@@ -269,7 +269,9 @@ function build() {
|
|
|
269
269
|
};
|
|
270
270
|
});
|
|
271
271
|
|
|
272
|
-
|
|
272
|
+
// 'home' is in every craft: there the plate under the brand IS the Home button
|
|
273
|
+
// (tasks 1005065, 1005070), so no mode lists the Home tab beside it.
|
|
274
|
+
const inAnyCraft = new Set(['home', ...CRAFTS.flatMap((c) => crafts.tabs[c])]);
|
|
273
275
|
const findings = {
|
|
274
276
|
broken: dedupe(broken).sort((a, b) => (a.from + a.href).localeCompare(b.from + b.href)),
|
|
275
277
|
orphan: rows.filter((r) => r.url !== '/' && !r.nav.length && !r.in.length).map((r) => r.url),
|