@bongos/core 1.19.514 → 1.19.516

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/.bongos-core.json +81 -36
  2. package/clients/bongos-client/README.md +1 -1
  3. package/clients/bongos-client/bongos-client.global.js +12 -0
  4. package/clients/bongos-client/index.cjs +12 -0
  5. package/clients/bongos-client/index.d.ts +22 -2
  6. package/clients/bongos-client/index.mjs +12 -0
  7. package/docs/adr/0240-a-skipped-module-picker-stores-nothing-and-resolves-the-bundle-on-read.md +157 -0
  8. package/docs/adr/README.md +1 -0
  9. package/docs/api/openapi.json +362 -6
  10. package/docs/api-reference.md +12 -2
  11. package/docs/copy-inventory.md +224 -218
  12. package/docs/copy-registry.json +357 -291
  13. package/docs/module-api-changelog.md +4 -0
  14. package/modules/dev-box/app/src/vendor/bongos-client.cjs +12 -0
  15. package/modules/government/catalog.js +6 -0
  16. package/modules/government/migrations/government_009_mingle_manage.sql +55 -0
  17. package/modules/lifecycle/migrations/lifecycle_007_mingle.sql +134 -0
  18. package/modules/lifecycle/mingle.js +337 -0
  19. package/modules/lifecycle/module.json +1 -0
  20. package/modules/lifecycle/routes/mingle.js +116 -0
  21. package/modules/provisioning/migrations/provisioning_014_module_selection.sql +60 -0
  22. package/modules/provisioning/provisioning.js +24 -4
  23. package/modules/provisioning/routes/provisioning.js +59 -2
  24. package/modules/provisioning/starter-bundles.js +91 -0
  25. package/modules/public-landing/public/assets/cosmos.css +1 -1
  26. package/modules/public-landing/public/projects.html +230 -35
  27. package/modules/public-landing/public/projects.probes.json +2 -2
  28. package/modules/public-landing/public/projects.states.json +5 -4
  29. package/modules/ui-design/kit/serve.js +32 -0
  30. package/package-lock.json +2 -2
  31. package/package.json +1 -1
  32. package/src/module-api.js +1 -1
  33. package/tests/government_seed.mjs +35 -0
  34. package/tests/mingle.mjs +370 -0
  35. package/tests/projects_hub.mjs +72 -7
  36. package/tests/projects_hub_module_picker.mjs +369 -0
  37. package/tests/provisioning_module_selection.mjs +286 -0
  38. package/tests/provisioning_starter_bundles.mjs +131 -0
@@ -328,6 +328,16 @@ summary{min-height:24px;padding:3px 0;}
328
328
  .tpill:hover::before{border-color:var(--ink-faint);}
329
329
  .tpill[aria-pressed=true]{background:none;border-color:var(--rule-soft);color:var(--ink);}
330
330
  .tpill[aria-pressed=true]::before{background:var(--accent);border-color:var(--accent);outline:4px solid color-mix(in srgb,var(--accent) 22%,transparent);}
331
+ /* the module picker (task 1002339) wears the SAME band as the type and team
332
+ questions — one hairline cell per choice, the dot on the chosen ones. The only
333
+ difference is that a module carries a one-line summary under its name, so its
334
+ cells stack full width and align to the top instead of centring on one line. */
335
+ #modCards{grid-template-columns:1fr;}
336
+ #modCards .tpill{align-items:flex-start;border-left:0;padding-left:0;border-top:1px solid var(--rule-soft);}
337
+ #modCards .tpill:first-child{border-top:0;}
338
+ #modCards .tpill::before{margin-top:5px;}
339
+ #modCards .tpill b{display:block;font-weight:500;}
340
+ #modCards .tpill small{display:block;font-family:var(--font-body);font-size:14px;font-weight:400;color:var(--ink-soft);line-height:1.5;max-width:62ch;margin-top:3px;}
331
341
  .typeCard{border:0;border-radius:0;padding:4px 0 0;margin-bottom:6px;background:none;}
332
342
  .typeCard b{display:block;font-family:var(--font-body);font-size:15px;font-weight:500;color:var(--ink);margin-bottom:4px;}
333
343
  .typeCard p{font-size:14px;font-weight:400;color:var(--ink-soft);line-height:1.5;max-width:62ch;}
@@ -877,9 +887,10 @@ summary{min-height:24px;padding:3px 0;}
877
887
  <li class="now" data-step="1" aria-current="step"><span class="snum">1</span><span class="slbl">Name</span></li>
878
888
  <li data-step="2"><span class="snum">2</span><span class="slbl">Type</span></li>
879
889
  <li data-step="3"><span class="snum">3</span><span class="slbl">About</span></li>
880
- <li data-step="4"><span class="snum">4</span><span class="slbl">Home</span></li>
881
- <li data-step="5"><span class="snum">5</span><span class="slbl">Address</span></li>
882
- <li data-step="6"><span class="snum">6</span><span class="slbl">Create</span></li>
890
+ <li data-step="4"><span class="snum">4</span><span class="slbl">Modules</span></li>
891
+ <li data-step="5"><span class="snum">5</span><span class="slbl">Home</span></li>
892
+ <li data-step="6"><span class="snum">6</span><span class="slbl">Address</span></li>
893
+ <li data-step="7"><span class="snum">7</span><span class="slbl">Create</span></li>
883
894
  </ol>
884
895
 
