@bongos/core 1.21.17 → 1.21.19

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.
@@ -590,7 +590,6 @@ summary{min-height:24px;padding:3px 0;}
590
590
 
591
591
  /* GitHub repo picker (task 1002099): hairline rows, the chosen one marked on
592
592
  the accent; the private badge in the label face */
593
- #ghConnect .btn{margin-top:4px;}
594
593
  .ghlist{margin-top:4px;max-height:220px;overflow-y:auto;border:0;border-top:1px solid var(--rule);border-radius:0;background:none;}
595
594
  .ghlist:empty{display:none;}
596
595
  .ghrow{display:flex;align-items:center;gap:8px;width:100%;min-height:40px;text-align:left;background:none;border:0;border-bottom:1px solid var(--rule-soft);padding:10px 0;font:inherit;font-size:14px;color:var(--ink);cursor:pointer;}
@@ -1637,11 +1636,11 @@ summary{min-height:24px;padding:3px 0;}
1637
1636
  <!-- GitHub-integrated repo picker (adopt) + auto-create (fresh) — task 1002099.
1638
1637
  Without repo access we fall back to the manual owner/repo inputs
1639
1638
  (#manualHome), which stay authoritative. -->
1640
- <div id="ghConnect" class="reassure">
1641
- <p id="ghConnectMsg">&nbsp;</p>
1642
- <button type="button" class="btn" id="ghConnectBtn">Allow repo access</button>
1643
- <button type="button" class="linklike" id="ghManualToggle">or enter it manually</button>
1644
- </div>
1639
+ <!-- no repo-access ask in the wizard (task 1004555): every hub sign-in asks for repo
1640
+ access (ADR 0364), and a missing token is fetched by one automatic GitHub
1641
+ round-trip. Only when that hop could not help does this line show, above
1642
+ the manual fields. -->
1643
+ <div id="ghNoAccess" class="hint" hidden>We couldn’t read your GitHub repositories just now, so enter the repo by hand below.</div>
1645
1644
  <div id="ghBusy" class="hint" hidden>Reading your GitHub repositories…</div>
1646
1645
  <div id="ghAdopt" class="wizField" hidden>
1647
1646
  <span>Pick one of your repositories</span>
@@ -1667,8 +1666,6 @@ summary{min-height:24px;padding:3px 0;}
1667
1666
  </label>
1668
1667
  <!-- visibility stated BEFORE GitHub's own radio is the first place it appears (task 1002721) -->
1669
1668
  <div class="hint" style="grid-column:1 / -1;">Creating the repo yourself on GitHub? Public or private is your choice there — both work here, and we never change it.</div>
1670
- <!-- the way back from the manual fallback (task 1003683) -->
1671
- <button type="button" class="linklike" id="ghManualBack" style="grid-column:1 / -1;" hidden>Use my GitHub repositories instead</button>
1672
1669
  </div>
1673
1670
  <div class="hint" id="h-home">&nbsp;</div>
1674
1671
  <button type="button" class="btn small rowUse" data-row-use>Use this</button>
@@ -5276,44 +5273,39 @@ summary{min-height:24px;padding:3px 0;}
5276
5273
  403 → offer to grant repo access (a ?repos=1 re-sign-in);
5277
5274
  503 (server can't store the token) or any error → the manual inputs.
5278
5275
  #manualHome (the classic owner/repo fields) is the always-safe fallback. ── */
5279
- var repoAccess = null; /* null=unknown/loading · true · 'need-connect' · 'unavailable' · 'manual' */
5276
+ var repoAccess = null; /* null=unknown/loading · true · 'unavailable' (manual fields) */
5280
5277
  var repoList = []; /* the user's repos (from the probe) — feeds the picker + the fresh-name clash hint */
5281
5278
  var ghProbed = false;
5282
5279
 
5283
- function isManualHome() { return repoAccess === 'manual' || repoAccess === 'unavailable'; }
5280
+ function isManualHome() { return repoAccess === 'unavailable'; }
5284
5281
  function validRepoName(s) { return /^[A-Za-z0-9._-]{1,100}$/.test(String(s || '')); }
5285
5282
  function myLogin() { return (me && me.github_login) || 'you'; }
5286
5283
 
5287
- function connectRepos(e) {
5288
- if (e) e.preventDefault();
5289
- /* Same-tab navigation so the sessionStorage draft round-trips back to the Home step. */
5284
+ /* The one automatic GitHub round-trip (task 1004555). The token is missing (a session
5285
+ from before ADR 0364, or a sign-out elsewhere), and GitHub answers a repo sign-in at
5286
+ once for anyone who already approved it — so the page goes and fetches it instead of
5287
+ asking. Same tab, so the sessionStorage draft comes back with its step (task 1004549).
5288
+ ONCE per tab session: a hop that came back still without access cannot be helped by a
5289
+ second, so the caller falls back to the manual fields instead of looping. A probe that
5290
+ answers 200 clears the mark, so a later expiry may hop again. */
5291
+ var HOP_KEY = 'cb-repo-hop';
5292
+ function repoHopDone() { try { return sessionStorage.getItem(HOP_KEY) === '1'; } catch (e) { return true; } }
5293
+ function hopForRepoAccess() {
5294
+ if (repoHopDone()) return false;
5295
+ try { sessionStorage.setItem(HOP_KEY, '1'); } catch (e) { return false; }
5296
+ saveDraft();
5290
5297
  window.location.assign(API + '/auth/web/start?return=/projects%3Fview%3Dnew&repos=1');
5298
+ return true;
5291
5299
  }
5292
- $('ghConnectBtn').addEventListener('click', connectRepos);
5293
- $('ghManualToggle').addEventListener('click', function () { repoAccess = 'manual'; syncHome(); $('f-owner').focus(); });
5294
- $('ghManualBack').addEventListener('click', function () { repoAccess = 'need-connect'; syncHome(); $('ghConnectBtn').focus(); });
5295
5300
 
5296
5301
  function syncHome() {
5297
- var need = repoAccess === 'need-connect';
5298
5302
  var ok = repoAccess === true;
5299
5303
  var adopt = state.mode === 'adopt';
5300
5304
  $('ghBusy').hidden = !!repoAccess; /* busy only while still probing (null) */
5301
- $('ghConnect').classList.toggle('on', need); /* .reassure box: shown via .on (not hidden — .reassure sets display:none) */
5305
+ $('ghNoAccess').hidden = repoAccess !== 'unavailable';
5302
5306
  $('ghAdopt').hidden = !(ok && adopt);
5303
5307
  $('ghFresh').hidden = !(ok && !adopt);
5304
5308
  $('manualHome').hidden = !isManualHome();
5305
- /* only when the visitor CHOSE manual: with 'unavailable' the server cannot hold a
5306
- token, so a connect round-trip would land back here having changed nothing */
5307
- $('ghManualBack').hidden = repoAccess !== 'manual';
5308
- if (need) {
5309
- /* Informed consent (ADR 0155): this grants `repo`, which GitHub's own screen
5310
- words as full control of ALL your repositories — so say plainly why we ask
5311
- and what we do with it, rather than letting GitHub's wording be the first
5312
- the user hears of it. Private repos are the reason the scope is this wide. */
5313
- $('ghConnectMsg').textContent = adopt
5314
- ? 'You’re signed in — creating your project needs one more GitHub permission. GitHub will ask for repository access (it words this as access to all your repositories — that is the only scope it offers that can write to a private repo). We use it to read the repo you pick and push the setup commit to it. Nothing else is touched.'
5315
- : 'You’re signed in — creating your project needs one more GitHub permission. GitHub will ask for repository access (it words this as access to all your repositories — the narrower option cannot write to private repos). We use it to create your new empty repo and push the setup commit to it. Nothing else is touched.';
5316
- }
5317
5309
  if (ok && adopt) initAdoptPicker();
5318
5310
  if (ok && !adopt) syncFresh();
5319
5311
  }
@@ -5413,13 +5405,16 @@ summary{min-height:24px;padding:3px 0;}
5413
5405
 
5414
5406
  function ensureRepoAccess() {
5415
5407
  if (ghProbed && repoAccess !== null) return; /* already known this session */
5416
- if (isManualHome()) return; /* user chose manual — don't nag */
5417
5408
  repoAccess = null; syncHome(); /* show "reading your repos…" */
5418
5409
  fetch(API + '/github/repos', { credentials: 'same-origin' })
5419
5410
  .then(function (res) {
5420
5411
  ghProbed = true;
5421
- if (res.status === 200) return res.json().then(function (d) { repoList = (d && d.repos) || []; repoAccess = true; });
5422
- if (res.status === 403 || res.status === 401) { repoAccess = 'need-connect'; return; }
5412
+ if (res.status === 200) {
5413
+ try { sessionStorage.removeItem(HOP_KEY); } catch (e) {}
5414
+ return res.json().then(function (d) { repoList = (d && d.repos) || []; repoAccess = true; });
5415
+ }
5416
+ /* no stored token: fetch it (one hop), else the manual fields */
5417
+ if (res.status === 403 || res.status === 401) { if (!hopForRepoAccess()) repoAccess = 'unavailable'; return; }
5423
5418
  repoAccess = 'unavailable'; return; /* 503 or anything else → manual fallback */
5424
5419
  })
5425
5420
  .catch(function () { ghProbed = true; repoAccess = 'unavailable'; })
@@ -5901,7 +5896,6 @@ summary{min-height:24px;padding:3px 0;}
5901
5896
  if (!works.length) return homeErr('Pick what already exists, then your main one.');
5902
5897
  if (works.indexOf(a.main_work) === -1) return homeErr('Mark which of them is your main one.');
5903
5898
  }
5904
- if (repoAccess === 'need-connect') return homeErr('Allow GitHub repo access to pick or create your repository — or choose “enter it manually”.');
5905
5899
  if (repoAccess === true && state.mode !== 'adopt') {
5906
5900
  /* Fresh with access: just the new repo NAME (owner is the signed-in account). */
5907
5901
  if (!validRepoName(state.repo)) return homeErr('Name the new repository (letters, numbers, and . _ -).');
@@ -6346,9 +6340,12 @@ summary{min-height:24px;padding:3px 0;}
6346
6340
  }).then(function (res) {
6347
6341
  return res.json().catch(function () { return {}; }).then(function (data) {
6348
6342
  if (res.status === 401 || res.status === 403) {
6349
- repoAccess = 'need-connect';
6350
- var e0 = new Error('reconnect'); e0.gotoHome = true;
6351
- e0.homeMsg = 'Your GitHub authorization expired — reconnect on the Home step to create the repo.';
6343
+ /* the token went between the probe and Create: the hop fetches it again and
6344
+ brings the founder back to this screen; if it cannot, the manual fields */
6345
+ var hopped = hopForRepoAccess();
6346
+ if (!hopped) { repoAccess = 'unavailable'; syncHome(); }
6347
+ var e0 = new Error('reconnect'); e0.gotoHome = !hopped;
6348
+ e0.homeMsg = 'We couldn’t create the repo on GitHub just now — enter one you made yourself below.';
6352
6349
  throw e0;
6353
6350
  }
6354
6351
  if (!res.ok) {
@@ -0,0 +1,184 @@
1
+ {
2
+ "_": "GET /government/constitution as the street reads it for a drafted amendment (task 1004530): the board block and the who-decides-what table, resolved by modules/government/decision-rules.js resolveAll (Release already amended to a sitting; every other building as today). Regenerate from the resolver, never by hand.",
3
+ "board": {
4
+ "membership": "rank:metic+",
5
+ "pass_rule": "majority",
6
+ "window_minutes": 4320,
7
+ "close_early_on_full_turnout": true
8
+ },
9
+ "history": [],
10
+ "headcount": 6,
11
+ "decision_rules": {
12
+ "areas": [
13
+ {
14
+ "area": "charter",
15
+ "name": "Charter",
16
+ "decided_by": "board",
17
+ "rule": "majority",
18
+ "rule_words": "majority vote",
19
+ "who": "the board (metic and above (metic, archon))",
20
+ "as_today": true,
21
+ "sitting": false,
22
+ "gate": null,
23
+ "today": "the board, under the constitution in force",
24
+ "acts": [],
25
+ "set_by_item_id": null,
26
+ "updated_at": null
27
+ },
28
+ {
29
+ "area": "rules",
30
+ "name": "Rules",
31
+ "decided_by": "gate",
32
+ "rule": "decide",
33
+ "rule_words": "decides alone",
34
+ "who": "whoever may change the project settings",
35
+ "as_today": true,
36
+ "sitting": false,
37
+ "gate": "project.settings.manage",
38
+ "today": "whoever may change the project settings",
39
+ "acts": [],
40
+ "set_by_item_id": null,
41
+ "updated_at": null
42
+ },
43
+ {
44
+ "area": "disputes",
45
+ "name": "Disputes",
46
+ "decided_by": "gate",
47
+ "rule": "decide",
48
+ "rule_words": "decides alone",
49
+ "who": "whoever may judge reports",
50
+ "as_today": true,
51
+ "sitting": false,
52
+ "gate": "security.report.adjudicate",
53
+ "today": "whoever may judge reports",
54
+ "acts": [],
55
+ "set_by_item_id": null,
56
+ "updated_at": null
57
+ },
58
+ {
59
+ "area": "people",
60
+ "name": "People",
61
+ "decided_by": "gate",
62
+ "rule": "decide",
63
+ "rule_words": "decides alone",
64
+ "who": "whoever may manage ranks",
65
+ "as_today": true,
66
+ "sitting": false,
67
+ "gate": "government.manage",
68
+ "today": "whoever may manage ranks",
69
+ "acts": [
70
+ "POST /government/assignments",
71
+ "DELETE /government/assignments/:builderId/:rankKey"
72
+ ],
73
+ "set_by_item_id": null,
74
+ "updated_at": null
75
+ },
76
+ {
77
+ "area": "planning",
78
+ "name": "Planning",
79
+ "decided_by": "gate",
80
+ "rule": "decide",
81
+ "rule_words": "decides alone",
82
+ "who": "whoever may create goals",
83
+ "as_today": true,
84
+ "sitting": false,
85
+ "gate": "goal.create",
86
+ "today": "whoever may create goals",
87
+ "acts": [],
88
+ "set_by_item_id": null,
89
+ "updated_at": null
90
+ },
91
+ {
92
+ "area": "automation",
93
+ "name": "Automation",
94
+ "decided_by": "gate",
95
+ "rule": "decide",
96
+ "rule_words": "decides alone",
97
+ "who": "whoever may switch unattended building on and off",
98
+ "as_today": true,
99
+ "sitting": false,
100
+ "gate": "autonomy.fence.manage",
101
+ "today": "whoever may switch unattended building on and off",
102
+ "acts": [],
103
+ "set_by_item_id": null,
104
+ "updated_at": null
105
+ },
106
+ {
107
+ "area": "review",
108
+ "name": "Review",
109
+ "decided_by": "gate",
110
+ "rule": "decide",
111
+ "rule_words": "decides alone",
112
+ "who": "whoever may mark work as meeting its checks",
113
+ "as_today": true,
114
+ "sitting": false,
115
+ "gate": "criterion.satisfy",
116
+ "today": "whoever may mark work as meeting its checks",
117
+ "acts": [],
118
+ "set_by_item_id": null,
119
+ "updated_at": null
120
+ },
121
+ {
122
+ "area": "release",
123
+ "name": "Release",
124
+ "decided_by": "rank:archon",
125
+ "rule": "unanimous",
126
+ "rule_words": "everyone agrees",
127
+ "who": "exactly archon",
128
+ "as_today": false,
129
+ "sitting": true,
130
+ "gate": "core.pin.move",
131
+ "today": "whoever may put a release live",
132
+ "acts": [],
133
+ "set_by_item_id": "3",
134
+ "updated_at": null
135
+ },
136
+ {
137
+ "area": "treasury",
138
+ "name": "Treasury",
139
+ "decided_by": "gate",
140
+ "rule": "decide",
141
+ "rule_words": "decides alone",
142
+ "who": "whoever may set spending limits",
143
+ "as_today": true,
144
+ "sitting": false,
145
+ "gate": "builder.budget.set",
146
+ "today": "whoever may set spending limits",
147
+ "acts": [],
148
+ "set_by_item_id": null,
149
+ "updated_at": null
150
+ }
151
+ ],
152
+ "rules": [
153
+ {
154
+ "key": "decide",
155
+ "words": "decides alone"
156
+ },
157
+ {
158
+ "key": "first",
159
+ "words": "first yes wins"
160
+ },
161
+ {
162
+ "key": "consent",
163
+ "words": "passes unless someone objects"
164
+ },
165
+ {
166
+ "key": "majority",
167
+ "words": "majority vote"
168
+ },
169
+ {
170
+ "key": "unanimous",
171
+ "words": "everyone agrees"
172
+ },
173
+ {
174
+ "key": "signoff",
175
+ "words": "signed off by someone who did not build it"
176
+ },
177
+ {
178
+ "key": "vacant",
179
+ "words": "no one holds this yet"
180
+ }
181
+ ],
182
+ "changed_by": "a passed amendment only — POST /government/board/decision-rules files one"
183
+ }
184
+ }
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.21.17",
3
+ "version": "1.21.19",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.21.17",
9
+ "version": "1.21.19",
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.21.17",
3
+ "version": "1.21.19",
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",
@@ -8858,5 +8858,17 @@
8858
8858
  "id": "1004554",
8859
8859
  "text": "Bongos no longer plays any sounds: no ship bell, credits chime, fanfare or end-of-turn chime. Settings → Sound now just says they're off for now."
8860
8860
  }
8861
+ ],
8862
+ "1.21.18": [
8863
+ {
8864
+ "id": "1004555",
8865
+ "text": "Project setup no longer shows an \"Allow repo access\" button — if the site needs GitHub access it fetches it in a quick reload and brings you straight back to where you were."
8866
+ }
8867
+ ],
8868
+ "1.21.19": [
8869
+ {
8870
+ "id": "1004530",
8871
+ "text": "Inside any building on the street, \"change how this works\" now drafts a real amendment. It shows how the building decides today crossed out, and the proposed change written in. You pick who decides and how from the project's o"
8872
+ }
8861
8873
  ]
8862
8874
  }
package/src/module-api.js CHANGED
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
75
75
  // MAJOR (see allowBoxScope below): passes the request through untouched.
76
76
  function deprecatedNoopMiddleware(_req, _res, next) { next(); }
77
77
 
78
- const CORE_VERSION = '1.21.17'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
78
+ const CORE_VERSION = '1.21.19'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
79
79
 
80
80
  // A namespaced logger so a module's log lines are attributable + consistent.
81
81
  // Usage: const log = api.logger('discord'); log.info('mounted');
@@ -377,7 +377,7 @@ test('the page holds the prototype\'s header, the street, the readings and the c
377
377
  for (const id of ['hudNeeds', 'hudMoving', 'hudShare', 'streetMode', 'streetSigns', 'streetPins', 'streetDoors', 'streetCard']) assert.match(html, new RegExp(`id="${id}"`), id);
378
378
  assert.doesNotMatch(html, /app-sidebar|shell\.js/, 'no hall chrome, as the city');
379
379
  const scripts = [...html.matchAll(/<script src="\/builders\/([a-z-]+)\.js/g)].map((m) => m[1]);
380
- assert.deepEqual(scripts, ['city-draw', 'street-draw', 'street-card', 'street-inside', 'street'], 'the shared kit loads first, then the card (task 1004528) and the interiors (task 1004526)');
380
+ assert.deepEqual(scripts, ['city-draw', 'street-draw', 'street-card', 'street-inside', 'street-amend', 'street'], 'the shared kit loads first, then the card (task 1004528), the interiors (task 1004526) and the drafted amendment (task 1004530)');
381
381
  assert.equal(City.STREET_HREF, '/builders/street', 'the city\'s door is this page');
382
382
  });
383
383