@bongos/core 1.24.43 → 1.24.44

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 (62) hide show
  1. package/.bongos-core.json +70 -75
  2. package/docs/copy-inventory.md +33 -65
  3. package/docs/copy-registry.json +78 -437
  4. package/docs/file-map.md +1 -1
  5. package/docs/module-api-changelog.md +2 -0
  6. package/docs/page-inventory.json +8 -36
  7. package/docs/page-readings.json +188 -245
  8. package/modules/hall-ui/public/ai-studio.html +2 -2
  9. package/modules/hall-ui/public/atlas.html +2 -2
  10. package/modules/hall-ui/public/blockers.html +2 -2
  11. package/modules/hall-ui/public/board-room.html +2 -2
  12. package/modules/hall-ui/public/city.html +2 -2
  13. package/modules/hall-ui/public/collab.html +2 -2
  14. package/modules/hall-ui/public/commands.html +2 -2
  15. package/modules/hall-ui/public/copy-desk.html +2 -2
  16. package/modules/hall-ui/public/craft-mode.js +1 -1
  17. package/modules/hall-ui/public/deploy.html +2 -2
  18. package/modules/hall-ui/public/goal-map.html +2 -2
  19. package/modules/hall-ui/public/goals.html +14 -2
  20. package/modules/hall-ui/public/government.html +2 -2
  21. package/modules/hall-ui/public/idea.html +2 -2
  22. package/modules/hall-ui/public/ideas.html +2 -2
  23. package/modules/hall-ui/public/index.html +2 -2
  24. package/modules/hall-ui/public/market-watch-item.html +2 -2
  25. package/modules/hall-ui/public/market-watch-rules.html +2 -2
  26. package/modules/hall-ui/public/market-watch.html +2 -2
  27. package/modules/hall-ui/public/modules.html +2 -2
  28. package/modules/hall-ui/public/money.html +2 -2
  29. package/modules/hall-ui/public/my-agents.html +2 -2
  30. package/modules/hall-ui/public/primer.html +2 -2
  31. package/modules/hall-ui/public/profile.html +2 -2
  32. package/modules/hall-ui/public/project-settings.html +2 -2
  33. package/modules/hall-ui/public/roadmap.css +7 -138
  34. package/modules/hall-ui/public/roadmap.js +35 -293
  35. package/modules/hall-ui/public/roster.html +2 -2
  36. package/modules/hall-ui/public/settings.html +2 -2
  37. package/modules/hall-ui/public/shell.js +3 -3
  38. package/modules/hall-ui/public/street.html +2 -2
  39. package/modules/hall-ui/public/studio.html +2 -2
  40. package/modules/hall-ui/public/task.html +2 -2
  41. package/modules/hall-ui/public/thinking.html +2 -2
  42. package/modules/hall-ui/public/tweak-editor.html +2 -2
  43. package/modules/hall-ui/public/watch.html +2 -2
  44. package/modules/hall-ui/public/work.html +3 -3
  45. package/modules/onboarding/founding-mode.js +2 -1
  46. package/package-lock.json +2 -2
  47. package/package.json +1 -1
  48. package/release-notes.json +6 -0
  49. package/scripts/bongos/gen-hall-sitemap.js +1 -0
  50. package/src/bongos/serve-internal.js +6 -6
  51. package/src/module-api.js +1 -1
  52. package/tests/craft_mode_nav.mjs +2 -1
  53. package/tests/founding_mode.mjs +5 -3
  54. package/tests/hall_goals_band.mjs +88 -0
  55. package/tests/hall_kit_charts.mjs +4 -3
  56. package/tests/hall_nav.mjs +5 -2
  57. package/tests/hall_page_gate_map.mjs +5 -1
  58. package/tests/hall_reading_world.mjs +17 -18
  59. package/tests/hall_sitemap.mjs +2 -1
  60. package/tests/nav_permission_atoms.mjs +3 -2
  61. package/modules/hall-ui/public/roadmap.html +0 -58
  62. package/modules/hall-ui/public/roadmap.states.json +0 -31
@@ -1,29 +1,23 @@
1
- // roadmap.js — the Roadmap page, "Command deck" layout (task 1002426, owner-
2
- // approved redesign from the mockup review; supersedes the flat progress-bar
3
- // stack introduced in task 1757).
1
+ // roadmap.js — the version band at the top of the Goals page (task 1005051).
4
2
  //