885
896
  <div class="createSplit">
@@ -993,11 +1004,41 @@ summary{min-height:24px;padding:3px 0;}
993
1004
  </form>
994
1005
  </section>
995
1006
 
996
- <!-- step 4 · home (repo + mode) -->
1007
+ <!-- step 4 · modules (task 1002339, BV1.R20) — the picker. Skippable
1008
+ like About, and for the same reason: an owner who has met none of
1009
+ these modules should not have to audit a list to get a project.
1010
+ What Skip means here is spelled out on the panel and is TRUE by
1011
+ construction — the request omits the field, and the server resolves
1012
+ the same starter bundle this panel preselected (ADR 0237), so the
1013
+ set is the one they were shown either way.
1014
+
1015
+ The always-on core is stated as a clause, never as a row of dead
1016
+ toggles: sixteen disabled controls read as choices the owner failed
1017
+ to make, when in fact they were never choices. -->
1018
+ <section class="panel" id="panel-modules" aria-labelledby="mod-h">
1019
+ <h1 id="mod-h">What it comes with</h1>
1020
+ <p class="lede">Optional — skip it and you get the suggested set below. These are extras on top of everything every project already has, and you can switch any of them on or off later.</p>
1021
+ <form data-next="5">
1022
+ <div class="wizField"><span id="modLbl">Extras for this project</span>
1023
+ <div class="typeCards" id="modCards" role="group" aria-labelledby="modLbl"></div>
1024
+ </div>
1025
+ <div class="reassure" id="modCoreNote"></div>
1026
+ <div class="hint" id="h-modules">&nbsp;</div>
1027
+ <div class="nav">
1028
+ <button type="button" class="btn ghost" data-back="3">← Back</button>
1029
+ <span class="navPair">
1030
+ <button type="button" class="btn ghost" id="skipModules">Skip for now</button>
1031
+ <button type="submit" class="btn">Next →</button>
1032
+ </span>
1033
+ </div>
1034
+ </form>
1035
+ </section>
1036
+
1037
+ <!-- step 5 · home (repo + mode) -->
997
1038
  <section class="panel" id="panel-home" aria-labelledby="home-h">
998
1039
  <h1 id="home-h">Where the code lives</h1>
999
1040
  <p class="lede">Every project is a real GitHub repository that you own. Start from a clean slate, or bring a repo you already have.</p>
1000
- <form data-next="5">
1041
+ <form data-next="6">
1001
1042
  <div class="choices" role="group" aria-label="Starting point">
1002
1043
  <button type="button" class="choice" id="c-green" aria-pressed="true">
1003
1044
  <!-- token-built, not platform orange — public-landing/ ships wholesale to
@@ -1056,13 +1097,13 @@ summary{min-height:24px;padding:3px 0;}
1056
1097
  </div>
1057
1098
  <div class="hint" id="h-home">&nbsp;</div>
1058
1099
  <div class="nav">
1059
- <button type="button" class="btn ghost" data-back="3">← Back</button>
1100
+ <button type="button" class="btn ghost" data-back="4">← Back</button>
1060
1101
  <button type="submit" class="btn">Next →</button>
1061
1102
  </div>
1062
1103
  </form>
1063
1104
  </section>
1064
1105
 
