@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.
Files changed (113) hide show
  1. package/.bongos-core.json +198 -108
  2. package/clients/bongos-client/README.md +1 -1
  3. package/clients/bongos-client/bongos-client.global.js +12 -0
  4. package/clients/bongos-client/index.cjs +12 -0
  5. package/clients/bongos-client/index.d.ts +12 -0
  6. package/clients/bongos-client/index.mjs +12 -0
  7. package/docs/api/openapi.json +174 -1
  8. package/docs/api-reference.md +12 -2
  9. package/docs/copy-inventory.md +226 -224
  10. package/docs/copy-registry.json +583 -549
  11. package/docs/module-api-changelog.md +2 -0
  12. package/docs/page-inventory.json +18 -37
  13. package/docs/page-readings.json +306 -361
  14. package/modules/copy-desk/module.json +1 -1
  15. package/modules/copy-desk/routes/copy-desk-studio.js +103 -0
  16. package/modules/copy-desk/tests/copy_no_cms.mjs +2 -2
  17. package/modules/government/db.js +29 -0
  18. package/modules/government/docket.js +61 -4
  19. package/modules/government/routes/government.js +2 -0
  20. package/modules/hall-ui/public/ai-studio.html +1 -1
  21. package/modules/hall-ui/public/atlas.html +1 -1
  22. package/modules/hall-ui/public/blockers.html +1 -1
  23. package/modules/hall-ui/public/board-room.html +1 -1
  24. package/modules/hall-ui/public/builders.js +3 -79
  25. package/modules/hall-ui/public/city.html +1 -1
  26. package/modules/hall-ui/public/collab.html +1 -1
  27. package/modules/hall-ui/public/commands.html +1 -1
  28. package/modules/hall-ui/public/copy-desk.html +1 -1
  29. package/modules/hall-ui/public/craft-mode.js +1 -1
  30. package/modules/hall-ui/public/deploy.html +1 -1
  31. package/modules/hall-ui/public/diagrams.html +1 -1
  32. package/modules/hall-ui/public/drachmae.html +1 -1
  33. package/modules/hall-ui/public/fleet.html +1 -1
  34. package/modules/hall-ui/public/gate.html +1 -1
  35. package/modules/hall-ui/public/goal-map.html +1 -1
  36. package/modules/hall-ui/public/goals.html +1 -1
  37. package/modules/hall-ui/public/government.html +1 -1
  38. package/modules/hall-ui/public/hall-render.js +68 -167
  39. package/modules/hall-ui/public/hall-widgets.js +4 -45
  40. package/modules/hall-ui/public/home-city-draw.js +583 -0
  41. package/modules/hall-ui/public/home-city.css +102 -0
  42. package/modules/hall-ui/public/home-city.js +339 -0
  43. package/modules/hall-ui/public/home-gallery.css +129 -0
  44. package/modules/hall-ui/public/home-gallery.js +349 -0
  45. package/modules/hall-ui/public/home-studio.css +72 -0
  46. package/modules/hall-ui/public/home-studio.js +203 -0
  47. package/modules/hall-ui/public/idea.html +1 -1
  48. package/modules/hall-ui/public/ideas.html +1 -1
  49. package/modules/hall-ui/public/index.html +9 -7
  50. package/modules/hall-ui/public/index.states.json +73 -8
  51. package/modules/hall-ui/public/market-watch-item.html +1 -1
  52. package/modules/hall-ui/public/market-watch-rules.html +1 -1
  53. package/modules/hall-ui/public/market-watch-settings.js +6 -1
  54. package/modules/hall-ui/public/market-watch.html +141 -54
  55. package/modules/hall-ui/public/market-watch.js +54 -0
  56. package/modules/hall-ui/public/market-watch.states.json +2 -1
  57. package/modules/hall-ui/public/modules.html +1 -1
  58. package/modules/hall-ui/public/my-agents.html +1 -1
  59. package/modules/hall-ui/public/primer.html +1 -1
  60. package/modules/hall-ui/public/profile.html +1 -1
  61. package/modules/hall-ui/public/project-settings.html +1 -1
  62. package/modules/hall-ui/public/ranks.html +1 -1
  63. package/modules/hall-ui/public/roadmap.html +1 -1
  64. package/modules/hall-ui/public/roster.html +1 -1
  65. package/modules/hall-ui/public/sessions.html +1 -1
  66. package/modules/hall-ui/public/settings.html +1 -1
  67. package/modules/hall-ui/public/shell.js +2 -3
  68. package/modules/hall-ui/public/street.html +1 -1
  69. package/modules/hall-ui/public/studio.html +9 -1
  70. package/modules/hall-ui/public/style.css +74 -27
  71. package/modules/hall-ui/public/task.html +1 -1
  72. package/modules/hall-ui/public/thinking.html +1 -1
  73. package/modules/hall-ui/public/tweak-editor.html +1 -1
  74. package/modules/hall-ui/public/watch.html +1 -1
  75. package/modules/hall-ui/public/work.html +1 -1
  76. package/modules/hall-ui/records/home-city.md +189 -0
  77. package/modules/hall-ui/records/loader.md +1 -1
  78. package/modules/hall-ui/records/market-watch-settings.md +16 -3
  79. package/modules/hall-ui/records/market-watch.md +3 -1
  80. package/modules/lifecycle/home-city.js +388 -0
  81. package/modules/lifecycle/home-gallery.js +320 -0
  82. package/modules/lifecycle/migrations/lifecycle_020_work_appreciations.sql +36 -0
  83. package/modules/lifecycle/module.json +2 -0
  84. package/modules/lifecycle/routes/home-city.js +29 -0
  85. package/modules/lifecycle/routes/home-gallery.js +37 -0
  86. package/modules/market-watch/routes/settings.js +1 -1
  87. package/package-lock.json +2 -2
  88. package/package.json +1 -1
  89. package/release-notes.json +38 -0
  90. package/scripts/bongos/leak-scan-allowlist.js +8 -0
  91. package/scripts/hall-preview/README.md +1 -0
  92. package/scripts/hall-preview/server.js +10 -0
  93. package/src/bongos/serve-internal.js +17 -3
  94. package/src/module-api.js +1 -1
  95. package/tests/claim_from_task_and_home.mjs +8 -43
  96. package/tests/hall_board_world.mjs +10 -12
  97. package/tests/hall_builders_page_pass.mjs +18 -27
  98. package/tests/hall_home_ships_bounded.mjs +4 -3
  99. package/tests/hall_loading.mjs +1 -1
  100. package/tests/hall_nav.mjs +11 -4
  101. package/tests/hall_page_gate_map.mjs +7 -4
  102. package/tests/home_bounded_reads.mjs +16 -31
  103. package/tests/home_city.mjs +580 -0
  104. package/tests/home_city_widget.mjs +260 -0
  105. package/tests/home_gallery.mjs +255 -0
  106. package/tests/home_gallery_widget.mjs +220 -0
  107. package/tests/home_studio.mjs +169 -0
  108. package/tests/home_studio_widget.mjs +157 -0
  109. package/tests/leaderboard_roles.mjs +3 -2
  110. package/tests/market_watch_pages.mjs +162 -0
  111. package/tests/nav_permission_atoms.mjs +2 -2
  112. package/modules/hall-ui/public/market-watch-settings.html +0 -115
  113. package/modules/hall-ui/public/market-watch-settings.states.json +0 -14
