@bongos/core 1.21.105 → 1.21.107
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 +88 -58
- 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 +6 -1
- package/clients/bongos-client/index.mjs +4 -0
- package/docs/api/openapi.json +91 -2
- package/docs/api-reference.md +5 -3
- package/docs/copy-inventory.md +368 -366
- package/docs/copy-registry.json +475 -457
- package/docs/design/project-startup-direction.md +14 -0
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-inventory.json +10 -3
- package/docs/page-readings.json +1023 -977
- package/migrations/core_275_claims_auto_pickup.sql +24 -0
- package/modules/government/decision-rules.js +1 -0
- package/modules/lifecycle/autopickup.js +94 -0
- package/modules/lifecycle/db-claims.js +18 -4
- package/modules/lifecycle/routes/claims.js +12 -1
- package/modules/lifecycle/routes/project-settings.js +59 -0
- package/modules/public-landing/public/projects-hall-live.states.json +3 -2
- package/modules/public-landing/public/projects-setup.states.json +91 -0
- package/modules/public-landing/public/projects.html +535 -804
- package/modules/public-landing/public/projects.probes.json +3 -3
- package/modules/public-landing/public/projects.states.json +1 -1
- package/modules/ui-design/kit/fixtures/provisioning-instances-hall-live.json +22 -2
- package/modules/ui-design/kit/fixtures/provisioning-instances-setup.json +195 -0
- package/modules/ui-design/kit/serve.js +22 -4
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +12 -0
- package/scripts/gds/government-walk.js +1 -0
- package/scripts/gds/run-unit-tests.js +3 -0
- package/src/bongos/project-settings.js +79 -0
- package/src/module-api.js +8 -1
- package/tests/autopickup_cap.mjs +236 -0
- package/tests/autopickup_cap_db.mjs +115 -0
- package/tests/cosmos_token_contrast.mjs +10 -18
- package/tests/github_handback_any_device.mjs +4 -3
- package/tests/github_one_trip.mjs +45 -47
- package/tests/github_org_account.mjs +2 -2
- package/tests/github_pick_up.mjs +40 -29
- package/tests/government_act_alone_buildings.mjs +1 -0
- package/tests/onboard.mjs +13 -7
- package/tests/project_invite_ui.mjs +6 -3
- package/tests/project_modules_ui.mjs +3 -3
- package/tests/projects_hub.mjs +8 -9
- package/tests/projects_hub_cloud_hop.mjs +77 -125
- package/tests/projects_hub_dns_ready.mjs +42 -51
- package/tests/projects_hub_pre_uat.mjs +68 -69
- package/tests/public_landing_projects.mjs +45 -102
- package/tests/setup_status_facts.mjs +400 -0
- package/tests/wizard_github_feedback.mjs +90 -87
- package/tests/wizard_one_page.mjs +11 -9
- package/tests/wizard_auto_open.mjs +0 -94
|
@@ -167,12 +167,12 @@
|
|
|
167
167
|
{ "name": "part 02 (task 1004648): Change opens a row in place and reads Done, opening another closes it, and nothing needs confirming", "auth": true, "width": 1440, "url": "/projects?view=new",
|
|
168
168
|
"steps": [["wait",600],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["wait",300],["click","#reviewRows .setRow[data-row=\"team\"] [data-row-chg]"],["wait",200],["click","[data-phys-solo=\"many\"]"],["wait",600],["click","#reviewRows .setRow[data-row=\"run\"] [data-row-chg]"],["wait",300]],
|
|
169
169
|
"expect": {"visible":["#reviewRows .setRow[data-row=\"rewards\"]","#reviewRows .setRow.open[data-row=\"run\"] .opts"],"hidden":["#reviewRows .setRow[data-row=\"team\"] .opts"],"text":[["#reviewRows .setRow[data-row=\"run\"] [data-row-chg]","Done"],["#reviewRows .setRow[data-row=\"team\"] [data-row-chg]","Change"]]} },
|
|
170
|
-
{ "name": "the wizard driver (task 1004648): part 01, the checks, the address row (the dotless stub host takes a domain the founder owns), the tick, then Create — part 03 goes live
|
|
170
|
+
{ "name": "the wizard driver (task 1004648): part 01, the checks, the address row (the dotless stub host takes a domain the founder owns), the tick, then Create — part 03 goes live as Getting <name> ready: one sentence, the checked facts, and the cloud hop behind its link", "auth": true, "width": 1440, "url": "/projects?view=new",
|
|
171
171
|
"steps": [["wait",600],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["wait",300],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#termsAck"],["wait",100],["click","#doCreate"],["wait",1500]],
|
|
172
|
-
"expect": {"visible":["#panel-done","#
|
|
172
|
+
"expect": {"visible":["#panel-done","#setupNow","#homeFacts","#ghFacts","#hopLink"],"hidden":["#wizParts","#hop","#doneAlive"],"text":[["#donePartT","Getting Mercury ready"]]} },
|
|
173
173
|
{ "name": "the wizard driver at 320 (narrower than a 375 phone): the page stacks, nothing scrolls sideways, and the walk still reaches part 03", "auth": true, "width": 320, "url": "/projects?view=new",
|
|
174
174
|
"steps": [["wait",600],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["wait",300],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#termsAck"],["wait",100],["click","#doCreate"],["wait",1500]],
|
|
175
|
-
"expect": {"noOverflow":true,"visible":["#panel-done","#
|
|
175
|
+
"expect": {"noOverflow":true,"visible":["#panel-done","#homeFacts","#ghFacts"],"hidden":["#wizParts"]} },
|
|
176
176
|
{ "name": "invite a guild (task 1002303, R27): a /g/ address gives one pending line per member on its public page, each a link to the hall", "auth": true, "width": 1440, "url": "/projects?view=manage&id=101",
|
|
177
177
|
"steps": [["wait",900],["fill","#mInviteGuild","/g/forge-crew"],["click","#mInvite [data-invite-guild]"],["wait",400]],
|
|
178
178
|
"expect": { "count": [["#mInviteList li", 4]], "value": [["#mInviteGuild", ""]],
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"in-new-look": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["wait",300],["click","#reviewRows .setRow[data-row=\"look\"] [data-row-chg]"],["wait",400]],"expect":{"visible":["#reviewRows .setRow.open[data-row=\"look\"] .opts","#wizLook"]}},
|
|
57
57
|
"in-new-look-custom": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["wait",300],["click","#reviewRows .setRow[data-row=\"look\"] [data-row-chg]"],["wait",400],["click","#wizLook-custom"],["wait",200]],"expect":{"visible":["#reviewRows .setRow.open[data-row=\"look\"] .opts","#wizLook","#wizAccent","#wizAccentNote"]}},
|
|
58
58
|
"in-new-create-blocked": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["click","#termsAck"],["wait",100],["click","#doCreate"],["wait",400]],"expect":{"visible":["#reviewRows .setRow.open[data-row=\"address\"] .opts","#h-domain"],"hidden":["#doneBody"],"text":[["#createWhy","Fix the row that’s open, then create."]]}},
|
|
59
|
-
"in-new-done": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["wait",300],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#termsAck"],["wait",100],["click","#doCreate"],["wait",1500]],"expect":{"visible":["#panel-done","#doneBody","#
|
|
59
|
+
"in-new-done": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["wait",300],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#termsAck"],["wait",100],["click","#doCreate"],["wait",1500]],"expect":{"visible":["#panel-done","#doneBody","#setupNow","#homeFacts","#ghFacts","#hopLink","#wizRecap"],"hidden":["#wizParts","#qWrap","#wizInvite","#hop","#doneAlive"],"attr":[["#doCreate","disabled",""],["#wizRoom","data-screen","done"]],"text":[["#donePartT","Getting Mercury ready"],["#doCreate","Created"]]}},
|
|
60
60
|
"in-new-resume-offer": {"auth":true,"url":"/projects?view=new","actions":[["wait",600],["fill","#newName","Mercury"],["click","#typeCards .tpill[data-type=\"game\"]"],["fill","#f-desc","A co-op farming game where the seasons never repeat."],["select","#physCountry","US"],["wait",100],["click","[data-phys=\"builder_geography\"][data-val=\"global\"]"],["click","[data-phys=\"minors_on_board\"][data-val=\"false\"]"],["click","[data-phys=\"entity\"][data-val=\"intent-solo\"]"],["wait",300],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["fill","#f-slug","mercury"],["wait",900],["fill","#f-domain","mercury.example.com"],["wait",200],["click","#reviewRows .setRow[data-row=\"address\"] [data-row-chg]"],["wait",200],["click","#termsAck"],["wait",100],["click","#doCreate"],["wait",1500],["click","#projTrig"],["wait",350],["click","#menuProjects [data-nav=\"new\"]"],["wait",500]],"expect":{"visible":["#panel-page","#wizResume","#wizResumeGo","#wizParts"],"hidden":["#doneBody"],"text":[["#wizResumeName","Mercury"]]}},
|
|
61
61
|
"out-new": {"auth":false,"url":"/projects?view=new","expect":{"visible":["#panel-gate","#wizSignin"],"hidden":["#qWrap","#panel-page"]}},
|
|
62
62
|
"out-map-projects": { "auth": false, "actions": [["click", "#projTrig"], ["wait", 350]], "expect": { "expanded": true, "hidden": ["#menuProjects [data-nav=\"mine\"]"] } },
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_": "The owner's projects for the kit (task 1003345): four states the mine list and the manage page render — live, standing up, snagged, torn down. Timestamps written \"$now-3m\" / \"$now-1d\" are resolved by the stub at request time so \"checked 3m ago\" stays true. Mooring (task 1003345, the grade's finding): live but unreachable since its last probe, so the restart door renders and the restart probe has a row to drive.",
|
|
2
|
+
"_": "The owner's projects for the kit (task 1003345): four states the mine list and the manage page render — live, standing up, snagged, torn down. Timestamps written \"$now-3m\" / \"$now-1d\" are resolved by the stub at request time so \"checked 3m ago\" stays true. Mooring (task 1003345, the grade's finding): live but unreachable since its last probe, so the restart door renders and the restart probe has a row to drive. Mercury (102) also carries the owner read's GitHub halves (github_app, github_setup), produced by the REAL projections with the app approved, installed and signed in, so part 03 reaches every required fact done (task 1004683).",
|
|
3
3
|
"instances": [
|
|
4
4
|
{
|
|
5
5
|
"id": 101,
|
|
@@ -44,7 +44,27 @@
|
|
|
44
44
|
"state": "running",
|
|
45
45
|
"created_at": "$now-6m"
|
|
46
46
|
},
|
|
47
|
-
"dns_resolves": true
|
|
47
|
+
"dns_resolves": true,
|
|
48
|
+
"github_app": {
|
|
49
|
+
"status": "done",
|
|
50
|
+
"app_slug": "mercury-cloudbongos-com",
|
|
51
|
+
"failure": null,
|
|
52
|
+
"started_at": "2026-10-09T12:52:53.145Z"
|
|
53
|
+
},
|
|
54
|
+
"github_setup": {
|
|
55
|
+
"checklist": {
|
|
56
|
+
"approve": "done",
|
|
57
|
+
"install": "done",
|
|
58
|
+
"sign_in": "done",
|
|
59
|
+
"device_flow": "later",
|
|
60
|
+
"connected": true
|
|
61
|
+
},
|
|
62
|
+
"install_url": null,
|
|
63
|
+
"repo": "builder/mercury",
|
|
64
|
+
"verifying": true,
|
|
65
|
+
"pick_up": null,
|
|
66
|
+
"app_org": null
|
|
67
|
+
}
|
|
48
68
|
},
|
|
49
69
|
{
|
|
50
70
|
"id": 103,
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_": "Part 03 after Create, one project per scenario of the design of record docs/design/mocks/day-night-rooms/setup-status.html (task 1004683): 201 just created, 202 waiting on GitHub, 203 installing, 204 stopped (the install never confirmed, the checking window over), 205 all done. Each row carries the github_app and github_setup halves of the owner's instance read, produced by the REAL projections (manifest-truth publicGithubApp, github-setup githubSetupRead); serve.js hands them out beside the instance on the single read. Regenerate the GitHub halves rather than hand-editing them.",
|
|
3
|
+
"instances": [
|
|
4
|
+
{
|
|
5
|
+
"id": 201,
|
|
6
|
+
"slug": "kestrel",
|
|
7
|
+
"domain": "kestrel.cloudbongos.com",
|
|
8
|
+
"target_ref": "founder/kestrel",
|
|
9
|
+
"hosting_shape": "cloud-host",
|
|
10
|
+
"onboard_mode": "greenfield",
|
|
11
|
+
"error_note": null,
|
|
12
|
+
"dns_note": null,
|
|
13
|
+
"torn_down_at": null,
|
|
14
|
+
"open_intent": null,
|
|
15
|
+
"status": "provisioning",
|
|
16
|
+
"created_at": "$now-1m",
|
|
17
|
+
"provisioned_at": null,
|
|
18
|
+
"updated_at": "$now-1m",
|
|
19
|
+
"last_verified_at": null,
|
|
20
|
+
"last_verified_up": null,
|
|
21
|
+
"dns_resolves": false,
|
|
22
|
+
"github_app": null,
|
|
23
|
+
"github_setup": {
|
|
24
|
+
"checklist": {
|
|
25
|
+
"approve": "todo",
|
|
26
|
+
"install": "todo",
|
|
27
|
+
"sign_in": "todo",
|
|
28
|
+
"device_flow": "todo",
|
|
29
|
+
"connected": false
|
|
30
|
+
},
|
|
31
|
+
"install_url": null,
|
|
32
|
+
"repo": "founder/kestrel",
|
|
33
|
+
"verifying": false,
|
|
34
|
+
"app_org": null,
|
|
35
|
+
"pick_up": null
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"id": 202,
|
|
40
|
+
"slug": "heron",
|
|
41
|
+
"domain": "heron.cloudbongos.com",
|
|
42
|
+
"target_ref": "founder/heron",
|
|
43
|
+
"hosting_shape": "cloud-host",
|
|
44
|
+
"onboard_mode": "greenfield",
|
|
45
|
+
"error_note": null,
|
|
46
|
+
"dns_note": null,
|
|
47
|
+
"torn_down_at": null,
|
|
48
|
+
"open_intent": null,
|
|
49
|
+
"status": "active",
|
|
50
|
+
"created_at": "$now-9m",
|
|
51
|
+
"provisioned_at": "$now-6m",
|
|
52
|
+
"updated_at": "$now-6m",
|
|
53
|
+
"last_verified_at": null,
|
|
54
|
+
"last_verified_up": null,
|
|
55
|
+
"dns_resolves": false,
|
|
56
|
+
"github_app": {
|
|
57
|
+
"status": "pending",
|
|
58
|
+
"app_slug": null,
|
|
59
|
+
"failure": null,
|
|
60
|
+
"started_at": "2026-10-09T12:48:57.326Z"
|
|
61
|
+
},
|
|
62
|
+
"github_setup": {
|
|
63
|
+
"checklist": {
|
|
64
|
+
"approve": "now",
|
|
65
|
+
"install": "todo",
|
|
66
|
+
"sign_in": "todo",
|
|
67
|
+
"device_flow": "todo",
|
|
68
|
+
"connected": false
|
|
69
|
+
},
|
|
70
|
+
"install_url": null,
|
|
71
|
+
"repo": "founder/heron",
|
|
72
|
+
"verifying": false,
|
|
73
|
+
"pick_up": "reopen",
|
|
74
|
+
"app_org": null
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"id": 203,
|
|
79
|
+
"slug": "plover",
|
|
80
|
+
"domain": "plover.cloudbongos.com",
|
|
81
|
+
"target_ref": "founder/plover",
|
|
82
|
+
"hosting_shape": "cloud-host",
|
|
83
|
+
"onboard_mode": "greenfield",
|
|
84
|
+
"error_note": null,
|
|
85
|
+
"dns_note": null,
|
|
86
|
+
"torn_down_at": null,
|
|
87
|
+
"open_intent": null,
|
|
88
|
+
"status": "active",
|
|
89
|
+
"created_at": "$now-14m",
|
|
90
|
+
"provisioned_at": "$now-11m",
|
|
91
|
+
"updated_at": "$now-11m",
|
|
92
|
+
"last_verified_at": "$now-9s",
|
|
93
|
+
"last_verified_up": true,
|
|
94
|
+
"dns_resolves": true,
|
|
95
|
+
"github_app": {
|
|
96
|
+
"status": "converting",
|
|
97
|
+
"app_slug": "plover-cloudbongos-com",
|
|
98
|
+
"failure": null,
|
|
99
|
+
"started_at": "2026-10-09T12:45:57.326Z"
|
|
100
|
+
},
|
|
101
|
+
"github_setup": {
|
|
102
|
+
"checklist": {
|
|
103
|
+
"approve": "done",
|
|
104
|
+
"install": "now",
|
|
105
|
+
"sign_in": "now",
|
|
106
|
+
"device_flow": "todo",
|
|
107
|
+
"connected": false
|
|
108
|
+
},
|
|
109
|
+
"install_url": "https://github.com/apps/plover-cloudbongos-com/installations/new",
|
|
110
|
+
"repo": "founder/plover",
|
|
111
|
+
"verifying": true,
|
|
112
|
+
"pick_up": null,
|
|
113
|
+
"app_org": null
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"id": 204,
|
|
118
|
+
"slug": "tern",
|
|
119
|
+
"domain": "tern.cloudbongos.com",
|
|
120
|
+
"target_ref": "founder/tern",
|
|
121
|
+
"hosting_shape": "cloud-host",
|
|
122
|
+
"onboard_mode": "greenfield",
|
|
123
|
+
"error_note": null,
|
|
124
|
+
"dns_note": null,
|
|
125
|
+
"torn_down_at": null,
|
|
126
|
+
"open_intent": null,
|
|
127
|
+
"status": "active",
|
|
128
|
+
"created_at": "$now-4h",
|
|
129
|
+
"provisioned_at": "$now-235m",
|
|
130
|
+
"updated_at": "$now-235m",
|
|
131
|
+
"last_verified_at": "$now-9s",
|
|
132
|
+
"last_verified_up": true,
|
|
133
|
+
"dns_resolves": true,
|
|
134
|
+
"github_app": {
|
|
135
|
+
"status": "done",
|
|
136
|
+
"app_slug": "tern-cloudbongos-com",
|
|
137
|
+
"failure": null,
|
|
138
|
+
"started_at": "2026-10-09T08:59:57.326Z"
|
|
139
|
+
},
|
|
140
|
+
"github_setup": {
|
|
141
|
+
"checklist": {
|
|
142
|
+
"approve": "done",
|
|
143
|
+
"install": "now",
|
|
144
|
+
"sign_in": "done",
|
|
145
|
+
"device_flow": "later",
|
|
146
|
+
"connected": false
|
|
147
|
+
},
|
|
148
|
+
"install_url": "https://github.com/apps/tern-cloudbongos-com/installations/new",
|
|
149
|
+
"repo": "founder/tern",
|
|
150
|
+
"verifying": false,
|
|
151
|
+
"pick_up": "check",
|
|
152
|
+
"app_org": null
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"id": 205,
|
|
157
|
+
"slug": "wren",
|
|
158
|
+
"domain": "wren.cloudbongos.com",
|
|
159
|
+
"target_ref": "founder/wren",
|
|
160
|
+
"hosting_shape": "cloud-host",
|
|
161
|
+
"onboard_mode": "greenfield",
|
|
162
|
+
"error_note": null,
|
|
163
|
+
"dns_note": null,
|
|
164
|
+
"torn_down_at": null,
|
|
165
|
+
"open_intent": null,
|
|
166
|
+
"status": "active",
|
|
167
|
+
"created_at": "$now-30m",
|
|
168
|
+
"provisioned_at": "$now-27m",
|
|
169
|
+
"updated_at": "$now-27m",
|
|
170
|
+
"last_verified_at": "$now-3s",
|
|
171
|
+
"last_verified_up": true,
|
|
172
|
+
"dns_resolves": true,
|
|
173
|
+
"github_app": {
|
|
174
|
+
"status": "done",
|
|
175
|
+
"app_slug": "wren-cloudbongos-com",
|
|
176
|
+
"failure": null,
|
|
177
|
+
"started_at": "2026-10-09T12:25:57.326Z"
|
|
178
|
+
},
|
|
179
|
+
"github_setup": {
|
|
180
|
+
"checklist": {
|
|
181
|
+
"approve": "done",
|
|
182
|
+
"install": "done",
|
|
183
|
+
"sign_in": "done",
|
|
184
|
+
"device_flow": "later",
|
|
185
|
+
"connected": true
|
|
186
|
+
},
|
|
187
|
+
"install_url": null,
|
|
188
|
+
"repo": "founder/wren",
|
|
189
|
+
"verifying": true,
|
|
190
|
+
"pick_up": null,
|
|
191
|
+
"app_org": null
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
}
|
|
@@ -309,12 +309,27 @@ function instances(res, id, settings) {
|
|
|
309
309
|
if (FAIL.has(name)) return json(res, 500, { error: { code: 'stub_failure', message: `FAIL=${name}` } });
|
|
310
310
|
const file = path.join(FIXTURES, (ALIAS[name] || name) + '.json');
|
|
311
311
|
if (!fs.existsSync(file)) return json(res, 404, { error: { code: 'no_fixture', message: name } });
|
|
312
|
-
|
|
312
|
+
// a fixture row may carry the owner read's two GitHub halves (github_app, github_setup —
|
|
313
|
+
// the shapes modules/provisioning/routes/provisioning.js adds, task 1004683); they ride
|
|
314
|
+
// the single read beside the instance, never inside it, and never the list
|
|
315
|
+
const rows = stamp((JSON.parse(fs.readFileSync(file, 'utf8')).instances || []).map(withAsk).map(withPlanet));
|
|
316
|
+
const bare = (i) => { const { github_app, github_setup, ...rest } = i; return rest; };
|
|
317
|
+
const list = rows.map(bare);
|
|
313
318
|
if (id === null) return json(res, 200, { instances: list });
|
|
314
|
-
const
|
|
315
|
-
if (!
|
|
319
|
+
const row = rows.find((i) => String(i.id) === String(id));
|
|
320
|
+
if (!row) return json(res, 404, { error: { code: 'not_found', message: 'no such instance' } });
|
|
321
|
+
const inst = bare(row);
|
|
316
322
|
if (settings) return json(res, 200, { settings: { platform_visibility: 'public', joinability: 'open', visibility: 'public' }, planet: PLANETS.get(String(id)) || NO_PLANET, options: { platform_visibility: ['public', 'gated'], joinability: ['open', 'apply', 'invite_only'], visibility: ['public', 'private', 'stealth'] } });
|
|
317
|
-
return json(res, 200, { instance: inst, open_intent: inst.open_intent || null });
|
|
323
|
+
return json(res, 200, { instance: inst, open_intent: inst.open_intent || null, github_app: row.github_app || null, github_setup: row.github_setup || null });
|
|
324
|
+
}
|
|
325
|
+
// GET /provisioning/github-app/resume?id=N — the route's resume record (routes/github-setup.js
|
|
326
|
+
// resumeRecord) for a fixture project, so a render can open part 03 on it by URL alone
|
|
327
|
+
function resumeAnswer(res, id) {
|
|
328
|
+
const file = path.join(FIXTURES, (ALIAS['provisioning-instances'] || 'provisioning-instances') + '.json');
|
|
329
|
+
const rows = fs.existsSync(file) ? (JSON.parse(fs.readFileSync(file, 'utf8')).instances || []) : [];
|
|
330
|
+
const inst = id ? rows.find((i) => String(i.id) === String(id)) : null;
|
|
331
|
+
const repo = inst && /^[A-Za-z0-9][A-Za-z0-9-]{0,38}\/[A-Za-z0-9._-]{1,100}$/.test(String(inst.target_ref || '')) ? inst.target_ref : null;
|
|
332
|
+
return json(res, 200, { ok: true, project: inst ? { id: inst.id, slug: inst.slug, domain: inst.domain || null, onboard_mode: inst.onboard_mode || 'greenfield', repo } : null });
|
|
318
333
|
}
|
|
319
334
|
// A project's art in the featured fixture may be root-relative (the surface's
|
|
320
335
|
// own /assets/…, served by this stub) — the page accepts only an http(s) art
|
|
@@ -644,6 +659,9 @@ const server = http.createServer((req, res) => {
|
|
|
644
659
|
if (/^\/provisioning\/instances\/\d+\/invite-suggestions$/.test(rest) && req.method === 'GET') {
|
|
645
660
|
return AUTH ? json(res, 200, { suggested: [], seeking: null, reason: null }) : json(res, 401, UNAUTH);
|
|
646
661
|
}
|
|
662
|
+
if (rest === '/provisioning/github-app/resume' && req.method === 'GET') {
|
|
663
|
+
return AUTH ? resumeAnswer(res, u.searchParams.get('id')) : json(res, 401, UNAUTH);
|
|
664
|
+
}
|
|
647
665
|
const one = rest.match(/^\/provisioning\/instances\/(\d+)(\/settings)?$/);
|
|
648
666
|
if (one && req.method === 'GET') return AUTH ? instances(res, one[1], !!one[2]) : json(res, 401, UNAUTH);
|
|
649
667
|
// the hub's own cross-project surface (task 1003774): the requested/leave/
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.107",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.21.
|
|
9
|
+
"version": "1.21.107",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.21.
|
|
3
|
+
"version": "1.21.107",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
package/release-notes.json
CHANGED
|
@@ -9476,5 +9476,17 @@
|
|
|
9476
9476
|
"id": "1004666",
|
|
9477
9477
|
"text": "A check that is still running is no longer reported as a failed one. Before, a project whose test service posts a \"still working\" status would see its change called broken and trigger a pointless repair attempt; now it is corr"
|
|
9478
9478
|
}
|
|
9479
|
+
],
|
|
9480
|
+
"1.21.106": [
|
|
9481
|
+
{
|
|
9482
|
+
"id": "1004683",
|
|
9483
|
+
"text": "After pressing Create, a founder now sees \"Getting <name> ready\" with a plain count (\"4 of 6 done\", or \"ready\" with a tick) and one sentence saying what is happening and whether it is their turn. Below it are two short lists,"
|
|
9484
|
+
}
|
|
9485
|
+
],
|
|
9486
|
+
"1.21.107": [
|
|
9487
|
+
{
|
|
9488
|
+
"id": "1004677",
|
|
9489
|
+
"text": "A project now has a switch for letting a runner pick up submitted page tweaks on its own, off by default, with a monthly limit of 20 rounds. The server enforces the limit when the work is claimed, so two runners can never both"
|
|
9490
|
+
}
|
|
9479
9491
|
]
|
|
9480
9492
|
}
|
|
@@ -70,6 +70,7 @@ const BAD = 'x';
|
|
|
70
70
|
const REQUESTS = Object.freeze({
|
|
71
71
|
'PATCH /project-settings/rot-days': ['PATCH', '/project-settings/rot-days', { days: 'soon' }],
|
|
72
72
|
'PATCH /project-settings/ship-mode': ['PATCH', '/project-settings/ship-mode', { mode: 'sometimes' }],
|
|
73
|
+
'PATCH /project-settings/tweak-autopickup': ['PATCH', '/project-settings/tweak-autopickup', { mode: 'sometimes' }],
|
|
73
74
|
'PATCH /work-categories/:id': ['PATCH', `/work-categories/${BAD}`, {}],
|
|
74
75
|
'POST /security/reports/:id/confirm': ['POST', `/security/reports/${BAD}/confirm`, {}],
|
|
75
76
|
'POST /security/reports/:id/dispute': ['POST', `/security/reports/${BAD}/dispute`, {}],
|
|
@@ -121,6 +121,9 @@ const INTEGRATION = new Set([
|
|
|
121
121
|
// index actually refuses the commingling INSERT and still allows the parallel
|
|
122
122
|
// -worktree case. It self-skips without a DB, and refuses any database that
|
|
123
123
|
// has a `builders` table, since it TRUNCATEs claims.
|
|
124
|
+
// task 1004677: two marked auto-pickup claims racing for the last slot of the
|
|
125
|
+
// monthly cap; only real connections prove the advisory lock. Scratch DB only.
|
|
126
|
+
'autopickup_cap_db',
|
|
124
127
|
'claims_requested_worktree_db',
|
|
125
128
|
'clawback_confirmed_backlog',
|
|
126
129
|
// task 1003113: the copy_desk_flags schema half. The module's own tests are
|
|
@@ -50,6 +50,8 @@ const SETTING_KEYS = {
|
|
|
50
50
|
ROT_DAYS: 'attention.rot_days',
|
|
51
51
|
FOCUS_VERSION: 'roadmap.focus_version',
|
|
52
52
|
SHIP_MODE: 'ship.mode',
|
|
53
|
+
TWEAK_AUTOPICKUP: 'tweak.autopickup',
|
|
54
|
+
TWEAK_AUTOPICKUP_CAP: 'tweak.autopickup_monthly_cap',
|
|
53
55
|
};
|
|
54
56
|
|
|
55
57
|
// SHIP MODE (task 1004628, ADR 0369) — WHO starts a ship, never HOW it lands.
|
|
@@ -278,6 +280,75 @@ function describeShipMode(resolved) {
|
|
|
278
280
|
return `${what}${how}`;
|
|
279
281
|
}
|
|
280
282
|
|
|
283
|
+
|
|
284
|
+
// TWEAK AUTO-PICKUP (task 1004677, ADR 0371 D3/D5). Whether the Autobongos runner's
|
|
285
|
+
// tweak lane may pick up a submitted page round on its own, and how many rounds it
|
|
286
|
+
// may pick up per calendar month. Off by default: a project that never turns it on
|
|
287
|
+
// keeps today's queue exactly. The cap is ENFORCED by the server at claim time
|
|
288
|
+
// (modules/lifecycle/autopickup.js); this file only resolves the numbers.
|
|
289
|
+
const AUTOPICKUP_MODES = Object.freeze(['off', 'on']);
|
|
290
|
+
const DEFAULT_AUTOPICKUP = 'off';
|
|
291
|
+
// 20 rounds: a grade costs the project a few dollars, so 20 is a bounded, legible
|
|
292
|
+
// monthly spend at about the level of one active artist (ADR 0371 D5).
|
|
293
|
+
const DEFAULT_AUTOPICKUP_CAP = 20;
|
|
294
|
+
// Past 1000 the cap is an off switch typed as a big number; that should be asked
|
|
295
|
+
// for out loud, the same reasoning as MAX_ROT_DAYS.
|
|
296
|
+
const MAX_AUTOPICKUP_CAP = 1000;
|
|
297
|
+
|
|
298
|
+
function parseAutopickup(raw) {
|
|
299
|
+
if (raw == null || String(raw).trim() === '') return { reason: 'empty' };
|
|
300
|
+
const v = String(raw).trim().toLowerCase();
|
|
301
|
+
if (!AUTOPICKUP_MODES.includes(v)) return { reason: `must be one of ${AUTOPICKUP_MODES.join(' | ')}` };
|
|
302
|
+
return { mode: v };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function parseAutopickupCap(raw) {
|
|
306
|
+
if (raw == null || raw === '') return { reason: 'empty' };
|
|
307
|
+
const n = Number(raw);
|
|
308
|
+
if (!Number.isFinite(n)) return { reason: 'not a number' };
|
|
309
|
+
if (!Number.isInteger(n)) return { reason: 'must be a whole number of rounds' };
|
|
310
|
+
if (n <= 0) return { reason: 'must be positive' };
|
|
311
|
+
if (n > MAX_AUTOPICKUP_CAP) return { reason: `out of range (1..${MAX_AUTOPICKUP_CAP})` };
|
|
312
|
+
return { cap: n };
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// One knob through its parser: { value, source, invalid }. Never throws; an
|
|
316
|
+
// unreadable database answers the default, the resolveShipMode posture.
|
|
317
|
+
async function resolveOne(key, parse, pick, fallback) {
|
|
318
|
+
let row = null;
|
|
319
|
+
try {
|
|
320
|
+
row = await getSetting(key);
|
|
321
|
+
} catch (err) {
|
|
322
|
+
logger.error(`project_settings read failed; falling back to the default ${key}: ${err && err.message}`);
|
|
323
|
+
return { value: fallback, source: 'default', invalid: null };
|
|
324
|
+
}
|
|
325
|
+
if (row && row.value != null && row.value !== '') {
|
|
326
|
+
const got = parse(row.value);
|
|
327
|
+
if (pick(got) != null) return { value: pick(got), source: 'project_settings', invalid: null };
|
|
328
|
+
return { value: fallback, source: 'default', invalid: { where: `project_settings['${key}']`, raw: String(row.value), reason: got.reason } };
|
|
329
|
+
}
|
|
330
|
+
return { value: fallback, source: 'default', invalid: null };
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// resolveTweakAutopickup() — { mode, cap, source: { mode, cap }, invalid: [...] }.
|
|
334
|
+
// mode is always 'off' or 'on', cap always a usable positive integer.
|
|
335
|
+
async function resolveTweakAutopickup() {
|
|
336
|
+
const m = await resolveOne(SETTING_KEYS.TWEAK_AUTOPICKUP, parseAutopickup, (g) => g.mode, DEFAULT_AUTOPICKUP);
|
|
337
|
+
const c = await resolveOne(SETTING_KEYS.TWEAK_AUTOPICKUP_CAP, parseAutopickupCap, (g) => g.cap, DEFAULT_AUTOPICKUP_CAP);
|
|
338
|
+
return {
|
|
339
|
+
mode: m.value,
|
|
340
|
+
cap: c.value,
|
|
341
|
+
source: { mode: m.source, cap: c.source },
|
|
342
|
+
invalid: [m.invalid, c.invalid].filter(Boolean),
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function describeTweakAutopickup(resolved) {
|
|
347
|
+
const r = resolved || {};
|
|
348
|
+
if (r.mode !== 'on') return 'Off: a submitted page waits until a person runs /tweak for it.';
|
|
349
|
+
return `On: a running Autobongos runner applies submitted pages on its own, up to ${r.cap} a month. The artist still approves every one.`;
|
|
350
|
+
}
|
|
351
|
+
|
|
281
352
|
// parseDays is exported for the tests only — the range check and the default are
|
|
282
353
|
// asserted against the SAME constants the resolver reads, because a test that
|
|
283
354
|
// re-typed 30 or 365 would be the second copy of the number this file exists to
|
|
@@ -299,4 +370,12 @@ module.exports = {
|
|
|
299
370
|
parseShipMode,
|
|
300
371
|
resolveShipMode,
|
|
301
372
|
describeShipMode,
|
|
373
|
+
AUTOPICKUP_MODES,
|
|
374
|
+
DEFAULT_AUTOPICKUP,
|
|
375
|
+
DEFAULT_AUTOPICKUP_CAP,
|
|
376
|
+
MAX_AUTOPICKUP_CAP,
|
|
377
|
+
parseAutopickup,
|
|
378
|
+
parseAutopickupCap,
|
|
379
|
+
resolveTweakAutopickup,
|
|
380
|
+
describeTweakAutopickup,
|
|
302
381
|
};
|
package/src/module-api.js
CHANGED
|
@@ -75,7 +75,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
|
|
|
75
75
|
// MAJOR (see allowBoxScope below): passes the request through untouched.
|
|
76
76
|
function deprecatedNoopMiddleware(_req, _res, next) { next(); }
|
|
77
77
|
|
|
78
|
-
const CORE_VERSION = '1.21.
|
|
78
|
+
const CORE_VERSION = '1.21.107'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
|
|
79
79
|
|
|
80
80
|
// A namespaced logger so a module's log lines are attributable + consistent.
|
|
81
81
|
// Usage: const log = api.logger('discord'); log.info('mounted');
|
|
@@ -474,6 +474,13 @@ module.exports = {
|
|
|
474
474
|
get parseShipMode() { return require('./bongos/project-settings').parseShipMode; },
|
|
475
475
|
get resolveShipMode() { return require('./bongos/project-settings').resolveShipMode; },
|
|
476
476
|
get describeShipMode() { return require('./bongos/project-settings').describeShipMode; },
|
|
477
|
+
// Tweak auto-pickup (task 1004677, ADR 0371 D3/D5): the switch and the monthly cap.
|
|
478
|
+
get AUTOPICKUP_MODES() { return require('./bongos/project-settings').AUTOPICKUP_MODES; },
|
|
479
|
+
get MAX_AUTOPICKUP_CAP() { return require('./bongos/project-settings').MAX_AUTOPICKUP_CAP; },
|
|
480
|
+
get parseAutopickup() { return require('./bongos/project-settings').parseAutopickup; },
|
|
481
|
+
get parseAutopickupCap() { return require('./bongos/project-settings').parseAutopickupCap; },
|
|
482
|
+
get resolveTweakAutopickup() { return require('./bongos/project-settings').resolveTweakAutopickup; },
|
|
483
|
+
get describeTweakAutopickup() { return require('./bongos/project-settings').describeTweakAutopickup; },
|
|
477
484
|
},
|
|
478
485
|
readConfigFileSync: instanceConfig.readConfigFileSync,
|
|
479
486
|
configHome: instanceConfig.configHome,
|