1065
- <!-- step 5 · address — a CONFIRMATION, not a form (task 1002704: every project
1106
+ <!-- step 6 · address — a CONFIRMATION, not a form (task 1002704: every project
1066
1107
  gets its slug subdomain assigned up front). The domainless default was a
1067
1108
  proven dead-end (QA walk, task 1002702).
1068
1109
  Three choices, all legible without opening anything (task 1002898 N8):
@@ -1074,7 +1115,7 @@ summary{min-height:24px;padding:3px 0;}
1074
1115
  <section class="panel" id="panel-address" aria-labelledby="addr-h">
1075
1116
  <h1 id="addr-h">Its address in the sky</h1>
1076
1117
  <p class="lede">The handle names your project on the platform — and its address comes with it. Once it stands up, a few GitHub steps (the next screen walks you through them) switch sign-in on.</p>
1077
- <form data-next="6">
1118
+ <form data-next="7">
1078
1119
  <label class="wizField"><span>Project handle</span>
1079
1120
  <input type="text" id="f-slug" placeholder="mercury" autocomplete="off" maxlength="40" spellcheck="false">
1080
1121
  <div class="hint" id="h-slug">&nbsp;</div>
@@ -1096,20 +1137,20 @@ summary{min-height:24px;padding:3px 0;}
1096
1137
  </label>
1097
1138
  </div>
1098
1139
  <div class="nav">
1099
- <button type="button" class="btn ghost" data-back="4">← Back</button>
1140
+ <button type="button" class="btn ghost" data-back="5">← Back</button>
1100
1141
  <button type="submit" class="btn">Next →</button>
1101
1142
  </div>
1102
1143
  </form>
1103
1144
  </section>
1104
1145
 
1105
- <!-- step 6 · review + create -->
1146
+ <!-- step 7 · review + create -->
1106
1147
  <section class="panel" id="panel-review" aria-labelledby="rev-h">
1107
1148
  <h1 id="rev-h">Ready to roll</h1>
1108
1149
  <p class="lede">One tap files the request and the platform starts standing your project up — server, database, address. Here's the beat you're about to drop:</p>
1109
1150
  <ul class="review" id="reviewList"></ul>
1110
1151
  <div class="alert" id="createAlert" role="alert"></div>
1111
1152
  <div class="nav">
1112
- <button type="button" class="btn ghost" data-back="5">← Back</button>
1153
+ <button type="button" class="btn ghost" data-back="6">← Back</button>
1113
1154
  <button type="button" class="btn big" id="doCreate">Create my project</button>
1114
1155
  </div>
1115
1156
  <p class="finePrint">This files the request; the control plane does the real standup and nothing touches servers from this page. You'll get the few steps only you can do right after.</p>
@@ -2690,11 +2731,16 @@ summary{min-height:24px;padding:3px 0;}
2690
2731
  reinterpreted — a stale step 4 meant Address under v1 and Home under v3,
2691
2732
  and replaying it would land on the wrong panel with the rail already
2692
2733
  marking the skipped ones done. Bump this key on EVERY renumbering. */
2693
- var DRAFT_KEY = 'cb-create-draft-v3';
2734
+ var DRAFT_KEY = 'cb-create-draft-v4';
2694
2735
  var CREATED_KEY = 'cb-created-instance';
2695
2736
  /* `description` + `teamShape` are the optional About step (task 1002334) —
2696
2737
  both legitimately stay empty, and empty is what the create request omits. */
2697
- var state = { step: 0, name: '', projType: null, description: '', teamShape: null, mode: 'greenfield', owner: '', ownerTouched: false, repo: '', repoTouched: false, slug: '', slugTouched: false, domain: '', noAddress: false };
2738
+ /* `modules` is the picker's answer (task 1002339) and is THREE-VALUED like
2739
+ the column behind it: null = not answered (the preselect is shown and the
2740
+ request omits the field), [] = the owner switched them all off, [...] =
2741
+ what they picked. Never seed it with the preselect — that would store an
2742
+ answer nobody gave. */
2743
+ var state = { step: 0, name: '', projType: null, description: '', teamShape: null, modules: null, mode: 'greenfield', owner: '', ownerTouched: false, repo: '', repoTouched: false, slug: '', slugTouched: false, domain: '', noAddress: false };
2698
2744
  try { Object.assign(state, JSON.parse(sessionStorage.getItem(DRAFT_KEY) || '{}')); } catch (e) {}
2699
2745
  var authKnown = false; /* flips true when the boot's /me settles, either way */
2700
2746
 
@@ -2708,7 +2754,7 @@ summary{min-height:24px;padding:3px 0;}
2708
2754
  state.name = handedName.trim().slice(0, 80);
2709
2755
  state.slug = deriveSlug(state.name); state.slugTouched = false;
2710
2756
  state.repo = deriveRepo(state.name); state.repoTouched = false;
2711
- state.step = state.step >= 1 && state.step <= 6 ? state.step : 1;
2757
+ state.step = state.step >= 1 && state.step <= 7 ? state.step : 1;
2712
2758
  try { sessionStorage.setItem(DRAFT_KEY, JSON.stringify(state)); } catch (e) {}
2713
2759
  }
2714
2760
  } catch (e) {}
@@ -2900,17 +2946,18 @@ summary{min-height:24px;padding:3px 0;}
2900
2946
  }
2901
2947
 
2902
2948
  /* ── step navigation ──
2903
- Seven steps since task 1002334: gate 0 · name 1 · type 2 · about 3 ·
2904
- home 4 · address 5 · review 6 · done 7 (the rail shows 1-6; done marks it
2905
- all). About is the one step Next can pass with nothing filled in. */
2906
- var panels = { 0: 'panel-gate', 1: 'panel-name', 2: 'panel-type', 3: 'panel-detail', 4: 'panel-home', 5: 'panel-address', 6: 'panel-review', 7: 'panel-done' };
2949
+ Eight steps since task 1002339: gate 0 · name 1 · type 2 · about 3 ·
2950
+ modules 4 · home 5 · address 6 · review 7 · done 8 (the rail shows 1-7;
2951
+ done marks it all). About and Modules are the two steps Next can pass with
2952
+ nothing filled in. */
2953
+ var panels = { 0: 'panel-gate', 1: 'panel-name', 2: 'panel-type', 3: 'panel-detail', 4: 'panel-modules', 5: 'panel-home', 6: 'panel-address', 7: 'panel-review', 8: 'panel-done' };
2907
2954
  /* the rail at the done panel (task 1003244): "done" is the Create step
2908
2955
  still running — the marker stays on 5 until the project actually
2909
2956
  answers, and only then does the whole rail read finished. aria-current
2910
2957
  tells a screen reader the same thing. */