5
- // Shape: a program-level hero (whole-build rollup) → versions GROUPED BY their
6
- // real status (planning=Staged · building=In flight · frozen=Parked ·
7
- // shipped=Shipped), with the dominant in-flight version featured full-width. The
8
- // goal layer is SUMMARIZED per version — a status bar (achieved / in progress /
9
- // not started) plus only the goals active right now — instead of nesting every
10
- // goal + criterion inline (BONGOS-V1 alone carries ~30 goals; the old flat list
11
- // was the big-picture killer). The full per-version goal list, inline join/leave,
12
- // and Archon drag-to-reorder now live on the Goals page, which "See all N goals"
13
- // links to.
3
+ // It was the Roadmap page, the "command deck" (task 1002426): a program hero,
4
+ // every version grouped by status, and a summarized goal layer per version. Since
5
+ // the owner's call of one version at a time (task 1004821) that page mostly
6
+ // repeated Goals, so on 2026-11-03 (idea 1001315, item 4) the owner folded it in:
7
+ // Goals now OPENS with the version's progress and the timeline, and /roadmap
8
+ // redirects to /goals. The goal summaries did not come along — the goals are the
9
+ // page right below — and neither did the staged / parked / shipped cards; the
10
+ // timeline still draws every version that ran.
14
11
  //
