@bongos/core 1.19.441 → 1.19.443
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 +496 -441
- package/clients/bongos-client/index.d.ts +1 -2
- package/config/branding.neutral.json +1 -1
- package/docs/adr/0218-the-apex-root-web-surface.md +55 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +3 -20
- package/docs/api-reference.md +1 -1
- package/docs/architecture.md +2 -1
- package/docs/copy-inventory.md +363 -359
- package/docs/copy-registry.json +608 -561
- package/docs/design/mocks/hub/mine.html +1 -1
- package/docs/design/mocks/hub/picker.html +1 -1
- package/docs/design/mocks/hub/planets.html +1 -1
- package/docs/design/mocks/hub/v1.html +3 -3
- package/docs/design/mocks/hub/v2.html +2 -2
- package/docs/design/mocks/hub/v3.html +5 -5
- package/docs/design/mocks/hub/wizard.html +1 -1
- package/docs/design/mocks/landing/v1.html +1 -1
- package/docs/design/mocks/landing/v2.html +1 -1
- package/docs/design/mocks/landing/v3.html +1 -1
- package/docs/design/mocks/landing-page-mock.html +3 -3
- package/docs/design/mocks/projects-hub-mock.html +1 -1
- package/docs/design/mocks/projects-hub-variants.html +1 -1
- package/docs/design/mocks/projects-page-mock.html +1 -1
- package/docs/design/projects-hub-direction-v2.md +23 -0
- package/docs/file-map.md +3 -3
- package/docs/module-api-changelog.md +4 -0
- package/docs/modules-contract.md +9 -2
- package/docs/recipes/ui-look-before-you-ship.md +4 -4
- package/modules/copy-desk/tests/copy_flags.mjs +4 -4
- package/modules/platform-identity/routes/projects.js +2 -1
- package/modules/provisioning/provisioning.js +11 -2
- package/modules/provisioning/routes/provisioning.js +10 -5
- package/modules/public-landing/CLAUDE.md +15 -0
- package/modules/public-landing/module.json +15 -0
- package/{public-landing → modules/public-landing/public}/assets/cosmos.css +8 -0
- package/modules/public-landing/public/assets/world/r4-card-chrome.jpg +0 -0
- package/modules/public-landing/public/assets/world/r4-card-chrome.jpg.json +14 -0
- package/modules/public-landing/public/assets/world/r4-card-deepfield.jpg +0 -0
- package/modules/public-landing/public/assets/world/r4-card-deepfield.jpg.json +14 -0
- package/modules/public-landing/public/assets/world/r4-card-dust.jpg +0 -0
- package/modules/public-landing/public/assets/world/r4-card-dust.jpg.json +14 -0
- package/modules/public-landing/public/assets/world/r4-card-nebula.jpg +0 -0
- package/modules/public-landing/public/assets/world/r4-card-nebula.jpg.json +14 -0
- package/{public-landing → modules/public-landing/public}/projects-floor.states.json +28 -1
- package/{public-landing → modules/public-landing/public}/projects.html +101 -1
- package/{public-landing → modules/public-landing/public}/projects.probes.json +6 -0
- package/{public-landing → modules/public-landing/public}/projects.states.json +1 -0
- package/modules/ui-design/kit/fixtures/featured-hub.json +1 -1
- package/modules/ui-design/kit/lib.js +2 -2
- package/modules/ui-design/kit/render.js +1 -1
- package/modules/ui-design/kit/serve.js +32 -7
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/copy-inventory.js +3 -3
- package/scripts/gds/fitness-checks-identity.js +18 -1
- package/scripts/gds/publish-manifest.js +1 -1
- package/src/bongos/module-scope-map.js +9 -7
- package/src/bongos/serve-internal.js +13 -1
- package/src/module-api.js +1 -1
- package/src/module-loader/loader.js +34 -17
- package/src/module-loader/manifest-schema.js +16 -8
- package/src/platform-server.js +50 -18
- package/tests/copy_inventory.mjs +0 -0
- package/tests/cosmos_token_contrast.mjs +6 -6
- package/tests/fitness.mjs +20 -0
- package/tests/landing_legal_pages.mjs +2 -1
- package/tests/landing_page.mjs +7 -5
- package/tests/module-scope-map.mjs +2 -2
- package/tests/module_loader.mjs +4 -1
- package/tests/onboard.mjs +5 -5
- package/tests/projects_hub.mjs +105 -20
- package/tests/projects_hub_pre_uat.mjs +5 -3
- package/tests/provisioning_settings.mjs +49 -4
- package/tests/public_landing_projects.mjs +2 -2
- package/tests/terms_acceptance.mjs +1 -1
- package/tests/ui_design_kit.mjs +3 -3
- package/tests/world_tokens.mjs +3 -2
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_00.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_01.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_02.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_03.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_04.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_05.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_06.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_07.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_08.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_09.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_10.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_11.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_12.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_13.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_14.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_15.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_16.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_17.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_18.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_19.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_20.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_21.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_22.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_23.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_24.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_25.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_26.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_27.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_28.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_29.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_30.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_31.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_32.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_33.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_34.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_35.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_36.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_37.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_38.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_39.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_40.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_41.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_42.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/anim/idle/frame_43.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/verro-medallion.png +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/og-card.jpg +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r2-bongos-chrome-a-cut.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r2-bongos-chrome-a-cut.webp.json +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r2-bongos-chrome-b.jpg +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r2-bongos-chrome-b.jpg.json +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r2-chrome-blob-one-cut.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r2-chrome-blob-one-cut.webp.json +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r2-chrome-blob-one.jpg +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r2-chrome-blob-one.jpg.json +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r2-chrome-blobs-set-cut.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r2-chrome-blobs-set-cut.webp.json +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r2-chrome-blobs-set.jpg +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r2-chrome-blobs-set.jpg.json +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-frost-bead-cut.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-frost-bead-cut.webp.json +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-frost-blob-cut.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-frost-blob-cut.webp.json +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-frost-pair-cut.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-frost-pair-cut.webp.json +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-gold-bead-cut.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-gold-bead-cut.webp.json +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-gold-blob-cut.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-gold-blob-cut.webp.json +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-gold-pair-cut.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-gold-pair-cut.webp.json +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-obsidian-bead-cut.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-obsidian-bead-cut.webp.json +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-obsidian-blob-cut.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-obsidian-blob-cut.webp.json +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-obsidian-pair-cut.webp +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-obsidian-pair-cut.webp.json +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-space-nebula.jpg +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world/r3-space-nebula.jpg.json +0 -0
- /package/{public-landing → modules/public-landing/public}/assets/world.css +0 -0
- /package/{public-landing → modules/public-landing/public}/buddha.png +0 -0
- /package/{public-landing → modules/public-landing/public}/contact.html +0 -0
- /package/{public-landing → modules/public-landing/public}/index.html +0 -0
- /package/{public-landing → modules/public-landing/public}/index.probes.json +0 -0
- /package/{public-landing → modules/public-landing/public}/index.states.json +0 -0
- /package/{public-landing → modules/public-landing/public}/privacy.html +0 -0
- /package/{public-landing → modules/public-landing/public}/projects-empty.states.json +0 -0
- /package/{public-landing → modules/public-landing/public}/terms.html +0 -0
- /package/{public-landing → modules/public-landing/public}/world/assets/clouds/cloud-island-large.png +0 -0
- /package/{public-landing → modules/public-landing/public}/world/assets/clouds/cloud-puff.png +0 -0
- /package/{public-landing → modules/public-landing/public}/world/assets/creatures/creature-whale-cargo.png +0 -0
- /package/{public-landing → modules/public-landing/public}/world/assets/creatures/creature-whale-glide.png +0 -0
- /package/{public-landing → modules/public-landing/public}/world/assets/plates/plate-clouds-near.png +0 -0
- /package/{public-landing → modules/public-landing/public}/world/assets/plates/scene-establishing.png +0 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "card-band",
|
|
3
|
+
"model": "soul_location (Higgsfield) — nano_banana_pro was asked first; its queue had not started a job after 25 minutes, so the round moved to the model that answered",
|
|
4
|
+
"job_id": "debaaba3-6094-4670-9b08-c578fc6e8bb9",
|
|
5
|
+
"aspect_ratio": "5:2",
|
|
6
|
+
"source_aspect_ratio": "21:9",
|
|
7
|
+
"generated": "2026-08-30T17:09Z",
|
|
8
|
+
"round": 4,
|
|
9
|
+
"task": 1003352,
|
|
10
|
+
"prompt": "Ultra-wide deep-field astronomical photograph on a pure black ground: many very small, very distant galaxies and faint stars scattered sparsely across an almost entirely black frame, a few with the faintest cool blue or warm amber tint, no bright object anywhere, extremely quiet and even. No planets, no moons, no large nebula, no ground plane, no horizon, no lens flare, no text, no watermark, no logo, no border. Pure black dominates the frame.",
|
|
11
|
+
"source": ".impeccable/mocks/world-assets/r4-card-deepfield-src.jpg",
|
|
12
|
+
"encoded": "ffmpeg crop=2560:1024:0:24 (21:9 → 5:2, centred), JPEG -q:v 4",
|
|
13
|
+
"use": "the poster card's backdrop on /projects, chosen by an owner as card_backdrop=\"deepfield\": a deep field of very distant galaxies on near-black; the quietest of the four. Dark only (ADR 0204); composed so the left third and the lower corners stay near-black, where the card puts its disc, its name and its two figure callouts."
|
|
14
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "card-band",
|
|
3
|
+
"model": "soul_location (Higgsfield) — nano_banana_pro was asked first; its queue had not started a job after 25 minutes, so the round moved to the model that answered",
|
|
4
|
+
"job_id": "7ec35328-3943-4765-8dd7-8392fb8c48c3",
|
|
5
|
+
"aspect_ratio": "5:2",
|
|
6
|
+
"source_aspect_ratio": "21:9",
|
|
7
|
+
"generated": "2026-08-30T17:09Z",
|
|
8
|
+
"round": 4,
|
|
9
|
+
"task": 1003352,
|
|
10
|
+
"prompt": "Ultra-wide cinematic deep-space photograph on a pure black ground: soft interstellar dust lanes in warm bronze and dark amber sweeping diagonally across the centre-right, very low contrast, a haze rather than a shape, a scatter of tiny faint stars. The left third and both lower corners stay almost pure black and empty. No planets, no moons, no nebula pillars, no ground plane, no horizon, no lens flare, no text, no watermark, no logo, no border. Pure black dominates the frame.",
|
|
11
|
+
"source": ".impeccable/mocks/world-assets/r4-card-dust-src.jpg",
|
|
12
|
+
"encoded": "ffmpeg crop=2560:1024:0:24 (21:9 → 5:2, centred), eq=gamma=1.20, JPEG -q:v 4",
|
|
13
|
+
"use": "the poster card's backdrop on /projects, chosen by an owner as card_backdrop=\"dust\": warm bronze dust lanes, very low contrast; a haze rather than a shape. Dark only (ADR 0204); composed so the left third and the lower corners stay near-black, where the card puts its disc, its name and its two figure callouts."
|
|
14
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"kind": "card-band",
|
|
3
|
+
"model": "soul_location (Higgsfield) — nano_banana_pro was asked first; its queue had not started a job after 25 minutes, so the round moved to the model that answered",
|
|
4
|
+
"job_id": "35ad2c18-aaa3-413c-9fd8-958e5017d77c",
|
|
5
|
+
"aspect_ratio": "5:2",
|
|
6
|
+
"source_aspect_ratio": "21:9",
|
|
7
|
+
"generated": "2026-08-30T17:09Z",
|
|
8
|
+
"round": 4,
|
|
9
|
+
"task": 1003352,
|
|
10
|
+
"prompt": "Ultra-wide cinematic deep-space photograph on a pure black ground: a faint nebula wash in muted violet and deep blue with a soft bronze warmth drifting through the centre-right of the frame, very sparse pin-sharp stars of varied brightness, gentle depth. The left third and both lower corners stay almost pure black and empty. No planets, no moons, no ground plane, no horizon, no lens flare, no text, no watermark, no logo, no border. Pure black dominates the frame.",
|
|
11
|
+
"source": ".impeccable/mocks/world-assets/r4-card-nebula-src.jpg",
|
|
12
|
+
"encoded": "ffmpeg crop=2560:1024:0:24 (21:9 → 5:2, centred), eq=gamma=1.75:saturation=1.30, JPEG -q:v 4 — the render was too dark to read at the card's 560px; a gamma lift raises the wash without crushing the black the disc and the name sit on (contrast would have)",
|
|
13
|
+
"use": "the poster card's backdrop on /projects, chosen by an owner as card_backdrop=\"nebula\": a violet-and-bronze nebula wash, sparse stars — the field's own language in the card's shape. Dark only (ADR 0204); composed so the left third and the lower corners stay near-black, where the card puts its disc, its name and its two figure callouts."
|
|
14
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_": "The all-null floor card (task 1003301) on the hub's own catalog (task 1003345): the same page, the same stub, the catalog aliased to featured-hub.json — the landing's three plus Tidewater, a null row nobody owns. Demo, the landing catalog's floor row, is owned by the signed-in stub, and an owned orb's plain click opens its manage view, never its card. Rendered by name: node modules/ui-design/kit/render.js --states public-landing/projects-floor.states.json",
|
|
2
|
+
"_": "The all-null floor card (task 1003301) on the hub's own catalog (task 1003345): the same page, the same stub, the catalog aliased to featured-hub.json — the landing's three plus Tidewater, a null row nobody owns. Demo, the landing catalog's floor row, is owned by the signed-in stub, and an owned orb's plain click opens its manage view, never its card. Since task 1003352 it also carries the card BACKDROP state: featured-hub gives Off the Boats a card_backdrop, so the owner's plate can be seen standing in front of the project's own picture. Rendered by name: node modules/ui-design/kit/render.js --states public-landing/projects-floor.states.json",
|
|
3
3
|
"page": "/projects",
|
|
4
4
|
"surface": "public-landing",
|
|
5
5
|
"modes": [
|
|
@@ -58,6 +58,33 @@
|
|
|
58
58
|
"#cardListed"
|
|
59
59
|
]
|
|
60
60
|
}
|
|
61
|
+
},
|
|
62
|
+
"card-backdrop": {
|
|
63
|
+
"auth": true,
|
|
64
|
+
"actions": [
|
|
65
|
+
[
|
|
66
|
+
"dispatchClick",
|
|
67
|
+
"#orbField a.worldOrb[data-origin=\"https://amazonprimea.com\"]"
|
|
68
|
+
],
|
|
69
|
+
[
|
|
70
|
+
"wait",
|
|
71
|
+
600
|
|
72
|
+
]
|
|
73
|
+
],
|
|
74
|
+
"expect": {
|
|
75
|
+
"dialog": true,
|
|
76
|
+
"visible": [
|
|
77
|
+
"#cardArt",
|
|
78
|
+
"#cardOpen"
|
|
79
|
+
],
|
|
80
|
+
"attr": [
|
|
81
|
+
[
|
|
82
|
+
"#cardArt",
|
|
83
|
+
"src",
|
|
84
|
+
"/assets/world/r4-card-nebula.jpg"
|
|
85
|
+
]
|
|
86
|
+
]
|
|
87
|
+
}
|
|
61
88
|
}
|
|
62
89
|
}
|
|
63
90
|
}
|
|
@@ -769,6 +769,18 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
769
769
|
</details>
|
|
770
770
|
</div>
|
|
771
771
|
|
|
772
|
+
<!-- its card (task 1003352): the poster card's backdrop, a plate the
|
|
773
|
+
platform drew. Beside the planet, on the same settings channel, and
|
|
774
|
+
like it: nothing is pushed to the project and nothing restarts. -->
|
|
775
|
+
<div class="card mSec" id="mCard">
|
|
776
|
+
<h2>Its card</h2>
|
|
777
|
+
<div id="mCardPick"><p class="finePrint">Reading the project…</p></div>
|
|
778
|
+
<details class="finePrint" id="mCardWhat">
|
|
779
|
+
<summary>What the choice changes</summary>
|
|
780
|
+
<p>The backdrop behind this project’s name on its card — the panel that opens when someone picks it out of the map. Four plates the platform drew, or the project’s own picture if you have given it one. Saving here changes nothing on the project itself: no restart, nothing to apply.</p>
|
|
781
|
+
</details>
|
|
782
|
+
</div>
|
|
783
|
+
|
|
772
784
|
<!-- live since task 1003042: the teardown control, for a project in ANY
|
|
773
785
|
state that still holds something. The server used to refuse a
|
|
774
786
|
'requested' or an 'error' project outright — and force-teardown reused
|
|
@@ -1820,7 +1832,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1820
1832
|
var href = p.isSelf ? '/' : safeHttpUrl(p.origin);
|
|
1821
1833
|
/* the art: catalog text an Archon typed, scheme-checked server-side and
|
|
1822
1834
|
again here before it becomes a src */
|
|
1823
|
-
cardArt(p.isSelf ? '' : safeHttpUrl(p.art_url));
|
|
1835
|
+
cardArt(p.isSelf ? '' : (backdropPlate(p.card_backdrop) || safeHttpUrl(p.art_url)));
|
|
1824
1836
|
$('cardDot').style.setProperty('--acc', p.isSelf ? 'var(--accent)' : accentOf(p));
|
|
1825
1837
|
$('cardName').textContent = p.name || '';
|
|
1826
1838
|
$('cardTag').textContent = p.tagline || '';
|
|
@@ -4851,6 +4863,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
4851
4863
|
/* its planet (task 1003347) reads off the row — fetch-free too; a pick in
|
|
4852
4864
|
progress survives the tick because only a different project re-renders it */
|
|
4853
4865
|
renderPick(inst);
|
|
4866
|
+
renderCardPick(inst);
|
|
4854
4867
|
/* the page names its project — the heading holds #mName; and in the tab */
|
|
4855
4868
|
document.title = (inst.slug ? inst.slug + ' — ' : '') + BASE_TITLE;
|
|
4856
4869
|
/* the card's own story, on the project's own page (task 1003232): the
|
|
@@ -5432,6 +5445,20 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5432
5445
|
var PICK_STYLES = [['tint', 'tinted'], ['wash', 'washed'], ['plain', 'plain']];
|
|
5433
5446
|
var PICK_ACCENTS = [['sea', 'sea'], ['terracotta', 'terracotta'], ['gold-soft', 'soft gold'], ['sun-lit', 'sunlit'], ['accent', 'the accent']];
|
|
5434
5447
|
var PICK_FIELDS = { mPickShape: 'shape', mPickMaterial: 'material', mPickStyle: 'style', mPickAccent: 'accent' };
|
|
5448
|
+
/* ── the card's backdrop (task 1003352): the same KIND of key as the planet —
|
|
5449
|
+
a closed list of platform-drawn plates, saved on the same settings channel,
|
|
5450
|
+
pushed to no instance and restarting nothing. '' is "its own picture": the
|
|
5451
|
+
card falls back to the project's art_url, which is all it read before. The
|
|
5452
|
+
plates are the world's own material (DESIGN.md, round 4), dark only. ── */
|
|
5453
|
+
var CARD_BACKDROPS = [['', 'its own picture'], ['nebula', 'nebula'], ['dust', 'dust'], ['deepfield', 'deep field'], ['chrome', 'chrome']];
|
|
5454
|
+
var CARD_PLATES = { nebula: 'r4-card-nebula.jpg', dust: 'r4-card-dust.jpg', deepfield: 'r4-card-deepfield.jpg', chrome: 'r4-card-chrome.jpg' };
|
|
5455
|
+
var CARD_WORDS = { nebula: 'a violet and bronze nebula wash', dust: 'warm bronze dust lanes',
|
|
5456
|
+
deepfield: 'a deep field of distant galaxies', chrome: 'a sweep of liquid chrome' };
|
|
5457
|
+
/* a key → the plate's path, or '' for anything this platform does not draw
|
|
5458
|
+
(an unknown key from an older row must never become a broken src) */
|
|
5459
|
+
function backdropPlate(key) {
|
|
5460
|
+
return CARD_PLATES[key] ? '/assets/world/' + CARD_PLATES[key] : '';
|
|
5461
|
+
}
|
|
5435
5462
|
var mPick = null; /* the pending choice: { shape, material, style, accent } */
|
|
5436
5463
|
var mPickFor = null; /* which project it belongs to — a different one starts afresh */
|
|
5437
5464
|
var mPickTouched = false; /* the owner changed something since the seed — a late catalog must not undo it */
|
|
@@ -5540,6 +5567,79 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5540
5567
|
});
|
|
5541
5568
|
}
|
|
5542
5569
|
|
|
5570
|
+
/* ── its card (task 1003352; the owner's ask on the planet round: "this could
|
|
5571
|
+
also house project card backdrop customization"). Its own section BESIDE the
|
|
5572
|
+
planet rather than inside it, so each heading stays true — one is how the
|
|
5573
|
+
project is drawn everywhere, the other is one surface's backdrop. Same
|
|
5574
|
+
mechanics as the planet, key for key: reads off the row, saves on the
|
|
5575
|
+
settings PATCH, and the reply is a receipt the card repaints from. ── */
|
|
5576
|
+
var mCard = null; /* the pending choice: the backdrop key, or '' */
|
|
5577
|
+
var mCardFor = null; /* which project it belongs to */
|
|
5578
|
+
function renderCardPick(inst) {
|
|
5579
|
+
if (mCard === null || mCardFor !== inst.id) { mCard = inst.card_backdrop || ''; mCardFor = inst.id; }
|
|
5580
|
+
var plate = backdropPlate(mCard);
|
|
5581
|
+
$('mCardPick').innerHTML =
|
|
5582
|
+
'<div class="pick">' +
|
|
5583
|
+
'<div class="pickPreview"><span class="cardBand"' +
|
|
5584
|
+
(plate ? ' style="background-image:url(' + esc(plate) + ')"' : ' data-own') + ' aria-hidden="true"></span>' +
|
|
5585
|
+
/* the line speaks for the ROW, so a pick that is ahead of the row says
|
|
5586
|
+
so — the preview above it is already showing the plate */
|
|
5587
|
+
'<p class="pickWords">' + esc(CARD_WORDS[mCard] || 'the project’s own picture, when it has one') +
|
|
5588
|
+
'<small>' + (mCard !== (inst.card_backdrop || '') ? 'not saved yet'
|
|
5589
|
+
: inst.card_backdrop ? 'your choice' : 'nothing chosen') + '</small></p></div>' +
|
|
5590
|
+
'<div class="pickBands">' + pickBand('Backdrop', 'mCardBackdrop', CARD_BACKDROPS, mCard) + '</div>' +
|
|
5591
|
+
'</div>' +
|
|
5592
|
+
'<div class="actions"><button type="button" class="btn small" data-card-save>Save its card</button></div>' +
|
|
5593
|
+
'<div class="projMsg" id="mCardMsg" role="status" aria-live="polite"></div>';
|
|
5594
|
+
}
|
|
5595
|
+
$('mCardPick').addEventListener('change', function (ev) {
|
|
5596
|
+
var t = ev.target;
|
|
5597
|
+
if (!t || t.type !== 'radio' || t.name !== 'mCardBackdrop' || !manageInst) return;
|
|
5598
|
+
mCard = t.value;
|
|
5599
|
+
renderCardPick(manageInst); /* the preview follows the pick, keeping the cell's focus */
|
|
5600
|
+
var again = document.querySelector('#mCardPick input[value="' + t.value + '"]');
|
|
5601
|
+
if (again) again.focus({ preventScroll: true });
|
|
5602
|
+
});
|
|
5603
|
+
$('mCardPick').addEventListener('click', function (ev) {
|
|
5604
|
+
var t = ev.target.closest && ev.target.closest('button');
|
|
5605
|
+
if (t && t.hasAttribute('data-card-save')) doCardSave();
|
|
5606
|
+
});
|
|
5607
|
+
function doCardSave() {
|
|
5608
|
+
if (!manageInst || mCard === null) return;
|
|
5609
|
+
var box = $('mCardMsg');
|
|
5610
|
+
document.querySelectorAll('#mCardPick .actions button').forEach(function (b) { b.disabled = true; });
|
|
5611
|
+
box.className = 'projMsg'; box.textContent = 'Saving…';
|
|
5612
|
+
fetch(API + '/provisioning/instances/' + encodeURIComponent(manageId) + '/settings', {
|
|
5613
|
+
method: 'PATCH', credentials: 'same-origin', headers: { 'Content-Type': 'application/json' },
|
|
5614
|
+
body: JSON.stringify({ card_backdrop: mCard || null })
|
|
5615
|
+
}).then(function (res) {
|
|
5616
|
+
return jsonOrEmpty(res).then(function (data) {
|
|
5617
|
+
if (res.ok) {
|
|
5618
|
+
/* the row now holds the choice; the card reads it from there */
|
|
5619
|
+
manageInst.card_backdrop = (data.planet || {}).card_backdrop || null;
|
|
5620
|
+
var row = byId[manageInst.id];
|
|
5621
|
+
if (row) row.card_backdrop = manageInst.card_backdrop;
|
|
5622
|
+
mCard = null;
|
|
5623
|
+
renderCardPick(manageInst);
|
|
5624
|
+
var done = $('mCardMsg');
|
|
5625
|
+
done.className = 'projMsg ok';
|
|
5626
|
+
done.textContent = (data.push && data.push.message) || 'Saved.';
|
|
5627
|
+
loadCatalog();
|
|
5628
|
+
return;
|
|
5629
|
+
}
|
|
5630
|
+
var e = apiError(data, res.status);
|
|
5631
|
+
if (res.status === 401) { box.className = 'projMsg err'; box.innerHTML = signInAgain(); return; }
|
|
5632
|
+
box.className = 'projMsg err';
|
|
5633
|
+
box.textContent = e.message || 'The choice could not be saved — try again in a moment.';
|
|
5634
|
+
});
|
|
5635
|
+
}).catch(function () {
|
|
5636
|
+
box.className = 'projMsg err';
|
|
5637
|
+
box.textContent = 'Couldn’t reach the platform — check your connection and try again.';
|
|
5638
|
+
}).then(function () {
|
|
5639
|
+
document.querySelectorAll('#mCardPick .actions button').forEach(function (b) { b.disabled = false; });
|
|
5640
|
+
});
|
|
5641
|
+
}
|
|
5642
|
+
|
|
5543
5643
|
/* ── how people join (task 1003044): the joinability toggle — the visibility
|
|
5544
5644
|
section's shape, key for key: the CHOICE from the platform's settings row,
|
|
5545
5645
|
what the project RUNS from its own manifest (brand.project.joinability),
|
|
@@ -131,6 +131,12 @@
|
|
|
131
131
|
{ "name": "manage flows: try again from the snagged project's own page re-files it too", "auth": true, "width": 1440, "url": "/projects?view=manage&id=103",
|
|
132
132
|
"steps": [["wait",900],["click","[data-m-retry]"],["wait",900]],
|
|
133
133
|
"expect": {"text":[["#mMsg","Queued. The platform picks it up from here."]],"hasClass":[["#mMsg","ok",true]],"hidden":["#mAddress"]} },
|
|
134
|
+
{ "name": "manage flows: its card (task 1003352) — pick the nebula plate; save; the receipt lands and the preview takes the plate", "auth": true, "width": 1440, "url": "/projects?view=manage&id=101",
|
|
135
|
+
"steps": [["wait",900],["check","#mCardPick input[name=\"mCardBackdrop\"][value=\"nebula\"]"],["wait",150],["click","[data-card-save]"],["wait",700]],
|
|
136
|
+
"expect": {"text":[["#mCardMsg","Saved. The sky, your list and the card draw it this way from here; nothing on your project restarts for it."]],"hasClass":[["#mCardMsg","ok",true]],"count":[["#mCardPick .cardBand[data-own]",0]]} },
|
|
137
|
+
{ "name": "manage flows: its card — handing it back to the platform restores the project's own picture", "continue": true,
|
|
138
|
+
"steps": [["check","#mCardPick input[name=\"mCardBackdrop\"][value=\"\"]"],["wait",150],["click","[data-card-save]"],["wait",700]],
|
|
139
|
+
"expect": {"hasClass":[["#mCardMsg","ok",true]],"count":[["#mCardPick .cardBand[data-own]",1]]} },
|
|
134
140
|
{ "name": "manage flows: how people join — pick a cell, save, the receipt lands under the choice", "auth": true, "width": 1440, "url": "/projects?view=manage&id=101",
|
|
135
141
|
"steps": [["wait",900],["check","input[name=\"mJoinPick\"][value=\"apply\"]"],["click","[data-join-save]"],["wait",600]],
|
|
136
142
|
"expect": {"text":[["#mJoinMsg","Saved. The project restarts to apply it."],["#mName","demo"]],"checked":"input[name=\"mJoinPick\"][value=\"apply\"]","visible":["#mOrb .worldOrb","#mFacts .ledger .k"]} },
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"in-manage": { "auth": true, "url": "/projects?view=manage&id=101", "expect": { "visible": ["#mMain", "#mChip .chip", "#mOrb .worldOrb", "#mFacts .ledger .k", "#mActs", "#mAddress"], "hidden": ["#qWrap", "#mGate", "#mMissing", "#mErr"], "text": [["#mName", "demo"], ["#mTitle", "demo"]] } },
|
|
22
22
|
"in-manage-address": { "auth": true, "url": "/projects?view=manage&id=101", "expect": { "visible": ["#mAddress", "#mAddress h2", "#mDom .plain", "#mDomName", "#mDom [data-m-attach]", "#mDomWhat summary"], "count": [["#mDom .attach input[type=text]", 1], ["#mDom .attach button", 1]] } },
|
|
23
23
|
"in-manage-planet": { "auth": true, "url": "/projects?view=manage&id=101", "expect": { "visible": ["#mPlanetPick", "#mPick .pick .pickPreview .worldOrb .disc", "#mPick [data-pick-save]", "#mPick [data-pick-reset]", "#mPickWhat summary"], "count": [["#mPick .pickCells[role=\"radiogroup\"]", 3], ["#mPick .pickCell", 10], ["#mPick .pickCell.on", 3], ["#mPick .pickSw", 5], ["#mPick .pickSw.on", 1]], "attr": [["#mPick .pickPreview .worldOrb", "data-template", "pair-obsidian-tint"], ["#mOrb .worldOrb", "data-template", "pair-obsidian-tint"]], "checked": "#mPick input[name=\"mPickAccent\"][value=\"sea\"]", "text": [["#mPlanet .v small", "your choice."]] } },
|
|
24
|
+
"in-manage-card": { "auth": true, "url": "/projects?view=manage&id=101", "expect": { "visible": ["#mCard", "#mCardPick .pick .pickPreview .cardBand", "#mCardPick [data-card-save]", "#mCardWhat summary"], "count": [["#mCardPick .pickCells[role=\"radiogroup\"]", 1], ["#mCardPick .pickCell", 5], ["#mCardPick .pickCell.on", 1]], "checked": "#mCardPick input[name=\"mCardBackdrop\"][value=\"\"]", "attr": [["#mCardPick .cardBand", "data-own", ""]] } },
|
|
24
25
|
"in-manage-snag": { "auth": true, "url": "/projects?view=manage&id=103", "expect": { "visible": ["#mMain", "#mErr", "#mActs"], "hidden": ["#mAddress"], "text": [["#mName", "tidepool"]] } },
|
|
25
26
|
"in-manage-gone": { "auth": true, "url": "/projects?view=manage&id=104", "expect": { "visible": ["#mMain", "#mActs"], "hidden": ["#mErr", "#mAddress"], "text": [["#mName", "old-lantern"]] } },
|
|
26
27
|
"in-manage-missing": { "auth": true, "url": "/projects?view=manage&id=999", "expect": { "visible": ["#mMissing"], "hidden": ["#mMain", "#mGate"] } },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"_":"The hub's catalog (task 1003345): the landing's featured.json plus Tidewater, an all-null row nobody owns — the card-floor state opens ITS card, because Demo is owned by the signed-in stub (provisioning-instances.json) and an owned orb's plain click opens the manage view, never the card. Served under the name \"featured\" by projects.states.json's stub.fixtures alias; the landing keeps featured.json.","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","planet_template":null,"planet_accent":null,"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","planet_template":"pair-obsidian-tint","planet_accent":"sea","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,"planet_template":null,"planet_accent":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},{"origin":"https://tidewater.cloudbongos.com","name":"Tidewater","tagline":"A project that has declared nothing yet.","art_url":null,"type":null,"template":null,"planet_template":null,"planet_accent":null,"created_at":"2026-07-20T08:00:00.000Z","builders":null,"tags":null,"owner":null,"owner_avatar":null,"faces":null,"shipped_30d":null,"shipped_30d_capped":null,"hall_origin":null}]}
|
|
1
|
+
{"_":"The hub's catalog (task 1003345): the landing's featured.json plus Tidewater, an all-null row nobody owns — the card-floor state opens ITS card, because Demo is owned by the signed-in stub (provisioning-instances.json) and an owned orb's plain click opens the manage view, never the card. Served under the name \"featured\" by projects.states.json's stub.fixtures alias; the landing keeps featured.json.","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","planet_template":null,"planet_accent":null,"card_backdrop":"nebula","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","planet_template":"pair-obsidian-tint","planet_accent":"sea","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,"planet_template":null,"planet_accent":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},{"origin":"https://tidewater.cloudbongos.com","name":"Tidewater","tagline":"A project that has declared nothing yet.","art_url":null,"type":null,"template":null,"planet_template":null,"planet_accent":null,"created_at":"2026-07-20T08:00:00.000Z","builders":null,"tags":null,"owner":null,"owner_avatar":null,"faces":null,"shipped_30d":null,"shipped_30d_capped":null,"hall_origin":null}]}
|
|
@@ -53,7 +53,7 @@ function normalizePageArg(page) {
|
|
|
53
53
|
if (typeof page === 'string' && /^[A-Za-z]:[\\/]/.test(page) && /[\\/]$/.test(page)) return '/';
|
|
54
54
|
return page;
|
|
55
55
|
}
|
|
56
|
-
// A surface is a directory of the checkout
|
|
56
|
+
// A surface is a directory of the checkout ('modules/<key>/public') or a module KEY
|
|
57
57
|
// or a module KEY whose manifest declares a webSurfaces dir. Nothing is hardcoded.
|
|
58
58
|
function resolveSurfaceDir(surface, root = REPO_ROOT) {
|
|
59
59
|
if (!surface) return null;
|
|
@@ -234,7 +234,7 @@ function reducedMotionProblems({ running = [], frames = 0 } = {}) {
|
|
|
234
234
|
}
|
|
235
235
|
|
|
236
236
|
// ── the states file ───────────────────────────────────────────────────────────
|
|
237
|
-
// { "page": "/projects", "surface"?: "public-landing", "stub"?: { prefix?, child? },
|
|
237
|
+
// { "page": "/projects", "surface"?: "public-landing" (a module key), "stub"?: { prefix?, child? },
|
|
238
238
|
// "modes"?: ["dark" | "light", …] (the modes the page renders; default both — a page that pins
|
|
239
239
|
// one mode declares it here), "modeQuery"?: "mode" | false, "ignoreRequests"?: [regex strings], "ignoreConsole"?: [...],
|
|
240
240
|
// stub.fixtures?: { "<fixture name>": "<other fixture file>" } (serve another file under a name — an empty list, a
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
// through two stubs this file starts itself (signed out, signed in), then a
|
|
9
9
|
// reduced-motion pass per mode. Writes the shots + a JSON report; exits non-zero on any problem.
|
|
10
10
|
//
|
|
11
|
-
// node modules/ui-design/kit/render.js --page /projects (public-landing
|
|
11
|
+
// node modules/ui-design/kit/render.js --page /projects (surface defaults to the public-landing module key)
|
|
12
12
|
// node modules/ui-design/kit/render.js --page /builders/ranks --surface hall-ui
|
|
13
13
|
// node modules/ui-design/kit/render.js --states <file> (explicit file)
|
|
14
14
|
// --modes dark,light --sizes 1440,390,320 --only a,b --out <dir>
|
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
// fixtures/ so a page boots with real-shaped data and no database. Serves docs/
|
|
11
11
|
// raw so a mock under docs/design/mocks/ renders with its relative links.
|
|
12
12
|
//
|
|
13
|
-
// node modules/ui-design/kit/serve.js -> http://127.0.0.1:3412/ (public-landing)
|
|
14
|
-
// SURFACE=<dir or module key> a checkout dir ("
|
|
15
|
-
//
|
|
13
|
+
// node modules/ui-design/kit/serve.js -> http://127.0.0.1:3412/ (the public-landing module)
|
|
14
|
+
// SURFACE=<dir or module key> a checkout dir ("modules/hall-ui/public") or a module
|
|
15
|
+
// key ("public-landing") whose manifest declares a webSurfaces dir
|
|
16
16
|
// PREFIX=/builders/ the URL prefix the surface's pages + assets are served under
|
|
17
17
|
// STUB_REPO=<checkout> the checkout to serve (default: the one this file sits in)
|
|
18
18
|
// STUB_PORT=<n> default 3412 (0 = any free port; the port is printed)
|
|
@@ -137,15 +137,40 @@ const ASKED = new Map();
|
|
|
137
137
|
// the run — the page repaints from the row it re-reads, so the row must carry the choice; a
|
|
138
138
|
// PATCH with only planet keys gets the route's no-restart receipt, a policy key the push
|
|
139
139
|
const PLANETS = new Map();
|
|
140
|
-
const PLANET_KEYS = ['planet_template', 'planet_accent'];
|
|
141
|
-
const NO_PLANET = { planet_template: null, planet_accent: null };
|
|
140
|
+
const PLANET_KEYS = ['planet_template', 'planet_accent', 'card_backdrop'];
|
|
141
|
+
const NO_PLANET = { planet_template: null, planet_accent: null, card_backdrop: null };
|
|
142
142
|
function readBody(req, cb) {
|
|
143
143
|
let d = '';
|
|
144
144
|
req.on('data', (c) => { d += c; });
|
|
145
145
|
req.on('end', () => { let b = {}; try { b = JSON.parse(d || '{}'); } catch { b = {}; } cb(b); });
|
|
146
146
|
}
|
|
147
|
+
// The real route validates STRICTLY against its vocabularies (an undeclared key
|
|
148
|
+
// or an off-vocabulary value is a 400). The stub mirrored none of that, so the
|
|
149
|
+
// probes stayed green on a body the server would have refused — which is how a
|
|
150
|
+
// missing card_backdrop entry in the route's schema shipped past a full probe
|
|
151
|
+
// run (task 1003352, grade round 2). The lists are copied on purpose: a kit
|
|
152
|
+
// module may not import core. They are small, closed, and pinned by a test.
|
|
153
|
+
const STUB_VOCAB = {
|
|
154
|
+
platform_visibility: ['public', 'gated'],
|
|
155
|
+
joinability: ['open', 'apply', 'invite_only'],
|
|
156
|
+
planet_template: null, // the template set is long; shape is checked, not membership
|
|
157
|
+
planet_accent: ['sea', 'terracotta', 'gold-soft', 'sun-lit', 'accent'],
|
|
158
|
+
card_backdrop: ['nebula', 'dust', 'deepfield', 'chrome'],
|
|
159
|
+
};
|
|
160
|
+
function settingsRejection(body) {
|
|
161
|
+
for (const [key, value] of Object.entries(body)) {
|
|
162
|
+
if (!(key in STUB_VOCAB)) return `unknown field: ${key}`;
|
|
163
|
+
if (value === null) continue; // null hands a pick back
|
|
164
|
+
if (typeof value !== 'string') return `${key} must be a string`;
|
|
165
|
+
const allowed = STUB_VOCAB[key];
|
|
166
|
+
if (allowed && !allowed.includes(value)) return `${key} is not one of: ${allowed.join(', ')}`;
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
147
170
|
function settingsPatch(res, id, body) {
|
|
148
|
-
|
|
171
|
+
const bad = settingsRejection(body);
|
|
172
|
+
if (bad) return json(res, 400, { error: { code: 'validation_failed', message: bad } });
|
|
173
|
+
if (PLANET_KEYS.some((k) => k in body)) PLANETS.set(String(id), Object.fromEntries(PLANET_KEYS.map((k) => [k, body[k] || null])));
|
|
149
174
|
const policy = ['platform_visibility', 'joinability'].some((k) => k in body);
|
|
150
175
|
const push = policy ? FLOW_RECEIPTS.settings.push
|
|
151
176
|
: { queued: false, restart: false, message: 'Saved. The sky, your list and the card draw it this way from here; nothing on your project restarts for it.' };
|
|
@@ -154,7 +179,7 @@ function settingsPatch(res, id, body) {
|
|
|
154
179
|
// the row carries its planet: a saved pick for the run, else the fixture's own (or null)
|
|
155
180
|
function withPlanet(inst) {
|
|
156
181
|
const saved = PLANETS.get(String(inst.id));
|
|
157
|
-
return { ...inst,
|
|
182
|
+
return { ...inst, ...Object.fromEntries(PLANET_KEYS.map((k) => [k, inst[k] || null])), ...(saved || {}) };
|
|
158
183
|
}
|
|
159
184
|
function flowReceipt(rest, method) {
|
|
160
185
|
if (method === 'POST' && rest === '/provisioning/instances') return FLOW_RECEIPTS.provision;
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.443",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.443",
|
|
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.
|
|
3
|
+
"version": "1.19.443",
|
|
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",
|
|
@@ -54,12 +54,12 @@ const REPORT_REL = 'docs/copy-inventory.md';
|
|
|
54
54
|
const SURFACES = [
|
|
55
55
|
{ id: 'builders-hall', kind: 'dir', at: 'modules/hall-ui/public', exts: ['.html', '.js'] },
|
|
56
56
|
{ id: 'status-dashboard', kind: 'dir', at: 'modules/status-ui/public', exts: ['.html', '.js'] },
|
|
57
|
-
// THE LANDING SURFACE IS public-landing/, NOT src/platform-server.js. The first
|
|
57
|
+
// THE LANDING SURFACE IS modules/public-landing/public/, NOT src/platform-server.js. The first
|
|
58
58
|
// cut pointed here at the server file, which only fs.readFileSync's these
|
|
59
59
|
// templates — so its JS patterns matched a handful of incidental literals and the
|
|
60
60
|
// page's ACTUAL copy (hero, meta/og text, every section) was silently absent
|
|
61
61
|
// while the registry reported a number. Caught by the grader on task 1003112.
|
|
62
|
-
{ id: 'landing', kind: 'dir', at: 'public-landing', exts: ['.html'] },
|
|
62
|
+
{ id: 'landing', kind: 'dir', at: 'modules/public-landing/public', exts: ['.html'] },
|
|
63
63
|
];
|
|
64
64
|
|
|
65
65
|
// Attributes whose value is COPY a person reads (not wiring). The matchers are
|
|
@@ -70,7 +70,7 @@ const COPY_ATTR_RES = COPY_ATTRS.map((attr) => ({ attr, re: new RegExp(`\\b${att
|
|
|
70
70
|
// What this tool knowingly does not see. Printed in the report — an inventory that
|
|
71
71
|
// implies completeness it does not have is worse than a short one.
|
|
72
72
|
const BLIND_SPOTS = [
|
|
73
|
-
'Landing-page transforms: `public-landing/*.html` is scanned as authored, but the server rewrites it before a visitor sees it — `{{token}}` placeholders are filled from the branding pack, and `<!--apex-only:start--> … <!--apex-only:end-->` sections are STRIPPED on a child instance (src/platform-server.js `landingPage()`). So a row here may be copy that this instance never renders, and a rendered sentence may differ from its authored form.',
|
|
73
|
+
'Landing-page transforms: `modules/public-landing/public/*.html` is scanned as authored, but the server rewrites it before a visitor sees it — `{{token}}` placeholders are filled from the branding pack, and `<!--apex-only:start--> … <!--apex-only:end-->` sections are STRIPPED on a child instance (src/platform-server.js `landingPage()`). So a row here may be copy that this instance never renders, and a rendered sentence may differ from its authored form.',
|
|
74
74
|
'Server-side messages: route failures (`res.fail` reasons), validation text and API error copy live in server JS and are not walked here. A person reads them, so widening to them is a deliberate later step, not an oversight to quietly fix.',
|
|
75
75
|
'Skill and playbook prose: `.claude/skills/**/SKILL.md` is read by agents and by builders and is arguably the largest body of project voice. Out of scope on purpose — it is not UI, and mixing it in would drown the UI signal.',
|
|
76
76
|
'Discord copy: outbound broadcast text is composed in `modules/discord/*.js` at send time. User-facing, but not part of any UI surface.',
|
|
@@ -141,11 +141,27 @@ const IDENTITY_SCAN_SKIP_DIRS = new Set(['node_modules', '.git', 'generated', 'i
|
|
|
141
141
|
// silently hid ~330 provision.js lines from this gate). Re-exported for the tests.
|
|
142
142
|
const { stripCommentsForIdentity } = require('./identity-strip');
|
|
143
143
|
|
|
144
|
+
// An APEX-ONLY region is content the server DELETES on every instance that is not
|
|
145
|
+
// the hub (`stripApexOnly`, src/platform-server.js — ADR 0152; the front door became
|
|
146
|
+
// a module in ADR 0218, which is what first brought these pages under this gate).
|
|
147
|
+
// This check exists so "a non-OTB instance renders ITS OWN brand"; text inside these
|
|
148
|
+
// sentinels never reaches a non-hub instance at all, so it cannot produce that
|
|
149
|
+
// failure. The hub's own landing legitimately NAMES the projects hosted on it, and
|
|
150
|
+
// that catalog is exactly what the sentinels wrap.
|
|
151
|
+
//
|
|
152
|
+
// BLANKED, not removed, so a violation's reported line number still points at the
|
|
153
|
+
// real line. Applied BEFORE the comment strip, which would otherwise eat the
|
|
154
|
+
// sentinels (they are HTML comments) and take the region's boundaries with them.
|
|
155
|
+
const APEX_ONLY_REGION = /<!--apex-only:start-->[\s\S]*?<!--apex-only:end-->/g;
|
|
156
|
+
function blankApexOnly(text) {
|
|
157
|
+
return String(text).replace(APEX_ONLY_REGION, (m) => m.replace(/[^\n]/g, ' '));
|
|
158
|
+
}
|
|
159
|
+
|
|
144
160
|
// Pure: scan one file's comment-stripped source for a hardcoded host-brand literal.
|
|
145
161
|
// Returns violation strings. Exported for the "actually catches a re-hardcode" test.
|
|
146
162
|
function scanPromptIdentity(relFile, text, ext = path.extname(relFile)) {
|
|
147
163
|
const violations = [];
|
|
148
|
-
stripCommentsForIdentity(text, ext).split('\n').forEach((raw, i) => {
|
|
164
|
+
stripCommentsForIdentity(blankApexOnly(text), ext).split('\n').forEach((raw, i) => {
|
|
149
165
|
if (!raw.trim()) return;
|
|
150
166
|
for (const re of PROMPT_IDENTITY_LITERALS) {
|
|
151
167
|
const m = raw.match(re);
|
|
@@ -318,4 +334,5 @@ module.exports = {
|
|
|
318
334
|
collectIdentityFiles,
|
|
319
335
|
scanPromptIdentity,
|
|
320
336
|
stripCommentsForIdentity,
|
|
337
|
+
blankApexOnly, // ADR 0218 — apex-only regions never reach a child instance (tested)
|
|
321
338
|
};
|
|
@@ -66,6 +66,7 @@ const PUBLISH_ALLOWLIST = [
|
|
|
66
66
|
'modules/ui-design/', // the design contract + its adapters/CLIs + the /design playbook (task 1003324, ADR 0197). default:true, so it MUST be here: a released core without it would materialize skills (design, design-sync, figma-design-sync) whose CLIs and adapters do not exist.
|
|
67
67
|
'modules/hall-ui/',
|
|
68
68
|
'modules/status-ui/',
|
|
69
|
+
'modules/public-landing/', // the front door (task 1003322, ADR 0218) — the repo-root public-landing/ entry this replaces was always here, and for the same reason: default:true, and it is the only thing an instance serves at its apex root. Omit it and a released core answers the game-less 404 tail on its own home page.
|
|
69
70
|
'modules/dev-box/',
|
|
70
71
|
'modules/discord/',
|
|
71
72
|
'modules/provisioning/', // instance-hosting control plane — CORE like its sibling dev-box (same do-api/cf-tunnel toolchain, all core). Reclassified IN so the core CLI scripts/gds/provision.js resolves it from the core package post-split, not a MODULE_NOT_FOUND (task 2135). default:false — shipped ≠ enabled.
|
|
@@ -137,7 +138,6 @@ const PUBLISH_ALLOWLIST = [
|
|
|
137
138
|
'.gitattributes',
|
|
138
139
|
'package.json',
|
|
139
140
|
'package-lock.json',
|
|
140
|
-
'public-landing/',
|
|
141
141
|
// --- served-at-runtime app assets a standalone MUST carry (task 2078) ---
|
|
142
142
|
// The generated typed API client is served at /lib/bongos-client.js as
|
|
143
143
|
// window.BongosClient; shell.js + builders.js call it at the TOP of every hall
|
|
@@ -206,13 +206,15 @@ const MODULE_GLOBS = {
|
|
|
206
206
|
// (moduleWebSurfaces → serve-internal). Its public read endpoints stay SHARED
|
|
207
207
|
// core in routes/public.js (the hall + the off-box mirror consume them too).
|
|
208
208
|
'status-ui': ['modules/status-ui/'],
|
|
209
|
-
//
|
|
210
|
-
//
|
|
211
|
-
//
|
|
212
|
-
//
|
|
213
|
-
//
|
|
214
|
-
//
|
|
215
|
-
|
|
209
|
+
// Task 1003322 (ADR 0218): the front door graduated to modules/public-landing/ —
|
|
210
|
+
// public-landing/ moved to modules/public-landing/public/ and is now a loader-
|
|
211
|
+
// discovered web-surface module. Where hall-ui and status-ui name a Host PREFIX it
|
|
212
|
+
// declares apexRoot:true, since the apex is whatever host is left once the
|
|
213
|
+
// subdomains are taken; like the hall it keeps its DEDICATED serving block (in
|
|
214
|
+
// platform-server.js, re-pointed at the module dir). The KEY is unchanged from
|
|
215
|
+
// task 1002690 — its own key, not provisioning's, because the front door is broader
|
|
216
|
+
// than instance hosting and the web-surface precedent is one key per surface.
|
|
217
|
+
'public-landing': ['modules/public-landing/'],
|
|
216
218
|
// --- the design capability (task 1003324, ADR 0197): the design contract, its
|
|
217
219
|
// adapters + sync CLIs + validator, the /design playbook, and the reserved homes
|
|
218
220
|
// for the kit / the vendored UI skills / the style library. default-on,
|
|
@@ -211,7 +211,8 @@ function dropEmptyHrefLinks(html, values) {
|
|
|
211
211
|
// '<' would otherwise break out of its attribute or close <title> early, and
|
|
212
212
|
// the values now reach four times as many places as when this was written.
|
|
213
213
|
// No token is ever emitted into a <script> or a CSS context (checked across
|
|
214
|
-
// public-landing/, hall-ui/, status-ui/
|
|
214
|
+
// public-landing/, hall-ui/, status-ui/ — all under modules/ since task 1003322)
|
|
215
|
+
// — that would need different escaping.
|
|
215
216
|
// Ampersand first, or the other replacements get double-escaped.
|
|
216
217
|
function esc(s) {
|
|
217
218
|
return String(s == null ? '' : s)
|
|
@@ -701,9 +702,20 @@ function mountInternalSurfaces(app) {
|
|
|
701
702
|
// non-builders host. (The webSurfaces manifest declaration still makes the hall a
|
|
702
703
|
// loader-DISCOVERED module — it feeds the scope map, the fitness keys, docs, and
|
|
703
704
|
// window.__MODULES__; it is the dedicated serving below that reads the bytes.)
|
|
705
|
+
//
|
|
706
|
+
// The APEX-ROOT surface (modules/public-landing/, ADR 0218) is partitioned out the
|
|
707
|
+
// same way, on its own declaration rather than on a host: it carries no host prefix
|
|
708
|
+
// at all, because the apex is whatever is LEFT once the subdomains are taken. It is
|
|
709
|
+
// served by platform-server.js's dedicated landing block (the apex-only strip, the
|
|
710
|
+
// brand-token stamp, the ?v= content stamp, the five routes). Keeping it out of
|
|
711
|
+
// isModuleSurfaceHost is load-bearing, not tidiness — that predicate is what the
|
|
712
|
+
// /builders guards below use to step aside, and a surface matching every host would
|
|
713
|
+
// make all of them self-skip.
|
|
704
714
|
const buildersHostReq = (host) => ({ headers: { host } });
|
|
705
715
|
const allWebSurfaces = moduleWebSurfaces({ isEnabled: isModuleEnabled });
|
|
706
716
|
const webSurfaces = allWebSurfaces
|
|
717
|
+
.filter((s) => typeof s.host === 'string') // APEX-ROOT surfaces carry no host (ADR 0218): they are not
|
|
718
|
+
// routed by prefix here, and must stay OUT of isModuleSurfaceHost
|
|
707
719
|
.filter((s) => !isBuildersHost(buildersHostReq(s.host))) // hall served by the dedicated block, not here
|
|
708
720
|
.map((s) => ({
|
|
709
721
|
key: s.key,
|
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.
|
|
36
|
+
const CORE_VERSION = '1.19.443'; // 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');
|