2911
2958
  function paintRail(step) {
2912
- var landed = step === 7 && !!(live && live.instStatus === 'active');
2913
- var cur = step === 7 ? 6 : step;
2959
+ var landed = step === 8 && !!(live && live.instStatus === 'active');
2960
+ var cur = step === 8 ? 7 : step;
2914
2961
  root.querySelectorAll('.steps li').forEach(function (li) {
2915
2962
  var n = Number(li.getAttribute('data-step'));
2916
2963
  var now = !landed && n === cur;
@@ -2921,7 +2968,7 @@ summary{min-height:24px;padding:3px 0;}
2921
2968
  the button role, the tab stop and the data-goto the delegated handler
2922
2969
  below reads. The done panel offers none — the project is filed, and
2923
2970
  there is nothing behind it to change. */
2924
- if (step < 7 && n < cur) {
2971
+ if (step < 8 && n < cur) {
2925
2972
  li.setAttribute('role', 'button');
2926
2973
  li.setAttribute('tabindex', '0');
2927
2974
  li.setAttribute('data-goto', String(n));
@@ -2940,7 +2987,7 @@ summary{min-height:24px;padding:3px 0;}
2940
2987
  paintRail(step);
2941
2988
  saveDraft();
2942
2989
  /* the done step takes the whole card — the bongo pair steps aside */
2943
- root.querySelector('.createSplit').classList.toggle('wizDone', step === 7);
2990
+ root.querySelector('.createSplit').classList.toggle('wizDone', step === 8);
2944
2991
  /* focus moves ONLY on a deliberate step (the hub rule setView already
2945
2992
  applies to this view: never on boot, never on a background settle) */
2946
2993
  if (moveFocus) {
@@ -2950,15 +2997,18 @@ summary{min-height:24px;padding:3px 0;}
2950
2997
  /* the repo probe is authenticated — never fire it before /me settles
2951
2998
  (a pre-auth probe 401s for strangers and, worse, could seed the
2952
2999
  draft's owner with myLogin()'s placeholder) */
2953
- if (step === 4) { syncHome(); if (authKnown) ensureRepoAccess(); }
3000
+ /* the picker repaints on entry, not once at boot: the owner can walk back
3001
+ to the Type step and change the answer the preselect is keyed to */
3002
+ if (step === 4) renderModules();
3003
+ if (step === 5) { syncHome(); if (authKnown) ensureRepoAccess(); }
2954
3004
  /* a resumed draft can land PAST the Home step with the probe never run —
2955
3005
  Create would then file a repo it promised to make but never did */
2956
- else if (step > 4 && step < 7 && authKnown && repoAccess === null) ensureRepoAccess();
3006
+ else if (step > 5 && step < 8 && authKnown && repoAccess === null) ensureRepoAccess();
2957
3007
  /* re-check on every entry, not just on typing: a resumed draft arrives here with
2958
3008
  a handle nobody has asked the server about, and a handle free ten minutes ago
2959
3009
  may not be now (task 1002898 N10) */
2960
- if (step === 5) { checkSlugAvailable(); renderAddrPanel(); }
2961
- if (step === 6) renderReview();
3010
+ if (step === 6) { checkSlugAvailable(); renderAddrPanel(); }
3011
+ if (step === 7) renderReview();
2962
3012
  }
2963
3013
 
2964
3014
  /* One delegated pair on the rail, so paintRail can add and drop data-goto
@@ -3110,6 +3160,142 @@ summary{min-height:24px;padding:3px 0;}
3110
3160
  then pressed Skip meant "I'm done here", not "throw that away". */
3111
3161
  $('skipDetail').addEventListener('click', function () { show(4, true); });
3112
3162
 
3163
+ /* ── the module picker (task 1002339, BV1.R20) ──
3164
+ ONE public read gives the panel both halves it needs: `optional` is the
3165
+ catalog it renders, `bundles` is what each project type preselects
3166
+ (GET /provisioning/starter-bundles, ADR 0237). They arrive together
3167
+ because a second round trip is exactly what would let a preselect name a
3168
+ module the panel has no row for.
3169
+
3170
+ state.modules is null until the owner SAYS something — a toggle, or Next
3171
+ off this panel. While it is null the panel paints the bundle and the
3172
+ create request omits the field, and the server resolves the same bundle
3173
+ from the same declaration. So "skip" and "accept what you were shown"
3174
+ reach the same modules by construction rather than by two copies of a
3175
+ list agreeing. Changing the project type while nothing has been said
3176
+ re-preselects for free; once the owner has answered, their answer stands.
3177
+
3178
+ No fetch failure mode is fatal: with no catalog the band renders its
3179
+ "couldn't load" clause, state.modules stays null, and the create still
3180
+ files — the server applies the bundle. A picker that could block a
3181
+ project from being created would be worse than no picker. */
3182
+ var starterBundles = null; /* the payload once it lands; null until then */
3183
+ var bundlesPromise = null; /* memoised like fetchPlan below — entering the step twice asks once */
3184
+ var bundlesFailed = false; /* asked and did not get it; distinct from not yet asked */
3185
+ /* Lazy, not at boot: /projects serves four views from one file, and the map
3186
+ has no business paying for the picker's read. */
3187
+ function ensureBundles() {
3188
+ if (bundlesPromise) return bundlesPromise;
3189
+ bundlesPromise = fetch(API + '/provisioning/starter-bundles', { credentials: 'same-origin' })
3190
+ .then(function (res) { return res.ok ? res.json() : null; })
3191
+ .then(function (data) {
3192
+ if (data && data.optional) starterBundles = data; else bundlesFailed = true;
3193
+ repaintForBundles();
3194
+ })
3195
+ .catch(function () { bundlesFailed = true; repaintForBundles(); });
3196
+ return bundlesPromise;
3197
+ }
3198
+ /* Whichever panel is asking gets repainted when the read lands. The review
3199
+ is in this list because a resumed draft can arrive there having never
3200
+ opened the picker — and a review that named the wrong extras would be the
3201
+ exact dishonesty the About row's "off the public map" clause exists to
3202
+ prevent, one screen before Create. */
3203
+ function repaintForBundles() {
3204
+ if (state.step === 4) renderModules();
3205
+ else if (state.step === 7) renderReview();
3206
+ }
3207
+ function preselectedModules() {
3208
+ if (!starterBundles) return [];
3209
+ var forType = (starterBundles.bundles || []).filter(function (b) { return b.type === state.projType; })[0];
3210
+ return forType ? forType.bundle.slice() : [];
3211
+ }
3212
+ /* What the panel paints and the review names: the owner's answer if they
3213
+ gave one, otherwise the type's preselect. */
3214
+ function pickedModules() {
3215
+ return state.modules === null ? preselectedModules() : state.modules.slice();
3216
+ }
3217
+ /* What rides the create request — undefined while nothing has been said, so
3218
+ an unanswered picker files no answer (task 1002339). */
3219
+ function modulesForCreate() {
3220
+ return state.modules === null ? undefined : state.modules.slice();
3221
+ }
3222
+ function moduleLabel(key) {
3223
+ var rows = (starterBundles && starterBundles.optional) || [];
3224
+ for (var i = 0; i < rows.length; i++) if (rows[i].key === key) return rows[i].label;
3225
+ return key;
3226
+ }
3227
+ function renderModules() {
3228
+ ensureBundles();
3229
+ var band = $('modCards');
3230
+ var note = $('modCoreNote');
3231
+ if (!starterBundles) {
3232
+ /* nothing yet. While the read is still in flight that is a blank band for
3233
+ a moment; only a read that ANSWERED badly gets the clause, so a slow
3234
+ network never tells the owner something is broken when it isn't. */
3235
+ band.innerHTML = '';
3236
+ note.textContent = bundlesFailed
3237
+ ? 'We couldn’t load the list of extras just now. Your project is created with everything it needs either way, and you can add extras from its settings.'
3238
+ : '';
3239
+ note.classList.toggle('on', bundlesFailed);
3240
+ return;
3241
+ }
3242
+ var on = pickedModules();
3243
+ band.innerHTML = (starterBundles.optional || []).map(function (m) {
3244
+ return '<button type="button" class="tpill" data-mod="' + esc(m.key) + '" aria-pressed="'
3245
+ + (on.indexOf(m.key) === -1 ? 'false' : 'true') + '"><span><b>' + esc(m.label)
3246
+ + '</b><small>' + esc(m.summary) + '</small></span></button>';
3247
+ }).join('');
3248
+ var core = (starterBundles.core || []).length;
3249
+ note.textContent = core
3250
+ ? 'Every project also starts with the same ' + core + ' core parts — the platform itself, the way it tracks work, and the builders’ hall. They are always on, so they are not part of this choice.'
3251
+ : '';
3252
+ note.classList.toggle('on', core > 0);
3253
+ }
3254
+ /* One delegated listener, because the rows are painted from the fetch. */
3255
+ $('modCards').addEventListener('click', function (ev) {
3256
+ var btn = ev.target.closest && ev.target.closest('.tpill[data-mod]');
3257
+ if (!btn) return;
3258
+ var key = btn.getAttribute('data-mod');
3259
+ var on = pickedModules();
3260
+ var want = on.indexOf(key) === -1 ? on.concat([key]) : on.filter(function (k) { return k !== key; });
3261
+ /* Re-derive in CATALOG order rather than appending: the draft, the review
3262
+ row and the array the server stores are then the same list in the same
3263
+ order, so a resumed draft or a re-read can never look like a change. */
3264
+ state.modules = (starterBundles.optional || []).map(function (m) { return m.key; })
3265
+ .filter(function (k) { return want.indexOf(k) !== -1; });
3266
+ renderModules();
3267
+ saveDraft();
3268
+ });
3269
+ /* Next off this panel CONFIRMS what is on screen — the owner looked at the
3270
+ set and accepted it, which is a different fact from never being asked. */
3271
+ root.querySelector('#panel-modules form').addEventListener('submit', function () {
3272
+ state.modules = pickedModules();
3273
+ saveDraft();
3274
+ });
3275
+ /* What the review ledger names. Three cases, and the third is the one worth
3276
+ writing down: with nothing said and the catalog not (yet) loaded we do not
3277
+ know the module NAMES, but we do know the owner will get the type's
3278
+ suggested set — so the row says that rather than "None", which would be a
3279
+ false promise on the last screen before Create. */
3280
+ function extrasRow() {
3281
+ ensureBundles();
3282
+ if (!starterBundles) {
3283
+ /* the names live in the catalog, so until it lands say the true thing
3284
+ WITHOUT them — never the raw module keys, which are jargon, and never
3285
+ "None", which would be a false promise */
3286
+ return state.modules === null
3287
+ ? 'The suggested set for the kind of project you picked'
3288
+ : 'The extras you picked';
3289
+ }
3290
+ var picked = pickedModules();
3291
+ return picked.length ? esc(picked.map(moduleLabel).join(', ')) : 'None — just the core';
3292
+ }
3293
+
3294
+ /* Skip = walk past without answering. It does NOT erase a toggle already
3295
+ made (the About step's rule), and with nothing said it leaves state.modules
3296
+ null so the server applies the bundle this panel was showing. */
3297
+ $('skipModules').addEventListener('click', function () { show(5, true); });
3298
+
3113
3299
  /* ── GitHub-integrated repo picker (adopt) + auto-create (fresh) — task 1002099 / ADR 0143.
3114
3300
  Progressive enhancement: probe GET /github/repos once we reach the Home step.
3115
3301
  200 → we hold repo access → show the picker (adopt) / name field (fresh);
@@ -3306,9 +3492,10 @@ summary{min-height:24px;padding:3px 0;}
3306
3492
  }
3307
3493
  return true;
3308
3494
  }
3309
- /* step 3 (About) has no clause: it is the optional one, and Next past an
3310
- empty answer is the whole point (task 1002334). */
3311
- if (step === 4) {
3495
+ /* steps 3 (About) and 4 (Modules) have no clause: they are the optional
3496
+ ones, and Next past an empty answer is the whole point (tasks 1002334,
3497
+ 1002339). */
3498
+ if (step === 5) {
3312
3499
  var homeErr = function (msg) { $('h-home').textContent = msg; $('h-home').className = 'hint err'; return false; };
3313
3500
  if (repoAccess === 'need-connect') return homeErr('Allow GitHub repo access to pick or create your repository — or choose “enter it manually”.');
3314
3501
  if (repoAccess === true && state.mode !== 'adopt') {
@@ -3323,7 +3510,7 @@ summary{min-height:24px;padding:3px 0;}
3323
3510
  $('h-home').innerHTML = '&nbsp;'; $('h-home').className = 'hint';
3324
3511
  return true;
3325
3512
  }
3326
- if (step === 5) {
3513
+ if (step === 6) {
3327
3514
  if (!validSlug(state.slug)) {
3328
3515
  $('h-slug').textContent = '2–40 lowercase letters, numbers, or dashes; no leading/trailing/double dash.';
3329
3516
  $('h-slug').className = 'hint err'; $('f-slug').classList.add('bad');
@@ -3396,6 +3583,10 @@ summary{min-height:24px;padding:3px 0;}
3396
3583
  ? esc(String(state.description).trim())
3397
3584
  : '<em style="font-weight:600;color:var(--warn)">not said — off the public map until you add it</em>'],
3398
3585
  ['Who’s building it', teamLabel(state.teamShape) || 'Not said'],
3586
+ /* the picker is optional too, so this row shows what the project will
3587
+ ACTUALLY come with — the owner's picks, or the preselect they walked
3588
+ past — never a blank that a Create is about to turn into a surprise */
3589
+ ['Extras', extrasRow()],
3399
3590
  ['Starting from', state.mode === 'adopt' ? 'A repo you already have' : 'A fresh, empty repo'],
3400
3591
  ['Repository', '<code>' + esc(state.owner + '/' + state.repo) + '</code>'],
3401
3592
  ['Handle', '<code>' + esc(state.slug) + '</code>'],
@@ -3487,6 +3678,10 @@ summary{min-height:24px;padding:3px 0;}
3487
3678
  answered-but-blank description to the publish gate */
3488
3679
  description: String(state.description || '').trim() || undefined,
3489
3680
  detail: state.teamShape ? { team_shape: state.teamShape } : undefined,
3681
+ /* the picker's answer (task 1002339) — omitted while unanswered, and
3682
+ the server then applies the SAME starter bundle the panel showed,
3683
+ so a skip and an accept land the same modules */
3684
+ modules: modulesForCreate(),
3490
3685
  hosting_shape: 'standalone',
3491
3686
  /* ONLY a bring-your-own domain travels (task 1002898 N2). The server owns
3492
3687
  the assigned <slug>.<apex> default (task 1002704) and applies it to an
@@ -3531,7 +3726,7 @@ summary{min-height:24px;padding:3px 0;}
3531
3726
  sessionStorage.removeItem(DRAFT_KEY);
3532
3727
  } catch (e) {}
3533
3728
  renderDone();
3534
- show(7, true);
3729
+ show(8, true);
3535
3730
  startProgress(data.instance && data.instance.id);
3536
3731
  }).catch(function (err) {
3537
3732
  if (String(err && err.message) === 'signing in') return;
@@ -3539,7 +3734,7 @@ summary{min-height:24px;padding:3px 0;}
3539
3734
  if (err && err.gotoHome) {
3540
3735
  /* A repo-creation problem (name taken / reconnect) — bounce back to the Home
3541
3736
  step with the reason, so the user fixes it where the repo is chosen. */
3542
- show(4, true);
3737
+ show(5, true);
3543
3738
  $('h-home').textContent = err.homeMsg || 'Please revisit the repository step.';
3544
3739
  $('h-home').className = 'hint err';
3545
3740
  return;
@@ -4085,7 +4280,7 @@ summary{min-height:24px;padding:3px 0;}
4085
4280
  5s tick never clobbers an open dropdown or mid-flight button) */
4086
4281
  if (inst && inst.status !== live.instStatus) {
4087
4282
  live.instStatus = inst.status; renderSteps();
4088
- if (state.step === 7) paintRail(7); /* the rail finishes only when the project answers */
4283
+ if (state.step === 8) paintRail(8); /* the rail finishes only when the project answers */
4089
4284
  }
4090
4285
  var terminal = renderProgress(inst);
4091
4286
  if (!terminal) pollTimer = setTimeout(tick, 5000);
@@ -4162,7 +4357,7 @@ summary{min-height:24px;padding:3px 0;}
4162
4357
  manifestState = created.manifest_state; manifestPending = true;
4163
4358
  }
4164
4359
  renderDone();
4165
- show(7, !!focus);
4360
+ show(8, !!focus);
4166
4361
  startProgress(created.id);
4167
4362
  resumePolls();
4168
4363
  return;
@@ -4170,7 +4365,7 @@ summary{min-height:24px;padding:3px 0;}
4170
4365
  /* before identity settles, never render past the name step: deeper steps
4171
4366
  carry authenticated side effects (the Home-step repo probe), and onAuth()
4172
4367
  re-enters to the real draft step the moment /me answers */
4173
- var step = authKnown && state.step >= 1 && state.step <= 6 ? state.step : 1;
4368
+ var step = authKnown && state.step >= 1 && state.step <= 7 ? state.step : 1;
4174
4369
  if (step === 1) {
4175
4370
  show(1, false);
4176
4371
  if (focus) $('newName').focus();
@@ -156,10 +156,10 @@
156
156
  "steps": [["wait",900],["click","[data-del-ask]"],["wait",200],["fill","#mDelName","mooring"],["wait",100],["click","[data-del-yes]"],["wait",900]],
157
157
  "expect": {"text":[["#mDel .addr","A teardown is queued for this project."],["#mName","mooring"]],"hidden":["#mDelName","[data-del-ask]"],"visible":["#mStrip .strip.flowDown"]} },
158
158
  { "name": "the wizard driver (task 1003346): name, type, about, home (fresh; the stub grants repo access, so the new repository is named from the project), address (the handle's verdict), the review as a ledger, then the filing — the done panel with the standup rail and the human steps", "auth": true, "width": 1440, "url": "/projects?view=new",
159
- "steps": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400],["click","#doCreate"],["wait",1500]],
159
+ "steps": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400],["click","#doCreate"],["wait",1500]],
160
160
  "expect": {"visible":["#panel-done","#liveProgress .prog","#liveProgress .prog .stages li.now","#humanSteps li"],"hidden":["#panel-review","#panel-name"],"text":[["#done-h","The drums are rolling"]],"attr":[["#panel-done","class","panel on"]]} },
161
161
  { "name": "the wizard driver at 390: the rail folds to two rows of three and the walk still reaches the review", "auth": true, "width": 390, "url": "/projects?view=new",
162
- "steps": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400]],
162
+ "steps": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400]],
163
163
  "expect": {"visible":["#panel-review","#reviewList li","#doCreate"],"hidden":["#panel-address"],"text":[["#rev-h","Ready to roll"]]} }
