@bongos/core 1.24.29 → 1.24.31
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 +198 -108
- 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 +12 -0
- package/clients/bongos-client/index.mjs +12 -0
- package/docs/api/openapi.json +174 -1
- package/docs/api-reference.md +12 -2
- package/docs/copy-inventory.md +226 -224
- package/docs/copy-registry.json +583 -549
- package/docs/module-api-changelog.md +2 -0
- package/docs/page-inventory.json +18 -37
- package/docs/page-readings.json +306 -361
- package/modules/copy-desk/module.json +1 -1
- package/modules/copy-desk/routes/copy-desk-studio.js +103 -0
- package/modules/copy-desk/tests/copy_no_cms.mjs +2 -2
- package/modules/government/db.js +29 -0
- package/modules/government/docket.js +61 -4
- package/modules/government/routes/government.js +2 -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/builders.js +3 -79
- 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 +1 -1
- 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/hall-render.js +68 -167
- package/modules/hall-ui/public/hall-widgets.js +4 -45
- package/modules/hall-ui/public/home-city-draw.js +583 -0
- package/modules/hall-ui/public/home-city.css +102 -0
- package/modules/hall-ui/public/home-city.js +339 -0
- package/modules/hall-ui/public/home-gallery.css +129 -0
- package/modules/hall-ui/public/home-gallery.js +349 -0
- package/modules/hall-ui/public/home-studio.css +72 -0
- package/modules/hall-ui/public/home-studio.js +203 -0
- 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 +9 -7
- package/modules/hall-ui/public/index.states.json +73 -8
- package/modules/hall-ui/public/market-watch-item.html +1 -1
- package/modules/hall-ui/public/market-watch-rules.html +1 -1
- package/modules/hall-ui/public/market-watch-settings.js +6 -1
- package/modules/hall-ui/public/market-watch.html +141 -54
- package/modules/hall-ui/public/market-watch.js +54 -0
- package/modules/hall-ui/public/market-watch.states.json +2 -1
- 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 +2 -3
- package/modules/hall-ui/public/street.html +1 -1
- package/modules/hall-ui/public/studio.html +9 -1
- package/modules/hall-ui/public/style.css +74 -27
- 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/home-city.md +189 -0
- package/modules/hall-ui/records/loader.md +1 -1
- package/modules/hall-ui/records/market-watch-settings.md +16 -3
- package/modules/hall-ui/records/market-watch.md +3 -1
- package/modules/lifecycle/home-city.js +388 -0
- package/modules/lifecycle/home-gallery.js +320 -0
- package/modules/lifecycle/migrations/lifecycle_020_work_appreciations.sql +36 -0
- package/modules/lifecycle/module.json +2 -0
- package/modules/lifecycle/routes/home-city.js +29 -0
- package/modules/lifecycle/routes/home-gallery.js +37 -0
- package/modules/market-watch/routes/settings.js +1 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +38 -0
- package/scripts/bongos/leak-scan-allowlist.js +8 -0
- package/scripts/hall-preview/README.md +1 -0
- package/scripts/hall-preview/server.js +10 -0
- package/src/bongos/serve-internal.js +17 -3
- package/src/module-api.js +1 -1
- package/tests/claim_from_task_and_home.mjs +8 -43
- package/tests/hall_board_world.mjs +10 -12
- package/tests/hall_builders_page_pass.mjs +18 -27
- package/tests/hall_home_ships_bounded.mjs +4 -3
- package/tests/hall_loading.mjs +1 -1
- package/tests/hall_nav.mjs +11 -4
- package/tests/hall_page_gate_map.mjs +7 -4
- package/tests/home_bounded_reads.mjs +16 -31
- package/tests/home_city.mjs +580 -0
- package/tests/home_city_widget.mjs +260 -0
- package/tests/home_gallery.mjs +255 -0
- package/tests/home_gallery_widget.mjs +220 -0
- package/tests/home_studio.mjs +169 -0
- package/tests/home_studio_widget.mjs +157 -0
- package/tests/leaderboard_roles.mjs +3 -2
- package/tests/market_watch_pages.mjs +162 -0
- package/tests/nav_permission_atoms.mjs +2 -2
- package/modules/hall-ui/public/market-watch-settings.html +0 -115
- package/modules/hall-ui/public/market-watch-settings.states.json +0 -14
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"category": { "parent": "quality" },
|
|
8
8
|
"default": true,
|
|
9
9
|
"maintenance": { "status": "core-maintained" },
|
|
10
|
-
"contributes": { "routes": ["copy-desk", "copy-desk-artist", "copy-desk-release"], "migrations": true, "skills": ["tweak"], "pollers": ["stuck-page-digest", "auto-close-rounds"] },
|
|
10
|
+
"contributes": { "routes": ["copy-desk", "copy-desk-artist", "copy-desk-release", "copy-desk-studio"], "migrations": true, "skills": ["tweak"], "pollers": ["stuck-page-digest", "auto-close-rounds"] },
|
|
11
11
|
"provides": ["copy-desk.artistStats", "copy-desk.stuckPageTweaksFor"],
|
|
12
12
|
"consumes": ["lifecycle"]
|
|
13
13
|
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// modules/copy-desk/routes/copy-desk-studio.js — the artist's studio, its copy-desk
|
|
2
|
+
// half (task 1005061, Home v16 step 4; mock docs/design/mocks/hall/home/v16-studio.html).
|
|
3
|
+
//
|
|
4
|
+
// GET /copy-desk/studio — one bounded read for three of the studio's shelves:
|
|
5
|
+
//
|
|
6
|
+
// easel the page round the caller holds (the Tweak/CopyWrite Flow's `resume`), or,
|
|
7
|
+
// with nothing held, the page the flow would recommend next.
|
|
8
|
+
// waiting "Waiting for new inspiration": pages whose words changed since an artist
|
|
9
|
+
// last designed them (the recommendation's `changed` tier, drift from the
|
|
10
|
+
// page readings), most changed lines first, at most MAX_WAITING, with the
|
|
11
|
+
// whole count.
|
|
12
|
+
// words "Words to tune": the newest open copy-desk flags, at most MAX_WORDS.
|
|
13
|
+
//
|
|
14
|
+
// Everything is composed by the flow's own reader (page-status.js composeNext over
|
|
15
|
+
// the inventory, the readings, the ledger's page rounds and the page asks), so the
|
|
16
|
+
// studio and the Studio page can never disagree about a page. Read only: it writes no
|
|
17
|
+
// row and returns no replacement wording (copy_no_cms.mjs holds every route file here
|
|
18
|
+
// to that). The other two shelves, the works the caller pinned and signed, are the
|
|
19
|
+
// lifecycle module's (GET /home/studio/works).
|
|
20
|
+
//
|
|
21
|
+
// Apart from routes/copy-desk.js because that file sits at the 1,500-line ratchet.
|
|
22
|
+
|
|
23
|
+
'use strict';
|
|
24
|
+
|
|
25
|
+
const express = require('express');
|
|
26
|
+
const api = require('../../../src/module-api');
|
|
27
|
+
const flags = require('../flags');
|
|
28
|
+
const pageData = require('../page-data');
|
|
29
|
+
const pageStatus = require('../page-status');
|
|
30
|
+
|
|
31
|
+
const log = api.logger('copy-desk');
|
|
32
|
+
|
|
33
|
+
const MAX_WAITING = 4;
|
|
34
|
+
const MAX_WORDS = 3;
|
|
35
|
+
const SURFACE_NAMES = Object.freeze({ landing: 'The public site', builders: 'The builders’ hall', status: 'The status page' });
|
|
36
|
+
|
|
37
|
+
function editorPath(pageId) { return `/tweak-editor?page=${encodeURIComponent(pageId)}`; }
|
|
38
|
+
|
|
39
|
+
// The studio's shape from the flow's answer and the newest open flags. Pure.
|
|
40
|
+
function composeStudio({ next, flagRows }) {
|
|
41
|
+
const n = next || {};
|
|
42
|
+
const all = [n.recommended, ...(n.alternatives || [])].filter(Boolean);
|
|
43
|
+
const changed = all.filter((c) => c.tier === 'changed' && c.changed_lines > 0)
|
|
44
|
+
.sort((a, b) => b.changed_lines - a.changed_lines);
|
|
45
|
+
const r = n.resume;
|
|
46
|
+
const easel = r
|
|
47
|
+
? { held: true, page_id: r.page_id, title: r.title, place: SURFACE_NAMES[r.surface] || null, round: r.round || null, open: editorPath(r.page_id) }
|
|
48
|
+
: (n.recommended
|
|
49
|
+
? { held: false, page_id: n.recommended.page_id, title: n.recommended.title, place: SURFACE_NAMES[n.recommended.surface] || null, why: n.recommended.why || null, open: editorPath(n.recommended.page_id) }
|
|
50
|
+
: null);
|
|
51
|
+
return {
|
|
52
|
+
easel,
|
|
53
|
+
waiting: {
|
|
54
|
+
total: changed.length,
|
|
55
|
+
pages: changed.slice(0, MAX_WAITING).map((c) => ({
|
|
56
|
+
page_id: c.page_id, title: c.title, place: SURFACE_NAMES[c.surface] || null, changed_lines: c.changed_lines, open: editorPath(c.page_id),
|
|
57
|
+
})),
|
|
58
|
+
},
|
|
59
|
+
words: (flagRows || []).slice(0, MAX_WORDS).map((f) => ({
|
|
60
|
+
id: String(f.id),
|
|
61
|
+
text: f.text_at_flag || null,
|
|
62
|
+
reason: f.reason || '',
|
|
63
|
+
place: SURFACE_NAMES[f.surface] || null,
|
|
64
|
+
})),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
module.exports = function buildCopyDeskStudioRouter() {
|
|
69
|
+
const router = express.Router();
|
|
70
|
+
|
|
71
|
+
// rank: any-builder — GET /copy-desk/studio, the caller's easel, the pages waiting
|
|
72
|
+
// for an artist and the newest open flags. A work list, like /copy-desk/next and
|
|
73
|
+
// /copy-desk/queue, which any signed-in builder may already read.
|
|
74
|
+
router.get('/copy-desk/studio', api.requireBuilder, async (req, res) => {
|
|
75
|
+
try {
|
|
76
|
+
const inv = pageData.loadInventory();
|
|
77
|
+
const lifecycle = api.resolveOptional && api.resolveOptional('lifecycle');
|
|
78
|
+
const readsOk = inv.ok && !!lifecycle && typeof lifecycle.listPageTweakTasks === 'function';
|
|
79
|
+
const readings = pageData.loadReadings();
|
|
80
|
+
const [tasks, asks, flagRows] = await Promise.all([
|
|
81
|
+
readsOk ? lifecycle.listPageTweakTasks() : Promise.resolve([]),
|
|
82
|
+
readsOk ? flags.pageAskSummary() : Promise.resolve([]),
|
|
83
|
+
flags.listFlags({ status: 'open', scopes: flags.FLAG_SCOPES, limit: MAX_WORDS }),
|
|
84
|
+
]);
|
|
85
|
+
// Every candidate, not the flow's four alternatives: the waiting shelf is a filter
|
|
86
|
+
// over all of them. Bounded by the inventory's pages, never the corpus.
|
|
87
|
+
const next = readsOk ? pageStatus.composeNext({
|
|
88
|
+
inventory: inv.data, readings: readings.ok ? readings.data : null, tasks, asks, builderId: req.builder.id,
|
|
89
|
+
alternatives: (inv.data.pages || []).length,
|
|
90
|
+
}) : null;
|
|
91
|
+
res.set('Cache-Control', 'private, no-store');
|
|
92
|
+
res.json(composeStudio({ next, flagRows }));
|
|
93
|
+
} catch (err) {
|
|
94
|
+
log.error({ err }, 'GET /copy-desk/studio failed');
|
|
95
|
+
if (!res.headersSent) res.fail('copy_studio_failed', 500);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return router;
|
|
100
|
+
};
|
|
101
|
+
module.exports.composeStudio = composeStudio;
|
|
102
|
+
module.exports.MAX_WAITING = MAX_WAITING;
|
|
103
|
+
module.exports.MAX_WORDS = MAX_WORDS;
|
|
@@ -74,8 +74,8 @@ const MODULE_FILES = ['registry.js', 'flags.js', 'proposals.js', 'pages.js', 'pa
|
|
|
74
74
|
|
|
75
75
|
console.log('copy-desk: NO LIVE CMS');
|
|
76
76
|
|
|
77
|
-
await test('the route files are exactly the
|
|
78
|
-
assert.deepEqual(ROUTE_FILES, ['routes/copy-desk-artist.js', 'routes/copy-desk-release.js', 'routes/copy-desk.js'],
|
|
77
|
+
await test('the route files are exactly the four the module declares', () => {
|
|
78
|
+
assert.deepEqual(ROUTE_FILES, ['routes/copy-desk-artist.js', 'routes/copy-desk-release.js', 'routes/copy-desk-studio.js', 'routes/copy-desk.js'],
|
|
79
79
|
'a new route file is a new place a write could hide: add it here, and to the write-surface scan below');
|
|
80
80
|
});
|
|
81
81
|
|
package/modules/government/db.js
CHANGED
|
@@ -902,6 +902,34 @@ async function listAccessHolders() {
|
|
|
902
902
|
return rows;
|
|
903
903
|
}
|
|
904
904
|
|
|
905
|
+
// THE GOAL OF A DOCKET MATTER (task 1005056): which goal each goal-shaped matter
|
|
906
|
+
// belongs to, so Home's project city can put the docket's "yours" over the right
|
|
907
|
+
// tower. A blocker belongs to every goal whose tasks it holds (task_blockers →
|
|
908
|
+
// tasks.goal_id); a criterion and a goal join request carry their goal. The
|
|
909
|
+
// tables are core's; this is a read across the wall in SQL, never a require.
|
|
910
|
+
// → [{ matter_id: 'blockers:12', goal_id: '1000095' }, …]; ids are numeric or skipped.
|
|
911
|
+
async function goalsOfMatters({ blockers = [], criteria = [], joinRequests = [] } = {}) {
|
|
912
|
+
const ids = (list) => (Array.isArray(list) ? list : []).map(String).filter((v) => /^\d{1,18}$/.test(v));
|
|
913
|
+
const [b, c, j] = [ids(blockers), ids(criteria), ids(joinRequests)];
|
|
914
|
+
if (!b.length && !c.length && !j.length) return [];
|
|
915
|
+
const { rows } = await pool.query(
|
|
916
|
+
`SELECT 'blockers:' || tb.blocker_id AS matter_id, t.goal_id::text AS goal_id
|
|
917
|
+
FROM task_blockers tb
|
|
918
|
+
JOIN tasks t ON t.id = tb.task_id
|
|
919
|
+
WHERE tb.blocker_id = ANY($1::bigint[]) AND t.goal_id IS NOT NULL
|
|
920
|
+
UNION
|
|
921
|
+
SELECT 'criteria_uat:' || c.id, c.goal_id::text
|
|
922
|
+
FROM done_when_criteria c
|
|
923
|
+
WHERE c.id = ANY($2::bigint[]) AND c.goal_id IS NOT NULL
|
|
924
|
+
UNION
|
|
925
|
+
SELECT 'goal_join_requests:' || r.id, r.goal_id::text
|
|
926
|
+
FROM goal_membership_requests r
|
|
927
|
+
WHERE r.id = ANY($3::bigint[])`,
|
|
928
|
+
[b, c, j],
|
|
929
|
+
);
|
|
930
|
+
return rows;
|
|
931
|
+
}
|
|
932
|
+
|
|
905
933
|
// Record that a builder holds an access. Idempotent: an existing row is kept as it
|
|
906
934
|
// was (its original granted_at/by). → true when a row was added.
|
|
907
935
|
async function grantAccess(accessKey, builderId, grantedBy) {
|
|
@@ -1342,6 +1370,7 @@ module.exports = {
|
|
|
1342
1370
|
listLeads,
|
|
1343
1371
|
listActiveArchons,
|
|
1344
1372
|
listAccessHolders,
|
|
1373
|
+
goalsOfMatters,
|
|
1345
1374
|
grantAccess,
|
|
1346
1375
|
revokeAccess,
|
|
1347
1376
|
getActiveBuilder,
|
|
@@ -932,14 +932,69 @@ async function blockersByPerson(builder, { contributions = registeredContributio
|
|
|
932
932
|
return { available: false, reason: 'unavailable', people: [], nobody: [], yours: waiting.forViewer(null, builder.id) };
|
|
933
933
|
}
|
|
934
934
|
await attachAsks(matters, asksDb.asksFor, timeoutMs);
|
|
935
|
+
const seats = await seatsFor(matters);
|
|
936
|
+
if (!seats) return { available: false, reason: 'unavailable', people: [], nobody: [], yours: waiting.forViewer(null, builder.id) };
|
|
937
|
+
const grouped = waiting.groupByPerson(matters, seats);
|
|
938
|
+
return { available: true, ...grouped, yours: waiting.forViewer(grouped, builder.id) };
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// The seats a set of matters routes against (loadSeats), reading a board's holders
|
|
942
|
+
// only when some matter names a board seat. null when the seats are unreadable.
|
|
943
|
+
async function seatsFor(matters) {
|
|
935
944
|
const boardSeat = matters.some((m) => /^board:/.test(routing.seatOf(m)));
|
|
936
945
|
const government = boardSeat
|
|
937
946
|
? await loadGovernment({ decisions: await board.decisionRulesView().catch(() => null) })
|
|
938
947
|
: { holders: {} };
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
948
|
+
return loadSeats(government);
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
// WHAT IS YOURS, PER GOAL (task 1005056). Home's project city puts a salmon dot
|
|
952
|
+
// over a working area for each matter waiting on the viewer; this answers it the
|
|
953
|
+
// docket's way, so the city and the docket can never disagree. The goal-shaped
|
|
954
|
+
// sources are read exactly as buildDocket reads them (each under its own gate),
|
|
955
|
+
// carry their stored asks, and are routed against the seats with routing.js — a
|
|
956
|
+
// matter counts when its route is `yours`. Like docketFor, a viewer without the
|
|
957
|
+
// docket's own atom is routed nothing. Each matter is then placed on its goal(s)
|
|
958
|
+
// (db.goalsOfMatters). A source that fails is left out, as the docket leaves it
|
|
959
|
+
// unavailable; unreadable seats say `available: false` rather than "none".
|
|
960
|
+
// → { available: true, goals: { [goal_id]: count } } | { available: false, reason }
|
|
961
|
+
const GOAL_SOURCES = Object.freeze(['blockers', 'criteria_uat', 'goal_join_requests']);
|
|
962
|
+
async function yoursByGoal(builder, {
|
|
963
|
+
contributions = registeredContributions(), now = Date.now(), timeoutMs = SOURCE_TIMEOUT_MS,
|
|
964
|
+
permissions = null, readAsks = asksDb.asksFor, readSeats = seatsFor, goalsOf = db.goalsOfMatters,
|
|
965
|
+
} = {}) {
|
|
966
|
+
if (!builder || builder.id == null) return { available: true, goals: {} };
|
|
967
|
+
const held = new Set(permissions || await resolver.resolveBuilderPermissions(builder.id));
|
|
968
|
+
if (!held.has(GOVERNMENT_GATE.permission)) return { available: true, goals: {} };
|
|
969
|
+
const ctx = Object.freeze({ builderId: String(builder.id), rank: builder.rank || null, has: (p) => held.has(p) });
|
|
970
|
+
const matters = [];
|
|
971
|
+
await Promise.all(GOAL_SOURCES.map(async (source) => {
|
|
972
|
+
const c = contributions.sources.get(source);
|
|
973
|
+
if (!c || !gateAllows(c.gate, ctx)) return;
|
|
974
|
+
const decl = EXPECTED_SOURCES.find((d) => d.source === source);
|
|
975
|
+
try {
|
|
976
|
+
const raw = await withTimeout(Promise.resolve(c.read(ctx, { now })), timeoutMs);
|
|
977
|
+
matters.push(...(Array.isArray(raw) ? raw : []).map((m) => normalizeMatter(m, { source, building: c.building || decl.building })).filter(Boolean));
|
|
978
|
+
} catch (err) {
|
|
979
|
+
log.warn({ source, err: err && err.message }, 'yours by goal: a source could not be read');
|
|
980
|
+
}
|
|
981
|
+
}));
|
|
982
|
+
const needs = matters.filter((m) => m.state === 'needs');
|
|
983
|
+
if (!needs.length) return { available: true, goals: {} };
|
|
984
|
+
await attachAsks(needs, readAsks, timeoutMs);
|
|
985
|
+
const seats = await readSeats(needs);
|
|
986
|
+
if (!seats) return { available: false, reason: 'unavailable' };
|
|
987
|
+
routing.routeAll(needs, ctx.builderId, seats, now);
|
|
988
|
+
const mine = needs.filter((m) => m.route && m.route.yours);
|
|
989
|
+
if (!mine.length) return { available: true, goals: {} };
|
|
990
|
+
const tail = (m) => m.id.slice(m.source.length + 1);
|
|
991
|
+
const of = (source) => mine.filter((m) => m.source === source).map(tail);
|
|
992
|
+
const rows = await goalsOf({ blockers: of('blockers'), criteria: of('criteria_uat'), joinRequests: of('goal_join_requests') });
|
|
993
|
+
const goals = {};
|
|
994
|
+
for (const r of rows || []) {
|
|
995
|
+
if (r && r.goal_id != null) goals[String(r.goal_id)] = (goals[String(r.goal_id)] || 0) + 1;
|
|
996
|
+
}
|
|
997
|
+
return { available: true, goals };
|
|
943
998
|
}
|
|
944
999
|
|
|
945
1000
|
// One matter with its stored ask and the seats, for passing it on to `toSeat`
|
|
@@ -1024,5 +1079,7 @@ module.exports = {
|
|
|
1024
1079
|
findMatterFor,
|
|
1025
1080
|
loadSeats,
|
|
1026
1081
|
blockersByPerson,
|
|
1082
|
+
yoursByGoal,
|
|
1083
|
+
GOAL_SOURCES,
|
|
1027
1084
|
matterWithSeats,
|
|
1028
1085
|
};
|
|
@@ -171,6 +171,8 @@ const governmentPort = {
|
|
|
171
171
|
askRequiredMessage: asks.ASK_REQUIRED_MESSAGE,
|
|
172
172
|
saveAsk: (...a) => asksDb.saveAsk(...a),
|
|
173
173
|
asksFor: (...a) => asksDb.asksFor(...a),
|
|
174
|
+
// task 1005056: per goal, the docket matters routed to this viewer (Home's city)
|
|
175
|
+
yoursByGoal: (viewer) => docket.yoursByGoal(viewer),
|
|
174
176
|
};
|
|
175
177
|
|
|
176
178
|
// An ask arrives bare, or wrapped as { ask } (the shape POST /blockers takes it in).
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
90
90
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
91
91
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
92
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
92
|
+
<script src="/builders/shell.js?v=2026-11-04-one-market-watch"></script>
|
|
93
93
|
<script src="/builders/palette.js" defer></script>
|
|
94
94
|
<script src="/builders/tweak-editor-lib.js?v=2026-09-28-tw23"></script>
|
|
95
95
|
<script src="/builders/studio-lib.js?v=2026-10-09-day"></script>
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
93
93
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
94
94
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
95
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
95
|
+
<script src="/builders/shell.js?v=2026-11-04-one-market-watch"></script>
|
|
96
96
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
97
97
|
<script src="/builders/palette.js" defer></script>
|
|
98
98
|
<script src="/builders/atlas.js?v=2026-09-02-index" defer></script>
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
62
62
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
63
63
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
64
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
64
|
+
<script src="/builders/shell.js?v=2026-11-04-one-market-watch"></script>
|
|
65
65
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
66
66
|
<script src="/builders/palette.js" defer></script>
|
|
67
67
|
<!-- the branding pack's currency label, loaded BEFORE blockers.js. -->
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
64
64
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
65
65
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
66
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
66
|
+
<script src="/builders/shell.js?v=2026-11-04-one-market-watch"></script>
|
|
67
67
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
68
68
|
<script src="/builders/palette.js" defer></script>
|
|
69
69
|
<script src="/builders/hall-kit.js"></script>
|
|
@@ -185,9 +185,6 @@
|
|
|
185
185
|
// (same pattern as lastNeedsState). These are un-landed confirmed tasks that
|
|
186
186
|
// GATE new claims and whose reward is pending until they land on ship.
|
|
187
187
|
let lastRebaseState = null;
|
|
188
|
-
// The public version progress, which the Home version tab draws (task 1004774).
|
|
189
|
-
// Null until it lands.
|
|
190
|
-
let lastProgressState = null;
|
|
191
188
|
|
|
192
189
|
// Cached /me payload from the most recent loadAll() call. Used by the
|
|
193
190
|
// welcome-chip reopen path (#408) so we can re-render the welcome panel
|
|
@@ -874,62 +871,6 @@
|
|
|
874
871
|
if (btn) window.location.hash = `#/idea/${encodeURIComponent(btn.dataset.triageIdea)}`;
|
|
875
872
|
});
|
|
876
873
|
|
|
877
|
-
// ---- Home "Next up" slice (task 2184) -------------------------------------
|
|
878
|
-
|
|
879
|
-
// Fill the top of the "Your work" card with the first five rows of the
|
|
880
|
-
// claimable queue (server order: priority, then calibrated estimate) so Home
|
|
881
|
-
// answers "what's my next action". Best-effort: a primer-gated 403, an auth
|
|
882
|
-
// hiccup, or an empty queue hides the slice — Home never shows an error for
|
|
883
|
-
// it. Re-runs with every loadAll (SSE nudges keep it current).
|
|
884
|
-
async function loadNextUp() {
|
|
885
|
-
const wrap = document.getElementById('home-next');
|
|
886
|
-
const list = document.getElementById('home-next-list');
|
|
887
|
-
const subhead = document.getElementById('home-recent-subh');
|
|
888
|
-
if (!wrap || !list) return;
|
|
889
|
-
try {
|
|
890
|
-
// ?limit=2 — this card is TWO lines. It used to download the entire claimable
|
|
891
|
-
// feed, every row with its full description, and `.slice(0, 2)` it: the same
|
|
892
|
-
// shape goal 1000072's criterion names, and the one goal-rollup.js was carved to
|
|
893
|
-
// fix on the Goals page (5,229,455 bytes to produce forty counters). The server
|
|
894
|
-
// already orders by priority then calibrated estimate, so the first two rows ARE
|
|
895
|
-
// the top two. `claimable_total` comes back so the foot can say how many are
|
|
896
|
-
// waiting instead of leaving two rows to imply there are only two.
|
|
897
|
-
// task 1004774: five rows, level with the five Recently shipped rows beside it.
|
|
898
|
-
const resp = await getJSON(`${API}/tasks/claimable?limit=5`, { softAuth: true });
|
|
899
|
-
// capped as well as asked for: a server that ignores ?limit must not grow the card.
|
|
900
|
-
const rows = resp && Array.isArray(resp.claimable) ? resp.claimable.slice(0, 5) : [];
|
|
901
|
-
if (!rows.length) { wrap.hidden = true; if (subhead) subhead.hidden = true; return; }
|
|
902
|
-
const total = Number(resp.claimable_total);
|
|
903
|
-
const cur = (window.__BRANDING__ && window.__BRANDING__.currency && window.__BRANDING__.currency.label) || 'credits';
|
|
904
|
-
list.innerHTML = rows.map((t) => {
|
|
905
|
-
const credits = window.OTBBranding?.taskCreditValue?.(t) ?? t.credits_reward;
|
|
906
|
-
const mins = t.est_minutes ? ` · ~${escapeHtml(String(t.est_minutes))}m` : '';
|
|
907
|
-
// task 1003459 — this row now claims directly (the extracted claim
|
|
908
|
-
// action, task 1003458) rather than only linking to the record and
|
|
909
|
-
// making the builder find the door there.
|
|
910
|
-
return `<li class="next-up__item">
|
|
911
|
-
<a class="next-up__title" href="/task/${encodeURIComponent(t.id)}">${escapeHtml(t.title || `task ${t.id}`)}</a>
|
|
912
|
-
<span class="next-up__meta">#${escapeHtml(String(t.id))}${mins} · ${fmtNum(credits)} ${escapeHtml(cur)} est.</span>
|
|
913
|
-
<button class="task__claim next-up__claim" type="button" data-task-id="${escapeHtml(String(t.id))}" aria-label="Claim task ${escapeHtml(String(t.id))}">Claim</button>
|
|
914
|
-
</li>`;
|
|
915
|
-
}).join('');
|
|
916
|
-
// Name the size of the queue these two came off. Silent when the count is
|
|
917
|
-
// missing or not bigger than what is shown — "See all 2" under two rows is
|
|
918
|
-
// noise, and a made-up number is worse than none.
|
|
919
|
-
const foot = document.getElementById('home-next-foot');
|
|
920
|
-
if (foot) {
|
|
921
|
-
foot.textContent = Number.isFinite(total) && total > rows.length
|
|
922
|
-
? `See all ${fmtNum(total)} ready work →`
|
|
923
|
-
: 'See all ready work →';
|
|
924
|
-
}
|
|
925
|
-
wrap.hidden = false;
|
|
926
|
-
if (subhead) subhead.hidden = false;
|
|
927
|
-
} catch (_) {
|
|
928
|
-
wrap.hidden = true;
|
|
929
|
-
if (subhead) subhead.hidden = true;
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
|
|
933
874
|
// ---- the Roll’s rows, for the Standing card -----------------------------
|
|
934
875
|
//
|
|
935
876
|
// task 1003441 moved the leaderboard TABLE to its own page (/roster,
|
|
@@ -1084,11 +1025,6 @@
|
|
|
1084
1025
|
// /me because it spans all builders' work), loaded fire-and-forget so a
|
|
1085
1026
|
// slow/failed feed can't hold up the rest of the hall.
|
|
1086
1027
|
loadAcclaimFeed();
|
|
1087
|
-
// task 2184: the Home "Next up" slice — top of the claimable queue, so the
|
|
1088
|
-
// page answers "what's my next action" without a trip to the Work board.
|
|
1089
|
-
// Fire-and-forget; hides itself when the feed is gated (primer 403) or empty.
|
|
1090
|
-
loadNextUp();
|
|
1091
|
-
|
|
1092
1028
|
// #408: build the sticky nav now that every section has settled its
|
|
1093
1029
|
// hidden state (welcome → karma → onboarding → security/roster).
|
|
1094
1030
|
// The renderNav call also wires the IntersectionObserver for active-
|
|
@@ -1108,21 +1044,15 @@
|
|
|
1108
1044
|
// down the page — log and show an inline placeholder.
|
|
1109
1045
|
// #726: the per-skill model picker (Voices) moved to /builders/settings,
|
|
1110
1046
|
// so the hall no longer fetches /me/skill-prefs here.
|
|
1111
|
-
const [achAll, ships, versionsResp, lb
|
|
1047
|
+
const [achAll, ships, versionsResp, lb] = await Promise.allSettled([
|
|
1112
1048
|
getJSON(`${API}/achievements`),
|
|
1113
1049
|
// task 1004854: Home's newest few + a count — never the whole history,
|
|
1114
1050
|
// which only the Accomplishments ledger fetches (hall-render.js loadShipLedger).
|
|
1115
1051
|
getJSON(`${API}/me/recent-ships?limit=${HOME_SHIPS_LIMIT}`),
|
|
1116
1052
|
getJSON(`${API}/versions`),
|
|
1117
1053
|
getJSON(`${API}/public/leaderboard`), // task 1004437: the list with each builder's roles
|
|
1118
|
-
getJSON(`${API}/public/progress`), // the Home version tab (task 1004774)
|
|
1119
1054
|
]);
|
|
1120
1055
|
|
|
1121
|
-
if (progress.status === 'fulfilled') {
|
|
1122
|
-
lastProgressState = progress.value;
|
|
1123
|
-
renderGreeting();
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
1056
|
if (achAll.status === 'fulfilled') {
|
|
1127
1057
|
// Show top 10 by sort_order — already ordered server-side. (Set is locked at 8
|
|
1128
1058
|
// for V2, so the slice is defensive.) Unlocked + locked-with-progress come
|
|
@@ -1227,11 +1157,6 @@
|
|
|
1227
1157
|
// task 1004833: the shared vote control reports each landed vote to the feed.
|
|
1228
1158
|
$('#acclaim-feed')?.addEventListener('otb-vote', onAcclaimVote);
|
|
1229
1159
|
|
|
1230
|
-
// task 1003459 — the Claim button on each Next-up row; the action + the
|
|
1231
|
-
// delegated listener live in hall-render.js (builders.js is at the
|
|
1232
|
-
// 1,500-line ratchet, hall-ui/CLAUDE.md's "growth goes in the lib/renderer").
|
|
1233
|
-
wireNextUpClaim();
|
|
1234
|
-
|
|
1235
1160
|
$('#f-ships-window')?.addEventListener('change', onShipsFilterChange);
|
|
1236
1161
|
$('#f-ships-version')?.addEventListener('change', onShipsFilterChange);
|
|
1237
1162
|
$('#f-ships-sort')?.addEventListener('change', renderRecentShips);
|
|
@@ -1346,14 +1271,13 @@
|
|
|
1346
1271
|
// reads what this page holds NOW rather than what it held at boot. Everything
|
|
1347
1272
|
// else is stable for the life of the page. This call sits AFTER every
|
|
1348
1273
|
// declaration it passes — moving it earlier is a temporal-dead-zone crash.
|
|
1349
|
-
const { acclaimEvidenceHtml, analyticsState, lbState, mountHallWidgets, renderCurveBody, renderGreeting, renderLanding, renderSessionRefused, renderNav, renderOnboarding, renderProfile, renderRecentShips, renderWelcomeCore, HOME_SHIPS_LIMIT, populateShipVersionFilter, setRecentShips, shipsPager, welcomeDismissalKey
|
|
1350
|
-
API, RANK_ORDER, api, brandWorld, curLabel, emptyRow, fmtDate, getJSON,
|
|
1274
|
+
const { acclaimEvidenceHtml, analyticsState, lbState, mountHallWidgets, renderCurveBody, renderGreeting, renderLanding, renderSessionRefused, renderNav, renderOnboarding, renderProfile, renderRecentShips, renderWelcomeCore, HOME_SHIPS_LIMIT, populateShipVersionFilter, setRecentShips, shipsPager, welcomeDismissalKey } = window.OTBHallRender({
|
|
1275
|
+
API, RANK_ORDER, api, brandWorld, curLabel, emptyRow, fmtDate, getJSON, mins, normalizeRank, pct, postJSON, rankLabel, rankWeight, signClasses, toast, usd,
|
|
1351
1276
|
get instanceRepo() { return instanceRepo; },
|
|
1352
1277
|
get lastCostState() { return lastCostState; },
|
|
1353
1278
|
get lastKarmaState() { return lastKarmaState; },
|
|
1354
1279
|
get lastMeState() { return lastMeState; },
|
|
1355
1280
|
get lastNeedsState() { return lastNeedsState; },
|
|
1356
|
-
get lastProgressState() { return lastProgressState; },
|
|
1357
1281
|
get lastRebaseState() { return lastRebaseState; },
|
|
1358
1282
|
get newcomerGated() { return newcomerGated; },
|
|
1359
1283
|
});
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
109
109
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab" defer></script>
|
|
110
110
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch" defer></script>
|
|
111
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
111
|
+
<script src="/builders/shell.js?v=2026-11-04-one-market-watch" defer></script>
|
|
112
112
|
<script src="/builders/palette.js" defer></script>
|
|
113
113
|
<script src="/builders/gov-living.js?v=2026-10-09-positions1" defer></script>
|
|
114
114
|
<script src="/builders/gov-feeds.js?v=2026-10-09-living3" defer></script>
|
|
@@ -162,7 +162,7 @@
|
|
|
162
162
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
163
163
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
164
164
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
165
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
165
|
+
<script src="/builders/shell.js?v=2026-11-04-one-market-watch"></script>
|
|
166
166
|
<script src="/builders/palette.js" defer></script>
|
|
167
167
|
<script src="/builders/collab-lib.js?v=2026-09-24-archive"></script>
|
|
168
168
|
<script src="/builders/collab.js?v=2026-09-24-archive"></script>
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
55
55
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
56
56
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
57
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
57
|
+
<script src="/builders/shell.js?v=2026-11-04-one-market-watch"></script>
|
|
58
58
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
59
59
|
<script src="/builders/palette.js" defer></script>
|
|
60
60
|
<script src="/builders/hall-kit.js"></script>
|
|
@@ -180,7 +180,7 @@
|
|
|
180
180
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
181
181
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
182
182
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
183
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
183
|
+
<script src="/builders/shell.js?v=2026-11-04-one-market-watch"></script>
|
|
184
184
|
<script src="/builders/palette.js" defer></script>
|
|
185
185
|
<script src="/builders/hall-kit.js"></script>
|
|
186
186
|
<script src="/builders/copy-desk.js?v=2026-09-01-redesign"></script>
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
artistry: ['studio', 'goals', 'thinking'],
|
|
108
108
|
ideating: ['goal-map', 'thinking', 'goals', 'market-watch'],
|
|
109
109
|
governing: ['city', 'blockers', 'board-room',
|
|
110
|
-
'watch', 'gate', 'sessions', 'project-settings', 'deploy', 'fleet', '
|
|
110
|
+
'watch', 'gate', 'sessions', 'project-settings', 'deploy', 'fleet', 'government'],
|
|
111
111
|
};
|
|
112
112
|
// A mode can lead with ONE tab and fold the rest of its own under a dropdown
|
|
113
113
|
// (owner, 2026-10-08: the city is Governing's tab, everything else under it).
|
|
@@ -81,7 +81,7 @@
|
|
|
81
81
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
82
82
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
83
83
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
84
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
84
|
+
<script src="/builders/shell.js?v=2026-11-04-one-market-watch"></script>
|
|
85
85
|
<script src="/builders/palette.js" defer></script>
|
|
86
86
|
<script src="/builders/hall-kit.js"></script>
|
|
87
87
|
<!-- the shared ask card the move is drawn with (task 1004829) -->
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
98
98
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
99
99
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
100
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
100
|
+
<script src="/builders/shell.js?v=2026-11-04-one-market-watch"></script>
|
|
101
101
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
102
102
|
<script src="/builders/palette.js" defer></script>
|
|
103
103
|
<script src="/builders/diagram-viewer.js?v=2026-06-14-diagviewer-1" defer></script>
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
98
98
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
99
99
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
100
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
100
|
+
<script src="/builders/shell.js?v=2026-11-04-one-market-watch"></script>
|
|
101
101
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
102
102
|
<script src="/builders/palette.js" defer></script>
|
|
103
103
|
<script src="/builders/md-reader.js?v=2026-09-02-mdreader-2"></script>
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
61
61
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
62
62
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
63
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
63
|
+
<script src="/builders/shell.js?v=2026-11-04-one-market-watch"></script>
|
|
64
64
|
<script src="/builders/palette.js" defer></script>
|
|
65
65
|
<script src="/builders/hall-kit.js"></script>
|
|
66
66
|
<script src="/builders/fleet-costs.js"></script>
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
68
68
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
69
69
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
70
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
70
|
+
<script src="/builders/shell.js?v=2026-11-04-one-market-watch"></script>
|
|
71
71
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
72
72
|
<script src="/builders/palette.js" defer></script>
|
|
73
73
|
<script src="/builders/hall-kit.js"></script>
|
|
@@ -140,7 +140,7 @@
|
|
|
140
140
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
141
141
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
142
142
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
143
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
143
|
+
<script src="/builders/shell.js?v=2026-11-04-one-market-watch"></script>
|
|
144
144
|
<script src="/builders/palette.js" defer></script>
|
|
145
145
|
|
|
146
146
|
<script src="/builders/idea-objects.js?v=2026-09-27-split"></script>
|
|
@@ -244,7 +244,7 @@
|
|
|
244
244
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
245
245
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
246
246
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
247
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
247
|
+
<script src="/builders/shell.js?v=2026-11-04-one-market-watch"></script>
|
|
248
248
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
249
249
|
<script src="/builders/palette.js" defer></script>
|
|
250
250
|
<!-- task 1004727: the ONE karma vote control every task/idea list renders -->
|
|
@@ -157,7 +157,7 @@
|
|
|
157
157
|
<script src="/builders/dom-utils.js?v=2026-10-25-escape"></script>
|
|
158
158
|
<script src="/builders/shell-trail.js?v=2026-10-08-city-tab"></script>
|
|
159
159
|
<script src="/builders/craft-mode.js?v=2026-10-30-market-watch"></script>
|
|
160
|
-
<script src="/builders/shell.js?v=2026-11-
|
|
160
|
+
<script src="/builders/shell.js?v=2026-11-04-one-market-watch"></script>
|
|
161
161
|
<!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
|
|
162
162
|
<script src="/builders/palette.js" defer></script>
|
|
163
163
|
<script src="/builders/hall-kit.js"></script>
|