@bongos/core 1.19.124 → 1.19.126

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 CHANGED
@@ -2,11 +2,11 @@
2
2
  "artifact": "bongos-core",
3
3
  "manifest_schema": 1,
4
4
  "generator": "scripts/gds/package-core.js",
5
- "core_version": "1.19.124",
6
- "core_contract": "1.19.124",
7
- "source_commit": "1b39368056a8f66b989d44a29bf23106497e1f61",
5
+ "core_version": "1.19.126",
6
+ "core_contract": "1.19.126",
7
+ "source_commit": "b56cedc2da8ef6691bd7990c6e3219896694e540",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-08-14T21:52:54.130Z",
9
+ "built_at": "2026-08-15T00:16:09.571Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
12
  "docs_redacted": 303,
@@ -17,7 +17,7 @@
17
17
  "gate": "passed"
18
18
  },
19
19
  "file_count": 1569,
20
- "tree_sha256": "582a4f5e59904421feb7b95deaa1e4c386cf327462d0746b3b27e7160ea67abc",
20
+ "tree_sha256": "5703791569f4c26556ad21f0bbcf605faf1b42cfd3631a1ce5f25a05269fd4b4",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -4167,12 +4167,12 @@
4167
4167
  {
4168
4168
  "path": "package-lock.json",
4169
4169
  "mode": "0000644",
4170
- "sha256": "b596ffed4e442327f612e616590c1dc64e53c8b1a0ccca011b29678569f272eb"
4170
+ "sha256": "64edc372bb49cd5c30eb5a1d46f54361293de787d1717d2e116f52568ff93f2c"
4171
4171
  },
4172
4172
  {
4173
4173
  "path": "package.json",
4174
4174
  "mode": "0000644",
4175
- "sha256": "ea424af2d952a539697392ddb1467606d5b48417f4093d9ee91eba3a818bfc87"
4175
+ "sha256": "c652ffaf3901e4ccc8f64ff9340f7f4f6c418ab738fc52f2b71450083d2a4e97"
4176
4176
  },
