@bongos/core 1.21.64 → 1.21.66

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.
@@ -3,17 +3,17 @@
3
3
  "generator": "scripts/gds/copy-inventory.js",
4
4
  "goal": 1000074,
5
5
  "counts": {
6
- "strings": 2557,
7
- "unique_ids": 2184,
8
- "certain": 2557,
6
+ "strings": 2556,
7
+ "unique_ids": 2183,
8
+ "certain": 2556,
9
9
  "heuristic": 0,
10
10
  "duplicate_ids": 227
11
11
  },
12
12
  "surfaces": {
13
13
  "builders-hall": {
14
14
  "at": "modules/hall-ui/public",
15
- "files": 134,
16
- "strings": 1829
15
+ "files": 133,
16
+ "strings": 1828
17
17
  },
18
18
  "status-dashboard": {
19
19
  "at": "modules/status-ui/public",
@@ -669,7 +669,7 @@
669
669
  "at": [
670
670
  "modules/hall-ui/public/profile-charts.js:605",
671
671
  "modules/hall-ui/public/profile.js:841",
672
- "modules/hall-ui/public/profile.js:982"
672
+ "modules/hall-ui/public/profile.js:977"
673
673
  ]
674
674
  },
675
675
  {
@@ -2659,7 +2659,7 @@
2659
2659
  "surface": "builders-hall",
2660
2660
  "text": ", or",
2661
2661
  "file": "modules/hall-ui/public/profile.js",
2662
- "line": 982,
2662
+ "line": 977,
2663
2663
  "origin": "js-markup",
2664
2664
  "confidence": "certain"
2665
2665
  },
@@ -3037,7 +3037,7 @@
3037
3037
  "surface": "builders-hall",
3038
3038
  "text": "Activity, by role",
3039
3039
  "file": "modules/hall-ui/public/profile.html",
3040
- "line": 137,
3040
+ "line": 138,
3041
3041
  "origin": "html-text",
3042
3042
  "confidence": "certain"
3043
3043
  },
@@ -3883,7 +3883,7 @@
3883
3883
  "surface": "builders-hall",
3884
3884
  "text": "By the numbers",
3885
3885
  "file": "modules/hall-ui/public/profile.html",
3886
- "line": 64,
3886
+ "line": 65,
3887
3887
  "origin": "html-text",
3888
3888
  "confidence": "certain"
3889
3889
  },
@@ -4900,7 +4900,7 @@
4900
4900
  "surface": "builders-hall",
4901
4901
  "text": "Could not load the profile.",
4902
4902
  "file": "modules/hall-ui/public/profile.js",
4903
- "line": 1059,
4903
+ "line": 1054,
4904
4904
  "origin": "js-notice",
4905
4905
  "confidence": "certain"
4906
4906
  },
@@ -5458,7 +5458,7 @@
5458
5458
  "surface": "builders-hall",
5459
5459
  "text": "Cross-project record",
5460
5460
  "file": "modules/hall-ui/public/profile.html",
5461
- "line": 98,
5461
+ "line": 99,
5462
5462
  "origin": "html-text",
5463
5463
  "confidence": "certain"
5464
5464
  },
@@ -6016,7 +6016,7 @@
6016
6016
  "surface": "builders-hall",
6017
6017
  "text": "Everything this builder has shipped. Seen one role at a time, with goals and ideas, under",
6018
6018
  "file": "modules/hall-ui/public/profile.html",
6019
- "line": 112,
6019
+ "line": 113,
6020
6020
  "origin": "html-text",
6021
6021
  "confidence": "certain"
6022
6022
  },
@@ -6673,7 +6673,7 @@
6673
6673
  "surface": "builders-hall",
6674
6674
  "text": "Goals created",
6675
6675
  "file": "modules/hall-ui/public/profile.html",
6676
- "line": 153,
6676
+ "line": 154,
6677
6677
  "origin": "html-text",
6678
6678
  "confidence": "certain"
6679
6679
  },
@@ -7843,7 +7843,7 @@
7843
7843
  "surface": "builders-hall",
