@bongos/core 1.23.11 → 1.23.12

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.
@@ -13,7 +13,7 @@
13
13
  "pages": 47,
14
14
  "surfaces": 3,
15
15
  "with_states": 40,
16
- "states": 338
16
+ "states": 342
17
17
  },
18
18
  "surfaces": [
19
19
  {
@@ -1452,6 +1452,8 @@
1452
1452
  "states_files": [
1453
1453
  "modules/public-landing/public/community.states.json",
1454
1454
  "modules/public-landing/public/community-empty.states.json",
1455
+ "modules/public-landing/public/community-owner.states.json",
1456
+ "modules/public-landing/public/community-owner-two.states.json",
1455
1457
  "modules/public-landing/public/community-recruiter.states.json"
1456
1458
  ],
1457
1459
  "states": [
@@ -1468,6 +1470,10 @@
1468
1470
  "in-recruiters",
1469
1471
  "in",
1470
1472
  "out-empty",
1473
+ "in-owner",
1474
+ "in-owner-find",
1475
+ "in-owner-two",
1476
+ "in-owner-two-picked",
1471
1477
  "in-recruiter"
1472
1478
  ]
1473
1479
  },
@@ -5,7 +5,7 @@
5
5
  "adr": "0341 D3",
6
6
  "reread": "node scripts/bongos/page-reader.js --stale",
7
7
  "how": ["Generated by node scripts/bongos/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":47,"lines":5220,"placed":1582,"shared":419,"unplaced":3219,"omitted_data":3532},
8
+ "counts": {"pages":47,"lines":5226,"placed":1582,"shared":419,"unplaced":3225,"omitted_data":3534},
9
9
  "pages": [
10
10
  {
11
11
  "id": "builders:atlas",
@@ -4490,79 +4490,85 @@
4490
4490
  "id": "landing:community",
4491
4491
  "surface": "landing",
4492
4492
  "title": "{{productName}} — Community",
4493
- "states_read": ["out","out-30d","out-artists","out-community-band","in-community-band","in-projects-band","out-projects-band","out-find","out-find-search","out-recruiters","in-recruiters","in","out-empty","in-recruiter"],
4494
- "files_hash": "349fcec5a572f87f",
4495
- "reading_hash": "45843b726fadc23f",
4496
- "counts": {"lines":66,"placed":27,"shared":12,"unplaced":27},
4497
- "omitted": {"data":28},
4493
+ "states_read": ["out","out-30d","out-artists","out-community-band","in-community-band","in-projects-band","out-projects-band","out-find","out-find-search","out-recruiters","in-recruiters","in","out-empty","in-owner","in-owner-find","in-owner-two","in-owner-two-picked","in-recruiter"],
4494
+ "files_hash": "8bd332ba8f0f0243",
4495
+ "reading_hash": "6b81da8cd8fdb090",
4496
+ "counts": {"lines":72,"placed":27,"shared":12,"unplaced":33},
4497
+ "omitted": {"data":30},
4498
4498
  "notes": [],
4499
4499
  "lines": [
4500
4500
  {"key":"L0001","section":"top bar","text":"Community","placement":"shared"},
4501
4501
  {"key":"L0002","section":"top bar","text":"Projects","placement":"shared"},
4502
4502
  {"key":"L0003","section":"top bar","text":"Review","placement":"shared","state":"in-community-band"},
4503
4503
  {"key":"L0004","section":"page top","text":"Sky","placement":"unplaced","state":"in-projects-band"},
4504
- {"key":"L0005","section":"page top","text":"the sky of every project","placement":"placed","file":"modules/public-landing/public/community.html","line":129,"string_id":"8fa12f730019","state":"in-projects-band"},
4504
+ {"key":"L0005","section":"page top","text":"the sky of every project","placement":"placed","file":"modules/public-landing/public/community.html","line":144,"string_id":"8fa12f730019","state":"in-projects-band"},
4505
4505
  {"key":"L0006","section":"page top","text":"Table","placement":"unplaced","state":"in-projects-band"},
4506
- {"key":"L0007","section":"page top","text":"the same projects as rows","placement":"placed","file":"modules/public-landing/public/community.html","line":130,"string_id":"21453b3ba5c3","state":"in-projects-band"},
4507
- {"key":"L0008","section":"page top","text":"Your projects","placement":"placed","file":"modules/public-landing/public/community.html","line":131,"string_id":"4e25e6872321","state":"in-projects-band"},
4508
- {"key":"L0009","section":"page top","text":"the ones you own","placement":"placed","file":"modules/public-landing/public/community.html","line":131,"string_id":"a0fa148c60a6","state":"in-projects-band"},
4509
- {"key":"L0010","section":"page top","text":"New project","placement":"placed","file":"modules/public-landing/public/community.html","line":132,"string_id":"0a81a82d10c4","state":"in-projects-band"},
4510
- {"key":"L0011","section":"page top","text":"five questions and a home","placement":"placed","file":"modules/public-landing/public/community.html","line":132,"string_id":"94a3462373d2","state":"in-projects-band"},
4511
- {"key":"L0012","section":"top bar","text":"Sign in","placement":"shared","file":"modules/public-landing/public/community.html","line":112,"string_id":"ad80586f802a"},
4512
- {"key":"L0013","section":"Open to recruiters","text":"Open to recruiters","placement":"placed","file":"modules/public-landing/public/community.html","line":121,"string_id":"1e116d8fe95a","state":"out-recruiters"},
4513
- {"key":"L0014","section":"Open to recruiters","text":"Builders who keep their numbers hidden but are open to hearing from projects. Only project owners see who they are, and only by name.","placement":"placed","file":"modules/public-landing/public/community.html","line":217,"string_id":"932bb6ad495b","state":"out-recruiters"},
4506
+ {"key":"L0007","section":"page top","text":"the same projects as rows","placement":"placed","file":"modules/public-landing/public/community.html","line":145,"string_id":"21453b3ba5c3","state":"in-projects-band"},
4507
+ {"key":"L0008","section":"page top","text":"Your projects","placement":"placed","file":"modules/public-landing/public/community.html","line":146,"string_id":"4e25e6872321","state":"in-projects-band"},
4508
+ {"key":"L0009","section":"page top","text":"the ones you own","placement":"placed","file":"modules/public-landing/public/community.html","line":146,"string_id":"a0fa148c60a6","state":"in-projects-band"},
4509
+ {"key":"L0010","section":"page top","text":"New project","placement":"placed","file":"modules/public-landing/public/community.html","line":147,"string_id":"0a81a82d10c4","state":"in-projects-band"},
4510
+ {"key":"L0011","section":"page top","text":"five questions and a home","placement":"placed","file":"modules/public-landing/public/community.html","line":147,"string_id":"94a3462373d2","state":"in-projects-band"},
4511
+ {"key":"L0012","section":"top bar","text":"Sign in","placement":"shared","file":"modules/public-landing/public/community.html","line":127,"string_id":"ad80586f802a"},
4512
+ {"key":"L0013","section":"Open to recruiters","text":"Open to recruiters","placement":"placed","file":"modules/public-landing/public/community.html","line":136,"string_id":"1e116d8fe95a","state":"out-recruiters"},
4513
+ {"key":"L0014","section":"Open to recruiters","text":"Builders who keep their numbers hidden but are open to hearing from projects. Only project owners see who they are, and only by name.","placement":"placed","file":"modules/public-landing/public/community.html","line":234,"string_id":"932bb6ad495b","state":"out-recruiters"},
4514
4514
  {"key":"L0015","section":"Open to recruiters","text":"GitHub: {…}","placement":"unplaced","state":"in-recruiter","occurrences":3},
4515
- {"key":"L0016","section":"Open to recruiters","text":"Only project owners can see this list.","placement":"placed","file":"modules/public-landing/public/community.html","line":226,"string_id":"6bcd385b13d5","state":"in-recruiters"},
4516
- {"key":"L0017","section":"Open to recruiters","text":"Sign in to see whether this list is open to you.","placement":"placed","file":"modules/public-landing/public/community.html","line":220,"string_id":"f59de6034af4","state":"out-recruiters"},
4517
- {"key":"L0018","section":"Open to recruiters","text":"Sign in with GitHub","placement":"placed","file":"modules/public-landing/public/community.html","line":222,"string_id":"df38941b25a6","state":"out-recruiters"},
4518
- {"key":"L0019","section":"footer","text":"A non-profit, capture-resistant commons.","placement":"shared","file":"modules/public-landing/public/community.html","line":234,"string_id":"afe83bbbd0d4","state":"out-recruiters","occurrences":3},
4519
- {"key":"L0020","section":"footer","text":"Built by agents, on Cloud Bongos.","placement":"shared","file":"modules/public-landing/public/community.html","line":234,"string_id":"bd315dd71ea7","state":"out-recruiters","occurrences":3},
4520
- {"key":"L0021","section":"footer","text":"Open source","placement":"shared","file":"modules/public-landing/public/community.html","line":234,"string_id":"d6314ddcc1d0","state":"out-recruiters","occurrences":3},
4515
+ {"key":"L0016","section":"Open to recruiters","text":"Only project owners can see this list.","placement":"placed","file":"modules/public-landing/public/community.html","line":243,"string_id":"6bcd385b13d5","state":"in-recruiters"},
4516
+ {"key":"L0017","section":"Open to recruiters","text":"Sign in to see whether this list is open to you.","placement":"placed","file":"modules/public-landing/public/community.html","line":237,"string_id":"f59de6034af4","state":"out-recruiters"},
4517
+ {"key":"L0018","section":"Open to recruiters","text":"Sign in with GitHub","placement":"placed","file":"modules/public-landing/public/community.html","line":239,"string_id":"df38941b25a6","state":"out-recruiters"},
4518
+ {"key":"L0019","section":"footer","text":"A non-profit, capture-resistant commons.","placement":"shared","file":"modules/public-landing/public/community.html","line":251,"string_id":"afe83bbbd0d4","state":"out-recruiters","occurrences":3},
4519
+ {"key":"L0020","section":"footer","text":"Built by agents, on Cloud Bongos.","placement":"shared","file":"modules/public-landing/public/community.html","line":251,"string_id":"bd315dd71ea7","state":"out-recruiters","occurrences":3},
4520
+ {"key":"L0021","section":"footer","text":"Open source","placement":"shared","file":"modules/public-landing/public/community.html","line":251,"string_id":"d6314ddcc1d0","state":"out-recruiters","occurrences":3},
4521
4521
  {"key":"L0022","section":"footer","text":"AGPL-3.0","placement":"shared","state":"out-recruiters","occurrences":3},
4522
4522
  {"key":"L0023","section":"navigation","text":"Privacy","placement":"shared","state":"out-recruiters","occurrences":3},
4523
4523
  {"key":"L0024","section":"navigation","text":"Terms","placement":"shared","state":"out-recruiters","occurrences":3},
4524
4524
  {"key":"L0025","section":"navigation","text":"Contact","placement":"shared","state":"out-recruiters","occurrences":3},
4525
- {"key":"L0026","section":"navigation","text":"Builders' hall","placement":"shared","file":"modules/public-landing/public/community.html","line":234,"string_id":"90a128c97eae","state":"out-recruiters","occurrences":3},
4526
- {"key":"L0027","section":"Find a builder","text":"Find a builder","placement":"placed","file":"modules/public-landing/public/community.html","line":120,"string_id":"f1e93ba98ceb","state":"out-find"},
4527
- {"key":"L0028","section":"Find a builder","text":"Their name or handle","placement":"placed","file":"modules/public-landing/public/community.html","line":192,"string_id":"541d5a7612a9","attr":"placeholder","state":"out-find"},
4528
- {"key":"L0029","section":"Find a builder","text":"Any role","placement":"placed","file":"modules/public-landing/public/community.html","line":196,"string_id":"648862c67a23","state":"out-find"},
4525
+ {"key":"L0026","section":"navigation","text":"Builders' hall","placement":"shared","file":"modules/public-landing/public/community.html","line":251,"string_id":"90a128c97eae","state":"out-recruiters","occurrences":3},
4526
+ {"key":"L0027","section":"Find a builder","text":"Find a builder","placement":"placed","file":"modules/public-landing/public/community.html","line":135,"string_id":"f1e93ba98ceb","state":"out-find"},
4527
+ {"key":"L0028","section":"Find a builder","text":"Their name or handle","placement":"placed","file":"modules/public-landing/public/community.html","line":208,"string_id":"541d5a7612a9","attr":"placeholder","state":"out-find"},
4528
+ {"key":"L0029","section":"Find a builder","text":"Any role","placement":"placed","file":"modules/public-landing/public/community.html","line":212,"string_id":"648862c67a23","state":"out-find"},
4529
4529
  {"key":"L0030","section":"Find a builder","text":"Engineers","placement":"unplaced","state":"out-find"},
4530
4530
  {"key":"L0031","section":"Find a builder","text":"Artists","placement":"unplaced","state":"out-find"},
4531
4531
  {"key":"L0032","section":"Find a builder","text":"Ideators","placement":"unplaced","state":"out-find"},
4532
4532
  {"key":"L0033","section":"Find a builder","text":"Designers","placement":"unplaced","state":"out-find"},
4533
4533
  {"key":"L0034","section":"Find a builder","text":"Search","placement":"unplaced","state":"out-find"},
4534
- {"key":"L0035","section":"Find a builder","text":"2 builders found.","placement":"unplaced","state":"out-find-search"},
4535
- {"key":"L0036","section":"Find a builder","text":"{…} · designer","placement":"unplaced","state":"out-find-search"},
4536
- {"key":"L0037","section":"page top","text":"Leaderboard","placement":"unplaced","state":"out-community-band"},
4537
- {"key":"L0038","section":"page top","text":"who earned the most, across every project","placement":"placed","file":"modules/public-landing/public/community.html","line":119,"string_id":"776e80cf232d","state":"out-community-band"},
4538
- {"key":"L0039","section":"page top","text":"Find a builder","placement":"placed","file":"modules/public-landing/public/community.html","line":120,"string_id":"f1e93ba98ceb","state":"out-community-band"},
4539
- {"key":"L0040","section":"page top","text":"anyone building here, by name or role","placement":"placed","file":"modules/public-landing/public/community.html","line":120,"string_id":"a4e84a1501c5","state":"out-community-band"},
4540
- {"key":"L0041","section":"page top","text":"Open to recruiters","placement":"placed","file":"modules/public-landing/public/community.html","line":121,"string_id":"1e116d8fe95a","state":"out-community-band"},
4541
- {"key":"L0042","section":"page top","text":"who is open to recruiting, for project owners","placement":"placed","file":"modules/public-landing/public/community.html","line":121,"string_id":"15ac45627fab","state":"out-community-band"},
4542
- {"key":"L0043","section":"page top","text":"Builders you may know","placement":"placed","file":"modules/public-landing/public/community.html","line":122,"string_id":"724de83c77c6","state":"in-community-band"},
4543
- {"key":"L0044","section":"page top","text":"connections or projects in common","placement":"placed","file":"modules/public-landing/public/community.html","line":122,"string_id":"171cee573b19","state":"in-community-band"},
4544
- {"key":"L0045","section":"Leaderboard","text":"Leaderboard","placement":"unplaced"},
4545
- {"key":"L0046","section":"Leaderboard","text":"Everyone","placement":"unplaced"},
4546
- {"key":"L0047","section":"Leaderboard","text":"Engineers","placement":"unplaced"},
4547
- {"key":"L0048","section":"Leaderboard","text":"Artists","placement":"unplaced"},
4548
- {"key":"L0049","section":"Leaderboard","text":"Ideators","placement":"unplaced"},
4549
- {"key":"L0050","section":"Leaderboard","text":"Designers","placement":"unplaced"},
4550
- {"key":"L0051","section":"Leaderboard","text":"Last 7 days","placement":"placed","file":"modules/public-landing/public/community.html","line":171,"string_id":"fc3582ac098d"},
4551
- {"key":"L0052","section":"Leaderboard","text":"Last 30 days","placement":"placed","file":"modules/public-landing/public/community.html","line":172,"string_id":"52f6b5974994"},
4552
- {"key":"L0053","section":"Leaderboard","text":"Past year","placement":"placed","file":"modules/public-landing/public/community.html","line":173,"string_id":"42a9479b375c"},
4553
- {"key":"L0054","section":"Leaderboard","text":"All time","placement":"placed","file":"modules/public-landing/public/community.html","line":174,"string_id":"23c761dcff62"},
4554
- {"key":"L0055","section":"Leaderboard","text":"Credits earned in the last 30 days, across every project.","placement":"unplaced","state":"out-30d"},
4555
- {"key":"L0056","section":"Leaderboard","text":"{{Currency}} earned across every project, since each builder started.","placement":"placed","file":"modules/public-landing/public/community.html","line":178,"string_id":"f81129627f7b"},
4556
- {"key":"L0057","section":"Leaderboard","text":"No builders on the leaderboard yet.","placement":"unplaced","state":"out-empty"},
4557
- {"key":"L0058","section":"Leaderboard","text":"212 works shipped · 64 karma","placement":"unplaced"},
4558
- {"key":"L0059","section":"Leaderboard","text":"131 works shipped · 40 karma","placement":"unplaced"},
4559
- {"key":"L0060","section":"Leaderboard","text":"19 works shipped · 9 karma","placement":"unplaced","state":"out-30d"},
4560
- {"key":"L0061","section":"Leaderboard","text":"11 works shipped · 4 karma","placement":"unplaced","state":"out-30d"},
4561
- {"key":"L0062","section":"Leaderboard","text":"77 works shipped · 52 karma","placement":"unplaced"},
4562
- {"key":"L0063","section":"Leaderboard","text":"31 works shipped · 18 karma","placement":"unplaced"},
4563
- {"key":"L0064","section":"Leaderboard","text":"12 works shipped · 7 karma","placement":"unplaced"},
4564
- {"key":"L0065","section":"Leaderboard","text":"1 work shipped · 0 karma","placement":"unplaced"},
4565
- {"key":"L0066","section":"Leaderboard","text":"A project reports a builder’s numbers when they sign in or ship there, so a window counts work when it was reported, not when it was done.","placement":"placed","file":"modules/public-landing/public/community.html","line":183,"string_id":"8d79db29b7d6"}
4534
+ {"key":"L0035","section":"Find a builder","text":"Recruit opens {…}’s hall with their name filled in, and you send the invite there.","placement":"unplaced","state":"in-owner-find"},
4535
+ {"key":"L0036","section":"Find a builder","text":"2 builders found.","placement":"unplaced","state":"out-find-search"},
4536
+ {"key":"L0037","section":"Find a builder","text":"{…} · designer","placement":"unplaced","state":"out-find-search"},
4537
+ {"key":"L0038","section":"Find a builder","text":"Recruit","placement":"unplaced","state":"in-owner-find","occurrences":2},
4538
+ {"key":"L0039","section":"page top","text":"Leaderboard","placement":"unplaced","state":"out-community-band"},
4539
+ {"key":"L0040","section":"page top","text":"who earned the most, across every project","placement":"placed","file":"modules/public-landing/public/community.html","line":134,"string_id":"776e80cf232d","state":"out-community-band"},
4540
+ {"key":"L0041","section":"page top","text":"Find a builder","placement":"placed","file":"modules/public-landing/public/community.html","line":135,"string_id":"f1e93ba98ceb","state":"out-community-band"},
4541
+ {"key":"L0042","section":"page top","text":"anyone building here, by name or role","placement":"placed","file":"modules/public-landing/public/community.html","line":135,"string_id":"a4e84a1501c5","state":"out-community-band"},
4542
+ {"key":"L0043","section":"page top","text":"Open to recruiters","placement":"placed","file":"modules/public-landing/public/community.html","line":136,"string_id":"1e116d8fe95a","state":"out-community-band"},
4543
+ {"key":"L0044","section":"page top","text":"who is open to recruiting, for project owners","placement":"placed","file":"modules/public-landing/public/community.html","line":136,"string_id":"15ac45627fab","state":"out-community-band"},
4544
+ {"key":"L0045","section":"page top","text":"Builders you may know","placement":"placed","file":"modules/public-landing/public/community.html","line":137,"string_id":"724de83c77c6","state":"in-community-band"},
4545
+ {"key":"L0046","section":"page top","text":"connections or projects in common","placement":"placed","file":"modules/public-landing/public/community.html","line":137,"string_id":"171cee573b19","state":"in-community-band"},
4546
+ {"key":"L0047","section":"Leaderboard","text":"Leaderboard","placement":"unplaced"},
4547
+ {"key":"L0048","section":"Leaderboard","text":"Everyone","placement":"unplaced"},
4548
+ {"key":"L0049","section":"Leaderboard","text":"Engineers","placement":"unplaced"},
4549
+ {"key":"L0050","section":"Leaderboard","text":"Artists","placement":"unplaced"},
4550
+ {"key":"L0051","section":"Leaderboard","text":"Ideators","placement":"unplaced"},
4551
+ {"key":"L0052","section":"Leaderboard","text":"Designers","placement":"unplaced"},
4552
+ {"key":"L0053","section":"Leaderboard","text":"Last 7 days","placement":"placed","file":"modules/public-landing/public/community.html","line":186,"string_id":"fc3582ac098d"},
4553
+ {"key":"L0054","section":"Leaderboard","text":"Last 30 days","placement":"placed","file":"modules/public-landing/public/community.html","line":187,"string_id":"52f6b5974994"},
4554
+ {"key":"L0055","section":"Leaderboard","text":"Past year","placement":"placed","file":"modules/public-landing/public/community.html","line":188,"string_id":"42a9479b375c"},
4555
+ {"key":"L0056","section":"Leaderboard","text":"All time","placement":"placed","file":"modules/public-landing/public/community.html","line":189,"string_id":"23c761dcff62"},
4556
+ {"key":"L0057","section":"Leaderboard","text":"Credits earned in the last 30 days, across every project.","placement":"unplaced","state":"out-30d"},
4557
+ {"key":"L0058","section":"Leaderboard","text":"{{Currency}} earned across every project, since each builder started.","placement":"placed","file":"modules/public-landing/public/community.html","line":193,"string_id":"f81129627f7b"},
4558
+ {"key":"L0059","section":"Leaderboard","text":"Recruit to","placement":"unplaced","state":"in-owner-two"},
4559
+ {"key":"L0060","section":"Leaderboard","text":"Recruit opens its hall with their name filled in, and you send the invite there.","placement":"unplaced","state":"in-owner-two"},
4560
+ {"key":"L0061","section":"Leaderboard","text":"Recruit opens {…}’s hall with their name filled in, and you send the invite there.","placement":"unplaced","state":"in-owner"},
4561
+ {"key":"L0062","section":"Leaderboard","text":"No builders on the leaderboard yet.","placement":"unplaced","state":"out-empty"},
4562
+ {"key":"L0063","section":"Leaderboard","text":"212 works shipped · 64 karma","placement":"unplaced"},
4563
+ {"key":"L0064","section":"Leaderboard","text":"Recruit","placement":"unplaced","state":"in-owner","occurrences":6},
4564
+ {"key":"L0065","section":"Leaderboard","text":"131 works shipped · 40 karma","placement":"unplaced"},
4565
+ {"key":"L0066","section":"Leaderboard","text":"19 works shipped · 9 karma","placement":"unplaced","state":"out-30d"},
4566
+ {"key":"L0067","section":"Leaderboard","text":"11 works shipped · 4 karma","placement":"unplaced","state":"out-30d"},
4567
+ {"key":"L0068","section":"Leaderboard","text":"77 works shipped · 52 karma","placement":"unplaced"},
4568
+ {"key":"L0069","section":"Leaderboard","text":"31 works shipped · 18 karma","placement":"unplaced"},
4569
+ {"key":"L0070","section":"Leaderboard","text":"12 works shipped · 7 karma","placement":"unplaced"},
4570
+ {"key":"L0071","section":"Leaderboard","text":"1 work shipped · 0 karma","placement":"unplaced"},
4571
+ {"key":"L0072","section":"Leaderboard","text":"A project reports a builder’s numbers when they sign in or ship there, so a window counts work when it was reported, not when it was done.","placement":"placed","file":"modules/public-landing/public/community.html","line":199,"string_id":"8d79db29b7d6"}
4566
4572
  ]
4567
4573
  },
4568
4574
  {
@@ -0,0 +1,16 @@
1
+ {
2
+ "_": "The community leaderboard for a builder who owns two projects (task 1002292): GET /my-projects answers Mercury and Tidepool (fixtures/my-projects-owner-two.json), so a labelled \"Recruit to\" choice sits above the rows, and picking Tidepool re-points every row's Recruit link at Tidepool's hall invite box.",
3
+ "page": "/community",
4
+ "surface": "public-landing",
5
+ "modes": ["dark"],
6
+ "modeQuery": false,
7
+ "stub": { "fixtures": { "my-projects": "my-projects-owner-two" } },
8
+ "ignoreRequests": ["/api/(gds|bongos)/me$", "/me/guild-", "/scouting$"],
9
+ "ignoreConsole": ["/api/(gds|bongos)/me", "/scouting", "\\b40[13]\\b", "Unauthorized", "Forbidden"],
10
+ "ignoreControls": [".vh"],
11
+ "states": {
12
+ "in-owner-two": { "auth": true, "actions": [["wait", 600]], "expect": { "visible": ["#boardFor", "#boardForPick"], "value": [["#boardForPick", "0"]], "count": [["#boardForPick option", 2], ["#boardList a.commRecruit", 6]], "href": [["#boardList .commRow:first-child .commRecruit", "https://mercury.example.test/builders/watch?invite=priyanair"]] } },
13
+ "in-owner-two-picked": { "auth": true, "actions": [["wait", 600], ["select", "#boardForPick", "1"], ["wait", 200]], "expect": { "value": [["#boardForPick", "1"]], "href": [["#boardList .commRow:first-child .commRecruit", "https://tidepool.example.test/builders/watch?invite=priyanair"]], "attr": [["#boardList .commRow:first-child .commRecruit", "aria-label", "Recruit Priya Nair to Tidepool (opens in a new tab)"]] } }
14
+ },
15
+ "reducedMotion": { "auth": true, "actions": [["wait", 600]] }
16
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "_": "The community pages for a project owner (task 1002292; spec D6, owner 2026-10-27): GET /my-projects answers one owned project (fixtures/my-projects-owner.json), so every leaderboard and search row carries a Recruit link to Mercury's hall invite box with the GitHub login filled in, and a line above the rows says what it opens. The rows are the ones everyone sees.",
3
+ "page": "/community",
4
+ "surface": "public-landing",
5
+ "modes": ["dark"],
6
+ "modeQuery": false,
7
+ "stub": { "fixtures": { "my-projects": "my-projects-owner" } },
8
+ "ignoreRequests": ["/api/(gds|bongos)/me$", "/me/guild-", "/scouting$"],
9
+ "ignoreConsole": ["/api/(gds|bongos)/me", "/scouting", "\\b40[13]\\b", "Unauthorized", "Forbidden"],
10
+ "ignoreControls": [".vh"],
11
+ "states": {
12
+ "in-owner": { "auth": true, "actions": [["wait", 600]], "expect": { "visible": ["#board", "#boardFor", "#who"], "hidden": ["#find", "#recruit", "#boardFailed"], "count": [["#boardList .commRow", 6], ["#boardList a.commRecruit", 6]], "text": [["#boardFor", "Recruit opens Mercury’s hall with their name filled in, and you send the invite there."], ["#boardList .commRow:first-child .commRecruit", "Recruit"]], "href": [["#boardList .commRow:first-child .commRecruit", "https://mercury.example.test/builders/watch?invite=priyanair"]] } },
13
+ "in-owner-find": { "auth": true, "url": "/community/find", "actions": [["wait", 600], ["fill", "#findQ", "pri"], ["click", "#findBtn"], ["wait", 300]], "expect": { "visible": ["#find", "#findFor"], "hidden": ["#board", "#recruit"], "count": [["#findList .mineRow", 2], ["#findList .mineRow__actions a.commRecruit", 2]], "href": [["#findList .mineRow:first-child .commRecruit", "https://mercury.example.test/builders/watch?invite=priyanair"]] } }
14
+ },
15
+ "reducedMotion": { "auth": true, "actions": [["wait", 600]] }
16
+ }
@@ -16,6 +16,11 @@
16
16
  their own. The one viewer-relative page is the recruiter list: drawn only from a 200 of
17
17
  GET /scouting, whose door (a project owner, or project.curate) is the server's, never
18
18
  this page's (scouting-authz.js). A 401 or 403 draws no row.
19
+ THE RECRUIT DOOR (task 1002292; spec D6, owner 2026-10-27): a project OWNER (GET
20
+ /my-projects `owned`) gets a Recruit link on each leaderboard and search row. The hub
21
+ cannot read a project's project.recruit, which lives on that project (ADR 0335 D1), so
22
+ ownership stands in. The link only opens the project's own hall invite box with the
23
+ login filled in (ADR 0353); the project decides. The rows themselves never change.
19
24
  Hub-only, like /account: platform-server.js serves /community only where
20
25
  platform-identity is on. noindex until the SEO goal decides (owner, 2026-10-27).
21
26
  Server-stamped like the footer doors: {{…}} tokens filled by landingPage(). -->
@@ -69,19 +74,29 @@
69
74
  .connAv{width:36px;height:36px;border-radius:50%;flex:none;object-fit:cover;background:color-mix(in srgb,var(--ink) 12%,transparent);}
70
75
  .commRow .mineRow__name,.connWho .mineRow__name{overflow-wrap:anywhere;}
71
76
  /* the two dropdowns (the window, the search's craft): the field's pill */
72
- .commWhen select,.commFind select{flex:0 1 auto;min-height:44px;padding:0 14px;font:inherit;font-size:14.5px;color:var(--ink);
77
+ .commWhen select,.commFind select,.commFor select{flex:0 1 auto;min-height:44px;padding:0 14px;font:inherit;font-size:14.5px;color:var(--ink);
73
78
  background:var(--bg-card);border:1px solid var(--ctl-line);border-radius:var(--radius-pill);cursor:pointer;}
74
- .commWhen select:focus-visible,.commFind input:focus-visible,.commFind select:focus-visible{outline:2px solid var(--focus);outline-offset:3px;box-shadow:0 0 0 8px var(--bg);}
79
+ .commWhen select:focus-visible,.commFind input:focus-visible,.commFind select:focus-visible,.commFor select:focus-visible{outline:2px solid var(--focus);outline-offset:3px;box-shadow:0 0 0 8px var(--bg);}
75
80
  #findStatus{margin-top:8px;}
81
+ /* the Recruit door, a project owner's only (task 1002292): what it opens, or which
82
+ project it recruits to; then a fifth column after the credits, where your own row
83
+ keeps an invisible twin so every row's credits line up */
84
+ .commFor{display:flex;flex-wrap:wrap;align-items:center;gap:6px 10px;margin:6px 0 4px;max-width:62ch;font-size:13.5px;line-height:1.5;color:var(--ink-soft);}
85
+ #findFor{margin-top:12px;}
86
+ .commRows.canRecruit .commRow{grid-template-columns:3ch 36px minmax(0,1fr) auto auto;}
87
+ .commGap{visibility:hidden;}
76
88
  /* the recruiter list, signed out: the account page's door */
77
89
  .commDoor{padding:8px 0 10px;max-width:62ch;}
78
90
  .commDoor p{margin:0 0 18px;font-size:15px;line-height:1.55;color:var(--ink-soft);}
79
91
  .commDoor .gh{width:18px;height:18px;fill:currentColor;}
80
92
  .commDoor .btn{text-transform:none;}
81
93
  @media (max-width:480px){
82
- .commRow{grid-template-columns:3ch 32px minmax(0,1fr) auto;gap:0 10px;}
94
+ .commRow,.commRows.canRecruit .commRow{grid-template-columns:3ch 32px minmax(0,1fr) auto;gap:0 10px;}
83
95
  .commRow .connAv{width:32px;height:32px;}
84
96
  .commNum{font-size:19px;}
97
+ /* no room for a fifth column: Recruit drops under the name, as a row's actions do */
98
+ .commRow .commRecruit{grid-column:3/-1;justify-self:start;margin-top:8px;}
99
+ .commGap{display:none;}
85
100
  }
86
101
  </style>
87
102
  </head>
@@ -176,6 +191,7 @@
176
191
  </div>
177
192
  </div>
178
193
  <p class="commNote" id="boardNote">{{Currency}} earned across every project, since each builder started.</p>
194
+ <div class="commFor" id="boardFor" hidden></div>
179
195
  <p class="commMsg" id="boardLoading" role="status">Reading the leaderboard…</p>
180
196
  <p class="projMsg err" id="boardFailed" role="alert" hidden></p>
181
197
  <p class="commMsg" id="boardEmpty" hidden></p>
@@ -201,6 +217,7 @@
201
217
  </select>
202
218
  <button id="findBtn" type="submit" class="btn small">Search</button>
203
219
  </form>
220
+ <div class="commFor" id="findFor" hidden></div>
204
221
  <p class="projMsg" id="findStatus" role="status" aria-live="polite"></p>
205
222
  <ul class="mineRows" id="findList"></ul>
206
223
  </section>
@@ -259,8 +276,9 @@
259
276
  var UNIT = $('boardList').getAttribute('data-unit') || 'credits';
260
277
 
261
278
  /* THREE THINGS THESE PAGES MUST NOT DO (ADR 0217 D1, ADR 0337 D4.1, ADR 0255):
262
- 1. differ for a viewer anywhere but the recruiter list — the leaderboard and the
263
- search are drawn from viewer-independent reads, and neither asks /me;
279
+ 1. differ for a viewer anywhere but the recruiter list and the Recruit door — the
280
+ leaderboard and the search rows come from viewer-independent reads, and a
281
+ project owner only gains a link on each (task 1002292, spec D6);
264
282
  2. draw a recruiter row from anything but a 200 of GET /scouting, or draw more of a
265
283
  name-only row than its name, face and login;
266
284
  3. re-rank, re-filter or re-count what the server sent — rows are drawn in the
@@ -317,6 +335,36 @@
317
335
  return arr(list).map(function (d) { return CRAFT_WORD[d] || String(d); }).join(' · ');
318
336
  }
319
337
 
338
+ // ── the Recruit door: a project owner's link on each row (task 1002292) ─────
339
+ // Who sees it is GET /my-projects' `owned` (owner, 2026-10-27): the hub cannot read a
340
+ // project's project.recruit. Where it goes is invite-notices.js hallInviteUrl's shape,
341
+ // the project's hall invite box, which only fills the name in; the project decides.
342
+ var recruit = { to: [], pick: 0, me: '' };
343
+ function originOf(raw) {
344
+ try {
345
+ var u = new URL(String(raw));
346
+ return (u.protocol === 'https:' || u.protocol === 'http:') ? u.origin : '';
347
+ } catch (e) { return ''; }
348
+ }
349
+ // `keepPlace`: a board row keeps an invisible twin on your own row, so its credits line up
350
+ function recruitCell(r, keepPlace) {
351
+ var p = recruit.to[recruit.pick];
352
+ if (!p) return null;
353
+ var login = String(r.github_login || '');
354
+ if (!login || login.toLowerCase() === recruit.me) {
355
+ if (!keepPlace) return null;
356
+ var gap = el('span', 'btn small ghost commRecruit commGap', 'Recruit');
357
+ gap.setAttribute('aria-hidden', 'true');
358
+ return gap;
359
+ }
360
+ var a = el('a', 'btn small ghost commRecruit', 'Recruit');
361
+ a.setAttribute('href', p.origin + '/builders/watch?invite=' + encodeURIComponent(login));
362
+ a.setAttribute('target', '_blank');
363
+ a.setAttribute('rel', 'noopener');
364
+ a.setAttribute('aria-label', 'Recruit ' + (r.display_name || r.handle || login) + ' to ' + p.name + ' (opens in a new tab)');
365
+ return a;
366
+ }
367
+
320
368
  // ── the leaderboard ─────────────────────────────────────────────────────────
321
369
  var board = { window: 'all', craft: '' };
322
370
  var boardSeq = 0;
@@ -338,6 +386,8 @@
338
386
  credits.appendChild(el('span', 'commNum', num(r.credits).toLocaleString()));
339
387
  credits.appendChild(el('span', 'commUnit', UNIT));
340
388
  li.appendChild(credits);
389
+ var door = recruitCell(r, true);
390
+ if (door) li.appendChild(door);
341
391
  return li;
342
392
  }
343
393
  function emptyText() {
@@ -345,8 +395,11 @@
345
395
  if (board.window === 'all') return 'No ' + who + ' on the leaderboard yet.';
346
396
  return 'No ' + who + ' earned ' + UNIT + ' in this window yet.';
347
397
  }
398
+ var boardShown = null; // the last board drawn, redrawn when the Recruit door changes
348
399
  function paintBoard(results) {
349
400
  var list = $('boardList');
401
+ boardShown = results;
402
+ list.className = 'commRows' + (recruit.to.length ? ' canRecruit' : '');
350
403
  list.textContent = '';
351
404
  results.forEach(function (r) { if (r) list.appendChild(boardRow(r)); });
352
405
  $('boardEmpty').textContent = list.children.length ? '' : emptyText();
@@ -366,6 +419,7 @@
366
419
  if (seq !== boardSeq) return; // a later pick answered first; its rows win
367
420
  $('boardLoading').hidden = true;
368
421
  if (!r.ok) {
422
+ boardShown = null;
369
423
  $('boardList').textContent = '';
370
424
  $('boardEmpty').hidden = true;
371
425
  $('boardFailed').textContent = failText(r.status, 'The leaderboard');
@@ -403,23 +457,35 @@
403
457
  if (c) main.appendChild(el('p', 'mineRow__hint', c));
404
458
  who.appendChild(main);
405
459
  li.appendChild(who);
460
+ var door = recruitCell(r, false);
461
+ if (door) {
462
+ var acts = el('div', 'mineRow__actions');
463
+ acts.appendChild(door);
464
+ li.appendChild(acts);
465
+ }
406
466
  return li;
407
467
  }
468
+ var found = null; // the last results drawn, redrawn when the Recruit door changes
469
+ function paintFound(results) {
470
+ var list = $('findList');
471
+ found = results;
472
+ list.textContent = '';
473
+ if (results) results.forEach(function (p) { if (p) list.appendChild(findRow(p)); });
474
+ }
408
475
  function find() {
409
476
  var q = String($('findQ').value || '').trim();
410
477
  var craft = $('findCraft').value || '';
411
478
  var list = $('findList');
412
- if (q.length < QUERY_MIN) { list.textContent = ''; say('Type at least ' + QUERY_MIN + ' letters of a name.', 'err'); return Promise.resolve(); }
413
- if (q.length > QUERY_MAX) { list.textContent = ''; say('A search is at most ' + QUERY_MAX + ' characters.', 'err'); return Promise.resolve(); }
479
+ if (q.length < QUERY_MIN) { paintFound(null); say('Type at least ' + QUERY_MIN + ' letters of a name.', 'err'); return Promise.resolve(); }
480
+ if (q.length > QUERY_MAX) { paintFound(null); say('A search is at most ' + QUERY_MAX + ' characters.', 'err'); return Promise.resolve(); }
414
481
  var btn = $('findBtn');
415
482
  btn.disabled = true;
416
483
  say('Searching…');
417
484
  var path = '/community/search?q=' + encodeURIComponent(q) + (craft ? '&discipline=' + encodeURIComponent(craft) : '');
418
485
  return read(path).then(function (r) {
419
486
  btn.disabled = false;
420
- list.textContent = '';
421
- if (!r.ok) { say(failText(r.status, 'The search'), 'err'); return; }
422
- arr(r.data.results).forEach(function (p) { if (p) list.appendChild(findRow(p)); });
487
+ if (!r.ok) { paintFound(null); say(failText(r.status, 'The search'), 'err'); return; }
488
+ paintFound(arr(r.data.results));
423
489
  var n = list.children.length;
424
490
  say(n ? plural(n, 'builder') + ' found.' : 'No builder by that name' + (craft ? ' in that role' : '') + '.');
425
491
  });
@@ -449,6 +515,51 @@
449
515
  });
450
516
  }
451
517
 
518
+ // ── the Recruit door's line, and the rows redrawn with it ───────────────────
519
+ function repaintRows() {
520
+ if (boardShown) paintBoard(boardShown);
521
+ if (found) paintFound(found);
522
+ }
523
+ // One project: what Recruit opens. Several: which one it recruits to, drawn once, so a
524
+ // pick redraws only the rows and the choice keeps the focus.
525
+ function paintFor(box) {
526
+ box.textContent = '';
527
+ box.hidden = false;
528
+ if (recruit.to.length === 1) {
529
+ box.textContent = 'Recruit opens ' + recruit.to[0].name + '’s hall with their name filled in, and you send the invite there.';
530
+ return;
531
+ }
532
+ var id = box.id + 'Pick';
533
+ var label = el('label', null, 'Recruit to');
534
+ label.setAttribute('for', id);
535
+ var pick = el('select', 'commForPick');
536
+ pick.id = id;
537
+ recruit.to.forEach(function (p, i) { var o = el('option', null, p.name); o.value = String(i); pick.appendChild(o); });
538
+ pick.value = String(recruit.pick);
539
+ pick.addEventListener('change', function () {
540
+ var i = Number(pick.value);
541
+ if (!recruit.to[i] || i === recruit.pick) return;
542
+ recruit.pick = i;
543
+ repaintRows();
544
+ });
545
+ box.appendChild(label);
546
+ box.appendChild(pick);
547
+ box.appendChild(el('span', null, 'Recruit opens its hall with their name filled in, and you send the invite there.'));
548
+ }
549
+ // A failed or empty read is no door at all: viewing never waits on it.
550
+ function loadOwned(me, box) {
551
+ return read('/my-projects').then(function (r) {
552
+ if (!r.ok) return;
553
+ recruit.me = String(me.github_login || '').toLowerCase();
554
+ recruit.to = arr(r.data.owned).map(function (p) {
555
+ return { name: String((p && (p.name || p.client_id)) || 'your project'), origin: originOf(p && p.origin) };
556
+ }).filter(function (p) { return p.origin; });
557
+ if (!recruit.to.length) return;
558
+ paintFor(box);
559
+ repaintRows();
560
+ });
561
+ }
562
+
452
563
  // ── the sub-page the path names: shown, named, marked current, and read ──────
453
564
  var VIEW = viewOf(window.location.pathname);
454
565
  var view = VIEWS[VIEW];
@@ -461,6 +572,7 @@
461
572
  $('signinTop').setAttribute('href', '/api/bongos/auth/web/start?return=' + view.path + '&repos=1');
462
573
  if (VIEW === 'leaderboard') loadBoard();
463
574
  if (VIEW === 'recruiters') bar.ready.then(function (me) { if (me) loadRecruit(); else showRecruit('recruitDoor'); });
575
+ else bar.ready.then(function (me) { if (me) loadOwned(me, $(VIEW === 'find' ? 'findFor' : 'boardFor')); });
464
576
  })();
465
577
  </script>
466
578
  <script src="/assets/invite-badge.js"></script>
@@ -0,0 +1 @@
1
+ {"_":"GET /my-projects for a builder who owns TWO projects (task 1002292): the community pages' Recruit door asks which project it recruits to.","owned":[{"client_id":"mercury","name":"Mercury","origin":"https://mercury.example.test","hall_origin":null,"membership_kind":"owner","joined_at":"2026-08-01T10:00:00.000Z","last_active":"2026-10-20T18:00:00.000Z"},{"client_id":"tidepool","name":"Tidepool","origin":"https://tidepool.example.test","hall_origin":null,"membership_kind":"owner","joined_at":"2026-09-02T10:00:00.000Z","last_active":"2026-10-24T18:00:00.000Z"}],"building":[],"applied":[]}
@@ -0,0 +1 @@
1
+ {"_":"GET /my-projects for a builder who owns ONE project (task 1002292): the community pages' Recruit door shows on every row and opens Mercury's hall invite box.","owned":[{"client_id":"mercury","name":"Mercury","origin":"https://mercury.example.test","hall_origin":null,"membership_kind":"owner","joined_at":"2026-08-01T10:00:00.000Z","last_active":"2026-10-20T18:00:00.000Z"}],"building":[{"client_id":"otb","name":"Example World","origin":"https://example.com","hall_origin":"https://builders.example.com","membership_kind":"member","joined_at":"2026-07-20T10:00:00.000Z","last_active":"2026-08-27T18:00:00.000Z"}],"applied":[]}
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.23.11",
3
+ "version": "1.23.12",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.23.11",
9
+ "version": "1.23.12",
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.23.11",
3
+ "version": "1.23.12",
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",
@@ -10453,5 +10453,11 @@
10453
10453
  "id": "1004256",
10454
10454
  "text": "Signed-in builders now find 'Builders you may know' in the Community menu; it opens their suggestions on the Connections page, which says so plainly when there's no one to suggest yet."
10455
10455
  }
10456
+ ],
10457
+ "1.23.12": [
10458
+ {
10459
+ "id": "1002292",
10460
+ "text": "Project owners can now press Recruit next to any builder on the community leaderboard or search, which opens their project's invite page with that builder's name already filled in."
10461
+ }
10456
10462
  ]
10457
10463
  }
package/src/module-api.js CHANGED
@@ -81,7 +81,7 @@ const { sleep } = require('./sleep');
81
81
  // MAJOR (see allowBoxScope below): passes the request through untouched.
82
82
  function deprecatedNoopMiddleware(_req, _res, next) { next(); }
83
83
 
84
- const CORE_VERSION = '1.23.11'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
84
+ const CORE_VERSION = '1.23.12'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
85
85
 
86
86
  // A namespaced logger so a module's log lines are attributable + consistent.
87
87
  // Usage: const log = api.logger('discord'); log.info('mounted');