4177
4177
  {
4178
4178
  "path": "public-docs/index.html",
@@ -4412,7 +4412,7 @@
4412
4412
  {
4413
4413
  "path": "public-landing/assets/cosmos.css",
4414
4414
  "mode": "0000644",
4415
- "sha256": "bd0b17f7a21120a9e754c88206bd718fdda1fce568df15441272af0b9004c7cb"
4415
+ "sha256": "a55b4dd516485060edb4f1cd0be5164bd2026d77186d9b19c1f728a64eda9c7b"
4416
4416
  },
4417
4417
  {
4418
4418
  "path": "public-landing/buddha.png",
@@ -4427,7 +4427,7 @@
4427
4427
  {
4428
4428
  "path": "public-landing/projects.html",
4429
4429
  "mode": "0000644",
4430
- "sha256": "925e7964f66d59f2df260a611e602bdeafe3f8f3b73d3d16db7bdfa073b4163c"
4430
+ "sha256": "6dcb73054fa58d3b0254f1d82a7f639ed465db2c2fd1e83e26f29b80532047e5"
4431
4431
  },
4432
4432
  {
4433
4433
  "path": "public-landing/world/assets/clouds/cloud-island-large.png",
@@ -5757,7 +5757,7 @@
5757
5757
  {
5758
5758
  "path": "src/module-api.js",
5759
5759
  "mode": "0000644",
5760
- "sha256": "64773898c77a0786d85960b90c05b25c5148c3992561284c91d011d703745e22"
5760
+ "sha256": "c64972eb393e195b4f24de3459036423077430a5cf643fd2e3a8da525ab3af56"
5761
5761
  },
5762
5762
  {
5763
5763
  "path": "src/module-loader/catalog.js",
@@ -6267,7 +6267,7 @@
6267
6267
  {
6268
6268
  "path": "tests/cosmos_token_contrast.mjs",
6269
6269
  "mode": "0000644",
6270
- "sha256": "257acb76f7b7e8bb925b005e8a7bad96d712f0a216201aacee11762a2a2ff328"
6270
+ "sha256": "3689ce2b23d0baedb5a277676e939af712cea8d0cb3c07857e33f36eb12e1233"
6271
6271
  },
6272
6272
  {
6273
6273
  "path": "tests/cost.mjs",
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.124",
3
+ "version": "1.19.126",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.124",
9
+ "version": "1.19.126",
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.124",
3
+ "version": "1.19.126",
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",
@@ -98,6 +98,7 @@
98
98
  --ink:#fdeedd; --ink-soft:#dcb693; --ink-faint:#b89272;
99
99
  --rule:#5e3521; --rule-soft:#3f2317;
100
100
  --accent-deep:color-mix(in srgb, var(--accent) 45%, #ffffff);
101
+ --sea-ink:color-mix(in srgb, var(--sea) 65%, #ffffff);
101
102
  --ok:#4ade80; --warn:#fbbf24; --err:#f87171;
102
103
  --on-ok:#0b2e18;
103
104
  --page-bg:#150c06;
@@ -124,6 +125,7 @@
124
125
  --ink:#fdeedd; --ink-soft:#dcb693; --ink-faint:#b89272;
125
126
  --rule:#5e3521; --rule-soft:#3f2317;
126
127
  --accent-deep:color-mix(in srgb, var(--accent) 45%, #ffffff);
128
+ --sea-ink:color-mix(in srgb, var(--sea) 65%, #ffffff);
127
129
  --ok:#4ade80; --warn:#fbbf24; --err:#f87171;
128
130
  --on-ok:#0b2e18;
129
131
  --page-bg:#150c06;
@@ -154,6 +156,10 @@
154
156
  --err-soft:color-mix(in srgb, var(--err) 12%, var(--bg-card));
155
157
  --accent-soft:color-mix(in srgb, var(--accent) 15%, var(--bg-card));
156
158
  --sea-soft:color-mix(in srgb, var(--sea) 16%, var(--bg-card));
159
+ /* the strip's STATE-carrying marks (done fill, now ring/pulse) — light uses
160
+ --sea as-is; dark derives brighter (the --accent-deep pattern) so a pack's
161
+ light-tuned sea clears WCAG 1.4.11's 3:1 on the near-black card too */
162
+ --sea-ink:var(--sea);
157
163
  --neutral-soft:color-mix(in srgb, var(--ink) 8%, var(--bg-card));
158
164
  /* ink-derived so the shadow is a WARM brown on the marigold ground instead of
159
165
  the cold navy it used to cast. The dark blocks override it with a plain black
@@ -207,8 +213,6 @@ h1{font-family:var(--font-display);font-weight:700;font-size:clamp(24px,3.4vw,32
207
213
  .lede{font-size:15.5px;color:var(--ink-soft);font-weight:500;line-height:1.55;margin-bottom:24px;max-width:62ch;}
208
214
  .lede a{color:var(--accent-deep);font-weight:700;text-decoration:none;}
209
215
  .lede a:hover{text-decoration:underline;}
210
- :root[data-mode="dark"] .lede a{color:var(--sun-lit);}
211
- @media (prefers-color-scheme: dark){:root:not([data-mode="light"]) .lede a{color:var(--sun-lit);}}
212
216
 
213
217
  /* ── buttons ── */
214
218
  .btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;cursor:pointer;font-family:var(--font-display);font-weight:700;font-size:15px;color:#fff;background:var(--accent);border:none;border-radius:999px;padding:11px 22px;text-decoration:none;box-shadow:0 10px 22px -8px color-mix(in srgb,var(--accent) 70%,transparent);transition:transform .15s ease, box-shadow .15s ease, background .15s ease;}
@@ -217,24 +221,18 @@ h1{font-family:var(--font-display);font-weight:700;font-size:clamp(24px,3.4vw,32
217
221
  .btn:disabled{background:color-mix(in srgb,var(--ink) 12%,var(--bg-deep));color:color-mix(in srgb,var(--ink) 40%,var(--bg-deep));box-shadow:none;cursor:not-allowed;transform:none;}
218
222
  .btn.ghost{background:transparent;color:var(--ink-soft);box-shadow:none;border:2px solid var(--line);}
219
223
  .btn.ghost:hover{border-color:var(--accent);color:var(--accent-deep);}
220
- :root[data-mode="dark"] .btn.ghost:hover{color:var(--sun-lit);}
221
- @media (prefers-color-scheme: dark){:root:not([data-mode="light"]) .btn.ghost:hover{color:var(--sun-lit);}}
222
224
  .btn.small{font-size:13.5px;padding:8px 16px;}
223
225
  .btn.danger{background:transparent;color:var(--err);box-shadow:none;border:2px solid color-mix(in srgb,var(--err) 35%,transparent);}
224
226
  .btn.danger:hover{background:var(--err-soft);border-color:var(--err);}
225
227
  .btn .gh{width:17px;height:17px;fill:currentColor;}
226
228
  .linklike{background:none;border:none;cursor:pointer;font-family:var(--font-body);font-weight:700;font-size:13.5px;color:var(--accent-deep);text-decoration:underline;padding:6px 4px;}
227
- :root[data-mode="dark"] .linklike{color:var(--sun-lit);}
228
- @media (prefers-color-scheme: dark){:root:not([data-mode="light"]) .linklike{color:var(--sun-lit);}}
229
229
 
230
230
  /* ── chips: the one status vocabulary (direction §4). Recency rides inside
231
231
  (.rec) — "live" always says when it was last verified. ── */
232
232
  .chip{display:inline-flex;align-items:center;gap:7px;font-size:12px;font-weight:700;letter-spacing:0.3px;border-radius:999px;padding:4px 12px;background:var(--neutral-soft);color:var(--ink-soft);}
233
233
  .chip .cdot{width:7px;height:7px;border-radius:50%;background:var(--ink-faint);flex:0 0 auto;}
234
- .chip .rec{font-weight:600;opacity:0.72;font-size:11.5px;}
234
+ .chip .rec{font-weight:600;font-size:11.5px;}
235
235
  .chip.busy{background:var(--accent-soft);color:var(--accent-deep);}
236
- :root[data-mode="dark"] .chip.busy{color:var(--sun-lit);}
237
- @media (prefers-color-scheme: dark){:root:not([data-mode="light"]) .chip.busy{color:var(--sun-lit);}}
238
236
  .chip.busy .cdot{background:var(--accent);animation:chipPulse 1.4s ease-in-out infinite;}
239
237
  .chip.live{background:var(--ok-soft);color:var(--ok);}
240
238
  .chip.live .cdot{background:var(--ok);}
@@ -272,17 +270,11 @@ h1{font-family:var(--font-display);font-weight:700;font-size:clamp(24px,3.4vw,32
272
270
  .projName{font-family:var(--font-display);font-weight:700;font-size:19px;letter-spacing:-0.3px;}
273
271
  .projMeta{font-size:13px;font-weight:600;color:var(--ink-faint);margin-top:6px;display:flex;gap:14px;flex-wrap:wrap;}
274
272
  .projMeta code{font-family:var(--font-mono);font-size:12.5px;background:var(--bg-deep);border-radius:6px;padding:1px 6px;color:var(--accent-deep);}
275
- :root[data-mode="dark"] .projMeta code{color:var(--sun-lit);}
276
- @media (prefers-color-scheme: dark){:root:not([data-mode="light"]) .projMeta code{color:var(--sun-lit);}}
277
273
  .addr{margin-top:12px;font-size:14.5px;font-weight:600;color:var(--ink-soft);}
278
274
  .addr a{color:var(--accent-deep);font-weight:700;text-decoration:none;font-family:var(--font-mono);font-size:13.5px;}
279
- :root[data-mode="dark"] .addr a{color:var(--sun-lit);}
280
- @media (prefers-color-scheme: dark){:root:not([data-mode="light"]) .addr a{color:var(--sun-lit);}}
281
275
  .addr a:hover{text-decoration:underline;}
282
276
  .addr .none{color:var(--ink-faint);font-style:italic;}
283
277
  .addr code{font-family:var(--font-mono);font-size:13px;background:var(--bg-deep);border-radius:6px;padding:1px 7px;color:var(--accent-deep);}
284
- :root[data-mode="dark"] .addr code{color:var(--sun-lit);}
285
- @media (prefers-color-scheme: dark){:root:not([data-mode="light"]) .addr code{color:var(--sun-lit);}}
286
278
  .attach{display:flex;gap:10px;margin-top:10px;flex-wrap:wrap;}
287
279
  .attach input[type=text]{flex:1;min-width:220px;font-family:var(--font-body);font-size:15px;font-weight:600;color:var(--ink);background:var(--bg-card);border:2px solid var(--line);border-radius:12px;padding:9px 14px;outline:none;transition:border-color .15s ease, box-shadow .15s ease;}
288
280
  .attach input[type=text]::placeholder{color:var(--ink-faint);font-weight:500;}
@@ -291,8 +283,6 @@ h1{font-family:var(--font-display);font-weight:700;font-size:clamp(24px,3.4vw,32
291
283
  .projMsg.ok{color:var(--ok);}
292
284
  .projMsg.err{color:var(--err);}
293
285
  .projMsg a{color:var(--accent-deep);font-weight:700;text-decoration:none;}
294
- :root[data-mode="dark"] .projMsg a{color:var(--sun-lit);}
295
- @media (prefers-color-scheme: dark){:root:not([data-mode="light"]) .projMsg a{color:var(--sun-lit);}}
296
286
  .projMsg a:hover{text-decoration:underline;}
297
287
  .projMsg details{margin-top:8px;}
298
288
  .projMsg summary{cursor:pointer;font-size:12.5px;font-weight:700;color:var(--ink-faint);}
@@ -305,8 +295,6 @@ h1{font-family:var(--font-display);font-weight:700;font-size:clamp(24px,3.4vw,32
305
295
  the browser's default blue, the one colour in the page that is not ours */
306
296
  .finePrint a{color:var(--accent-deep);font-weight:700;text-decoration:none;}
307
297
  .finePrint a:hover{text-decoration:underline;}
308
- :root[data-mode="dark"] .finePrint a{color:var(--sun-lit);}
309
- @media (prefers-color-scheme: dark){:root:not([data-mode="light"]) .finePrint a{color:var(--sun-lit);}}
310
298
 
311
299
  /* ── progress strips: one visual, per-flow vocabularies (direction §4).
312
300
  Default = the sea-blue "working" look; .isLive = verified-landed green;
@@ -315,13 +303,15 @@ h1{font-family:var(--font-display);font-weight:700;font-size:clamp(24px,3.4vw,32
315
303
  .strip .stages{display:flex;list-style:none;margin-bottom:6px;}
316
304
  .strip .stages li{flex:1;display:flex;flex-direction:column;align-items:center;gap:7px;position:relative;}
317
305
  .strip .stages li:not(:first-child)::before{content:"";position:absolute;top:11px;right:50%;width:100%;height:3px;border-radius:2px;background:var(--sea-soft);z-index:0;margin-right:12px;}
318
- .strip .stages li.done:not(:first-child)::before{background:var(--sea);}
306
+ .strip .stages li.done:not(:first-child)::before{background:var(--sea-ink);}
319
307
  .strip .sdot{position:relative;z-index:1;width:24px;height:24px;border-radius:50%;border:3px solid var(--sea-soft);background:var(--bg-card);display:flex;align-items:center;justify-content:center;}
320
- .strip .stages li.done .sdot{border-color:var(--sea);background:var(--sea);}
321
- .strip .stages li.done .sdot::after{content:"";width:8px;height:5px;border-left:2.5px solid #fff;border-bottom:2.5px solid #fff;transform:rotate(-45deg) translate(1px,-1px);}
322
- .strip .stages li.now .sdot{border-color:var(--sea);}
323
- .strip .stages li.now .sdot::after{content:"";width:8px;height:8px;border-radius:50%;background:var(--sea);animation:beatPulse 1.2s ease-in-out infinite;}
324
- @keyframes beatPulse{0%,100%{transform:scale(.7);opacity:.6}50%{transform:scale(1.15);opacity:1}}
308
+ .strip .stages li.done .sdot{border-color:var(--sea-ink);background:var(--sea-ink);}
309
+ /* tick ink is --on-ok, not #fff — dark's bright fills took a white tick to 1.7:1 */
310
+ .strip .stages li.done .sdot::after{content:"";width:8px;height:5px;border-left:2.5px solid var(--on-ok);border-bottom:2.5px solid var(--on-ok);transform:rotate(-45deg) translate(1px,-1px);}
311
+ .strip .stages li.now .sdot{border-color:var(--sea-ink);}
312
+ .strip .stages li.now .sdot::after{content:"";width:8px;height:8px;border-radius:50%;background:var(--sea-ink);animation:beatPulse 1.2s ease-in-out infinite;}
313
+ /* scale-only: an opacity trough took the pulse below the 3:1 its token clears */
314
+ @keyframes beatPulse{0%,100%{transform:scale(.7)}50%{transform:scale(1.15)}}
325
315
  .strip .slbl{font-size:12px;font-weight:700;color:var(--ink-faint);}
326
316
  .strip .stages li.now .slbl,.strip .stages li.done .slbl{color:color-mix(in srgb,var(--sea) 40%,var(--ink));}
327
317
  .strip .pnote{text-align:center;font-size:13px;font-weight:600;color:var(--ink-soft);min-height:18px;line-height:1.5;}
@@ -432,8 +422,8 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
432
422
  /* Home LEAVES the hub, so it never takes the current-view pill and a hairline
433
423
  sets it apart. Without it the header offered no way back to the landing page
434
424
  except the wordmark, which nothing announced as a link. */
435
- .navrow .navHome{color:var(--glass-ink);opacity:.9;}
436
- .navrow .navHome:hover{opacity:1;color:var(--chrome-accent);}
425
+ .navrow .navHome{color:var(--glass-ink);}
426
+ .navrow .navHome:hover{color:var(--chrome-accent);}
437
427
  .navSep{width:1px;align-self:stretch;margin:4px 3px;background:var(--glass-border);flex:0 0 auto;}
438
428
  /* below the mid breakpoint the three groups stack and centre rather than
439
429
  fighting for one row — equal flanks only buy a centred nav while it fits */
@@ -458,7 +448,9 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
458
448
  .search svg{width:15px;height:15px;fill:none;stroke:var(--glass-ink);stroke-width:2;flex:0 0 auto;}
459
449
  .search input{flex:1;min-width:0;background:none;border:none;outline:none;font-family:var(--font-mono);
460
450
  font-size:13px;color:var(--chrome-ink);}
461
- .search input::placeholder{color:var(--glass-ink);opacity:.75;}
451
+ /* no opacity dim — .75 washed this to 3.87:1, and the .mineBar override
452
+ inherited the multiply so its own token could never fix it (task 1002751) */
453
+ .search input::placeholder{color:var(--glass-ink);}
462
454
  .search .count{font-family:var(--font-mono);font-size:11.5px;color:var(--glass-ink);white-space:nowrap;}
463
455
 
464
456
  /* the only-mine filter — a real two-state control, not a link: it filters the
@@ -525,8 +517,6 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
525
517
  0 0 0 2px color-mix(in srgb,var(--accent) 55%,transparent);}
526
518
  .worldOrb[data-rel="builder"] .badge{background:var(--accent-soft);color:var(--accent-deep);
527
519
  border:1px solid color-mix(in srgb,var(--accent) 45%,transparent);}
528
- :root[data-mode="dark"] .worldOrb[data-rel="builder"] .badge{color:var(--sun-lit);}
529
- @media (prefers-color-scheme: dark){:root:not([data-mode="light"]) .worldOrb[data-rel="builder"] .badge{color:var(--sun-lit);}}
530
520
  .worldOrb .chip{transform:scale(.88);white-space:nowrap;}
531
521
 
532
522
  /* what the only-mine filter shows when you are connected to nothing. Not a
@@ -570,8 +560,6 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
570
560
  border:1px solid var(--line);color:var(--ink-faint);white-space:nowrap;}
571
561
  .tbl .pill{display:inline-block;font-family:var(--font-mono);font-size:11px;padding:3px 10px;
572
562
  border-radius:999px;background:var(--accent-soft);color:var(--accent-deep);}
573
- :root[data-mode="dark"] .tbl .pill{color:var(--sun-lit);}
574
- @media (prefers-color-scheme: dark){:root:not([data-mode="light"]) .tbl .pill{color:var(--sun-lit);}}
575
563
  .tbl td.right{text-align:right;}
576
564
  /* Nothing in an action or status cell may wrap: "Manage →" breaking after the
577
565
  space and the chip's recency dropping to a second line both read as a broken
@@ -627,8 +615,6 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
627
615
  .projCard .cardTag{font-size:14px;color:var(--ink-faint);font-weight:600;line-height:1.5;margin-bottom:14px;}
628
616
  .projCard .cardRel{display:inline-block;font-family:var(--font-mono);font-size:11px;padding:3px 10px;
629
617
  border-radius:999px;background:var(--accent-soft);color:var(--accent-deep);margin-bottom:18px;}
630
- :root[data-mode="dark"] .projCard .cardRel{color:var(--sun-lit);}
631
- @media (prefers-color-scheme: dark){:root:not([data-mode="light"]) .projCard .cardRel{color:var(--sun-lit);}}
632
618
  /* the facts strip (task 1002778): a label/value grid, centered as a block but
633
619
  read left-aligned. A hidden pair leaves no gap — the grid row collapses. */
634
620
  .projCard .cardMeta{display:grid;grid-template-columns:auto auto;justify-content:center;
@@ -679,8 +665,6 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
679
665
  font-size:14px;color:var(--ink-faint);position:relative;z-index:1;}
680
666
  .steps li.done .snum{border-color:var(--accent);background:var(--accent);color:#fff;}
681
667
  .steps li.now .snum{border-color:var(--accent);color:var(--accent-deep);}
682
- :root[data-mode="dark"] .steps li.now .snum{color:var(--sun-lit);}
683
- @media (prefers-color-scheme: dark){:root:not([data-mode="light"]) .steps li.now .snum{color:var(--sun-lit);}}
684
668
  .steps .slbl{font-size:12.5px;font-weight:700;color:var(--ink-faint);}
685
669
  .steps li.now .slbl,.steps li.done .slbl{color:var(--ink-soft);}
686
670
  .createSplit{display:grid;grid-template-columns:1fr 300px;gap:var(--pad-md);align-items:center;}
@@ -163,7 +163,12 @@ h1[tabindex="-1"]:focus{outline:none;}
163
163
  step that can't be performed yet (its prerequisite — usually the domain — is missing) */
164
164
  .humanSteps>li.sdone{border-color:color-mix(in srgb,var(--ok) 30%,var(--bg-card));background:linear-gradient(180deg,color-mix(in srgb,var(--ok) 8%,var(--bg-card)) 0%,var(--bg-card) 100%);}
165
165
  .humanSteps>li.sdone::before{content:"✓";background:var(--ok);color:var(--on-ok);}
166
- .humanSteps>li.slocked{opacity:.6;}
166
+ /* locked = muted TOKENS, not subtree opacity — opacity multiplies after the
167
+ token resolves and took this copy below AA (task 1002751) */
168
+ .humanSteps>li.slocked{border-color:color-mix(in srgb,var(--line) 55%,var(--bg-card));}
169
+ .humanSteps>li.slocked b{color:var(--ink-soft);}
170
+ .humanSteps>li.slocked p{color:var(--ink-faint);}
171
+ .humanSteps>li.slocked::before{color:var(--ink-faint);}
167
172
  .humanSteps details{margin-top:10px;}
168
173
  .humanSteps summary{cursor:pointer;font-weight:700;font-size:12.5px;color:var(--ink-faint);}
169
174
  .humanSteps details p{margin-top:8px;}
@@ -194,16 +199,19 @@ h1[tabindex="-1"]:focus{outline:none;}
194
199
  content:"";position:absolute;top:11px;right:50%;width:100%;height:3px;border-radius:2px;
195
200
  background:var(--sea-soft);z-index:0;margin-right:12px;
196
201
  }
197
- .prog .stages li.done:not(:first-child)::before{background:var(--sea);}
202
+ /* state marks paint --sea-ink / --on-ok, mirroring cosmos's .strip (task
203
+ 1002751) — raw --sea sat at 3.03:1 in dark and the #fff tick at 1.74:1 on
204
+ dark's bright --ok */
205
+ .prog .stages li.done:not(:first-child)::before{background:var(--sea-ink);}
198
206
  .prog .sdot{
199
207
  position:relative;z-index:1;width:24px;height:24px;border-radius:50%;
200
208
  border:3px solid var(--sea-soft);background:var(--bg-card);display:flex;align-items:center;justify-content:center;
201
209
  }
202
- .prog .stages li.done .sdot{border-color:var(--sea);background:var(--sea);}
203
- .prog .stages li.done .sdot::after{content:"";width:8px;height:5px;border-left:2.5px solid #fff;border-bottom:2.5px solid #fff;transform:rotate(-45deg) translate(1px,-1px);}
204
- .prog .stages li.now .sdot{border-color:var(--sea);}
210
+ .prog .stages li.done .sdot{border-color:var(--sea-ink);background:var(--sea-ink);}
211
+ .prog .stages li.done .sdot::after{content:"";width:8px;height:5px;border-left:2.5px solid var(--on-ok);border-bottom:2.5px solid var(--on-ok);transform:rotate(-45deg) translate(1px,-1px);}
212
+ .prog .stages li.now .sdot{border-color:var(--sea-ink);}
205
213
  .prog .stages li.now .sdot::after{
206
- content:"";width:8px;height:8px;border-radius:50%;background:var(--sea);
214
+ content:"";width:8px;height:8px;border-radius:50%;background:var(--sea-ink);
207
215
  animation:beatPulse 1.2s ease-in-out infinite;
208
216
  }
209
217
  /* idle labels match cosmos's .strip .slbl — the 55% sea mix washed to 4.15:1 in dark */
@@ -229,7 +237,9 @@ h1[tabindex="-1"]:focus{outline:none;}
229
237
 
230
238
  /* the collapsed address step — a confirmation, not a form (task 1002704) */
231
239
  .addrConfirm{border:2px solid color-mix(in srgb,var(--ok) 30%,var(--bg-card));border-radius:18px;padding:18px 20px;background:linear-gradient(180deg,color-mix(in srgb,var(--ok) 8%,var(--bg-card)) 0%,var(--bg-card) 100%);margin-bottom:16px;}
232
- .addrConfirm.optedOut{border-color:var(--line);background:var(--bg);opacity:.75;}
240
+ .addrConfirm.optedOut{border-color:var(--line);background:var(--bg);}
241
+ .addrConfirm.optedOut .addrBig{color:var(--ink-soft);}
242
+ .addrConfirm.optedOut>p{color:var(--ink-faint);}
233
243
  .addrBig{font-family:var(--font-display);font-weight:700;font-size:19px;letter-spacing:-0.3px;line-height:1.4;}
234
244
  .addrBig code{font-family:var(--font-mono);font-size:15px;background:var(--bg-deep);border-radius:8px;padding:2px 9px;color:var(--accent-deep);word-break:break-all;}
235
245
  .addrConfirm>p{font-size:13.5px;font-weight:500;color:var(--ink-soft);line-height:1.5;margin-top:6px;}
package/src/module-api.js CHANGED
@@ -675,8 +675,12 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
675
675
  // landed since 1.19.122 with no explicit bump. run 31842559470. (task 1002620)
676
676
  // 1.19.124 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
677
677
  // landed since 1.19.123 with no explicit bump. run 31844281033. (task 1002620)
678
+ // 1.19.125 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
679
+ // landed since 1.19.124 with no explicit bump. run 31849445746. (task 1002620)
680
+ // 1.19.126 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
681
+ // landed since 1.19.125 with no explicit bump. run 31853055269. (task 1002620)
678
682
  // ---------------------------------------------------------------------------
679
- const CORE_VERSION = '1.19.124'; // CI auto-patch carrier (ADR 0161); see changelog above
683
+ const CORE_VERSION = '1.19.126'; // CI auto-patch carrier (ADR 0161); see changelog above
680
684
 
681
685
  // A namespaced logger so a module's log lines are attributable + consistent.
682
686
  // Usage: const log = api.logger('dev-box'); log.info('mounted');
@@ -143,14 +143,14 @@ const PAIRS = [
143
143
  ['.btn label on its fill', '#ffffff', 'var(--accent)', 4.5],
144
144
  ['.btn.ghost:hover label', 'var(--accent-deep)', CARD, 4.5],
145
145
  ['.btn.danger label', 'var(--err)', CARD, 4.5],
146
- // Inline links: cosmos.css recolors its own to --sun-lit in dark (:179-262), but
147
- // the ?view=new wizard styles (absorbed from create.html, task 1002763) carry
148
- // eleven --accent-deep consumers and zero dark rules of their own, so there the
149
- // token itself must carry dark.
146
+ // Inline links, code chips, pills, the now-step number: every accent-colored
147
+ // text consumer paints --accent-deep in BOTH modes (task 1002750 deleted the
148
+ // dark --sun-lit recolors — they pinned the locked space palette over the
149
+ // branding pack, so a re-themed instance got Cloud Bongos orange links).
150
+ // The dark blocks derive --accent-deep from the pack-settable --accent.
150
151
  ['inline link on a card', 'var(--accent-deep)', CARD, 4.5],
151
152
  ['inline link on the page ground', 'var(--accent-deep)', 'var(--bg)', 4.5],
152
153
  ['code chip on --bg-deep', 'var(--accent-deep)', 'var(--bg-deep)', 4.5],
153
- ['cosmos.css dark link (--sun-lit)', 'var(--sun-lit)', CARD, 4.5, 'dark'],
154
154
  // body copy
155
155
  ['--ink body copy', 'var(--ink)', CARD, 4.5],
156
156
  ['--ink-soft secondary copy', 'var(--ink-soft)', CARD, 4.5],
@@ -162,9 +162,24 @@ const PAIRS = [
162
162
  ['.chip.live label', 'var(--ok)', 'var(--ok-soft)', 4.5],
163
163
  ['.chip.maybe label', 'var(--warn)', 'var(--warn-soft)', 4.5],
164
164
  ['.chip.down/.bad label', 'var(--err)', 'var(--err-soft)', 4.5],
165
- // .chip.busy recolors in dark (cosmos.css:194-195) — assert what each mode paints
166
- ['.chip.busy label', 'var(--accent-deep)', 'var(--accent-soft)', 4.5, 'light'],
167
- ['.chip.busy label', 'var(--sun-lit)', 'var(--accent-soft)', 4.5, 'dark'],
165
+ // .chip.busy + the .tbl .pill / .cardRel / builder-badge family: accent-deep
166
+ // ink on the accent-soft tint, both modes
167
+ ['.chip.busy label', 'var(--accent-deep)', 'var(--accent-soft)', 4.5],
168
+ // lifecycle strip STATE INDICATORS (task 1002751): non-text at the WCAG
169
+ // 1.4.11 bar of 3.0. --sea-ink derives brighter in dark exactly the way
170
+ // --accent-deep does, so a pack's light-tuned sea can't vanish on the dark
171
+ // card; the done tick takes --on-ok (the ink-on-a-filled-state-dot token).
172
+ ['strip done/now indicator on card', 'var(--sea-ink)', CARD, 3.0],
173
+ ['strip done fill vs its idle rail', 'var(--sea-ink)', 'var(--sea-soft)', 3.0],
174
+ ['strip indicator on the strip tint', 'var(--sea-ink)', 'color-mix(in srgb,var(--sea) 8%,var(--bg-card))', 3.0],
175
+ ['strip done tick on its fill', 'var(--on-ok)', 'var(--sea-ink)', 3.0],
176
+ ['flowDown done tick on its fill', 'var(--on-ok)', 'var(--ink-faint)', 3.0],
177
+ ['.steps done/now ring on card', 'var(--accent)', CARD, 3.0],
178
+ ['.steps done vs idle connector', 'var(--accent)', 'var(--line)', 3.0],
179
+ // the opted-out address card swaps to the page ground (--bg); its muted copy
180
+ // must still measure (19px/700 headline = large text, 3.0)
181
+ ['optedOut .addrBig (large) on --bg', 'var(--ink-soft)', 'var(--bg)', 3.0],
182
+ ['addrOpt warning on --bg', 'var(--warn)', 'var(--bg)', 4.5],
168
183
  // lifecycle strip: 12px/700
169
184
  ['.strip .slbl idle', 'var(--ink-faint)', CARD, 4.5],
170
185
  ['.strip .slbl now/done', 'color-mix(in srgb,var(--sea) 40%,var(--ink))', CARD, 4.5],
@@ -212,6 +227,123 @@ for (const mode of ['light', 'dark']) {
212
227
  });
213
228
  }
214
229
 
230
+ // The gate's LOCKED sky palette — deliberately absent from COLOR_VARS, so a
231
+ // branding pack can never re-theme these. Dark GROUNDS are allowed (dark mode IS
232
+ // the gate's space, by design); text painted with one shows Cloud Bongos orange
233
+ // on every re-themed instance, beside its own brand color (task 1002750; same
234
+ // class as the --chrome-accent leak in 1002749).
235
+ const LOCKED = ['sun-lit', 'gold', 'gold-soft', 'nebula', 'space-deep', 'space-mid', 'space-high'];
236
+ const LOCKED_REF = new RegExp(`var\\(\\s*--(?:${LOCKED.join('|')})\\s*[,)]`);
237
+
238
+ test('no rule paints text with the locked space palette', () => {
239
+ // The 2026-08-13 state: thirteen dark-mode pairs pinning links/chips/pills to
240
+ // --sun-lit. Property-anchored (background-color:/border-color: fills with
241
+ // their own fixed ink are fine) and value-wide, so color-mix()/fallback/padded
242
+ // spellings are caught too. Comments are stripped first — quoting the old rule
243
+ // in prose must not fail the build.
244
+ const stripped = src.replace(/\/\*[\s\S]*?\*\//g, '');
245
+ const leaks = [];
246
+ for (const m of stripped.matchAll(/(?:^|[;{\s])(color|fill|stroke)\s*:\s*([^;}]*)/g)) {
247
+ if (LOCKED_REF.test(m[2])) leaks.push(`${m[1]}:${m[2].trim()}`);
248
+ }
249
+ assert.deepEqual(leaks, [], `space-palette tokens used as text color:\n ${leaks.join('\n ')}\n`);
250
+ });
251
+
252
+ test('dark blocks never re-point a token at the locked space palette', () => {
253
+ // The seam the literal guard can't see: a dark block re-pointing a token the
254
+ // text consumers use (--accent-deep:var(--sun-lit)) paints every link orange
255
+ // while no color: declaration names a locked token — and at (0,2,0) no pack
256
+ // can ever win it back. Dark grounds that want the space look use literals.
257
+ // (The typeCard's 40%-gold-soft heading mix is fine: a per-TYPE tint in the
258
+ // light tier, where the pack still owns every brand token.)
259
+ for (const [where, block] of [
260
+ [':root[data-mode="dark"]', darkExplicit],
261
+ ['the prefers-color-scheme twin', darkMedia],
262
+ ]) {
263
+ for (const [name, value] of Object.entries(block)) {
264
+ assert.ok(
265
+ !LOCKED_REF.test(value),
266
+ `${where}: ${name}:${value} references a locked space-palette token`
267
+ );
268
+ }
269
+ }
270
+ });
271
+
272
+ // opacity multiplies AFTER the token resolves, so a dimmed subtree's contrast
273
+ // is invisible to every token assertion above (task 1002751: .slocked at .6
274
+ // took 13.5px copy to 3.02:1; .chip .rec at .72 took recency to 3.48:1;
275
+ // .addrConfirm.optedOut at .75 took its note to 3.43:1; a placeholder rule's
276
+ // .75 survived into an override that set only the color, so no token value
277
+ // could ever fix it). Dim with a muted TOKEN so the result stays measured.
278
+ // Known limits, accepted: @keyframes bodies, animation-fill-mode resting
279
+ // values, and JS-set inline opacity are out of static reach — never make an
280
+ // animation or script the resting presenter of text or a state mark.
281
+ const pageSrc = fs
282
+ .readFileSync(path.join(path.dirname(CSS), '..', 'projects.html'), 'utf8')
283
+ .replace(/<!--[\s\S]*?-->/g, '')
284
+ .replace(/\/\*[\s\S]*?\*\//g, '');
285
+ const cssSrc = src.replace(/\/\*[\s\S]*?\*\//g, '');
286
+ const dimsBelowFull = (body) => {
287
+ const m = body.match(/opacity\s*:\s*([\d.]+%?)/);
288
+ if (!m) return false;
289
+ const v = m[1].endsWith('%') ? parseFloat(m[1]) / 100 : parseFloat(m[1]);
290
+ return v < 1;
291
+ };
292
+
293
+ test('text is never dimmed by subtree opacity', () => {
294
+ // Two nets. (1) Anywhere in either sheet: a rule that declares BOTH a color
295
+ // and a sub-1 opacity is dimmed text by construction. (2) The create flow's
296
+ // known text containers may not carry opacity at all — this also catches
297
+ // ancestor dims (.createSplit) that net 1 can't see.
298
+ const offenders = [];
299
+ for (const [where, text] of [['cosmos.css', cssSrc], ['projects.html', pageSrc]]) {
300
+ for (const m of text.matchAll(/([^{}@]+)\{([^{}]*)\}/g)) {
301
+ if (/(?:^|[;{\s])color\s*:/.test(m[2]) && dimsBelowFull(m[2])) {
302
+ offenders.push(`${where}: ${m[1].trim().slice(0, 60)} declares color + opacity<1`);
303
+ }
304
+ }
305
+ }
306
+ for (const sel of ['\\.humanSteps', '\\.addrConfirm', '\\.prog\\b', '\\.choice', '\\.review', '\\.createSplit', '\\.chip \\.rec']) {
307
+ for (const text of [cssSrc, pageSrc]) {
308
+ for (const m of text.matchAll(new RegExp(`${sel}[^{}]*\\{([^}]*)\\}`, 'g'))) {
309
+ if (dimsBelowFull(m[1])) offenders.push(`text container dimmed: ${m[0].slice(0, 90)}`);
310
+ }
311
+ }
312
+ }
313
+ assert.deepEqual(offenders, [], `\n ${offenders.join('\n ')}\n`);
314
+ });
315
+
316
+ test('the .prog strip mirrors .strip: state marks are --sea-ink, ticks are --on-ok', () => {
317
+ // /create's done panel renders the page-local .prog, NOT cosmos's .strip —
318
+ // a re-point of one and not the other is exactly how the 1.74:1 dark tick
319
+ // shipped the first time. State-mark rules (done fill/connector, now
320
+ // ring/pulse, the tick) must use the derived tokens; raw --sea is only legal
321
+ // as a tint ingredient (color-mix) or the idle -soft rail.
322
+ const progRules = [...pageSrc.matchAll(/\.prog[^{}]*\{([^}]*)\}/g)];
323
+ assert.ok(progRules.length >= 5, 'the .prog rules moved out of projects.html — update this test');
324
+ const bad = [];
325
+ for (const m of progRules) {
326
+ const body = m[1];
327
+ if (/(?:border(?:-\w+)?|background)\s*:\s*var\(\s*--sea\s*\)/.test(body)) {
328
+ bad.push(`raw --sea state mark: ${m[0].slice(0, 80)}`);
329
+ }
330
+ if (/border-(?:left|bottom)\s*:[^;]*#fff\b/i.test(body)) {
331
+ bad.push(`hardcoded #fff tick: ${m[0].slice(0, 80)}`);
332
+ }
333
+ }
334
+ assert.deepEqual(bad, [], `\n ${bad.join('\n ')}\n`);
335
+ });
336
+
337
+ test('the pulse animation never dims its mark', () => {
338
+ // beatPulse drives the now-dot in BOTH .strip and .prog. An opacity trough
339
+ // (the old 0.6) put the pulsing mark below the 3:1 its token clears at rest
340
+ // — the PAIRS row above would assert a color the pixel never sustains.
341
+ const kf = cssSrc.match(/@keyframes beatPulse\{([\s\S]*?)\}\s*\}/) ||
342
+ cssSrc.match(/@keyframes beatPulse\{([^@]*)\}/);
343
+ assert.ok(kf, '@keyframes beatPulse not found in cosmos.css — update this test');
344
+ assert.ok(!/opacity\s*:/.test(kf[1]), 'beatPulse dims the now-mark by opacity again');
345
+ });
346
+
215
347
  test('the two dark blocks stay in lockstep', () => {
216
348
  // cosmos.css carries dark twice — an explicit data-mode hook and a
217
349
  // prefers-color-scheme twin. A change to one and not the other is the