@bongos/core 1.19.495 → 1.19.497
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 +45 -25
- package/.claude/skills/planning-session/SKILL.md +3 -1
- package/docs/copy-inventory.md +39 -36
- package/docs/copy-registry.json +69 -42
- package/docs/file-map.md +1 -1
- package/docs/module-api-changelog.md +4 -0
- package/modules/discord/discord-inbound.js +73 -1
- package/modules/hall-ui/public/task.js +87 -3
- package/modules/ideas/inbox.js +8 -1
- package/modules/lifecycle/goal-advisory.js +9 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/scripts/gds/capture.js +151 -2
- package/scripts/gds/seed-bongos-v1-idea-routing-tasks.js +25 -1
- package/scripts/gds/seed-verify-criteria.js +69 -0
- package/src/module-api.js +1 -1
- package/tests/capture_goal_routing.mjs +242 -0
- package/tests/discord_inbound.mjs +89 -1
- package/tests/goal_advisory.mjs +28 -0
- package/tests/hall_record_world.mjs +71 -3
- package/tests/idea_routing.mjs +33 -0
- package/tests/planning_session_skill.mjs +57 -0
- package/tests/seed_verify_criteria.mjs +149 -0
package/docs/copy-registry.json
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
"generator": "scripts/gds/copy-inventory.js",
|
|
4
4
|
"goal": 1000074,
|
|
5
5
|
"counts": {
|
|
6
|
-
"strings":
|
|
7
|
-
"unique_ids":
|
|
8
|
-
"certain":
|
|
6
|
+
"strings": 1615,
|
|
7
|
+
"unique_ids": 1406,
|
|
8
|
+
"certain": 1615,
|
|
9
9
|
"heuristic": 0,
|
|
10
10
|
"duplicate_ids": 150
|
|
11
11
|
},
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"builders-hall": {
|
|
14
14
|
"at": "modules/hall-ui/public",
|
|
15
15
|
"files": 73,
|
|
16
|
-
"strings":
|
|
16
|
+
"strings": 1172
|
|
17
17
|
},
|
|
18
18
|
"status-dashboard": {
|
|
19
19
|
"at": "modules/status-ui/public",
|
|
@@ -234,10 +234,10 @@
|
|
|
234
234
|
"text": "Task {…}",
|
|
235
235
|
"occurrences": 4,
|
|
236
236
|
"at": [
|
|
237
|
-
"modules/hall-ui/public/task.js:
|
|
238
|
-
"modules/hall-ui/public/task.js:
|
|
239
|
-
"modules/hall-ui/public/task.js:
|
|
240
|
-
"modules/hall-ui/public/task.js:
|
|
237
|
+
"modules/hall-ui/public/task.js:454",
|
|
238
|
+
"modules/hall-ui/public/task.js:463",
|
|
239
|
+
"modules/hall-ui/public/task.js:691",
|
|
240
|
+
"modules/hall-ui/public/task.js:703"
|
|
241
241
|
]
|
|
242
242
|
},
|
|
243
243
|
{
|
|
@@ -315,8 +315,8 @@
|
|
|
315
315
|
"text": "Copy prompt",
|
|
316
316
|
"occurrences": 3,
|
|
317
317
|
"at": [
|
|
318
|
-
"modules/hall-ui/public/task.js:
|
|
319
|
-
"modules/hall-ui/public/task.js:
|
|
318
|
+
"modules/hall-ui/public/task.js:617",
|
|
319
|
+
"modules/hall-ui/public/task.js:637",
|
|
320
320
|
"modules/hall-ui/public/work.js:548"
|
|
321
321
|
]
|
|
322
322
|
},
|
|
@@ -473,8 +473,8 @@
|
|
|
473
473
|
"text": "Back to {…}",
|
|
474
474
|
"occurrences": 2,
|
|
475
475
|
"at": [
|
|
476
|
-
"modules/hall-ui/public/task.js:
|
|
477
|
-
"modules/hall-ui/public/task.js:
|
|
476
|
+
"modules/hall-ui/public/task.js:188",
|
|
477
|
+
"modules/hall-ui/public/task.js:197"
|
|
478
478
|
]
|
|
479
479
|
},
|
|
480
480
|
{
|
|
@@ -2333,7 +2333,7 @@
|
|
|
2333
2333
|
"surface": "builders-hall",
|
|
2334
2334
|
"text": "Awaiting the grader, or a confirmation, before it can ship.",
|
|
2335
2335
|
"file": "modules/hall-ui/public/task.js",
|
|
2336
|
-
"line":
|
|
2336
|
+
"line": 607,
|
|
2337
2337
|
"origin": "js-markup",
|
|
2338
2338
|
"confidence": "certain"
|
|
2339
2339
|
},
|
|
@@ -2405,7 +2405,7 @@
|
|
|
2405
2405
|
"surface": "builders-hall",
|
|
2406
2406
|
"text": "Back to {…}",
|
|
2407
2407
|
"file": "modules/hall-ui/public/task.js",
|
|
2408
|
-
"line":
|
|
2408
|
+
"line": 188,
|
|
2409
2409
|
"origin": "js-markup",
|
|
2410
2410
|
"confidence": "certain"
|
|
2411
2411
|
},
|
|
@@ -2414,7 +2414,7 @@
|
|
|
2414
2414
|
"surface": "builders-hall",
|
|
2415
2415
|
"text": "Back to {…}",
|
|
2416
2416
|
"file": "modules/hall-ui/public/task.js",
|
|
2417
|
-
"line":
|
|
2417
|
+
"line": 197,
|
|
2418
2418
|
"origin": "js-text-assign",
|
|
2419
2419
|
"confidence": "certain"
|
|
2420
2420
|
},
|
|
@@ -2706,6 +2706,15 @@
|
|
|
2706
2706
|
"origin": "js-text-assign",
|
|
2707
2707
|
"confidence": "certain"
|
|
2708
2708
|
},
|
|
2709
|
+
{
|
|
2710
|
+
"id": "b3d0e2af5176",
|
|
2711
|
+
"surface": "builders-hall",
|
|
2712
|
+
"text": "Choose a goal first.",
|
|
2713
|
+
"file": "modules/hall-ui/public/task.js",
|
|
2714
|
+
"line": 666,
|
|
2715
|
+
"origin": "js-notice",
|
|
2716
|
+
"confidence": "certain"
|
|
2717
|
+
},
|
|
2709
2718
|
{
|
|
2710
2719
|
"id": "1c6b0e3ffc0e",
|
|
2711
2720
|
"surface": "builders-hall",
|
|
@@ -2999,7 +3008,7 @@
|
|
|
2999
3008
|
"surface": "builders-hall",
|
|
3000
3009
|
"text": "Confirm that verification for #{…} actually passed?",
|
|
3001
3010
|
"file": "modules/hall-ui/public/task.js",
|
|
3002
|
-
"line":
|
|
3011
|
+
"line": 652,
|
|
3003
3012
|
"origin": "js-notice",
|
|
3004
3013
|
"confidence": "certain"
|
|
3005
3014
|
},
|
|
@@ -3125,7 +3134,7 @@
|
|
|
3125
3134
|
"surface": "builders-hall",
|
|
3126
3135
|
"text": "Copy prompt",
|
|
3127
3136
|
"file": "modules/hall-ui/public/task.js",
|
|
3128
|
-
"line":
|
|
3137
|
+
"line": 617,
|
|
3129
3138
|
"origin": "js-markup",
|
|
3130
3139
|
"confidence": "certain"
|
|
3131
3140
|
},
|
|
@@ -3134,7 +3143,7 @@
|
|
|
3134
3143
|
"surface": "builders-hall",
|
|
3135
3144
|
"text": "Copy prompt",
|
|
3136
3145
|
"file": "modules/hall-ui/public/task.js",
|
|
3137
|
-
"line":
|
|
3146
|
+
"line": 637,
|
|
3138
3147
|
"origin": "js-text-assign",
|
|
3139
3148
|
"confidence": "certain"
|
|
3140
3149
|
},
|
|
@@ -3359,7 +3368,7 @@
|
|
|
3359
3368
|
"surface": "builders-hall",
|
|
3360
3369
|
"text": "Could not load",
|
|
3361
3370
|
"file": "modules/hall-ui/public/task.js",
|
|
3362
|
-
"line":
|
|
3371
|
+
"line": 710,
|
|
3363
3372
|
"origin": "js-text-assign",
|
|
3364
3373
|
"confidence": "certain"
|
|
3365
3374
|
},
|
|
@@ -3503,7 +3512,7 @@
|
|
|
3503
3512
|
"surface": "builders-hall",
|
|
3504
3513
|
"text": "Could not load {…}",
|
|
3505
3514
|
"file": "modules/hall-ui/public/task.js",
|
|
3506
|
-
"line":
|
|
3515
|
+
"line": 703,
|
|
3507
3516
|
"origin": "js-markup",
|
|
3508
3517
|
"confidence": "certain"
|
|
3509
3518
|
},
|
|
@@ -4610,7 +4619,7 @@
|
|
|
4610
4619
|
"surface": "builders-hall",
|
|
4611
4620
|
"text": "Full log",
|
|
4612
4621
|
"file": "modules/hall-ui/public/task.js",
|
|
4613
|
-
"line":
|
|
4622
|
+
"line": 443,
|
|
4614
4623
|
"origin": "js-markup",
|
|
4615
4624
|
"confidence": "certain"
|
|
4616
4625
|
},
|
|
@@ -5411,7 +5420,7 @@
|
|
|
5411
5420
|
"surface": "builders-hall",
|
|
5412
5421
|
"text": "Last claimed by",
|
|
5413
5422
|
"file": "modules/hall-ui/public/task.js",
|
|
5414
|
-
"line":
|
|
5423
|
+
"line": 549,
|
|
5415
5424
|
"origin": "js-markup",
|
|
5416
5425
|
"confidence": "certain"
|
|
5417
5426
|
},
|
|
@@ -5847,6 +5856,15 @@
|
|
|
5847
5856
|
"origin": "html-text",
|
|
5848
5857
|
"confidence": "certain"
|
|
5849
5858
|
},
|
|
5859
|
+
{
|
|
5860
|
+
"id": "e52ae74f0052",
|
|
5861
|
+
"surface": "builders-hall",
|
|
5862
|
+
"text": "Move to goal",
|
|
5863
|
+
"file": "modules/hall-ui/public/task.js",
|
|
5864
|
+
"line": 510,
|
|
5865
|
+
"origin": "js-markup",
|
|
5866
|
+
"confidence": "certain"
|
|
5867
|
+
},
|
|
5850
5868
|
{
|
|
5851
5869
|
"id": "ae84ebe1d735",
|
|
5852
5870
|
"surface": "builders-hall",
|
|
@@ -6072,6 +6090,15 @@
|
|
|
6072
6090
|
"origin": "js-markup",
|
|
6073
6091
|
"confidence": "certain"
|
|
6074
6092
|
},
|
|
6093
|
+
{
|
|
6094
|
+
"id": "421ae031594e",
|
|
6095
|
+
"surface": "builders-hall",
|
|
6096
|
+
"text": "No open goals in this version",
|
|
6097
|
+
"file": "modules/hall-ui/public/task.js",
|
|
6098
|
+
"line": 509,
|
|
6099
|
+
"origin": "js-markup",
|
|
6100
|
+
"confidence": "certain"
|
|
6101
|
+
},
|
|
6075
6102
|
{
|
|
6076
6103
|
"id": "2a6d7552e21f",
|
|
6077
6104
|
"surface": "builders-hall",
|
|
@@ -6185,7 +6212,7 @@
|
|
|
6185
6212
|
"surface": "builders-hall",
|
|
6186
6213
|
"text": "Not found",
|
|
6187
6214
|
"file": "modules/hall-ui/public/task.js",
|
|
6188
|
-
"line":
|
|
6215
|
+
"line": 698,
|
|
6189
6216
|
"origin": "js-text-assign",
|
|
6190
6217
|
"confidence": "certain"
|
|
6191
6218
|
},
|
|
@@ -6464,7 +6491,7 @@
|
|
|
6464
6491
|
"surface": "builders-hall",
|
|
6465
6492
|
"text": "Open in Sessions",
|
|
6466
6493
|
"file": "modules/hall-ui/public/task.js",
|
|
6467
|
-
"line":
|
|
6494
|
+
"line": 443,
|
|
6468
6495
|
"origin": "js-markup",
|
|
6469
6496
|
"confidence": "certain"
|
|
6470
6497
|
},
|
|
@@ -7022,7 +7049,7 @@
|
|
|
7022
7049
|
"surface": "builders-hall",
|
|
7023
7050
|
"text": "Read the handoff notes",
|
|
7024
7051
|
"file": "modules/hall-ui/public/task.js",
|
|
7025
|
-
"line":
|
|
7052
|
+
"line": 441,
|
|
7026
7053
|
"origin": "js-markup",
|
|
7027
7054
|
"confidence": "certain"
|
|
7028
7055
|
},
|
|
@@ -7733,7 +7760,7 @@
|
|
|
7733
7760
|
"surface": "builders-hall",
|
|
7734
7761
|
"text": "Session log{…}",
|
|
7735
7762
|
"file": "modules/hall-ui/public/task.js",
|
|
7736
|
-
"line":
|
|
7763
|
+
"line": 447,
|
|
7737
7764
|
"origin": "js-markup",
|
|
7738
7765
|
"confidence": "certain"
|
|
7739
7766
|
},
|
|
@@ -7841,7 +7868,7 @@
|
|
|
7841
7868
|
"surface": "builders-hall",
|
|
7842
7869
|
"text": "Ship it",
|
|
7843
7870
|
"file": "modules/hall-ui/public/task.js",
|
|
7844
|
-
"line":
|
|
7871
|
+
"line": 603,
|
|
7845
7872
|
"origin": "js-markup",
|
|
7846
7873
|
"confidence": "certain"
|
|
7847
7874
|
},
|
|
@@ -7859,7 +7886,7 @@
|
|
|
7859
7886
|
"surface": "builders-hall",
|
|
7860
7887
|
"text": "Shipped {…}{…}, {…}.",
|
|
7861
7888
|
"file": "modules/hall-ui/public/task.js",
|
|
7862
|
-
"line":
|
|
7889
|
+
"line": 584,
|
|
7863
7890
|
"origin": "js-markup",
|
|
7864
7891
|
"confidence": "certain"
|
|
7865
7892
|
},
|
|
@@ -8228,7 +8255,7 @@
|
|
|
8228
8255
|
"surface": "builders-hall",
|
|
8229
8256
|
"text": "Task {…}",
|
|
8230
8257
|
"file": "modules/hall-ui/public/task.js",
|
|
8231
|
-
"line":
|
|
8258
|
+
"line": 454,
|
|
8232
8259
|
"origin": "js-text-assign",
|
|
8233
8260
|
"confidence": "certain"
|
|
8234
8261
|
},
|
|
@@ -8237,7 +8264,7 @@
|
|
|
8237
8264
|
"surface": "builders-hall",
|
|
8238
8265
|
"text": "Task {…}",
|
|
8239
8266
|
"file": "modules/hall-ui/public/task.js",
|
|
8240
|
-
"line":
|
|
8267
|
+
"line": 463,
|
|
8241
8268
|
"origin": "js-markup",
|
|
8242
8269
|
"confidence": "certain"
|
|
8243
8270
|
},
|
|
@@ -8246,7 +8273,7 @@
|
|
|
8246
8273
|
"surface": "builders-hall",
|
|
8247
8274
|
"text": "Task {…}",
|
|
8248
8275
|
"file": "modules/hall-ui/public/task.js",
|
|
8249
|
-
"line":
|
|
8276
|
+
"line": 691,
|
|
8250
8277
|
"origin": "js-markup",
|
|
8251
8278
|
"confidence": "certain"
|
|
8252
8279
|
},
|
|
@@ -8255,7 +8282,7 @@
|
|
|
8255
8282
|
"surface": "builders-hall",
|
|
8256
8283
|
"text": "Task {…}",
|
|
8257
8284
|
"file": "modules/hall-ui/public/task.js",
|
|
8258
|
-
"line":
|
|
8285
|
+
"line": 703,
|
|
8259
8286
|
"origin": "js-markup",
|
|
8260
8287
|
"confidence": "certain"
|
|
8261
8288
|
},
|
|
@@ -8291,7 +8318,7 @@
|
|
|
8291
8318
|
"surface": "builders-hall",
|
|
8292
8319
|
"text": "Tasks in the ledger are numbered separately from GitHub pull requests, so this may be a PR number rather than a task. The work board lists everything that does exist.",
|
|
8293
8320
|
"file": "modules/hall-ui/public/task.js",
|
|
8294
|
-
"line":
|
|
8321
|
+
"line": 691,
|
|
8295
8322
|
"origin": "js-markup",
|
|
8296
8323
|
"confidence": "certain"
|
|
8297
8324
|
},
|
|
@@ -8390,7 +8417,7 @@
|
|
|
8390
8417
|
"surface": "builders-hall",
|
|
8391
8418
|
"text": "The adversarial panel. Each worker reports pass, concerns or fail on its own; the gating workers must all pass before confirmed becomes {…}, and one concerns falls the task through to the manual confirm path.",
|
|
8392
8419
|
"file": "modules/hall-ui/public/task.js",
|
|
8393
|
-
"line":
|
|
8420
|
+
"line": 391,
|
|
8394
8421
|
"origin": "js-markup",
|
|
8395
8422
|
"confidence": "certain"
|
|
8396
8423
|
},
|
|
@@ -8471,7 +8498,7 @@
|
|
|
8471
8498
|
"surface": "builders-hall",
|
|
8472
8499
|
"text": "The grading panel could not run when this task was shipped, so no quality signal was recorded — an outage, not a verdict.",
|
|
8473
8500
|
"file": "modules/hall-ui/public/task.js",
|
|
8474
|
-
"line":
|
|
8501
|
+
"line": 373,
|
|
8475
8502
|
"origin": "js-markup",
|
|
8476
8503
|
"confidence": "certain"
|
|
8477
8504
|
},
|
|
@@ -8750,7 +8777,7 @@
|
|
|
8750
8777
|
"surface": "builders-hall",
|
|
8751
8778
|
"text": "The task feed did not answer. Nothing on the record has changed, only the read failed. Try again in a moment, or go back.",
|
|
8752
8779
|
"file": "modules/hall-ui/public/task.js",
|
|
8753
|
-
"line":
|
|
8780
|
+
"line": 703,
|
|
8754
8781
|
"origin": "js-markup",
|
|
8755
8782
|
"confidence": "certain"
|
|
8756
8783
|
},
|
|
@@ -9011,7 +9038,7 @@
|
|
|
9011
9038
|
"surface": "builders-hall",
|
|
9012
9039
|
"text": "This task has shipped and its credits have landed. Neither Ship it nor Confirm is available.",
|
|
9013
9040
|
"file": "modules/hall-ui/public/task.js",
|
|
9014
|
-
"line":
|
|
9041
|
+
"line": 612,
|
|
9015
9042
|
"origin": "js-markup",
|
|
9016
9043
|
"confidence": "certain"
|
|
9017
9044
|
},
|
|
@@ -11216,7 +11243,7 @@
|
|
|
11216
11243
|
"surface": "builders-hall",
|
|
11217
11244
|
"text": "verdict {…}",
|
|
11218
11245
|
"file": "modules/hall-ui/public/task.js",
|
|
11219
|
-
"line":
|
|
11246
|
+
"line": 395,
|
|
11220
11247
|
"origin": "js-markup",
|
|
11221
11248
|
"confidence": "certain"
|
|
11222
11249
|
},
|
|
@@ -11432,7 +11459,7 @@
|
|
|
11432
11459
|
"surface": "builders-hall",
|
|
11433
11460
|
"text": "{…} awarded",
|
|
11434
11461
|
"file": "modules/hall-ui/public/task.js",
|
|
11435
|
-
"line":
|
|
11462
|
+
"line": 438,
|
|
11436
11463
|
"origin": "js-markup",
|
|
11437
11464
|
"confidence": "certain"
|
|
11438
11465
|
},
|
|
@@ -11576,7 +11603,7 @@
|
|
|
11576
11603
|
"surface": "builders-hall",
|
|
11577
11604
|
"text": "{…} sessions",
|
|
11578
11605
|
"file": "modules/hall-ui/public/task.js",
|
|
11579
|
-
"line":
|
|
11606
|
+
"line": 446,
|
|
11580
11607
|
"origin": "js-markup",
|
|
11581
11608
|
"confidence": "certain"
|
|
11582
11609
|
},
|
|
@@ -11756,7 +11783,7 @@
|
|
|
11756
11783
|
"surface": "builders-hall",
|
|
11757
11784
|
"text": "{…}, no such task",
|
|
11758
11785
|
"file": "modules/hall-ui/public/task.js",
|
|
11759
|
-
"line":
|
|
11786
|
+
"line": 691,
|
|
11760
11787
|
"origin": "js-markup",
|
|
11761
11788
|
"confidence": "certain"
|
|
11762
11789
|
},
|
|
@@ -11819,7 +11846,7 @@
|
|
|
11819
11846
|
"surface": "builders-hall",
|
|
11820
11847
|
"text": "{…}{…} · {…}",
|
|
11821
11848
|
"file": "modules/hall-ui/public/task.js",
|
|
11822
|
-
"line":
|
|
11849
|
+
"line": 546,
|
|
11823
11850
|
"origin": "js-markup",
|
|
11824
11851
|
"confidence": "certain"
|
|
11825
11852
|
},
|
package/docs/file-map.md
CHANGED
|
@@ -185,7 +185,7 @@
|
|
|
185
185
|
│ ├── box.js ← (#598/ADR 0031) per-builder dev box lifecycle CLI: provision·park·wake·deprovision·sweep-idle·sweep-dormant·status (dry-run default, --apply to act; rank-gated)
|
|
186
186
|
│ ├── smoke-box.sh ← (#598) hermetic box-lifecycle smoke (no DB/DO/network): runs tests/boxes.mjs + tests/cf_tunnel.mjs (#771 named tunnel) + route-rank + module-load
|
|
187
187
|
│ ├── recall.js ← `/recall` CLI: two-call progressive-disclosure search (summary → `--expand <id>`) over Bongos retrieval layer (ADR 0060); `node scripts/gds/recall.js <query>`
|
|
188
|
-
│ ├── capture.js ← file an idea_inbox row from the CLI (the ideas.capture kernel port); backs the "capture a tangent" flow across skills. Task 1002927 / R151 taught it the GRADES: `--full --fields-file f.json` files a Full Idea, `--spark` is capture-then-mark (two requests — the route refuses a pre-marked capture), and after a plain quick capture it OFFERS to develop further. That offer is TTY-gated (`--no-prompt` too): capture.js is run by agents with no terminal and reads its body from stdin, so an unguarded prompt would hang every non-interactive caller
|
|
188
|
+
│ ├── capture.js ← file an idea_inbox row from the CLI (the ideas.capture kernel port); backs the "capture a tangent" flow across skills. Task 1002927 / R151 taught it the GRADES: `--full --fields-file f.json` files a Full Idea, `--spark` is capture-then-mark (two requests — the route refuses a pre-marked capture), and after a plain quick capture it OFFERS to develop further. That offer is TTY-gated (`--no-prompt` too): capture.js is run by agents with no terminal and reads its body from stdin, so an unguarded prompt would hang every non-interactive caller. Task 1003070 / R09 (goal 1000071) taught it capture-time ROUTING: `--goal N` sends goal_id, `--goal none` opts out explicitly, and with neither it defaults to the caller's active claim's goal, else prints a one-line R07 suggester hint
|
|
189
189
|
│ ├── spark.js ← (tasks 1002920 / R144 + 1002921 / R145 + 1002922 / R146) the whole spark handoff from the terminal: mark one of your OWN quick ideas as a Big Idea Spark (`--undo` withdraws it), `--list` is the SPARK QUEUE, `--template` prints the five Full Idea fields, `--develop <id> --fields-file <json>` is the PICKUP (any builder; author + developer both recorded). Author-only server-side for the mark (it is the consent); its own command because sparking acts on an EXISTING idea, which is why capture.js refuses grade='spark'
|
|
190
190
|
│ ├── stale-worktrees.js ← (task 1003365) the per-claim worktrees whose branch already landed on origin/main or is gone — pure git facts, no API; `--line` is the SessionStart probe, `/worktree-clean` walks the list through worktree.js remove
|
|
191
191
|
│ ├── worktree-claim-guard.js ← (task 1002579) the PURE decision behind worktree-per-claim, enforced at claim time: claiming in the MAIN CHECKOUT is refused (escape hatch `--here` → loud warning), a linked worktree passes, an unclassifiable tree fails open. Stops the ship-time "the main checkout is on <branch>, not main" strand at its cause
|
|
@@ -1427,5 +1427,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
1427
1427
|
landed since 1.19.493 with no explicit bump. run 33697218855. (task 1002620)
|
|
1428
1428
|
1.19.495 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1429
1429
|
landed since 1.19.494 with no explicit bump. run 33766103175. (task 1002620)
|
|
1430
|
+
1.19.496 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1431
|
+
landed since 1.19.495 with no explicit bump. run 33766363834. (task 1002620)
|
|
1432
|
+
1.19.497 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1433
|
+
landed since 1.19.496 with no explicit bump. run 33767382661. (task 1002620)
|
|
1430
1434
|
---------------------------------------------------------------------------
|
|
1431
1435
|
```
|
|
@@ -20,6 +20,25 @@
|
|
|
20
20
|
//
|
|
21
21
|
// Requires (go-live): the Message Content PRIVILEGED intent (to read message
|
|
22
22
|
// text) + DISCORD_IDEAS_CHANNEL_ID. Inert until both are set.
|
|
23
|
+
//
|
|
24
|
+
// ROUTING HINTS, NEVER ROUTING (task 1003068, BV1.R12; goal 1000071 criterion
|
|
25
|
+
// C5, deliberately not C1/C3). Unlike #bugs (discord-bugs.js, R11), this vector
|
|
26
|
+
// never routes: a Discord author is outside the trust boundary (ADR 0016,
|
|
27
|
+
// ADR 0032), so their message may SUGGEST a goal but must never BIND one. The
|
|
28
|
+
// suggester (modules/lifecycle/goal-suggest.js, resolved through the same
|
|
29
|
+
// `lifecycle` port #bugs already consumes) supplies a candidate goal; its id
|
|
30
|
+
// rides onto the row as the advisory `suggested_goal_id` hint (core_222) and its
|
|
31
|
+
// `version_id` as `suggested_version` — both read-only guesses for /idea-triage
|
|
32
|
+
// (R13), never `goalId`, which is what would make captureIdea actually route.
|
|
33
|
+
//
|
|
34
|
+
// suggested_version DEFAULTS even without a goal match: when the classifier
|
|
35
|
+
// finds no confident goal, `suggested_version` falls back to whichever open
|
|
36
|
+
// version most recently started `status='building'` — the version a filer is
|
|
37
|
+
// overwhelmingly likely to mean when they post right now, absent any other
|
|
38
|
+
// signal. A matched goal's OWN version always wins over the default (the two
|
|
39
|
+
// travel together elsewhere in this goal — a routed capture binds to the
|
|
40
|
+
// goal's version, never a caller-supplied one), so the fallback only fires
|
|
41
|
+
// when there is truly nothing more specific to say.
|
|
23
42
|
|
|
24
43
|
// captureIdea is no longer a doorway export (retired at BV1.R78 / ADR 0093 §5).
|
|
25
44
|
// We resolve the `ideas.capture` kernel PORT the ideas module registers — the
|
|
@@ -31,6 +50,7 @@ const auth = require('./auth');
|
|
|
31
50
|
const attachments = require('./bug-attachments');
|
|
32
51
|
const { createMessageAggregator } = require('./discord-message-buffer');
|
|
33
52
|
const { LIMITS } = api;
|
|
53
|
+
const log = api.logger('discord');
|
|
34
54
|
|
|
35
55
|
// Auto-pairing window for #ideas (task #979) — same default as #bugs. Env
|
|
36
56
|
// DISCORD_IDEAS_PAIR_WINDOW_MS overrides.
|
|
@@ -68,10 +88,37 @@ const realDeps = {
|
|
|
68
88
|
// Lazy: resolve the `ideas.capture` port at CALL time, so the ideas module's
|
|
69
89
|
// registration (at its route-factory mount) need not precede this module's load.
|
|
70
90
|
captureIdea: (...a) => api.resolve('ideas.capture')(...a),
|
|
91
|
+
// resolveOptional, not resolve: a vanilla instance without the lifecycle
|
|
92
|
+
// module has no goals to suggest, and that is a reason to skip the hint —
|
|
93
|
+
// never a reason to lose the idea (the discord-bugs.js precedent).
|
|
94
|
+
suggestGoalForCapture: async (...a) => {
|
|
95
|
+
const lifecycle = api.resolveOptional && api.resolveOptional('lifecycle');
|
|
96
|
+
if (!lifecycle || !lifecycle.suggestGoalForCapture) return null;
|
|
97
|
+
return lifecycle.suggestGoalForCapture(...a);
|
|
98
|
+
},
|
|
99
|
+
// Same resolveOptional posture — a vanilla instance without lifecycle has no
|
|
100
|
+
// versions to default to, which is a reason to skip the default, never the idea.
|
|
101
|
+
listVersions: async () => {
|
|
102
|
+
const lifecycle = api.resolveOptional && api.resolveOptional('lifecycle');
|
|
103
|
+
if (!lifecycle || !lifecycle.listVersions) return [];
|
|
104
|
+
return lifecycle.listVersions();
|
|
105
|
+
},
|
|
71
106
|
insertAuditLog: db.insertAuditLog,
|
|
72
107
|
persistImages: attachments.persistImages,
|
|
73
108
|
};
|
|
74
109
|
|
|
110
|
+
// mostRecentBuildingVersionId — PURE: the id of whichever `status='building'`
|
|
111
|
+
// version most recently started, or null. The suggested_version fallback for a
|
|
112
|
+
// filing whose classifier found no confident goal (see header). `started_at` is
|
|
113
|
+
// a timestamp string/Date; String() comparison is wrong for it, so this compares
|
|
114
|
+
// parsed dates explicitly rather than relying on default sort.
|
|
115
|
+
function mostRecentBuildingVersionId(versions) {
|
|
116
|
+
const building = (Array.isArray(versions) ? versions : []).filter((v) => v && v.status === 'building');
|
|
117
|
+
if (building.length === 0) return null;
|
|
118
|
+
building.sort((a, b) => new Date(b.started_at) - new Date(a.started_at));
|
|
119
|
+
return building[0].id;
|
|
120
|
+
}
|
|
121
|
+
|
|
75
122
|
// File one inbound message as an idea. NEVER throws (the caller is a gateway
|
|
76
123
|
// event handler). Returns a small status object for logging/tests.
|
|
77
124
|
async function recordInboundIdea({ discordUserId, content, imageUrls = [], messageId }, deps = realDeps) {
|
|
@@ -114,12 +161,34 @@ async function recordInboundIdea({ discordUserId, content, imageUrls = [], messa
|
|
|
114
161
|
}
|
|
115
162
|
bodyMd = bodyMd.slice(0, LIMITS.DESCRIPTION);
|
|
116
163
|
|
|
164
|
+
// HINTS ONLY (see header). Neither lookup throws into the filing — a
|
|
165
|
+
// suggester/version-list outage must not cost the idea, only the hints.
|
|
166
|
+
let hint = null;
|
|
167
|
+
try {
|
|
168
|
+
hint = await deps.suggestGoalForCapture({ title: derived.title, body: derived.bodyMd });
|
|
169
|
+
} catch (e) {
|
|
170
|
+
log.warn({ err: e && e.message }, 'idea goal suggestion failed — filing without a goal hint');
|
|
171
|
+
}
|
|
172
|
+
let suggestedVersion = hint ? hint.version_id : null;
|
|
173
|
+
if (!suggestedVersion) {
|
|
174
|
+
try {
|
|
175
|
+
suggestedVersion = mostRecentBuildingVersionId(await deps.listVersions());
|
|
176
|
+
} catch (e) {
|
|
177
|
+
log.warn({ err: e && e.message }, 'default-version lookup failed — filing without a version hint');
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
117
181
|
const idea = await deps.captureIdea({
|
|
118
182
|
title: derived.title,
|
|
119
183
|
bodyMd,
|
|
120
184
|
capturedBy: builder ? builder.id : null,
|
|
121
185
|
// kind left null → inbox.captureIdea auto-classifies. We never pass
|
|
122
186
|
// 'criterion-proposal' (the rank-gated kind) from this path.
|
|
187
|
+
// suggestedGoalId/suggestedVersion are ADVISORY-ONLY columns — this call
|
|
188
|
+
// deliberately never names the routing parameter (see header + F12 in
|
|
189
|
+
// tests/filing_abuse_matrix.mjs, which greps this exact call for it).
|
|
190
|
+
suggestedGoalId: hint ? hint.id : null,
|
|
191
|
+
suggestedVersion,
|
|
123
192
|
});
|
|
124
193
|
|
|
125
194
|
await auditDiscordBot(deps, {
|
|
@@ -144,4 +213,7 @@ function createIdeaAggregator({ onFlush, windowMs = pairWindowMs(), timers = glo
|
|
|
144
213
|
return createMessageAggregator({ onFlush, windowMs, timers });
|
|
145
214
|
}
|
|
146
215
|
|
|
147
|
-
module.exports = {
|
|
216
|
+
module.exports = {
|
|
217
|
+
deriveIdea, recordInboundIdea, createIdeaAggregator, pairWindowMs, realDeps,
|
|
218
|
+
mostRecentBuildingVersionId,
|
|
219
|
+
};
|
|
@@ -66,6 +66,24 @@
|
|
|
66
66
|
return data;
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
// task 1003063 (R10): mutating call for the move-to-goal affordance. Same
|
|
70
|
+
// posture as postJSON — resolves to the parsed 2xx body, throws an Error
|
|
71
|
+
// carrying { status, data } on a non-2xx so the caller can read the server's
|
|
72
|
+
// own refusal sentence (ADR 0016 enforces; this only surfaces it).
|
|
73
|
+
async function patchJSON(path, body) {
|
|
74
|
+
const res = await api.request('PATCH', path, { body: body || {} });
|
|
75
|
+
if (res.status === 401) { toAuth(); throw new Error('redirecting to auth'); }
|
|
76
|
+
let data = null;
|
|
77
|
+
try { data = res.data; } catch (_) { /* empty / non-JSON body */ }
|
|
78
|
+
if (!res.ok) {
|
|
79
|
+
const err = new Error(`${path} → ${res.status}`);
|
|
80
|
+
err.status = res.status;
|
|
81
|
+
err.data = data || {};
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
return data;
|
|
85
|
+
}
|
|
86
|
+
|
|
69
87
|
// Map the server's typed lifecycle-error to a builder-readable line. Codes are
|
|
70
88
|
// exactly those POST /tasks/:id/ship + /confirm can return. The server is the
|
|
71
89
|
// authority (ADR 0016); the UI only surfaces the refusal.
|
|
@@ -430,11 +448,17 @@
|
|
|
430
448
|
}
|
|
431
449
|
|
|
432
450
|
function renderTask(t, lastClaim, viewer, goal, extras) {
|
|
433
|
-
const { grade = null, sessionLogs = null, claims = null } = extras || {};
|
|
451
|
+
const { grade = null, sessionLogs = null, claims = null, goalsList = null } = extras || {};
|
|
434
452
|
document.title = `#${t.id} — ${t.title || 'Task'} — ${document.title.split(' — ').pop()}`;
|
|
435
453
|
const sub = $('#task-sub');
|
|
436
454
|
if (sub) sub.textContent = `Task ${t.id}`;
|
|
437
455
|
|
|
456
|
+
// Metic+ is the floor for both the lifecycle supervisor controls below and
|
|
457
|
+
// the move-to-goal affordance (task 1003063 / R10) — hoisted so the two
|
|
458
|
+
// gates read the same rank once, rather than risking drift between copies.
|
|
459
|
+
const rank = (viewer && viewer.rank) ? String(viewer.rank).toLowerCase() : '';
|
|
460
|
+
const isSupervisor = rank === 'metic' || rank === 'archon';
|
|
461
|
+
|
|
438
462
|
const parts = [];
|
|
439
463
|
parts.push(`<p class="rec-kind">Task ${statusPill(t.status)}</p>`);
|
|
440
464
|
parts.push(`<h1 class="rec-title" id="task-h"><span class="rec-title__id">${escapeHtml(String(t.id))}</span>${escapeHtml(t.title || 'Untitled task')}</h1>`);
|
|
@@ -473,6 +497,28 @@
|
|
|
473
497
|
parts.push(`<p class="rec-goal">Goal <a href="/goals#/goal/${encodeURIComponent(t.goal_id)}">${escapeHtml(String(t.goal_id))}${goalName}</a>${catChip}</p>`);
|
|
474
498
|
}
|
|
475
499
|
|
|
500
|
+
// task 1003063 (R10): the reassignment affordance — so a wrong routing pick
|
|
501
|
+
// is one PATCH away, never a re-file. Metic+ only (the server's own floor on
|
|
502
|
+
// PATCH /tasks/:id, ADR 0090); goalsList is fetched by load() scoped to this
|
|
503
|
+
// task's own version, because the server refuses a cross-version move
|
|
504
|
+
// (goal_version_mismatch, 409) and there is no point offering one.
|
|
505
|
+
if (isSupervisor && t.version_id) {
|
|
506
|
+
const openGoals = (Array.isArray(goalsList) ? goalsList : []).filter((g) => g.status !== 'archived');
|
|
507
|
+
const goalOpts = openGoals.length
|
|
508
|
+
? openGoals.map((g) => `<option value="${escapeHtml(String(g.id))}"${String(g.id) === String(t.goal_id) ? ' selected' : ''}>${escapeHtml(String(g.id))} — ${escapeHtml(g.title || '')}</option>`).join('')
|
|
509
|
+
: '<option value="">No open goals in this version</option>';
|
|
510
|
+
parts.push(`
|
|
511
|
+
<div class="td-form" id="move-goal-form">
|
|
512
|
+
<label class="td-field td-field--wide"><span>Move to goal</span>
|
|
513
|
+
<select id="move-goal-select">${goalOpts}</select>
|
|
514
|
+
</label>
|
|
515
|
+
<div class="td-actions">
|
|
516
|
+
<button type="button" class="td-btn" id="move-goal-btn">Move</button>
|
|
517
|
+
</div>
|
|
518
|
+
</div>
|
|
519
|
+
`);
|
|
520
|
+
}
|
|
521
|
+
|
|
476
522
|
const creditVal = window.OTBBranding?.taskCreditValue?.(t) ?? t.credits_reward;
|
|
477
523
|
parts.push(figureBand([
|
|
478
524
|
[window.OTBBranding?.taskCreditLabel?.(t) ?? curLabel(), creditVal],
|
|
@@ -550,8 +596,6 @@
|
|
|
550
596
|
// toast. `confirmed → shipped` is the any-builder-supervisor / own-claim ship;
|
|
551
597
|
// `completed → confirmed` is the Archon-only grader-bypass confirm (others see
|
|
552
598
|
// a read-only note — the grader/Archon still has to act).
|
|
553
|
-
const rank = (viewer && viewer.rank) ? String(viewer.rank).toLowerCase() : '';
|
|
554
|
-
const isSupervisor = rank === 'metic' || rank === 'archon';
|
|
555
599
|
const ownsLastClaim = !!(viewer && lastClaim && lastClaim.github_login &&
|
|
556
600
|
lastClaim.github_login === viewer.github_login);
|
|
557
601
|
let lifecycle = '';
|
|
@@ -610,6 +654,33 @@
|
|
|
610
654
|
runLifecycle(confirmBtn, `${API}/tasks/${encodeURIComponent(t.id)}/confirm`, body, `#${t.id} confirmed.`);
|
|
611
655
|
});
|
|
612
656
|
}
|
|
657
|
+
|
|
658
|
+
// task 1003063 (R10): PATCH goal_id, then a full reload so the goal line, the
|
|
659
|
+
// picker's preselected option and everything else on the record re-render
|
|
660
|
+
// against the server's own state — no local patchwork of the DOM.
|
|
661
|
+
const moveGoalBtn = $('#move-goal-btn');
|
|
662
|
+
if (moveGoalBtn) {
|
|
663
|
+
moveGoalBtn.addEventListener('click', async () => {
|
|
664
|
+
const sel = $('#move-goal-select');
|
|
665
|
+
const goalId = sel ? Number(sel.value) : NaN;
|
|
666
|
+
if (!Number.isInteger(goalId) || goalId <= 0) { toast('Choose a goal first.'); return; }
|
|
667
|
+
const orig = moveGoalBtn.textContent;
|
|
668
|
+
moveGoalBtn.disabled = true;
|
|
669
|
+
moveGoalBtn.textContent = '…';
|
|
670
|
+
try {
|
|
671
|
+
await patchJSON(`${API}/tasks/${encodeURIComponent(t.id)}`, { goal_id: goalId });
|
|
672
|
+
toast(`#${t.id} moved to goal ${goalId}.`);
|
|
673
|
+
await load();
|
|
674
|
+
} catch (err) {
|
|
675
|
+
if (err && err.message === 'redirecting to auth') return;
|
|
676
|
+
// The server's own sentence, verbatim (a rank_forbidden 403 or a
|
|
677
|
+
// goal_version_mismatch 409 both say exactly what went wrong).
|
|
678
|
+
toast(errMessage(err && err.data) || `Could not move that task (${(err && err.status) || '?'}). Try again.`);
|
|
679
|
+
moveGoalBtn.disabled = false;
|
|
680
|
+
moveGoalBtn.textContent = orig;
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
}
|
|
613
684
|
}
|
|
614
685
|
|
|
615
686
|
// The two honest failures. Both keep the <h1> — the empty and error renders are
|
|
@@ -664,6 +735,18 @@
|
|
|
664
735
|
goal = (g && g.goal) ? g.goal : null;
|
|
665
736
|
} catch (_) { /* cosmetic — the goal link still resolves without it */ }
|
|
666
737
|
}
|
|
738
|
+
// task 1003063 (R10): the move-to-goal picker's options, fetched only for
|
|
739
|
+
// the rank that can act on it and scoped to this task's own version (the
|
|
740
|
+
// server refuses a cross-version move). Best-effort: a failed read just
|
|
741
|
+
// means an empty picker, never a blocked page.
|
|
742
|
+
let goalsList = null;
|
|
743
|
+
const viewerRank = (viewer && viewer.rank) ? String(viewer.rank).toLowerCase() : '';
|
|
744
|
+
if ((viewerRank === 'metic' || viewerRank === 'archon') && d.task.version_id) {
|
|
745
|
+
try {
|
|
746
|
+
const gl = await getJSON(`${API}/goals?version=${encodeURIComponent(d.task.version_id)}`, { softAuth: true });
|
|
747
|
+
goalsList = Array.isArray(gl && gl.goals) ? gl.goals : [];
|
|
748
|
+
} catch (_) { /* cosmetic — the picker just has nothing to offer */ }
|
|
749
|
+
}
|
|
667
750
|
// The repo bases a description's relative paths resolve onto (the primer's
|
|
668
751
|
// and drachmae's pattern). Best-effort: without them a path passes through.
|
|
669
752
|
try {
|
|
@@ -675,6 +758,7 @@
|
|
|
675
758
|
grade: d.grade || null,
|
|
676
759
|
sessionLogs: Array.isArray(d.session_logs) ? d.session_logs : null,
|
|
677
760
|
claims: Array.isArray(d.claims) ? d.claims : null,
|
|
761
|
+
goalsList,
|
|
678
762
|
});
|
|
679
763
|
} catch (err) {
|
|
680
764
|
if (err && err.code === AUTH_NEEDED) { toAuth(); return; }
|
package/modules/ideas/inbox.js
CHANGED
|
@@ -240,6 +240,13 @@ async function captureIdea({
|
|
|
240
240
|
// (a hall filing surface, a Discord classifier) can, and when it does the
|
|
241
241
|
// goal's scope wall is applied to it verbatim. See ./routing.js.
|
|
242
242
|
touches = null,
|
|
243
|
+
// task 1003068 (BV1.R12): a HINT-ONLY goal guess — NEVER fed to decideLanding,
|
|
244
|
+
// only recorded as the advisory suggested_goal_id column. `goalId` is a
|
|
245
|
+
// caller's routing DECISION; this is a machine guess with no such authority
|
|
246
|
+
// (the Discord #ideas classifier has no human behind it to have decided
|
|
247
|
+
// anything). Ignored when `goalId` is already set, so every existing caller
|
|
248
|
+
// is untouched.
|
|
249
|
+
suggestedGoalId = null,
|
|
243
250
|
}, deps = {}) {
|
|
244
251
|
if (!title || typeof title !== 'string') {
|
|
245
252
|
throw { code: 'TITLE_REQUIRED' };
|
|
@@ -277,7 +284,7 @@ async function captureIdea({
|
|
|
277
284
|
$7)
|
|
278
285
|
RETURNING ${IDEA_COLS}`,
|
|
279
286
|
[title, bodyMd, resolvedKind, suggestedVersion, suggestedPriority, capturedBy,
|
|
280
|
-
goalId ?? null]
|
|
287
|
+
goalId ?? suggestedGoalId ?? null]
|
|
281
288
|
);
|
|
282
289
|
// A brand-new idea has no ideas_grades row, so it is quick by definition —
|
|
283
290
|
// no join needed to know that. Grading it (R143's Full Idea capture) is a
|