@bongos/core 1.19.837 → 1.19.839
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 +62 -32
- package/config/branding.neutral.json +2 -1
- package/docs/adr/0302-the-third-identical-failure-stops-the-session-and-asks.md +74 -0
- package/docs/adr/README.md +1 -0
- package/docs/branding-contract.md +1 -1
- package/docs/branding-fill-prompt.md +1 -1
- package/docs/copy-inventory.md +3 -3
- package/docs/copy-registry.json +5 -5
- package/docs/file-map.md +2 -0
- package/docs/module-api-changelog.md +4 -0
- package/modules/agents/migrations/agents_003_output_ref_idx.sql +41 -0
- package/modules/agents/module.json +1 -1
- package/modules/agents/pollers/event-router.js +737 -0
- package/modules/discord/module.json +1 -1
- package/modules/discord/routes/discord.js +25 -0
- package/modules/status-ui/public/index.html +2 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/fitness-checks-identity.js +9 -1
- package/scripts/gds/fitness.js +34 -1
- package/scripts/gds/plain-cards.js +2 -0
- package/scripts/status-mirror-sync.js +6 -2
- package/src/bongos/serve-internal.js +9 -2
- package/src/branding.js +22 -2
- package/src/module-api.js +1 -1
- package/tests/agents_event_router.mjs +523 -0
- package/tests/fitness.mjs +47 -0
- package/tests/hooks_drain.mjs +1 -1
- package/tests/host_topology_skips.mjs +1 -0
- package/tests/status_company_attribution.mjs +60 -0
- package/tests/stuck_detector.mjs +233 -0
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.19.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.839",
|
|
6
|
+
"core_contract": "1.19.839",
|
|
7
|
+
"source_commit": "2799589e44782acf35629e14f332ac30686ed028",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-
|
|
9
|
+
"built_at": "2026-09-17T17:25:32.211Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 494,
|
|
13
13
|
"agent_docs_stubbed": 26,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 2258,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2778,
|
|
20
|
+
"tree_sha256": "209c682e3b1a302720d23b8e207501aba43d917dc24ebe0fe4d11836c49a6af8",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -382,7 +382,7 @@
|
|
|
382
382
|
{
|
|
383
383
|
"path": "config/branding.neutral.json",
|
|
384
384
|
"mode": "0000644",
|
|
385
|
-
"sha256": "
|
|
385
|
+
"sha256": "3c46f007c474b0fe300ad1c6e2c80237948a81a8ad726f15f74ec5ba7cf2b610"
|
|
386
386
|
},
|
|
387
387
|
{
|
|
388
388
|
"path": "config/design-tokens.neutral.json",
|
|
@@ -1994,10 +1994,15 @@
|
|
|
1994
1994
|
"mode": "0000644",
|
|
1995
1995
|
"sha256": "02f7e793bdf3665cf371f273dda6b84701918dc8fd031fac2893a19ed9f0bd9a"
|
|
1996
1996
|
},
|
|
1997
|
+
{
|
|
1998
|
+
"path": "docs/adr/0302-the-third-identical-failure-stops-the-session-and-asks.md",
|
|
1999
|
+
"mode": "0000644",
|
|
2000
|
+
"sha256": "08ea781a488d7454ca49b17e9540bb4c7bb9f1925aedf8ad4634057a89dd0d22"
|
|
2001
|
+
},
|
|
1997
2002
|
{
|
|
1998
2003
|
"path": "docs/adr/README.md",
|
|
1999
2004
|
"mode": "0000644",
|
|
2000
|
-
"sha256": "
|
|
2005
|
+
"sha256": "5c20d4bf4c0202a391a617d99daaa97d8a566d9d5c8534b5bd060faa8f8afe02"
|
|
2001
2006
|
},
|
|
2002
2007
|
{
|
|
2003
2008
|
"path": "docs/api-reference.md",
|
|
@@ -2017,12 +2022,12 @@
|
|
|
2017
2022
|
{
|
|
2018
2023
|
"path": "docs/branding-contract.md",
|
|
2019
2024
|
"mode": "0000644",
|
|
2020
|
-
"sha256": "
|
|
2025
|
+
"sha256": "78cb92eedefb10a2cd23b0dff8e2a6d5499295487372b7ef7da5619f99d4cdde"
|
|
2021
2026
|
},
|
|
2022
2027
|
{
|
|
2023
2028
|
"path": "docs/branding-fill-prompt.md",
|
|
2024
2029
|
"mode": "0000644",
|
|
2025
|
-
"sha256": "
|
|
2030
|
+
"sha256": "91e0fab10096223b9e99ba764330b61f5c64f754d0b2845a050057ddbe5a34e0"
|
|
2026
2031
|
},
|
|
2027
2032
|
{
|
|
2028
2033
|
"path": "docs/canonical-permissions.md",
|
|
@@ -2032,12 +2037,12 @@
|
|
|
2032
2037
|
{
|
|
2033
2038
|
"path": "docs/copy-inventory.md",
|
|
2034
2039
|
"mode": "0000644",
|
|
2035
|
-
"sha256": "
|
|
2040
|
+
"sha256": "e457040f6938ad12012fb8ceb1b59de65d43f2fca74849181f1e448979a4c811"
|
|
2036
2041
|
},
|
|
2037
2042
|
{
|
|
2038
2043
|
"path": "docs/copy-registry.json",
|
|
2039
2044
|
"mode": "0000644",
|
|
2040
|
-
"sha256": "
|
|
2045
|
+
"sha256": "77a0c2bb223c37af27b3dac3ca8864bad9b1bcb0bc6c326c96f40cec6f9ba95d"
|
|
2041
2046
|
},
|
|
2042
2047
|
{
|
|
2043
2048
|
"path": "docs/design/apex-pass-2-direction.md",
|
|
@@ -2552,7 +2557,7 @@
|
|
|
2552
2557
|
{
|
|
2553
2558
|
"path": "docs/file-map.md",
|
|
2554
2559
|
"mode": "0000644",
|
|
2555
|
-
"sha256": "
|
|
2560
|
+
"sha256": "b575f34143de0be30fadfd63bea76f77305fa175beee3960291414ff6b55d611"
|
|
2556
2561
|
},
|
|
2557
2562
|
{
|
|
2558
2563
|
"path": "docs/handoff-template.md",
|
|
@@ -2562,7 +2567,7 @@
|
|
|
2562
2567
|
{
|
|
2563
2568
|
"path": "docs/module-api-changelog.md",
|
|
2564
2569
|
"mode": "0000644",
|
|
2565
|
-
"sha256": "
|
|
2570
|
+
"sha256": "689361320ab6192aade8cb602c862af1f14454238575da829015077f55743628"
|
|
2566
2571
|
},
|
|
2567
2572
|
{
|
|
2568
2573
|
"path": "docs/modules-contract.md",
|
|
@@ -3709,10 +3714,20 @@
|
|
|
3709
3714
|
"mode": "0000644",
|
|
3710
3715
|
"sha256": "b576e8518bc8c9af9d5a1a86ace812f292fc2df1225cdfce8578d11703350391"
|
|
3711
3716
|
},
|
|
3717
|
+
{
|
|
3718
|
+
"path": "modules/agents/migrations/agents_003_output_ref_idx.sql",
|
|
3719
|
+
"mode": "0000644",
|
|
3720
|
+
"sha256": "7a9763f90da032c81aa0b1a8461a2e13b58d9f529c1b857b048648feeadc6fdd"
|
|
3721
|
+
},
|
|
3712
3722
|
{
|
|
3713
3723
|
"path": "modules/agents/module.json",
|
|
3714
3724
|
"mode": "0000644",
|
|
3715
|
-
"sha256": "
|
|
3725
|
+
"sha256": "4eea1864848b1217fef12070a981fda974d7105449d6a46bdf9ed04068b57c5e"
|
|
3726
|
+
},
|
|
3727
|
+
{
|
|
3728
|
+
"path": "modules/agents/pollers/event-router.js",
|
|
3729
|
+
"mode": "0000644",
|
|
3730
|
+
"sha256": "de64902d694acfc7d32aaf4d8629921f64cf851d600deb077a278b528d34c57d"
|
|
3716
3731
|
},
|
|
3717
3732
|
{
|
|
3718
3733
|
"path": "modules/agents/routes/agents.js",
|
|
@@ -4227,7 +4242,7 @@
|
|
|
4227
4242
|
{
|
|
4228
4243
|
"path": "modules/discord/module.json",
|
|
4229
4244
|
"mode": "0000644",
|
|
4230
|
-
"sha256": "
|
|
4245
|
+
"sha256": "437fa0e5174b2cbdd3c1949a3d048a8c8c09defce85afd04a27c0c635031db1b"
|
|
4231
4246
|
},
|
|
4232
4247
|
{
|
|
4233
4248
|
"path": "modules/discord/pollers/discord-bot.js",
|
|
@@ -4242,7 +4257,7 @@
|
|
|
4242
4257
|
{
|
|
4243
4258
|
"path": "modules/discord/routes/discord.js",
|
|
4244
4259
|
"mode": "0000644",
|
|
4245
|
-
"sha256": "
|
|
4260
|
+
"sha256": "b1316f763acef47e6f6a01b61d7d92868e54b5ded091919ad1d2bd8991f10ab8"
|
|
4246
4261
|
},
|
|
4247
4262
|
{
|
|
4248
4263
|
"path": "modules/discord/ship-broadcast.js",
|
|
@@ -7417,7 +7432,7 @@
|
|
|
7417
7432
|
{
|
|
7418
7433
|
"path": "modules/status-ui/public/index.html",
|
|
7419
7434
|
"mode": "0000644",
|
|
7420
|
-
"sha256": "
|
|
7435
|
+
"sha256": "25e98c2822bb8b3613edbe369ac374128e07bc6c8abf126f90a781e5e0238e8e"
|
|
7421
7436
|
},
|
|
7422
7437
|
{
|
|
7423
7438
|
"path": "modules/status-ui/public/snapshots/.gitkeep",
|
|
@@ -7852,12 +7867,12 @@
|
|
|
7852
7867
|
{
|
|
7853
7868
|
"path": "package-lock.json",
|
|
7854
7869
|
"mode": "0000644",
|
|
7855
|
-
"sha256": "
|
|
7870
|
+
"sha256": "fe22ff711989f39c34fa95045c0736e26abc2400d698548f353121fbd1ff8828"
|
|
7856
7871
|
},
|
|
7857
7872
|
{
|
|
7858
7873
|
"path": "package.json",
|
|
7859
7874
|
"mode": "0000644",
|
|
7860
|
-
"sha256": "
|
|
7875
|
+
"sha256": "f15fdfca04599ce879e85f8229aeba9019d17476e6020ebdec4ea03a98d4bd76"
|
|
7861
7876
|
},
|
|
7862
7877
|
{
|
|
7863
7878
|
"path": "public-docs/index.html",
|
|
@@ -8357,7 +8372,7 @@
|
|
|
8357
8372
|
{
|
|
8358
8373
|
"path": "scripts/gds/fitness-checks-identity.js",
|
|
8359
8374
|
"mode": "0000644",
|
|
8360
|
-
"sha256": "
|
|
8375
|
+
"sha256": "ae5589886edaa98d66d7bb4b918b47220628c82337535ffeaa2b5cb0c5e2adc0"
|
|
8361
8376
|
},
|
|
8362
8377
|
{
|
|
8363
8378
|
"path": "scripts/gds/fitness-checks-packaging.js",
|
|
@@ -8397,7 +8412,7 @@
|
|
|
8397
8412
|
{
|
|
8398
8413
|
"path": "scripts/gds/fitness.js",
|
|
8399
8414
|
"mode": "0000644",
|
|
8400
|
-
"sha256": "
|
|
8415
|
+
"sha256": "5f96e59cb4c27e20c6acaabf8f97b94dc69875b5fb5770fb37e4fb838128b189"
|
|
8401
8416
|
},
|
|
8402
8417
|
{
|
|
8403
8418
|
"path": "scripts/gds/gate-review.js",
|
|
@@ -8717,7 +8732,7 @@
|
|
|
8717
8732
|
{
|
|
8718
8733
|
"path": "scripts/gds/plain-cards.js",
|
|
8719
8734
|
"mode": "0000644",
|
|
8720
|
-
"sha256": "
|
|
8735
|
+
"sha256": "63344839b3cb2cba39757b5501759aaef8e1bf100468d7304a56d4e709f2f8cf"
|
|
8721
8736
|
},
|
|
8722
8737
|
{
|
|
8723
8738
|
"path": "scripts/gds/portability-smoke-art.sh",
|
|
@@ -9442,7 +9457,7 @@
|
|
|
9442
9457
|
{
|
|
9443
9458
|
"path": "scripts/status-mirror-sync.js",
|
|
9444
9459
|
"mode": "0000644",
|
|
9445
|
-
"sha256": "
|
|
9460
|
+
"sha256": "013860b6269302ca2b9db511cfdd3b915e9963bfcbd3d528dcd086cbe40f94a8"
|
|
9446
9461
|
},
|
|
9447
9462
|
{
|
|
9448
9463
|
"path": "scripts/status-probe.mjs",
|
|
@@ -9757,7 +9772,7 @@
|
|
|
9757
9772
|
{
|
|
9758
9773
|
"path": "src/bongos/serve-internal.js",
|
|
9759
9774
|
"mode": "0000644",
|
|
9760
|
-
"sha256": "
|
|
9775
|
+
"sha256": "fb6c9149b602356e3973b62c5042c373c92018e04242decd4a29c2c0897ddec3"
|
|
9761
9776
|
},
|
|
9762
9777
|
{
|
|
9763
9778
|
"path": "src/bongos/uptime-poller.js",
|
|
@@ -9767,7 +9782,7 @@
|
|
|
9767
9782
|
{
|
|
9768
9783
|
"path": "src/branding.js",
|
|
9769
9784
|
"mode": "0000644",
|
|
9770
|
-
"sha256": "
|
|
9785
|
+
"sha256": "086e80cd5effd41bcbada467b5538afa2d35f549b8efd90408d99e39ab2e29ec"
|
|
9771
9786
|
},
|
|
9772
9787
|
{
|
|
9773
9788
|
"path": "src/build-info.js",
|
|
@@ -9787,7 +9802,7 @@
|
|
|
9787
9802
|
{
|
|
9788
9803
|
"path": "src/module-api.js",
|
|
9789
9804
|
"mode": "0000644",
|
|
9790
|
-
"sha256": "
|
|
9805
|
+
"sha256": "027988bab5433e279e5b5cd479a768f655dd68bc1e7e19d31279d140be462d2a"
|
|
9791
9806
|
},
|
|
9792
9807
|
{
|
|
9793
9808
|
"path": "src/module-loader/catalog.js",
|
|
@@ -9924,6 +9939,11 @@
|
|
|
9924
9939
|
"mode": "0000644",
|
|
9925
9940
|
"sha256": "eea8f9e0bdf2c84fabc81e16c30e4026a7a336c1e705d614e68b805957b9b74a"
|
|
9926
9941
|
},
|
|
9942
|
+
{
|
|
9943
|
+
"path": "tests/agents_event_router.mjs",
|
|
9944
|
+
"mode": "0000644",
|
|
9945
|
+
"sha256": "919ca0ea3fe5f78a194781f6b634b9da23212073cf0c7cbe8ae36a65cc3fecdb"
|
|
9946
|
+
},
|
|
9927
9947
|
{
|
|
9928
9948
|
"path": "tests/agents_routes.mjs",
|
|
9929
9949
|
"mode": "0000644",
|
|
@@ -11012,7 +11032,7 @@
|
|
|
11012
11032
|
{
|
|
11013
11033
|
"path": "tests/fitness.mjs",
|
|
11014
11034
|
"mode": "0000644",
|
|
11015
|
-
"sha256": "
|
|
11035
|
+
"sha256": "7252f8134ae29b16df7c136865acb110263cc862781d7ae26ee416398caa1f09"
|
|
11016
11036
|
},
|
|
11017
11037
|
{
|
|
11018
11038
|
"path": "tests/fitness_gds_ratchet.mjs",
|
|
@@ -11682,12 +11702,12 @@
|
|
|
11682
11702
|
{
|
|
11683
11703
|
"path": "tests/hooks_drain.mjs",
|
|
11684
11704
|
"mode": "0000644",
|
|
11685
|
-
"sha256": "
|
|
11705
|
+
"sha256": "84ccd5167165bbf12aba03cdf444b0d94f1e7cc4b4dccbc37ed59afd01cf39cf"
|
|
11686
11706
|
},
|
|
11687
11707
|
{
|
|
11688
11708
|
"path": "tests/host_topology_skips.mjs",
|
|
11689
11709
|
"mode": "0000644",
|
|
11690
|
-
"sha256": "
|
|
11710
|
+
"sha256": "22ac76bc38e6b52262a2afb664b6f883066daaa2864ad71d971f7ff2a6995d28"
|
|
11691
11711
|
},
|
|
11692
11712
|
{
|
|
11693
11713
|
"path": "tests/hub_disconnect_ui.mjs",
|
|
@@ -13489,6 +13509,11 @@
|
|
|
13489
13509
|
"mode": "0000644",
|
|
13490
13510
|
"sha256": "369981934c65608fb4198c7840bd918c25caa3e5a1aa44b22fe38663e728e7f2"
|
|
13491
13511
|
},
|
|
13512
|
+
{
|
|
13513
|
+
"path": "tests/status_company_attribution.mjs",
|
|
13514
|
+
"mode": "0000644",
|
|
13515
|
+
"sha256": "4b92c96ae496736b0f44182e64d42a24f108cb51fbeb5cf7cd5814ad336b14f6"
|
|
13516
|
+
},
|
|
13492
13517
|
{
|
|
13493
13518
|
"path": "tests/status_mirror_sync.mjs",
|
|
13494
13519
|
"mode": "0000644",
|
|
@@ -13514,6 +13539,11 @@
|
|
|
13514
13539
|
"mode": "0000644",
|
|
13515
13540
|
"sha256": "eafd4354dc95fae94d720ed4cda05638eb5719c3ab0e3af084e7bf9d41b1be35"
|
|
13516
13541
|
},
|
|
13542
|
+
{
|
|
13543
|
+
"path": "tests/stuck_detector.mjs",
|
|
13544
|
+
"mode": "0000644",
|
|
13545
|
+
"sha256": "005839daeb067c0de9c93e92f9bfc733c031fe572bf383cb29ea8105b56b8cd3"
|
|
13546
|
+
},
|
|
13517
13547
|
{
|
|
13518
13548
|
"path": "tests/subdomain_auth.mjs",
|
|
13519
13549
|
"mode": "0000644",
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# ADR 0302 — The third identical failure stops the session and asks
|
|
2
|
+
|
|
3
|
+
**Status:** accepted · **Date:** 2026-09-17 · **Task:** [task 1003990](https://cloudbongos.com/builders#/task/1003990) (goal 1000095 — BONGOS-V2)
|
|
4
|
+
|
|
5
|
+
**Related:** [ADR 0301](0301-a-red-pr-names-its-failing-tests-in-a-commit-status.md) (a red PR names its failing tests) · [ADR 0291](0291-the-accessibility-dimension-scores-the-transcript-not-the-diff.md) (accessibility is scored after the ship) · [ADR 0162](0162-review-gates-retired-rank-consistent-ci-only.md) (no new gates on the ship path) · [ADR 0043](0043-git-ssh-trust-boundary-and-rank-floor-on-permission-paths.md) (cooperative, fail-open hook layer)
|
|
6
|
+
|
|
7
|
+
## Context
|
|
8
|
+
|
|
9
|
+
The 2026-09-17 investigation of a non-technical builder's stalls found sessions that looped
|
|
10
|
+
without noticing: one pull request re-shipped into the same red check **nine times in four
|
|
11
|
+
hours**, and **45 of 169 claim attempts** in 4.5 days were refusals — the same task id retried
|
|
12
|
+
minutes apart. Each retry costs money the builder watches tick up, and they have no way to tell
|
|
13
|
+
that the agent is going round in a circle (idea 1000281).
|
|
14
|
+
|
|
15
|
+
Abandoned task 1002828 asked for a written bail-out rule. A rule in a memory file loses to the
|
|
16
|
+
next retry for the same reason the `git stash` ban did: the agent is not looking at it at the
|
|
17
|
+
moment it matters. Nothing else watches a session mid-flight — the accessibility score is
|
|
18
|
+
computed after the ship.
|
|
19
|
+
|
|
20
|
+
## Decision
|
|
21
|
+
|
|
22
|
+
1. **Count walls, per session, in a hook.** `.claude/hooks/stuck-detector.js` runs after every
|
|
23
|
+
shell tool call (`PostToolUse` and `PostToolUseFailure`, `Bash|PowerShell`). On the **third**
|
|
24
|
+
identical wall — and each one after — it injects one instruction: stop retrying, diagnose
|
|
25
|
+
(the path is named), or run `/ask-for-help`.
|
|
26
|
+
2. **The failure signature is the `[otb-plain]` card** (task 1003989). Every failure a builder
|
|
27
|
+
can be parked behind already prints one; its "What happened" line is static per failure code,
|
|
28
|
+
and its "By design" line already says whether a retry can ever help, which picks the
|
|
29
|
+
instruction's wording. Signature = that line + the task id in the command + the
|
|
30
|
+
`the PR is RED` line when present, so a red check that names *different* failing tests each
|
|
31
|
+
run reads as progress, not a wall. Only a builder CLI **run** (`node …scripts/gds/*.js`,
|
|
32
|
+
`bongos <verb>`) is read: `cat` or `git diff` over a file that quotes a card prints the
|
|
33
|
+
same text and is not a failure.
|
|
34
|
+
3. **A waiting card is not a wall.** Cards whose next step is "Nothing to do…" (`land_pending`,
|
|
35
|
+
a pending check, a pending join request) tell the session to poll; counting them would turn
|
|
36
|
+
the detector against the behaviour the card asks for. The set is pinned by test.
|
|
37
|
+
4. **The guards count through the same helper.** `git-guard.js` and `worktree-guard.js` call
|
|
38
|
+
`stuck-strikes.js` when they block — keyed on the rule, and on the file, respectively — and
|
|
39
|
+
append the instruction to the reason they already return. Three *different* wrong-tree files,
|
|
40
|
+
each corrected on the next call, is the guard working and is not counted.
|
|
41
|
+
5. **It never blocks, and it echoes nothing.** `additionalContext` only, exit 0, silent without
|
|
42
|
+
a card. Signatures are hashed before they touch `os.tmpdir()`; the instruction interpolates a
|
|
43
|
+
count and a digits-only task id, never text a tool printed.
|
|
44
|
+
|
|
45
|
+
## Alternatives rejected
|
|
46
|
+
|
|
47
|
+
- **Count any repeated failing command.** Red → green on a test *is* the same command failing
|
|
48
|
+
repeatedly; the detector would fire on ordinary work and become wallpaper.
|
|
49
|
+
- **Block the fourth attempt.** That is a new gate on the ship path (ADR 0162), and a wrong
|
|
50
|
+
signature match would strand a builder behind the tool meant to unstick them.
|
|
51
|
+
- **Server-side detection.** The server sees refused claims but not guard blocks or local red
|
|
52
|
+
runs, and cannot speak into the session at the moment of the retry.
|
|
53
|
+
- **Require `scripts/gds/plain-cards.js` from the hook.** Hooks are instance topology and the
|
|
54
|
+
script may live in the installed package; reading the printed card keeps the hook
|
|
55
|
+
dependency-free. The cost is a text coupling to the card's layout, pinned by test.
|
|
56
|
+
|
|
57
|
+
## Consequences
|
|
58
|
+
|
|
59
|
+
- Whether a non-zero exit routes to `PostToolUse` or `PostToolUseFailure` is the harness's
|
|
60
|
+
business; the hook is wired on both and reads the output from any of the four fields either
|
|
61
|
+
event has used. Only one event fires per call, so nothing is counted twice.
|
|
62
|
+
- The count is per session and lives in the OS temp dir as an **append-only** log of hashed
|
|
63
|
+
signatures — tool calls run in parallel and three hooks share the file, so a
|
|
64
|
+
read-modify-write would let one process wipe another's counts. A builder who opens a fresh
|
|
65
|
+
session after being told to stop starts from zero — acceptable: the instruction's job is to
|
|
66
|
+
break the loop inside one context, and the refusal itself still stands.
|
|
67
|
+
- Strikes do not decay inside a session: two hits, real progress, then a third hit hours later
|
|
68
|
+
still draws the instruction. It costs one paragraph the agent can weigh, and a time window
|
|
69
|
+
short enough to matter would have missed PR 922's runs, which were ~27 minutes apart.
|
|
70
|
+
- A box that cannot read which test failed gets a RED line with no names, so three red runs
|
|
71
|
+
share a signature whatever their causes. That is the PR 922 case the detector exists for;
|
|
72
|
+
the instruction says to run the suite locally when the status names nothing.
|
|
73
|
+
- A failure with no plain card is invisible to the detector. `tests/plain_cards.mjs` already
|
|
74
|
+
reds when a CLI gains a failure code without one, so the two stay in step.
|
package/docs/adr/README.md
CHANGED
|
@@ -393,3 +393,4 @@ This keeps the decision history honest and traceable.
|
|
|
393
393
|
| 0299 | [**The nod queue stays scoped to routed filings; the backlog walk is the widening** ([task 1003748](https://cloudbongos.com/builders#/task/1003748), goal 1000089). `listRoutedAwaitingNod` filters `origin='route'`, and on 2026-09-08 showed 11 of 232 backlog rows. Re-measured after `/backlog-review` (task 1003746) shipped: 115 backlog rows = 109 dep-gated + 2 spikes (not a person's decision) + 1 stranded + 3 awaiting a nod, all four walked by `/backlog-review`. **Decision: do not widen the card.** The omitted population already has a walkable review with the dependency predicate any widening needed; the copy says *reported* work, so the name is honest; a card slice would be a second copy of the walk's four-bucket partition. Revisit if the un-routed nod bucket outgrows a daily walk (then a count + link, derived from the same partition) or if the walk stops being run.](0299-the-nod-queue-stays-scoped-to-routed-filings.md) | ideas / backlog / attention |
|
|
394
394
|
| 0300 | [**A missing protected-surface registry is a broken install, not a module opt-out** ([task 1003662](https://cloudbongos.com/builders#/task/1003662), goal 1000091). Filed as a bug with a remedy attached: `require('./module-scope-map')` throws when `modules/government/protected-surfaces.json` is absent, so make it degrade to an empty protected set + a warning, matching `resolveOptional`. The throw reproduces, and the rest is wrong four times over. **The throw is not in the named file** — `module-scope-map.js` has no throw and no file read; it requires `permission-path-check.js`, whose `loadRegistry()` throws at module scope. **It is a written contract, not an oversight** — that file's header says FAIL-CLOSED in capitals and already pre-answers the layering worry (the read is `fs`, never `require`, so ADR 0083's one-way rule holds; static data, not code; `route-rank-check.js` reads `modules/*/routes/*.js` the same way). **Disabling the module cannot cause it** — enablement is a runtime flag, never a file deletion, and the read never consults it. **"Not yet materialized" cannot either** — no `files[]` allowlist, so `npm pack --dry-run` puts the 13,274-byte registry in the tarball; per ADR 0108 §1 you cannot have `@bongos/core` without it. The `resolveOptional` precedent does not transfer: that is the module **seam/port** resolver for a missing runtime *capability*, whereas this is the static data the boundary is made of, read where there is no server and no DB (a git hook, a CI job) — borrowing it converts "the boundary is unreadable" into "there is no boundary". **Decision: fail-closed stays, no behavior change, the remedy is rejected — and the one genuine defect is fixed: the message now names the cause and the fix** (reinstall the core; do not empty the set), because stating a refusal without a remedy is exactly how a reader concludes the module is optional and files this task. The asymmetry is the point: a false "nothing is protected" is silent, permanent and grants authority; a false "this install is broken" is loud and grants nothing. Revisit only if a core-only checkout without `modules/` becomes legitimate — and then the question is *where the registry lives*, not whether it is optional.](0300-a-missing-protected-surface-registry-is-a-broken-install.md) | governance / permissions |
|
|
395
395
|
| 0301 | [**A red PR names its failing tests in a commit status, and says so when it cannot see** ([task 1003988](https://cloudbongos.com/builders#/task/1003988), goal 1000095). One pull request took nine red CI runs in four hours because the builder box could not learn which test failed: without Checks:read GitHub nulls every check-run node, the server dropped the holes silently, and the red PR was reported as a review-or-pending wait. Now the merge state carries checks_unreadable and a red, unreadable rollup reads failing_check_unreadable; the unit workflow posts the failing test names as a unit-report commit STATUS (readable without the permission) from a separate job that holds the only write token and checks out no PR code; the server returns it as failing_tests, reduced to test-list vocabulary because the PR controls the workflow that wrote it.](0301-a-red-pr-names-its-failing-tests-in-a-commit-status.md) | GDS / shipping pipeline |
|
|
396
|
+
| 0302 | [**The third identical failure stops the session and asks** ([task 1003990](https://cloudbongos.com/builders#/task/1003990), goal 1000095). Sessions looped without noticing: one PR re-shipped into the same red check nine times, and 45 of 169 claim attempts were the same refusal retried minutes apart. A PostToolUse hook now counts walls per session — the signature is the `[otb-plain]` card's "What happened" line + the task id + the RED line, so a red check naming different failing tests each run reads as progress — and on the third identical one injects a single instruction: stop retrying, diagnose (path named), or /ask-for-help. Waiting cards ("Nothing to do…") are never counted; git-guard and worktree-guard count their own blocks through the same helper; it never blocks a tool call and echoes nothing a tool printed. Rejected: counting any repeated failing command (red→green is that), blocking the fourth attempt (a new gate, ADR 0162), server-side detection (cannot see guard blocks or speak into the session).](0302-the-third-identical-failure-stops-the-session-and-asks.md) | GDS / sessions / hooks |
|
|
@@ -21,7 +21,7 @@ env override → instance config (config/branding.json) → neutral star
|
|
|
21
21
|
|---|---|
|
|
22
22
|
| `identity.productName` | The product name (e.g. "Example"). **Required.** |
|
|
23
23
|
| `identity.worldName` | The in-world / civilization name rendered at runtime (e.g. "Example"). **Required.** |
|
|
24
|
-
| `identity.company` | `{ name, site }` of the operating company
|
|
24
|
+
| `identity.company` | `{ name, site, tagline }` of the operating company — every field may be empty. `tagline` is the free-text clause that follows the name in the status page footer ("{{worldName}} is the work of **name**, *tagline*."), so put a location there if you want one; there is no separate location field. The footer stamps them as `{{companyName}}` / `{{companySite}}` / `{{companyTagline}}`, and each clause sits in a `<!--brand-if:KEY-->…<!--/brand-if:KEY-->` region that drops whole when its field is empty: no name hides the whole sentence, no site renders the name unlinked, no tagline ends the sentence at the name. (task [#1002853](https://cloudbongos.com/builders#/task/1002853)) |
|
|
25
25
|
| `llmProjectName` | The project name injected into grader / architect / onboarding **LLM prompts**, so a non-OTB instance is graded against its own identity. **Required.** |
|
|
26
26
|
| `domains.publicOrigin` | Public game/app origin. **Required.** |
|
|
27
27
|
| `domains.buildersOrigin` / `statusOrigin` | Hall + status origins. **These three (`publicOrigin`/`buildersOrigin`/`statusOrigin`) are the docs-discovery contract** ([ADR 0114](adr/0114-instance-docs-discovery-contract.md)): the hall is the canonical doc-reading surface, `/docs` on `publicOrigin` renders the API reference, the status page is the outage fallback. `GET <publicOrigin>/api/bongos` re-advertises them (`hall`/`status`/`public`) so an agent resolves the surfaces with no hard-coded host. |
|
|
@@ -8,7 +8,7 @@ Paste the block below into your own LLM (Claude, etc.). It interviews you and em
|
|
|
8
8
|
>
|
|
9
9
|
> Interview me for:
|
|
10
10
|
> 1. **Product name** and the **in-world / display name** (can be the same).
|
|
11
|
-
> 2. **Operating company** name + website (optional).
|
|
11
|
+
> 2. **Operating company** name + website + a one-line descriptor such as "a rocket works in Dayton, Ohio" (all optional; `identity.company.tagline`).
|
|
12
12
|
> 3. **Domains:** public origin, hall origin, status origin, the OAuth sign-in origin, and the session cookie domain (use `""` for a host-only cookie unless you run subdomains — never widen it casually).
|
|
13
13
|
> 4. **GitHub repo** (`owner`, `name`) this instance ships to.
|
|
14
14
|
> 5. **Reward currency label** (the unit builders earn; the math is fixed — this is display only).
|
package/docs/copy-inventory.md
CHANGED
|
@@ -1472,7 +1472,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1472
1472
|
| id | conf | text | where |
|
|
1473
1473
|
|---|---|---|---|
|
|
1474
1474
|
| `33aaf5eb47b2` | certain | ${…} of ${…} spent | `modules/status-ui/public/status.js:222` |
|
|
1475
|
-
| `
|
|
1475
|
+
| `d31b6094f7ac` | certain | , {{companyTagline}} | `modules/status-ui/public/index.html:356` |
|
|
1476
1476
|
| `2045078fb115` | certain | 90 days ago | `modules/status-ui/public/status.js:477` |
|
|
1477
1477
|
| `e4848e2d9386` | certain | A new portrait is set down each time the city's stones are reshaped. Scroll to see how {{worldName}} has grown | `modules/status-ui/public/index.html:344` |
|
|
1478
1478
|
| `390faeaffb47` | certain | A project | `modules/status-ui/public/explore.js:71` |
|
|
@@ -1560,10 +1560,10 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1560
1560
|
| `b97f429c33fe` | certain | works shipped (90d) | `modules/status-ui/public/index.html:218` |
|
|
1561
1561
|
| `71a303e1ba09` | certain | works wrought, last 7 days | `modules/status-ui/public/index.html:167` |
|
|
1562
1562
|
| `24ea04f49b3d` | certain | year's purse | `modules/status-ui/public/index.html:259` |
|
|
1563
|
-
| `4c45bbf03088` | certain | {{Currency}} are wrought by the lawmen's review; redemption awaits their seal. | `modules/status-ui/public/index.html:
|
|
1563
|
+
| `4c45bbf03088` | certain | {{Currency}} are wrought by the lawmen's review; redemption awaits their seal. | `modules/status-ui/public/index.html:357` |
|
|
1564
1564
|
| `89aa84ef4595` | certain | {{Currency}} spent, against the value wrought. | `modules/status-ui/public/index.html:245` |
|
|
1565
1565
|
| `35872b9ecc49` | certain | {{currency}} spent | `modules/status-ui/public/index.html:251` |
|
|
1566
|
-
| `e87f28d25342` | certain | {{worldName}} is the work of | `modules/status-ui/public/index.html:
|
|
1566
|
+
| `e87f28d25342` | certain | {{worldName}} is the work of | `modules/status-ui/public/index.html:356` |
|
|
1567
1567
|
| `7a15e19adbd2` | certain | {{worldName}} — the public ledger | `modules/status-ui/public/index.html:6` |
|
|
1568
1568
|
| `7a15e19adbd2` | certain | {{worldName}} — the public ledger | `modules/status-ui/public/index.html:6` |
|
|
1569
1569
|
| `12e0efd49a32` | certain | {…} goal{…} · {…} achieved | `modules/status-ui/public/status.js:582` |
|
package/docs/copy-registry.json
CHANGED
|
@@ -17802,11 +17802,11 @@
|
|
|
17802
17802
|
"confidence": "certain"
|
|
17803
17803
|
},
|
|
17804
17804
|
{
|
|
17805
|
-
"id": "
|
|
17805
|
+
"id": "d31b6094f7ac",
|
|
17806
17806
|
"surface": "status-dashboard",
|
|
17807
|
-
"text": ",
|
|
17807
|
+
"text": ", {{companyTagline}}",
|
|
17808
17808
|
"file": "modules/status-ui/public/index.html",
|
|
17809
|
-
"line":
|
|
17809
|
+
"line": 356,
|
|
17810
17810
|
"origin": "html-text",
|
|
17811
17811
|
"confidence": "certain"
|
|
17812
17812
|
},
|
|
@@ -18598,7 +18598,7 @@
|
|
|
18598
18598
|
"surface": "status-dashboard",
|
|
18599
18599
|
"text": "{{Currency}} are wrought by the lawmen's review; redemption awaits their seal.",
|
|
18600
18600
|
"file": "modules/status-ui/public/index.html",
|
|
18601
|
-
"line":
|
|
18601
|
+
"line": 357,
|
|
18602
18602
|
"origin": "html-text",
|
|
18603
18603
|
"confidence": "certain"
|
|
18604
18604
|
},
|
|
@@ -18625,7 +18625,7 @@
|
|
|
18625
18625
|
"surface": "status-dashboard",
|
|
18626
18626
|
"text": "{{worldName}} is the work of",
|
|
18627
18627
|
"file": "modules/status-ui/public/index.html",
|
|
18628
|
-
"line":
|
|
18628
|
+
"line": 356,
|
|
18629
18629
|
"origin": "html-text",
|
|
18630
18630
|
"confidence": "certain"
|
|
18631
18631
|
},
|
package/docs/file-map.md
CHANGED
|
@@ -490,6 +490,8 @@ To install on a new machine, symlink each routine's directory under `~/.claude/s
|
|
|
490
490
|
├── conductor.js ← UserPromptSubmit: MAS Conductor advisory + dispatch directive (#427/#439); self-gates on OTB_CONDUCTOR_OFF / OTB_SUBAGENT
|
|
491
491
|
├── session-cards.js ← UserPromptSubmit: deterministic delivery of READ-ONLY in-session cards (builder-start, status) — pre-renders the card + injects a one-step render directive (task 1288)
|
|
492
492
|
├── lifecycle-card.js ← PostToolUse: deterministic delivery of MUTATING lifecycle cards (claim/ship/release) — the write-side mirror of session-cards.js (task 1288)
|
|
493
|
+
├── stuck-detector.js ← PostToolUse + PostToolUseFailure (Bash|PowerShell): the third time one session hits the same wall — the same `[otb-plain]` card for the same task id, or the same red check — it injects ONE instruction: stop retrying, diagnose (path named), or /ask-for-help. Never blocks; silent with no card; kill-switch BONGOS_STUCK_DETECTOR_OFF=1 (task 1003990)
|
|
494
|
+
├── stuck-strikes.js ← shared helper, not a hook: the per-session strike counter (hashed signatures in os.tmpdir()) + the static instruction text; stuck-detector.js, git-guard.js and worktree-guard.js all count through it (task 1003990)
|
|
493
495
|
├── pre-compact.js ← PreCompact: thin reader around eviction-policy.js — emits an eviction directive (blocks to drop + one-line recovery stubs) at the compaction seam (task 1349)
|
|
494
496
|
├── eviction-policy.js ← pure deterministic in-session compaction classifier (transcript turns → eviction set + replacement stubs); no I/O, unit-tested in tests/eviction_policy.mjs; consumed by pre-compact.js (task 1348)
|
|
495
497
|
└── stop-refcheck.js ← Stop: deterministic backstop that catches a bare `#NNN` Bongos ref in live session text (autolinks to a 404 GitHub issue) and asks for the clickable form (follow-on to task 929)
|
|
@@ -2148,5 +2148,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
2148
2148
|
landed since 1.19.835 with no explicit bump. run 35247495964. (task 1002620)
|
|
2149
2149
|
1.19.837 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2150
2150
|
landed since 1.19.836 with no explicit bump. run 35248928789. (task 1002620)
|
|
2151
|
+
1.19.838 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2152
|
+
landed since 1.19.837 with no explicit bump. run 35250225228. (task 1002620)
|
|
2153
|
+
1.19.839 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
2154
|
+
landed since 1.19.838 with no explicit bump. run 35252591179. (task 1002620)
|
|
2151
2155
|
---------------------------------------------------------------------------
|
|
2152
2156
|
```
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
-- agents_003_output_ref_idx.sql — the index the DEPTH GUARD reads (task 1002494).
|
|
2
|
+
--
|
|
3
|
+
-- agents_001 gave `agents_runs` three indexes, each built for a question someone
|
|
4
|
+
-- was already asking: per-agent history, walk-forward from a trigger, and the
|
|
5
|
+
-- monthly spend rollup. `output_ref` was not one of them, because until now
|
|
6
|
+
-- nothing read it as a KEY — it was a pointer you followed after you already had
|
|
7
|
+
-- the row.
|
|
8
|
+
--
|
|
9
|
+
-- The event-router reads it the other way round. Its loop guard asks "does any
|
|
10
|
+
-- run's output_ref name this ship?" (`WHERE output_ref = ANY($1::text[])`) on
|
|
11
|
+
-- EVERY fan-out that gets past the touches[] pre-filter — and `agents_runs` is
|
|
12
|
+
-- append-only, growing with every fire AND every refusal, so without this the
|
|
13
|
+
-- guard is a sequential scan over a table that only ever gets longer. The guard
|
|
14
|
+
-- is also FAIL-CLOSED: a read slow enough to time out reads as "attributed" and
|
|
15
|
+
-- silently stops agents firing at all, which is the failure mode that looks like
|
|
16
|
+
-- nothing is wrong.
|
|
17
|
+
--
|
|
18
|
+
-- PARTIAL, on `output_ref IS NOT NULL`. Most rows have none — every no-go is a
|
|
19
|
+
-- row (a refusal that leaves no trace is indistinguishable from a fire that
|
|
20
|
+
-- never happened), and a successful fire with nothing to report writes none
|
|
21
|
+
-- either, which is the outcome most fires should have. The lookup only ever
|
|
22
|
+
-- matches a non-null value, so the nulls are dead weight in the index exactly as
|
|
23
|
+
-- agents_001's own spend index argues about its own nulls.
|
|
24
|
+
--
|
|
25
|
+
-- NOT UNIQUE, deliberately. Two agents may each report on the same ship, and the
|
|
26
|
+
-- `discord` sink's pointer is `discord:<channel>:<taskId>` — stable by
|
|
27
|
+
-- construction, so a re-post of the same subject collides by design. A unique
|
|
28
|
+
-- index would turn a legitimate second observation into an insert failure inside
|
|
29
|
+
-- a fire that already cost money.
|
|
30
|
+
--
|
|
31
|
+
-- Module migrations are ADDITIVE and NAMESPACED (ADR 0083 §Decision #5): the
|
|
32
|
+
-- object is agents_-prefixed, there is no down-migration, and the filename is
|
|
33
|
+
-- agents_-prefixed (ADR 0108 §5). Idempotent: safe to re-run.
|
|
34
|
+
|
|
35
|
+
BEGIN;
|
|
36
|
+
|
|
37
|
+
CREATE INDEX IF NOT EXISTS agents_runs_output_ref_idx
|
|
38
|
+
ON agents_runs (output_ref)
|
|
39
|
+
WHERE output_ref IS NOT NULL;
|
|
40
|
+
|
|
41
|
+
COMMIT;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"coreVersion": "^1.19.0",
|
|
6
6
|
"default": false,
|
|
7
7
|
"maintenance": { "status": "core-maintained" },
|
|
8
|
-
"contributes": { "routes": ["agents"], "migrations": true },
|
|
8
|
+
"contributes": { "routes": ["agents"], "pollers": ["event-router"], "migrations": true },
|
|
9
9
|
"spend": { "requiresPayer": true },
|
|
10
10
|
"provides": [],
|
|
11
11
|
"consumes": ["grade"]
|