@bongos/core 1.21.63 → 1.21.65

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 CHANGED
@@ -2,22 +2,22 @@
2
2
  "artifact": "bongos-core",
3
3
  "manifest_schema": 1,
4
4
  "generator": "scripts/gds/package-core.js",
5
- "core_version": "1.21.63",
6
- "core_contract": "1.21.63",
7
- "source_commit": "d41368cb70cf813937608660b4f1ce0719e0523c",
5
+ "core_version": "1.21.65",
6
+ "core_contract": "1.21.65",
7
+ "source_commit": "bf961553e46095e169f2fb5218e409ff2ce03a8f",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-10-06T00:15:37.787Z",
9
+ "built_at": "2026-10-06T14:51:44.072Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
- "docs_redacted": 586,
12
+ "docs_redacted": 587,
13
13
  "agent_docs_stubbed": 28,
14
- "functional_verbatim": 2951,
14
+ "functional_verbatim": 2954,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 3566,
20
- "tree_sha256": "739a2690a0bb3d598fab3a9c799e365ba82d2b41522a3c5e5fc89bb736b5afec",
19
+ "file_count": 3570,
20
+ "tree_sha256": "bbb40e44796d6e7f5f34f46813add3554ad9d3a8ec722731a46bf1fa2ea1022f",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -2289,10 +2289,15 @@
2289
2289
  "mode": "0000644",
2290
2290
  "sha256": "29c841d193efa7b6f6479e14a6056057138574fcd7b526f667da6f219dece5a7"
2291
2291
  },
2292
+ {
2293
+ "path": "docs/adr/0367-the-support-bot-answers-from-public-docs-only.md",
2294
+ "mode": "0000644",
2295
+ "sha256": "6d02d69a8d1bc0378f143960b280e06b4d7777b0ef4d3e7aa8c2d5a4337f85b8"
2296
+ },
2292
2297
  {
2293
2298
  "path": "docs/adr/README.md",
2294
2299
  "mode": "0000644",
2295
- "sha256": "08f66fecefc58d9c09e45841dd9d47514e807934cfcd4621a8bfeaa390afb334"
2300
+ "sha256": "2d9e4ea00f57234d18adc9a8b95b1ccabb3fca6b61b859d629eb8e8f533cc9f8"
2296
2301
  },