@@ -22,7 +22,7 @@
22
22
  'use strict';
23
23
 
24
24
  window.OTBHallRender = function createHallRender(ctx) {
25
- const { API, RANK_ORDER, api, brandWorld, curLabel, emptyRow, fmtDate, getJSON, loadNextUp, mins, normalizeRank, pct, postJSON, rankLabel, rankWeight, signClasses, toast, usd } = ctx;
25
+ const { API, RANK_ORDER, api, brandWorld, curLabel, emptyRow, fmtDate, getJSON, mins, normalizeRank, pct, postJSON, rankLabel, rankWeight, signClasses, toast, usd } = ctx;
26
26
  const { escapeHtml, $, fmtNum, moduleOn } = window.OTB;
27
27
 
28
28
  const RANK_UNLOCKS = {
@@ -273,69 +273,40 @@
273
273
  return v === 'open' || v === 'invite_only' ? v : 'apply';
274
274
  }
275
275
 
276
- // One landing per door: open → sign in and you're in; invite_only → nothing to
277
- // apply for, sign in once invited; apply → the request form.
276
+ // The signed-out page is one centred card (task: sign-in redesign): the
277
+ // sign-in button leads in every mode, and the door below it is the only part
278
+ // that depends on joinability — open → nothing to do, invite_only → nothing
279
+ // to apply for, apply → the request form, folded behind a disclosure so the
280
+ // card stays a sign-in screen rather than a form.
278
281
  function landingDoorHtml(mode) {
279
- const hub = `This is the builders' hub for ${brandWorld()} — where builders claim work, earn ${curLabel()}, and see what they have accomplished.`;
280
282
  const admitter = escapeHtml(rankLabel('archon'));
281
- const signIn = `<p class="landing__cta"><a class="landing__btn" href="${API}/auth/web/start">Sign in with GitHub</a></p>`;
282
283
  if (mode === 'open') {
283
284
  return `
284
- <p class="landing__lede">${hub} Anyone with a GitHub account can join: sign in and you're in, starting at the newcomer rank.</p>
285
- <div class="landing__request" id="landing-request">
286
- <h3 class="landing__request-h">New here — sign in to join</h3>
287
- <p class="landing__request-lede">No request and no invite needed. Sign in with GitHub, and a step-by-step guide takes you the rest of the way.</p>
288
- ${signIn}
289
- </div>`;
285
+ <p class="landing__door" id="landing-request">New here? Signing in creates your account.</p>`;
290
286
  }
291
287
  if (mode === 'invite_only') {
292
288
  return `
293
- <p class="landing__lede">${hub} This project is invite-only: the ${admitter} invites each builder, and there is nothing to apply for here.</p>
294
- <div class="landing__request" id="landing-request">
295
- <h3 class="landing__request-h">Invited? Sign in</h3>
296
- <p class="landing__request-lede">If the ${admitter} has invited you, sign in with GitHub and you're in. If not, ask them for an invite — this project doesn't take requests.</p>
297
- ${signIn}
298
- </div>`;
289
+ <p class="landing__door" id="landing-request">Invite-only — there is nothing to apply for here.</p>`;
299
290
  }
300
291
  return `
301
- <p class="landing__lede">
302
- ${hub} Access is
303
- invite-based: an Archon approves each new builder. To begin, request
304
- access below.
305
- </p>
306
-
307
- <div class="landing__request" id="landing-request">
308
- <h3 class="landing__request-h">New here — request access</h3>
309
- <p class="landing__request-lede">
310
- Leave your GitHub username and an Archon will let you in. No coding
311
- needed to start — once you're approved, sign in with GitHub and a
312
- step-by-step guide takes you the rest of the way.
313
- </p>
314
- <form class="landing__form" id="access-request-form" novalidate>
315
- <label class="landing__field">
316
- <span>GitHub username <span class="landing__req-mark" aria-hidden="true">*</span></span>
317
- <input type="text" id="ar-login" name="github_login" autocomplete="username"
318
- placeholder="e.g. octocat" maxlength="40" required>
319
- </label>
320
- <label class="landing__field">
321
- <span>Your name <span class="landing__opt">(optional)</span></span>
322
- <input type="text" id="ar-name" name="display_name" autocomplete="name"
323
- placeholder="So the Archon knows who you are" maxlength="100">
324
- </label>
325
- <label class="landing__field">
326
- <span>A word on why <span class="landing__opt">(optional)</span></span>
327
- <input type="text" id="ar-note" name="note"
328
- placeholder="How you got here, what you'd build" maxlength="500">
329
- </label>
330
- <p class="landing__form-hint">
331
- No GitHub account yet? Make a free one at
332
- <a href="https://github.com/signup" target="_blank" rel="noopener noreferrer">github.com</a>
333
- first — your username is all we need.
334
- </p>
335
- <button type="submit" class="landing__btn landing__btn--request" id="ar-submit">Request access</button>
336
- <p class="landing__request-status" id="ar-status" aria-live="polite"></p>
337
- </form>
338
- </div>`;
292
+ <details class="landing__disclosure">
293
+ <summary class="landing__summary">New here? Request access</summary>
294
+ <div class="landing__request" id="landing-request">
295
+ <form class="landing__form" id="access-request-form" novalidate>
296
+ <label class="landing__field">
297
+ <span>GitHub username</span>
298
+ <input type="text" id="ar-login" name="github_login" autocomplete="username"
299
+ placeholder="e.g. octocat" maxlength="40" required>
300
+ </label>
301
+ <label class="landing__field">
302
+ <span>Why you'd like in <span class="landing__opt">(optional)</span></span>
303
+ <input type="text" id="ar-note" name="note" maxlength="500">
304
+ </label>
305
+ <button type="submit" class="landing__btn landing__btn--quiet landing__btn--block" id="ar-submit">Request access</button>
306
+ <p class="landing__request-status" id="ar-status" aria-live="polite"></p>
307
+ </form>
308
+ </div>
309
+ </details>`;
339
310
  }
340
311
 
341
312
  // task 1002835: a karma vote on a peer's ship was a vote on a title alone.
@@ -360,28 +331,39 @@
360
331
  }
361
332
  return `<span class="acclaim__evidence">${parts.join(' · ')}</span>`;
362
333
  }
363
- // The RETURNING builder's door (task 1002851). `open` and `invite_only`
364
- // already render a real .landing__btn sign-in inside the primary door, so a
365
- // second button there would be a third scattered path rather than a clearer
366
- // one — those modes keep the prose note alone. Only the `apply` door, whose
367
- // primary control is the request FORM, left signing in as a footnote link.
368
- // It gets a real button: the request button's geometry so it reads as a
369
- // control, a quieter fill so the request path still leads for the strangers
370
- // who are most of this page's traffic.
371
- function returningDoorHtml(mode) {
372
- if (mode !== 'apply') return '';
334
+ // The sign-in itself — the card's one primary control, in every mode, so a
335
+ // returning builder never hunts for it below a form (task 1002851's intent).
336
+ function returningDoorHtml() {
373
337
  return `
374
- <div class="landing__returning" id="landing-returning">
375
- <h3 class="landing__request-h">Already a member?</h3>
376
- <p class="landing__request-lede">You've been approved before — sign in and pick up where you left off.</p>
377
- <p class="landing__cta"><a class="landing__btn landing__btn--quiet" href="${API}/auth/web/start">Sign in with GitHub</a></p>
378
- </div>`;
338
+ <a class="landing__btn landing__btn--block" id="landing-signin" href="${API}/auth/web/start">
339
+ <svg class="landing__gh" viewBox="0 0 16 16" width="18" height="18" aria-hidden="true"><path fill="currentColor" d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>
340
+ Sign in with GitHub
341
+ </a>`;
342
+ }
343
+
344
+ // The project's mark, as the sidebar draws it: the pack's own logo when it has
345
+ // one (theme.logo, ADR 0358), otherwise the Cloud Bongos ring-and-moon.
346
+ function landingLogoHtml() {
347
+ const b = window.__BRANDING__ || {};
348
+ const logo = b.theme && b.theme.logo;
349
+ return logo
350
+ ? `<img class="landing__logo" src="${escapeHtml(logo)}" alt="" width="56" height="56" decoding="async">`
351
+ : '<svg class="landing__logo" viewBox="0 0 200 200" aria-hidden="true"><path class="landing__ring" d="M137 31 A78 78 0 1 0 170 66" fill="none" stroke-width="15" stroke-linecap="round"/><circle class="landing__planet" cx="100" cy="100" r="55"/><circle cx="156" cy="46" r="12" fill="#fb9569"/></svg>';
352
+ }
353
+
354
+ // Strip the hall's chrome (rail, top bar, foot) for the signed-out card —
355
+ // html[data-door="out"], styled in style.css. The test vm has no
356
+ // documentElement, hence the guard.
357
+ function markSignedOut() {
358
+ const root = document.documentElement;
359
+ if (root && root.dataset) root.dataset.door = 'out';
379
360
  }
380
361
 
381
362
  function renderLanding() {
382
- // task 1723: collapse the shell nav to the signed-out minimum (Home + a
383
- // Sign-in button). Cosmetic — the server gates every page regardless.
363
+ // task 1723: collapse the shell nav to the signed-out minimum. Cosmetic —
364
+ // the server gates every page regardless.
384
365
  window.OTBShell?.applyAccess({ authed: false });
366
+ markSignedOut();
385
367
 
386
368
  const welcome = $('#welcome-line');
387
369
  if (welcome) welcome.textContent = '';
@@ -390,20 +372,12 @@
390
372
  if (landing) {
391
373
  landing.hidden = false;
392
374
  const mode = joinabilityMode();
393
- // On the apply door the sign-in is a real button above, so the note keeps
394
- // only the privacy sentence — one sign-in path per landing, never two.
395
- const signInNote = mode === 'apply'
396
- ? ''
397
- : `Already a member? <a href="${API}/auth/web/start">Sign in with GitHub</a>. `;
398
375
  landing.innerHTML = `
399
- <div class="scroll landing">
400
- <h2 class="scroll__h">Welcome</h2>
401
- ${landingDoorHtml(mode)}
376
+ <div class="landing">
377
+ ${landingLogoHtml()}
378
+ <h2 class="landing__title">${brandWorld()}</h2>
402
379
  ${returningDoorHtml(mode)}
403
-
404
- <p class="landing__signin-note">
405
- ${signInNote}We store only your GitHub login, avatar, and the work you ship.
406
- </p>
380
+ ${landingDoorHtml(mode)}
407
381
  </div>
408
382
  `;
409
383
  wireAccessRequestForm();
@@ -419,6 +393,7 @@
419
393
  // deleted. Say so, and offer the way out: sign out, then sign in again.
420
394
  function renderSessionRefused(code) {
421
395
  window.OTBShell?.applyAccess({ authed: false });
396
+ markSignedOut();
422
397
  const landing = $('#landing-root');
423
398
  if (!landing) return;
424
399
  landing.hidden = false;
@@ -427,11 +402,12 @@
427
402
  ? `This account has been switched off, so it cannot open the hall. If that is a mistake, ask the ${admitter} to switch it back on.`
428
403
  : 'This sign-in is no longer accepted, so the hall cannot open with it.';
429
404
  landing.innerHTML = `
430
- <div class="scroll landing">
431
- <h2 class="scroll__h">You can't get in with this account</h2>
405
+ <div class="landing">
406
+ ${landingLogoHtml()}
407
+ <h2 class="landing__title">You can't get in with this account</h2>
432
408
  <p class="landing__request-lede">${why}</p>
433
409
  <p class="landing__request-lede">Sign out, then sign in with the GitHub account you use now. A new account needs the ${admitter}'s approval first.</p>
434
- <p class="landing__cta"><button type="button" class="landing__btn" id="refused-signout">Sign out</button></p>
410
+ <button type="button" class="landing__btn landing__btn--block" id="refused-signout">Sign out</button>
435
411
  </div>
436
412
  `;
437
413
  $('#refused-signout')?.addEventListener('click', () => {
@@ -603,14 +579,6 @@
603
579
  const el = document.getElementById(id);
604
580
  if (el) el.textContent = text;
605
581
  };
606
- setText('stat-credits', fmtNum(builder.total_credits));
607
- setText('stat-karma', fmtNum(karmaTotal));
608
- setText('stat-karma-sub', karmaTrendText);
609
- const statRank = document.getElementById('stat-rank');
610
- if (statRank) statRank.innerHTML = builder.rank ? renderRankBadge(normalizeRank(builder.rank), { tip: false }) : '—';
611
- setText('stat-rank-sub', rollPlace ? [`#${rollPlace.place} on the leaderboard`, rolePlaceText(rollPlace)].filter(Boolean).join(' · ') : '');
612
- const statsSection = document.getElementById('home-stats');
613
- if (statsSection && !ctx.newcomerGated) statsSection.hidden = false;
614
582
 
615
583
  // task 1003464: the Home card as the mock draws it — an identity LINE and a
616
584
  // three-row fact ledger. The mock repeats Credits and the place even though
@@ -635,7 +603,7 @@
635
603
  <dl class="home-facts home-facts--grid">
636
604
  <div><dt>${escapeHtml(curLabel(true))}</dt><dd>${fmtNum(builder.total_credits)}</dd></div>
637
605
  <div><dt>Karma</dt><dd>${fmtNum(karmaTotal)}</dd></div>
638
- <div><dt>Shipped</dt><dd id="standing-shipped">${escapeHtml((document.getElementById('stat-shipped') || {}).textContent || '—')}</dd></div>
606
+ <div><dt>Shipped</dt><dd id="standing-shipped">${shipState.seen ? fmtNum(shipState.total) : '—'}</dd></div>
639
607
  <div><dt>Leaderboard place</dt><dd>${place}</dd></div>
640
608
  </dl>
641
609
  <dl class="home-facts">
@@ -716,39 +684,15 @@
716
684
  }
717
685
 
718
686
  // task 1003464: the greeting IS Home's one h1 (the mock opens with it).
719
- // task 1004780: it has no sub-line any more. The version tab under the banner
720
- // shows how far the version is and Next up shows the ready work, so a sentence
721
- // repeating both was redundant (owner, 2026-10-23).
687
+ // task 1004780: it has no sub-line any more (owner, 2026-10-23); since task
688
+ // 1005054 the city under it (home-city.js) is how far the project has come.
722
689
  const greetingWord = (hour) => (hour < 12 ? 'Good morning' : (hour < 18 ? 'Good afternoon' : 'Good evening'));
723
- // The version the tab is about: the one being built with the most work in it.
724
- function currentVersion(progress) {
725
- const rows = progress && Array.isArray(progress.progress) ? progress.progress : [];
726
- return rows.filter((r) => r && r.status === 'building')
727
- .sort((a, b) => (Number(b.total_count) || 0) - (Number(a.total_count) || 0))[0] || null;
728
- }
729
- // task 1004774: the version tab under the Home banner, drawn as a bar.
730
- // Hidden while no version is being built.
731
- function renderVersionTab(v) {
732
- const tab = document.getElementById('home-version');
733
- if (!tab) return;
734
- if (!v) { tab.hidden = true; return; }
735
- const pct = Math.max(0, Math.min(100, Math.round(Number(v.percent_complete) || 0)));
736
- const name = document.getElementById('home-version-name');
737
- const fill = document.getElementById('home-version-fill');
738
- const pctEl = document.getElementById('home-version-pct');
739
- if (name) name.textContent = v.version_id;
740
- if (fill) fill.style.width = `${pct}%`;
741
- if (pctEl) pctEl.textContent = `${pct}% complete`;
742
- tab.setAttribute('aria-label', `${v.version_id} is ${pct} percent complete`);
743
- tab.hidden = false;
744
- }
745
690
  function renderGreeting() {
746
691
  const h1 = document.getElementById('hall-h1');
747
692
  const builder = ctx.lastMeState && ctx.lastMeState.builder;
748
693
  if (h1 && builder) {
749
694
  h1.textContent = `${greetingWord(new Date().getHours())}, ${builder.display_name || builder.github_login || 'builder'}`;
750
695
  }
751
- renderVersionTab(currentVersion(ctx.lastProgressState));
752
696
  }
753
697
  const ONBOARDING_STAGE_ORDER = [
754
698
  'auth_complete',
@@ -1096,28 +1040,9 @@
1096
1040
  const countEl = $('#ships-count');
1097
1041
  const pagerEl = $('#ships-pager');
1098
1042
 
1099
- // task 1723: Home mirrors — the shipped-count stat tile and the compact
1100
- // 5-row recent-work table are fed from the same feed. Guarded writes.
1101
- const statShipped = document.getElementById('stat-shipped');
1102
- if (statShipped) statShipped.textContent = fmtNum(shipState.total);
1103
- // task 1004774: the Standing hover card carries the same count.
1043
+ // task 1004774: the Standing hover card carries the shipped count.
1104
1044
  const standingShipped = document.getElementById('standing-shipped');
1105
1045
  if (standingShipped) standingShipped.textContent = fmtNum(shipState.total);
1106
- const homeRecent = document.querySelector('#home-recent-table tbody');
1107
- if (homeRecent) {
1108
- const latest = sortShips(shipState.recent, 'recent').slice(0, HOME_SHIPS_LIMIT);
1109
- homeRecent.innerHTML = latest.length
1110
- ? latest.map((s) => `
1111
- <tr>
1112
- <td class="ledger__num">#${s.id}</td>
1113
- <td>${escapeHtml(s.title)}</td>
1114
- <td class="ledger__drachmae">${fmtNum(window.OTBBranding?.taskCreditValue?.(s) ?? s.credits_reward)}</td>
1115
- <td class="ledger__date">${fmtDate(s.shipped_at)}</td>
1116
- </tr>`).join('')
1117
- : emptyRow(4, 'Nothing shipped yet. Claim a task on the Work board to get started.');
1118
- }
1119
- const homeRecentSection = document.getElementById('home-recent');
1120
- if (homeRecentSection && !ctx.newcomerGated) homeRecentSection.hidden = false;
1121
1046
 
1122
1047
  // The ledger waits for its own fetch (loadShipLedger); until then it keeps its placeholder.
1123
1048
  const all = shipState.all;
@@ -1378,31 +1303,7 @@
1378
1303
  return `#${p.role.place} as ${ROLE_AS[p.role.craft]}`;
1379
1304
  }
1380
1305
 
1381
- // task 1003459 — the Claim button on each Home "Next up" row, through the
1382
- // hall's ONE claim action (task 1003458) rather than a third hand-rolled
1383
- // POST /claims. Lives here, not builders.js, because that file is at the
1384
- // 1,500-line ratchet (hall-ui/CLAUDE.md: "growth in the page half has to be
1385
- // paid for; new pure helper → the lib/renderer"). refresh re-fetches the
1386
- // two-row slice so a claimed task drops out of it immediately.
1387
- function wireNextUpClaim() {
1388
- const claimAction = window.OTBClaimAction.makeClaimAction({ postJSON, toast, API, refresh: loadNextUp });
1389
- $('#home-next-list')?.addEventListener('click', (e) => {
1390
- const btn = e.target.closest('.next-up__claim');
1391
- if (!btn || btn.disabled) return;
1392
- const taskId = btn.dataset.taskId;
1393
- btn.disabled = true;
1394
- btn.textContent = '…';
1395
- claimAction.claim(taskId).then((out) => {
1396
- // A redirect to sign-in is already navigating away; re-enabling here
1397
- // would just invite a click that can't land.
1398
- if (!out.ok && !out.redirected) {
1399
- btn.disabled = false;
1400
- btn.textContent = 'Claim';
1401
- }
1402
- });
1403
- });
1404
- }
1405
1306
 
1406
- return { acclaimEvidenceHtml, analyticsState, lbState, mountHallWidgets, renderCurveBody, renderGreeting, renderLanding, renderSessionRefused, renderNav, renderOnboarding, renderProfile, renderRecentShips, renderWelcomeCore, HOME_SHIPS_LIMIT, loadShipLedger, populateShipVersionFilter, setRecentShips, shipState, shipsPager, welcomeDismissalKey, wireNextUpClaim };
1307
+ return { acclaimEvidenceHtml, analyticsState, lbState, mountHallWidgets, renderCurveBody, renderGreeting, renderLanding, renderSessionRefused, renderNav, renderOnboarding, renderProfile, renderRecentShips, renderWelcomeCore, HOME_SHIPS_LIMIT, loadShipLedger, populateShipVersionFilter, setRecentShips, shipState, shipsPager, welcomeDismissalKey };
1407
1308
  };
1408
1309
  })();
@@ -62,29 +62,8 @@
62
62
  '<p class="ledger__note" id="onboarding-status" hidden></p>',
63
63
  { view: 'home', zone: 'home-banner', labelledby: 'onboarding-h', hiddenByDefault: true });
