@bongos/core 1.24.11 → 1.24.12
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 +138 -73
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +6 -0
- package/clients/bongos-client/index.cjs +6 -0
- package/clients/bongos-client/index.d.ts +8 -0
- package/clients/bongos-client/index.mjs +6 -0
- package/docs/api/openapi.json +175 -3
- package/docs/api-reference.md +5 -2
- package/docs/copy-inventory.md +92 -20
- package/docs/copy-registry.json +777 -33
- package/docs/module-api-changelog.md +2 -0
- package/docs/page-inventory.json +68 -4
- package/docs/page-readings.json +856 -802
- package/modules/hall-ui/public/ai-studio.html +1 -1
- package/modules/hall-ui/public/atlas.html +1 -1
- package/modules/hall-ui/public/blockers.html +1 -1
- package/modules/hall-ui/public/board-room.html +1 -1
- package/modules/hall-ui/public/city.html +1 -1
- package/modules/hall-ui/public/collab.html +1 -1
- package/modules/hall-ui/public/commands.html +1 -1
- package/modules/hall-ui/public/copy-desk.html +1 -1
- package/modules/hall-ui/public/craft-mode.js +2 -2
- package/modules/hall-ui/public/deploy.html +1 -1
- package/modules/hall-ui/public/diagrams.html +1 -1
- package/modules/hall-ui/public/drachmae.html +1 -1
- package/modules/hall-ui/public/fleet.html +1 -1
- package/modules/hall-ui/public/gate.html +1 -1
- package/modules/hall-ui/public/goal-map.html +1 -1
- package/modules/hall-ui/public/goals.html +1 -1
- package/modules/hall-ui/public/government.html +1 -1
- package/modules/hall-ui/public/idea.html +1 -1
- package/modules/hall-ui/public/ideas.html +1 -1
- package/modules/hall-ui/public/index.html +1 -1
- package/modules/hall-ui/public/market-watch-item.html +83 -0
- package/modules/hall-ui/public/market-watch-item.js +150 -0
- package/modules/hall-ui/public/market-watch-item.states.json +143 -0
- package/modules/hall-ui/public/market-watch-lib.js +243 -0
- package/modules/hall-ui/public/market-watch-rules.html +1 -1
- package/modules/hall-ui/public/market-watch-settings.html +1 -1
- package/modules/hall-ui/public/market-watch.css +169 -0
- package/modules/hall-ui/public/market-watch.html +111 -0
- package/modules/hall-ui/public/market-watch.js +129 -0
- package/modules/hall-ui/public/market-watch.states.json +14 -0
- package/modules/hall-ui/public/modules.html +1 -1
- package/modules/hall-ui/public/my-agents.html +1 -1
- package/modules/hall-ui/public/primer.html +1 -1
- package/modules/hall-ui/public/profile.html +1 -1
- package/modules/hall-ui/public/project-settings.html +1 -1
- package/modules/hall-ui/public/ranks.html +1 -1
- package/modules/hall-ui/public/roadmap.html +1 -1
- package/modules/hall-ui/public/roster.html +1 -1
- package/modules/hall-ui/public/sessions.html +1 -1
- package/modules/hall-ui/public/settings.html +1 -1
- package/modules/hall-ui/public/shell.js +3 -2
- package/modules/hall-ui/public/street.html +1 -1
- package/modules/hall-ui/public/studio.html +1 -1
- package/modules/hall-ui/public/task.html +1 -1
- package/modules/hall-ui/public/thinking.html +1 -1
- package/modules/hall-ui/public/tweak-editor.html +1 -1
- package/modules/hall-ui/public/watch.html +1 -1
- package/modules/hall-ui/public/work.html +1 -1
- package/modules/hall-ui/records/market-watch.md +40 -0
- package/modules/market-watch/db.js +10 -8
- package/modules/market-watch/module.json +2 -1
- package/modules/market-watch/pages.js +323 -0
- package/modules/market-watch/proposals.js +66 -4
- package/modules/market-watch/routes/pages.js +116 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/src/bongos/module-scope-map.js +1 -0
- package/src/module-api.js +1 -1
- package/tests/fixtures/market-watch-seed-rows.cjs +60 -0
- package/tests/hall_nav.mjs +4 -1
- package/tests/hall_page_gate_map.mjs +4 -0
- package/tests/market_watch_module.mjs +2 -1
- package/tests/market_watch_pages.mjs +356 -0
- package/tests/market_watch_rules.mjs +63 -3
- package/tests/nav_permission_atoms.mjs +1 -1
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
// market-watch.js — the Market Watch home, the watchtower desk (task 1004939; design of
|
|
2
|
+
// record docs/design/mocks/market-watch/home.html, option D). One read,
|
|
3
|
+
// GET /market-watch/home, which every signed-in member may make. Where the module is off
|
|
4
|
+
// its routes do not exist, so the page shows its quiet off state rather than an error.
|
|
5
|
+
(() => {
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
const MW = window.OTBMarketWatch;
|
|
9
|
+
const esc = MW.esc;
|
|
10
|
+
const $ = (id) => document.getElementById(id);
|
|
11
|
+
const toast = (m) => (window.OTB && window.OTB.toast ? window.OTB.toast(m, { duration: 1800, clear: true }) : null);
|
|
12
|
+
let HOME = null;
|
|
13
|
+
|
|
14
|
+
function show(id) {
|
|
15
|
+
$('mw-loading').hidden = true;
|
|
16
|
+
for (const k of ['mw-off', 'mw-fail', 'mw-desk']) $(k).hidden = k !== id;
|
|
17
|
+
}
|
|
18
|
+
function moduleOff() {
|
|
19
|
+
const m = window.__MODULES__;
|
|
20
|
+
return !!(m && m.enabled && 'market-watch' in m.enabled && m.enabled['market-watch'] !== true);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function renderScouts(h) {
|
|
24
|
+
const weeks = h.weeks_watched;
|
|
25
|
+
$('mw-watched').innerHTML = `<i class="mw-dot" style="background:var(--accent)" aria-hidden="true"></i> <b>${weeks}</b> ${weeks === 1 ? 'week' : 'weeks'} watched`;
|
|
26
|
+
$('mw-scouts').innerHTML = h.scouts.map((s) => `<div class="mw-scout">
|
|
27
|
+
<span class="mw-scout__ic" aria-hidden="true">${MW.KIND_ICON[s.kind]}</span>
|
|
28
|
+
<span class="mw-scout__t">${MW.KIND_PLURAL[s.kind]}<br><span class="mw-scout__lv">${s.watched} watched${s.level ? ` · lvl ${s.level}` : ''}</span></span>
|
|
29
|
+
<span class="mw-badge${s.reports ? '' : ' mw-badge--none'}" title="${s.reports} with a report this week">${s.reports}</span></div>`).join('');
|
|
30
|
+
$('mw-streak').innerHTML = h.streak.length
|
|
31
|
+
? h.streak.map((r) => `<i class="${r.ran ? '' : 'no'}" title="${r.ran ? `run ${r.n} · ${MW.fmtDay(r.date)}` : `skipped · ${MW.fmtDay(r.date)}`}"></i>`).join('')
|
|
32
|
+
: '<span class="mw-quiet">no runs yet</span>';
|
|
33
|
+
const pins = MW.pins.all();
|
|
34
|
+
$('mw-pins').hidden = !pins.length;
|
|
35
|
+
$('mw-pin-list').innerHTML = pins.map((p) => `<div class="mw-pin-row"><span aria-hidden="true">${MW.KIND_ICON[p.kind] || ''}</span><a class="mw-one" href="${MW.itemHref(p.id)}">${esc(p.name)}</a></div>`).join('');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function renderScope() {
|
|
39
|
+
$('mw-scope').innerHTML = MW.scopeSvg(HOME, $('mw-ax').value, $('mw-ay').value);
|
|
40
|
+
}
|
|
41
|
+
function initAxes(h) {
|
|
42
|
+
const opts = h.axes.map((a) => `<option value="${esc(a.key)}">${esc(a.label)}</option>`).join('');
|
|
43
|
+
$('mw-ax').innerHTML = opts;
|
|
44
|
+
$('mw-ay').innerHTML = opts;
|
|
45
|
+
// The mockup opens on open-source ethos across, contributor economy up.
|
|
46
|
+
const has = (k) => h.axes.some((a) => a.key === k);
|
|
47
|
+
$('mw-ax').value = has('open_source_ethos') ? 'open_source_ethos' : h.axes[0].key;
|
|
48
|
+
$('mw-ay').value = has('contributor_economy') ? 'contributor_economy' : (h.axes[1] || h.axes[0]).key;
|
|
49
|
+
$('mw-ax').onchange = renderScope;
|
|
50
|
+
$('mw-ay').onchange = renderScope;
|
|
51
|
+
// A dot is a picture, not a control (36 of them would be 36 undersized links); a
|
|
52
|
+
// click on one opens its file, and the picker below reaches every item by name.
|
|
53
|
+
$('mw-scope').addEventListener('click', (ev) => {
|
|
54
|
+
const blip = ev.target.closest && ev.target.closest('[data-id]');
|
|
55
|
+
if (blip) window.location.assign(MW.itemHref(blip.getAttribute('data-id')));
|
|
56
|
+
});
|
|
57
|
+
const groups = ['competitor', 'partner', 'topic'].map((k) => {
|
|
58
|
+
const mine = (h.index || []).filter((i) => i.kind === k);
|
|
59
|
+
return mine.length ? `<optgroup label="${MW.KIND_PLURAL[k]}">${mine.map((i) => `<option value="${i.id}">${esc(i.name)}</option>`).join('')}</optgroup>` : '';
|
|
60
|
+
}).join('');
|
|
61
|
+
$('mw-open').innerHTML = `<option value="">pick one…</option>${groups}`;
|
|
62
|
+
$('mw-open').onchange = () => { if ($('mw-open').value) window.location.assign(MW.itemHref($('mw-open').value)); };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function renderSweep(h) {
|
|
66
|
+
const s = MW.nextSweep(h);
|
|
67
|
+
$('mw-cd').textContent = s.big;
|
|
68
|
+
$('mw-when').textContent = s.small;
|
|
69
|
+
const b = h.budget;
|
|
70
|
+
const used = b.cap_usd > 0 ? Math.min(100, Math.round((b.spent_usd / b.cap_usd) * 100)) : 0;
|
|
71
|
+
$('mw-tank-fill').style.height = `${used}%`;
|
|
72
|
+
$('mw-tank').title = `${MW.money(b.spent_usd)} of ${MW.money(b.cap_usd)} used this month`;
|
|
73
|
+
$('mw-left').textContent = `${MW.money(b.left_usd)} left`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function renderReports(h) {
|
|
77
|
+
const read = new Set(MW.reads.get());
|
|
78
|
+
const total = h.reports.length;
|
|
79
|
+
const count = () => h.reports.filter((r) => read.has(String(r.id))).length;
|
|
80
|
+
const bar = () => {
|
|
81
|
+
const n = count();
|
|
82
|
+
$('mw-xpn').textContent = `${n}/${total}`;
|
|
83
|
+
$('mw-xpb').style.width = total ? `${(n / total) * 100}%` : '0';
|
|
84
|
+
};
|
|
85
|
+
$('mw-reps').innerHTML = total
|
|
86
|
+
? h.reports.map((r) => `<div class="mw-rep${read.has(String(r.id)) ? ' done' : ''}" data-id="${r.id}">
|
|
87
|
+
<span class="mw-rep__ic" aria-hidden="true">${MW.KIND_ICON[r.kind]}</span>
|
|
88
|
+
<a class="mw-rep__t" href="${MW.itemHref(r.item_id)}" title="${esc(r.takeaway)}"><b>${esc(r.item_name)}</b> · ${esc(r.takeaway)}</a>
|
|
89
|
+
<button type="button" aria-label="Mark the ${esc(r.item_name)} report read">ok</button></div>`).join('')
|
|
90
|
+
: '<p class="mw-quiet">No reports this week yet.</p>';
|
|
91
|
+
bar();
|
|
92
|
+
$('mw-reps').querySelectorAll('.mw-rep button').forEach((btn) => {
|
|
93
|
+
btn.addEventListener('click', () => {
|
|
94
|
+
const row = btn.closest('.mw-rep');
|
|
95
|
+
if (row.classList.contains('done')) return;
|
|
96
|
+
row.classList.add('done');
|
|
97
|
+
MW.reads.add(row.dataset.id);
|
|
98
|
+
read.add(row.dataset.id);
|
|
99
|
+
bar();
|
|
100
|
+
toast(count() === total ? 'All caught up — every report read' : '+1 report read');
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async function load() {
|
|
106
|
+
if (moduleOff()) return show('mw-off');
|
|
107
|
+
let res;
|
|
108
|
+
try {
|
|
109
|
+
res = await fetch('/api/bongos/market-watch/home', { credentials: 'same-origin' });
|
|
110
|
+
} catch (e) {
|
|
111
|
+
return show('mw-fail');
|
|
112
|
+
}
|
|
113
|
+
if (res.status === 401) { window.location.assign(`/api/bongos/auth/web/start?return=${encodeURIComponent(window.location.pathname)}`); return; }
|
|
114
|
+
if (res.status === 404) return show('mw-off'); // the module's routes are not mounted
|
|
115
|
+
if (!res.ok) return show('mw-fail');
|
|
116
|
+
HOME = await res.json();
|
|
117
|
+
show('mw-desk');
|
|
118
|
+
renderScouts(HOME);
|
|
119
|
+
initAxes(HOME);
|
|
120
|
+
renderScope();
|
|
121
|
+
renderSweep(HOME);
|
|
122
|
+
renderReports(HOME);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
$('mw-send').addEventListener('click', () => {
|
|
126
|
+
MW.openPropose('scout', {}, () => toast('Scout proposed — the government will decide'));
|
|
127
|
+
});
|
|
128
|
+
document.addEventListener('DOMContentLoaded', load);
|
|
129
|
+
})();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_": "MARKET WATCH HOME, the watchtower desk (task 1004939; design of record docs/design/mocks/market-watch/home.html, option D). One read, GET /market-watch/home. Against the kit's own stub it 404s, which the page reads as the module being off, so the stub render is the quiet off state. The populated desk needs the hall-preview harness with the module reported on; its market-watch__home.json fixture was built from the committed 2026-10-28 seed through the real shapes (tests/fixtures/market-watch-seed-rows.cjs):\n\n node scripts/hall-preview/server.js --port 4639 --fixture-me --modules-on market-watch\n node modules/ui-design/kit/render.js --states modules/hall-ui/public/market-watch.states.json --base-in http://localhost:4639 --base-out http://localhost:4639",
|
|
3
|
+
"page": "/builders/market-watch",
|
|
4
|
+
"surface": "hall-ui",
|
|
5
|
+
"stub": { "prefix": "/builders/" },
|
|
6
|
+
"modeQuery": false,
|
|
7
|
+
"ignoreRequests": ["/api/(gds|bongos)/"],
|
|
8
|
+
"ignoreConsole": ["/api/(gds|bongos)/", "\\b401\\b", "\\b403\\b", "\\b404\\b", "Unauthorized", "Forbidden", "Not Found", "load failed"],
|
|
9
|
+
"states": {
|
|
10
|
+
"desk": { "auth": true, "actions": [["wait", 900], ["clickIfVisible", "[data-craft-close]"], ["wait", 200]], "expect": { "visible": ["#mw-desk", "#mw-scope svg"] } },
|
|
11
|
+
"out": { "auth": false, "actions": [["wait", 600], ["clickIfVisible", "[data-craft-close]"]] }
|
|
12
|
+
},
|
|
13
|
+
"reducedMotion": { "auth": true, "actions": [["wait", 900], ["clickIfVisible", "[data-craft-close]"], ["wait", 200]] }
|
|
14
|
+
}
|
|
@@ -128,7 +128,7 @@
|
|
|
128
128
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
129
129
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
130
130
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
131
|
-
<script src="/builders/shell.js?v=2026-10-30-market-watch"></script>
|
|
131
|
+
<script src="/builders/shell.js?v=2026-10-30-market-watch-pages"></script>
|
|
132
132
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
133
133
|
<script src="/builders/palette.js" defer></script>
|
|
134
134
|
<script src="/builders/brand-apply.js"></script>
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
63
63
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
64
64
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
65
|
-
<script src="/builders/shell.js?v=2026-10-30-market-watch"></script>
|
|
65
|
+
<script src="/builders/shell.js?v=2026-10-30-market-watch-pages"></script>
|
|
66
66
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
67
67
|
<script src="/builders/palette.js" defer></script>
|
|
68
68
|
<script src="/builders/gov-living.js?v=2026-10-10-real1"></script>
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
114
114
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
115
115
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
116
|
-
<script src="/builders/shell.js?v=2026-10-30-market-watch"></script>
|
|
116
|
+
<script src="/builders/shell.js?v=2026-10-30-market-watch-pages"></script>
|
|
117
117
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
118
118
|
<script src="/builders/palette.js" defer></script>
|
|
119
119
|
<!-- the shared markdown reader (window.OTBMdReader) — primer.js renders through
|
|
@@ -208,7 +208,7 @@
|
|
|
208
208
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
209
209
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
210
210
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
211
|
-
<script src="/builders/shell.js?v=2026-10-30-market-watch"></script>
|
|
211
|
+
<script src="/builders/shell.js?v=2026-10-30-market-watch-pages"></script>
|
|
212
212
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
213
213
|
<script src="/builders/palette.js" defer></script>
|
|
214
214
|
<!-- task 1004727: the ONE karma vote control every task/idea list renders -->
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
73
73
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
74
74
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
75
|
-
<script src="/builders/shell.js?v=2026-10-30-market-watch"></script>
|
|
75
|
+
<script src="/builders/shell.js?v=2026-10-30-market-watch-pages"></script>
|
|
76
76
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
77
77
|
<script src="/builders/palette.js" defer></script>
|
|
78
78
|
<script src="/builders/hall-kit.js"></script>
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
75
75
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
76
76
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
77
|
-
<script src="/builders/shell.js?v=2026-10-30-market-watch"></script>
|
|
77
|
+
<script src="/builders/shell.js?v=2026-10-30-market-watch-pages"></script>
|
|
78
78
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
79
79
|
<script src="/builders/palette.js" defer></script>
|
|
80
80
|
<script src="/builders/founding-mark.js?v=2026-09-30-founding"></script>
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
45
45
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
46
46
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
47
|
-
<script src="/builders/shell.js?v=2026-10-30-market-watch"></script>
|
|
47
|
+
<script src="/builders/shell.js?v=2026-10-30-market-watch-pages"></script>
|
|
48
48
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
49
49
|
<script src="/builders/palette.js" defer></script>
|
|
50
50
|
<!-- The chart kit and the timeline's series builders (task 1003447). Both MUST
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
57
57
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
58
58
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
59
|
-
<script src="/builders/shell.js?v=2026-10-30-market-watch"></script>
|
|
59
|
+
<script src="/builders/shell.js?v=2026-10-30-market-watch-pages"></script>
|
|
60
60
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
61
61
|
<script src="/builders/palette.js" defer></script>
|
|
62
62
|
<!-- the branding pack's currency label, loaded BEFORE roster.js. -->
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
113
113
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
114
114
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
115
|
-
<script src="/builders/shell.js?v=2026-10-30-market-watch"></script>
|
|
115
|
+
<script src="/builders/shell.js?v=2026-10-30-market-watch-pages"></script>
|
|
116
116
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
117
117
|
<script src="/builders/palette.js" defer></script>
|
|
118
118
|
<script src="/builders/task-detail.js?v=2026-07-03-taskdetail"></script>
|
|
@@ -487,7 +487,7 @@
|
|
|
487
487
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
488
488
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
489
489
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
490
|
-
<script src="/builders/shell.js?v=2026-10-30-market-watch"></script>
|
|
490
|
+
<script src="/builders/shell.js?v=2026-10-30-market-watch-pages"></script>
|
|
491
491
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
492
492
|
<script src="/builders/palette.js" defer></script>
|
|
493
493
|
<!-- shared component kit (window.OTBKit) — section rail (task 2166). -->
|
|
@@ -101,6 +101,7 @@
|
|
|
101
101
|
// the zen-garden reading of "a place an artist wants to be", so it does not
|
|
102
102
|
// reuse a glyph that would read as one more docs/goals page.
|
|
103
103
|
studio: '<path d="M9 15.5V8.5"/><path d="M9 8.5C9 5.5 6.5 3.5 3.5 3.5c0 3 2.5 5 5.5 5Z"/><path d="M9 10C9 7.5 11.2 5.5 14 5.5c0 2.7-2.2 4.5-5 4.5Z"/>',
|
|
104
|
+
marketwatch: '<circle cx="9" cy="9" r="6.5"/><path d="M9 9 13.6 4.4"/><path d="M9 5.2a3.8 3.8 0 1 0 3.8 3.8"/><circle cx="11.6" cy="11.2" r="0.9" fill="currentColor"/>',
|
|
104
105
|
// You
|
|
105
106
|
accomplishments: '<path d="M6 2.5l1.6 4.2M12 2.5l-1.6 4.2"/><circle cx="9" cy="10.8" r="4.2"/><circle cx="9" cy="10.8" r="1.8"/>',
|
|
106
107
|
profile: '<rect x="2.5" y="4" width="13" height="10" rx="1.5"/><circle cx="6.3" cy="8" r="1.6"/><path d="M4.1 12c.3-1.1 1.1-1.7 2.2-1.7s1.9.6 2.2 1.7"/><path d="M10.4 7.5h3M10.4 10.3h3"/>',
|
|
@@ -280,6 +281,7 @@
|
|
|
280
281
|
// the artist. NO module gate: it degrades to reviews alone on an instance
|
|
281
282
|
// without copy-desk, rather than vanishing.
|
|
282
283
|
{ id: 'studio', label: 'Studio', icon: 'studio', href: '/studio', gate: 'signed' },
|
|
284
|
+
{ id: 'market-watch', label: 'Market Watch', icon: 'marketwatch', href: '/market-watch', gate: 'signed', module: 'market-watch' }, // task 1004939: 'signed' is its page floor; dropped where the module is off
|
|
283
285
|
] },
|
|
284
286
|
// Your own numbers. task 2174 folded Achievements into Standing and this task
|
|
285
287
|
// retires its nav entry; VIEW_ALIASES still routes #achievements here, so old
|
|
@@ -428,7 +430,6 @@
|
|
|
428
430
|
}
|
|
429
431
|
|
|
430
432
|
// ---------- render ----------
|
|
431
|
-
|
|
432
433
|
function renderSidebar() {
|
|
433
434
|
var el = document.getElementById('app-sidebar');
|
|
434
435
|
if (!el) return;
|
|
@@ -1472,7 +1473,7 @@
|
|
|
1472
1473
|
// task 1004430: someone else's profile belongs to Builders, not to your own
|
|
1473
1474
|
// Profile; probeMe hands the mark back if the id turns out to be yours.
|
|
1474
1475
|
var otherProfile = path === '/profile' && /(^|[?&])id=/.test(window.location.search || '');
|
|
1475
|
-
if (otherProfile) path = '/roster';
|
|
1476
|
+
if (otherProfile) path = '/roster'; else if (/^\/market-watch-(item|rules)$/.test(path)) path = '/market-watch'; // task 1004939
|
|
1476
1477
|
document.querySelectorAll('#app-sidebar .nav-item').forEach(function (a) {
|
|
1477
1478
|
var target = a.getAttribute('href');
|
|
1478
1479
|
if (target && !a.dataset.view && target === path) a.classList.add('nav-item--active');
|
|
@@ -231,7 +231,7 @@
|
|
|
231
231
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
232
232
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
233
233
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
234
|
-
<script src="/builders/shell.js?v=2026-10-30-market-watch"></script>
|
|
234
|
+
<script src="/builders/shell.js?v=2026-10-30-market-watch-pages"></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>
|
|
@@ -219,7 +219,7 @@
|
|
|
219
219
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
220
220
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
221
221
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
222
|
-
<script src="/builders/shell.js?v=2026-10-30-market-watch"></script>
|
|
222
|
+
<script src="/builders/shell.js?v=2026-10-30-market-watch-pages"></script>
|
|
223
223
|
<script src="/builders/palette.js" defer></script>
|
|
224
224
|
<!-- The kit, for its ONE renderer: the full-size ship-time visual figure
|
|
225
225
|
(task 1003829), which the older reviews still show. -->
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
60
60
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
61
61
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
62
|
-
<script src="/builders/shell.js?v=2026-10-30-market-watch"></script>
|
|
62
|
+
<script src="/builders/shell.js?v=2026-10-30-market-watch-pages"></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
|
|
@@ -141,7 +141,7 @@
|
|
|
141
141
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
142
142
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
143
143
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
144
|
-
<script src="/builders/shell.js?v=2026-10-30-market-watch"></script>
|
|
144
|
+
<script src="/builders/shell.js?v=2026-10-30-market-watch-pages"></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>
|
|
@@ -145,7 +145,7 @@
|
|
|
145
145
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
146
146
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
147
147
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
148
|
-
<script src="/builders/shell.js?v=2026-10-30-market-watch"></script>
|
|
148
|
+
<script src="/builders/shell.js?v=2026-10-30-market-watch-pages"></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>
|
|
@@ -166,7 +166,7 @@
|
|
|
166
166
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
167
167
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
168
168
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
169
|
-
<script src="/builders/shell.js?v=2026-10-30-market-watch"></script>
|
|
169
|
+
<script src="/builders/shell.js?v=2026-10-30-market-watch-pages"></script>
|
|
170
170
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
171
171
|
<script src="/builders/palette.js" defer></script>
|
|
172
172
|
<!-- shared component kit (window.OTBKit) — tabs, ledger, pager (task 2166). -->
|
|
@@ -174,7 +174,7 @@
|
|
|
174
174
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
175
175
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
176
176
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
177
|
-
<script src="/builders/shell.js?v=2026-10-30-market-watch"></script>
|
|
177
|
+
<script src="/builders/shell.js?v=2026-10-30-market-watch-pages"></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 -->
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Market Watch — the watchtower desk (/market-watch) and the dossier (/market-watch-item) (task 1004939)
|
|
2
|
+
|
|
3
|
+
Goal 1000137, "Run a business in Bongos". The designs of record are the owner's picks of 2026-10-29:
|
|
4
|
+
`docs/design/mocks/market-watch/home.html` (option D) and `item.html` (option B). Both pages ride
|
|
5
|
+
`market-watch.css` plus `market-watch-lib.js` (the drawing both share), and read the market-watch
|
|
6
|
+
module's one route file, `modules/market-watch/routes/pages.js`.
|
|
7
|
+
|
|
8
|
+
**What the desk holds.** A link to the rules page (`/market-watch-rules`, task 1004941) under the streak; three scouts (competitors, partners, topics) as levelled cards, each with the
|
|
9
|
+
number of its items reported this week as the badge; the run streak; the landscape, every competitor as a
|
|
10
|
+
dot on two dimensions the viewer picks from the rules' own rubric (open-source ethos across and
|
|
11
|
+
contributor economy up by default, as the mock opens); the next sweep with the budget left as a tank;
|
|
12
|
+
and this week's reports, one per item, competitors and partners taken in turn because they are scored on
|
|
13
|
+
different scales. A report row links to the item; "ok" marks it read for this viewer only.
|
|
14
|
+
|
|
15
|
+
**What the dossier holds.** The item as a card that levels up run by run (level and streak are derived in
|
|
16
|
+
`modules/market-watch/db.js`), the overall line and one sparkline per rubric dimension, why it is watched,
|
|
17
|
+
its known facts, and the sortable case file (found, run, type, takeaway, confidence, source), filtered by type through the kit filter bar (`?type=` in the URL). Sparklines and the dimension bars are the kit's (`OTBKit.sparklineHtml`, `barSeriesHtml`); only the landscape scope is drawn here, since the kit has no scatter plot.
|
|
18
|
+
|
|
19
|
+
**Decisions a later edit must keep.**
|
|
20
|
+
|
|
21
|
+
- **No page head, no ask card.** The topbar names the page. Nothing here asks the reader to decide
|
|
22
|
+
something about the project, so there is no OTBAsk; proposals go to the government, which decides on
|
|
23
|
+
the rules page (task 1004941).
|
|
24
|
+
- **A dot is a picture, not a control.** Thirty-six 6–14px links failed the 24px floor and would overlap
|
|
25
|
+
at phone width. A click on a dot opens its file; the "open a case file" picker under the landscape
|
|
26
|
+
reaches every item by name, partners and topics included.
|
|
27
|
+
- **Every button that changes what is watched files a proposal.** Propose a change (re-score, move
|
|
28
|
+
between partner and competitor, add a source, correct an entry), Stop tracking (a `remove_item`) and
|
|
29
|
+
"+ send a new scout" (an `add_item`), each with a one-line reason. Nothing on these pages changes an
|
|
30
|
+
item, a score or an entry. The direction of a move comes from the item on the server, never the client.
|
|
31
|
+
- **Pin and "read" are the viewer's own** (localStorage `mw-pins`, `mw-read`): conveniences, not the
|
|
32
|
+
record. Pinned items list under the scouts.
|
|
33
|
+
- **"sweep now" is not here.** A manual run spends the project's AI budget, so it is the settings page's
|
|
34
|
+
(task 1004938) to offer with the cap beside it.
|
|
35
|
+
- **Off is quiet.** Where the default-off module is not enabled the nav item is dropped, the page shows
|
|
36
|
+
one line pointing at Modules, and it asks the server nothing; a 404 from unmounted routes reads the same.
|
|
37
|
+
|
|
38
|
+
**Verified** through `modules/ui-design/kit/render.js` against the hall-preview harness
|
|
39
|
+
(`--fixture-me --modules-on market-watch`), whose `market-watch__*.json` fixtures were built from the
|
|
40
|
+
committed seed through the real shapes. Tests: `tests/market_watch_pages.mjs`.
|
|
@@ -130,14 +130,16 @@ const FOUND_IN_SQL = `
|
|
|
130
130
|
JOIN market_watch_items i ON i.id = e.item_id
|
|
131
131
|
WHERE i.project = $1 AND e.run_id IS NOT NULL AND ($2::bigint IS NULL OR e.item_id = $2)`;
|
|
132
132
|
|
|
133
|
+
// `knownRuns` (here, in getItem, caseFile and scoreHistory): the numbered runs when the caller
|
|
134
|
+
// already read them, so one page load reads market_watch_runs once (task 1004939).
|
|
133
135
|
// listItems — the home desk: every tracked item with its latest overall score, last
|
|
134
136
|
// finding, level and streak.
|
|
135
|
-
async function listItems(pool, { project, kind = null, includeRemoved = false, id = null } = {}) {
|
|
137
|
+
async function listItems(pool, { project, kind = null, includeRemoved = false, id = null, knownRuns = null } = {}) {
|
|
136
138
|
need(project);
|
|
137
139
|
const [{ rows: items }, { rows: found }, runs] = await Promise.all([
|
|
138
140
|
pool.query(ITEMS_SQL, [project, kind, includeRemoved, id]),
|
|
139
141
|
pool.query(FOUND_IN_SQL, [project, id]),
|
|
140
|
-
numberedRuns(pool, { project }),
|
|
142
|
+
knownRuns || numberedRuns(pool, { project }),
|
|
141
143
|
]);
|
|
142
144
|
const byItem = new Map();
|
|
143
145
|
for (const f of found) {
|
|
@@ -154,8 +156,8 @@ async function listItems(pool, { project, kind = null, includeRemoved = false, i
|
|
|
154
156
|
}
|
|
155
157
|
|
|
156
158
|
// getItem — one item, with removed ones included (its page still opens).
|
|
157
|
-
async function getItem(pool, { project, id }) {
|
|
158
|
-
const rows = await listItems(pool, { project, includeRemoved: true, id });
|
|
159
|
+
async function getItem(pool, { project, id, knownRuns = null }) {
|
|
160
|
+
const rows = await listItems(pool, { project, includeRemoved: true, id, knownRuns });
|
|
159
161
|
return rows[0] || null;
|
|
160
162
|
}
|
|
161
163
|
|
|
@@ -168,11 +170,11 @@ const CASE_FILE_SQL = `
|
|
|
168
170
|
ORDER BY e.pinned DESC, e.found_on DESC, e.id DESC`;
|
|
169
171
|
|
|
170
172
|
// caseFile — the item page's sortable table: found, run, type, takeaway, confidence, source.
|
|
171
|
-
async function caseFile(pool, { project, itemId }) {
|
|
173
|
+
async function caseFile(pool, { project, itemId, knownRuns = null }) {
|
|
172
174
|
need(project);
|
|
173
175
|
const [{ rows }, runs] = await Promise.all([
|
|
174
176
|
pool.query(CASE_FILE_SQL, [project, itemId]),
|
|
175
|
-
numberedRuns(pool, { project }),
|
|
177
|
+
knownRuns || numberedRuns(pool, { project }),
|
|
176
178
|
]);
|
|
177
179
|
const runN = new Map(runs.map((r) => [String(r.id), r.n]));
|
|
178
180
|
return rows.map((e) => ({ ...e, id: Number(e.id), run: e.run_id == null ? null : runN.get(String(e.run_id)) ?? null }));
|
|
@@ -185,11 +187,11 @@ const SCORES_SQL = `
|
|
|
185
187
|
WHERE i.project = $1 AND s.item_id = $2`;
|
|
186
188
|
|
|
187
189
|
// scoreHistory — the item page's sparklines, one per dimension plus 'overall'.
|
|
188
|
-
async function scoreHistory(pool, { project, itemId }) {
|
|
190
|
+
async function scoreHistory(pool, { project, itemId, knownRuns = null }) {
|
|
189
191
|
need(project);
|
|
190
192
|
const [{ rows }, runs] = await Promise.all([
|
|
191
193
|
pool.query(SCORES_SQL, [project, itemId]),
|
|
192
|
-
numberedRuns(pool, { project }),
|
|
194
|
+
knownRuns || numberedRuns(pool, { project }),
|
|
193
195
|
]);
|
|
194
196
|
return sparklines(rows, runs);
|
|
195
197
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"key": "market-watch",
|
|
3
3
|
"title": "Market Watch (a weekly AI watch on a project's market)",
|
|
4
|
-
"description": "A weekly AI watch on a project's competitors, partners and topics, with a sourced log per tracked item and a weekly question to members (goal 1000137). Its data model (tables, read queries and the hub seed of the 2026-10-28 research), its weekly run (an hourly poller that researches each tracked item on the project's own Anthropic key, under the monthly cap), the weekly ask card in the hall's attention inbox (task 1004940), its rules page (the watch rules in plain words, members' proposed changes, and the owner's or the government's decision on each, task 1004941) and the owner's settings (on/off, monthly cap, cadence, the project key; task 1004938) are in. Off by default, and it cannot be turned on behind an open join door with no payer, because every run will spend the project's AI budget.",
|
|
4
|
+
"description": "A weekly AI watch on a project's competitors, partners and topics, with a sourced log per tracked item and a weekly question to members (goal 1000137). Its data model (tables, read queries and the hub seed of the 2026-10-28 research), its weekly run (an hourly poller that researches each tracked item on the project's own Anthropic key, under the monthly cap), the weekly ask card in the hall's attention inbox (task 1004940), its home and tracked-item pages (task 1004939), its rules page (the watch rules in plain words, members' proposed changes, and the owner's or the government's decision on each, task 1004941) and the owner's settings (on/off, monthly cap, cadence, the project key; task 1004938) are in. Off by default, and it cannot be turned on behind an open join door with no payer, because every run will spend the project's AI budget.",
|
|
5
5
|
"version": "0.4.0",
|
|
6
6
|
"coreVersion": "^1.22.0",
|
|
7
7
|
"category": {
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
"migrations": true,
|
|
16
16
|
"routes": [
|
|
17
17
|
"ask",
|
|
18
|
+
"pages",
|
|
18
19
|
"rules",
|
|
19
20
|
"settings"
|
|
20
21
|
],
|