@bongos/core 1.19.297 → 1.19.299
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 +60 -30
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +4 -0
- package/clients/bongos-client/index.cjs +4 -0
- package/clients/bongos-client/index.d.ts +7 -0
- package/clients/bongos-client/index.mjs +4 -0
- package/docs/adr/0185-spark-handoff-credit-split.md +129 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +128 -4
- package/docs/api-reference.md +4 -2
- package/docs/copy-inventory.md +28 -13
- package/docs/copy-registry.json +162 -17
- package/docs/file-map.md +1 -1
- package/modules/dev-box/app/src/vendor/bongos-client.cjs +4 -0
- package/modules/economy/credits.js +156 -31
- package/modules/hall-ui/public/ideas.css +120 -0
- package/modules/hall-ui/public/ideas.html +95 -0
- package/modules/hall-ui/public/ideas.js +335 -0
- package/modules/hall-ui/public/shell.js +8 -0
- package/modules/ideas/routes/inbox.js +121 -12
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/bongos/serve-internal.js +6 -1
- package/src/module-api.js +5 -1
- package/tests/hall_nav.mjs +6 -1
- package/tests/idea_credit_split.mjs +224 -0
- package/tests/idea_develop_spark.mjs +47 -10
- package/tests/idea_full_pass_credit.mjs +50 -18
- package/tests/idea_hall_composer.mjs +246 -0
package/docs/copy-registry.json
CHANGED
|
@@ -3,17 +3,17 @@
|
|
|
3
3
|
"generator": "scripts/gds/copy-inventory.js",
|
|
4
4
|
"goal": 1000074,
|
|
5
5
|
"counts": {
|
|
6
|
-
"strings":
|
|
7
|
-
"unique_ids":
|
|
8
|
-
"certain":
|
|
6
|
+
"strings": 1354,
|
|
7
|
+
"unique_ids": 1202,
|
|
8
|
+
"certain": 1354,
|
|
9
9
|
"heuristic": 0,
|
|
10
|
-
"duplicate_ids":
|
|
10
|
+
"duplicate_ids": 122
|
|
11
11
|
},
|
|
12
12
|
"surfaces": {
|
|
13
13
|
"builders-hall": {
|
|
14
14
|
"at": "modules/hall-ui/public",
|
|
15
|
-
"files":
|
|
16
|
-
"strings":
|
|
15
|
+
"files": 63,
|
|
16
|
+
"strings": 1070
|
|
17
17
|
},
|
|
18
18
|
"status-dashboard": {
|
|
19
19
|
"at": "modules/status-ui/public",
|
|
@@ -191,7 +191,7 @@
|
|
|
191
191
|
"at": [
|
|
192
192
|
"modules/hall-ui/public/builders.js:553",
|
|
193
193
|
"modules/hall-ui/public/builders.js:660",
|
|
194
|
-
"modules/hall-ui/public/shell.js:
|
|
194
|
+
"modules/hall-ui/public/shell.js:273"
|
|
195
195
|
]
|
|
196
196
|
},
|
|
197
197
|
{
|
|
@@ -658,6 +658,16 @@
|
|
|
658
658
|
"modules/hall-ui/public/primer.js:496"
|
|
659
659
|
]
|
|
660
660
|
},
|
|
661
|
+
{
|
|
662
|
+
"id": "ef224c9de7c8",
|
|
663
|
+
"surface": "builders-hall",
|
|
664
|
+
"text": "Ideas — {{worldName}}",
|
|
665
|
+
"occurrences": 2,
|
|
666
|
+
"at": [
|
|
667
|
+
"modules/hall-ui/public/ideas.html:6",
|
|
668
|
+
"modules/hall-ui/public/ideas.html:6"
|
|
669
|
+
]
|
|
670
|
+
},
|
|
661
671
|
{
|
|
662
672
|
"id": "41cc7f5ec97e",
|
|
663
673
|
"surface": "status-dashboard",
|
|
@@ -1024,8 +1034,8 @@
|
|
|
1024
1034
|
"text": "Sign out",
|
|
1025
1035
|
"occurrences": 2,
|
|
1026
1036
|
"at": [
|
|
1027
|
-
"modules/hall-ui/public/shell.js:
|
|
1028
|
-
"modules/hall-ui/public/shell.js:
|
|
1037
|
+
"modules/hall-ui/public/shell.js:291",
|
|
1038
|
+
"modules/hall-ui/public/shell.js:426"
|
|
1029
1039
|
]
|
|
1030
1040
|
},
|
|
1031
1041
|
{
|
|
@@ -1604,6 +1614,15 @@
|
|
|
1604
1614
|
"origin": "html-text",
|
|
1605
1615
|
"confidence": "certain"
|
|
1606
1616
|
},
|
|
1617
|
+
{
|
|
1618
|
+
"id": "8ffdf4995e81",
|
|
1619
|
+
"surface": "builders-hall",
|
|
1620
|
+
"text": "A Full Idea answers five questions, so that someone else can deliver against it without having to interview you first. Open to every role — the score below is the only filter, and it counts substance, not who you are.",
|
|
1621
|
+
"file": "modules/hall-ui/public/ideas.html",
|
|
1622
|
+
"line": 33,
|
|
1623
|
+
"origin": "html-text",
|
|
1624
|
+
"confidence": "certain"
|
|
1625
|
+
},
|
|
1607
1626
|
{
|
|
1608
1627
|
"id": "3e5a62bc4887",
|
|
1609
1628
|
"surface": "builders-hall",
|
|
@@ -3044,6 +3063,15 @@
|
|
|
3044
3063
|
"origin": "js-markup",
|
|
3045
3064
|
"confidence": "certain"
|
|
3046
3065
|
},
|
|
3066
|
+
{
|
|
3067
|
+
"id": "b2147cc90e36",
|
|
3068
|
+
"surface": "builders-hall",
|
|
3069
|
+
"text": "Could not load the idea template. Reload, or file from the terminal with capture.js.",
|
|
3070
|
+
"file": "modules/hall-ui/public/ideas.js",
|
|
3071
|
+
"line": 311,
|
|
3072
|
+
"origin": "js-text-assign",
|
|
3073
|
+
"confidence": "certain"
|
|
3074
|
+
},
|
|
3047
3075
|
{
|
|
3048
3076
|
"id": "6264ebc9ede0",
|
|
3049
3077
|
"surface": "builders-hall",
|
|
@@ -3458,6 +3486,15 @@
|
|
|
3458
3486
|
"origin": "js-text-assign",
|
|
3459
3487
|
"confidence": "certain"
|
|
3460
3488
|
},
|
|
3489
|
+
{
|
|
3490
|
+
"id": "28ec06f6060a",
|
|
3491
|
+
"surface": "builders-hall",
|
|
3492
|
+
"text": "Counted, not judged: each question is worth the same, and earns its share in proportion to how much real text it carries. Whitespace and padding score nothing.",
|
|
3493
|
+
"file": "modules/hall-ui/public/ideas.html",
|
|
3494
|
+
"line": 82,
|
|
3495
|
+
"origin": "html-text",
|
|
3496
|
+
"confidence": "certain"
|
|
3497
|
+
},
|
|
3461
3498
|
{
|
|
3462
3499
|
"id": "38827a954f3b",
|
|
3463
3500
|
"surface": "builders-hall",
|
|
@@ -3611,6 +3648,15 @@
|
|
|
3611
3648
|
"origin": "html-text",
|
|
3612
3649
|
"confidence": "certain"
|
|
3613
3650
|
},
|
|
3651
|
+
{
|
|
3652
|
+
"id": "65322ac89221",
|
|
3653
|
+
"surface": "builders-hall",
|
|
3654
|
+
"text": "Discard draft",
|
|
3655
|
+
"file": "modules/hall-ui/public/ideas.html",
|
|
3656
|
+
"line": 60,
|
|
3657
|
+
"origin": "html-text",
|
|
3658
|
+
"confidence": "certain"
|
|
3659
|
+
},
|
|
3614
3660
|
{
|
|
3615
3661
|
"id": "0f685daf1bd5",
|
|
3616
3662
|
"surface": "builders-hall",
|
|
@@ -3746,6 +3792,15 @@
|
|
|
3746
3792
|
"origin": "html-title",
|
|
3747
3793
|
"confidence": "certain"
|
|
3748
3794
|
},
|
|
3795
|
+
{
|
|
3796
|
+
"id": "2f8d0404039e",
|
|
3797
|
+
"surface": "builders-hall",
|
|
3798
|
+
"text": "Draft discarded.",
|
|
3799
|
+
"file": "modules/hall-ui/public/ideas.js",
|
|
3800
|
+
"line": 301,
|
|
3801
|
+
"origin": "js-notice",
|
|
3802
|
+
"confidence": "certain"
|
|
3803
|
+
},
|
|
3749
3804
|
{
|
|
3750
3805
|
"id": "d59beed97d58",
|
|
3751
3806
|
"surface": "builders-hall",
|
|
@@ -3926,6 +3981,15 @@
|
|
|
3926
3981
|
"origin": "html-text",
|
|
3927
3982
|
"confidence": "certain"
|
|
3928
3983
|
},
|
|
3984
|
+
{
|
|
3985
|
+
"id": "077cf99f49f6",
|
|
3986
|
+
"surface": "builders-hall",
|
|
3987
|
+
"text": "File this idea",
|
|
3988
|
+
"file": "modules/hall-ui/public/ideas.html",
|
|
3989
|
+
"line": 59,
|
|
3990
|
+
"origin": "html-text",
|
|
3991
|
+
"confidence": "certain"
|
|
3992
|
+
},
|
|
3929
3993
|
{
|
|
3930
3994
|
"id": "1def0e7c716e",
|
|
3931
3995
|
"surface": "builders-hall",
|
|
@@ -3998,6 +4062,15 @@
|
|
|
3998
4062
|
"origin": "html-text",
|
|
3999
4063
|
"confidence": "certain"
|
|
4000
4064
|
},
|
|
4065
|
+
{
|
|
4066
|
+
"id": "18ab1be9d3ad",
|
|
4067
|
+
"surface": "builders-hall",
|
|
4068
|
+
"text": "Five questions, and a score that counts how much of each you actually answered.",
|
|
4069
|
+
"file": "modules/hall-ui/public/ideas.js",
|
|
4070
|
+
"line": 321,
|
|
4071
|
+
"origin": "js-text-assign",
|
|
4072
|
+
"confidence": "certain"
|
|
4073
|
+
},
|
|
4001
4074
|
{
|
|
4002
4075
|
"id": "b9267033b1f1",
|
|
4003
4076
|
"surface": "builders-hall",
|
|
@@ -4475,6 +4548,24 @@
|
|
|
4475
4548
|
"origin": "js-markup",
|
|
4476
4549
|
"confidence": "certain"
|
|
4477
4550
|
},
|
|
4551
|
+
{
|
|
4552
|
+
"id": "ef224c9de7c8",
|
|
4553
|
+
"surface": "builders-hall",
|
|
4554
|
+
"text": "Ideas — {{worldName}}",
|
|
4555
|
+
"file": "modules/hall-ui/public/ideas.html",
|
|
4556
|
+
"line": 6,
|
|
4557
|
+
"origin": "html-text",
|
|
4558
|
+
"confidence": "certain"
|
|
4559
|
+
},
|
|
4560
|
+
{
|
|
4561
|
+
"id": "ef224c9de7c8",
|
|
4562
|
+
"surface": "builders-hall",
|
|
4563
|
+
"text": "Ideas — {{worldName}}",
|
|
4564
|
+
"file": "modules/hall-ui/public/ideas.html",
|
|
4565
|
+
"line": 6,
|
|
4566
|
+
"origin": "html-title",
|
|
4567
|
+
"confidence": "certain"
|
|
4568
|
+
},
|
|
4478
4569
|
{
|
|
4479
4570
|
"id": "b6c47d4ee42c",
|
|
4480
4571
|
"surface": "builders-hall",
|
|
@@ -4615,7 +4706,7 @@
|
|
|
4615
4706
|
"surface": "builders-hall",
|
|
4616
4707
|
"text": "Jump to a page or record",
|
|
4617
4708
|
"file": "modules/hall-ui/public/shell.js",
|
|
4618
|
-
"line":
|
|
4709
|
+
"line": 312,
|
|
4619
4710
|
"origin": "js-markup",
|
|
4620
4711
|
"confidence": "certain"
|
|
4621
4712
|
},
|
|
@@ -4624,7 +4715,7 @@
|
|
|
4624
4715
|
"surface": "builders-hall",
|
|
4625
4716
|
"text": "Jump to…",
|
|
4626
4717
|
"file": "modules/hall-ui/public/shell.js",
|
|
4627
|
-
"line":
|
|
4718
|
+
"line": 314,
|
|
4628
4719
|
"origin": "js-markup",
|
|
4629
4720
|
"confidence": "certain"
|
|
4630
4721
|
},
|
|
@@ -4916,6 +5007,24 @@
|
|
|
4916
5007
|
"origin": "html-text",
|
|
4917
5008
|
"confidence": "certain"
|
|
4918
5009
|
},
|
|
5010
|
+
{
|
|
5011
|
+
"id": "fcd755e8dd30",
|
|
5012
|
+
"surface": "builders-hall",
|
|
5013
|
+
"text": "Loading the five questions…",
|
|
5014
|
+
"file": "modules/hall-ui/public/ideas.html",
|
|
5015
|
+
"line": 53,
|
|
5016
|
+
"origin": "html-text",
|
|
5017
|
+
"confidence": "certain"
|
|
5018
|
+
},
|
|
5019
|
+
{
|
|
5020
|
+
"id": "95a5382e5277",
|
|
5021
|
+
"surface": "builders-hall",
|
|
5022
|
+
"text": "Loading the idea template…",
|
|
5023
|
+
"file": "modules/hall-ui/public/ideas.html",
|
|
5024
|
+
"line": 23,
|
|
5025
|
+
"origin": "html-text",
|
|
5026
|
+
"confidence": "certain"
|
|
5027
|
+
},
|
|
4919
5028
|
{
|
|
4920
5029
|
"id": "9972fc9b4727",
|
|
4921
5030
|
"surface": "builders-hall",
|
|
@@ -5717,6 +5826,15 @@
|
|
|
5717
5826
|
"origin": "js-text-assign",
|
|
5718
5827
|
"confidence": "certain"
|
|
5719
5828
|
},
|
|
5829
|
+
{
|
|
5830
|
+
"id": "d5f0b7156024",
|
|
5831
|
+
"surface": "builders-hall",
|
|
5832
|
+
"text": "One line. This is what the idea is called everywhere else.",
|
|
5833
|
+
"file": "modules/hall-ui/public/ideas.html",
|
|
5834
|
+
"line": 42,
|
|
5835
|
+
"origin": "html-text",
|
|
5836
|
+
"confidence": "certain"
|
|
5837
|
+
},
|
|
5720
5838
|
{
|
|
5721
5839
|
"id": "0bc1d1470e22",
|
|
5722
5840
|
"surface": "builders-hall",
|
|
@@ -5776,7 +5894,7 @@
|
|
|
5776
5894
|
"surface": "builders-hall",
|
|
5777
5895
|
"text": "Open navigation",
|
|
5778
5896
|
"file": "modules/hall-ui/public/shell.js",
|
|
5779
|
-
"line":
|
|
5897
|
+
"line": 303,
|
|
5780
5898
|
"origin": "js-markup",
|
|
5781
5899
|
"confidence": "certain"
|
|
5782
5900
|
},
|
|
@@ -7171,7 +7289,7 @@
|
|
|
7171
7289
|
"surface": "builders-hall",
|
|
7172
7290
|
"text": "Sign in with GitHub",
|
|
7173
7291
|
"file": "modules/hall-ui/public/shell.js",
|
|
7174
|
-
"line":
|
|
7292
|
+
"line": 273,
|
|
7175
7293
|
"origin": "js-markup",
|
|
7176
7294
|
"confidence": "certain"
|
|
7177
7295
|
},
|
|
@@ -7180,7 +7298,7 @@
|
|
|
7180
7298
|
"surface": "builders-hall",
|
|
7181
7299
|
"text": "Sign out",
|
|
7182
7300
|
"file": "modules/hall-ui/public/shell.js",
|
|
7183
|
-
"line":
|
|
7301
|
+
"line": 291,
|
|
7184
7302
|
"origin": "js-markup",
|
|
7185
7303
|
"confidence": "certain"
|
|
7186
7304
|
},
|
|
@@ -7189,7 +7307,7 @@
|
|
|
7189
7307
|
"surface": "builders-hall",
|
|
7190
7308
|
"text": "Sign out",
|
|
7191
7309
|
"file": "modules/hall-ui/public/shell.js",
|
|
7192
|
-
"line":
|
|
7310
|
+
"line": 426,
|
|
7193
7311
|
"origin": "js-markup",
|
|
7194
7312
|
"confidence": "certain"
|
|
7195
7313
|
},
|
|
@@ -7351,7 +7469,7 @@
|
|
|
7351
7469
|
"surface": "builders-hall",
|
|
7352
7470
|
"text": "Switch theme",
|
|
7353
7471
|
"file": "modules/hall-ui/public/shell.js",
|
|
7354
|
-
"line":
|
|
7472
|
+
"line": 318,
|
|
7355
7473
|
"origin": "js-markup",
|
|
7356
7474
|
"confidence": "certain"
|
|
7357
7475
|
},
|
|
@@ -7486,7 +7604,7 @@
|
|
|
7486
7604
|
"surface": "builders-hall",
|
|
7487
7605
|
"text": "Text size",
|
|
7488
7606
|
"file": "modules/hall-ui/public/shell.js",
|
|
7489
|
-
"line":
|
|
7607
|
+
"line": 317,
|
|
7490
7608
|
"origin": "js-markup",
|
|
7491
7609
|
"confidence": "certain"
|
|
7492
7610
|
},
|
|
@@ -8498,6 +8616,15 @@
|
|
|
8498
8616
|
"origin": "js-markup",
|
|
8499
8617
|
"confidence": "certain"
|
|
8500
8618
|
},
|
|
8619
|
+
{
|
|
8620
|
+
"id": "52e5fe3c6075",
|
|
8621
|
+
"surface": "builders-hall",
|
|
8622
|
+
"text": "Write a Full Idea",
|
|
8623
|
+
"file": "modules/hall-ui/public/ideas.html",
|
|
8624
|
+
"line": 32,
|
|
8625
|
+
"origin": "html-text",
|
|
8626
|
+
"confidence": "certain"
|
|
8627
|
+
},
|
|
8501
8628
|
{
|
|
8502
8629
|
"id": "c368b56ecf40",
|
|
8503
8630
|
"surface": "builders-hall",
|
|
@@ -9317,6 +9444,15 @@
|
|
|
9317
9444
|
"origin": "js-markup",
|
|
9318
9445
|
"confidence": "certain"
|
|
9319
9446
|
},
|
|
9447
|
+
{
|
|
9448
|
+
"id": "7df22e74c87a",
|
|
9449
|
+
"surface": "builders-hall",
|
|
9450
|
+
"text": "idea {…}",
|
|
9451
|
+
"file": "modules/hall-ui/public/ideas.js",
|
|
9452
|
+
"line": 288,
|
|
9453
|
+
"origin": "js-markup",
|
|
9454
|
+
"confidence": "certain"
|
|
9455
|
+
},
|
|
9320
9456
|
{
|
|
9321
9457
|
"id": "b2b399fdd6a4",
|
|
9322
9458
|
"surface": "builders-hall",
|
|
@@ -10001,6 +10137,15 @@
|
|
|
10001
10137
|
"origin": "html-text",
|
|
10002
10138
|
"confidence": "certain"
|
|
10003
10139
|
},
|
|
10140
|
+
{
|
|
10141
|
+
"id": "b1c5eaa20a9d",
|
|
10142
|
+
"surface": "builders-hall",
|
|
10143
|
+
"text": "what the idea is, in one line",
|
|
10144
|
+
"file": "modules/hall-ui/public/ideas.html",
|
|
10145
|
+
"line": 45,
|
|
10146
|
+
"origin": "html-attr:placeholder",
|
|
10147
|
+
"confidence": "certain"
|
|
10148
|
+
},
|
|
10004
10149
|
{
|
|
10005
10150
|
"id": "1651cb1876f5",
|
|
10006
10151
|
"surface": "builders-hall",
|
package/docs/file-map.md
CHANGED
|
@@ -234,7 +234,7 @@
|
|
|
234
234
|
│ ├── terrain.js ← (Phase D + ADR 0012, CJS): TERRAIN + OBJECT enums + BLOCKING_TERRAINS + BLOCKING_OBJECTS (server twin of public/game/world/terrain.js)
|
|
235
235
|
│ └── map.js ← (Phase D + ADR 0012): two-grid INITIAL_MAP {ground, objects}; isBlocked checks both layers (mirrors public/game/world/{terrain,map}.js)
|
|
236
236
|
│ (public-status/ moved to modules/status-ui/public/ in BV1.R84 — see the modules/ block above)
|
|
237
|
-
│ (public-builders/ moved to modules/hall-ui/public/ in BV1.R85 — the authenticated builders' hall; see the modules/ block above. It keeps its DEDICATED serving block in src/bongos/serve-internal.js: the builders.<apex> + apex /builders/* shim, canonical redirects, the citizen/Archon page gates, asset-stamping, and the module hall-widget injection — partitioned out of the generic web-surface loop. index.html=the hall dashboard; work/board/roadmap/goals/watch/harbor/gate/pair/settings/ranks/sessions/primer/diagrams/atlas pages; profile.html/profile.js=the public, shareable builder profile page (/profile?id=N — rank, works, credits, karma, achievements; task 1467); task.html/task.js=the task-detail page (/task/:id, task 751 — replaced the old #/task/N overlay); builders.js=the controller; dom-utils.js/hall-widgets.js=the widget+settings-panel registries.)
|
|
237
|
+
│ (public-builders/ moved to modules/hall-ui/public/ in BV1.R85 — the authenticated builders' hall; see the modules/ block above. It keeps its DEDICATED serving block in src/bongos/serve-internal.js: the builders.<apex> + apex /builders/* shim, canonical redirects, the citizen/Archon page gates, asset-stamping, and the module hall-widget injection — partitioned out of the generic web-surface loop. index.html=the hall dashboard; work/board/roadmap/goals/watch/harbor/gate/pair/settings/ranks/sessions/primer/diagrams/atlas pages; ideas.{html,js,css}=the Full Idea composer (task 1002928 / R152 of goal 1000062 — the five R141 fields with a live R142 completeness meter, both fetched from the server so the page carries no copy of the rules; drafts in localStorage so navigation costs nothing); profile.html/profile.js=the public, shareable builder profile page (/profile?id=N — rank, works, credits, karma, achievements; task 1467); task.html/task.js=the task-detail page (/task/:id, task 751 — replaced the old #/task/N overlay); builders.js=the controller; dom-utils.js/hall-widgets.js=the widget+settings-panel registries.)
|
|
238
238
|
│
|
|
239
239
|
├── infra/ ← ON-BOX scripts (task 1002699): the four reporter/heartbeat crons every dev box runs from its /workspace clone — box-heartbeat.sh (idle-sweep activity signal), box-report-version.sh (HEAD → POST /box/version), box-report-host-keys.sh (SSH host keys → POST /box/host-keys), box-report-terminal.sh (ttyd URL+credential → POST /box/terminal). Delivered by the workspace clone itself (modules/dev-box/box-access.js sparse scope includes infra/); crons self-installed by box-source-fetch.sh's per-tick bring-up. API base resolves GDS_BASE env → session-file api_base → skip (no hardcoded host in the neutral core).
|
|
240
240
|
├── devbox-app/ ← OTB Dev Box — Electron menu-bar/tray switch for the builder's box (#904, ADR 0045; internal). SELF-CONTAINED: own package.json (never feeds the droplet's npm ci); built by .github/workflows/build-devbox-app.yml; published to the public releases repo per config/devbox-app.json; release recipe docs/recipes/devbox-app-release.md
|
|
@@ -416,8 +416,12 @@ function createClient(opts = {}) {
|
|
|
416
416
|
postInboxIdSpark: (args) => request("POST", "/inbox/{id}/spark", { hasBody: true }, args),
|
|
417
417
|
// GET /inbox/awaiting-nod — rank: metic+archon — GET /inbox/awaiting-nod
|
|
418
418
|
getInboxAwaitingNod: (args) => request("GET", "/inbox/awaiting-nod", { hasBody: false }, args),
|
|
419
|
+
// POST /inbox/score — rank: any-builder — POST /inbox/score
|
|
420
|
+
postInboxScore: (args) => request("POST", "/inbox/score", { hasBody: true }, args),
|
|
419
421
|
// GET /inbox/sparks — rank: any-builder — GET /inbox/sparks
|
|
420
422
|
getInboxSparks: (args) => request("GET", "/inbox/sparks", { hasBody: false }, args),
|
|
423
|
+
// GET /inbox/template — rank: any-builder — GET /inbox/template
|
|
424
|
+
getInboxTemplate: (args) => request("GET", "/inbox/template", { hasBody: false }, args),
|
|
421
425
|
},
|
|
422
426
|
"instance": {
|
|
423
427
|
// GET /instance — rank: public — GET /instance
|
|
@@ -334,6 +334,102 @@ async function maybeAwardIdeaPromotionBonus(client, taskId, shipperBuilderId, mu
|
|
|
334
334
|
};
|
|
335
335
|
}
|
|
336
336
|
|
|
337
|
+
// -------------------------------------------------------------------------
|
|
338
|
+
// The spark-handoff split (task 1002923 / BV1.R147, goal 1000062; ADR 0185)
|
|
339
|
+
//
|
|
340
|
+
// When one builder develops ANOTHER builder's Big Idea Spark into a Full Idea
|
|
341
|
+
// (R146), two people made the idea: the one who had the thought and said "I do
|
|
342
|
+
// not have the full picture", and the one who wrote out the five fields. Both
|
|
343
|
+
// existing ideator streams paid `captured_by` alone, so the developer earned
|
|
344
|
+
// nothing and the R145 queue had no reason to ever be picked up.
|
|
345
|
+
//
|
|
346
|
+
// OWNER DECISION (2026-08-22): 70% to the DEVELOPER, 30% to the spark author,
|
|
347
|
+
// on BOTH existing streams. The developer takes the larger share because
|
|
348
|
+
// developing someone else's spark is the scarce act the queue depends on — at
|
|
349
|
+
// 50/50 it pays strictly worse than authoring your own Full Idea, so nobody
|
|
350
|
+
// picks anything up. The author is not left thin: they keep 30% here AND the
|
|
351
|
+
// full trailing stream-1 share on every task shipped under the idea forever.
|
|
352
|
+
//
|
|
353
|
+
// A POLICY CONSTANT IN THIS FILE, not a table — the ADR 0172 §Consequences rule
|
|
354
|
+
// ("thresholds/factors are policy constants in credits.js", the ADR 0023
|
|
355
|
+
// precedent): it deserves a review and a commit, not a runtime edit.
|
|
356
|
+
const IDEA_DEVELOPER_SHARE = 0.70;
|
|
357
|
+
|
|
358
|
+
// splitIdeaCredit — divide one payout between the two parties.
|
|
359
|
+
//
|
|
360
|
+
// THREE PROPERTIES THIS MUST HAVE, each of which is a real bug if missed:
|
|
361
|
+
//
|
|
362
|
+
// 1. THE TOTAL IS CONSERVED. The developer's share is rounded and the author
|
|
363
|
+
// gets THE REMAINDER — never both rounded independently, which would pay
|
|
364
|
+
// 6 credits out of a 5-credit payout (round(3.5) + round(1.5) = 4 + 2).
|
|
365
|
+
// 2. SELF-DEVELOPMENT COLLAPSES TO ONE PAYEE. R146 deliberately allows an
|
|
366
|
+
// author to develop their own spark. Emitting two rows for the same builder
|
|
367
|
+
// would not double-pay — it would UNDER-pay, because the idempotency
|
|
368
|
+
// predicate is (builder_id, reason) and the second insert is silently
|
|
369
|
+
// refused, so the builder would receive only one of their two shares.
|
|
370
|
+
// 3. ZERO-VALUE ROWS ARE DROPPED. A 1-credit payout splits to 1/0; booking a
|
|
371
|
+
// 0-credit ledger row is noise that reads like a payment.
|
|
372
|
+
//
|
|
373
|
+
// Pure — no DB, no config. Returns [{ builderId, delta, role }], biggest first.
|
|
374
|
+
function splitIdeaCredit(delta, { authorId, developedBy } = {}) {
|
|
375
|
+
const total = Math.round(Number(delta) || 0);
|
|
376
|
+
if (total <= 0 || authorId == null) return [];
|
|
377
|
+
// IDS PASS THROUGH UNCONVERTED. builder_id is a bigint, which pg hands back as
|
|
378
|
+
// a STRING; Number()-ing it would risk precision on a large id and would also
|
|
379
|
+
// change the value type the pre-R147 code fed straight into the INSERT. Only
|
|
380
|
+
// the COMPARISON below normalises, via String() — a number/string mismatch
|
|
381
|
+
// between captured_by and developed_by must not become a phantom second payee.
|
|
382
|
+
if (developedBy == null) {
|
|
383
|
+
return [{ builderId: authorId, delta: total, role: 'author' }];
|
|
384
|
+
}
|
|
385
|
+
if (String(developedBy) === String(authorId)) {
|
|
386
|
+
return [{ builderId: authorId, delta: total, role: 'author_developer' }];
|
|
387
|
+
}
|
|
388
|
+
const developerCut = Math.round(total * IDEA_DEVELOPER_SHARE);
|
|
389
|
+
const authorCut = total - developerCut; // the remainder — conserves the total
|
|
390
|
+
const out = [];
|
|
391
|
+
if (developerCut > 0) out.push({ builderId: developedBy, delta: developerCut, role: 'developer' });
|
|
392
|
+
if (authorCut > 0) out.push({ builderId: authorId, delta: authorCut, role: 'spark_author' });
|
|
393
|
+
return out;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Who are the two parties for this idea? Read from the DB rather than trusted
|
|
397
|
+
// from the caller, deliberately: R146's develop route could not safely pay
|
|
398
|
+
// anything precisely because the recipient was ambiguous at the call site. With
|
|
399
|
+
// the payees derived HERE, a caller cannot direct a payment at the wrong builder,
|
|
400
|
+
// and there is exactly one place that decides who earns from an idea.
|
|
401
|
+
async function ideaCreditParties(client, ideaId) {
|
|
402
|
+
const { rows } = await client.query(
|
|
403
|
+
`SELECT i.captured_by, g.developed_by
|
|
404
|
+
FROM idea_inbox i
|
|
405
|
+
LEFT JOIN ideas_grades g ON g.idea_id = i.id
|
|
406
|
+
WHERE i.id = $1`,
|
|
407
|
+
[ideaId]
|
|
408
|
+
);
|
|
409
|
+
if (rows.length === 0) return null;
|
|
410
|
+
return { authorId: rows[0].captured_by, developedBy: rows[0].developed_by };
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
// Book one split across the ledger, idempotently per (builder, reason). Returns
|
|
414
|
+
// { credited, payees } where credited is the sum of what was NEWLY booked — so a
|
|
415
|
+
// re-run that finds both rows present reports 0, and a partially-booked split
|
|
416
|
+
// (one payee inserted, then a crash) completes on the next call.
|
|
417
|
+
async function bookIdeaSplit(client, { payees, reason, taskId = null, describe }) {
|
|
418
|
+
let credited = 0;
|
|
419
|
+
const booked = [];
|
|
420
|
+
for (const p of payees) {
|
|
421
|
+
const { rowCount } = await client.query(
|
|
422
|
+
`INSERT INTO credit_log (builder_id, task_id, delta, reason, description)
|
|
423
|
+
SELECT $1, $2, $3, $4, $5
|
|
424
|
+
WHERE NOT EXISTS (SELECT 1 FROM credit_log WHERE builder_id = $1 AND reason = $4)`,
|
|
425
|
+
[p.builderId, taskId, p.delta, reason, describe(p)]
|
|
426
|
+
);
|
|
427
|
+
if (rowCount > 0) credited += p.delta;
|
|
428
|
+
booked.push({ ...p, booked: rowCount > 0 });
|
|
429
|
+
}
|
|
430
|
+
return { credited, payees: booked };
|
|
431
|
+
}
|
|
432
|
+
|
|
337
433
|
// Stream 2 (ADR 0172 / IC2): a Full Idea with a passing completeness score pays
|
|
338
434
|
// its author IDEA_FULL_PASS_CREDIT, ONCE. Transaction-participant like the rest
|
|
339
435
|
// of the reward port — the caller passes its `client` (a pool works too, since
|
|
@@ -362,29 +458,46 @@ async function awardIdeaFullPassCredit(client, { ideaId, authorId, completenessS
|
|
|
362
458
|
if (completenessScore == null || !Number.isFinite(Number(completenessScore)) || Number(completenessScore) < IDEA_FULL_PASS_BAR) {
|
|
363
459
|
return { credited: 0, reason: 'below_bar' };
|
|
364
460
|
}
|
|
365
|
-
if (
|
|
461
|
+
if (ideaId == null) {
|
|
366
462
|
return { credited: 0, reason: 'missing_author_or_idea' };
|
|
367
463
|
}
|
|
464
|
+
// R147: the PARTIES come from the DB, not the call site. `authorId` is kept as
|
|
465
|
+
// a fallback only for a caller holding an idea the lookup cannot see (and for
|
|
466
|
+
// the pre-R147 call shape); when the row is there, it wins.
|
|
467
|
+
const parties = await ideaCreditParties(client, ideaId);
|
|
468
|
+
const resolvedAuthor = (parties && parties.authorId != null) ? parties.authorId : authorId;
|
|
469
|
+
if (resolvedAuthor == null) {
|
|
470
|
+
return { credited: 0, reason: 'missing_author_or_idea' };
|
|
471
|
+
}
|
|
472
|
+
const payees = splitIdeaCredit(IDEA_FULL_PASS_CREDIT, {
|
|
473
|
+
authorId: resolvedAuthor,
|
|
474
|
+
developedBy: parties ? parties.developedBy : null,
|
|
475
|
+
});
|
|
476
|
+
if (payees.length === 0) {
|
|
477
|
+
return { credited: 0, reason: 'zero_after_scaling' };
|
|
478
|
+
}
|
|
479
|
+
// Idempotency predicate is `builder_id = payee AND reason = <per-idea key>`.
|
|
480
|
+
// The `builder_id` term was originally an index optimisation (hitting
|
|
481
|
+
// `idx_credit_log_builder` instead of scanning a growing credit_log — the
|
|
482
|
+
// R143/IC2 grader finding), and R147 makes it load-bearing for CORRECTNESS
|
|
483
|
+
// too: a split books one row per payee under the SAME reason, so the predicate
|
|
484
|
+
// has to be per-builder or the second payee would be refused. That is also
|
|
485
|
+
// exactly why splitIdeaCredit collapses a self-developed idea to a single
|
|
486
|
+
// payee — two rows for one builder would be silently under-paid, not
|
|
487
|
+
// double-paid. credit_log is a CORE table, so a module migration cannot add an
|
|
488
|
+
// index on `reason` anyway (the additive-namespace rule).
|
|
368
489
|
const reason = `idea.credit.full_pass:${ideaId}`;
|
|
369
|
-
const
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
`INSERT INTO credit_log (builder_id, task_id, delta, reason, description)
|
|
381
|
-
SELECT $1, NULL, $2, $3, $4
|
|
382
|
-
WHERE NOT EXISTS (SELECT 1 FROM credit_log WHERE builder_id = $1 AND reason = $3)`,
|
|
383
|
-
[authorId, IDEA_FULL_PASS_CREDIT, reason, description]
|
|
384
|
-
);
|
|
385
|
-
return rowCount > 0
|
|
386
|
-
? { credited: IDEA_FULL_PASS_CREDIT, reason: 'paid' }
|
|
387
|
-
: { credited: 0, reason: 'already_paid' };
|
|
490
|
+
const pct = Math.round(Number(completenessScore));
|
|
491
|
+
const { credited, payees: booked } = await bookIdeaSplit(client, {
|
|
492
|
+
payees,
|
|
493
|
+
reason,
|
|
494
|
+
describe: (p) => `Full Idea #${ideaId} passed (completeness ${pct}/100) → ${p.delta}c to its ${p.role.replace(/_/g, ' ')} (ideator lane, ADR 0172${p.role === 'developer' || p.role === 'spark_author' ? '; split ADR 0185' : ''})`,
|
|
495
|
+
});
|
|
496
|
+
return {
|
|
497
|
+
credited,
|
|
498
|
+
reason: credited > 0 ? 'paid' : 'already_paid',
|
|
499
|
+
payees: booked,
|
|
500
|
+
};
|
|
388
501
|
}
|
|
389
502
|
|
|
390
503
|
// Stream 1 (ADR 0172 / goal 1000067 IC4): as each task descended from an idea
|
|
@@ -420,7 +533,8 @@ async function awardIdeaTaskShipCredit(client, { taskId, config = rewardConfig()
|
|
|
420
533
|
`SELECT l.idea_id,
|
|
421
534
|
i.captured_by,
|
|
422
535
|
COALESCE(g.grade, 'quick') AS grade,
|
|
423
|
-
g.completeness_score AS completeness
|
|
536
|
+
g.completeness_score AS completeness,
|
|
537
|
+
g.developed_by AS developed_by
|
|
424
538
|
FROM ideas_task_lineage l
|
|
425
539
|
JOIN idea_inbox i ON i.id = l.idea_id
|
|
426
540
|
LEFT JOIN ideas_grades g ON g.idea_id = l.idea_id
|
|
@@ -430,7 +544,7 @@ async function awardIdeaTaskShipCredit(client, { taskId, config = rewardConfig()
|
|
|
430
544
|
if (rows.length === 0 || rows[0].captured_by == null) {
|
|
431
545
|
return { credited: 0, reason: 'no_source_idea', ideaId: null, authorId: null };
|
|
432
546
|
}
|
|
433
|
-
const { idea_id: ideaId, captured_by: authorId, grade, completeness } = rows[0];
|
|
547
|
+
const { idea_id: ideaId, captured_by: authorId, grade, completeness, developed_by: developedBy } = rows[0];
|
|
434
548
|
// Grade factor: a Full Idea earns in proportion to its completeness; a Quick
|
|
435
549
|
// or Spark idea (no completeness score) earns the flat quick factor. A full
|
|
436
550
|
// idea with no score yet also falls back to the quick factor rather than 0 —
|
|
@@ -442,16 +556,24 @@ async function awardIdeaTaskShipCredit(client, { taskId, config = rewardConfig()
|
|
|
442
556
|
if (delta <= 0) {
|
|
443
557
|
return { credited: 0, reason: 'zero_after_scaling', ideaId: Number(ideaId), authorId: Number(authorId) };
|
|
444
558
|
}
|
|
559
|
+
// R147 (ADR 0185): if someone else developed this spark, the per-task stream is
|
|
560
|
+
// split with them too — the same 70/30 rule as stream 2, so there is one rule
|
|
561
|
+
// to explain rather than two. A developer who shaped the idea shares in what
|
|
562
|
+
// the idea goes on to deliver.
|
|
563
|
+
const payees = splitIdeaCredit(delta, { authorId, developedBy });
|
|
564
|
+
if (payees.length === 0) {
|
|
565
|
+
return { credited: 0, reason: 'zero_after_scaling', ideaId: Number(ideaId), authorId: Number(authorId) };
|
|
566
|
+
}
|
|
445
567
|
const reason = `idea.credit.task_ship:${ideaId}:${taskId}`;
|
|
446
|
-
const
|
|
447
|
-
const {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
);
|
|
453
|
-
return
|
|
454
|
-
? { credited
|
|
568
|
+
const gradeLabel = `${grade}${completeness != null ? ` ${Math.round(Number(completeness))}%` : ''}`;
|
|
569
|
+
const { credited, payees: booked } = await bookIdeaSplit(client, {
|
|
570
|
+
payees,
|
|
571
|
+
reason,
|
|
572
|
+
taskId,
|
|
573
|
+
describe: (p) => `Task #${taskId} shipped under idea #${ideaId} (${gradeLabel}) → ${p.delta}c to its ${p.role.replace(/_/g, ' ')} (ideator lane stream 1, ADR 0172${p.role === 'developer' || p.role === 'spark_author' ? '; split ADR 0185' : ''})`,
|
|
574
|
+
});
|
|
575
|
+
return credited > 0
|
|
576
|
+
? { credited, reason: 'paid', ideaId: Number(ideaId), authorId: Number(authorId), payees: booked }
|
|
455
577
|
: { credited: 0, reason: 'already_paid', ideaId: Number(ideaId), authorId: Number(authorId) };
|
|
456
578
|
}
|
|
457
579
|
|
|
@@ -667,6 +789,9 @@ module.exports = {
|
|
|
667
789
|
maybeAwardIdeaPromotionBonus,
|
|
668
790
|
awardIdeaFullPassCredit,
|
|
669
791
|
awardIdeaTaskShipCredit,
|
|
792
|
+
// the spark-handoff split (task 1002923 / BV1.R147, ADR 0185)
|
|
793
|
+
splitIdeaCredit,
|
|
794
|
+
IDEA_DEVELOPER_SHARE,
|
|
670
795
|
IDEA_FULL_PASS_CREDIT,
|
|
671
796
|
IDEA_FULL_PASS_BAR,
|
|
672
797
|
IDEA_TASK_SHIP_BASE,
|