15
- // Reads (all GET): /public/progress (lifecycle rollup + counts + %, public),
16
- // /public/versions (started/shipped dates, public), and — for every
17
- // non-shipped version only — /goals?version=X + /goals/:id (per-goal criteria to
18
- // split in-progress from not-started). Shipped versions render as a pill strip
19
- // with no goal fetch. No writes. Server page gate: SIGNEDIN_BUILDERS_PAGE_RE.
12
+ // Reads (all GET): /public/progress (lifecycle rollup + counts + %),
13
+ // /public/versions (started/shipped dates) and /versions/focus. No goal reads —
14
+ // Goals already makes them. No writes. The page gate is Goals' own.
20
15
  (() => {
21
16
  'use strict';
22
17
 
23
18
  const API = '/api/bongos';
24
19
  const api = window.BongosClient.createClient({ baseUrl: '', credentials: 'same-origin', throwOnError: false });
25
20
  const { escapeHtml, $, fmtNum } = window.OTB;
26
- const toast = (msg) => window.OTB.toast(msg, { duration: 2200, clear: true });
27
21
 
28
22
  async function getJSON(path) {
29
23
  const res = await api.request('GET', path);
@@ -42,12 +36,8 @@
42
36
  planning: { label: 'Staged', cls: 'planning' },
43
37
  frozen: { label: 'Parked', cls: 'frozen' },
44
38
  };
45
- // The instance's Goals page (root-relative — resolves on the builders host).
46
- const GOALS_URL = '/goals';
47
- const goalDeepLink = (id) => `/goals#/goal/${encodeURIComponent(String(id))}`;
48
- // The TASK half of the roadmap's click-through (task 1002440). A goal title has
49
- // always deep-linked (goalDeepLink above), but the progress block — "X of Y
50
- // tasks" + the lifecycle bar — was inert, so the roadmap could show you how much
39
+ // The version's click-through to its tasks (task 1002440): the progress block —
40
+ // "X of Y tasks" + the lifecycle bar — was inert, so it could show you how much
51
41
  // work a version had and give you no way to go look at it. The Tasks board already
52
42
  // restores this filter from the query string (restoreUrlFilter('f-version') in
53
43
  // work.js), so the link lands pre-filtered with NO server or work.js change.
@@ -69,26 +59,8 @@
69
59
  <span class="rm-lbar__s rm-lbar__s--co" style="width:${pctOf(l.co, t)}%"></span>
70
60
  <span class="rm-lbar__s rm-lbar__s--cm" style="width:${pctOf(l.cm, t)}%"></span></div>`;
71
61
  }
72
- function gbar(g) {
73
- return `<div class="rm-gbar" role="img" aria-label="${g.ach} achieved, ${g.prog} in progress, ${g.plan} not started of ${g.total} goals">
74
- <span class="rm-gbar__s rm-gbar__s--ach" style="width:${pctOf(g.ach, g.total)}%"></span>
75
- <span class="rm-gbar__s rm-gbar__s--prog" style="width:${pctOf(g.prog, g.total)}%"></span>
76
- <span class="rm-gbar__s rm-gbar__s--plan" style="width:${pctOf(g.plan, g.total)}%"></span></div>`;
77
- }
78
-
79
62
  const arrowSvg = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12h14M13 6l6 6-6 6"/></svg>';
80
63
 
81
- // BV1.R15 (task 1003602, goal 1000086, ADR 0250 §3): "the overrides are
82
- // counted and shown on the version."
83
- //
84
- // RENDERS NOTHING AT ZERO, and that is the design rather than a shortcut. A
85
- // permanent "0 overrides" line is chrome a reader learns to skip, and the
86
- // moment it becomes "2 overrides" it would go on being skipped. Appearing only
87
- // when the gate was actually overridden makes the line itself the signal.
88
- //
89
- // It names no goals: which ones they are is one click away on the goals board
90
- // (they carry an "admitted" chip), and a roadmap card that grew a second list
91
- // stops being a roadmap.
92
64
  function admissionNote(v) {
93
65
  const n = Number(v && v.admissions) || 0;
94
66
  if (n === 0) return '';
@@ -97,49 +69,8 @@
97
69
  + `</p>`;
98
70
  }
99
71
 
100
- // The goals block that sits inside an in-flight / featured card. `active` are
101
- // the in-progress goals (sat>0), already sorted; we show up to 4 and let
102
- // "See all" carry the rest.
103
- function goalsBlock(v) {
104
- const g = v.goals;
105
- if (!g || g.total === 0) {
106
- return `<div class="rm-goals__frame"><div class="rm-goals__h"><span class="t">Goals</span></div>
107
- <p class="rm-gnone">No goals yet.</p></div>`;
108
- }
109
- const active = g.active.slice(0, 4);
110
- const rows = active.map((x) => {
111
- const ready = x.total > 0 && x.sat >= x.total;
112
- return `<div class="rm-goal">
113
- <span class="rm-goal__dot"></span>
114
- <a class="rm-goal__t" href="${goalDeepLink(x.id)}" title="Open goal #${escapeHtml(String(x.id))}">${escapeHtml(x.title || 'Untitled goal')}</a>
115
- ${ready ? '<span class="rm-goal__ready">criteria met</span>' : ''}
116
- <span class="rm-goal__meter"><i style="width:${pctOf(x.sat, x.total)}%"></i></span>
117
- <span class="rm-goal__n">${x.sat}/${x.total}</span>
118
- </div>`;
119
- }).join('');
120
- return `<div class="rm-goals__frame">
121
- <div class="rm-goals__h"><span class="t">Goals</span><span class="n">${g.total} total</span></div>
122
- ${gbar(g)}
123
- <div class="rm-gdist">
124
- <span><i class="rm-ach"></i><b>${g.ach}</b> achieved</span>
125
- <span><i class="rm-prog"></i><b>${g.prog}</b> in progress</span>
126
- <span><i class="rm-plan"></i><b>${g.plan}</b> not started</span>
127
- </div>
128
- ${active.length ? `<div class="rm-ginp"><div class="rm-ginp__cap">Active right now</div>${rows}</div>`
129
- : '<p class="rm-gnone" style="margin-top:12px">No goal has started yet.</p>'}
130
- ${admissionNote(v)}
131
- <a class="rm-gmore" href="${GOALS_URL}">See all ${g.total} goals ${arrowSvg}</a>
132
- </div>`;
133
- }
134
-
135
- // "View tasks ->" for a version's progress block. One helper, used by all three
136
- // task-bearing cards, so the label + a11y text cannot drift between them.
137
- //
138
- // GUARDED ON total > 0 deliberately: a staged version has no tasks yet, and a
139
- // link that filters the board to nothing is worse than no link — it reads as a
140
- // broken page rather than an empty backlog. stagedCard has no .rm-cardprog at
141
- // all, so it never calls this; the guard is for a building/parked version whose
142
- // task count is still zero.
72
+ // "View tasks ->" for a version's progress block. GUARDED ON total > 0: a link
73
+ // that filters the board to nothing reads as a broken page, not an empty backlog.
143
74
  function tasksMore(v) {
144
75
  const total = Number(v && v.total) || 0;
145
76
  if (total <= 0) return '';
@@ -149,8 +80,8 @@
149
80
 
150
81
  // The focus star (task 1004551): the project's default version, stored server-side.
151
82
  const focus = { id: null };
152
- // The star on each card only SHOWS the focus. Only one version is built at a
153
- // time, so the page asks nobody to pick one (owner, 2026-10-23, task 1004821).
83
+ // The star only SHOWS the focus. Only one version is built at a time, so the
84
+ // page asks nobody to pick one (owner, 2026-10-23, task 1004821).
154
85
  function starHtml(v) {
155
86
  const on = focus.id === v.id;
156
87
  const tip = on ? 'The focus version' : 'Not the focus version';
@@ -172,31 +103,10 @@
172
103
 
173
104
  // ---- card renderers -------------------------------------------------------
174
105
 
175
- function featCard(v) {
106
+ // One in-flight version: its phase, id and name, how far its tasks are, and
107
+ // the link to them on the Tasks board. The focus version leads.
108
+ function versionCard(v) {
176
109
  return `<article class="rm-card rm-card--feat">
177
- <div class="rm-featgrid">
178
- <div class="rm-featgrid__l">
179
- <div class="rm-feat-top">
180
- <div class="rt">
181
- <div class="rm-row">${phase(v)}<span class="rm-id">${escapeHtml(v.id)}</span>${starHtml(v)}</div>
182
- <h3 class="rm-name">${escapeHtml(v.name)}</h3>
183
- <p class="rm-desc rm-desc--clamp">${escapeHtml(v.desc)}</p>
184
- </div>
185
- ${ring(v)}
186
- </div>
187
- <div class="rm-cardprog">
188
- <div class="rm-count"><span><b>${fmtNum(v.shipped)}</b> of <b>${fmtNum(v.total)}</b> tasks</span><span>${fmtNum(v.criteria)} criteria · since ${ym(v.start)}</span></div>
189
- ${lbar(v)}
190
- ${tasksMore(v)}
191
- </div>
192
- </div>
193
- <div class="rm-goals">${goalsBlock(v)}</div>
194
- </div>
195
- </article>`;
196
- }
197
-
198
- function flightCard(v) {
199
- return `<article class="rm-card">
200
110
  <div class="rm-feat-top">
201
111
  <div class="rt">
202
112
  <div class="rm-row">${phase(v)}<span class="rm-id">${escapeHtml(v.id)}</span>${starHtml(v)}</div>
@@ -210,79 +120,10 @@
210
120
  ${lbar(v)}
211
121
  ${tasksMore(v)}
212
122
  </div>
213
- <div style="margin-top:13px">${goalsBlock(v)}</div>
214
- </article>`;
215
- }
216
-
217
- // Staged (planning) card — goals-forward. No task progress yet, so it leads
218
- // with the seeded plan instead of a lifecycle bar.
219
- function stagedCard(v) {
220
- const g = v.goals;
221
- const seeded = (g && g.all ? g.all : []).slice(0, 4);
222
- const rows = seeded.map((x) => `
223
- <div class="rm-goal"><span class="rm-goal__dot" style="background:var(--rm-plan)"></span>
224
- <a class="rm-goal__t" href="${goalDeepLink(x.id)}">${escapeHtml(x.title || 'Untitled goal')}</a>
225
- <span class="rm-goal__seed">seeded</span></div>`).join('');
226
- const totalGoals = g ? g.total : 0;
227
- const goalsFrame = totalGoals === 0
228
- ? `<div class="rm-goals__frame"><div class="rm-goals__h"><span class="t">Goals</span></div>
229
- <p class="rm-gnone">No goals seeded yet.</p></div>`
230
- : `<div class="rm-goals__frame">
231
- <div class="rm-goals__h"><span class="t">Goals</span><span class="n">${totalGoals} seeded</span></div>
232
- ${gbar(g)}
233
- <div class="rm-gdist">
234
- <span><i class="rm-ach"></i><b>${g.ach}</b> achieved</span>
235
- <span><i class="rm-prog"></i><b>${g.prog}</b> in progress</span>
236
- <span><i class="rm-plan"></i><b>${g.plan}</b> seeded</span>
237
- </div>
238
- <div class="rm-ginp"><div class="rm-ginp__cap">The seeded plan</div>${rows}</div>
239
- <a class="rm-gmore" href="${GOALS_URL}">See all ${totalGoals} goals ${arrowSvg}</a>
240
- </div>`;
241
- return `<article class="rm-card rm-card--staged" style="grid-column:1 / -1">
242
- <div class="rm-featgrid">
243
- <div class="rm-featgrid__l">
244
- <div class="rm-row">${phase(v)}<span class="rm-id">${escapeHtml(v.id)}</span>${starHtml(v)}</div>
245
- <h3 class="rm-name">${escapeHtml(v.name)}</h3>
246
- <p class="rm-desc rm-desc--clamp">${escapeHtml(v.desc)}</p>
247
- <div class="rm-staged-state">
248
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 3v4M9 6l3-3 3 3"/><rect x="4" y="9" width="16" height="12" rx="2"/></svg>
249
- <div><div class="s1">Not started</div>
250
- <div class="s2"><b>${totalGoals}</b> goals · <b>${fmtNum(v.criteria)}</b> criteria · no tasks yet</div></div>
251
- </div>
252
- </div>
253
- <div class="rm-goals">${goalsFrame}</div>
254
- </div>
255
- </article>`;
256
- }
257
-
258
- function parkedCard(v) {
259
- return `<article class="rm-card rm-card--parked" style="grid-column:1 / -1">
260
- <div class="rm-feat-top">
261
- <div class="rt">
262
- <div class="rm-row">${phase(v)}<span class="rm-id">${escapeHtml(v.id)}</span>${starHtml(v)}</div>
263
- <h3 class="rm-name">${escapeHtml(v.name)}</h3>
264
- <p class="rm-desc rm-desc--clamp">${escapeHtml(v.desc)}</p>
265
- </div>
266
- ${ring(v)}
267
- </div>
268
- <div class="rm-cardprog">
269
- <div class="rm-count"><span><b>${fmtNum(v.shipped)}</b> of <b>${fmtNum(v.total)}</b> tasks</span><span>${fmtNum(v.criteria)} criteria · started ${ym(v.start)}</span></div>
270
- ${lbar(v)}
271
- ${tasksMore(v)}
272
- </div>
273
- <div class="rm-parknote">
274
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="4" y="4" width="6" height="16" rx="1"/><rect x="14" y="4" width="6" height="16" rx="1"/></svg>
275
- <div>On hold. Nobody is claiming work here.</div>
276
- </div>
123
+ ${admissionNote(v)}
277
124
  </article>`;
278
125
  }
279
126
 
280
- // The program in time (task 1003447). The SERIES is roadmap-timeline.js's —
281
- // pure builders in their own file so a test runs the real arithmetic over the
282
- // real fixture — and the SECTION chrome is this page's, which is the same seam
283
- // the chart kit keeps everywhere: the module draws, the page frames. Missing
284
- // module or fewer than two started versions renders nothing at all, so the
285
- // deck reads exactly as it did before rather than growing an empty panel.
286
127
  function timelineHtml(all, now) {
287
128
  const mod = window.OTBRoadmapTimeline;
288
129
  const card = mod ? mod.timelineCardHtml(all, now) : null;
@@ -297,78 +138,6 @@
297
138
  </section>`;
298
139
  }
299
140
 
300
- function heroHtml(flight) {
301
- const A = flight.reduce((a, v) => ({
302
- total: a.total + v.total, shipped: a.shipped + v.shipped,
303
- sh: a.sh + v.life.sh, co: a.co + v.life.co, cm: a.cm + v.life.cm,
304
- }), { total: 0, shipped: 0, sh: 0, co: 0, cm: 0 });
305
- const G = flight.reduce((a, v) => ({
306
- total: a.total + (v.goals ? v.goals.total : 0), ach: a.ach + (v.goals ? v.goals.ach : 0),
307
- prog: a.prog + (v.goals ? v.goals.prog : 0), plan: a.plan + (v.goals ? v.goals.plan : 0),
308
- }), { total: 0, ach: 0, prog: 0, plan: 0 });
309
- const heroPct = Math.round(pctOf(A.shipped, A.total));
310
- const heroRem = A.total - A.shipped;
311
- const goalsRow = G.total > 0 ? `
312
- <div class="rm-hgoals">
313
- <span class="glab">Goals</span>
314
- <div class="rm-gbar">
315
- <span class="rm-gbar__s rm-gbar__s--ach" style="width:${pctOf(G.ach, G.total)}%"></span>
316
- <span class="rm-gbar__s rm-gbar__s--prog" style="width:${pctOf(G.prog, G.total)}%"></span>
317
- <span class="rm-gbar__s rm-gbar__s--plan" style="width:${pctOf(G.plan, G.total)}%"></span>
318
- </div>
319
- <span class="gleg"><b>${G.ach}</b> achieved · <b>${G.prog}</b> in progress · <b>${G.plan}</b> not started <span style="color:var(--ink-faint)">of ${G.total}</span></span>
320
- </div>` : '';
321
- return `<div class="rm-hero">
322
- <div class="rm-hero__top">
323
- <div>
324
- <div class="rm-big"><span class="n">${heroPct}</span><span class="u">%</span></div>
325
- <div class="rm-lab">of tasks shipped</div>
326
- </div>
327
- <div>
328
- <div class="rm-lbar rm-hbar" role="img" aria-label="${A.sh} shipped, ${A.co} confirmed, ${A.cm} completed, ${heroRem} remaining of ${A.total}">
329
- <span class="rm-lbar__s rm-lbar__s--sh" style="width:${pctOf(A.sh, A.total)}%"></span>
330
- <span class="rm-lbar__s rm-lbar__s--co" style="width:${pctOf(A.co, A.total)}%"></span>
331
- <span class="rm-lbar__s rm-lbar__s--cm" style="width:${pctOf(A.cm, A.total)}%"></span>
332
- </div>
333
- <div class="rm-stats">
334
- <div class="rm-stat"><span class="v">${flight.length}</span><span class="k">versions in flight</span></div>
335
- <div class="rm-stat"><span class="v sh">${fmtNum(A.sh)}</span><span class="k">shipped</span></div>
336
- <div class="rm-stat"><span class="v co">${fmtNum(A.co)}</span><span class="k">confirmed</span></div>
337
- <div class="rm-stat"><span class="v cm">${fmtNum(A.cm)}</span><span class="k">completed</span></div>
338
- <div class="rm-stat"><span class="v">${fmtNum(heroRem)}</span><span class="k">tasks remaining</span></div>
339
- </div>
340
- </div>
341
- </div>
342
- ${goalsRow}
343
- </div>`;
344
- }
345
-
346
- // ---- data ------------------------------------------------------------------
347
-
348
- // Enrich a version with its goal summary. `list` gives titles + status +
349
- // sort_order (1 request); `/goals/:id` gives per-goal criteria (needed to split
350
- // in-progress from not-started + to fill the "active now" list).
351
- async function goalSummary(versionId) {
352
- try {
353
- const list = await getJSON(`${API}/goals?version=${encodeURIComponent(versionId)}`);
354
- const goals = (list.goals || []).filter((g) => g.status !== 'archived');
355
- if (!goals.length) return { total: 0, ach: 0, prog: 0, plan: 0, active: [], all: [] };
356
- const details = await Promise.all(goals.map((g) => getJSON(`${API}/goals/${encodeURIComponent(g.id)}`).catch(() => null)));
357
- const enriched = goals.map((g, i) => {
358
- const d = details[i];
359
- const crit = d && Array.isArray(d.criteria) ? d.criteria : [];
360
- const sat = crit.filter((c) => c.satisfied).length;
361
- return { id: g.id, title: g.title, status: g.status, sort: Number(g.sort_order) || 0, sat, total: crit.length };
362
- }).sort((a, b) => a.sort - b.sort);
363
- const ach = enriched.filter((g) => g.status === 'achieved').length;
364
- const active = enriched.filter((g) => g.status !== 'achieved' && g.sat > 0);
365
- const plan = enriched.filter((g) => g.status !== 'achieved' && g.sat === 0).length;
366
- return { total: enriched.length, ach, prog: active.length, plan, active, all: enriched };
367
- } catch (_) {
368
- return { total: 0, ach: 0, prog: 0, plan: 0, active: [], all: [] };
369
- }
370
- }
371
-
372
141
  // Merge /public/progress (counts, %, lifecycle) with /public/versions (dates)
373
142
  // into one version model the renderers consume.
374
143
  function mergeVersions(progress, versions) {
@@ -403,6 +172,7 @@
403
172
 
404
173
  async function render() {
405
174
  const root = $('#rm-root');
175
+ if (!root) return;
406
176
  const [progress, versions] = await Promise.all([
407
177
  loadFocus(),
408
178
  getJSON(`${API}/public/progress`).then((d) => d.progress || []),
@@ -410,59 +180,31 @@
410
180
  ]).then(([, p, v]) => [p, v]);
411
181
  const all = mergeVersions(progress, versions);
412
182
 
413
- // Goal summaries only for versions we actually show goals on (everything but
414
- // shipped) — a real perf win over fetching goals for the whole history.
415
- const needGoals = all.filter((v) => v.status !== 'shipped');
416
- await Promise.all(needGoals.map(async (v) => { v.goals = await goalSummary(v.id); }));
417
-
418
- const flight = all.filter((v) => v.status === 'building').sort((a, b) => b.total - a.total);
419
- const staged = all.filter((v) => v.status === 'planning').sort((a, b) => b.total - a.total);
420
- const parked = all.filter((v) => v.status === 'frozen').sort((a, b) => b.pct - a.pct);
421
- const shipped = all.filter((v) => v.status === 'shipped')
422
- .sort((a, b) => (new Date(b.ship || 0)) - (new Date(a.ship || 0)));
183
+ // The focus version leads, then any other version in flight (most tasks first).
184
+ const flight = all.filter((v) => v.status === 'building')
185
+ .sort((a, b) => ((b.id === focus.id) - (a.id === focus.id)) || (b.total - a.total));
423
186
 
424
- let html = '';
187
+ let html = flight.length
188
+ ? sectionHtml('flight', 'In flight', flight.length, '', flight.length === 1
189
+ ? versionCard(flight[0])
190
+ : `<div class="rm-grid">${flight.map(versionCard).join('')}</div>`)
191
+ : '<p class="rm-gnone">No version is in flight right now.</p>';
425
192
 
426
- if (flight.length) {
427
- html += heroHtml(flight);
428
- const feat = flight[0]; // dominant in-flight version (most tasks)
429
- const rest = flight.slice(1);
430
- const inner = featCard(feat) + (rest.length ? `<div class="rm-grid" style="margin-top:16px">${rest.map(flightCard).join('')}</div>` : '');
431
- html += sectionHtml('flight', 'In flight', flight.length, '', inner);
432
- } else {
433
- html += `<div class="rm-hero"><div class="rm-lab" style="max-width:none">No version is in flight right now.</div></div>`;
434
- }
435
-
436
- // Directly under the hero: both are program-level readings, and this is the
437
- // one that answers "how fast" rather than "how much".
193
+ // Under the version: how fast, beside how much.
438
194
  html += timelineHtml(all, Date.now());
439
195
 
440
- if (staged.length) {
441
- html += sectionHtml('staged', 'Staged', staged.length, '', `<div class="rm-grid">${staged.map(stagedCard).join('')}</div>`);
442
- }
443
- if (parked.length) {
444
- html += sectionHtml('parked', 'Parked', parked.length, '', `<div class="rm-grid">${parked.map(parkedCard).join('')}</div>`);
445
- }
446
- if (shipped.length) {
447
- const pills = shipped.map((v) => `<a class="rm-shippill" href="${GOALS_URL}" title="${escapeHtml(v.name)}"><span class="ok">✓</span><span class="nm">${escapeHtml(v.id)}</span><span class="mt">${fmtNum(v.shipped)} tasks · ${ym(v.ship)}</span></a>`).join('');
448
- html += sectionHtml('shipped', 'Shipped', shipped.length, '', `<div class="rm-shipstrip">${pills}</div>`);
449
- }
450
-
451
- if (!all.length) html = `<div class="profile__placeholder">No versions declared yet.</div>`;
196
+ if (!all.length) html = '<p class="rm-gnone">No versions declared yet.</p>';
452
197
  root.innerHTML = html;
453
198
  }
454
199
 
455
200
  async function boot() {
456
- const sub = $('#roadmap-sub');
457
- if (sub) sub.textContent = 'Every version, and how far along it is.';
458
201
  try {
459
202
  await render();
460
203
  } catch (err) {
461
204
  if (err && err.message === 'redirecting to auth') return;
462
- console.error('[roadmap] load failed:', err);
205
+ console.error('[goals] version band failed:', err);
463
206
  const root = $('#rm-root');
464
- if (root) root.innerHTML = `<div class="profile__placeholder">The roadmap could not be loaded right now. Reload to try again.</div>`;
465
- toast('The roadmap failed to load. Reload to try again.');
207
+ if (root) root.innerHTML = '<p class="rm-gnone">The version\'s progress could not be loaded right now. Reload to try again.</p>';
466
208
  }
467
209
  }
468
210
 
@@ -58,8 +58,8 @@
58
58
  chrome exists before the page script runs. -->
59
59
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
60
60
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
61
- <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
62
- <script src="/builders/shell.js?v=2026-11-05-numbers"></script>
61
+ <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
62
+ <script src="/builders/shell.js?v=2026-11-05-goals"></script>
63
63
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
64
64
  <script src="/builders/palette.js" defer></script>
65
65
  <!-- the branding pack's currency label, loaded BEFORE roster.js. -->
@@ -393,8 +393,8 @@
393
393
  chrome exists before the page scripts run. -->
394
394
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
395
395
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
396
- <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
397
- <script src="/builders/shell.js?v=2026-11-05-numbers"></script>
396
+ <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
397
+ <script src="/builders/shell.js?v=2026-11-05-goals"></script>
398
398
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
399
399
  <script src="/builders/palette.js" defer></script>
400
400
  <!-- shared component kit (window.OTBKit) — section rail (task 2166). -->
@@ -214,11 +214,11 @@
214
214
  // because the href does: Goals' own "see which goals wait on each other"
215
215
  // link and every bookmark land here unchanged.
216
216
  { id: 'goal-map', label: 'Sky', icon: 'sky', href: '/goal-map', gate: 'member' },
217
- { id: 'roadmap', label: 'Roadmap', icon: 'roadmap', href: '/roadmap', gate: 'member' },
217
+ // task 1005051: Roadmap folded into Goals (its version band); /roadmap redirects there.
218
218
  // task 1003455 (goal 1000089): the open-blocker queue — what's stuck on the
219
- // owner. Lives with the project's own state (Roadmap/Goals/Tasks), not You
219
+ // owner. Lives with the project's own state (Goals/Tasks), not You
220
220
  // or Community, because its subject is the project, not a person. Its one
221
- // data source (GET /blockers) is deliberately public, like Roadmap's — but
221
+ // data source (GET /blockers) is deliberately public, like the version band's — but
222
222
  // the NAV FLAG stays 'signed' on the roster precedent: an ungated
223
223
  // page can still gate cosmetically wider in the sidebar than the API it
224
224
  // fronts, showing the link to signed-in builders while a stranger who
@@ -230,8 +230,8 @@
230
230
 
231
231
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
232
232
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
233
- <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
234
- <script src="/builders/shell.js?v=2026-11-05-numbers"></script>
233
+ <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
234
+ <script src="/builders/shell.js?v=2026-11-05-goals"></script>
235
235
  <script src="/builders/palette.js" defer></script>
236
236
  <script src="/builders/gov-living.js?v=2026-10-09-positions1"></script>
237
237
  <script src="/builders/gov-feeds.js?v=2026-10-09-living3"></script>
@@ -224,8 +224,8 @@
224
224
 
225
225
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
226
226
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
227
- <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
228
- <script src="/builders/shell.js?v=2026-11-05-numbers"></script>
227
+ <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
228
+ <script src="/builders/shell.js?v=2026-11-05-goals"></script>
229
229
  <script src="/builders/palette.js" defer></script>
230
230
  <!-- The kit, for its ONE renderer: the full-size ship-time visual figure
231
231
  (task 1003829), which the older reviews still show. -->
@@ -58,8 +58,8 @@
58
58
  time so the chrome exists before the deferred page scripts execute. -->
59
59
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
60
60
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
61
- <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
62
- <script src="/builders/shell.js?v=2026-11-05-numbers"></script>
61
+ <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
62
+ <script src="/builders/shell.js?v=2026-11-05-goals"></script>
63
63
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
64
64
  <script src="/builders/palette.js" defer></script>
65
65
  <!-- task-detail.js still owns the #/idea/N and #/blocker/N deep-link overlay
@@ -140,8 +140,8 @@
140
140
 
141
141
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
142
142
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
143
- <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
144
- <script src="/builders/shell.js?v=2026-11-05-numbers"></script>
143
+ <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
144
+ <script src="/builders/shell.js?v=2026-11-05-goals"></script>
145
145
  <script src="/builders/palette.js" defer></script>
146
146
 
147
147
  <script src="/builders/idea-objects.js?v=2026-09-27-split"></script>
@@ -144,8 +144,8 @@
144
144
 
145
145
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
146
146
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
147
- <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
148
- <script src="/builders/shell.js?v=2026-11-05-numbers"></script>
147
+ <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
148
+ <script src="/builders/shell.js?v=2026-11-05-goals"></script>
149
149
  <script src="/builders/palette.js" defer></script>
150
150
  <script src="/builders/brand-holes.js?v=2026-09-29-aqfix"></script>
151
151
  <script src="/builders/tweak-editor-lib.js?v=2026-09-29-tw27"></script>
@@ -270,8 +270,8 @@
270
270
  chrome exists before the page script runs. -->
271
271
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
272
272
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
273
- <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
274
- <script src="/builders/shell.js?v=2026-11-05-numbers"></script>
273
+ <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
274
+ <script src="/builders/shell.js?v=2026-11-05-goals"></script>
275
275
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
276
276
  <script src="/builders/palette.js" defer></script>
277
277
  <!-- shared component kit (window.OTBKit) — tabs, ledger, pager (task 2166). -->
@@ -113,7 +113,7 @@
113
113
  <!-- IN PROGRESS — the live in-flight list (active claims, all builders). -->
114
114
  <section class="scroll" id="wip-scroll" aria-labelledby="wip-h" hidden>
115
115
  <h2 id="wip-h" class="scroll__h">In Progress<span class="count-pill" id="wip-count" hidden></span></h2>
116
- <p class="scroll__lede">What is being built right now. See <a href="/roadmap">Roadmap</a> for version-level progress and goals.</p>
116
+ <p class="scroll__lede">What is being built right now. See <a href="/goals">Goals</a> for the version's progress and its goals.</p>
117
117
 
118
118
  <table class="ledger" id="wip-table">
119
119
  <thead><tr><th></th><th>#</th><th>Task</th><th>Builder</th><th>For</th><th></th></tr></thead>
@@ -173,8 +173,8 @@
173
173
  time so the chrome exists before the deferred page scripts execute. -->
174
174
  <script src="/builders/dom-utils.js?v=2026-11-05-numbers"></script>
175
175
  <script src="/builders/shell-trail.js?v=2026-11-05-numbers"></script>
176
- <script src="/builders/craft-mode.js?v=2026-11-05-numbers"></script>
177
- <script src="/builders/shell.js?v=2026-11-05-numbers"></script>
176
+ <script src="/builders/craft-mode.js?v=2026-11-05-goals"></script>
177
+ <script src="/builders/shell.js?v=2026-11-05-goals"></script>
178
178
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
179
179
  <script src="/builders/palette.js" defer></script>
180
180
  <!-- task 1004727: the ONE karma vote control every task/idea list renders -->
@@ -34,7 +34,8 @@ const STAGES = Object.freeze(['decide', 'scope', 'strategize', 'charter', 'legal
34
34
  // 'community' wakes only when the planet is ALIVE, which is when founding mode switches
35
35
  // off entirely, so during founding it is always asleep.
36
36
  const WAKES = Object.freeze({
37
- scope: Object.freeze(['roadmap', 'goals', 'goal-map']),
37
+ // task 1005051: Roadmap folded into Goals, so scope wakes Goals and its map.
38
+ scope: Object.freeze(['goals', 'goal-map']),
38
39
  strategize: Object.freeze(['work']),
39
40
  charter: Object.freeze(['board-room']),
40
41
  // task 1005046: legalize woke Economy and legislation woke Ranks. Both pages
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.24.43",
3
+ "version": "1.24.44",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.24.43",
9
+ "version": "1.24.44",
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.24.43",
3
+ "version": "1.24.44",
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",
@@ -11221,5 +11221,11 @@
11221
11221
  "id": "1005050",
11222
11222
  "text": "Your numbers are on one page now: your Profile. The separate Accomplishments page is gone from the sidebar. The parts only you should see — your rank and how to reach the next one, how your work was reviewed, your karma histor"
11223
11223
  }
11224
+ ],
11225
+ "1.24.44": [
11226
+ {
11227
+ "id": "1005051",
11228
+ "text": "Goals now opens with the version's progress and the timeline of every version, so the separate Roadmap page is gone from the sidebar. Old Roadmap links open Goals. The Tasks board's 'see the version' line points at Goals too."
11229
+ }
11224
11230
  ]
11225
11231
  }
@@ -64,6 +64,7 @@ const ROUTES = {
64
64
  '/diagrams': { to: '/primer', why: 'now a Primer chapter, redirects; its images still serve (task 1005046)' },
65
65
  '/sessions': { to: '/watch', why: 'now a Watch tab, redirects (task 1005049)' },
66
66
  '/gate': { to: '/watch', why: 'Autobongos, now a Watch tab, redirects (task 1005049)' },
67
+ '/roadmap': { to: '/goals', why: 'now the top of Goals, redirects (task 1005051)' },
67
68
  };
68
69
  // Targets that leave the hall on purpose: sign-in, the hub's /account (platform-server.js;
69
70
  // settings.js points the link at the hub), the API, module assets, and any file with an extension.