@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.
- package/.bongos-core.json +70 -75
- package/docs/copy-inventory.md +33 -65
- package/docs/copy-registry.json +78 -437
- package/docs/file-map.md +1 -1
- package/docs/module-api-changelog.md +2 -0
- package/docs/page-inventory.json +8 -36
- package/docs/page-readings.json +188 -245
- package/modules/hall-ui/public/ai-studio.html +2 -2
- package/modules/hall-ui/public/atlas.html +2 -2
- package/modules/hall-ui/public/blockers.html +2 -2
- package/modules/hall-ui/public/board-room.html +2 -2
- package/modules/hall-ui/public/city.html +2 -2
- package/modules/hall-ui/public/collab.html +2 -2
- package/modules/hall-ui/public/commands.html +2 -2
- package/modules/hall-ui/public/copy-desk.html +2 -2
- package/modules/hall-ui/public/craft-mode.js +1 -1
- package/modules/hall-ui/public/deploy.html +2 -2
- package/modules/hall-ui/public/goal-map.html +2 -2
- package/modules/hall-ui/public/goals.html +14 -2
- package/modules/hall-ui/public/government.html +2 -2
- package/modules/hall-ui/public/idea.html +2 -2
- package/modules/hall-ui/public/ideas.html +2 -2
- package/modules/hall-ui/public/index.html +2 -2
- package/modules/hall-ui/public/market-watch-item.html +2 -2
- package/modules/hall-ui/public/market-watch-rules.html +2 -2
- package/modules/hall-ui/public/market-watch.html +2 -2
- package/modules/hall-ui/public/modules.html +2 -2
- package/modules/hall-ui/public/money.html +2 -2
- package/modules/hall-ui/public/my-agents.html +2 -2
- package/modules/hall-ui/public/primer.html +2 -2
- package/modules/hall-ui/public/profile.html +2 -2
- package/modules/hall-ui/public/project-settings.html +2 -2
- package/modules/hall-ui/public/roadmap.css +7 -138
- package/modules/hall-ui/public/roadmap.js +35 -293
- package/modules/hall-ui/public/roster.html +2 -2
- package/modules/hall-ui/public/settings.html +2 -2
- package/modules/hall-ui/public/shell.js +3 -3
- package/modules/hall-ui/public/street.html +2 -2
- package/modules/hall-ui/public/studio.html +2 -2
- package/modules/hall-ui/public/task.html +2 -2
- package/modules/hall-ui/public/thinking.html +2 -2
- package/modules/hall-ui/public/tweak-editor.html +2 -2
- package/modules/hall-ui/public/watch.html +2 -2
- package/modules/hall-ui/public/work.html +3 -3
- package/modules/onboarding/founding-mode.js +2 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/scripts/bongos/gen-hall-sitemap.js +1 -0
- package/src/bongos/serve-internal.js +6 -6
- package/src/module-api.js +1 -1
- package/tests/craft_mode_nav.mjs +2 -1
- package/tests/founding_mode.mjs +5 -3
- package/tests/hall_goals_band.mjs +88 -0
- package/tests/hall_kit_charts.mjs +4 -3
- package/tests/hall_nav.mjs +5 -2
- package/tests/hall_page_gate_map.mjs +5 -1
- package/tests/hall_reading_world.mjs +17 -18
- package/tests/hall_sitemap.mjs +2 -1
- package/tests/nav_permission_atoms.mjs +3 -2
- package/modules/hall-ui/public/roadmap.html +0 -58
- package/modules/hall-ui/public/roadmap.states.json +0 -31
|
@@ -1,29 +1,23 @@
|
|
|
1
|
-
// roadmap.js — the
|
|
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
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
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 +
|
|
16
|
-
// /public/versions (started/shipped dates
|
|
17
|
-
//
|
|
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
|
|
46
|
-
|
|
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
|
-
//
|
|
101
|
-
// the
|
|
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
|
|
153
|
-
//
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
414
|
-
|
|
415
|
-
|
|
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
|
-
|
|
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 (
|
|
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('[
|
|
205
|
+
console.error('[goals] version band failed:', err);
|
|
463
206
|
const root = $('#rm-root');
|
|
464
|
-
if (root) root.innerHTML =
|
|
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-
|
|
62
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
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-
|
|
397
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
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
|
-
|
|
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 (
|
|
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
|
|
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-
|
|
234
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
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-
|
|
228
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
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-
|
|
62
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
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-
|
|
144
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
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-
|
|
148
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
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-
|
|
274
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
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="/
|
|
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-
|
|
177
|
-
<script src="/builders/shell.js?v=2026-11-05-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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",
|
package/release-notes.json
CHANGED
|
@@ -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.
|