@bongos/core 1.24.9 → 1.24.11
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 +146 -86
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +14 -0
- package/clients/bongos-client/index.cjs +14 -0
- package/clients/bongos-client/index.d.ts +20 -1
- package/clients/bongos-client/index.mjs +14 -0
- package/docs/api/openapi.json +308 -5
- package/docs/api-reference.md +15 -4
- package/docs/architecture.md +4 -0
- package/docs/copy-inventory.md +109 -75
- package/docs/copy-registry.json +425 -89
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +34 -4
- package/docs/page-readings.json +301 -244
- package/modules/government/decision-rules.js +1 -0
- 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/diagrams.html +2 -2
- package/modules/hall-ui/public/drachmae.html +2 -2
- package/modules/hall-ui/public/fleet.html +2 -2
- package/modules/hall-ui/public/gate.html +2 -2
- package/modules/hall-ui/public/goal-map.html +2 -2
- package/modules/hall-ui/public/goals.html +2 -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-rules.html +2 -2
- package/modules/hall-ui/public/market-watch-settings.css +217 -0
- package/modules/hall-ui/public/market-watch-settings.html +113 -0
- package/modules/hall-ui/public/market-watch-settings.js +230 -0
- package/modules/hall-ui/public/market-watch-settings.states.json +14 -0
- package/modules/hall-ui/public/modules.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.css +12 -0
- package/modules/hall-ui/public/project-settings.html +2 -2
- package/modules/hall-ui/public/project-settings.js +63 -1
- package/modules/hall-ui/public/ranks.html +2 -2
- package/modules/hall-ui/public/roadmap.html +2 -2
- package/modules/hall-ui/public/roster.html +2 -2
- package/modules/hall-ui/public/sessions.html +2 -2
- package/modules/hall-ui/public/settings.html +2 -2
- package/modules/hall-ui/public/shell.js +3 -0
- 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/task.js +34 -1
- 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 +2 -2
- package/modules/hall-ui/records/market-watch-settings.md +11 -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/module.json +3 -2
- package/modules/market-watch/routes/settings.js +128 -0
- package/modules/market-watch/settings.js +207 -0
- package/modules/provisioning/starter-bundles.js +11 -6
- package/modules/ui-design/kit/serve.js +1 -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/bongos/serve-internal.js +2 -2
- package/src/module-api.js +1 -1
- package/tests/government_act_alone_buildings.mjs +1 -0
- package/tests/hall_nav.mjs +4 -2
- package/tests/hall_page_gate_map.mjs +4 -0
- package/tests/market_watch_data_db.mjs +27 -0
- package/tests/market_watch_module.mjs +8 -4
- package/tests/market_watch_settings.mjs +392 -0
- package/tests/nav_permission_atoms.mjs +3 -2
- 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,230 @@
|
|
|
1
|
+
// market-watch-settings.js — the owner's Market Watch settings (task 1004938, goal 1000137),
|
|
2
|
+
// built to docs/design/mocks/market-watch/settings.html, option B, the fuel gauge.
|
|
3
|
+
//
|
|
4
|
+
// One read and three writes, all owner-only on the server (the project.settings.manage atom plus the owner seat):
|
|
5
|
+
// GET /market-watch/settings everything drawn here
|
|
6
|
+
// PATCH /market-watch/settings { enabled } | { monthly_cap_usd } | { cadence }
|
|
7
|
+
// PUT /market-watch/settings/key { key } (write-only: the answer says set or not set)
|
|
8
|
+
// DELETE /market-watch/settings/key
|
|
9
|
+
// Every write answers with the whole view, so the page redraws from what the server
|
|
10
|
+
// stored, never from what it asked for.
|
|
11
|
+
//
|
|
12
|
+
// A 403 shows the owner-only panel and a 404 the "module is off" panel, so a refusal is
|
|
13
|
+
// never mistaken for an empty gauge.
|
|
14
|
+
(() => {
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
const API = '/api/bongos/market-watch/settings';
|
|
18
|
+
const client = window.BongosClient.createClient({ baseUrl: '', credentials: 'same-origin', throwOnError: false });
|
|
19
|
+
const { escapeHtml, toast } = window.OTB;
|
|
20
|
+
const $ = (id) => document.getElementById(id);
|
|
21
|
+
|
|
22
|
+
const DAY_MS = 24 * 60 * 60 * 1000;
|
|
23
|
+
const CADENCE_DAYS = { weekly: 7, biweekly: 14, monthly: 30 };
|
|
24
|
+
const CADENCE_WORDS = { weekly: 'weekly', biweekly: 'every 2 weeks', monthly: 'monthly' };
|
|
25
|
+
const REASONS = {
|
|
26
|
+
cap_reached: 'cap reached',
|
|
27
|
+
off: 'switched off',
|
|
28
|
+
no_key: 'no key',
|
|
29
|
+
spend_unreadable: 'spend unreadable',
|
|
30
|
+
error: 'error',
|
|
31
|
+
lease_lost: 'cut off',
|
|
32
|
+
};
|
|
33
|
+
const FUTURE = 2; // empty cells after the next run, as the mockup draws them
|
|
34
|
+
|
|
35
|
+
let view = null;
|
|
36
|
+
let busy = false;
|
|
37
|
+
|
|
38
|
+
const money = (n) => (Number.isInteger(n) ? `$${n}` : `$${n.toFixed(2)}`);
|
|
39
|
+
const shortDate = (iso) => {
|
|
40
|
+
const d = new Date(iso);
|
|
41
|
+
return `${d.getUTCDate()} ${d.toLocaleString('en-US', { month: 'short', timeZone: 'UTC' }).toLowerCase()}`;
|
|
42
|
+
};
|
|
43
|
+
const weekday = (iso) => new Date(iso).toLocaleString('en-US', { weekday: 'long', timeZone: 'UTC' }).toLowerCase();
|
|
44
|
+
|
|
45
|
+
function show(id) {
|
|
46
|
+
for (const s of ['mws-sealed', 'mws-off', 'mws-body']) $(s).hidden = s !== id;
|
|
47
|
+
}
|
|
48
|
+
function error(message) {
|
|
49
|
+
$('mws-error').hidden = !message;
|
|
50
|
+
$('mws-error').textContent = message || '';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function send(method, body) {
|
|
54
|
+
let res;
|
|
55
|
+
try {
|
|
56
|
+
res = await client.request(method, API + (method === 'PUT' || method === 'DELETE' ? '/key' : ''), body === undefined ? undefined : { body });
|
|
57
|
+
} catch (_) {
|
|
58
|
+
return { ok: false, message: 'Could not reach the server.' };
|
|
59
|
+
}
|
|
60
|
+
if (res.status === 401) { window.location.assign('/api/bongos/auth/web/start'); return { ok: false }; }
|
|
61
|
+
if (res.status === 403) { show('mws-sealed'); return { ok: false }; }
|
|
62
|
+
if (res.status === 404) { show('mws-off'); return { ok: false }; }
|
|
63
|
+
if (!res.ok) {
|
|
64
|
+
const err = (res.data && res.data.error) || {};
|
|
65
|
+
return { ok: false, message: err.message && err.message !== err.code ? err.message : 'That did not save.' };
|
|
66
|
+
}
|
|
67
|
+
return { ok: true, data: res.data };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// ---- drawing ---------------------------------------------------------------------
|
|
71
|
+
|
|
72
|
+
function gauge(v, cap) {
|
|
73
|
+
const spent = v.spent_this_month_usd;
|
|
74
|
+
const run = v.run_estimate_usd || 0;
|
|
75
|
+
const p = Math.min(100, (spent / cap) * 100);
|
|
76
|
+
const n = Math.max(0, Math.min(100 - p, (run / cap) * 100));
|
|
77
|
+
$('mws-arc').setAttribute('stroke-dasharray', `${p} 100`);
|
|
78
|
+
$('mws-arc2').setAttribute('stroke-dasharray', `0 ${p} ${v.enabled ? n : 0} 100`);
|
|
79
|
+
$('mws-spent').textContent = money(spent);
|
|
80
|
+
$('mws-of').textContent = `of ${money(cap)} · ${v.month}`;
|
|
81
|
+
$('mws-ring').classList.toggle('mws-ring--off', !v.enabled);
|
|
82
|
+
let left;
|
|
83
|
+
if (!v.enabled) left = 'paused · $0 from now';
|
|
84
|
+
else if (!run) left = 'no run yet to measure';
|
|
85
|
+
else {
|
|
86
|
+
const l = Math.floor(Math.max(0, cap - spent) / run);
|
|
87
|
+
left = l > 0 ? `${l} run${l > 1 ? 's' : ''} of fuel left` : 'next run will be skipped';
|
|
88
|
+
}
|
|
89
|
+
$('mws-left').textContent = left;
|
|
90
|
+
$('mws-left').classList.toggle('mws-pill--empty', v.enabled && !!run && Math.floor(Math.max(0, cap - spent) / run) <= 0);
|
|
91
|
+
$('mws-cap-t').textContent = money(cap);
|
|
92
|
+
$('mws-hint').textContent = run
|
|
93
|
+
? `about ${Math.floor(cap / run)} runs a month · a run costs ${v.run_estimate_basis === 'ceiling' ? 'at most' : 'about'} $${run.toFixed(2)}`
|
|
94
|
+
: 'a run’s cost shows here after the first one';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function strip(v) {
|
|
98
|
+
const ran = v.recent_runs.slice().reverse(); // oldest first, left to right
|
|
99
|
+
const cells = ran.map((r) => {
|
|
100
|
+
const skipped = r.status === 'skipped' || r.status === 'failed';
|
|
101
|
+
const why = REASONS[r.reason] || r.reason || '';
|
|
102
|
+
const label = skipped
|
|
103
|
+
? `${shortDate(r.created_at)}: skipped${why ? `, ${why}` : ''}`
|
|
104
|
+
: `${shortDate(r.created_at)}: ${r.items_total} names watched${r.status === 'partial' ? `, stopped early${why ? ` (${why})` : ''}` : ''}`;
|
|
105
|
+
return `<li class="mws-wk ${skipped ? 'mws-wk--skip' : 'mws-wk--done'}" title="${escapeHtml(label)}" aria-label="${escapeHtml(label)}">`
|
|
106
|
+
+ `<b>${skipped ? '—' : escapeHtml(String(r.items_total))}</b><span>${escapeHtml(shortDate(r.created_at))}</span></li>`;
|
|
107
|
+
});
|
|
108
|
+
if (v.enabled) {
|
|
109
|
+
const days = CADENCE_DAYS[v.cadence] || 7;
|
|
110
|
+
const next = v.next_run.due_at ? new Date(v.next_run.due_at) : new Date();
|
|
111
|
+
const nextLabel = v.next_run.state === 'soon' ? 'soon' : shortDate(next.toISOString());
|
|
112
|
+
cells.push(`<li class="mws-wk mws-wk--next" aria-label="next run: ${escapeHtml(nextLabel)}"><b>?</b><span>${escapeHtml(nextLabel)}</span></li>`);
|
|
113
|
+
for (let i = 1; i <= FUTURE; i += 1) {
|
|
114
|
+
const d = new Date(next.getTime() + i * days * DAY_MS).toISOString();
|
|
115
|
+
cells.push(`<li class="mws-wk" aria-hidden="true"><b></b><span>${escapeHtml(shortDate(d))}</span></li>`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
$('mws-weeks').style.setProperty('--mws-cells', String(Math.max(cells.length, 3)));
|
|
119
|
+
$('mws-weeks').innerHTML = cells.join('') || '';
|
|
120
|
+
const reasons = [...new Set(v.recent_runs.filter((r) => r.status === 'skipped' || r.status === 'failed').map((r) => REASONS[r.reason] || r.reason).filter(Boolean))];
|
|
121
|
+
const legend = [];
|
|
122
|
+
if (ran.length) legend.push('<span>number = names watched</span>');
|
|
123
|
+
if (reasons.length) legend.push(`<span class="mws-legend__skip">— = skipped, ${escapeHtml(reasons.join(', '))}</span>`);
|
|
124
|
+
if (v.enabled) legend.push('<span>dashed = next run</span>');
|
|
125
|
+
if (!ran.length) legend.unshift('<span>no runs yet</span>');
|
|
126
|
+
$('mws-legend').innerHTML = legend.join('');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function draw(v) {
|
|
130
|
+
view = v;
|
|
131
|
+
show('mws-body');
|
|
132
|
+
error('');
|
|
133
|
+
const cap = v.monthly_cap_usd;
|
|
134
|
+
$('mws-power').setAttribute('aria-pressed', String(v.enabled));
|
|
135
|
+
$('mws-power-t').textContent = v.enabled ? 'Market Watch is on' : 'Market Watch is off';
|
|
136
|
+
const slider = $('mws-cap');
|
|
137
|
+
slider.max = String(Math.max(60, Math.ceil(cap / 5) * 5));
|
|
138
|
+
slider.value = String(cap);
|
|
139
|
+
slider.setAttribute('aria-valuetext', `${money(cap)} a month`);
|
|
140
|
+
for (const b of $('mws-cad').querySelectorAll('button')) {
|
|
141
|
+
const on = b.dataset.c === v.cadence;
|
|
142
|
+
b.classList.toggle('mws-chip--on', on);
|
|
143
|
+
b.setAttribute('aria-pressed', String(on));
|
|
144
|
+
}
|
|
145
|
+
gauge(v, cap);
|
|
146
|
+
strip(v);
|
|
147
|
+
$('mws-key-state').textContent = v.key_set ? 'set' : 'not set';
|
|
148
|
+
$('mws-key-state').classList.toggle('mws-key__state--set', v.key_set);
|
|
149
|
+
$('mws-key-clear').hidden = !v.key_set;
|
|
150
|
+
$('mws-key-save').textContent = v.key_set ? 'Replace' : 'Save';
|
|
151
|
+
$('mws-key-in').disabled = !v.storage_configured;
|
|
152
|
+
$('mws-key-save').disabled = !v.storage_configured;
|
|
153
|
+
$('mws-key-note').textContent = !v.storage_configured
|
|
154
|
+
? 'Key storage is not set up on this project yet, so a key cannot be saved.'
|
|
155
|
+
: v.enabled && !v.key_set
|
|
156
|
+
? 'Market Watch is on but has no key, so each run will be skipped until one is set.'
|
|
157
|
+
: 'Runs use this key, so the project’s Anthropic account pays for them. It is stored encrypted and never shown again.';
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function write(method, body, done) {
|
|
161
|
+
if (busy) return;
|
|
162
|
+
busy = true;
|
|
163
|
+
const r = await send(method, body);
|
|
164
|
+
busy = false;
|
|
165
|
+
if (!r.ok) { if (r.message) error(r.message); if (view) draw(view); return; }
|
|
166
|
+
draw(r.data);
|
|
167
|
+
if (done) toast(done(r.data), { clear: true, duration: 2200 });
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// ---- controls --------------------------------------------------------------------
|
|
171
|
+
|
|
172
|
+
$('mws-power').addEventListener('click', () => {
|
|
173
|
+
if (!view) return;
|
|
174
|
+
write('PATCH', { enabled: !view.enabled }, (v) => {
|
|
175
|
+
if (!v.enabled) return 'off · nothing runs';
|
|
176
|
+
return v.next_run.due_at ? `on · next run ${weekday(v.next_run.due_at)}` : 'on · next run soon';
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
$('mws-cap').addEventListener('input', (e) => {
|
|
181
|
+
if (!view) return;
|
|
182
|
+
const cap = Number(e.target.value);
|
|
183
|
+
e.target.setAttribute('aria-valuetext', `${money(cap)} a month`);
|
|
184
|
+
gauge(view, cap);
|
|
185
|
+
});
|
|
186
|
+
$('mws-cap').addEventListener('change', (e) => {
|
|
187
|
+
const cap = Number(e.target.value);
|
|
188
|
+
if (!view || cap === view.monthly_cap_usd) return;
|
|
189
|
+
write('PATCH', { monthly_cap_usd: cap }, (v) => `cap ${money(v.monthly_cap_usd)} a month`);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
$('mws-cad').addEventListener('click', (e) => {
|
|
193
|
+
const b = e.target.closest('button[data-c]');
|
|
194
|
+
if (!b || !view || b.dataset.c === view.cadence) return;
|
|
195
|
+
write('PATCH', { cadence: b.dataset.c }, (v) => `runs ${CADENCE_WORDS[v.cadence] || v.cadence}`);
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
$('mws-key-save').addEventListener('click', () => {
|
|
199
|
+
const input = $('mws-key-in');
|
|
200
|
+
const key = input.value.trim();
|
|
201
|
+
if (!key) { error('Paste the key first.'); input.focus(); return; }
|
|
202
|
+
// The field is emptied before the request goes out: the key never sits in the page
|
|
203
|
+
// longer than it takes to send it.
|
|
204
|
+
input.value = '';
|
|
205
|
+
write('PUT', { key }, () => 'key saved');
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Clearing asks twice: the first press turns the button into its own question.
|
|
209
|
+
let clearArmed = null;
|
|
210
|
+
$('mws-key-clear').addEventListener('click', () => {
|
|
211
|
+
const b = $('mws-key-clear');
|
|
212
|
+
if (!clearArmed) {
|
|
213
|
+
b.textContent = 'Clear the key?';
|
|
214
|
+
clearArmed = setTimeout(() => { clearArmed = null; b.textContent = 'Clear'; }, 4000);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
clearTimeout(clearArmed);
|
|
218
|
+
clearArmed = null;
|
|
219
|
+
b.textContent = 'Clear';
|
|
220
|
+
write('DELETE', undefined, () => 'key cleared · runs will be skipped');
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
async function load() {
|
|
224
|
+
const r = await send('GET');
|
|
225
|
+
if (r.ok) draw(r.data);
|
|
226
|
+
else if (r.message) error(r.message);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
document.addEventListener('DOMContentLoaded', load);
|
|
230
|
+
})();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_": "MARKET WATCH SETTINGS (task 1004938; design of record docs/design/mocks/market-watch/settings.html, option B, the fuel gauge) through modules/ui-design/kit/render.js. The page reads ONE owner-only route, GET /market-watch/settings, so against the kit's own stub it renders its 'module is off' state (the 404), which the floors must hold too. To look at the populated gauge run it against the hall-preview harness, whose market-watch__settings.json fixture carries the mockup's month (12.40 of 20 spent, six runs, one skipped at the cap):\n\n node scripts/hall-preview/server.js --port 4620 --fixture-me --rank archon\n node modules/ui-design/kit/render.js --states modules/hall-ui/public/market-watch-settings.states.json --base-in http://localhost:4620 --base-out http://localhost:4620",
|
|
3
|
+
"page": "/builders/market-watch-settings",
|
|
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
|
+
"gauge": { "auth": true, "actions": [["wait", 900], ["clickIfVisible", "[data-craft-close]"], ["wait", 300]], "expect": { "visible": ["#mws-body"] } },
|
|
11
|
+
"out": { "auth": false, "actions": [["wait", 600]] }
|
|
12
|
+
},
|
|
13
|
+
"reducedMotion": { "auth": true, "actions": [["wait", 800]] }
|
|
14
|
+
}
|
|
@@ -127,8 +127,8 @@
|
|
|
127
127
|
|
|
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
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
131
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
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>
|
|
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>
|
|
@@ -61,8 +61,8 @@
|
|
|
61
61
|
|
|
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
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
65
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
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>
|
|
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>
|
|
@@ -112,8 +112,8 @@
|
|
|
112
112
|
|
|
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
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
116
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
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>
|
|
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
|
|
@@ -207,8 +207,8 @@
|
|
|
207
207
|
chrome exists before the page script runs. -->
|
|
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
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
211
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
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>
|
|
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);
|
|
@@ -71,8 +71,8 @@
|
|
|
71
71
|
|
|
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
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
75
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
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>
|
|
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 () => {
|
|
@@ -73,8 +73,8 @@
|
|
|
73
73
|
chrome exists before the page script runs. -->
|
|
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
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
77
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
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>
|
|
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>
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
|
|
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
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
47
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
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>
|
|
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
|
|
@@ -55,8 +55,8 @@
|
|
|
55
55
|
chrome exists before the page script runs. -->
|
|
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
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
59
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
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>
|
|
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. -->
|
|
@@ -111,8 +111,8 @@
|
|
|
111
111
|
chrome exists before the page script runs. -->
|
|
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
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
115
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
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>
|
|
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>
|
|
@@ -486,8 +486,8 @@
|
|
|
486
486
|
chrome exists before the page scripts run. -->
|
|
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
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
490
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
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>
|
|
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). -->
|
|
@@ -125,6 +125,7 @@
|
|
|
125
125
|
settings: '<circle cx="9" cy="9" r="2.5"/><path d="M9 2.5v2m0 9v2M2.5 9h2m9 0h2M4.4 4.4l1.4 1.4m6.4 6.4 1.4 1.4m0-9.2-1.4 1.4M5.8 12.2l-1.4 1.4"/>',
|
|
126
126
|
deploy: '<path d="M9 12V3M5.5 6.5 9 3l3.5 3.5M3 12.5v2a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1v-2"/>',
|
|
127
127
|
fleet: '<path d="M4 2.5h10v13l-1.7-1-1.6 1-1.7-1-1.7 1-1.6-1-1.7 1v-13Z"/><path d="M6.5 6h5M6.5 9h5M6.5 12h3"/>',
|
|
128
|
+
fuel: '<path d="M3 13a6 6 0 1 1 12 0"/><path d="M9 13l3-4"/>',
|
|
128
129
|
permissions: '<circle cx="6" cy="12" r="3.5"/><path d="M8.5 9.5 15 3M12.5 5.5l2 2M10.5 7.5l1.5 1.5"/>',
|
|
129
130
|
// The governor city: a pediment over columns, the Governing mode's own glyph.
|
|
130
131
|
city: '<path d="M2.5 7 9 3l6.5 4M4 7.5v6.5M7 7.5v6.5M11 7.5v6.5M14 7.5v6.5M2.5 15.5h13"/>',
|
|
@@ -386,6 +387,8 @@
|
|
|
386
387
|
// of `provisioning.fleet.manage`, the atom its page and its one read check (the
|
|
387
388
|
// Deploy precedent above); dropped where the provisioning module is not enabled.
|
|
388
389
|
{ id: 'fleet', label: 'Fleet costs', icon: 'fleet', href: '/fleet', gate: 'government', module: 'provisioning', position: true },
|
|
390
|
+
// task 1004938: owner-only Market Watch settings; 'archon' mirrors its routes (cosmetic, ADR 0016).
|
|
391
|
+
{ id: 'market-watch-settings', label: 'Market Watch', icon: 'fuel', href: '/market-watch-settings', gate: 'archon', module: 'market-watch', position: true },
|
|
389
392
|
// BV1.R113: the roles-and-permissions tab (ADR 0151). The one genuinely
|
|
390
393
|
// Archon-only item the 'government' gate comment anticipated — cosmetic
|
|
391
394
|
// only. The wall is the `government.manage` atom checked on each of the
|
|
@@ -230,8 +230,8 @@
|
|
|
230
230
|
|
|
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
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
234
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
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>
|
|
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>
|
|
@@ -218,8 +218,8 @@
|
|
|
218
218
|
|
|
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
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
222
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
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>
|
|
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. -->
|
|
@@ -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-10-25-escape"></script>
|
|
60
60
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
61
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
62
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
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>
|
|
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) {
|
|
@@ -140,8 +140,8 @@
|
|
|
140
140
|
|
|
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
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
144
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
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>
|
|
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-10-25-escape"></script>
|
|
146
146
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
147
|
-
<script src="/builders/craft-mode.js?v=2026-10-
|
|
148
|
-
<script src="/builders/shell.js?v=2026-10-
|
|
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>
|
|
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>
|