164
164
  ]
165
165
  }
@@ -30,10 +30,11 @@
30
30
  "out-new": { "auth": false, "url": "/projects?view=new", "expect": { "hidden": ["#qWrap"] } },
31
31
  "in-new-type": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-type"],"hidden":["#panel-name","#qWrap"]} },
32
32
  "in-new-about": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-detail"],"hidden":["#panel-type","#qWrap"]} },
33
- "in-new-home": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900]], "expect": {"visible":["#panel-home"],"hidden":["#panel-detail","#qWrap"]} },
34
- "in-new-address": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-home button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-address"],"hidden":["#panel-home","#qWrap"]} },
35
- "in-new-review": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-review","#reviewList li"],"hidden":["#panel-address","#qWrap"]} },
36
- "in-new-done": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400],["click","#doCreate"],["wait",1500]], "expect": {"visible":["#panel-done","#liveProgress .prog","#humanSteps li"],"hidden":["#panel-review","#qWrap"]} },
33
+ "in-new-modules": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900]], "expect": {"visible":["#panel-modules","#modCards .tpill[aria-pressed=\"true\"]","#modCoreNote"],"hidden":["#panel-detail","#qWrap"]} },
34
+ "in-new-home": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-home"],"hidden":["#panel-detail","#qWrap"]} },
35
+ "in-new-address": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-address"],"hidden":["#panel-home","#qWrap"]} },
36
+ "in-new-review": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400]], "expect": {"visible":["#panel-review","#reviewList li"],"hidden":["#panel-address","#qWrap"]} },
37
+ "in-new-done": { "auth": true, "url": "/projects?view=new", "actions": [["wait",600],["fill","#newName","Mercury"],["click","#panel-name button[type=submit]"],["wait",400],["click","#typeCards .tpill[data-type=\"game\"]"],["click","#panel-type button[type=submit]"],["wait",400],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#teamCards .tpill[data-team=\"small-team\"]"],["click","#panel-detail button[type=submit]"],["wait",900],["click","#panel-modules button[type=submit]"],["wait",400],["click","#panel-home button[type=submit]"],["wait",400],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#panel-address button[type=submit]"],["wait",400],["click","#doCreate"],["wait",1500]], "expect": {"visible":["#panel-done","#liveProgress .prog","#humanSteps li"],"hidden":["#panel-review","#qWrap"]} },
37
38
  "out-map-projects": { "auth": false, "actions": [["click", "#projTrig"], ["wait", 350]], "expect": { "expanded": true, "hidden": ["#menuProjects [data-nav=\"mine\"]"] } },
38
39
  "card": { "auth": true, "actions": [["dispatchClick", "#orbField a.worldOrb[data-origin=\"https://amazonprimea.com\"]"], ["wait", 900]], "expect": { "dialog": true, "hasClass": [["#projCard", "has-art"]], "visible": ["#cardArt", "#cardFaces", "#cardBuildersFig", "#cardShippedFig", "#cardOpen", "#cardHall"], "text": [["#cardBuilders", "11"], ["#cardShipped", "0"], ["#cardRel", "you build here"], ["#cardRuns", "amazonprimea.com"], ["#cardFaces .facesMore", "+6"]], "count": [["#cardFaces img", 5]], "href": [["#cardHall", "https://builders.amazonprimea.com/"]] } },
39
40
  "card-noart": { "auth": true, "actions": [["dispatchClick", "#orbField a.worldOrb[data-origin=\"https://emersonian-circles.cloudbongos.com\"]"], ["wait", 600]], "expect": { "dialog": true, "hasClass": [["#projCard", "has-art", false]], "hidden": ["#cardArt", "#cardFaces", "#cardHall"], "visible": ["#cardBuildersFig", "#cardShippedFig", "#cardOpen"], "text": [["#cardShipped", "30+"], ["#cardRel", "you build here"]] } },
@@ -108,6 +108,32 @@ const FEEDS = {
108
108
  const AUTHED = { '/my-projects': 'my-projects' };
109
109
  const UNAUTH = { error: { code: 'unauthenticated', message: 'unauthenticated' } };
110
110
 
111
+ // GET /provisioning/starter-bundles as the create wizard's module picker reads it
112
+ // (task 1002339 / BV1.R19's route, ADR 0237). A fixture rather than an import —
113
+ // the kit stub deliberately requires no module — kept honest by
114
+ // tests/provisioning_starter_bundles.mjs, which reads this file and compares it
115
+ // against the real declaration. `core` is the count the panel's clause names, so
116
+ // only its length matters here; `optional` and `bundles` are byte-for-byte.
117
+ const STARTER_BUNDLES_FIXTURE = {
118
+ core: [
119
+ 'autonomy', 'builder-settings', 'copy-desk', 'economy', 'government', 'grading',
120
+ 'hall-ui', 'ideas', 'lifecycle', 'memory', 'onboarding', 'public-landing',
121
+ 'security', 'sessions', 'status-ui', 'ui-design',
122
+ ],
123
+ bundles: [
124
+ { type: 'game', bundle: ['dev-box', 'discord'] },
125
+ { type: 'research', bundle: ['agents'] },
126
+ { type: 'business', bundle: ['agents', 'dev-box'] },
127
+ { type: 'non-profit', bundle: ['discord'] },
128
+ { type: 'not-sure', bundle: [] },
129
+ ],
130
+ optional: [
131
+ { key: 'agents', label: 'Extra agents', summary: 'More AI agents alongside the ones every project gets, for reading, research and review.' },
132
+ { key: 'dev-box', label: 'Dev boxes', summary: 'A cloud workspace where a change can be tried out before anyone else sees it. Nothing is charged until someone asks for one.' },
133
+ { key: 'discord', label: 'Discord', summary: 'A Discord server that carries your build news, and lets people send ideas and bug reports back.' },
134
+ ],
135
+ };
136
+
111
137
  function json(res, status, body) { res.writeHead(status, { 'content-type': 'application/json', 'cache-control': 'no-store' }); res.end(JSON.stringify(body)); }
112
138
  function fixture(name, res, origin) {
113
139
  if (FAIL.has(name)) return json(res, 500, { error: { code: 'stub_failure', message: `FAIL=${name}` } });
@@ -277,6 +303,12 @@ const server = http.createServer((req, res) => {
277
303
  }
278
304
  if (rest.startsWith('/github/repo')) return AUTH ? json(res, 200, { repo: { private: false, admin: true } }) : json(res, 401, UNAUTH);
279
305
  if (rest.startsWith('/provisioning/slug-available')) return json(res, 200, { available: true }); /* the wizard's handle check (task 1003346): every handle is free on the stub */
306
+ /* the module picker's one read (task 1002339). A FIXTURE, like everything
307
+ else here — the stub imports no module — but not a free-hand one:
308
+ tests/provisioning_starter_bundles.mjs pins it against the real
309
+ starter-bundles.js declaration, so the picture the kit renders and the
310
+ payload the route serves cannot drift apart silently. */
311
+ if (rest === '/provisioning/starter-bundles') return json(res, 200, STARTER_BUNDLES_FIXTURE);
280
312
  if (rest === '/auth/logout') return json(res, 200, { ok: true });
281
313
  if (rest.startsWith('/auth/web/start')) { res.writeHead(302, { location: PREFIX }); return res.end(); }
282
314
  if (rest === '/instance') { const b = pages.size ? pages.values().next().value.brand : {}; return json(res, 200, { brand: { productName: (b && b.productName) || '', project: {} }, modules: {} }); }
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.514",
3
+ "version": "1.19.516",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.514",
9
+ "version": "1.19.516",
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.514",
3
+ "version": "1.19.516",
4
4
  "description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
5
5
  "license": "AGPL-3.0-or-later",
6
6
  "main": "src/platform-server.js",
package/src/module-api.js CHANGED
@@ -45,7 +45,7 @@ const { validateOrRespond, LIMITS, parseId, asyncHandler, corsPublicGet, parsePa
45
45
  // there. scripts/gds/bump-version.js still rewrites the literal below; it appends
46
46
  // the entry to that file. Look for a version's history there, not here.
47
47
  // ---------------------------------------------------------------------------
48
- const CORE_VERSION = '1.19.514'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
48
+ const CORE_VERSION = '1.19.516'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
49
49
 
50
50
  // A namespaced logger so a module's log lines are attributable + consistent.
51
51
  // Usage: const log = api.logger('dev-box'); log.info('mounted');