@bongos/core 1.19.414 → 1.19.416

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 (47) hide show
  1. package/.bongos-core.json +173 -23
  2. package/docs/design/mocks/hub/assets/r3-space-deepfield-half.jpg +0 -0
  3. package/docs/design/mocks/hub/assets/r3-space-deepfield-half.jpg.json +13 -0
  4. package/docs/design/mocks/hub/assets/r3-space-dust-half.jpg +0 -0
  5. package/docs/design/mocks/hub/assets/r3-space-dust-half.jpg.json +13 -0
  6. package/docs/design/mocks/hub/assets/r3-space-nebula-half.jpg +0 -0
  7. package/docs/design/mocks/hub/assets/r3-space-nebula-half.jpg.json +13 -0
  8. package/docs/design/mocks/hub/assets/r3-space-starfield-half.jpg +0 -0
  9. package/docs/design/mocks/hub/assets/r3-space-starfield-half.jpg.json +13 -0
  10. package/docs/design/mocks/hub/planets.html +127 -3
  11. package/docs/design/projects-hub-direction-v2.md +10 -0
  12. package/docs/file-map.md +1 -0
  13. package/docs/module-api-changelog.md +4 -0
  14. package/modules/platform-identity/migrations/platform_identity_013_project_template_materials.sql +31 -0
  15. package/modules/platform-identity/platform-identity.js +18 -5
  16. package/modules/platform-identity/routes/projects.js +3 -2
  17. package/modules/platform-identity/tests/platform-identity.mjs +23 -12
  18. package/modules/ui-design/kit/fixtures/featured.json +1 -1
  19. package/package-lock.json +2 -2
  20. package/package.json +1 -1
  21. package/public-landing/assets/cosmos.css +47 -1
  22. package/public-landing/assets/world/r3-frost-bead-cut.webp +0 -0
  23. package/public-landing/assets/world/r3-frost-bead-cut.webp.json +15 -0
  24. package/public-landing/assets/world/r3-frost-blob-cut.webp +0 -0
  25. package/public-landing/assets/world/r3-frost-blob-cut.webp.json +15 -0
  26. package/public-landing/assets/world/r3-frost-pair-cut.webp +0 -0
  27. package/public-landing/assets/world/r3-frost-pair-cut.webp.json +15 -0
  28. package/public-landing/assets/world/r3-gold-bead-cut.webp +0 -0
  29. package/public-landing/assets/world/r3-gold-bead-cut.webp.json +15 -0
  30. package/public-landing/assets/world/r3-gold-blob-cut.webp +0 -0
  31. package/public-landing/assets/world/r3-gold-blob-cut.webp.json +15 -0
  32. package/public-landing/assets/world/r3-gold-pair-cut.webp +0 -0
  33. package/public-landing/assets/world/r3-gold-pair-cut.webp.json +15 -0
  34. package/public-landing/assets/world/r3-obsidian-bead-cut.webp +0 -0
  35. package/public-landing/assets/world/r3-obsidian-bead-cut.webp.json +15 -0
  36. package/public-landing/assets/world/r3-obsidian-blob-cut.webp +0 -0
  37. package/public-landing/assets/world/r3-obsidian-blob-cut.webp.json +15 -0
  38. package/public-landing/assets/world/r3-obsidian-pair-cut.webp +0 -0
  39. package/public-landing/assets/world/r3-obsidian-pair-cut.webp.json +15 -0
  40. package/public-landing/assets/world/r3-space-nebula.jpg +0 -0
  41. package/public-landing/assets/world/r3-space-nebula.jpg.json +13 -0
  42. package/public-landing/projects.html +23 -14
  43. package/public-landing/projects.states.json +1 -1
  44. package/src/module-api.js +1 -1
  45. package/tests/memory_isolation.mjs +4 -0
  46. package/tests/own_scope_guard.mjs +472 -0
  47. package/tests/projects_hub.mjs +68 -18