7844
7844
  "text": "Loading profile…",
7845
7845
  "file": "modules/hall-ui/public/profile.html",
7846
- "line": 35,
7846
+ "line": 36,
7847
7847
  "origin": "html-text",
7848
7848
  "confidence": "certain"
7849
7849
  },
@@ -8527,7 +8527,7 @@
8527
8527
  "surface": "builders-hall",
8528
8528
  "text": "No builder selected. Open a builder from the",
8529
8529
  "file": "modules/hall-ui/public/profile.js",
8530
- "line": 982,
8530
+ "line": 977,
8531
8531
  "origin": "js-markup",
8532
8532
  "confidence": "certain"
8533
8533
  },
@@ -9688,7 +9688,7 @@
9688
9688
  "surface": "builders-hall",
9689
9689
  "text": "Over time",
9690
9690
  "file": "modules/hall-ui/public/profile.html",
9691
- "line": 76,
9691
+ "line": 77,
9692
9692
  "origin": "html-text",
9693
9693
  "confidence": "certain"
9694
9694
  },
@@ -9994,7 +9994,7 @@
9994
9994
  "surface": "builders-hall",
9995
9995
  "text": "Profile sections",
9996
9996
  "file": "modules/hall-ui/public/profile.html",
9997
- "line": 45,
9997
+ "line": 46,
9998
9998
  "origin": "html-attr:aria-label",
9999
9999
  "confidence": "certain"
10000
10000
  },
@@ -11150,15 +11150,6 @@
11150
11150
  "origin": "js-markup",
11151
11151
  "confidence": "certain"
11152
11152
  },
11153
- {
11154
- "id": "a9c398651862",
11155
- "surface": "builders-hall",
11156
- "text": "See the cross-project record",
11157
- "file": "modules/hall-ui/public/profile-canonical.js",
11158
- "line": 114,
11159
- "origin": "js-markup",
11160
- "confidence": "certain"
11161
- },
11162
11153
  {
11163
11154
  "id": "17d5b5739f46",
11164
11155
  "surface": "builders-hall",
@@ -14476,7 +14467,7 @@
14476
14467
  "surface": "builders-hall",
14477
14468
  "text": "What this builder has done, seen one role at a time.",
14478
14469
  "file": "modules/hall-ui/public/profile.html",
14479
- "line": 138,
14470
+ "line": 139,
14480
14471
  "origin": "html-text",
14481
14472
  "confidence": "certain"
14482
14473
  },
@@ -14800,7 +14791,7 @@
14800
14791
  "surface": "builders-hall",
14801
14792
  "text": "Works completed",
14802
14793
  "file": "modules/hall-ui/public/profile.html",
14803
- "line": 148,
14794
+ "line": 149,
14804
14795
  "origin": "html-text",
14805
14796
  "confidence": "certain"
14806
14797
  },
@@ -17050,7 +17041,7 @@
17050
17041
  "surface": "builders-hall",
17051
17042
  "text": "sign in",
17052
17043
  "file": "modules/hall-ui/public/profile.js",
17053
- "line": 982,
17044
+ "line": 977,
17054
17045
  "origin": "js-markup",
17055
17046
  "confidence": "certain"
17056
17047
  },
@@ -17356,7 +17347,7 @@
17356
17347
  "surface": "builders-hall",
17357
17348
  "text": "the page everyone else reads",
17358
17349
  "file": "modules/hall-ui/public/profile.html",
17359
- "line": 33,
17350
+ "line": 34,
17360
17351
  "origin": "html-text",
17361
17352
  "confidence": "certain"
17362
17353
  },
@@ -17518,7 +17509,7 @@
17518
17509
  "surface": "builders-hall",
17519
17510
  "text": "to see your own profile.",
17520
17511
  "file": "modules/hall-ui/public/profile.js",
17521
- "line": 982,
17512
+ "line": 977,
17522
17513
  "origin": "js-markup",
17523
17514
  "confidence": "certain"
17524
17515
  },
