@bongos/core 1.20.50 → 1.20.52

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.
Files changed (77) hide show
  1. package/.bongos-core.json +172 -62
  2. package/docs/copy-inventory.md +54 -52
  3. package/docs/copy-registry.json +106 -88
  4. package/docs/module-api-changelog.md +4 -0
  5. package/docs/page-inventory.json +6 -1
  6. package/docs/page-readings.json +449 -427
  7. package/modules/hall-ui/public/atlas.html +1 -1
  8. package/modules/hall-ui/public/blockers.html +1 -1
  9. package/modules/hall-ui/public/board-room.html +1 -1
  10. package/modules/hall-ui/public/collab.html +1 -1
  11. package/modules/hall-ui/public/commands.html +1 -1
  12. package/modules/hall-ui/public/copy-desk.html +1 -1
  13. package/modules/hall-ui/public/deploy.html +1 -1
  14. package/modules/hall-ui/public/diagrams.html +1 -1
  15. package/modules/hall-ui/public/drachmae.html +1 -1
  16. package/modules/hall-ui/public/fleet.html +1 -1
  17. package/modules/hall-ui/public/gate.html +1 -1
  18. package/modules/hall-ui/public/goal-map.html +29 -14
  19. package/modules/hall-ui/public/goal-map.states.json +12 -0
  20. package/modules/hall-ui/public/goals.html +1 -1
  21. package/modules/hall-ui/public/government.html +1 -1
  22. package/modules/hall-ui/public/idea.html +1 -1
  23. package/modules/hall-ui/public/ideas.html +1 -1
  24. package/modules/hall-ui/public/index.html +1 -1
  25. package/modules/hall-ui/public/modules.html +1 -1
  26. package/modules/hall-ui/public/primer.html +1 -1
  27. package/modules/hall-ui/public/profile.html +1 -1
  28. package/modules/hall-ui/public/project-settings.html +1 -1
  29. package/modules/hall-ui/public/ranks.html +1 -1
  30. package/modules/hall-ui/public/roadmap.html +1 -1
  31. package/modules/hall-ui/public/roster.html +1 -1
  32. package/modules/hall-ui/public/sessions.html +1 -1
  33. package/modules/hall-ui/public/settings.html +1 -1
  34. package/modules/hall-ui/public/shell.js +145 -15
  35. package/modules/hall-ui/public/studio.html +1 -1
  36. package/modules/hall-ui/public/style.css +61 -5
  37. package/modules/hall-ui/public/task.html +1 -1
  38. package/modules/hall-ui/public/thinking.css +15 -11
  39. package/modules/hall-ui/public/thinking.html +1 -1
  40. package/modules/hall-ui/public/tweak-editor.css +6 -0
  41. package/modules/hall-ui/public/tweak-editor.html +2 -2
  42. package/modules/hall-ui/public/watch.html +1 -1
  43. package/modules/hall-ui/public/work.html +1 -1
  44. package/modules/hall-ui/records/hall-navigation.md +22 -0
  45. package/modules/hall-ui/records/sky.md +2 -0
  46. package/modules/provisioning/soft-limits.js +17 -0
  47. package/modules/public-landing/public/assets/world/r5-life-1-primordial-sea-cut.webp +0 -0
  48. package/modules/public-landing/public/assets/world/r5-life-1-primordial-sea-cut.webp.json +16 -0
  49. package/modules/public-landing/public/assets/world/r5-life-1-primordial-sea.jpg +0 -0
  50. package/modules/public-landing/public/assets/world/r5-life-1-primordial-sea.jpg.json +16 -0
  51. package/modules/public-landing/public/assets/world/r5-life-2-first-cells-cut.webp +0 -0
  52. package/modules/public-landing/public/assets/world/r5-life-2-first-cells-cut.webp.json +16 -0
  53. package/modules/public-landing/public/assets/world/r5-life-2-first-cells.jpg +0 -0
  54. package/modules/public-landing/public/assets/world/r5-life-2-first-cells.jpg.json +16 -0
  55. package/modules/public-landing/public/assets/world/r5-life-3-genome-cut.webp +0 -0
  56. package/modules/public-landing/public/assets/world/r5-life-3-genome-cut.webp.json +16 -0
  57. package/modules/public-landing/public/assets/world/r5-life-3-genome.jpg +0 -0
  58. package/modules/public-landing/public/assets/world/r5-life-3-genome.jpg.json +16 -0
  59. package/modules/public-landing/public/assets/world/r5-life-4-membrane-cut.webp +0 -0
  60. package/modules/public-landing/public/assets/world/r5-life-4-membrane-cut.webp.json +16 -0
  61. package/modules/public-landing/public/assets/world/r5-life-4-membrane.jpg +0 -0
  62. package/modules/public-landing/public/assets/world/r5-life-4-membrane.jpg.json +16 -0
  63. package/modules/public-landing/public/assets/world/r5-life-5-nervous-system-cut.webp +0 -0
  64. package/modules/public-landing/public/assets/world/r5-life-5-nervous-system-cut.webp.json +16 -0
  65. package/modules/public-landing/public/assets/world/r5-life-5-nervous-system.jpg +0 -0
  66. package/modules/public-landing/public/assets/world/r5-life-5-nervous-system.jpg.json +16 -0
  67. package/package-lock.json +2 -2
  68. package/package.json +1 -1
  69. package/release-notes.json +16 -0
  70. package/src/module-api.js +1 -1
  71. package/tests/genesis_life_plates.mjs +63 -0
  72. package/tests/goal_map_page.mjs +10 -7
  73. package/tests/hall_audit.mjs +3 -4
  74. package/tests/hall_fullscreen.mjs +99 -14
  75. package/tests/hall_tweak_editor.mjs +1 -1
  76. package/tests/nav_permission_atoms.mjs +1 -1
  77. package/tests/provisioning_planet_physics.mjs +14 -0