64
64
 
65
- // 0c. Quick stats — headline numbers for the Home dashboard (task 1723).
66
- // Filled by builders.js: credits + karma + rank from /me (renderProfile),
67
- // shipped count once the ships feed settles (renderRecentShips).
68
- // task 2169: each tile is a LINK — the stat row doubles as the fastest
69
- // navigation on the page (Credits → the economy doc, Shipped/Karma → the
70
- // Standing sections that explain them, Rank → the ladder).
71
- w('home-stats', 'Overview', 15,
72
- '<h2 id="home-stats-h" class="sr-only">Overview</h2>' +
73
- '<div class="stat-row" id="home-stats-row">' +
74
- '<a class="stat-tile" href="/drachmae"><span class="stat-tile__label" data-currency-label="caps">Credits</span><span class="stat-tile__num" id="stat-credits">—</span></a>' +
75
- '<a class="stat-tile" href="#accomplishments-work"><span class="stat-tile__label">Shipped</span><span class="stat-tile__num" id="stat-shipped">—</span><span class="stat-tile__sub" id="stat-shipped-sub"></span></a>' +
76
- '<a class="stat-tile" href="#accomplishments-karma"><span class="stat-tile__label">Karma</span><span class="stat-tile__num" id="stat-karma">—</span><span class="stat-tile__sub" id="stat-karma-sub"></span></a>' +
77
- '<a class="stat-tile" href="/ranks"><span class="stat-tile__label">Rank</span><span class="stat-tile__num stat-tile__num--badge" id="stat-rank">—</span><span class="stat-tile__sub" id="stat-rank-sub"></span></a>' +
78
- '</div>' +
79
- // task 1004774: the version tab under the banner — how far the version being
80
- // built has come. hall-render.js renderGreeting fills it from the same
81
- // progress read as the greeting's sub-line; hidden until there is a version.
82
- '<a class="version-tab" id="home-version" href="/roadmap" hidden>' +
83
- '<span class="version-tab__name" id="home-version-name"></span>' +
84
- '<span class="version-tab__bar" aria-hidden="true"><span class="version-tab__fill" id="home-version-fill"></span></span>' +
85
- '<span class="version-tab__pct" id="home-version-pct"></span>' +
86
- '</a>',
87
- { view: 'home', zone: 'home-stats', sectionClass: 'scroll scroll--bare', labelledby: 'home-stats-h', hiddenByDefault: true });
65
+ // (task 1005054: the quick-stats strip and the version tab left Home. The
66
+ // zone they held, home-stats, is the project city's — home-city.js.)
88
67
 