package/docs/file-map.md CHANGED
@@ -98,7 +98,7 @@
98
98
  │ └── <redacted>.sql ← task 615: rename disciplines engineering→engineer, creative→artist|ideator; swaps both CHECK constraints + backfills tasks.discipline & builders.preferred_disciplines
99
99
  ├── modules/ ← self-contained optional feature modules (ADR 0083 / docs/modules-contract.md). Each `modules/<key>/` is a full vertical slice — routes, migrations, skills, seam registrations — reached only through src/module-api.js. The loader discovers them at boot; no core file changes to add one.
100
100
  │ ├── game/ ← the playable Example world — Phaser client, Colyseus rooms (WorldRoom/QueueRoom), world/terrain model, game DB tables, static assets. Moved BV1.R49 (task 1418). CLAUDE.md, module.json, routes/game.js, rooms/, world/, public/, migrations/.
101
- │ ├── discord/ ← outbound ship broadcast + inbound #ideas/#bugs bot + role-sync + channel management + account-link state + the Board Room mirror. Mirror only, never authority (ADR 0033). CLAUDE.md, module.json, routes/discord.js, routes/bug-attachments.js, discord-bot.js, discord-lib.js, board-broadcast.js (announce a window / edit the outcome onto it — R21/R22 of goal 1000069), migrations/discord_001_board_messages.sql.
101
+ │ ├── discord/ ← outbound ship broadcast + inbound #ideas/#bugs bot + role-sync + channel management + account-link state + the Board Room mirror. Mirror only, never authority (ADR 0033). CLAUDE.md, module.json, routes/discord.js, routes/bug-attachments.js, discord-bot.js, discord-lib.js, board-broadcast.js (announce a window / edit the outcome onto it — R21/R22 of goal 1000069), discord-support.js (the #support answerer: @-mention → public-docs answer, read-only, monthly cap — ADR 0367), migrations/discord_001_board_messages.sql.
102
102
  │ ├── art-pipeline/ ← self-improving pixel-art generation pipeline (its otb-* skills are the pixel-art module's since task 1004470; artist is a core discipline since ADR 0272). CLAUDE.md, module.json, routes/art-key.js, art/ (lives under art/ at repo root; module owns the route and discipline entry).
103
103
  │ ├── memory/ ← CORE-DOMAIN carve (BV1.R72, default-on): per-builder session memory + the recall/search retrieval layer (ADR 0060) + learnings. CLAUDE.md, module.json, routes/{memory,search,learnings}.js, memory.js, search.js, search-chunker.js, learnings.js.
104
104
  │ ├── grading/ ← CORE-DOMAIN carve (BV1.R73, default-on): the grade-a-diff adversarial worker panel + deterministic pre-passes + rubric; registers the `grade` kernel port. CLAUDE.md, module.json, routes/grade.js, grader.js, grader-rubric.json.
@@ -2960,5 +2960,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
2960
2960
  stays and still answers the profile alone.
2961
2961
  1.21.64 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2962
2962
  landed since 1.21.63 with no explicit bump. run 37477390735. (task 1002620)
2963
+ 1.21.65 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2964
+ landed since 1.21.64 with no explicit bump. run 37482378254. (task 1002620)
2965
+ 1.21.66 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2966
+ landed since 1.21.65 with no explicit bump. run 37486828045. (task 1002620)
2963
2967
  ---------------------------------------------------------------------------
2964
2968
  ```
@@ -758,7 +758,6 @@
758
758
  "modules/hall-ui/public/hall-visual.js",
759
759
  "modules/hall-ui/public/hall-kit.js",
760
760
  "modules/hall-ui/public/profile-connections.js",
761
- "modules/hall-ui/public/profile-canonical.js",
762
761
  "modules/hall-ui/public/profile-charts.js",
763
762
  "modules/hall-ui/public/founding-mark.js",
764
763
  "modules/hall-ui/public/profile-roles.js",
@@ -2374,7 +2374,7 @@
2374
2374
  "surface": "builders",
2375
2375
  "title": "Profile",
2376
2376
  "states_read": ["own","works","activity","activity-artist","activity-ideator","other","artist","ideator","out"],
2377
- "files_hash": "6fe4be25444cf73f",
2377
+ "files_hash": "47ded1e58ca035ec",
2378
2378
  "reading_hash": "8d48fe73a48c9fc3",
2379
2379
  "counts": {"lines":200,"placed":72,"shared":9,"unplaced":119},
2380
2380
  "omitted": {"data":195},
@@ -2388,7 +2388,7 @@
2388
2388
  {"key":"L0006","section":"top bar","text":"Artistry","placement":"shared"},
2389
2389
  {"key":"L0007","section":"top bar","text":"Ideating","placement":"shared"},
2390
2390
  {"key":"L0008","section":"top bar","text":"Governing","placement":"shared"},
2391
- {"key":"L0009","section":"page top","text":"the page everyone else reads","placement":"placed","file":"modules/hall-ui/public/profile.html","line":33,"string_id":"95854f8315ca"},
2391
+ {"key":"L0009","section":"page top","text":"the page everyone else reads","placement":"placed","file":"modules/hall-ui/public/profile.html","line":34,"string_id":"95854f8315ca"},
2392
2392
  {"key":"L0010","section":"profile","text":"profile","placement":"unplaced"},
2393
2393
  {"key":"L0011","section":"profile","text":"{…} file{…}","placement":"placed","file":"modules/hall-ui/public/gate.js","line":151,"string_id":"3efa67dc6e07","state":"activity-ideator","occurrences":2},
2394
2394
  {"key":"L0012","section":"profile","text":"{…} — {…} · 9 pages approved","placement":"unplaced","state":"activity-artist"},
@@ -2400,8 +2400,8 @@
2400
2400
  {"key":"L0018","section":"profile","text":"Member since {…}","placement":"placed","file":"modules/hall-ui/public/profile.js","line":118,"string_id":"df335a1224ae","state":"ideator","occurrences":3},
2401
2401
  {"key":"L0019","section":"profile","text":"Ideator, {…} role","placement":"unplaced","state":"ideator"},
2402
2402
  {"key":"L0020","section":"profile","text":"Artist, {…} role","placement":"unplaced","state":"artist"},
2403
- {"key":"L0021","section":"Activity, by role","text":"Activity, by role","placement":"placed","file":"modules/hall-ui/public/profile.html","line":137,"string_id":"d628b36ec58e","state":"activity"},
2404
- {"key":"L0022","section":"Activity, by role","text":"What this builder has done, seen one role at a time.","placement":"placed","file":"modules/hall-ui/public/profile.html","line":138,"string_id":"a444ad812e4f","state":"activity"},
2403
+ {"key":"L0021","section":"Activity, by role","text":"Activity, by role","placement":"placed","file":"modules/hall-ui/public/profile.html","line":138,"string_id":"d628b36ec58e","state":"activity"},
2404
+ {"key":"L0022","section":"Activity, by role","text":"What this builder has done, seen one role at a time.","placement":"placed","file":"modules/hall-ui/public/profile.html","line":139,"string_id":"a444ad812e4f","state":"activity"},
2405
2405
  {"key":"L0023","section":"Activity, by role","text":"Artist","placement":"unplaced","state":"activity-artist"},
2406
2406
  {"key":"L0024","section":"Activity, by role","text":"Ideator","placement":"unplaced","state":"activity"},
2407
2407
  {"key":"L0025","section":"Activity, by role","text":"{…} file{…}","placement":"placed","file":"modules/hall-ui/public/gate.js","line":151,"string_id":"3efa67dc6e07","state":"activity-ideator","occurrences":3},
@@ -2446,7 +2446,7 @@
2446
2446
  {"key":"L0064","section":"Activity, by role","text":"after","placement":"unplaced","state":"activity-artist","occurrences":4},
2447
2447
  {"key":"L0065","section":"footer","text":"Sign out","placement":"shared","file":"modules/hall-ui/public/shell.js","line":471,"string_id":"1883ad7f07fc","state":"activity-artist","occurrences":4},
2448
2448
  {"key":"L0066","section":"Activity, by role","text":"Builders hall · this session","placement":"placed","file":"modules/hall-ui/public/craft-mode.js","line":352,"string_id":"fd1903586e8e","state":"activity-artist"},
2449
- {"key":"L0067","section":"Works completed","text":"Works completed","placement":"placed","file":"modules/hall-ui/public/profile.html","line":148,"string_id":"0e6b8e8f26cc","state":"activity"},
2449
+ {"key":"L0067","section":"Works completed","text":"Works completed","placement":"placed","file":"modules/hall-ui/public/profile.html","line":149,"string_id":"0e6b8e8f26cc","state":"activity"},
2450
2450
  {"key":"L0068","section":"Works completed","text":"Role","placement":"unplaced","state":"activity"},
2451
2451
  {"key":"L0069","section":"Works completed","text":"Shipped","placement":"placed","file":"modules/hall-ui/public/task.js","line":677,"string_id":"0c0a50ba1666","state":"activity"},
2452
2452
  {"key":"L0070","section":"Works completed","text":"{…} file{…}","placement":"placed","file":"modules/hall-ui/public/gate.js","line":151,"string_id":"3efa67dc6e07","state":"activity","occurrences":3},
@@ -2459,7 +2459,7 @@
2459
2459
  {"key":"L0077","section":"Works completed","text":"‹ Prev","placement":"placed","file":"modules/hall-ui/public/hall-kit.js","line":74,"string_id":"de8fa5223aee","state":"activity"},
2460
2460
  {"key":"L0078","section":"Works completed","text":"Page {…} of {…}","placement":"placed","file":"modules/hall-ui/public/hall-kit.js","line":112,"string_id":"15a49bf15007","state":"activity"},
2461
2461
  {"key":"L0079","section":"Works completed","text":"Next ›","placement":"placed","file":"modules/hall-ui/public/hall-kit.js","line":74,"string_id":"f52ba261d930","state":"activity"},
2462
- {"key":"L0080","section":"Goals created","text":"Goals created","placement":"placed","file":"modules/hall-ui/public/profile.html","line":153,"string_id":"6a85997bda21","state":"activity"},
2462
+ {"key":"L0080","section":"Goals created","text":"Goals created","placement":"placed","file":"modules/hall-ui/public/profile.html","line":154,"string_id":"6a85997bda21","state":"activity"},
2463
2463
  {"key":"L0081","section":"Goals created","text":"Goal","placement":"placed","file":"modules/hall-ui/public/gate.js","line":360,"string_id":"ec88b946ad16","state":"activity"},
2464
2464
  {"key":"L0082","section":"Goals created","text":"Roles","placement":"unplaced","state":"activity"},
2465
2465
  {"key":"L0083","section":"Goals created","text":"Progress","placement":"unplaced","state":"activity"},
@@ -2467,7 +2467,7 @@
2467
2467
  {"key":"L0085","section":"Goals created","text":"{…} and {…}","placement":"placed","file":"modules/hall-ui/public/profile-nudge.js","line":77,"string_id":"56b00d773232","state":"activity"},
2468
2468
  {"key":"L0086","section":"Goals created","text":"Builders hall · this session","placement":"placed","file":"modules/hall-ui/public/craft-mode.js","line":352,"string_id":"fd1903586e8e","state":"activity"},
2469
2469
  {"key":"L0087","section":"Works","text":"Works","placement":"unplaced","state":"works"},
2470
- {"key":"L0088","section":"Works","text":"Everything this builder has shipped. Seen one role at a time, with goals and ideas, under","placement":"placed","file":"modules/hall-ui/public/profile.html","line":112,"string_id":"37110f7e2b27","state":"works"},
2470
+ {"key":"L0088","section":"Works","text":"Everything this builder has shipped. Seen one role at a time, with goals and ideas, under","placement":"placed","file":"modules/hall-ui/public/profile.html","line":113,"string_id":"37110f7e2b27","state":"works"},
2471
2471
  {"key":"L0089","section":"Works","text":"activity","placement":"unplaced","state":"works"},
2472
2472
  {"key":"L0090","section":"Works","text":"Credits","placement":"unplaced","state":"works"},
2473
2473
  {"key":"L0091","section":"Works","text":"Shipped","placement":"placed","file":"modules/hall-ui/public/task.js","line":677,"string_id":"0c0a50ba1666","state":"works"},
@@ -2485,7 +2485,7 @@
2485
2485
  {"key":"L0103","section":"profile","text":"this week","placement":"unplaced","state":"artist"},
2486
2486
  {"key":"L0104","section":"profile","text":"works {…}","placement":"unplaced"},
2487
2487
  {"key":"L0105","section":"profile","text":"karma","placement":"unplaced"},
2488
- {"key":"L0106","section":"By the numbers","text":"By the numbers","placement":"placed","file":"modules/hall-ui/public/profile.html","line":64,"string_id":"181cfa50b127"},
2488
+ {"key":"L0106","section":"By the numbers","text":"By the numbers","placement":"placed","file":"modules/hall-ui/public/profile.html","line":65,"string_id":"181cfa50b127"},
2489
2489
  {"key":"L0107","section":"By the numbers","text":"works {…}","placement":"unplaced"},
2490
2490
  {"key":"L0108","section":"By the numbers","text":"all kinds, see below","placement":"unplaced"},
2491
2491
  {"key":"L0109","section":"By the numbers","text":"karma","placement":"unplaced"},
@@ -2508,7 +2508,7 @@
2508
2508
  {"key":"L0126","section":"pages tweaked, per surface","text":"pages tweaked, per surface","placement":"placed","file":"modules/hall-ui/public/profile.js","line":183,"string_id":"356573640ce1","state":"artist"},
2509
2509
  {"key":"L0127","section":"pages tweaked, per surface","text":"{…} of {…}","placement":"placed","file":"modules/hall-ui/public/sky-panel.js","line":120,"string_id":"73c1372b43d9","state":"artist","occurrences":3},
2510
2510
  {"key":"L0128","section":"By the numbers","text":"2 ignited","placement":"unplaced"},
2511
- {"key":"L0129","section":"Over time","text":"Over time","placement":"placed","file":"modules/hall-ui/public/profile.html","line":76,"string_id":"695ffff13730"},
2511
+ {"key":"L0129","section":"Over time","text":"Over time","placement":"placed","file":"modules/hall-ui/public/profile.html","line":77,"string_id":"695ffff13730"},
2512
2512
  {"key":"L0130","section":"Over time","text":"30 days","placement":"unplaced"},
2513
2513
  {"key":"L0131","section":"Over time","text":"90 days","placement":"unplaced"},
2514
2514
  {"key":"L0132","section":"Over time","text":"1 year","placement":"unplaced"},
@@ -2548,7 +2548,7 @@
2548
2548
  {"key":"L0166","section":"Achievements","text":"Achievements","placement":"unplaced"},
2549
2549
  {"key":"L0167","section":"Achievements","text":"No achievements earned yet.","placement":"placed","file":"modules/hall-ui/public/profile.js","line":199,"string_id":"c255749ec989","state":"ideator"},
2550
2550
  {"key":"L0168","section":"Achievements","text":"Won {…}","placement":"placed","file":"modules/hall-ui/public/profile.js","line":206,"string_id":"c5fd76a9e03d","occurrences":2},
2551
- {"key":"L0169","section":"Cross-project record","text":"Cross-project record","placement":"placed","file":"modules/hall-ui/public/profile.html","line":98,"string_id":"a55e423263f0"},
2551
+ {"key":"L0169","section":"Cross-project record","text":"Cross-project record","placement":"placed","file":"modules/hall-ui/public/profile.html","line":99,"string_id":"a55e423263f0"},
2552
2552
  {"key":"L0170","section":"Cross-project record","text":"Works {…}","placement":"unplaced"},
2553
2553
  {"key":"L0171","section":"Cross-project record","text":"Credits","placement":"unplaced"},
2554
2554
  {"key":"L0172","section":"Cross-project record","text":"Karma","placement":"unplaced"},
@@ -79,6 +79,36 @@ async function countRecentDiscordBugFilings(builderId, sinceIso) {
79
79
  return rows[0].n;
80
80
  }
81
81
 
82
+ // --- the #support answerer's ledger (task 1002534, ADR 0367) -----------------
83
+ //
84
+ // The bot's own builders row (migration 067, system_role='discord_bot'): the
85
+ // principal an answer is booked to. The asker is never looked up.
86
+ async function getBotPrincipalId() {
87
+ const { rows } = await pool.query(
88
+ `SELECT id FROM builders WHERE system_role = 'discord_bot' ORDER BY id LIMIT 1`
89
+ );
90
+ return rows.length ? Number(rows[0].id) : null;
91
+ }
92
+
93
+ // Month-to-date #support spend, the UTC calendar month, summed from the cost each
94
+ // answer's audit row carries. The monthly cap reads this, so it survives a
95
+ // restart. Throws on a DB error; the caller treats that as "no answer".
96
+ // INDEXED: `route = $1 AND recorded_at >= <month start>` is exactly the shape of
97
+ // migrations/033_audit_log.sql's idx_audit_log_route (route, recorded_at DESC), so
98
+ // this reads one month of one route's rows, never a scan of the whole log. It
99
+ // runs once per answer, after the free checks (rate limit, cap, concurrency).
100
+ async function sumSupportSpendThisMonth(route = 'discord:support') {
101
+ const { rows } = await pool.query(
102
+ `SELECT COALESCE(SUM((request_body_redacted->>'cost_usd')::numeric), 0)::float8 AS usd
103
+ FROM audit_log
104
+ WHERE route = $1
105
+ AND surface = 'discord_bot'
106
+ AND recorded_at >= date_trunc('month', now() AT TIME ZONE 'UTC') AT TIME ZONE 'UTC'`,
107
+ [route]
108
+ );
109
+ return Number(rows[0].usd) || 0;
110
+ }
111
+
82
112
  // --- the board mirror's own bookkeeping (R22 of goal 1000069) ----------------
83
113
  //
84
114
  // Which message announced which board item, so the close can be EDITED onto the
@@ -137,6 +167,8 @@ module.exports = {
137
167
  getDiscordLink,
138
168
  getDiscordLinks,
139
169
  countRecentDiscordBugFilings,
170
+ getBotPrincipalId,
171
+ sumSupportSpendThisMonth,
140
172
  recordApplause,
141
173
  recordBoardMessage,
142
174
  getBoardMessage,
@@ -471,6 +471,20 @@ async function start() {
471
471
  channelId: bugsChannelId, aggregator: bugAggregator, logLabel: 'bug handler',
472
472
  }));
473
473
 
474
+ // #support answers (task 1002534, ADR 0367): an @-mention in the pinned
475
+ // support channel gets a short answer from the public docs. Read-only, its
476
+ // own kill switch (DISCORD_SUPPORT_ENABLED) on top of this one, and a
477
+ // monthly spend cap. Everything lives in discord-support.js; it is the
478
+ // first feature that REPLIES through the gateway, so the bot needs Send
479
+ // Messages in that one channel.
480
+ const support = require('./discord-support').createLiveSupport({ client: c, db });
481
+ if (support.handler) {
482
+ c.on(Events.MessageCreate, support.handler);
483
+ log.info(`[discord-bot] answering @-mentions in #support (${support.channelId}), cap $${support.capUsd}/month.`);
484
+ } else {
485
+ log.info(`[discord-bot] #support answers are inert (${support.inertReason}).`);
486
+ }
487
+
474
488
  // Role-on-arrival (F7, ADR 0033 — the link-first onboarding gate's safety
475
489
  // net). The auto-join path (routes/auth.js) already sets the rank role at
476
490
  // add-time, so a builder who linked-then-joined is correct immediately. This