@@ -1051,15 +1051,71 @@ html[data-fullscreen="on"] .app-topbar {
1051
1051
  }
1052
1052
  html[data-fullscreen="on"] .app-topbar > :not(.topbar-side),
1053
1053
  html[data-fullscreen="on"] .topbar-side > :not(.fullscreen-toggle) { display: none; }
1054
+ /* task 1004430: the way back must read on ANY ground. The hall's glass is
1055
+ near-clear, so over the black sky in the light theme it was a dark glyph on
1056
+ black — the page looked stuck without its bars. The card tone plus a drop
1057
+ shadow and a ring keeps it legible on paper, on the sky and in either mode. */
1054
1058
  html[data-fullscreen="on"] .fullscreen-toggle {
1055
1059
  pointer-events: auto;
1056
- width: 36px; height: 36px;
1060
+ width: auto; height: 36px;
1061
+ gap: 6px;
1062
+ padding: 0 14px 0 10px;
1057
1063
  color: var(--ink);
1058
- background: var(--glass);
1059
- box-shadow: inset 0 0 0 1px var(--glass-edge);
1060
- backdrop-filter: blur(var(--glass-blur));
1061
- -webkit-backdrop-filter: blur(var(--glass-blur));
1064
+ background: var(--bg-card);
1065
+ box-shadow: 0 0 0 1px var(--rule), 0 4px 16px rgba(0, 0, 0, .35);
1066
+ font-size: 13px;
1067
+ font-weight: 500;
1068
+ white-space: nowrap;
1069
+ }
1070
+ /* A phone has no room for the words; the round button keeps its card tone. */
1071
+ @media (max-width: 640px) {
1072
+ html[data-fullscreen="on"] .fullscreen-toggle { width: 36px; padding: 0; }
1073
+ .fullscreen-toggle__label { display: none; }
1074
+ }
1075
+ html[data-fullscreen="on"] .fullscreen-toggle:hover { color: var(--accent-ink); }
1076
+ /* A page with no full-screen layout of its own steps its content clear of the
1077
+ corner button; the sky and the studio lay themselves out (shell.js `plain`). */
1078
+ html[data-fullscreen="on"][data-fullscreen-plain="on"] .app-content { padding-top: 60px; }
1079
+ /* Said once on entering, since the choice is remembered and a page can open in it. */
1080
+ .fullscreen-hint {
1081
+ position: fixed;
1082
+ top: 56px;
1083
+ right: 12px;
1084
+ z-index: var(--z-topbar);
1085
+ max-width: min(320px, calc(100vw - 24px));
1086
+ padding: 8px 12px;
1087
+ border-radius: 10px;
1088
+ background: var(--bg-card);
1089
+ color: var(--ink);
1090
+ font-size: 13px;
1091
+ line-height: 1.4;
1092
+ box-shadow: 0 0 0 1px var(--rule), 0 4px 16px rgba(0, 0, 0, .35);
1093
+ pointer-events: none;
1094
+ }
1095
+
1096
+ /* THE BACK LINK (task 1004430): at the head of the bar when the page was
1097
+ reached from inside another hall page. 24px floor on both axes. */
1098
+ .topbar-back {
1099
+ display: inline-flex;
1100
+ align-items: center;
1101
+ gap: 4px;
1102
+ min-height: 32px;
1103
+ min-width: 32px;
1104
+ padding: 0 10px 0 6px;
1105
+ margin-right: -4px;
1106
+ border-radius: var(--radius-pill);
1107
+ color: var(--ink-soft);
1108
+ font-size: 13px;
1109
+ text-decoration: none;
1110
+ white-space: nowrap;
1111
+ flex-shrink: 0;
1112
+ max-width: 40vw;
1113
+ overflow: hidden;
1114
+ text-overflow: ellipsis;
1062
1115
  }