@@ -0,0 +1,31 @@
1
+ -- platform_identity_013_project_template_materials.sql — the planet templates grow from
2
+ -- shape × style to shape × MATERIAL × style (task 1003337).
3
+ --
4
+ -- Round 3 rendered the world's three whole objects (blob / pair / bead) in three new
5
+ -- materials — frosted glass, brushed gold, polished obsidian — beside the first round's
6
+ -- chrome. A template name is `<shape>-<style>` for chrome (the first round's names, kept
7
+ -- verbatim so every stored row stays valid) or `<shape>-<material>-<style>` for a new
8
+ -- material, where style is tint / wash / plain (plain = the material as rendered; the
9
+ -- chrome material's plain is spelled `chrome`). Thirty-six names; the hub's default walk
10
+ -- (PROJECT_TEMPLATE_WALK) uses twelve of them, each step changing shape, material and style.
11
+ --
12
+ -- Additive + platform_identity_-namespaced (ADR 0083 §5): the CHECK from migration 012 is
13
+ -- re-stated with the wider set; the column, its nullability and the curation route are
14
+ -- unchanged. Idempotent: safe to re-run.
15
+
16
+ BEGIN;
17
+
18
+ ALTER TABLE platform_identity_projects DROP CONSTRAINT IF EXISTS platform_identity_projects_template_check;
19
+ ALTER TABLE platform_identity_projects ADD CONSTRAINT platform_identity_projects_template_check
20
+ CHECK (template IS NULL OR template IN (
21
+ -- chrome, the first round (migration 012), names kept verbatim
22
+ 'blob-tint', 'blob-wash', 'blob-chrome',
23
+ 'pair-tint', 'pair-wash', 'pair-chrome',
24
+ 'bead-tint', 'bead-wash', 'bead-chrome',
25
+ -- the materials round
26
+ 'blob-frost-tint', 'blob-frost-wash', 'blob-frost-plain', 'blob-gold-tint', 'blob-gold-wash', 'blob-gold-plain', 'blob-obsidian-tint', 'blob-obsidian-wash', 'blob-obsidian-plain',
27
+ 'pair-frost-tint', 'pair-frost-wash', 'pair-frost-plain', 'pair-gold-tint', 'pair-gold-wash', 'pair-gold-plain', 'pair-obsidian-tint', 'pair-obsidian-wash', 'pair-obsidian-plain',
28
+ 'bead-frost-tint', 'bead-frost-wash', 'bead-frost-plain', 'bead-gold-tint', 'bead-gold-wash', 'bead-gold-plain', 'bead-obsidian-tint', 'bead-obsidian-wash', 'bead-obsidian-plain'
29
+ ));
30
+
31
+ COMMIT;
@@ -234,11 +234,21 @@ async function getClientByOrigin(origin, { pool = defaultPool } = {}) {
234
234
  // provisioning's copy on its own instances table).
235
235
  const PROJECT_TYPES = ['game', 'research', 'business', 'non-profit', 'not-sure'];
236
236
 
237
- // The sky's planet templates (task 1003319): shape (blob / pair / bead, the world's three
238
- // whole chrome objects) × style (how the accent lands: tint / wash / chrome). A CATALOG field
239
- // curated through POST /projects; null = the hub's deterministic walk (the accent's), whose
240
- // order this list IS. Pinned by the DB CHECK (migration 012); mirrored by ORB_TEMPLATES.
241
- const PROJECT_TEMPLATES = ['blob-tint', 'pair-wash', 'bead-chrome', 'blob-wash', 'pair-chrome', 'bead-tint', 'blob-chrome', 'pair-tint', 'bead-wash'];
237
+ // The sky's planet templates (task 1003319; the materials, task 1003337): SHAPE (blob / pair /
238
+ // bead, the world's three whole objects) × MATERIAL (chrome — the first round, named by omission
239
+ // — frost / gold / obsidian) × STYLE (how the accent lands: tint / wash / plain; the chrome
240
+ // material's plain is spelled `chrome`, the first round's name, so stored rows stay valid).
241
+ // PROJECT_TEMPLATES is EVERY valid name (the DB CHECK, migrations 012 + 013; the POST /projects
242
+ // enum); PROJECT_TEMPLATE_WALK is the hub's deterministic default order — consecutive positions
243
+ // change shape, material AND style. A CATALOG field curated through POST /projects; null = the
244
+ // walk. Mirrored by ORB_TEMPLATES / ORB_WALK in projects.html.
245
+ const PROJECT_SHAPES = ['blob', 'pair', 'bead'];
246
+ const PROJECT_MATERIALS = ['frost', 'gold', 'obsidian'];
247
+ const PROJECT_TEMPLATES = [
248
+ ...PROJECT_SHAPES.flatMap((s) => ['tint', 'wash', 'chrome'].map((y) => `${s}-${y}`)),
249
+ ...PROJECT_SHAPES.flatMap((s) => PROJECT_MATERIALS.flatMap((m) => ['tint', 'wash', 'plain'].map((y) => `${s}-${m}-${y}`))),
250
+ ];
251
+ const PROJECT_TEMPLATE_WALK = ['blob-tint', 'pair-gold-wash', 'bead-obsidian-plain', 'blob-frost-wash', 'pair-chrome', 'bead-gold-tint', 'blob-obsidian-wash', 'pair-frost-tint', 'bead-wash', 'blob-gold-plain', 'pair-obsidian-tint', 'bead-frost-wash'];
242
252
 
243
253
  // The stored JOIN DOOR (ADR 0182 D1, task 1002323). Three values, and ALL THREE ARE
244
254
  // VISIBLE: `private` is visible-but-closed (the `goals.visibility` meaning, ADR 0112),
@@ -1357,6 +1367,9 @@ module.exports = {
1357
1367
  upsertProject,
1358
1368
  PROJECT_TYPES,
1359
1369
  PROJECT_TEMPLATES,
1370
+ PROJECT_TEMPLATE_WALK,
1371
+ PROJECT_MATERIALS,
1372
+ PROJECT_SHAPES,
1360
1373
  getProject,
1361
1374
  listFeaturedProjects,
1362
1375
  listAllProjects,
@@ -48,8 +48,9 @@ module.exports = function projectsRoutes() {
48
48
  description: { type: 'string', maxLength: 2000 },
49
49
  art_url: { type: 'string', maxLength: 2048 },
50
50
  type: { type: 'string', enum: [...pi.PROJECT_TYPES] },
51
- // The planet template (task 1003319): the shape and style the sky cuts this
52
- // project's orb in. Curated here, never self-reported; absent means the hub's walk.
51
+ // The planet template (task 1003319; materials, task 1003337): the shape, material and
52
+ // style the sky cuts this project's orb in. Curated here, never self-reported; absent
53
+ // means the hub's walk.
53
54
  template: { type: 'string', enum: [...pi.PROJECT_TEMPLATES] },
54
55
  })) return;
55
56
  try {
@@ -235,19 +235,30 @@ await ta('upsertProject carries the planet template, COALESCE-preserved; an unkn
235
235
  const pool2 = mockPool([{ rows: [{ id: '2' }] }]);
236
236
  await P.upsertProject({ origin: 'https://q', name: 'Q', template: 'saucer-neon' }, { pool: pool2 });
237
237
  assert.ok(!pool2.calls[0].params.includes('saucer-neon'), 'unknown templates never reach the SQL');
238
- // the vocabulary: the world's three whole objects × the three ways the accent lands,
239
- // in the walk's order (consecutive positions change shape AND style)
240
- assert.equal(P.PROJECT_TEMPLATES.length, 9);
241
- assert.equal(new Set(P.PROJECT_TEMPLATES).size, 9, 'nine distinct templates');
242
- for (const t of P.PROJECT_TEMPLATES) assert.match(t, /^(blob|pair|bead)-(tint|wash|chrome)$/);
243
- for (let i = 1; i < P.PROJECT_TEMPLATES.length; i++) {
244
- const [s0, y0] = P.PROJECT_TEMPLATES[i - 1].split('-');
245
- const [s1, y1] = P.PROJECT_TEMPLATES[i].split('-');
246
- assert.ok(s0 !== s1 && y0 !== y1, `walk step ${i} changes both shape and style`);
238
+ // the vocabulary: the world's three whole objects × four materials (chrome named by
239
+ // omission) × the three ways the accent lands — every valid name (the CHECK's set)
240
+ const RE = /^(blob|pair|bead)-(tint|wash|chrome|(frost|gold|obsidian)-(tint|wash|plain))$/;
241
+ assert.deepEqual(P.PROJECT_SHAPES, ['blob', 'pair', 'bead']);
242
+ assert.deepEqual(P.PROJECT_MATERIALS, ['frost', 'gold', 'obsidian']);
243
+ assert.equal(P.PROJECT_TEMPLATES.length, 36);
244
+ assert.equal(new Set(P.PROJECT_TEMPLATES).size, 36, 'thirty-six distinct templates');
245
+ for (const t of P.PROJECT_TEMPLATES) assert.match(t, RE);
246
+ for (const t of ['blob-tint', 'pair-wash', 'bead-chrome', 'blob-wash', 'pair-chrome', 'bead-tint', 'blob-chrome', 'pair-tint', 'bead-wash']) assert.ok(P.PROJECT_TEMPLATES.includes(t), `the first round's ${t} stays valid`);
247
+ // the walk: twelve of them, consecutive positions (wrapping) change shape, material AND style
248
+ const parts = (t) => { const p = t.split('-'); return p.length === 3 ? p : [p[0], 'chrome', p[1]]; };
249
+ assert.equal(P.PROJECT_TEMPLATE_WALK.length, 12);
250
+ assert.equal(new Set(P.PROJECT_TEMPLATE_WALK).size, 12, 'twelve distinct walk positions');
251
+ for (let i = 0; i < P.PROJECT_TEMPLATE_WALK.length; i++) {
252
+ const [s0, m0, y0] = parts(P.PROJECT_TEMPLATE_WALK[(i + 11) % 12]);
253
+ const [s1, m1, y1] = parts(P.PROJECT_TEMPLATE_WALK[i]);
254
+ assert.ok(s0 !== s1 && m0 !== m1 && y0 !== y1, `walk step ${i} changes shape, material and style`);
255
+ assert.ok(P.PROJECT_TEMPLATES.includes(P.PROJECT_TEMPLATE_WALK[i]), 'every walk position is a valid name');
247
256
  }
248
- // the migration pins the same nine
249
- const mig = readFileSync(new URL('../migrations/platform_identity_012_project_template.sql', import.meta.url), 'utf8');
250
- for (const t of P.PROJECT_TEMPLATES) assert.ok(mig.includes(`'${t}'`), `migration 012 names ${t}`);
257
+ // migration 013 pins the same thirty-six (012 named the first nine)
258
+ const mig = readFileSync(new URL('../migrations/platform_identity_013_project_template_materials.sql', import.meta.url), 'utf8');
259
+ for (const t of P.PROJECT_TEMPLATES) assert.ok(mig.includes(`'${t}'`), `migration 013 names ${t}`);
260
+ const mig12 = readFileSync(new URL('../migrations/platform_identity_012_project_template.sql', import.meta.url), 'utf8');
261
+ for (const t of P.PROJECT_TEMPLATES.slice(0, 9)) assert.ok(mig12.includes(`'${t}'`), `migration 012 named ${t}`);
251
262
  // and the public projection carries it, so the sky can read a curated choice
252
263
  const pool3 = mockPool([{ rows: [] }]);
253
264
  await P.listFeaturedProjects({ pool: pool3 });
@@ -1 +1 @@
1
- {"projects":[{"origin":"https://amazonprimea.com","name":"Off the Boats","tagline":"A singular shop, and a broader videogame in a persistent ancient Greek world that collides with modern day commerce & products.","art_url":"/assets/world/r2-chrome-blob-one.jpg","type":"game","template":"bead-wash","created_at":"2026-07-15T22:12:57.732Z","builders":11,"tags":["art-pipeline","character-anim","demo","game"],"owner":"lars589","owner_avatar":"https://avatars.githubusercontent.com/u/283020391?v=4","faces":["https://avatars.githubusercontent.com/u/283020391?v=4","https://avatars.githubusercontent.com/u/64553469?v=4","https://avatars.githubusercontent.com/u/290184380?v=4","https://avatars.githubusercontent.com/u/295990394?v=4","https://avatars.githubusercontent.com/u/35337514?v=4"],"shipped_30d":0,"shipped_30d_capped":false,"hall_origin":"https://builders.amazonprimea.com"},{"origin":"https://demo.cloudbongos.com","name":"Demo","tagline":"A live demo instance on Cloud Bongos.","art_url":null,"type":null,"template":null,"created_at":"2026-07-14T19:16:38.160Z","builders":null,"tags":null,"owner":null,"owner_avatar":null,"faces":null,"shipped_30d":null,"shipped_30d_capped":null,"hall_origin":null},{"origin":"https://emersonian-circles.cloudbongos.com","name":"Emersonian Circles","tagline":"An independent project built on Cloud Bongos.","art_url":null,"type":null,"template":null,"created_at":"2026-07-25T02:21:55.641Z","builders":1,"tags":[],"owner":"lars589","owner_avatar":"https://avatars.githubusercontent.com/u/283020391?v=4","faces":[],"shipped_30d":30,"shipped_30d_capped":true,"hall_origin":null}]}
1
+ {"projects":[{"origin":"https://amazonprimea.com","name":"Off the Boats","tagline":"A singular shop, and a broader videogame in a persistent ancient Greek world that collides with modern day commerce & products.","art_url":"/assets/world/r2-chrome-blob-one.jpg","type":"game","template":"bead-wash","created_at":"2026-07-15T22:12:57.732Z","builders":11,"tags":["art-pipeline","character-anim","demo","game"],"owner":"lars589","owner_avatar":"https://avatars.githubusercontent.com/u/283020391?v=4","faces":["https://avatars.githubusercontent.com/u/283020391?v=4","https://avatars.githubusercontent.com/u/64553469?v=4","https://avatars.githubusercontent.com/u/290184380?v=4","https://avatars.githubusercontent.com/u/295990394?v=4","https://avatars.githubusercontent.com/u/35337514?v=4"],"shipped_30d":0,"shipped_30d_capped":false,"hall_origin":"https://builders.amazonprimea.com"},{"origin":"https://demo.cloudbongos.com","name":"Demo","tagline":"A live demo instance on Cloud Bongos.","art_url":null,"type":null,"template":"pair-gold-wash","created_at":"2026-07-14T19:16:38.160Z","builders":null,"tags":null,"owner":null,"owner_avatar":null,"faces":null,"shipped_30d":null,"shipped_30d_capped":null,"hall_origin":null},{"origin":"https://emersonian-circles.cloudbongos.com","name":"Emersonian Circles","tagline":"An independent project built on Cloud Bongos.","art_url":null,"type":null,"template":null,"created_at":"2026-07-25T02:21:55.641Z","builders":1,"tags":[],"owner":"lars589","owner_avatar":"https://avatars.githubusercontent.com/u/283020391?v=4","faces":[],"shipped_30d":30,"shipped_30d_capped":true,"hall_origin":null}]}
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.414",
3
+ "version": "1.19.416",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.414",
9
+ "version": "1.19.416",
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.19.414",
3
+ "version": "1.19.416",
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",
@@ -264,6 +264,22 @@
264
264
  masthead's reason. */
265
265
  --plate-bead-cut:url(/assets/world/r2-chrome-blob-one-cut.webp);
266
266
  --plate-bongos-cut:url(/assets/world/r2-bongos-chrome-a-cut.webp);
267
+ /* the materials round (task 1003337): each material is its own whole-object plate
268
+ per shape — the alpha cutout, masked and blended exactly like the chrome ones.
269
+ Sidecars beside each file carry the prompt and the take. */
270
+ --plate-frost-blob-cut:url(/assets/world/r3-frost-blob-cut.webp);
271
+ --plate-frost-pair-cut:url(/assets/world/r3-frost-pair-cut.webp);
272
+ --plate-frost-bead-cut:url(/assets/world/r3-frost-bead-cut.webp);
273
+ --plate-gold-blob-cut:url(/assets/world/r3-gold-blob-cut.webp);
274
+ --plate-gold-pair-cut:url(/assets/world/r3-gold-pair-cut.webp);
275
+ --plate-gold-bead-cut:url(/assets/world/r3-gold-bead-cut.webp);
276
+ --plate-obsidian-blob-cut:url(/assets/world/r3-obsidian-blob-cut.webp);
277
+ --plate-obsidian-pair-cut:url(/assets/world/r3-obsidian-pair-cut.webp);
278
+ --plate-obsidian-bead-cut:url(/assets/world/r3-obsidian-bead-cut.webp);
279
+ /* the field's ground: a rendered deep-space plate on the dark-only apex (task 1003337,
280
+ the owner's ask on the planets review). One token, so the pick is one line; the
281
+ candidates sit beside it in .impeccable/mocks/world-assets/. */
282
+ --field-plate:url(/assets/world/r3-space-nebula.jpg);
267
283
  --mast-shadow:drop-shadow(0 26px 30px color-mix(in srgb,var(--ink) 20%,transparent));
268
284
  /* the planet's contact shadow, scaled to a 60-130px object. Dark's twin is
269
285
  a NO-OP filter (opacity(1), not none) so the search spotlight's
@@ -725,7 +741,14 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
725
741
  the branding pack spliced after this file. ── */
726
742
  .fieldWrap{padding:0;max-width:none;}
727
743
  .field{position:relative;height:calc(100vh - var(--top-h) - var(--foot-h));min-height:340px;overflow:hidden;}
728
- .field .ground{position:absolute;inset:0;background:var(--sky-bg);}
744
+ /* the ground is a rendered plate (task 1003337): the page ground under a deep-space
745
+ render at cover, drifting a few percent over 90s (transform only, dead under
746
+ reduced motion); the inset is the room the drift needs. */
747
+ .field .ground{position:absolute;inset:-4%;background:var(--sky-bg) var(--field-plate) center/cover no-repeat;}
748
+ @media (prefers-reduced-motion:no-preference){
749
+ .field .ground{animation:groundDrift 90s ease-in-out infinite alternate;}
750
+ @keyframes groundDrift{from{transform:translate3d(-1.2%,-.8%,0) scale(1.02);}to{transform:translate3d(1.2%,.8%,0) scale(1.06);}}
751
+ }
729
752
 
730
753
  /* ── THE PLANET: a project rendered as one of the world's three whole chrome
731
754
  objects in the project's accent (task 1003319; DESIGN.md "Project orbs" and
@@ -769,6 +792,29 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
769
792
  .worldOrb[data-style="tint"]{--tpl-ink:var(--acc);--tpl-blend:color;}
770
793
  .worldOrb[data-style="wash"]{--tpl-ink:var(--acc);--tpl-blend:overlay;}
771
794
  .worldOrb[data-style="chrome"]{--tpl-ink:transparent;--tpl-blend:normal;}
795
+ .worldOrb[data-style="plain"]{--tpl-ink:transparent;--tpl-blend:normal;}
796
+ /* the materials (task 1003337): a whole object per plate, the rectangle measured from the
797
+ cutout's alpha (bbox at alpha>=16 on the 2048² plate, a 1.5% margin each side; zoom = 1/width,
798
+ px/py = -u0/-v0 × zoom — tests/projects_hub.mjs recomputes these from the boxes):
799
+ frost-blob u[0.168,0.8223] v[0.2495,0.7642]
800
+ frost-pair u[0.0591,0.9409] v[0.2764,0.7231]
801
+ frost-bead u[0.1685,0.8066] v[0.1738,0.8198]
802
+ gold-blob u[0.1274,0.8584] v[0.1035,0.9019]
803
+ gold-pair u[0.0552,0.9458] v[0.2739,0.7383]
804
+ gold-bead u[0.0542,0.9478] v[0.0542,0.9434]
805
+ obsidian-blob u[0.1885,0.8018] v[0.1177,0.8979]
806
+ obsidian-pair u[0.0249,0.9707] v[0.3096,0.7158]
807
+ obsidian-bead u[0.1416,0.854] v[0.1431,0.8535]
808
+ Two attributes outrank the one-attribute chrome crops above. */
809
+ .worldOrb[data-material="frost"][data-shape="blob"]{--tpl-src:var(--plate-frost-blob-cut);--tpl-ar:1.25629;--tpl-zoom:1.46135;--tpl-px:-.22359;--tpl-py:-.34269;}
810
+ .worldOrb[data-material="frost"][data-shape="pair"]{--tpl-src:var(--plate-frost-pair-cut);--tpl-ar:1.91273;--tpl-zoom:1.09673;--tpl-px:-.04837;--tpl-py:-.28669;}
811
+ .worldOrb[data-material="frost"][data-shape="bead"]{--tpl-src:var(--plate-frost-bead-cut);--tpl-ar:.98831;--tpl-zoom:1.49678;--tpl-px:-.22976;--tpl-py:-.23769;}
812
+ .worldOrb[data-material="gold"][data-shape="blob"]{--tpl-src:var(--plate-gold-blob-cut);--tpl-ar:.91864;--tpl-zoom:1.31406;--tpl-px:-.1477;--tpl-py:-.11629;}
813
+ .worldOrb[data-material="gold"][data-shape="pair"]{--tpl-src:var(--plate-gold-pair-cut);--tpl-ar:1.86206;--tpl-zoom:1.08625;--tpl-px:-.04367;--tpl-py:-.28123;}
814
+ .worldOrb[data-material="gold"][data-shape="bead"]{--tpl-src:var(--plate-gold-bead-cut);--tpl-ar:1.00479;--tpl-zoom:1.08272;--tpl-px:-.04244;--tpl-py:-.04244;}
815
+ .worldOrb[data-material="obsidian"][data-shape="blob"]{--tpl-src:var(--plate-obsidian-blob-cut);--tpl-ar:.794;--tpl-zoom:1.55448;--tpl-px:-.2697;--tpl-py:-.15965;}
816
+ .worldOrb[data-material="obsidian"][data-shape="pair"]{--tpl-src:var(--plate-obsidian-pair-cut);--tpl-ar:2.23705;--tpl-zoom:1.0248;--tpl-px:-.01015;--tpl-py:-.30191;}
817
+ .worldOrb[data-material="obsidian"][data-shape="bead"]{--tpl-src:var(--plate-obsidian-bead-cut);--tpl-ar:1.0027;--tpl-zoom:1.34698;--tpl-px:-.17053;--tpl-py:-.17255;}
772
818
  /* the relationship ring: yours takes the accent (the one live thing about the
773
819
  orb); building there is a hairline of it */
774
820
  .worldOrb[data-rel="owner"] .disc::after,.worldOrb[data-rel="builder"] .disc::after{content:"";position:absolute;
@@ -0,0 +1,15 @@
1
+ {
2
+ "kind": "cutout",
3
+ "model": "nano_banana_2 (requested as nano_banana_pro; the catalog served nano_banana_2)",
4
+ "aspect_ratio": "1:1",
5
+ "resolution": "2k",
6
+ "generated": "2026-08-29T15:37Z",
7
+ "round": 3,
8
+ "task": 1003337,
9
+ "material": "frosted glass",
10
+ "object": "the small bead",
11
+ "prompt": "Background removed (Higgsfield image_background_remover) from r3-frost-bead: the small bead in frosted glass. Cut out so the planet can be masked and colour-blended in the project's accent (the hub's template grammar, task 1003319); alpha verified with ffmpeg alphaextract (min 0, max 255).",
12
+ "source": ".impeccable/mocks/world-assets/r3-frost-bead-cut.webp",
13
+ "encoded": "ffmpeg libwebp quality 88, alpha kept (yuva420p), 1600px (task 1003337)",
14
+ "use": "the hub's planet in frosted glass: --plate-frost-bead-cut, masked and blended by [data-material=\"frost\"][data-shape=\"bead\"] in cosmos.css"
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "kind": "cutout",
3
+ "model": "nano_banana_2 (requested as nano_banana_pro; the catalog served nano_banana_2)",
4
+ "aspect_ratio": "1:1",
5
+ "resolution": "2k",
6
+ "generated": "2026-08-29T15:37Z",
7
+ "round": 3,
8
+ "task": 1003337,
9
+ "material": "frosted glass",
10
+ "object": "the round blob",
11
+ "prompt": "Background removed (Higgsfield image_background_remover) from r3-frost-blob: the round blob in frosted glass. Cut out so the planet can be masked and colour-blended in the project's accent (the hub's template grammar, task 1003319); alpha verified with ffmpeg alphaextract (min 0, max 255).",
12
+ "source": ".impeccable/mocks/world-assets/r3-frost-blob-cut.webp",
13
+ "encoded": "ffmpeg libwebp quality 88, alpha kept (yuva420p), 1600px (task 1003337)",
14
+ "use": "the hub's planet in frosted glass: --plate-frost-blob-cut, masked and blended by [data-material=\"frost\"][data-shape=\"blob\"] in cosmos.css"
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "kind": "cutout",
3
+ "model": "nano_banana_2 (requested as nano_banana_pro; the catalog served nano_banana_2)",
4
+ "aspect_ratio": "1:1",
5
+ "resolution": "2k",
6
+ "generated": "2026-08-29T15:37Z",
7
+ "round": 3,
8
+ "task": 1003337,
9
+ "material": "frosted glass",
10
+ "object": "the bongo pair",
11
+ "prompt": "Background removed (Higgsfield image_background_remover) from r3-frost-pair: the bongo pair in frosted glass. Cut out so the planet can be masked and colour-blended in the project's accent (the hub's template grammar, task 1003319); alpha verified with ffmpeg alphaextract (min 0, max 255).",
12
+ "source": ".impeccable/mocks/world-assets/r3-frost-pair-cut.webp",
13
+ "encoded": "ffmpeg libwebp quality 88, alpha kept (yuva420p), 1600px (task 1003337)",
14
+ "use": "the hub's planet in frosted glass: --plate-frost-pair-cut, masked and blended by [data-material=\"frost\"][data-shape=\"pair\"] in cosmos.css"
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "kind": "cutout",
3
+ "model": "nano_banana_2 (requested as nano_banana_pro; the catalog served nano_banana_2)",
4
+ "aspect_ratio": "1:1",
5
+ "resolution": "2k",
6
+ "generated": "2026-08-29T15:37Z",
7
+ "round": 3,
8
+ "task": 1003337,
9
+ "material": "brushed gold",
10
+ "object": "the small bead",
11
+ "prompt": "Background removed (Higgsfield image_background_remover) from r3-gold-bead: the small bead in brushed gold. Cut out so the planet can be masked and colour-blended in the project's accent (the hub's template grammar, task 1003319); alpha verified with ffmpeg alphaextract (min 0, max 255).",
12
+ "source": ".impeccable/mocks/world-assets/r3-gold-bead-cut.webp",
13
+ "encoded": "ffmpeg libwebp quality 88, alpha kept (yuva420p), 1600px (task 1003337)",
14
+ "use": "the hub's planet in brushed gold: --plate-gold-bead-cut, masked and blended by [data-material=\"gold\"][data-shape=\"bead\"] in cosmos.css"
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "kind": "cutout",
3
+ "model": "nano_banana_2 (requested as nano_banana_pro; the catalog served nano_banana_2)",
4
+ "aspect_ratio": "1:1",
5
+ "resolution": "2k",
6
+ "generated": "2026-08-29T15:37Z",
7
+ "round": 3,
8
+ "task": 1003337,
9
+ "material": "brushed gold",
10
+ "object": "the round blob",
11
+ "prompt": "Background removed (Higgsfield image_background_remover) from r3-gold-blob: the round blob in brushed gold. Cut out so the planet can be masked and colour-blended in the project's accent (the hub's template grammar, task 1003319); alpha verified with ffmpeg alphaextract (min 0, max 255).",
12
+ "source": ".impeccable/mocks/world-assets/r3-gold-blob-cut.webp",
13
+ "encoded": "ffmpeg libwebp quality 88, alpha kept (yuva420p), 1600px (task 1003337)",
14
+ "use": "the hub's planet in brushed gold: --plate-gold-blob-cut, masked and blended by [data-material=\"gold\"][data-shape=\"blob\"] in cosmos.css"
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "kind": "cutout",
3
+ "model": "nano_banana_2 (requested as nano_banana_pro; the catalog served nano_banana_2)",
4
+ "aspect_ratio": "1:1",
5
+ "resolution": "2k",
6
+ "generated": "2026-08-29T15:37Z",
7
+ "round": 3,
8
+ "task": 1003337,
9
+ "material": "brushed gold",
10
+ "object": "the bongo pair",
11
+ "prompt": "Background removed (Higgsfield image_background_remover) from r3-gold-pair: the bongo pair in brushed gold. Cut out so the planet can be masked and colour-blended in the project's accent (the hub's template grammar, task 1003319); alpha verified with ffmpeg alphaextract (min 0, max 255).",
12
+ "source": ".impeccable/mocks/world-assets/r3-gold-pair-cut.webp",
13
+ "encoded": "ffmpeg libwebp quality 88, alpha kept (yuva420p), 1600px (task 1003337)",
14
+ "use": "the hub's planet in brushed gold: --plate-gold-pair-cut, masked and blended by [data-material=\"gold\"][data-shape=\"pair\"] in cosmos.css"
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "kind": "cutout",
3
+ "model": "nano_banana_2 (requested as nano_banana_pro; the catalog served nano_banana_2)",
4
+ "aspect_ratio": "1:1",
5
+ "resolution": "2k",
6
+ "generated": "2026-08-29T15:37Z",
7
+ "round": 3,
8
+ "task": 1003337,
9
+ "material": "polished obsidian",
10
+ "object": "the small bead",
11
+ "prompt": "Background removed (Higgsfield image_background_remover) from r3-obsidian-bead: the small bead in polished obsidian. Cut out so the planet can be masked and colour-blended in the project's accent (the hub's template grammar, task 1003319); alpha verified with ffmpeg alphaextract (min 0, max 255).",
12
+ "source": ".impeccable/mocks/world-assets/r3-obsidian-bead-cut.webp",
13
+ "encoded": "ffmpeg libwebp quality 88, alpha kept (yuva420p), 1600px (task 1003337)",
14
+ "use": "the hub's planet in polished obsidian: --plate-obsidian-bead-cut, masked and blended by [data-material=\"obsidian\"][data-shape=\"bead\"] in cosmos.css"
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "kind": "cutout",
3
+ "model": "nano_banana_2 (requested as nano_banana_pro; the catalog served nano_banana_2)",
4
+ "aspect_ratio": "1:1",
5
+ "resolution": "2k",
6
+ "generated": "2026-08-29T15:37Z",
7
+ "round": 3,
8
+ "task": 1003337,
9
+ "material": "polished obsidian",
10
+ "object": "the round blob",
11
+ "prompt": "Background removed (Higgsfield image_background_remover) from r3-obsidian-blob: the round blob in polished obsidian. Cut out so the planet can be masked and colour-blended in the project's accent (the hub's template grammar, task 1003319); alpha verified with ffmpeg alphaextract (min 0, max 255).",
12
+ "source": ".impeccable/mocks/world-assets/r3-obsidian-blob-cut.webp",
13
+ "encoded": "ffmpeg libwebp quality 88, alpha kept (yuva420p), 1600px (task 1003337)",
14
+ "use": "the hub's planet in polished obsidian: --plate-obsidian-blob-cut, masked and blended by [data-material=\"obsidian\"][data-shape=\"blob\"] in cosmos.css"
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "kind": "cutout",
3
+ "model": "nano_banana_2 (requested as nano_banana_pro; the catalog served nano_banana_2)",
4
+ "aspect_ratio": "1:1",
5
+ "resolution": "2k",
6
+ "generated": "2026-08-29T15:37Z",
7
+ "round": 3,
8
+ "task": 1003337,
9
+ "material": "polished obsidian",
10
+ "object": "the bongo pair",
11
+ "prompt": "Background removed (Higgsfield image_background_remover) from r3-obsidian-pair: the bongo pair in polished obsidian. Cut out so the planet can be masked and colour-blended in the project's accent (the hub's template grammar, task 1003319); alpha verified with ffmpeg alphaextract (min 0, max 255).",
12
+ "source": ".impeccable/mocks/world-assets/r3-obsidian-pair-cut.webp",
13
+ "encoded": "ffmpeg libwebp quality 88, alpha kept (yuva420p), 1600px (task 1003337)",
14
+ "use": "the hub's planet in polished obsidian: --plate-obsidian-pair-cut, masked and blended by [data-material=\"obsidian\"][data-shape=\"pair\"] in cosmos.css"
15
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "kind": "backdrop",
3
+ "model": "nano_banana_2 (requested as nano_banana_pro; the catalog served nano_banana_2)",
4
+ "aspect_ratio": "16:9",
5
+ "resolution": "2k",
6
+ "generated": "2026-08-29T15:37Z",
7
+ "round": 3,
8
+ "task": 1003337,
9
+ "prompt": "Deep-space photograph on a pure black ground: a faint wide nebula wash in muted violet and deep blue with a soft bronze warmth along one edge, very sparse pin-sharp stars of varied brightness and size, gentle depth, most of the frame near-black, nothing else, no planets, no ground plane, no horizon, no lens flare, no text, no watermark. Pure black dominates.",
10
+ "source": ".impeccable/mocks/world-assets/r3-space-nebula.jpg",
11
+ "encoded": "ffmpeg JPEG -q:v 4, 2752×1536 (task 1003337)",
12
+ "use": "the field's ground on /projects: --field-plate, `cover`, a 90s transform drift dead under reduced motion (the builder's recommendation; the owner may swap any candidate)"
13
+ }
@@ -1244,16 +1244,23 @@ summary{min-height:24px;padding:3px 0;}
1244
1244
  deriving it from contract tokens is what makes it survive a re-brand. */
1245
1245
  var ORB_ACCENTS = ['var(--sea)', 'var(--terracotta)', 'var(--gold-soft)', 'var(--sun-lit)', 'var(--accent)'];
1246
1246
  var ORB_GLOWS = ORB_ACCENTS.map(function (t) { return 'color-mix(in srgb,' + t + ' 45%,transparent)'; });
1247
- /* THE PLANET TEMPLATES (task 1003319). A project's orb is one of the world's
1248
- three whole chrome objects (blob / pair / bead, on the landing's own crop
1249
- maths) in the project's accent, styled tint (the accent replaces the
1250
- chrome's colour), wash (the accent lights it) or chrome (as rendered). A
1247
+ /* THE PLANET TEMPLATES (task 1003319; the materials, task 1003337). A project's
1248
+ orb is one of the world's three whole objects (blob / pair / bead) in one of
1249
+ four materials (chrome — the first round, named by omission — frost, gold,
1250
+ obsidian) in the project's accent, styled tint (the accent replaces the
1251
+ material's colour), wash (the accent lights it) or plain (as rendered; the
1252
+ chrome material's plain is spelled `chrome`, the first round's name). A
1251
1253
  curated `template` on the catalog row wins; otherwise the same canonical
1252
- walk that assigns the accent assigns the template, and the order here IS
1253
- the walk: consecutive positions change both the shape and the style, and
1254
- nine templates against five accents repeat only every forty-five projects.
1255
- Mirrors platform-identity's PROJECT_TEMPLATES (the DB CHECK). */
1256
- var ORB_TEMPLATES = ['blob-tint', 'pair-wash', 'bead-chrome', 'blob-wash', 'pair-chrome', 'bead-tint', 'blob-chrome', 'pair-tint', 'bead-wash'];
1254
+ walk that assigns the accent assigns the template: ORB_WALK, whose
1255
+ consecutive positions change shape, material AND style, so twelve against
1256
+ five accents repeat only every sixty projects. ORB_TEMPLATES is every valid
1257
+ name — platform-identity's PROJECT_TEMPLATES (the DB CHECK); ORB_WALK mirrors
1258
+ its PROJECT_TEMPLATE_WALK. */
1259
+ var ORB_SHAPES = ['blob', 'pair', 'bead'], ORB_MATERIALS = ['frost', 'gold', 'obsidian'];
1260
+ var ORB_TEMPLATES = [];
1261
+ ORB_SHAPES.forEach(function (s) { ['tint', 'wash', 'chrome'].forEach(function (y) { ORB_TEMPLATES.push(s + '-' + y); }); });
1262
+ ORB_SHAPES.forEach(function (s) { ORB_MATERIALS.forEach(function (m) { ['tint', 'wash', 'plain'].forEach(function (y) { ORB_TEMPLATES.push(s + '-' + m + '-' + y); }); }); });
1263
+ var ORB_WALK = ['blob-tint', 'pair-gold-wash', 'bead-obsidian-plain', 'blob-frost-wash', 'pair-chrome', 'bead-gold-tint', 'blob-obsidian-wash', 'pair-frost-tint', 'bead-wash', 'blob-gold-plain', 'pair-obsidian-tint', 'bead-frost-wash'];
1257
1264
  var catalog = [];
1258
1265
  var mineOrigins = {}; /* normalized host → 'owner' | 'builder' */
1259
1266
  var ownedInstances = []; /* the viewer's OWN provisioning rows (never torn_down) */
@@ -1291,13 +1298,13 @@ summary{min-height:24px;padding:3px 0;}
1291
1298
  .sort(function (a, b) { return String(a.origin).localeCompare(String(b.origin)); })
1292
1299
  .forEach(function (p, i) {
1293
1300
  p._acc = ORB_ACCENTS[i % ORB_ACCENTS.length];
1294
- p._tpl = ORB_TEMPLATES[i % ORB_TEMPLATES.length];
1301
+ p._tpl = ORB_WALK[i % ORB_WALK.length];
1295
1302
  });
1296
1303
  }
1297
1304
  function accentOf(p) { return p._acc || ORB_ACCENTS[0]; }
1298
1305
  /* a curated template wins over the walk; an unknown name is not a choice */
1299
1306
  function templateOf(p) {
1300
- return ORB_TEMPLATES.indexOf(p.template) !== -1 ? p.template : (p._tpl || ORB_TEMPLATES[0]);
1307
+ return ORB_TEMPLATES.indexOf(p.template) !== -1 ? p.template : (p._tpl || ORB_WALK[0]);
1301
1308
  }
1302
1309
  /* the manage cards' accent rule — one project, one colour, in the sky AND
1303
1310
  the list. Shared with orbFor(), which used to carry this hash inline. */
@@ -1308,7 +1315,7 @@ summary{min-height:24px;padding:3px 0;}
1308
1315
  }
1309
1316
  function slugAccent(slug) { return slugHash(slug) % ORB_ACCENTS.length; }
1310
1317
  /* an owned-only entry has no catalog row to carry a template: its slug walks */
1311
- function slugTemplate(slug) { return ORB_TEMPLATES[slugHash(slug) % ORB_TEMPLATES.length]; }
1318
+ function slugTemplate(slug) { return ORB_WALK[slugHash(slug) % ORB_WALK.length]; }
1312
1319
  function relOf(p) {
1313
1320
  /* an instance the viewer provisioned is a server-resolved authority too —
1314
1321
  own-scoped by owner_builder_id, no client inference (direction §2
@@ -1401,11 +1408,13 @@ summary{min-height:24px;padding:3px 0;}
1401
1408
  a.setAttribute('data-generated', '');
1402
1409
  var rel = relOf(p);
1403
1410
  a.setAttribute('data-rel', rel);
1404
- /* the planet: shape and style as data attributes, cosmos.css does the rest */
1411
+ /* the planet: shape, material and style as data attributes (shape[-material]-style;
1412
+ no material = chrome), cosmos.css does the rest */
1405
1413
  var tpl = templateOf(p), tplParts = tpl.split('-');
1406
1414
  a.setAttribute('data-template', tpl);
1407
1415
  a.setAttribute('data-shape', tplParts[0]);
1408
- a.setAttribute('data-style', tplParts[1]);
1416
+ a.setAttribute('data-material', tplParts.length === 3 ? tplParts[1] : 'chrome');
1417
+ a.setAttribute('data-style', tplParts[tplParts.length - 1]);
1409
1418
  var inst = p._inst;
1410
1419
  var href = safeHttpUrl(p.origin);
1411
1420
  if (inst) {
@@ -9,7 +9,7 @@
9
9
  "ignoreControls": [".skip", ".vh"],
10
10
  "states": {
11
11
  "out-map": { "auth": false, "expect": { "expanded": false } },
12
- "in-map": { "auth": true, "expect": { "expanded": false, "count": [["#orbField .worldOrb[data-shape][data-style]", 4]], "attr": [["#selfOrb", "data-template", "pair-chrome"], ["#orbField a.worldOrb[data-origin=\"https://amazonprimea.com\"]", "data-template", "bead-wash"], ["#orbField a.worldOrb[data-origin=\"https://demo.cloudbongos.com\"]", "data-shape", "pair"], ["#orbField a.worldOrb[data-origin=\"https://emersonian-circles.cloudbongos.com\"]", "data-template", "bead-chrome"]] } },
12
+ "in-map": { "auth": true, "expect": { "expanded": false, "count": [["#orbField .worldOrb[data-shape][data-style]", 4]], "attr": [["#selfOrb", "data-template", "pair-chrome"], ["#orbField a.worldOrb[data-origin=\"https://amazonprimea.com\"]", "data-template", "bead-wash"], ["#orbField a.worldOrb[data-origin=\"https://demo.cloudbongos.com\"]", "data-shape", "pair"], ["#orbField a.worldOrb[data-origin=\"https://demo.cloudbongos.com\"]", "data-material", "gold"], ["#orbField a.worldOrb[data-origin=\"https://demo.cloudbongos.com\"]", "data-template", "pair-gold-wash"], ["#orbField a.worldOrb[data-origin=\"https://emersonian-circles.cloudbongos.com\"]", "data-template", "bead-obsidian-plain"]] } },
13
13
  "in-map-projects": { "auth": true, "actions": [["click", "#projTrig"], ["wait", 350]], "expect": { "expanded": true, "open": "#menuProjects" } },
14
14
  "in-map-profile": { "auth": true, "actions": [["click", "#who"], ["wait", 350]], "expect": { "expanded": true, "open": "#menuProfile" } },
15
15
  "in-map-kbd": { "auth": true, "actions": [["focus", "#projTrig"], ["press", "Enter"], ["wait", 350], ["press", "ArrowDown"], ["wait", 120]], "expect": { "expanded": true, "activeText": "Table" } },
package/src/module-api.js CHANGED
@@ -33,7 +33,7 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
33
33
  // there. scripts/gds/bump-version.js still rewrites the literal below; it appends
34
34
  // the entry to that file. Look for a version's history there, not here.
35
35
  // ---------------------------------------------------------------------------
36
- const CORE_VERSION = '1.19.414'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
36
+ const CORE_VERSION = '1.19.416'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
37
37
 
38
38
  // A namespaced logger so a module's log lines are attributable + consistent.
39
39
  // Usage: const log = api.logger('dev-box'); log.info('mounted');
@@ -212,6 +212,10 @@ await t('production code contains no grader-system references', () => {
212
212
  assert.ok(!/hacker worker/i.test(rawSrc), 'memory.js route source must not mention grader workers');
213
213
  });
214
214
 
215
+ // GENERALISED (task 1003343): this check now also exists — for memory.js AND for
216
+ // every other own-scoped surface (/me, builder-settings, platform-identity) — in
217
+ // tests/own_scope_guard.mjs, which runs in the DB-free unit lane. This file is in
218
+ // the INTEGRATION set, so the copy below gates nothing in CI; keep them agreeing.
215
219
  await t('the SELF read endpoints never derive the owner from request input', () => {
216
220
  assert.ok(!/req\.query\.builder/i.test(codeOnly), 'no owner should come from req.query.builder*');
217
221
  assert.ok(!/req\.body\.builder/i.test(codeOnly), 'no owner should come from req.body.builder*');