@bongos/core 1.20.81 → 1.20.83
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 +71 -46
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +2 -0
- package/clients/bongos-client/index.cjs +2 -0
- package/clients/bongos-client/index.d.ts +2 -0
- package/clients/bongos-client/index.mjs +2 -0
- package/docs/api/openapi.json +46 -1
- package/docs/api-reference.md +3 -2
- package/docs/copy-inventory.md +33 -26
- package/docs/copy-registry.json +164 -92
- package/docs/module-api-changelog.md +4 -0
- package/docs/page-readings.json +58 -43
- package/modules/economy/earnings.js +117 -0
- package/modules/economy/module.json +1 -1
- package/modules/economy/routes/earnings.js +47 -0
- package/modules/hall-ui/public/gate.css +49 -0
- package/modules/hall-ui/public/gate.html +5 -7
- package/modules/hall-ui/public/gate.js +215 -127
- package/modules/hall-ui/public/profile-charts.js +206 -104
- package/modules/hall-ui/public/profile.css +23 -0
- package/modules/hall-ui/public/profile.js +3 -3
- package/modules/hall-ui/public/profile.states.json +8 -4
- package/modules/hall-ui/records/profile-roles.md +21 -0
- package/modules/lifecycle/db-goals.js +6 -0
- package/modules/lifecycle/db-versions.js +3 -0
- package/modules/lifecycle/done-when.js +85 -41
- package/modules/lifecycle/goal-close-sweep.js +34 -0
- package/modules/provisioning/app-status.js +17 -4
- package/modules/public-landing/public/projects.html +7 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +28 -0
- package/scripts/hall-preview/server.js +4 -0
- package/src/module-api.js +1 -1
- package/tests/auto_satisfy_criteria.mjs +28 -6
- package/tests/autonomy_builder_scope.mjs +1 -1
- package/tests/autonomy_fence_priority.mjs +5 -3
- package/tests/autonomy_runner_identity.mjs +1 -1
- package/tests/criterion_goal_attach.mjs +5 -1
- package/tests/goal_close_cancels_requests.mjs +82 -0
- package/tests/goal_closure_invariants.mjs +73 -2
- package/tests/hall_profile_world.mjs +57 -58
- package/tests/profile_earnings.mjs +237 -0
- package/tests/projects_hub_app_status.mjs +17 -0
- package/tests/provisioning_app_status.mjs +25 -1
package/docs/page-readings.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"adr": "0341 D3",
|
|
6
6
|
"reread": "node scripts/gds/page-reader.js --stale",
|
|
7
7
|
"how": ["Generated by node scripts/gds/page-reader.js (BV2.TW04, task 1004315; ADR 0341 D3). Do not hand-edit.","One entry per page of docs/page-inventory.json. Lines are in page order: the default state first, and a line only another state shows sits where that state shows it, with its state named.","placement: placed = a docs/copy-registry.json row renders it (file, line, string_id); shared = shell text every page of the surface carries (top bar, sidebar, footer), with its row when the registry has one; unplaced = the registry cannot place it (a rewrite becomes an engineer task, ADR 0341 D4).","text keeps {…} where the registry row has a hole, or where a value the page read from its API was filled in. A string that is only such data is counted under omitted.data, not listed.","reading_hash is over the non-shared lines (key, section, text); files_hash is over the page's inventory files. --check compares files_hash only and never opens a browser.","Rendered through the hall-preview harness (hall) and the ui-design kit stub (landing, status), at 1280x800 with the clock pinned, so a reading is the fixture world's page."],
|
|
8
|
-
"counts": {"pages":40,"lines":
|
|
8
|
+
"counts": {"pages":40,"lines":4586,"placed":1619,"shared":245,"unplaced":2722,"omitted_data":2258},
|
|
9
9
|
"pages": [
|
|
10
10
|
{
|
|
11
11
|
"id": "builders:atlas",
|
|
@@ -946,7 +946,7 @@
|
|
|
946
946
|
"surface": "builders",
|
|
947
947
|
"title": "Gate",
|
|
948
948
|
"states_read": ["queue","out","approve"],
|
|
949
|
-
"files_hash": "
|
|
949
|
+
"files_hash": "55951b0b8d356514",
|
|
950
950
|
"reading_hash": "08d3e5d3657e5afb",
|
|
951
951
|
"counts": {"lines":41,"placed":17,"shared":5,"unplaced":19},
|
|
952
952
|
"omitted": {"data":34},
|
|
@@ -957,15 +957,15 @@
|
|
|
957
957
|
{"key":"L0003","section":"top bar","text":"Jump to…","placement":"shared","file":"modules/hall-ui/public/shell.js","line":495,"string_id":"eba610d25523"},
|
|
958
958
|
{"key":"L0004","section":"top bar","text":"⌘K","placement":"shared"},
|
|
959
959
|
{"key":"L0005","section":"Gate","text":"Gate","placement":"unplaced"},
|
|
960
|
-
{"key":"L0006","section":"Gate","text":"Changes touching protected build surfaces, held for your approval.","placement":"placed","file":"modules/hall-ui/public/gate.js","line":
|
|
960
|
+
{"key":"L0006","section":"Gate","text":"Changes touching protected build surfaces, held for your approval.","placement":"placed","file":"modules/hall-ui/public/gate.js","line":605,"string_id":"4b714191b423"},
|
|
961
961
|
{"key":"L0007","section":"Gate","text":"Awaiting your approval","placement":"unplaced"},
|
|
962
962
|
{"key":"L0008","section":"Gate","text":"held at the gate check","placement":"unplaced"},
|
|
963
963
|
{"key":"L0009","section":"Gate","text":"Hard-floor holds","placement":"unplaced"},
|
|
964
964
|
{"key":"L0010","section":"Gate","text":"touch a surface only an owner may clear","placement":"unplaced"},
|
|
965
965
|
{"key":"L0011","section":"Gate","text":"Trust holds","placement":"unplaced"},
|
|
966
966
|
{"key":"L0012","section":"Gate","text":"an author the gate does not yet trust","placement":"unplaced"},
|
|
967
|
-
{"key":"L0013","section":"Held pull requests","text":"Held pull requests","placement":"placed","file":"modules/hall-ui/public/gate.html","line":
|
|
968
|
-
{"key":"L0014","section":"Held pull requests","text":"Each one touched a protected surface and stopped at the gate check. Approving records your seal on that commit and re-runs the check.","placement":"placed","file":"modules/hall-ui/public/gate.html","line":
|
|
967
|
+
{"key":"L0013","section":"Held pull requests","text":"Held pull requests","placement":"placed","file":"modules/hall-ui/public/gate.html","line":64,"string_id":"adac4cd8b973"},
|
|
968
|
+
{"key":"L0014","section":"Held pull requests","text":"Each one touched a protected surface and stopped at the gate check. Approving records your seal on that commit and re-runs the check.","placement":"placed","file":"modules/hall-ui/public/gate.html","line":65,"string_id":"77b7e3abfb07"},
|
|
969
969
|
{"key":"L0015","section":"Held pull requests","text":"{…} and {…}","placement":"placed","file":"modules/hall-ui/public/profile-nudge.js","line":77,"string_id":"56b00d773232","occurrences":2},
|
|
970
970
|
{"key":"L0016","section":"Held pull requests","text":"Hard floor","placement":"unplaced","occurrences":2},
|
|
971
971
|
{"key":"L0017","section":"Held pull requests","text":"task {…}","placement":"placed","file":"modules/hall-ui/public/gate.js","line":157,"string_id":"4d8ee7d68974","occurrences":2},
|
|
@@ -2180,10 +2180,10 @@
|
|
|
2180
2180
|
"surface": "builders",
|
|
2181
2181
|
"title": "Profile",
|
|
2182
2182
|
"states_read": ["own","works","activity","activity-artist","activity-ideator","other","artist","ideator","out"],
|
|
2183
|
-
"files_hash": "
|
|
2184
|
-
"reading_hash": "
|
|
2185
|
-
"counts": {"lines":
|
|
2186
|
-
"omitted": {"data":
|
|
2183
|
+
"files_hash": "80ffb3177c51a643",
|
|
2184
|
+
"reading_hash": "01da0310cc842191",
|
|
2185
|
+
"counts": {"lines":173,"placed":63,"shared":5,"unplaced":105},
|
|
2186
|
+
"omitted": {"data":196},
|
|
2187
2187
|
"notes": [],
|
|
2188
2188
|
"lines": [
|
|
2189
2189
|
{"key":"L0001","section":"skip link","text":"Skip to content","placement":"shared","file":"modules/hall-ui/public/shell.js","line":884,"string_id":"87cee78ca134"},
|
|
@@ -2220,7 +2220,7 @@
|
|
|
2220
2220
|
{"key":"L0032","section":"Activity, by role","text":"Size","placement":"unplaced","state":"activity-ideator"},
|
|
2221
2221
|
{"key":"L0033","section":"Activity, by role","text":"Still alive","placement":"unplaced","state":"activity-ideator"},
|
|
2222
2222
|
{"key":"L0034","section":"Activity, by role","text":"64 cr","placement":"unplaced","state":"activity-ideator"},
|
|
2223
|
-
{"key":"L0035","section":"Activity, by role","text":"Became a
|
|
2223
|
+
{"key":"L0035","section":"Activity, by role","text":"Became a {…}","placement":"unplaced","state":"activity-ideator","occurrences":6},
|
|
2224
2224
|
{"key":"L0036","section":"Activity, by role","text":"Earned 64 {…} so far","placement":"unplaced","state":"activity-ideator"},
|
|
2225
2225
|
{"key":"L0037","section":"Activity, by role","text":"42 cr","placement":"unplaced","state":"activity-ideator"},
|
|
2226
2226
|
{"key":"L0038","section":"Activity, by role","text":"Earned 42 {…} so far","placement":"unplaced","state":"activity-ideator"},
|
|
@@ -2312,38 +2312,53 @@
|
|
|
2312
2312
|
{"key":"L0124","section":"Over time","text":"90 days","placement":"unplaced"},
|
|
2313
2313
|
{"key":"L0125","section":"Over time","text":"1 year","placement":"unplaced"},
|
|
2314
2314
|
{"key":"L0126","section":"Over time","text":"All time","placement":"placed","file":"modules/hall-ui/public/hall-widgets.js","line":161,"string_id":"627c683a5e5d"},
|
|
2315
|
-
{"key":"L0127","section":"Over time","text":"
|
|
2316
|
-
{"key":"L0128","section":"Over time","text":"{…}
|
|
2317
|
-
{"key":"L0129","section":"Over time","text":"
|
|
2318
|
-
{"key":"L0130","section":"Over time","text":"
|
|
2319
|
-
{"key":"L0131","section":"Over time","text":"
|
|
2320
|
-
{"key":"L0132","section":"Over time","text":"
|
|
2321
|
-
{"key":"L0133","section":"Over time","text":"{…} earned","placement":"placed","file":"modules/hall-ui/public/goals-render.js","line":828,"string_id":"cf89ac85440d"},
|
|
2322
|
-
{"key":"L0134","section":"
|
|
2323
|
-
{"key":"L0135","section":"
|
|
2324
|
-
{"key":"L0136","section":"
|
|
2325
|
-
{"key":"L0137","section":"
|
|
2326
|
-
{"key":"L0138","section":"
|
|
2327
|
-
{"key":"L0139","section":"
|
|
2328
|
-
{"key":"L0140","section":"
|
|
2329
|
-
{"key":"L0141","section":"
|
|
2330
|
-
{"key":"L0142","section":"
|
|
2331
|
-
{"key":"L0143","section":"
|
|
2332
|
-
{"key":"L0144","section":"
|
|
2333
|
-
{"key":"L0145","section":"
|
|
2334
|
-
{"key":"L0146","section":"
|
|
2335
|
-
{"key":"L0147","section":"
|
|
2336
|
-
{"key":"L0148","section":"
|
|
2337
|
-
{"key":"L0149","section":"
|
|
2338
|
-
{"key":"L0150","section":"
|
|
2339
|
-
{"key":"L0151","section":"
|
|
2340
|
-
{"key":"L0152","section":"
|
|
2341
|
-
{"key":"L0153","section":"
|
|
2342
|
-
{"key":"L0154","section":"
|
|
2343
|
-
{"key":"L0155","section":"
|
|
2344
|
-
{"key":"L0156","section":"
|
|
2345
|
-
{"key":"L0157","section":"
|
|
2346
|
-
{"key":"L0158","section":"
|
|
2315
|
+
{"key":"L0127","section":"Over time","text":"Earnings over time","placement":"placed","file":"modules/hall-ui/public/profile-charts.js","line":455,"string_id":"8e04bed00757"},
|
|
2316
|
+
{"key":"L0128","section":"Over time","text":"{…} has never {…} a {…}. Every credit here came from ideas.","placement":"unplaced","state":"ideator"},
|
|
2317
|
+
{"key":"L0129","section":"Over time","text":"{…} of {…}","placement":"placed","file":"modules/hall-ui/public/sky-panel.js","line":120,"string_id":"73c1372b43d9","state":"ideator","occurrences":4},
|
|
2318
|
+
{"key":"L0130","section":"Over time","text":"{…} and {…}","placement":"placed","file":"modules/hall-ui/public/profile-nudge.js","line":77,"string_id":"56b00d773232","state":"artist"},
|
|
2319
|
+
{"key":"L0131","section":"Over time","text":"today","placement":"unplaced"},
|
|
2320
|
+
{"key":"L0132","section":"Over time","text":"as a table","placement":"placed","file":"modules/hall-ui/public/profile-charts.js","line":267,"string_id":"b4033000c537"},
|
|
2321
|
+
{"key":"L0133","section":"Over time","text":"{…} earned","placement":"placed","file":"modules/hall-ui/public/goals-render.js","line":828,"string_id":"cf89ac85440d","state":"ideator","occurrences":3},
|
|
2322
|
+
{"key":"L0134","section":"where it came from","text":"where it came from","placement":"placed","file":"modules/hall-ui/public/profile-charts.js","line":455,"string_id":"0d4acfac53bc"},
|
|
2323
|
+
{"key":"L0135","section":"where it came from","text":"Pages approved","placement":"unplaced","state":"artist"},
|
|
2324
|
+
{"key":"L0136","section":"where it came from","text":"Session rewards","placement":"unplaced"},
|
|
2325
|
+
{"key":"L0137","section":"where it came from","text":"Tasks {…}","placement":"unplaced"},
|
|
2326
|
+
{"key":"L0138","section":"where it came from","text":"Ideas that became {…} work","placement":"unplaced"},
|
|
2327
|
+
{"key":"L0139","section":"where it came from","text":"{…} split from their ideas","placement":"unplaced","state":"ideator"},
|
|
2328
|
+
{"key":"L0140","section":"where it came from","text":"Ratified by the board","placement":"unplaced","state":"ideator"},
|
|
2329
|
+
{"key":"L0141","section":"where it came from","text":"Full Ideas passed","placement":"unplaced","state":"ideator"},
|
|
2330
|
+
{"key":"L0142","section":"where it came from","text":"No works shipped yet — the works charts draw once the first one lands.","placement":"placed","file":"modules/hall-ui/public/profile-charts.js","line":574,"string_id":"9cf1c752b65d","state":"artist"},
|
|
2331
|
+
{"key":"L0143","section":"where it came from","text":"Other {…}","placement":"unplaced"},
|
|
2332
|
+
{"key":"L0144","section":"where it came from","text":"Works {…}","placement":"unplaced"},
|
|
2333
|
+
{"key":"L0145","section":"where it came from","text":"{…} of {…}","placement":"placed","file":"modules/hall-ui/public/sky-panel.js","line":120,"string_id":"73c1372b43d9","occurrences":2},
|
|
2334
|
+
{"key":"L0146","section":"where it came from","text":"24 works","placement":"unplaced"},
|
|
2335
|
+
{"key":"L0147","section":"where it came from","text":"today","placement":"unplaced"},
|
|
2336
|
+
{"key":"L0148","section":"where it came from","text":"as a table","placement":"placed","file":"modules/hall-ui/public/profile-charts.js","line":267,"string_id":"b4033000c537","occurrences":2},
|
|
2337
|
+
{"key":"L0149","section":"where it came from","text":"24 total · all time","placement":"unplaced"},
|
|
2338
|
+
{"key":"L0150","section":"where it came from","text":"Works by role","placement":"unplaced"},
|
|
2339
|
+
{"key":"L0151","section":"where it came from","text":"UI","placement":"unplaced"},
|
|
2340
|
+
{"key":"L0152","section":"where it came from","text":"Artist","placement":"unplaced"},
|
|
2341
|
+
{"key":"L0153","section":"where it came from","text":"No role yet","placement":"unplaced"},
|
|
2342
|
+
{"key":"L0154","section":"where it came from","text":"Ideator","placement":"unplaced"},
|
|
2343
|
+
{"key":"L0155","section":"where it came from","text":"24 works · all time","placement":"unplaced"},
|
|
2344
|
+
{"key":"L0156","section":"where it came from","text":"Your pace, by week","placement":"unplaced"},
|
|
2345
|
+
{"key":"L0157","section":"where it came from","text":"this week","placement":"unplaced"},
|
|
2346
|
+
{"key":"L0158","section":"where it came from","text":"61 in the last 90 days · only you see this one","placement":"unplaced"},
|
|
2347
|
+
{"key":"L0159","section":"Achievements","text":"Achievements","placement":"unplaced"},
|
|
2348
|
+
{"key":"L0160","section":"Achievements","text":"No achievements earned yet.","placement":"placed","file":"modules/hall-ui/public/profile.js","line":199,"string_id":"c255749ec989","state":"ideator"},
|
|
2349
|
+
{"key":"L0161","section":"Achievements","text":"Won {…}","placement":"placed","file":"modules/hall-ui/public/profile.js","line":206,"string_id":"c5fd76a9e03d","occurrences":2},
|
|
2350
|
+
{"key":"L0162","section":"Cross-project record","text":"Cross-project record","placement":"placed","file":"modules/hall-ui/public/profile.html","line":98,"string_id":"a55e423263f0"},
|
|
2351
|
+
{"key":"L0163","section":"Cross-project record","text":"Works {…}","placement":"unplaced"},
|
|
2352
|
+
{"key":"L0164","section":"Cross-project record","text":"Credits","placement":"unplaced"},
|
|
2353
|
+
{"key":"L0165","section":"Cross-project record","text":"Karma","placement":"unplaced"},
|
|
2354
|
+
{"key":"L0166","section":"Cross-project record","text":"Roles: {…}, UI · Last {…} {…}","placement":"unplaced"},
|
|
2355
|
+
{"key":"L0167","section":"Cross-project record","text":"Project","placement":"unplaced"},
|
|
2356
|
+
{"key":"L0168","section":"Cross-project record","text":"Standing there","placement":"placed","file":"modules/hall-ui/public/profile.js","line":369,"string_id":"ec1c2dae4a90"},
|
|
2357
|
+
{"key":"L0169","section":"Cross-project record","text":"Archon","placement":"unplaced"},
|
|
2358
|
+
{"key":"L0170","section":"Cross-project record","text":"Metic","placement":"unplaced"},
|
|
2359
|
+
{"key":"L0171","section":"Cross-project record","text":"+ {…} more project{…} not publicly listed.","placement":"placed","file":"modules/hall-ui/public/profile.js","line":375,"string_id":"646fab1157fe"},
|
|
2360
|
+
{"key":"L0172","section":"Cross-project record","text":"This is you —","placement":"placed","file":"modules/hall-ui/public/profile.js","line":345,"string_id":"23f531302e9e"},
|
|
2361
|
+
{"key":"L0173","section":"Cross-project record","text":"manage its visibility in Settings → Profile and privacy","placement":"placed","file":"modules/hall-ui/public/profile.js","line":345,"string_id":"bc7923324b96"}
|
|
2347
2362
|
]
|
|
2348
2363
|
},
|
|
2349
2364
|
{
|
|
@@ -3759,7 +3774,7 @@
|
|
|
3759
3774
|
"surface": "landing",
|
|
3760
3775
|
"title": "{{productName}} — projects",
|
|
3761
3776
|
"states_read": ["out-map","in-map","in-map-projects","in-map-profile","in-map-kbd","in-map-search","in-map-mine-on","in-table","in-table-by-builders","in-mine","in-manage","in-manage-address","in-manage-planet","in-manage-card","in-manage-look","in-manage-kind","in-manage-invite","in-manage-invite-handoff","in-manage-app-live","in-manage-app-building","in-manage-app-failed","in-manage-app-retry","in-manage-app-offer","in-manage-app-connect","in-manage-snag","in-manage-gone","in-manage-missing","out-manage","in-new","in-new-terms-refused","in-new-fork","in-new-fork-demo","in-new-demo-ready","in-new-physics","in-new-physics-state","in-new-physics-geo","in-new-physics-ages","in-new-physics-entity","in-new-physics-builders","in-new-physics-rewards","in-new-physics-framing","in-new-physics-summary","in-new-physics-summary-pro","in-new-name-prefilled","in-new-name","out-new","in-new-type","in-new-about","in-new-modules","in-new-modules-search","in-new-home","in-new-address","in-new-app","in-new-app-render","in-new-look","in-new-look-custom","in-new-review","in-new-done","in-new-resume-offer","out-map-projects","card","card-noart","card-self","in-manage-demo","in-mine-empty","card-floor","card-backdrop"],
|
|
3762
|
-
"files_hash": "
|
|
3777
|
+
"files_hash": "df0129f8a210238c",
|
|
3763
3778
|
"reading_hash": "63ad2b703268b065",
|
|
3764
3779
|
"counts": {"lines":1153,"placed":386,"shared":20,"unplaced":747},
|
|
3765
3780
|
"omitted": {"data":125},
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// modules/economy/earnings.js — WHAT EARNED A BUILDER'S CREDITS, DAY BY DAY
|
|
4
|
+
// (task 1004436 / WA6.RP06, goal 1000095, criterion wa6-roles-read-true-in-public).
|
|
5
|
+
//
|
|
6
|
+
// WHY. The profile's earnings chart drew only for someone who had shipped a task
|
|
7
|
+
// (profile-charts.js stopped at "No works shipped yet" before it read a credit),
|
|
8
|
+
// and its line came from the itemised ledger, which another builder may read only
|
|
9
|
+
// with builder.roster.read. So an artist or an ideator with a full credit history
|
|
10
|
+
// got no chart at all, and a signed-in viewer below Metic got an estimate summed
|
|
11
|
+
// from shipped tasks, which for those two crafts is nothing. The owner's decision
|
|
12
|
+
// (a), 2026-09-30, is that earnings are a SIGNED-IN read, for everyone.
|
|
13
|
+
//
|
|
14
|
+
// This is that read: per day, per KIND of credit, summed. No row, no task, no
|
|
15
|
+
// session and no description leaves here, so it carries none of the itemisation
|
|
16
|
+
// the ledger route gates. The total already sits on the public leaderboard; what
|
|
17
|
+
// this adds is when it was earned and by what kind of work.
|
|
18
|
+
//
|
|
19
|
+
// THE KINDS, per credit_log row (first match wins):
|
|
20
|
+
//
|
|
21
|
+
// task.shipped · task.confirmed task Tasks shipped
|
|
22
|
+
// session.token_reward session Session rewards
|
|
23
|
+
// tweak.page_approved:<task> page Pages approved
|
|
24
|
+
// idea.credit.task_ship: (a descent hop) idea-descent Work split from their ideas
|
|
25
|
+
// idea.credit.task_ship: · idea_promotion_ idea-ship Ideas that became shipped work
|
|
26
|
+
// bonus
|
|
27
|
+
// idea.credit.full_pass: idea-full Full Ideas passed
|
|
28
|
+
// idea.credit.governance_pass: idea-board Ratified by the board
|
|
29
|
+
// ship.net_negative net-negative Simplification bonuses
|
|
30
|
+
// anything else other achievements, rank, a grant
|
|
31
|
+
//
|
|
32
|
+
// A descent payment shares stream 1's reason key by design (credits.js: one
|
|
33
|
+
// payment per origin per ship, however the descent is re-walked), so the reason
|
|
34
|
+
// cannot tell it apart. Its description can: the descent writer, and only it,
|
|
35
|
+
// says the task shipped "N hop(s) below idea #". DESCENT_MARK is that phrase, and
|
|
36
|
+
// tests/profile_earnings.mjs holds it against the writer's own text.
|
|
37
|
+
//
|
|
38
|
+
// The words for each kind live with the picture (profile-charts.js); this file
|
|
39
|
+
// owns only which kind a credit is.
|
|
40
|
+
//
|
|
41
|
+
// ONLY READS. Nothing here writes, and nothing is reachable from a ship path.
|
|
42
|
+
|
|
43
|
+
const api = require('../../src/module-api');
|
|
44
|
+
const { pool } = api;
|
|
45
|
+
const { ARTIST_PAGE_REASON_PREFIX, IDEA_STREAM_REASON_PREFIXES } = require('./credits');
|
|
46
|
+
|
|
47
|
+
const KINDS = Object.freeze([
|
|
48
|
+
'task', 'session', 'page', 'idea-ship', 'idea-descent', 'idea-full', 'idea-board', 'net-negative', 'other',
|
|
49
|
+
]);
|
|
50
|
+
|
|
51
|
+
const DESCENT_MARK = ' below idea #';
|
|
52
|
+
|
|
53
|
+
// The prefixes go into the SQL as LIKE patterns, so their underscores (a LIKE
|
|
54
|
+
// wildcard) are escaped. Checked at load: they are economy's own constants,
|
|
55
|
+
// never a request value, and the check keeps it that way.
|
|
56
|
+
function likePrefix(prefix) {
|
|
57
|
+
if (!/^[a-z0-9_.]+:$/i.test(prefix)) {
|
|
58
|
+
throw new Error(`economy/earnings: reason prefix is not a plain literal: ${prefix}`);
|
|
59
|
+
}
|
|
60
|
+
return prefix.replace(/([\\%_])/g, '\\$1') + '%';
|
|
61
|
+
}
|
|
62
|
+
const LIKE = Object.freeze({
|
|
63
|
+
page: likePrefix(ARTIST_PAGE_REASON_PREFIX),
|
|
64
|
+
ship: likePrefix(IDEA_STREAM_REASON_PREFIXES.task_ship),
|
|
65
|
+
full: likePrefix(IDEA_STREAM_REASON_PREFIXES.full_pass),
|
|
66
|
+
board: likePrefix(IDEA_STREAM_REASON_PREFIXES.governance_pass),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// One statement, one builder: idx_credit_log_builder (builder_id, recorded_at)
|
|
70
|
+
// serves the WHERE, so the read is that person's rows, never the whole ledger.
|
|
71
|
+
// The day is the UTC day, the one the page's axis and the ledger fixture use.
|
|
72
|
+
const EARNINGS_SQL = `
|
|
73
|
+
SELECT to_char((recorded_at AT TIME ZONE 'UTC')::date, 'YYYY-MM-DD') AS day,
|
|
74
|
+
CASE
|
|
75
|
+
WHEN reason IN ('task.shipped', 'task.confirmed') THEN 'task'
|
|
76
|
+
WHEN reason = 'session.token_reward' THEN 'session'
|
|
77
|
+
WHEN reason LIKE $2 THEN 'page'
|
|
78
|
+
WHEN reason LIKE $3 AND COALESCE(description, '') LIKE $6 THEN 'idea-descent'
|
|
79
|
+
WHEN reason LIKE $3 OR reason = 'idea_promotion_bonus' THEN 'idea-ship'
|
|
80
|
+
WHEN reason LIKE $4 THEN 'idea-full'
|
|
81
|
+
WHEN reason LIKE $5 THEN 'idea-board'
|
|
82
|
+
WHEN reason = 'ship.net_negative' THEN 'net-negative'
|
|
83
|
+
ELSE 'other'
|
|
84
|
+
END AS kind,
|
|
85
|
+
SUM(delta)::float8 AS credits
|
|
86
|
+
FROM credit_log
|
|
87
|
+
WHERE builder_id = $1
|
|
88
|
+
GROUP BY 1, 2
|
|
89
|
+
ORDER BY 1, 2`;
|
|
90
|
+
|
|
91
|
+
// foldEarnings — the grouped rows into the route's shape. Pure. Days ascending;
|
|
92
|
+
// `totals` names every kind (0 where nothing was earned, so a reader never has
|
|
93
|
+
// to guess whether a missing key means zero); `total` is the sum of the parts,
|
|
94
|
+
// which is the ledger's own sum for this builder.
|
|
95
|
+
function foldEarnings(rows) {
|
|
96
|
+
const totals = {};
|
|
97
|
+
for (const k of KINDS) totals[k] = 0;
|
|
98
|
+
const days = [];
|
|
99
|
+
for (const r of rows || []) {
|
|
100
|
+
const kind = KINDS.includes(r.kind) ? r.kind : 'other';
|
|
101
|
+
const credits = Math.round(Number(r.credits) || 0);
|
|
102
|
+
if (!r.day || credits === 0) continue;
|
|
103
|
+
days.push({ day: String(r.day), kind, credits });
|
|
104
|
+
totals[kind] += credits;
|
|
105
|
+
}
|
|
106
|
+
days.sort((a, b) => (a.day < b.day ? -1 : a.day > b.day ? 1 : KINDS.indexOf(a.kind) - KINDS.indexOf(b.kind)));
|
|
107
|
+
const total = Object.values(totals).reduce((a, b) => a + b, 0);
|
|
108
|
+
return { days, totals, total };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async function readEarnings(builderId, { client = null } = {}) {
|
|
112
|
+
const q = client || pool;
|
|
113
|
+
const { rows } = await q.query(EARNINGS_SQL, [builderId, LIKE.page, LIKE.ship, LIKE.full, LIKE.board, `%${DESCENT_MARK}%`]);
|
|
114
|
+
return foldEarnings(rows);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = { readEarnings, foldEarnings, KINDS, DESCENT_MARK, EARNINGS_SQL };
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
"coreVersion": "^1.7.0",
|
|
7
7
|
"default": true,
|
|
8
8
|
"maintenance": { "status": "core-maintained" },
|
|
9
|
-
"contributes": { "routes": ["cost", "credits", "leaderboard", "reward"] },
|
|
9
|
+
"contributes": { "routes": ["cost", "credits", "earnings", "leaderboard", "reward"] },
|
|
10
10
|
"provides": ["reward"],
|
|
11
11
|
"consumes": []
|
|
12
12
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// modules/economy/routes/earnings.js — GET /builders/:builderId/earnings: what a
|
|
2
|
+
// builder earned, per day and per kind of credit (task 1004436 / WA6.RP06).
|
|
3
|
+
//
|
|
4
|
+
// → { builder_id, days: [{ day, kind, credits }], totals: { <kind>: credits }, total }
|
|
5
|
+
//
|
|
6
|
+
// The profile's earnings chart reads it for EVERY builder, whatever their craft:
|
|
7
|
+
// an ideator or an artist who never shipped a task is paid all the same, and the
|
|
8
|
+
// chart used to draw only for someone who had. `../earnings.js` owns the read and
|
|
9
|
+
// the kinds; this file is only the doorway, and must never grow SQL.
|
|
10
|
+
//
|
|
11
|
+
// SIGNED IN. The owner's decision (a), 2026-09-30: "no one should be able to open
|
|
12
|
+
// up the hood of data without being signed in." Sums only — the itemised ledger
|
|
13
|
+
// (/builders/:builderId/credits) stays behind builder.roster.read.
|
|
14
|
+
//
|
|
15
|
+
// MODULE BOUNDARY: reaches core only through the published doorway
|
|
16
|
+
// `../../../src/module-api`; `../earnings` is an intra-module sibling.
|
|
17
|
+
|
|
18
|
+
'use strict';
|
|
19
|
+
|
|
20
|
+
const express = require('express');
|
|
21
|
+
const api = require('../../../src/module-api');
|
|
22
|
+
const { readEarnings } = require('../earnings');
|
|
23
|
+
|
|
24
|
+
module.exports = function buildEarningsRouter() {
|
|
25
|
+
const router = express.Router();
|
|
26
|
+
|
|
27
|
+
// rank: builder — any signed-in builder may read any builder's earnings by
|
|
28
|
+
// kind, as the profile's other signed-in sections are; a stranger gets the
|
|
29
|
+
// sign-in line. The param names the builder (`:builderId`), the credits
|
|
30
|
+
// route's convention, because this route deliberately reads someone else.
|
|
31
|
+
router.get('/builders/:builderId/earnings', api.requireBuilder, async (req, res) => {
|
|
32
|
+
const targetId = api.parseId(req, res, { param: 'builderId', code: 'bad_builder_id', positiveInt: true });
|
|
33
|
+
if (targetId === null) return;
|
|
34
|
+
try {
|
|
35
|
+
const b = await api.getBuilderById(targetId);
|
|
36
|
+
if (!b) return res.fail('builder_not_found', 404);
|
|
37
|
+
const out = await readEarnings(targetId);
|
|
38
|
+
res.set('Cache-Control', 'no-store');
|
|
39
|
+
res.json({ builder_id: targetId, ...out });
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.error('[gds] GET /builders/:builderId/earnings', err);
|
|
42
|
+
if (!res.headersSent) res.fail('earnings_read_failed', { status: 500, message: 'internal error' });
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return router;
|
|
47
|
+
};
|
|
@@ -23,3 +23,52 @@
|
|
|
23
23
|
.gate-reason > span:nth-of-type(2) { flex: 1 1 100%; order: 3; }
|
|
24
24
|
.gate-reason__floor { margin-left: auto; }
|
|
25
25
|
}
|
|
26
|
+
|
|
27
|
+
/* The Autobongos runner panel (task 1004509): a status line with the switch,
|
|
28
|
+
a checklist only while something is missing, goals as chips, and the
|
|
29
|
+
machine facts folded under Details. */
|
|
30
|
+
.rn-head { display: flex; align-items: center; gap: 12px; }
|
|
31
|
+
.rn-head__text { flex: 1; min-width: 0; }
|
|
32
|
+
.rn-title { margin: 0; font-size: 17px; color: var(--ink); }
|
|
33
|
+
.rn-meta { margin: 2px 0 0; font-size: 12.5px; color: var(--ink-faint); }
|
|
34
|
+
.rn-dot { width: 12px; height: 12px; border-radius: 50%; flex: none; background: var(--info); }
|
|
35
|
+
.rn-dot--ok { background: var(--ok); }
|
|
36
|
+
.rn-dot--warn { background: var(--warn); }
|
|
37
|
+
.rn-dot--danger { background: var(--danger); }
|
|
38
|
+
|
|
39
|
+
.rn-switch { display: inline-flex; align-items: center; gap: 8px; background: none; border: 0; padding: 4px; cursor: pointer; color: var(--ink-soft); font: inherit; font-size: 13px; }
|
|
40
|
+
.rn-switch__track { position: relative; width: 40px; height: 22px; border-radius: var(--radius-pill); background: var(--rule); transition: background .15s; }
|
|
41
|
+
.rn-switch__track::after { content: ""; position: absolute; top: 3px; left: 3px; width: 16px; height: 16px; border-radius: 50%; background: var(--bg-card); transition: transform .15s; }
|
|
42
|
+
.rn-switch[aria-checked="true"] .rn-switch__track { background: var(--ok); }
|
|
43
|
+
.rn-switch[aria-checked="true"] .rn-switch__track::after { transform: translateX(18px); }
|
|
44
|
+
.rn-switch:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; border-radius: var(--radius-sm); }
|
|
45
|
+
|
|
46
|
+
.rn-check { list-style: none; margin: 16px 0 0; padding: 0; display: grid; gap: 10px; }
|
|
47
|
+
.rn-check__item { display: flex; align-items: flex-start; gap: 10px; font-size: 14px; color: var(--ink); }
|
|
48
|
+
.rn-check__icon { width: 20px; height: 20px; border-radius: 50%; flex: none; display: inline-flex; align-items: center; justify-content: center; font-size: 12px; }
|
|
49
|
+
.rn-check__item--ok .rn-check__icon { background: var(--ok-weak); color: var(--ok); }
|
|
50
|
+
.rn-check__item--todo .rn-check__icon { background: var(--warn-weak); color: var(--warn); }
|
|
51
|
+
.rn-check__item--wait { color: var(--ink-faint); }
|
|
52
|
+
.rn-check__item--wait .rn-check__icon { border: var(--line) solid var(--rule); color: var(--ink-faint); }
|
|
53
|
+
.rn-check__text { flex: 1; min-width: 0; }
|
|
54
|
+
.rn-check__hint { display: block; font-size: 12.5px; color: var(--ink-faint); }
|
|
55
|
+
.rn-check .rn-meta { display: inline; }
|
|
56
|
+
|
|
57
|
+
.rn-sub { margin: 20px 0 8px; font-size: 13px; font-weight: 500; color: var(--ink-soft); }
|
|
58
|
+
.rn-chips { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
|
|
59
|
+
.rn-chip { display: inline-flex; align-items: center; gap: 4px; padding: 3px 6px; border: var(--line) solid var(--rule); border-radius: var(--radius-pill); font-size: 13px; background: none; color: var(--ink); }
|
|
60
|
+
.rn-chip a { color: inherit; text-decoration: none; padding: 0 2px; }
|
|
61
|
+
.rn-chip a:hover { text-decoration: underline; }
|
|
62
|
+
.rn-chip--pri { border-color: var(--accent); }
|
|
63
|
+
.rn-chip__star, .rn-chip__x { background: none; border: 0; padding: 0 4px; cursor: pointer; color: var(--ink-faint); font: inherit; font-size: 14px; line-height: 1; }
|
|
64
|
+
.rn-chip--pri .rn-chip__star { color: var(--accent); }
|
|
65
|
+
.rn-chip__star:hover, .rn-chip__x:hover { color: var(--ink); }
|
|
66
|
+
.rn-chip--add { border-style: dashed; color: var(--ink-soft); padding: 3px 12px; cursor: pointer; font: inherit; font-size: 13px; }
|
|
67
|
+
.rn-add { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
|
|
68
|
+
.rn-add input[type="number"] { width: 140px; }
|
|
69
|
+
|
|
70
|
+
.rn-details { margin: 18px 0 0; font-size: 12.5px; color: var(--ink-faint); }
|
|
71
|
+
.rn-details summary { cursor: pointer; width: fit-content; }
|
|
72
|
+
.rn-details p, .rn-details ul { margin: 8px 0 0; max-width: var(--measure); }
|
|
73
|
+
.rn-details ul { padding-left: 18px; display: grid; gap: 4px; }
|
|
74
|
+
.rn-dot--sm { display: inline-block; width: 8px; height: 8px; vertical-align: 1px; }
|
|
@@ -51,12 +51,10 @@
|
|
|
51
51
|
behind it are system:true/archon server-side regardless. -->
|
|
52
52
|
<section class="scroll" id="fence-panel" aria-labelledby="fence-h" hidden>
|
|
53
53
|
<h2 class="scroll__h" id="fence-h">Autobongos runner</h2>
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
<div id="fence-
|
|
58
|
-
<div id="fence-state"><span class="kit-skel"></span></div>
|
|
59
|
-
<div id="fence-goals"></div>
|
|
54
|
+
<!-- One panel (task 1004509): a checklist while anything the runner
|
|
55
|
+
needs is missing, one status line with the switch once it is all
|
|
56
|
+
ticked. Drawn from /autonomy/runners + /autonomy/fence together. -->
|
|
57
|
+
<div id="fence-body"><span class="kit-skel"></span></div>
|
|
60
58
|
<!-- Every builder's runner (task 1004501): what each one picked to
|
|
61
59
|
work, whether you paused it, and whether it is checking in. -->
|
|
62
60
|
<div id="fence-builders"></div>
|
|
@@ -84,6 +82,6 @@
|
|
|
84
82
|
<script src="/builders/palette.js" defer></script>
|
|
85
83
|
<script src="/builders/dom-utils.js?v=2026-06-16-gate"></script>
|
|
86
84
|
<script src="/builders/hall-kit.js"></script>
|
|
87
|
-
<script src="/builders/gate.js?v=2026-10-01-
|
|
85
|
+
<script src="/builders/gate.js?v=2026-10-01-runner-checklist"></script>
|
|
88
86
|
</body>
|
|
89
87
|
</html>
|