89
68
  // (task 1004774: the Standing summary is no longer a Home card. It is the
90
69
  // chip at the top right of the page head — index.html #hall-standing — whose
@@ -97,28 +76,8 @@
97
76
  // goal-inbox.js owns (#attention-ideas), the single landing place for every
98
77
  // actionable item. builders.js still gates the /inbox fetch to Metic+.)
99
78
 
100
- // 0e. Your work — "what's my next action" + the compact recent table
101
- // (task 1723; grown by task 2184). The Next-up slice surfaces the top of the
102
- // claimable queue (builders.js loadNextUp fills it; hidden when the feed is
103
- // gated or empty), then the 5-row recent-ships table below it.
104
- w('home-recent', 'Your work', 28,
105
- '<h2 id="home-recent-h" class="scroll__h">Your work</h2>' +
106
- // task 1004774: Next up and Recently shipped sit side by side (stacking
107
- // again on a phone); when Next up is hidden, shipped takes the full width.
108
- '<div class="home-split">' +
109
- '<div class="home-split__col" id="home-next" hidden>' +
110
- '<h3 class="ginbox__sec-h">Next up — ready to claim</h3>' +
111
- '<ul class="next-up" id="home-next-list"></ul>' +
112
- '<p class="next-up__foot"><a href="/work" id="home-next-foot">See all ready work →</a></p>' +
113
- '</div>' +
114
- '<div class="home-split__col">' +
115
- '<h3 class="ginbox__sec-h" id="home-recent-subh" hidden>Recently shipped</h3>' +
116
- '<table class="ledger" id="home-recent-table"><thead><tr><th>№</th><th>Work</th><th data-currency-label="caps">Credits</th><th>Shipped</th></tr></thead>' +
117
- '<tbody><tr><td colspan="4" class="ledger__empty"><span class="hall-loading" role="status">Loading…</span></td></tr></tbody></table>' +
118
- '<p class="card-foot"><a href="#accomplishments-work">All your work →</a></p>' +
119
- '</div>' +
120
- '</div>',
121
- { view: 'home', zone: 'home-main', labelledby: 'home-recent-h', hiddenByDefault: true });
79
+ // (task 1005054: Your work — Next up and Recently shipped — left Home; ready
80
+ // work is the Work board's and your history is Accomplishments'.)
122
81
 
123
82
  // 0f. The founding wall (task 1004424) — everyone who joined while the project was
124
83
  // being founded, founder first, each with their mark. Hidden until the founding