@bongos/core 1.24.10 → 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 +171 -86
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +12 -0
- package/clients/bongos-client/index.cjs +12 -0
- package/clients/bongos-client/index.d.ts +18 -1
- package/clients/bongos-client/index.mjs +12 -0
- package/docs/api/openapi.json +304 -5
- package/docs/api-reference.md +14 -4
- package/docs/architecture.md +4 -0
- package/docs/copy-inventory.md +154 -76
- package/docs/copy-registry.json +923 -125
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +68 -4
- package/docs/page-readings.json +873 -819
- package/modules/government/decision-rules.js +1 -0
- 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.css +12 -0
- package/modules/hall-ui/public/project-settings.html +1 -1
- package/modules/hall-ui/public/project-settings.js +63 -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/task.js +34 -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/lifecycle/migrations/lifecycle_018_pay_terms.sql +64 -0
- package/modules/lifecycle/pay-terms.js +171 -0
- package/modules/lifecycle/routes/project-settings.js +94 -0
- package/modules/lifecycle/routes/task-write-routes.js +35 -4
- 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 +12 -0
- package/scripts/bongos/government-walk.js +1 -0
- package/scripts/bongos/run-unit-tests.js +4 -0
- package/scripts/bongos/start.js +42 -2
- 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/government_act_alone_buildings.mjs +1 -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
- package/tests/patch_gate_ordering.mjs +3 -0
- package/tests/pay_terms.mjs +254 -0
- package/tests/pay_terms_db.mjs +86 -0
- package/tests/project_settings_page.mjs +25 -6
|
@@ -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 -->
|
|
@@ -60,6 +60,18 @@
|
|
|
60
60
|
border-top: var(--line) solid var(--rule-soft);
|
|
61
61
|
}
|
|
62
62
|
.ps-cat--off { opacity: 0.6; }
|
|
63
|
+
|
|
64
|
+
/* Pay terms (task 1004990): one wrapping line per term, the category list's shape. */
|
|
65
|
+
.ps-pays { margin: 10px 0 0; padding: 0; list-style: none; }
|
|
66
|
+
.ps-pay {
|
|
67
|
+
display: flex;
|
|
68
|
+
flex-wrap: wrap;
|
|
69
|
+
align-items: center;
|
|
70
|
+
gap: 8px;
|
|
71
|
+
padding: 8px 0;
|
|
72
|
+
border-top: var(--line) solid var(--rule-soft);
|
|
73
|
+
}
|
|
74
|
+
.ps-pay__note { flex: 1 1 200px; min-width: 0; }
|
|
63
75
|
.ps-cat__slug {
|
|
64
76
|
font-size: 11px;
|
|
65
77
|
color: var(--ink-soft);
|
|
@@ -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>
|
|
@@ -198,6 +198,44 @@
|
|
|
198
198
|
</div>`;
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
// Pay terms (task 1004990, ADR 0385 decision 3): the project's standing pay, in
|
|
202
|
+
// its own words. One line per term plus a blank one to add; picking "remove"
|
|
203
|
+
// drops a line. Every box starts EMPTY on a new line — Bongos suggests no amount.
|
|
204
|
+
// The history under it is the audit trail: who changed the terms, and when.
|
|
205
|
+
function payTermLineHtml(types, t) {
|
|
206
|
+
const opts = [`<option value="">${t ? 'remove' : 'add a term…'}</option>`]
|
|
207
|
+
.concat(types.map((o) => `<option value="${escapeHtml(o.type)}" ${t && t.pay_type === o.type ? 'selected' : ''}>${escapeHtml(o.label)}</option>`));
|
|
208
|
+
const val = (f) => escapeHtml(t && t[f] != null ? String(t[f]) : '');
|
|
209
|
+
return `
|
|
210
|
+
<li class="ps-pay">
|
|
211
|
+
<select class="ps-pay__type" aria-label="Kind of pay">${opts.join('')}</select>
|
|
212
|
+
<input type="text" class="ps-pay__amount" maxlength="64" value="${val('amount')}" placeholder="amount, as you state it" aria-label="Amount">
|
|
213
|
+
<input type="text" class="ps-pay__unit" maxlength="32" value="${val('unit')}" placeholder="unit" aria-label="Unit">
|
|
214
|
+
<input type="text" class="ps-pay__note" maxlength="500" value="${val('note')}" placeholder="note" aria-label="Note">
|
|
215
|
+
</li>`;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function payTermsBodyHtml(row) {
|
|
219
|
+
const v = row.value || {};
|
|
220
|
+
const types = Array.isArray(v.types) ? v.types : [];
|
|
221
|
+
const terms = Array.isArray(v.terms) ? v.terms : [];
|
|
222
|
+
const lines = terms.map((t) => payTermLineHtml(types, t));
|
|
223
|
+
if (terms.length < (v.max || 10)) lines.push(payTermLineHtml(types, null));
|
|
224
|
+
const selfManaged = terms.some((t) => t.pay_type === 'self_managed')
|
|
225
|
+
? `<p class="ps-sibling">${escapeHtml(String(v.self_managed_label || ''))}</p>` : '';
|
|
226
|
+
const history = (Array.isArray(v.history) ? v.history : []).map((h) => `
|
|
227
|
+
<li>${escapeHtml(h.set_by_login ? '@' + h.set_by_login : 'builder ' + h.set_by)} set
|
|
228
|
+
${escapeHtml(String(h.terms.length))} term${h.terms.length === 1 ? '' : 's'}
|
|
229
|
+
on ${escapeHtml(new Date(h.set_at).toLocaleString())}</li>`).join('');
|
|
230
|
+
return `
|
|
231
|
+
<ul class="ps-pays">${lines.join('')}</ul>
|
|
232
|
+
${selfManaged}
|
|
233
|
+
<div class="ps-controls">
|
|
234
|
+
<button type="button" class="btn" id="ps-pay-save">Save pay terms</button>
|
|
235
|
+
</div>
|
|
236
|
+
${history ? `<details class="ps-sibling"><summary>Who changed the pay terms</summary><ul>${history}</ul></details>` : ''}`;
|
|
237
|
+
}
|
|
238
|
+
|
|
201
239
|
// ---- render + wire -------------------------------------------------------
|
|
202
240
|
|
|
203
241
|
let rows = [];
|
|
@@ -209,6 +247,7 @@
|
|
|
209
247
|
if (row.key === 'tweak_round_cap' && row.editable) return rowHtml(row, tweakCapBodyHtml(row));
|
|
210
248
|
if (row.key === 'rot_timer' && row.editable) return rowHtml(row, rotBodyHtml(row));
|
|
211
249
|
if (row.key === 'work_categories' && row.editable) return rowHtml(row, catsBodyHtml(row));
|
|
250
|
+
if (row.key === 'pay_terms' && row.editable) return rowHtml(row, payTermsBodyHtml(row));
|
|
212
251
|
return rowHtml(row, '');
|
|
213
252
|
}).join('')}</ul>`;
|
|
214
253
|
wire();
|
|
@@ -219,10 +258,33 @@
|
|
|
219
258
|
// reply, arriving first, can repaint a value nobody asked for.
|
|
220
259
|
function setBusy(on) {
|
|
221
260
|
const mount = $('ps-rows');
|
|
222
|
-
mount.querySelectorAll('input, button').forEach((el) => { el.disabled = on; });
|
|
261
|
+
mount.querySelectorAll('input, button, select').forEach((el) => { el.disabled = on; });
|
|
223
262
|
}
|
|
224
263
|
|
|
225
264
|
function wire() {
|
|
265
|
+
const paySave = $('ps-pay-save');
|
|
266
|
+
if (paySave) {
|
|
267
|
+
paySave.addEventListener('click', async () => {
|
|
268
|
+
// Only what was typed is sent; an empty box stays empty (null), never filled.
|
|
269
|
+
const terms = [];
|
|
270
|
+
document.querySelectorAll('.ps-pay').forEach((li) => {
|
|
271
|
+
const type = li.querySelector('.ps-pay__type').value;
|
|
272
|
+
if (!type) return;
|
|
273
|
+
const text = (cls) => { const s = String(li.querySelector(cls).value || '').trim(); return s || null; };
|
|
274
|
+
terms.push({ pay_type: type, amount: text('.ps-pay__amount'), unit: text('.ps-pay__unit'), note: text('.ps-pay__note') });
|
|
275
|
+
});
|
|
276
|
+
setBusy(true);
|
|
277
|
+
try {
|
|
278
|
+
await sendJson('PATCH', '/project-settings/pay-terms', { terms });
|
|
279
|
+
toast(terms.length ? 'Pay terms saved.' : 'Pay terms cleared.');
|
|
280
|
+
await load();
|
|
281
|
+
} catch (err) {
|
|
282
|
+
toast(err.message || 'Could not save the pay terms.');
|
|
283
|
+
setBusy(false);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
|
|
226
288
|
const shipSave = $('ps-ship-save');
|
|
227
289
|
if (shipSave) {
|
|
228
290
|
shipSave.addEventListener('click', async () => {
|
|
@@ -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
|
|
@@ -484,8 +484,29 @@
|
|
|
484
484
|
return `<section class="rec-sec" id="task-sessions"><p class="rec-sec__h">Session log${count}</p>${blocks.join('')}</section>`;
|
|
485
485
|
}
|
|
486
486
|
|
|
487
|
+
// The task's pay terms as the project stated them (task 1004990). Amounts are the
|
|
488
|
+
// project's text, shown verbatim — never a figure Bongos computed or suggested,
|
|
489
|
+
// and never a balance. A missing amount reads "not stated". A self-managed term
|
|
490
|
+
// carries the server's label saying Bongos moves and guarantees none of it.
|
|
491
|
+
// Terms that come from the project's standing policy say so. Nothing to show
|
|
492
|
+
// (none stated anywhere) draws nothing.
|
|
493
|
+
function payTermsHtml(view) {
|
|
494
|
+
if (!view || !Array.isArray(view.terms) || !view.terms.length) return '';
|
|
495
|
+
const lines = view.terms.map((term) => {
|
|
496
|
+
const amount = term.amount
|
|
497
|
+
? `${escapeHtml(term.amount)}${term.unit ? ' ' + escapeHtml(term.unit) : ''}`
|
|
498
|
+
: '<em>amount not stated</em>';
|
|
499
|
+
return `<li><strong>${escapeHtml(term.type_label || term.pay_type)}</strong> — ${amount}`
|
|
500
|
+
+ (term.note ? `<br>${escapeHtml(term.note)}` : '')
|
|
501
|
+
+ (term.label ? `<br><em>${escapeHtml(term.label)}</em>` : '')
|
|
502
|
+
+ '</li>';
|
|
503
|
+
}).join('');
|
|
504
|
+
const from = view.source === 'project' ? ' <em>this project’s standing terms</em>' : '';
|
|
505
|
+
return `<section class="rec-sec" id="task-pay"><p class="rec-sec__h">Pay${from}</p><ul>${lines}</ul></section>`;
|
|
506
|
+
}
|
|
507
|
+
|
|
487
508
|
function renderTask(t, lastClaim, viewer, goal, extras) {
|
|
488
|
-
const { grade = null, sessionLogs = null, claims = null, goalsList = null } = extras || {};
|
|
509
|
+
const { grade = null, sessionLogs = null, claims = null, goalsList = null, payTerms = null } = extras || {};
|
|
489
510
|
document.title = `#${t.id} — ${t.title || 'Task'} — ${document.title.split(' — ').pop()}`;
|
|
490
511
|
const sub = $('#task-sub');
|
|
491
512
|
if (sub) sub.textContent = `Task ${t.id}`;
|
|
@@ -631,6 +652,10 @@
|
|
|
631
652
|
['Priority', t.priority == null ? null : 'P' + t.priority],
|
|
632
653
|
]));
|
|
633
654
|
parts.push(lifecycleRail(t, creditVal));
|
|
655
|
+
// What the task pays, in the PROJECT's words (task 1004990, ADR 0385 D3), drawn
|
|
656
|
+
// beside the Claim button so a builder reads it before claiming.
|
|
657
|
+
const pay = payTermsHtml(payTerms);
|
|
658
|
+
if (pay) parts.push(pay);
|
|
634
659
|
// A slot a MODULE may fill (task 1004301): the module's public/task-widget.js is
|
|
635
660
|
// injected just before this script and writes into it on `otb:task-shown` below. The
|
|
636
661
|
// page itself names no module and draws nothing here — with none enabled it stays
|
|
@@ -1086,10 +1111,18 @@
|
|
|
1086
1111
|
if (ri && typeof ri.blob_base === 'string') repoBlobBase = ri.blob_base;
|
|
1087
1112
|
if (ri && typeof ri.tree_base === 'string') repoTreeBase = ri.tree_base;
|
|
1088
1113
|
} catch (_) { /* links degrade to the original href */ }
|
|
1114
|
+
// task 1004990: the task's pay terms, in the project's words. Best-effort: an
|
|
1115
|
+
// older server without the read draws no pay section rather than no page.
|
|
1116
|
+
let payTerms = null;
|
|
1117
|
+
try {
|
|
1118
|
+
const pt = await getJSON(`${API}/pay-terms?task_ids=${encodeURIComponent(d.task.id)}`, { softAuth: true });
|
|
1119
|
+
payTerms = (pt && pt.pay_terms && pt.pay_terms[String(d.task.id)]) || null;
|
|
1120
|
+
} catch (_) { /* no pay section */ }
|
|
1089
1121
|
renderTask(d.task, d.last_claim || null, viewer, goal, {
|
|
1090
1122
|
grade: d.grade || null,
|
|
1091
1123
|
sessionLogs: Array.isArray(d.session_logs) ? d.session_logs : null,
|
|
1092
1124
|
claims: Array.isArray(d.claims) ? d.claims : null,
|
|
1125
|
+
payTerms,
|
|
1093
1126
|
goalsList,
|
|
1094
1127
|
});
|
|
1095
1128
|
} catch (err) {
|
|
@@ -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`.
|