1116
+ .topbar-back:hover { color: var(--ink); background: var(--rule-soft); }
1117
+ .topbar-back svg { width: 16px; height: 16px; flex-shrink: 0; }
1118
+ @media (max-width: 480px) { .topbar-back__label { display: none; } .topbar-back { padding: 0 8px; } }
1063
1119
  .zoom-toggle__pct {
1064
1120
  font-size: 12px;
1065
1121
  font-weight: 500;
@@ -57,7 +57,7 @@
57
57
 
58
58
  <!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
59
59
  time so the chrome exists before the deferred page scripts execute. -->
60
- <script src="/builders/shell.js?v=2026-09-30-founding-commands"></script>
60
+ <script src="/builders/shell.js?v=2026-09-30-navback2"></script>
61
61
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
62
62
  <script src="/builders/palette.js" defer></script>
63
63
  <!-- shared frontend toolkit (window.OTB) — loaded FIRST so task.js can pull from it. -->
@@ -7,11 +7,11 @@
7
7
  ONE SHEET, TWO DOORS (part 4, task 1004232): /thinking and /goal-map are the
8
8
  same page opened in different lenses, so both are named below.
9
9
  IN THE HALL (task 1004327): /thinking now loads style.css and sits in the
10
- hall chrome; /goal-map is still standalone. So the sky's tokens and ground
11
- sit on /thinking's STAGE (main.sky-stage) instead of its body — the chrome
10
+ hall chrome, and /goal-map joined it in task 1004430. So the sky's tokens and
11
+ ground sit on the STAGE (main.sky-stage) instead of its body — the chrome
12
12
  around the stage keeps the hall's own palette in both modes — and the sky's
13
13
  few global rules are written :where()-scoped, so they cost no specificity
14
- (goal-map renders exactly as before) and never reach the chrome. */
14
+ and never reach the chrome. */
15
15
  /* The template declared these on :root. They sit on the page's body here,
16
16
  with the template's values untouched, because the server splices the
17
17
  branding pack's own :root (--ink, --accent, --rule …) into <head> AFTER this
@@ -21,7 +21,7 @@
21
21
  its own — tests/hall_reading_world.mjs; color-scheme inherits from body
22
22
  too). Same reason `body` below is qualified: a pack backdrop is written as
23
23
  `body{background:…}` and the void must win. */
24
- body[data-page="goal-map"], body[data-page="thinking"] .sky-stage {
24
+ body[data-page] .sky-stage {
25
25
  color-scheme: dark;
26
26
  --void: #000000; --ink: #ffffff; --ink-soft: #c0c0c0; --ink-faint: #a8a8a8; --ink-dim: #65656a; --ink-ghost: #3a3a3f;
27
27
  --rule: rgba(255,255,255,.16); --rule-soft: rgba(255,255,255,.08); --glass: rgba(255,255,255,.075); --glass-strong: rgba(255,255,255,.14);
@@ -30,15 +30,15 @@ body[data-page="goal-map"], body[data-page="thinking"] .sky-stage {
30
30
  --face: Manrope, "Segoe UI", system-ui, -apple-system, sans-serif;
31
31
  }
32
32
  html, body { height: 100%; }
33
- body[data-page="goal-map"], body[data-page="thinking"] .sky-stage { margin: 0; background: var(--void); color: var(--ink); font-family: var(--face); font-size: 15px; line-height: 1.5; overflow: hidden; overflow: clip; -webkit-font-smoothing: antialiased; }
33
+ body[data-page] .sky-stage { margin: 0; background: var(--void); color: var(--ink); font-family: var(--face); font-size: 15px; line-height: 1.5; overflow: hidden; overflow: clip; -webkit-font-smoothing: antialiased; }
34
34
  /* clip, not just hidden (task 1004342): a hidden-overflow box is still scrollable, so
35
35
  focus scrolled the stage sideways to show the footer hint and the whole sky slid
36
36
  left. clip cuts the same pixels and cannot be scrolled; hidden stays as the fallback. */
37
- body[data-page="thinking"] { overflow: hidden; overflow: clip; }
38
- :where(body[data-page="goal-map"], .sky-stage) button { font-family: inherit; }
39
- :where(body[data-page="goal-map"], .sky-stage) a { color: var(--accent); }
40
- :where(body[data-page="goal-map"], .sky-stage) .lbl { font: var(--label); letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint); }
41
- :where(body[data-page="goal-map"], .sky-stage) .mono { font: var(--mono); font-variant-numeric: tabular-nums; }
37
+ body[data-page="thinking"], body[data-page="goal-map"] { overflow: hidden; overflow: clip; }
38
+ :where(.sky-stage) button { font-family: inherit; }
39
+ :where(.sky-stage) a { color: var(--accent); }
40
+ :where(.sky-stage) .lbl { font: var(--label); letter-spacing: .14em; text-transform: uppercase; color: var(--ink-faint); }
41
+ :where(.sky-stage) .mono { font: var(--mono); font-variant-numeric: tabular-nums; }
42
42
  [hidden] { display: none !important; }
43
43
 
44
44
  /* THE STAGE (task 1004327). The box under the hall's top bar and beside its
@@ -55,11 +55,15 @@ html[data-fullscreen="on"] .sky-stage { top: 0; left: 0; }
55
55
  @media (max-width: 768px) { .sky-stage { left: 0; } }
56
56
  /* The full-screen switch floats top right over the stage, where the sky's
57
57
  tools end; the strip steps in by the button's width so nothing sits under it. */
58
- html[data-fullscreen="on"] .sky-stage .top { padding-right: 64px; }
58
+ html[data-fullscreen="on"] .sky-stage .top { padding-right: 184px; }
59
59
  /* Under 900 the brand takes a row of its own (the template's rule below), so
60
60
  only that row steps in and the tools keep their width. */
61
61
  @media (max-width: 900px) {
62
62
  html[data-fullscreen="on"] .sky-stage .top { padding-right: 16px; }
63
+ html[data-fullscreen="on"] .sky-stage .brand { padding-right: 168px; }
64
+ }
65
+ /* Under 640 the exit is the round button again (style.css). */
66
+ @media (max-width: 640px) {
63
67
  html[data-fullscreen="on"] .sky-stage .brand { padding-right: 48px; }
64
68
  }
65
69
  /* style.css rules whose names the template also uses — each one reset to what
@@ -138,7 +138,7 @@
138
138
  </div>
139
139
  </div>
140
140
 
141
- <script src="/builders/shell.js?v=2026-09-30-founding-commands"></script>
141
+ <script src="/builders/shell.js?v=2026-09-30-navback2"></script>
142
142
  <script src="/builders/palette.js" defer></script>
143
143
 
144
144
  <script src="/builders/idea-objects.js?v=2026-09-27-split"></script>
@@ -136,10 +136,16 @@ html[data-fullscreen="on"] .tw-room { top: 0; left: 0; }
136
136
  /* In full screen the shell's one button floats over the night room in either
137
137
  theme, so on this page it wears the room's glass, not the hall's light ink. */
138
138
  html[data-fullscreen="on"] body[data-page="tweak-editor"] .fullscreen-toggle {
139
+ /* task 1004430: the shell's exit is a labelled pill, but this bar is laid out
140
+ for a 36px button in the corner, so the words would sit on submit. The
141
+ room keeps the round button; its name and title still say "Exit". */
142
+ width: 36px;
143
+ padding: 0;
139
144
  color: #ffffff;
140
145
  background: rgba(255, 255, 255, .07);
141
146
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .16);
142
147
  }
148
+ body[data-page="tweak-editor"] .fullscreen-toggle__label { display: none; }
143
149
 
144
150
  /* THE GLASS TOP BAR. Sticky, so the four doors stay in reach down a long page
145
151
  and the paper passes under the glass. In full screen it steps in far enough
@@ -49,7 +49,7 @@
49
49
  <!-- A <div>, not a <header>: the shell's top bar is the page's one
50
50
  <header> (the kit's one-header-main-h1 floor). -->
51
51
  <div class="tw-bar" id="tw-bar">
52
- <a class="tw-back" href="/builders/studio">← studio</a>
52
+ <a class="tw-back" data-own-back href="/builders/studio">← studio</a>
53
53
  <span class="tw-name"><span id="tw-name">Tweak a page</span> <span class="tw-surface" id="tw-surface"></span></span>
54
54
  <span class="tw-counter" id="tw-counter" aria-live="polite"></span>
55
55
  <div class="tw-actions">
@@ -140,7 +140,7 @@
140
140
 
141
141
  <div id="toast" class="toast" role="status" aria-live="polite" data-visible="0"></div>
142
142
 
143
- <script src="/builders/shell.js?v=2026-09-30-founding-commands"></script>
143
+ <script src="/builders/shell.js?v=2026-09-30-navback2"></script>
144
144
  <script src="/builders/palette.js" defer></script>
145
145
  <script src="/builders/dom-utils.js?v=2026-06-23-contrib"></script>
146
146
  <script src="/builders/brand-holes.js?v=2026-09-29-aqfix"></script>
@@ -162,7 +162,7 @@
162
162
 
163
163
  <!-- The app shell (sidebar + top bar). Dependency-free; loaded FIRST so the
164
164
  chrome exists before the page script runs. -->
165
- <script src="/builders/shell.js?v=2026-09-30-founding-commands"></script>
165
+ <script src="/builders/shell.js?v=2026-09-30-navback2"></script>
166
166
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
167
167
  <script src="/builders/palette.js" defer></script>
168
168
  <!-- shared frontend toolkit (window.OTB) — loaded before watch.js, which pulls from it. -->
@@ -171,7 +171,7 @@
171
171
 
172
172
  <!-- The app shell (sidebar + top bar + footer). Dependency-free; runs at parse
173
173
  time so the chrome exists before the deferred page scripts execute. -->
174
- <script src="/builders/shell.js?v=2026-09-30-founding-commands"></script>
174
+ <script src="/builders/shell.js?v=2026-09-30-navback2"></script>
175
175
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
176
176
  <script src="/builders/palette.js" defer></script>
177
177
  <!-- shared frontend toolkit (window.OTB) — loaded FIRST so work.js can pull from it. -->
@@ -0,0 +1,22 @@
1
+ # Hall navigation — the Back link and the standard full-screen switch (task 1004430)
2
+
3
+ Covers: every shelled hall page (`shell.js`, `style.css`), plus `goal-map.html` joining the chrome.
4
+
5
+ ## The Back link
6
+
7
+ - `shell.js` renders `#topbar-back` ("← <label>") at the head of the top bar when the page was reached from INSIDE another hall page. The origin is `?from=` first, then a same-origin `document.referrer`; the label is that page's `NAV` label (or "task N" / "idea N" / "goal N" for a record).
8
+ - **A sidebar hop is not a drill-down.** A sidebar link click writes a one-shot `sessionStorage['hall-nav-hop']` naming its target; the page it lands on reads and deletes it, and shows no Back link. Without this every page reached from the sidebar would carry a Back link, which is just history.
9
+ - When the origin came from the referrer, the click runs `history.back()`, so a list comes back with its scroll and filters; the `href` is still the real address for a new tab.
10
+ - A page drawing its own record back link (`.rec-back` — `task.html`, `idea.html`) keeps it; the bar adds no second one. The origin's own `?from=` is dropped so back links never chain.
11
+ - `/profile?id=N` highlights **Builders**, not your own Profile, unless `/me` says the id is yours.
12
+
13
+ ## Full screen is standard
14
+
15
+ - Every shelled page gets the switch. `<body data-fullscreen-switch="off">` opts out. The bare attribute means "this page lays itself out for full screen" (sky, studio, tweak editor); a page without it is **plain**, and the shell sets `html[data-fullscreen-plain="on"]` so `style.css` pads `.app-content` clear of the corner button.
16
+ - In full screen the button is a labelled pill, an icon plus "Exit full screen", in `--bg-card` with a ring and a drop shadow, not the hall's near-clear glass. Under 640px it drops the words, and the tweak editor keeps the 36px round button because its bar is laid out for one. A page whose own back link is not `.rec-back` marks it `data-own-back`. On the black sky in the light theme the glass button was a dark icon on black, and because the choice is remembered per page, `/thinking` looked stuck with no bars. Entering full screen (or a page opening in it) also shows a 4.5s `#fullscreen-hint` saying how to leave.
17
+
18
+ ## The project sky joined the hall
19
+
20
+ `goal-map.html` now uses thinking.html's structure: sidebar and top-bar mounts, `style.css`, `shell.js`, the sky inside `<main id="hall-main" class="sky-stage">`, and a `<div class="brand">` because the shell's bar is the page's one `<header>`. In `thinking.css` both pages share one stage rule, `body[data-page] .sky-stage`. No hall page is standalone any more (`tests/hall_audit.mjs` `STANDALONE_EXEMPT` is empty).
21
+
22
+ Tests: `tests/hall_fullscreen.mjs` (the standard switch, `plain`, Back link cases), `tests/goal_map_page.mjs`, `tests/hall_audit.mjs` §8.
@@ -31,6 +31,8 @@ Pinned by `tests/hall_sky.mjs` (31 cases: the reads, the five objects, the two c
31
31
  - **The sky's palette lives on the stage, not the body, on `/thinking`** — so the chrome around it keeps the hall's own light and dark. `style.css` now loads under `thinking.css`, and the handful of hall rules whose names the template also uses (`.skip`, `.filters`, `.foot`, `:focus-visible`, text inputs, `* { box-sizing }`) are reset under `:where(.sky-stage)` at the same specificity, before the sky's own rules. The sky's global rules are `:where()`-scoped for the same reason, which leaves `/goal-map` byte-for-byte in its old cascade.
32
32
  - **Both pages render in both modes now.** The chrome follows the hall theme; the sky's stage is the void in both. The `full` and `full-back` states in both states files (and `full-list` in the sky's) press the switch.
33
33
 
34
+ **Superseded for `/goal-map` by task 1004430 (owner, 2026-09-30):** the project sky joined the hall chrome the same way, with the same switch. Both pages now share one stage rule (`body[data-page] .sky-stage`), `STANDALONE_EXEMPT` is empty, and the full-screen button uses the card tone so it can be seen on the void. See [`hall-navigation.md`](hall-navigation.md).
35
+
34
36
  ## Two skies, one renderer (task 1004328, BV2.TW21)
35
37
 
36
38
  **Owner decision 2026-09-27: "Your thinking is only your thinking."** Part 4's "one page, two doors" above is SUPERSEDED: the two pages share the renderer and nothing else. The owner confirmed that "the your projects tab" means the whole-project lens, its switch and the project-map features. What this settled:
@@ -56,6 +56,23 @@ const SOFT_LIMIT_RULES = Object.freeze([
56
56
  effect: 'grey',
57
57
  reason: 'A nonprofit has no owners, so it has no shares to give.',
58
58
  }),
59
+ // The owner's call (task 1004475, 2026-09-30): a single-owner company and a research
60
+ // entity are greyed too, each with the reason that fits it. Only a for-profit company
61
+ // or a division of a corporation is offered equity.
62
+ Object.freeze({
63
+ id: 'equity-not-in-a-single-owner-llc',
64
+ when: { entity: { in: ['single-owner-llc'] } },
65
+ options: ['reward_intents:equity'],
66
+ effect: 'grey',
67
+ reason: 'A single-owner company has one owner. Giving builders a stake means taking on more owners first.',
68
+ }),
69
+ Object.freeze({
70
+ id: 'equity-not-in-a-research-entity',
71
+ when: { entity: { in: ['research'] } },
72
+ options: ['reward_intents:equity'],
73
+ effect: 'grey',
74
+ reason: "A research entity usually has no shares to give, so equity is off unless it's set up as a company.",
75
+ }),
59
76
  Object.freeze({
60
77
  id: 'no-cash-payouts-to-eu-builders-yet',
61
78
  when: { builder_geography: { in: ['us-eu', 'global'] } },
@@ -0,0 +1,16 @@
1
+ {
2
+ "kind": "cutout",
3
+ "model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
4
+ "aspect_ratio": "1:1",
5
+ "generated": "2026-09-30",
6
+ "round": 5,
7
+ "task": 1004425,
8
+ "stage": "scope",
9
+ "label": "primordial sea",
10
+ "prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. A chrome planet whose whole surface is a slow, liquid chrome ocean with faint swirling currents, calm and empty, nothing living yet.",
11
+ "reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
12
+ "resolution": "1024x1024 from the same panel",
13
+ "source": "r5-life-1-primordial-sea.jpg with the black ground removed: flood-fill from the edges on near-black pixels, luminance-feathered",
14
+ "encoded": "canvas WebP quality 0.88 with alpha",
15
+ "use": "light mode: the genesis life strip, stage scope (primordial sea)"
16
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "kind": "plate",
3
+ "model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
4
+ "aspect_ratio": "1:1",
5
+ "generated": "2026-09-30",
6
+ "round": 5,
7
+ "task": 1004425,
8
+ "stage": "scope",
9
+ "label": "primordial sea",
10
+ "prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. A chrome planet whose whole surface is a slow, liquid chrome ocean with faint swirling currents, calm and empty, nothing living yet.",
11
+ "reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
12
+ "resolution": "source panel about 220px across the object in a 1024x559 sheet, squared and upscaled to 1024px; soft if shown large, sized for the genesis life strip",
13
+ "source": "the owner's Gemini sheet, split by the ui-design kit browser (panel detection on the white dividers)",
14
+ "encoded": "canvas JPEG quality 0.92, 1024x1024, pure black ground",
15
+ "use": "dark mode: the genesis life strip, stage scope (primordial sea)"
16
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "kind": "cutout",
3
+ "model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
4
+ "aspect_ratio": "1:1",
5
+ "generated": "2026-09-30",
6
+ "round": 5,
7
+ "task": 1004425,
8
+ "stage": "strategize",
9
+ "label": "first cells",
10
+ "prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. The same chrome planet with a cluster of small glossy chrome beads, like the first cells, gathering on one side of its surface.",
11
+ "reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
12
+ "resolution": "1024x1024 from the same panel",
13
+ "source": "r5-life-2-first-cells.jpg with the black ground removed: flood-fill from the edges on near-black pixels, luminance-feathered",
14
+ "encoded": "canvas WebP quality 0.88 with alpha",
15
+ "use": "light mode: the genesis life strip, stage strategize (first cells)"
16
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "kind": "plate",
3
+ "model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
4
+ "aspect_ratio": "1:1",
5
+ "generated": "2026-09-30",
6
+ "round": 5,
7
+ "task": 1004425,
8
+ "stage": "strategize",
9
+ "label": "first cells",
10
+ "prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. The same chrome planet with a cluster of small glossy chrome beads, like the first cells, gathering on one side of its surface.",
11
+ "reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
12
+ "resolution": "source panel about 220px across the object in a 1024x559 sheet, squared and upscaled to 1024px; soft if shown large, sized for the genesis life strip",
13
+ "source": "the owner's Gemini sheet, split by the ui-design kit browser (panel detection on the white dividers)",
14
+ "encoded": "canvas JPEG quality 0.92, 1024x1024, pure black ground",
15
+ "use": "dark mode: the genesis life strip, stage strategize (first cells)"
16
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "kind": "cutout",
3
+ "model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
4
+ "aspect_ratio": "1:1",
5
+ "generated": "2026-09-30",
6
+ "round": 5,
7
+ "task": 1004425,
8
+ "stage": "charter",
9
+ "label": "the genome",
10
+ "prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. The same chrome planet wrapped loosely by a single double-helix ribbon of polished chrome, like DNA, circling it at an angle.",
11
+ "reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
12
+ "resolution": "1024x1024 from the same panel",
13
+ "source": "r5-life-3-genome.jpg with the black ground removed: flood-fill from the edges on near-black pixels, luminance-feathered",
14
+ "encoded": "canvas WebP quality 0.88 with alpha",
15
+ "use": "light mode: the genesis life strip, stage charter (the genome)"
16
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "kind": "plate",
3
+ "model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
4
+ "aspect_ratio": "1:1",
5
+ "generated": "2026-09-30",
6
+ "round": 5,
7
+ "task": 1004425,
8
+ "stage": "charter",
9
+ "label": "the genome",
10
+ "prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. The same chrome planet wrapped loosely by a single double-helix ribbon of polished chrome, like DNA, circling it at an angle.",
11
+ "reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
12
+ "resolution": "source panel about 220px across the object in a 1024x559 sheet, squared and upscaled to 1024px; soft if shown large, sized for the genesis life strip",
13
+ "source": "the owner's Gemini sheet, split by the ui-design kit browser (panel detection on the white dividers)",
14
+ "encoded": "canvas JPEG quality 0.92, 1024x1024, pure black ground",
15
+ "use": "dark mode: the genesis life strip, stage charter (the genome)"
16
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "kind": "cutout",
3
+ "model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
4
+ "aspect_ratio": "1:1",
5
+ "generated": "2026-09-30",
6
+ "round": 5,
7
+ "task": 1004425,
8
+ "stage": "legalize",
9
+ "label": "the membrane",
10
+ "prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. The same chrome planet sealed inside a thin, translucent frosted-glass shell, a soft membrane with a gentle inner glow.",
11
+ "reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
12
+ "resolution": "1024x1024 from the same panel",
13
+ "source": "r5-life-4-membrane.jpg with the black ground removed: flood-fill from the edges on near-black pixels, luminance-feathered; the halo outside the shell rim un-multiplied from black so the glow fades on light paper instead of darkening",
14
+ "encoded": "canvas WebP quality 0.88 with alpha",
15
+ "use": "light mode: the genesis life strip, stage legalize (the membrane)"
16
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "kind": "plate",
3
+ "model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
4
+ "aspect_ratio": "1:1",
5
+ "generated": "2026-09-30",
6
+ "round": 5,
7
+ "task": 1004425,
8
+ "stage": "legalize",
9
+ "label": "the membrane",
10
+ "prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. The same chrome planet sealed inside a thin, translucent frosted-glass shell, a soft membrane with a gentle inner glow.",
11
+ "reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
12
+ "resolution": "source panel about 220px across the object in a 1024x559 sheet, squared and upscaled to 1024px; soft if shown large, sized for the genesis life strip",
13
+ "source": "the owner's Gemini sheet, split by the ui-design kit browser (panel detection on the white dividers)",
14
+ "encoded": "canvas JPEG quality 0.92, 1024x1024, pure black ground",
15
+ "use": "dark mode: the genesis life strip, stage legalize (the membrane)"
16
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "kind": "cutout",
3
+ "model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
4
+ "aspect_ratio": "1:1",
5
+ "generated": "2026-09-30",
6
+ "round": 5,
7
+ "task": 1004425,
8
+ "stage": "legislation",
9
+ "label": "the nervous system",
10
+ "prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. The same chrome planet with fine branching chrome filaments spreading across its surface like neurons, a faint warm glow where they meet.",
11
+ "reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
12
+ "resolution": "1024x1024 from the same panel",
13
+ "source": "r5-life-5-nervous-system.jpg with the black ground removed: flood-fill from the edges on near-black pixels, luminance-feathered",
14
+ "encoded": "canvas WebP quality 0.88 with alpha",
15
+ "use": "light mode: the genesis life strip, stage legislation (the nervous system)"
16
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "kind": "plate",
3
+ "model": "Gemini image model (generated by the owner in the Gemini app, 2026-09-30; one 5-panel sheet)",
4
+ "aspect_ratio": "1:1",
5
+ "generated": "2026-09-30",
6
+ "round": 5,
7
+ "task": 1004425,
8
+ "stage": "legislation",
9
+ "label": "the nervous system",
10
+ "prompt": "Mirror-polished liquid chrome, soft key light from the top left, a slightly long lens, the object whole and centred, pure black background everywhere the object is not. No text, no letters, no logo, no watermark, no UI. The same chrome planet with fine branching chrome filaments spreading across its surface like neurons, a faint warm glow where they meet.",
11
+ "reference": "modules/public-landing/public/assets/world/r2-chrome-blob-one.jpg, uploaded as the material/light/ground reference",
12
+ "resolution": "source panel about 220px across the object in a 1024x559 sheet, squared and upscaled to 1024px; soft if shown large, sized for the genesis life strip",
13
+ "source": "the owner's Gemini sheet, split by the ui-design kit browser (panel detection on the white dividers)",
14
+ "encoded": "canvas JPEG quality 0.92, 1024x1024, pure black ground",
15
+ "use": "dark mode: the genesis life strip, stage legislation (the nervous system)"
16
+ }
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.20.50",
3
+ "version": "1.20.52",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.20.50",
9
+ "version": "1.20.52",
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.20.50",
3
+ "version": "1.20.52",
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",
@@ -8413,5 +8413,21 @@
8413
8413
  "id": "1004417",
8414
8414
  "text": "Starting a new project now begins with two decisions before any setup. First screen, \"anything you want to make\": the founder ticks that they can sign legally for the platform and acknowledge the terms, with a link to read the"
8415
8415
  }
8416
+ ],
8417
+ "1.20.51": [
8418
+ {
8419
+ "id": "1004430",
8420
+ "text": "Hall navigation is more standard. The top bar now has a Back button when you open a page from inside another one. A builder's profile opened from the Builders list shows \"Back to Builders\" and returns you to the same spot in t"
8421
+ }
8422
+ ],
8423
+ "1.20.52": [
8424
+ {
8425
+ "id": "1004425",
8426
+ "text": "The project-startup flow now has its five life-stage pictures, one per stage of a new project's genesis: a primordial sea, first cells, a genome, a membrane and a nervous system, each in the chrome world's style with a dark an"
8427
+ },
8428
+ {
8429
+ "id": "1004475",
8430
+ "text": "Equity or shares is now also greyed out for a single-owner company and for a research entity, each with its own short reason. Only a regular for-profit company or a division of a corporation is offered equity. As before, it's"
8431
+ }
8416
8432
  ]
8417
8433
  }
package/src/module-api.js CHANGED
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
75
75
  // MAJOR (see allowBoxScope below): passes the request through untouched.
76
76
  function deprecatedNoopMiddleware(_req, _res, next) { next(); }
77
77
 
78
- const CORE_VERSION = '1.20.50'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
78
+ const CORE_VERSION = '1.20.52'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
79
79
 
80
80
  // A namespaced logger so a module's log lines are attributable + consistent.
81
81
  // Usage: const log = api.logger('discord'); log.info('mounted');
@@ -0,0 +1,63 @@
1
+ // tests/genesis_life_plates.mjs — the five genesis life-stage plates (task 1004425 /
2
+ // BV2.PS12, goal 1000121).
3
+ //
4
+ // One plate per genesis stage, shipped as the world's pair: a black-ground JPEG for
5
+ // dark mode and an alpha WebP cutout for light, each with the provenance sidecar every
6
+ // world plate carries. The genesis home's life strip (the GenesisHall mock today, the
7
+ // live genesis home in PS08) reads them by these names, so a renamed or half-shipped
8
+ // pair breaks the strip silently: a missing image is an empty square, not an error.
9
+
10
+ import { test } from 'node:test';
11
+ import assert from 'node:assert/strict';
12
+ import fs from 'node:fs';
13
+ import path from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
15
+
16
+ const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
17
+ const WORLD = path.join(ROOT, 'modules', 'public-landing', 'public', 'assets', 'world');
18
+ const MOCK = path.join(ROOT, 'docs', 'design', 'mocks', 'project-startup', 'GenesisHall.dc.html');
19
+
20
+ // The stages in genesis order, with the label the strip captions them by.
21
+ const PLATES = [
22
+ ['scope', 'primordial sea', 'r5-life-1-primordial-sea'],
23
+ ['strategize', 'first cells', 'r5-life-2-first-cells'],
24
+ ['charter', 'the genome', 'r5-life-3-genome'],
25
+ ['legalize', 'the membrane', 'r5-life-4-membrane'],
26
+ ['legislation', 'the nervous system', 'r5-life-5-nervous-system'],
27
+ ];
28
+
29
+ // The first bytes of each format, so a mislabelled file (a PNG saved as .jpg) fails here.
30
+ const isJpeg = (b) => b[0] === 0xff && b[1] === 0xd8 && b[2] === 0xff;
31
+ const isWebp = (b) => b.toString('ascii', 0, 4) === 'RIFF' && b.toString('ascii', 8, 12) === 'WEBP';
32
+
33
+ test('each genesis stage ships the world pair: a dark JPEG and a light WebP cutout', () => {
34
+ for (const [, , slug] of PLATES) {
35
+ const jpg = fs.readFileSync(path.join(WORLD, `${slug}.jpg`));
36
+ const cut = fs.readFileSync(path.join(WORLD, `${slug}-cut.webp`));
37
+ assert.ok(isJpeg(jpg), `${slug}.jpg is a JPEG`);
38
+ assert.ok(isWebp(cut), `${slug}-cut.webp is a WebP`);
39
+ }
40
+ });
41
+
42
+ test('every plate file carries a provenance sidecar naming its stage and label', () => {
43
+ for (const [stage, label, slug] of PLATES) {
44
+ for (const [file, kind] of [[`${slug}.jpg`, 'plate'], [`${slug}-cut.webp`, 'cutout']]) {
45
+ const side = JSON.parse(fs.readFileSync(path.join(WORLD, `${file}.json`), 'utf8'));
46
+ assert.equal(side.kind, kind, `${file}: kind`);
47
+ assert.equal(side.task, 1004425, `${file}: task`);
48
+ assert.equal(side.stage, stage, `${file}: stage`);
49
+ assert.equal(side.label, label, `${file}: label`);
50
+ assert.ok(side.prompt && side.model, `${file}: prompt and model recorded`);
51
+ }
52
+ }
53
+ });
54
+
55
+ test('the genesis mock life strip shows all five plates and no placeholder', () => {
56
+ const html = fs.readFileSync(MOCK, 'utf8');
57
+ assert.ok(!/plate to render/.test(html), 'no "plate to render" placeholder left');
58
+ for (const [stage, label, slug] of PLATES) {
59
+ const tile = html.split('\n').find((l) => l.includes(`<p class="cap">${stage}<b>${label}</b>`));
60
+ assert.ok(tile, `a tile for ${stage}`);
61
+ assert.ok(tile.includes(`/assets/world/${slug}-cut.webp`), `${stage} tile uses ${slug}`);
62
+ }
63
+ });