2297
2302
  {
2298
2303
  "path": "docs/api-reference.md",
@@ -2327,12 +2332,12 @@
2327
2332
  {
2328
2333
  "path": "docs/copy-inventory.md",
2329
2334
  "mode": "0000644",
2330
- "sha256": "f6f91263fa72b1b421dd9c0868bfd750f8a65466b34e67b4305d1243fb6386a0"
2335
+ "sha256": "73103f4bef4fcc8dca7b91dff1ae4cf5d4f3dd7e188efcef3a6a848633816cc5"
2331
2336
  },
2332
2337
  {
2333
2338
  "path": "docs/copy-registry.json",
2334
2339
  "mode": "0000644",
2335
- "sha256": "d8483d26f99e8ad3b73bb4490e8fbcf78b0dd0b919d80bd5749bb87768b40687"
2340
+ "sha256": "2c74dc77936690dbc7879a1b7bef5680555fdf2cb99dbe74de2ee5ee89e7035d"
2336
2341
  },
2337
2342
  {
2338
2343
  "path": "docs/design/apex-pass-2-direction.md",
@@ -2832,7 +2837,7 @@
2832
2837
  {
2833
2838
  "path": "docs/file-map.md",
2834
2839
  "mode": "0000644",
2835
- "sha256": "f9943f6b4d4859358b4d2d71af9f043d47b2f5a70532bbb9489f34423c596ff8"
2840
+ "sha256": "30be6ac52963f8b85a7b608b21505279e0560f208aba36d52cfc42350be663b5"
2836
2841
  },
2837
2842
  {
2838
2843
  "path": "docs/handoff-template.md",
@@ -2842,7 +2847,7 @@
2842
2847
  {
2843
2848
  "path": "docs/module-api-changelog.md",
2844
2849
  "mode": "0000644",
2845
- "sha256": "628a2c51dfe01f436ee3d1dbc8c79d731c4d2c8be8a7f32d0f4aaa8a649703a7"
2850
+ "sha256": "3e70987df8e2ab36308b3ad31db5f3d8de001ee085b77c0093fb8962511cd07f"
2846
2851
  },
2847
2852
  {
2848
2853
  "path": "docs/modules-contract.md",
@@ -2932,7 +2937,7 @@
2932
2937
  {
2933
2938
  "path": "docs/page-readings.json",
2934
2939
  "mode": "0000644",
2935
- "sha256": "339badbdc838f18409778ace02e3e820e948be47fef16997c2ffd5d8b9b7149d"
2940
+ "sha256": "e5649a86d502afffa3d71a744f49511bad2ceffdbfc1eecae1206a79761f53f6"
2936
2941
  },
2937
2942
  {
2938
2943
  "path": "docs/project-context.template.md",
@@ -4647,7 +4652,7 @@
4647
4652
  {
4648
4653
  "path": "modules/discord/db.js",
4649
4654
  "mode": "0000644",
4650
- "sha256": "e265e853cf153a76e7b84f304e6f4338bd2bbfc2098383c358a0df7ac02a6817"
4655
+ "sha256": "130cdbc84ce8298c6dffd03bc125d5c3b102ccff11fb95d24d6e7e999ac2aaa7"
4651
4656
  },
4652
4657
  {
4653
4658
  "path": "modules/discord/discord-approvals.js",
@@ -4662,7 +4667,7 @@
4662
4667
  {
4663
4668
  "path": "modules/discord/discord-bot.js",
4664
4669
  "mode": "0000644",
4665
- "sha256": "15d3bf047731fa50cca8bc3b06ef1c2b27e9b8a0067f3151a00974f99eb2ac3d"
4670
+ "sha256": "a96d9e55db1d66813fd552419bee6548edf2587e9510ae0c2e61640d1e80fc34"
4666
4671
  },
4667
4672
  {
4668
4673
  "path": "modules/discord/discord-bugs.js",
@@ -4694,6 +4699,11 @@
4694
4699
  "mode": "0000644",
4695
4700
  "sha256": "852abc58bdec011b6d3bf222a35e198c6ea98a761d57a76be85477ef85ae54f5"
4696
4701
  },
4702
+ {
4703
+ "path": "modules/discord/discord-support.js",
4704
+ "mode": "0000644",
4705
+ "sha256": "68c32e054d07133f685ce9dd80a4b6d53fada4b1e97939491b958b1bb8994a9e"
4706
+ },
4697
4707
  {
4698
4708
  "path": "modules/discord/lifecycle-port.js",
4699
4709
  "mode": "0000644",
@@ -4707,7 +4717,7 @@
4707
4717
  {
4708
4718
  "path": "modules/discord/module.json",
4709
4719
  "mode": "0000644",
4710
- "sha256": "3d9e8fc9dd7991b3636904bb4680a92be08f1dfc698d1f87452581412048911f"
4720
+ "sha256": "f3b7314eaea0977841d71a02fdc032c2fea7f4cf242450161b2d17824409b7f7"
4711
4721
  },
4712
4722
  {
4713
4723
  "path": "modules/discord/pollers/discord-bot.js",
@@ -6382,17 +6392,17 @@
6382
6392
  {
6383
6393
  "path": "modules/hall-ui/public/watch.css",
6384
6394
  "mode": "0000644",
6385
- "sha256": "f11f73ec3649c7547cb59b7d5fdf090a1e52ad96f6fb28bdb3b5a14691153de1"
6395
+ "sha256": "45b811f469be853704dfb9f66da96cd3be7b04c41a99de0ee77e54b9b352bf2c"
6386
6396
  },
6387
6397
  {
6388
6398
  "path": "modules/hall-ui/public/watch.html",
6389
6399
  "mode": "0000644",
6390
- "sha256": "43e2a20dc01c2f577e9307557a08a8e826143273275515f4570e14f07ccaa066"
6400
+ "sha256": "19d3e14184d3b649e08eef88d436370abb546568482d1a5930870ae8e02c14d8"
6391
6401
  },
6392
6402
  {
6393
6403
  "path": "modules/hall-ui/public/watch.js",
6394
6404
  "mode": "0000644",
6395
- "sha256": "6746170acdd097b14bdccce236ac236b4962bdd1679de2c5624ca1a10cf784d0"
6405
+ "sha256": "e919ccc5d8edfae77f699efed5d98fd19f793b857c827c6b09b8333612abc231"
6396
6406
  },
6397
6407
  {
6398
6408
  "path": "modules/hall-ui/public/watch.states.json",
@@ -6497,7 +6507,7 @@
6497
6507
  {
6498
6508
  "path": "modules/hall-ui/records/oversight-family.md",
6499
6509
  "mode": "0000644",
6500
- "sha256": "1c3288b390de98be0c7eabd377d27631d7873f748e42378da848e9c64ba02de7"
6510
+ "sha256": "42ae5b5f51c636c5d7e09e09e4ab25e417229799bc8db4a80e9c5082a23e5151"
6501
6511
  },
6502
6512
  {
6503
6513
  "path": "modules/hall-ui/records/page-notes.md",
@@ -7447,7 +7457,7 @@
7447
7457
  {
7448
7458
  "path": "modules/memory/module.json",
7449
7459
  "mode": "0000644",
7450
- "sha256": "997d6c1ce5707c64f8ab74ae51b34234ecf975fbf62ee7160e9e6750233cbf96"
7460
+ "sha256": "f04e13545b12ef46a1233bf295cc696f64bad72866207b6be9c8166528bf8208"
7451
7461
  },
7452
7462
  {
7453
7463
  "path": "modules/memory/pollers/search-index.js",
@@ -7467,7 +7477,7 @@
7467
7477
  {
7468
7478
  "path": "modules/memory/routes/search.js",
7469
7479
  "mode": "0000644",
7470
- "sha256": "a79f61f7906393fcb0bffb67f224c47b7129223e3a6806357de091a9339fa42e"
7480
+ "sha256": "3b8fffea8e90c461c9fdc2957ca60300734b44257f9b3d5aca49e73d04019b0e"
7471
7481
  },
7472
7482
  {
7473
7483
  "path": "modules/memory/search-chunker.js",
@@ -7477,7 +7487,7 @@
7477
7487
  {
7478
7488
  "path": "modules/memory/search.js",
7479
7489
  "mode": "0000644",
7480
- "sha256": "43ede2e90d0124c42f6f9dfc3695898b83c8e876c01f70b0ae6e9d2f61d41085"
7490
+ "sha256": "0540c9034480a9ee78159449b9cbc86b8415a9c70b9d108f1d4fafa66bd997a5"
7481
7491
  },
7482
7492
  {
7483
7493
  "path": "modules/npm-release/module.json",
@@ -7627,7 +7637,7 @@
7627
7637
  {
7628
7638
  "path": "modules/onboarding/routes/access-requests.js",
7629
7639
  "mode": "0000644",
7630
- "sha256": "a48c44be60f77d3b8734beb203fb895a26d26d63666a2516032c10eea1ebf576"
7640
+ "sha256": "5a498b7b0a5f5b67fdfce415eeafbc54426752360c716451d22d180e3f6e8c54"
7631
7641
  },
7632
7642
  {
7633
7643
  "path": "modules/onboarding/routes/onboarding.js",
@@ -9967,12 +9977,12 @@
9967
9977
  {
9968
9978
  "path": "package-lock.json",
9969
9979
  "mode": "0000644",
9970
- "sha256": "420432c580d464d69a8e202ef34d471ca361e48a9ba41bc2db2c0d4cad874f58"
9980
+ "sha256": "f74efc3baf5ed890ece42d60d96e89fcaf6a798f44558b400c079839ff46e3e8"
9971
9981
  },
9972
9982
  {
9973
9983
  "path": "package.json",
9974
9984
  "mode": "0000644",
9975
- "sha256": "046e83692e6f67d76f0724f15fdd0b0848a27df4c72a931f794e644bed628257"
9985
+ "sha256": "33e94ab67e0bf024b19d2a59ac82e00a76e2878e28e5d962c5222fbec419cbb9"
9976
9986
  },
9977
9987
  {
9978
9988
  "path": "public-docs/index.html",
@@ -9992,7 +10002,7 @@
9992
10002
  {
9993
10003
  "path": "release-notes.json",
9994
10004
  "mode": "0000644",
9995
- "sha256": "d967b31f93314c9df18eb5346bb761835cbf1bd69a69443e515f4adad9fe5ea9"
10005
+ "sha256": "f5e7adc2ee05b1a16498418766dba9f6ca27c708c750ba9dfbb501a899d476c1"
9996
10006
  },
9997
10007
  {
9998
10008
  "path": "scripts/cron/style_guide_audit.sh",
@@ -10372,7 +10382,7 @@
10372
10382
  {
10373
10383
  "path": "scripts/gds/credential-presence.js",
10374
10384
  "mode": "0000644",
10375
- "sha256": "9b78c4d3459474fe4b89355a0d3fe0aa67e4fabfe7ef10b4bf061abdcb339b63"
10385
+ "sha256": "36ecef8da86e9905c8c3e8431881828aba0ecca7e10b63db014f86658c5bf58b"
10376
10386
  },
10377
10387
  {
10378
10388
  "path": "scripts/gds/cutover-codemod.js",
@@ -11847,7 +11857,7 @@
11847
11857
  {
11848
11858
  "path": "src/bongos/auth-admission.js",
11849
11859
  "mode": "0000644",
11850
- "sha256": "213c008fbb81f403a2f4acad34af3ad8cd27c3299efce0cc96d714be6d9424ca"
11860
+ "sha256": "e0e8a5b10571aa0248486e8ae6f556439e385938ca5cee9255a93df69f8ba549"
11851
11861
  },
11852
11862
  {
11853
11863
  "path": "src/bongos/auth-config.js",
@@ -11872,7 +11882,7 @@
11872
11882
  {
11873
11883
  "path": "src/bongos/auth.js",
11874
11884
  "mode": "0000644",
11875
- "sha256": "fad15cdbd6652b2a699f48769baaaed0e59314ac6bef326e663d6c7b06b1eaae"
11885
+ "sha256": "f09f297371563a361002d1443414aa5ab65da36ad5e68bfadb634c659aeda6f3"
11876
11886
  },
11877
11887
  {
11878
11888
  "path": "src/bongos/cascade-dispatch.js",
@@ -12212,7 +12222,7 @@
12212
12222
  {
12213
12223
  "path": "src/module-api.js",
12214
12224
  "mode": "0000644",
12215
- "sha256": "abc57062eb654fff799c6a526b21509943ac0b70c3e500680c139ac20b115f6b"
12225
+ "sha256": "be6c5f14da4c58d5449936a2c21aa984d00db13cb51c804e26dcdc25e36a18e3"
12216
12226
  },
12217
12227
  {
12218
12228
  "path": "src/module-loader/catalog.js",
@@ -12504,6 +12514,11 @@
12504
12514
  "mode": "0000644",
12505
12515
  "sha256": "185fed67d8b3e65121af637e37392c21d4bcd892bb2b305704479f42ff5987c7"
12506
12516
  },
12517
+ {
12518
+ "path": "tests/applicant_view_guild.mjs",
12519
+ "mode": "0000644",
12520
+ "sha256": "2f9f15d2191efe8a4d572afcc819ae3bf6e1ee52f9d6e36157669d58ece9ab8a"
12521
+ },
12507
12522
  {
12508
12523
  "path": "tests/application_echo.mjs",
12509
12524
  "mode": "0000644",
@@ -12512,7 +12527,7 @@
12512
12527
  {
12513
12528
  "path": "tests/application_lifecycle.mjs",
12514
12529
  "mode": "0000644",
12515
- "sha256": "f84e1b46315e57514f00545038eaa94a9617a83ebb8b5e4b55814fa3b063e155"
12530
+ "sha256": "7b711002b5ffe53c8246e115b60dd648cfe7768e39079b935eba0a31c402b8f7"
12516
12531
  },
12517
12532
  {
12518
12533
  "path": "tests/architect.mjs",
@@ -13534,6 +13549,11 @@
13534
13549
  "mode": "0000644",
13535
13550
  "sha256": "858e20374f392b3444ea6ef65253eb6563425ab772c4988a33434f350effac54"
13536
13551
  },
13552
+ {
13553
+ "path": "tests/discord_support.mjs",
13554
+ "mode": "0000644",
13555
+ "sha256": "3823845cfaf33c108d96f23d2f652a51df0ae859aff827fb18556a2cd7110cca"
13556
+ },
13537
13557
  {
13538
13558
  "path": "tests/display_name.mjs",
13539
13559
  "mode": "0000644",
@@ -15427,7 +15447,7 @@
15427
15447
  {
15428
15448
  "path": "tests/module_api.mjs",
15429
15449
  "mode": "0000644",
15430
- "sha256": "35a7e4c2e2afe2cd61e51318e7e3300e7629acd24683b515287aa9dcbe8501ae"
15450
+ "sha256": "9244fee28297a30d3effb01db58772ccb4ab234d180beaaf14a4bded5f9841f1"
15431
15451
  },
15432
15452
  {
15433
15453
  "path": "tests/module_api_lazy.mjs",
@@ -16772,7 +16792,7 @@
16772
16792
  {
16773
16793
  "path": "tests/search_isolation.mjs",
16774
16794
  "mode": "0000644",
16775
- "sha256": "6bc8729f739c83383c859b464c1cde2e7cfc3d148b09ea6478017ee135089267"
16795
+ "sha256": "5833e5642257d933a7245e44a8dd9b8365d4e050df2deb0bde7a1456901cbcd2"
16776
16796
  },
16777
16797
  {
16778
16798
  "path": "tests/search_route.mjs",
@@ -17732,7 +17752,7 @@
17732
17752
  {
17733
17753
  "path": "tests/watch_applications_queue.mjs",
17734
17754
  "mode": "0000644",
17735
- "sha256": "6f0dc184d552c4db4048894286cff96ade91e44c8070fae66fdeef324ba09a7d"
17755
+ "sha256": "faf5965df0ad69873aae07333890adc63193664f57659f3a6259e39edd5d892b"
17736
17756
  },
17737
17757
  {
17738
17758
  "path": "tests/watch_roster_self_row.mjs",
@@ -0,0 +1,69 @@
1
+ # ADR 0367 — The Discord support bot answers from public docs only, under a monthly cap that fails closed
2
+
3
+ - **Status:** accepted
4
+ - **Date:** 2026-10-06
5
+ - **Task:** [task 1002534](https://cloudbongos.com/builders#/task/1002534) (goal 1000110, BONGOS-V2), promoted from idea 1000440
6
+ - **Builds on:** [ADR 0033](0033-discord-bot-service-principal.md) (the bot is a service principal and a mirror, never authority; every bot action is audited), [ADR 0060](0060-gds-retrieval-layer.md) (recall: `search_chunks`, scoped in SQL), [ADR 0080](0080-llm-cache-api-transport-and-trust-model.md) (anything a stranger can type is untrusted input to a model), [ADR 0083](0083-modular-architecture-strangler-build-plan.md) (modules talk through ports, never a require)
7
+ - **Decided with:** the owner. Scope at the 2026-07-29 inbox triage: read-only, public docs only, one support channel, no writes. The rest on 2026-10-06, all five the recommended option: a $5/month cap, `claude-haiku-4-5`, answer only when @-mentioned, the newcomer docs as the corpus, and ship it switched off until a key is on the server.
8
+
9
+ ## Context
10
+
11
+ Support questions in Discord are answered by whoever happens to be around. The answers mostly already exist in the docs. A bot can point people at them, but it sits outside the trust boundary in a channel anyone can read and type into. That raises three risks the triage named: leaking rank-gated content into a public channel, prompt injection from channel text, and per-message model spend on a bootstrapped budget.
12
+
13
+ Two facts shaped the design. First, `search_chunks` at `min_rank='xenos'` is not "public". Every tracked `.md` is ingested at xenos unless it is in the protected set: session logs, every ADR, `.claude/` skills, `docs/credentials.md`, `docs/architecture.md`, the red-team recipe. Xenos is still a signed-in builder, and `#support` is visible to everyone. Second, nothing on the server calls a model today. The Claude login lives on builders' machines (`docs/credentials.md`), never on the host.
14
+
15
+ ## Decision
16
+
17
+ **D1 — Where and when.** The bot answers in ONE channel (`DISCORD_SUPPORT_CHANNEL_ID`, the guild's existing `support` channel) and only when a message @-mentions it. A reply ping does not count. Bots, webhooks and DMs are ignored. This is the first feature that replies through the gateway (every other post is a webhook), so the bot needs Send Messages in that channel.
18
+
19
+ **D2 — What it may read: an allowlist, enforced in SQL.** The corpus is `SUPPORT_DOC_REFS` in `modules/discord/discord-support.js`: `README.md`, `docs/onboarding/`, and `docs/api-reference.md`. The memory module serves it through a new port, `memory.searchPublicDocs`, whose scope is fixed inside the function and never taken from the caller:
20
+ - `source_kind='doc'`;
21
+ - `min_rank='xenos'`;
22
+ - no owner;
23
+ - `source_ref` on the allowlist.
24
+
25
+ The allowlist predicate rides into the same scoped CTE as the rank predicate, never a post-filter, and the body read re-applies the whole scope. An empty allowlist returns nothing. Widening the corpus is a code change reviewed by merge, because the list *is* the privacy boundary. The asker is never looked up: who they are cannot change what the bot reads.
26
+
27
+ **D3 — What it may write: nothing but the reply and its audit row.** It does not file an idea, a bug, a task or a blocker, and it requires no port that does. Each answer writes one `audit_log` row:
28
+ - `route='discord:support'`, `surface='discord_bot'`;
29
+ - booked to the bot's own principal (migration 067);
30
+ - carrying the cost, token counts, message and channel ids, and the doc refs.
31
+
32
+ ADR 0033 already audits every bot action, and the `discord:bugs` rate cap reads audit_log the same way. That row is also the spend ledger (D5), so the feature needs no migration.
33
+
34
+ **D4 — Untrusted text.**
35
+ - The question and the passages are fenced with a per-call random marker, and any fence-shaped run in either is rewritten first (the page-notes split's posture).
36
+ - The model gets no tools, so a jailbroken reply can only be one wrong message.
37
+ - That message cannot ping anyone. Mentions are rewritten in the text, and the reply is sent with `allowedMentions: { parse: [] }`.
38
+ - An injection's most useful payload is a link under the bot's name ("reset your account here"). So a masked link is rewritten to show its address, any URL not found verbatim in the passages the model was given is replaced with `[link removed]`, and the reply goes out with embeds suppressed.
39
+ - A rate limit (D5) applies before every reply, including the fixed lines, so repeating a bare mention cannot farm a reply flood.
40
+ - The model never sees anything private, so there is nothing for an injection to extract.
41
+
42
+ **D5 — Spend: Haiku, a monthly cap, fail closed.**
43
+ - **Model and price.** The model is `claude-haiku-4-5` (about $0.008 an answer at ≤600 output tokens), called through the official SDK. The SDK is lazily required, so an instance with the feature off never loads it.
44
+ - **The cap.** `DISCORD_SUPPORT_MONTHLY_CAP_USD`, default $5, is read before every call from month-to-date spend: the sum of the D3 rows in the current UTC month.
45
+ - **Failing closed.** No answer is given when the cap is set to anything but a positive number, when the ledger read fails, or when there is no bot principal to book against. The rule is the agents spend-ceiling's (task 1001393).
46
+ - **The ledger read is indexed.** Its `route = 'discord:support' AND recorded_at >= <month start>` is the shape of migration 033's `idx_audit_log_route (route, recorded_at DESC)`, so it reads one month of one route, not the whole log.
47
+ - **Concurrency.** Answers in flight are reserved at a worst case derived from the price table, and at most two run at once, so concurrency cannot overshoot. The reservation is per process, which holds because there is one: a bot token is one gateway connection, run in the single web process. A second answering process would need a shared reservation first.
48
+ - **Unbooked spend.** A cost that could not be booked counts in memory until the month turns. So does a call that died after it may have been billed (a timeout or a 5xx). A 4xx costs nothing.
49
+ - **Reaching the cap.** The bot posts one "out of answers for this month" line, then stays quiet until the month turns.
50
+ - **Per-asker limit.** A silent in-memory rate limit (4 per 10 minutes) bounds a burst. It is not the bill.
51
+
52
+ **D6 — Off by default, with its own switch and its own key.**
53
+ - **The switch.** `DISCORD_SUPPORT_ENABLED` must be exactly `true`, on top of `DISCORD_BOT_ENABLED`.
54
+ - **The key.** `DISCORD_SUPPORT_ANTHROPIC_API_KEY` is read from env only and namespaced on purpose. A host-wide `ANTHROPIC_API_KEY` would also be picked up by the `claude` CLI the grader spawns, and bill that key for grading.
55
+ - **Inert states.** Missing the switch, the channel or the key makes the feature inert, and the boot log says which one.
56
+
57
+ **D7 — Who reviews wrong answers.** The humans in the channel, as before. Each answer names its sources and has an audit row with the message id. The fix for a wrong answer is the doc it was drawn from, not the bot.
58
+
59
+ ## Consequences
60
+
61
+ - On cloudbongos.com it does nothing until the bot itself runs there ([task 1002506](https://cloudbongos.com/builders#/task/1002506)) and the owner sets the switch, the channel and the key (`docs/discord/support.md`).
62
+ - A question the newcomer docs do not cover gets "I couldn't find that". That is the intended answer, not a gap to fill by widening the corpus to internal docs.
63
+ - The memory module now provides a port. A future public surface that wants recall must go through `searchPublicDocs` and bring its own allowlist; there is still no unauthenticated HTTP search route.
64
+ - The audit-log ledger is approximate in one direction only: a lost booking undercounts until a restart forgets it. The in-flight reservation and the small per-answer cost keep that inside cents.
65
+
66
+ ## Reproduce / verify
67
+
68
+ - `node tests/discord_support.mjs`: config and kill switch, the message guard, the fence, mention-proofing, the cap, the reservation and fail-closed paths, the model request (no tools), read-only by source, and the port's fixed SQL scope.
69
+ - `node tests/search_isolation.mjs`, Layer C (needs a Postgres): the public port never returns a metic chunk, an owned chunk, a DB-prose row, or a doc off the allowlist.
@@ -485,3 +485,4 @@ These 20 numbers are each shared by exactly two files. They are **accepted histo
485
485
  | 0364 | [**Repo access is asked at sign-in on the hub, and held for as long as the builder stays signed in** ([task 1004549](https://cloudbongos.com/builders#/task/1004549)). Amends ADR 0143 and 0155. **D1:** every sign-in door on the hub's own pages carries `repos=1`; the hall and CLI stay identity-only. **D2:** the repo token's TTL is the web session's remaining life; a web sign-out deletes it, a CLI logout does not. **D3:** a draft at a wizard step beside a stored create keeps its place on a reload or a GitHub round-trip.](0364-repo-access-is-asked-at-sign-in-and-held-for-the-session.md) | auth / GitHub / create wizard |
486
486
  | 0365 | [**A guild engages a project through each member's own door** ([task 1002303](https://cloudbongos.com/builders#/task/1002303)). Amends ADR 0336 D7's invite half after ADR 0353. **D1:** the engagement row as D7 specified, plus `guild_id` on the echo (the latest application decides the label). **D2:** a guild applies = each member applies as themselves through the relay, naming the engagement. **D3:** a project invites a guild = the hub records it and returns one hall hand-off link per public-roster member; no hub-written invites, the one-step relay stays deferred. **D4:** a project is named only when on the map and public, for both kinds. **D5:** the label rides the applicant-profile read; the queue is R28's. **D6:** the D4.4 guild cards are their own task.](0365-a-guild-engages-a-project-through-each-members-own-door.md) | guilds / invites / applications |
487
487
  | 0366 | [**Hall planning is forms; a goal made there carries its own spec task, and the interviews stay in the CLI** ([task 1003461](https://cloudbongos.com/builders#/task/1003461), goal 1000089). Spike answer. **D1:** form-shaped, build in the hall: create a task under a goal, create a goal (name + subtitle, version, category, no scope field), attach a criterion to a goal you hold; every server refusal rendered as copy. **D2:** interview-shaped, deliberately absent from the hall: version planning, scope inference, drafting criteria and seed tasks, reuse-vs-create judgement, carry-forward. **D3 (owner):** a hall-made goal is created with a "Spec goal N" task (`POST /goals` `spec_task: true`, `source_ref goal-<id>-spec`) and every task in the goal depends on it until it ships; no new permission, no migration. **D4:** an optional "Plan with Claude" hand-off button.](0366-hall-planning-is-forms-and-a-goal-made-there-carries-its-own-spec-task.md) | lifecycle / planning |
488
+ | 0367 | [**The Discord support bot answers from public docs only, under a monthly cap that fails closed** ([task 1002534](https://cloudbongos.com/builders#/task/1002534), goal 1000110). **D1:** one pinned channel, only when @-mentioned; the first gateway reply. **D2:** the corpus is an allowlist (`README.md`, `docs/onboarding/`, `docs/api-reference.md`) served by a new `memory.searchPublicDocs` port whose scope (doc, xenos, no owner, allowlisted ref) is fixed in SQL; the asker is never looked up. **D3:** no writes but the reply and one `audit_log` row per answer, which is also the spend ledger, so no migration. **D4:** a nonce-fenced prompt, no tools, mentions disabled. **D5:** `claude-haiku-4-5`, `DISCORD_SUPPORT_MONTHLY_CAP_USD` default $5, fail closed, with in-flight answers reserved at worst case. **D6:** off by default; its own switch and an env-only, namespaced key. **D7:** the channel's humans review wrong answers; the fix is the doc.](0367-the-support-bot-answers-from-public-docs-only.md) | discord / support / spend |