@bongos/core 1.19.1021 → 1.19.1023

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 CHANGED
@@ -2,22 +2,22 @@
2
2
  "artifact": "bongos-core",
3
3
  "manifest_schema": 1,
4
4
  "generator": "scripts/gds/package-core.js",
5
- "core_version": "1.19.1021",
6
- "core_contract": "1.19.1021",
7
- "source_commit": "360443be4aed5fa5091203a152fd5a72c4cc64c7",
5
+ "core_version": "1.19.1023",
6
+ "core_contract": "1.19.1023",
7
+ "source_commit": "f1709fb9168ad999a9e19c281031097e2c5e7d66",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-25T02:23:54.486Z",
9
+ "built_at": "2026-09-25T19:19:01.574Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
- "docs_redacted": 528,
12
+ "docs_redacted": 529,
13
13
  "agent_docs_stubbed": 26,
14
14
  "functional_verbatim": 2443,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 2997,
20
- "tree_sha256": "d5ed91981613c4deb12b7cd2ff11d48b490de86693475fb6141c8439ed5d6d0b",
19
+ "file_count": 2998,
20
+ "tree_sha256": "764165bfe692a37fd3627ba40e362a91f07442b09312055bb843444b529412aa",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/ask-for-help/SKILL.md",
@@ -2177,12 +2177,17 @@
2177
2177
  {
2178
2178
  "path": "docs/adr/0336-a-guilds-record-is-the-sum-of-what-its-members-already-show.md",
2179
2179
  "mode": "0000644",
2180
- "sha256": "b2c4c09eb12644a0d00c3c277b5f36753abd91b9637d64741f7e10de89e0645d"
2180
+ "sha256": "d62d6550483fe11386ce097cb37112adf76111dabcd898291ff3d5ce5b63f69d"
2181
+ },
2182
+ {
2183
+ "path": "docs/adr/0337-the-hub-account-is-the-person-a-hall-account-is-their-seat-in-one-project.md",
2184
+ "mode": "0000644",
2185
+ "sha256": "b64fbfec7c6bda4ec654cb89a31878d401ab4c9ea5401d3129b74cc5627edecd"
2181
2186
  },
2182
2187
  {
2183
2188
  "path": "docs/adr/README.md",
2184
2189
  "mode": "0000644",
2185
- "sha256": "e221ffd25568bcc9ba803b80538d83b270be7f60e19e301aa1f630a28cf55c74"
2190
+ "sha256": "0a22c2623b9a4f8daa0235d9f7b99089300aadaca2e66119ce3f31822b9d7797"
2186
2191
  },
2187
2192
  {
2188
2193
  "path": "docs/api-reference.md",
@@ -2752,7 +2757,7 @@
2752
2757
  {
2753
2758
  "path": "docs/module-api-changelog.md",
2754
2759
  "mode": "0000644",
2755
- "sha256": "df510d1f2cfccd7b86c323d527da4db89cb25d08bb1c4abda4d0d22af71de17e"
2760
+ "sha256": "a0b4b728b9f072ef594daec6b954009202a8ed79341de077d8a7f447ebbbb7d7"
2756
2761
  },
2757
2762
  {
2758
2763
  "path": "docs/modules-contract.md",
@@ -8307,12 +8312,12 @@
8307
8312
  {
8308
8313
  "path": "package-lock.json",
8309
8314
  "mode": "0000644",
8310
- "sha256": "ecdd7d72f83abfc4a967ae9682ccc7c7cde5251b3708eb0d3eb059b4dbacda77"
8315
+ "sha256": "37992bffa6073d8ed94d8e960640e73f6bd128bb24787b50253a1e393b8c91b0"
8311
8316
  },
8312
8317
  {
8313
8318
  "path": "package.json",
8314
8319
  "mode": "0000644",
8315
- "sha256": "3b64b284d40618876f556d276ec93bd06cc865bb1c4136fb23f3707dabf7d316"
8320
+ "sha256": "97df5c02b04233580f36c9bd8c0bff0be8d0984b148b05ef45f8039ad4a50600"
8316
8321
  },
8317
8322
  {
8318
8323
  "path": "public-docs/index.html",
@@ -10357,7 +10362,7 @@
10357
10362
  {
10358
10363
  "path": "src/module-api.js",
10359
10364
  "mode": "0000644",
10360
- "sha256": "54d5dfa5de9f665e08e4e2276453476a1489cb97c53c18e24f3a1c961670b0f3"
10365
+ "sha256": "97d9786da4519b10d5025fded13c1e8c765f1510f1c7df3ba101f809bb95639c"
10361
10366
  },
10362
10367
  {
10363
10368
  "path": "src/module-loader/catalog.js",
@@ -1,6 +1,6 @@
1
1
  # ADR 0336 — A guild is a hub-side group whose members each consent twice, and its collective record is only ever the sum of what those members already show
2
2
 
3
- - **Status:** accepted for D1–D4 and D6–D8. **D5 (the collective record) is a recommendation awaiting the owner's ratification** before [task 1002302](https://cloudbongos.com/builders#/task/1002302) (R26) builds it.
3
+ - **Status:** accepted for D1–D4 and D6–D8. **D5 was not ratified: the owner chose a different rule, recorded in [ADR 0337](0337-the-hub-account-is-the-person-a-hall-account-is-their-seat-in-one-project.md) D4.4** (totals count hidden and private members by default, with a notice and a per-guild off switch, rounded, once a guild has at least 3 of them, refreshed weekly; D4's size rule is unchanged). [Task 1002302](https://cloudbongos.com/builders#/task/1002302) (R26) builds to ADR 0337, and D8's map ranks by the rounded figure.
4
4
  - **Date:** 2026-09-24
5
5
  - **Task:** [task 1002299](https://cloudbongos.com/builders#/task/1002299) (R23, goal 1000110 — which folds in goal 1000045's criteria C7/C8/C9)
6
6
  - **Namespace:** `/g/<slug>`
@@ -109,7 +109,7 @@ R35 (a cluster invited to form a guild) needs a batch. R24's invite route takes
109
109
 
110
110
  Every count is the length of a list the reader can see (ADR 0202 D4). The guild's public **size** is the length of its public roster. There is never a "+N more".
111
111
 
112
- ### D5 — The collective record counts a member if and only if that member's own record is public right now *(recommendation, for ratification)*
112
+ ### D5 — The collective record counts a member if and only if that member's own record is public right now *(recommendation, NOT ratified — superseded by [ADR 0337](0337-the-hub-account-is-the-person-a-hall-account-is-their-seat-in-one-project.md) D4.4)*
113
113
 
114
114
  **The rule:** a member is **counted** iff `shown_publicly AND rollupVisibleSql(a)`: they agreed to be on this guild's public page, **and** their own cross-project record is publicly visible (active, public, not hidden). The record is computed **at read time, on every read**, sent `no-store`, and never stored, cached or snapshotted. It shows:
115
115
 
@@ -150,7 +150,7 @@ A project still admits **people**, through the one door ADR 0201 built, so no ne
150
150
 
151
151
  ### D8 — The map ranks public guilds by two published numbers, computed at read time
152
152
 
153
- **Who:** `visibility = 'public'` and a non-empty public roster. **Two orderings, no stored score:** **size** = M, and **shipped** = the counted members' summed works shipped (D5). Ties break on `slug`. Every sort key is a number the row itself publishes (ADR 0255: a public list's order is a covert column otherwise), and nothing ranks on a hidden input. The read is `no-store` and paged like the projects feed. **Cost:** one aggregate over public-guild memberships joined to the activity rollup, per read. At the hub's current scale (hundreds of accounts) that is a few index scans. The revisit trigger is a measured p95, and any cache added then must be invalidated by a hide, a privacy flip, a `shown_publicly` change, a membership change and a visibility change. Otherwise a stale rank position would keep publishing a withdrawn number (the ADR 0202 D3 reason). The ranking inputs are decided here. [Task 1002305](https://cloudbongos.com/builders#/task/1002305) (R29) keeps the visual approach, plus whether a combined score earns its place. Any such score may use only M and the D5 totals.
153
+ **Who:** `visibility = 'public'` and a non-empty public roster. **Two orderings, no stored score:** **size** = M, and **shipped** = the counted members' summed works shipped (D5; per [ADR 0337](0337-the-hub-account-is-the-person-a-hall-account-is-their-seat-in-one-project.md) D4.4, the rounded weekly total). Ties break on `slug`. Every sort key is a number the row itself publishes (ADR 0255: a public list's order is a covert column otherwise), and nothing ranks on a hidden input. The read is `no-store` and paged like the projects feed. **Cost:** one aggregate over public-guild memberships joined to the activity rollup, per read. At the hub's current scale (hundreds of accounts) that is a few index scans. The revisit trigger is a measured p95, and any cache added then must be invalidated by a hide, a privacy flip, a `shown_publicly` change, a membership change and a visibility change. Otherwise a stale rank position would keep publishing a withdrawn number (the ADR 0202 D3 reason). The ranking inputs are decided here. [Task 1002305](https://cloudbongos.com/builders#/task/1002305) (R29) keeps the visual approach, plus whether a combined score earns its place. Any such score may use only M and the D5 totals.
154
154
 
155
155
  ## What this unblocks, and how each task's scope changes
156
156
 
@@ -158,7 +158,7 @@ A project still admits **people**, through the one door ADR 0201 built, so no ne
158
158
  |---|---|
159
159
  | [task 1002300](https://cloudbongos.com/builders#/task/1002300) R24 — CRUD + membership + visibility | Adds: D1 migration (three tables, single-owner index, `shown_publicly`, `membership_kind` not `role`); D2 owner/member + transfer (two statements, one transaction) + last-member dissolve; D3 two-consent join, the connections lifecycle and constants reused, **no self-join**, **unlisted takes invites only**, batch invite (≤ 25); D4 public read 404s unlisted to everyone, own-scope read for members, `shown_publicly` own-scope toggle, and an accept that carries its card's visibility (409 if it changed); D6 slug rules + reserved words, slug immutable; new files `guilds.js` + `routes/guilds.js`, every route in `tests/own_scope_guard.mjs`, every account read joining `account-visibility.js`. |
160
160
  | [task 1002301](https://cloudbongos.com/builders#/task/1002301) R25 — guild page UI | Page at `/g/<slug>`; public roster per D4 (handle, display name, avatar, kind; never `github_id`/login); "N of M" per D5; members' view via own-scope call; the "show me on this guild's public page?" prompt after an unlisted→public flip. |
161
- | [task 1002302](https://cloudbongos.com/builders#/task/1002302) R26 — collective rollup | **Retitle:** "counts only members whose own record is public" replaces "excludes hidden members". Read-time, never stored, `no-store`; counted = `shown_publicly AND rollupVisibleSql`; projects = named public projects only, **no union count of non-public projects**. **Blocked on the owner ratifying D5.** |
161
+ | [task 1002302](https://cloudbongos.com/builders#/task/1002302) R26 — collective rollup | **Superseded: build to [ADR 0337](0337-the-hub-account-is-the-person-a-hall-account-is-their-seat-in-one-project.md) D4.4.** Original text: **Retitle:** "counts only members whose own record is public" replaces "excludes hidden members". Read-time, never stored, `no-store`; counted = `shown_publicly AND rollupVisibleSql`; projects = named public projects only, **no union count of non-public projects**. **Blocked on the owner ratifying D5.** |
162
162
  | [task 1002303](https://cloudbongos.com/builders#/task/1002303) R27 — guild applications + invites | Adds the engagement table (D7). Apply = each member's own vouched application through the existing relay, tagged via the echo. Invite = individual invites fanned out to the public roster at that moment, **never** a guild-level pre-approval; the "P invited this guild" notice names P only when P is on the map and Public. The guild label travels on the applicant-profile read, never a new instance field. |
163
163
  | [task 1002304](https://cloudbongos.com/builders#/task/1002304) R28 — owner view of guild applications | **Dependency fix:** it depends on R11 ([task 1002287](https://cloudbongos.com/builders#/task/1002287)), which is **abandoned** (merged into [task 1002332](https://cloudbongos.com/builders#/task/1002332), which shipped), so R28 can never auto-promote. Repoint that edge to 1002332. Scope: group vouched rows by the guild label; "approve all" = N individual approvals. |
164
164
  | [task 1002305](https://cloudbongos.com/builders#/task/1002305) R29 — ranking + viz spike | **Narrows to the visual approach.** The ranking inputs, audience and ordering are decided in D8. Must also confirm which orb surface exists, since goals 1000044 and 1000049, which it names, are both **archived**. |
@@ -0,0 +1,71 @@
1
+ # 0337 — The hub account is the person, a hall account is their seat in one project, so anything that spans projects lives on the hub
2
+
3
+ - **Status:** Accepted
4
+ - **Date:** 2026-09-25
5
+ - **Deciders:** the owner (direction given 2026-09-24 while reviewing goal 1000110 Wave 1; the D4 calls answered 2026-09-25), Claude.
6
+ - **Task:** [task 1004255](https://cloudbongos.com/builders#/task/1004255) (goal 1000110, Working area 2: account creation, management & community)
7
+ - **Amends:** [ADR 0336](0336-a-guilds-record-is-the-sum-of-what-its-members-already-show.md) D5 (superseded by D4.4 below) and D8 (the map ranks by the rounded total); `docs/specs/<redacted>.md` D8 (D4.1); the privacy spec's "`hide_stats` beats everything" rule, for guild totals only, and only for a member who was told and left counting on (D4.4). ADR 0336 D4 (a guild's size is its public roster length) is **unchanged**.
8
+ - **Relates to:** [ADR 0141](<redacted>.md) (the hub is the identity provider; authority stays local), [ADR 0217](0217-rank-gates-inviting-not-viewing.md) (rank gates inviting, not viewing), [ADR 0334](0334-the-recruiting-directory-lists-a-hidden-builder-by-name.md) (the name-only recruiting tier), [ADR 0335](0335-an-invite-is-a-pre-approved-row-on-the-projects-own-instance.md) (an invite is the project's own row), [ADR 0255](0255-a-public-lists-ordering-is-part-of-its-payload.md) (a public list is ordered only by what it publishes)
9
+
10
+ ## Context
11
+
12
+ ADR 0141 made cloudbongos.com (the hub) the identity provider. One platform account signs in to every hosted project, and each project still decides rank, credits and admission for itself. That settled *who you are*. It did not settle *where a page goes*, so each task's text decided that for itself.
13
+
14
+ Goal 1000110 Wave 1 (2026-09-24) showed what that costs. R15 ([task 1002291](https://cloudbongos.com/builders#/task/1002291), ADR 0334) built a cross-project recruiting directory into the hall's `/people` because its task text said to. An owner call of 2026-08-30 had already moved cross-project discovery to the hub ([task 1003363](https://cloudbongos.com/builders#/task/1003363), [task 1003444](https://cloudbongos.com/builders#/task/1003444)). A placement audit of the goal's 66 open tasks found more of the same: recruiting, guild-map and settings tasks that placed a cross-project surface in the hall, or said nothing and would default there. Reviewing the wave, the owner said the people directory "shouldn't exist within the builder hall but instead the hub itself… the builders hall account is essentially a sub account that is specific to its project's domain", and that guilds "and many of these tasks also likely belong on the hub."
15
+
16
+ ## Decision
17
+
18
+ **The hub account is the platform identity. A hall account is a sub-account: the same person's seat in one project, scoped to that project's domain. A surface is placed by the scope of what it shows.**
19
+
20
+ ### D1 — The placement rule
21
+
22
+ | A surface goes on… | when what it shows is… | e.g. |
23
+ |---|---|---|
24
+ | **the HUB** (`modules/public-landing/`, `modules/platform-identity/`) | the person across projects, or more than one project | the people directory, community (leaderboards, search, builder of the week), guilds, connections, recruiting and scouting, the public profile, platform-account settings (D4.3) |
25
+ | **the HALL** (the project's own instance, `modules/hall-ui/`) | this project's people and work | `/roster` (Builders), the project console (invites, admission, ranks), tasks, goals, in-project activity and achievements |
26
+ | **both** | a person-centred view that has a useful in-project cut | "Builders you may know" (D4.2) |
27
+
28
+ `modules/status-ui/` is neither: it is the public status dashboard at `status.<apex>`, not the hall. A hall page may *link* to the hub for the cross-project view, but it does not rebuild it. When a task's text places a cross-project surface in the hall, the task is re-scoped before anyone claims it, never built as written.
29
+
30
+ ### D2 — Authority does not move
31
+
32
+ D1 decides where pages go. It moves no authority. ADR 0141's line stands: the hub carries identity, and each project computes rank, credits and admission locally. A hub surface that acts on a project, such as an invite or an application, still lands as a row on that project (ADR 0335, ADR 0201). Hub pages read hub tables and the rollups projects already report. They never become a second authority over a project.
33
+
34
+ ### D3 — The hall's `/people` retires, and `/roster` is the in-project directory
35
+
36
+ The hall's `/people` retires ([task 1003444](https://cloudbongos.com/builders#/task/1003444)). Its cross-project job, including ADR 0334's two-tier recruiting directory, moves to the one hub community page ([task 1003363](https://cloudbongos.com/builders#/task/1003363)). ADR 0334's predicates move unchanged; only the page that renders them changes. `/roster` ("Builders") stays as the in-project directory and stops linking out to `/people`.
37
+
38
+ ### D4 — The owner's calls from the re-scope
39
+
40
+ 1. **Hidden builders open to recruiters: the recruiter panel is recruiter-only, and public search is unchanged.** A builder who hides their numbers but stays open to recruiters appears in ADR 0334's name-only panel (login, display name, avatar) on the hub community page. The panel is shown only to a signed-in viewer that the recruiting door admits, which ADR 0334 defines as a project owner or a holder of `project.curate`. The public builder search (`GET /community/search`, [task 1002979](https://cloudbongos.com/builders#/task/1002979)) keeps listing such a builder by name with no numbers, the same as their public profile page already does. Private accounts stay out of both, and the private-and-discoverable sliver keeps its archon floor (ADR 0217 D2). This amends community-tab spec D8's "to every viewer identically": the public view stays the same for every viewer, and a signed-in recruiter also sees the panel. It adds no new disclosure class.
41
+ 2. **"Builders you may know" is on both sides.** The hall profile panel ([task 1002297](https://cloudbongos.com/builders#/task/1002297)) stays. The hub community page gets a signed-in panel with the same suggestions across projects, over the existing own-scoped endpoint (`connection-suggestions.js`), with no new scoring. Every candidate keeps clearing `accountVisibleSql` there, so no private or provisional account can appear. The panel adds no read of `platform_identity_accounts` of its own (`tests/effective_visibility_predicate.mjs` holds that line).
42
+ 3. **Platform-account settings live on a new hub account page.** Public/private, open to recruiters, hide my numbers, and handle/bio/links are properties of the platform account, so they are edited on cloudbongos.com. Each hall's Settings keeps per-project options only and links to the hub page. [Task 1002784](https://cloudbongos.com/builders#/task/1002784) (the settings inventory) and [task 1002800](https://cloudbongos.com/builders#/task/1002800) build to this.
43
+ 4. **Guild totals count hidden and private members by default, with a notice and a per-guild switch (supersedes ADR 0336 D5).** The owner rejected D5's "count only members whose own record is public", because guilds of privacy-minded builders would look smaller than they are. A guild's totals should include its hidden and private members, while their own numbers stay unreadable. The rule for [task 1002302](https://cloudbongos.com/builders#/task/1002302) (R26):
44
+ - **Terms.** A *non-public member* is one whose numbers are not public on the guild's page: a private account, hidden numbers, or not agreeing to be shown (`shown_publicly = false`).
45
+ - **Notice and switch, which are the consent.** Every member is counted in the totals unless they switched it off for that guild. That is a per-membership flag, `counted_in_totals boolean NOT NULL DEFAULT true`, carried by R24's migration ([task 1002300](https://cloudbongos.com/builders#/task/1002300)). The invite, request and accept cards tell a non-public member, before they accept, that "your numbers count toward this guild's rounded totals", and they offer the switch there. The hide-my-numbers and private-account settings say the same and link to each guild's switch. A member who switches it off drops out at the next refresh.
46
+ - **Rounded, never exact.** Totals are published on a 1–2–5 band scale (10, 20, 50, 100, 200, 500, …), shown as the band floor ("100+"). The scale is a named constant in R26.
47
+ - **A minimum of 3.** Non-public members' numbers enter the totals only while the guild has at least 3 counted non-public members (a named constant, `k = 3`). Below that, the totals add up only its public members.
48
+ - **A weekly refresh.** Totals are a snapshot recomputed at most once a week, never on a read. Each before/after comparison then costs an observer a week.
49
+ - **Size is unchanged.** A guild's size stays the length of its public roster (ADR 0336 D4, ADR 0202 D4). Non-public members are never in it, so size minus roster reveals nothing, and a non-public member joining or leaving never moves it.
50
+ - ADR 0336 D8's map ranks by the rounded published total, never the exact sum (ADR 0255). Private members' projects never enter a project count (ADR 0336 D5's union rule stands).
51
+
52
+ **The honest limit, accepted by the owner (2026-09-25).** Rounding, the minimum and the weekly refresh stop a casual reader. They do not stop a determined guild owner, who can:
53
+ - add members whose numbers they already know, such as two fake private accounts, so that a real non-public member becomes the third and enters the total;
54
+ - then compare totals across refreshes, or pad with known members until the total sits on a band edge, and so learn a range for that member's numbers.
55
+
56
+ The notice and the switch exist for this reason. A non-public member is told that their numbers count, and can turn it off for any guild they don't trust. This relaxes the owner-signed privacy rule "`hide_stats` beats everything" (privacy spec D2) for **guild totals only**, and only for a member who was told and left counting on. Every other surface keeps the rule.
57
+
58
+ ## Consequences
59
+
60
+ - Goal 1000110 is re-scoped against D1 before its Wave 2, in one owner-run script. Retargeted tasks carry a description append citing this ADR. Duplicates of the hub community page merge into task 1003363. Dependency edges are re-pointed before any merge or abandon, so no dependent is left on a dead edge. The goal's module scope widens to `public-landing` and `hall-ui`.
61
+ - Hub work grows. `modules/platform-identity/` and `modules/public-landing/` carry most of the remaining community work, so they reach their line ratchets sooner. ADR 0336 D1 already split guilds into their own files for this reason.
62
+ - A self-hosted instance that does not federate (ADR 0141 §4) has no hub, and so no cross-project surfaces. Its hall keeps `/roster` and the console, which is all a single project needs.
63
+ - Guild totals become a weekly stored snapshot instead of ADR 0336 D5's read-time sum, and R24 gains the `counted_in_totals` column. The guild cards and privacy settings gain the notice copy (R25, R27, and the hub account settings page of D4.3).
64
+
65
+ ## Alternatives considered
66
+
67
+ - **Keep cross-project surfaces in each hall.** Rejected. Every hall would render the same platform-wide data, each copy would need the hub's privacy predicates, and a builder would meet a different "community" in each project.
68
+ - **Decide placement task by task.** Rejected, because that is what produced R15's hall directory. With a written rule, a task author can place a surface without asking.
69
+ - **Guild totals per ADR 0336 D5 (public records only).** This leaks nothing, even over time. The owner turned it down because guilds of privacy-minded builders would look smaller than they are (D4.4).
70
+ - **Count non-public members only if they opt in.** This is the safest for them, but totals understate until people tick the box. The owner chose counting by default with a notice and an off switch.
71
+ - **Count every member silently, protected only by rounding and the minimum of 3.** This was the first version of D4.4. It was withdrawn before landing, because the fake-account padding above defeats it for a member who was never told.
@@ -427,4 +427,5 @@ This keeps the decision history honest and traceable.
427
427
  | 0333 | [**The application vouch is a third token kind, spent once, bound to the login it names, and carries a handle only past the consent floor** ([task 1002285](https://cloudbongos.com/builders#/task/1002285), goal 1000045 — criterion C4), resolving [ADR 0208](0208-application-lifecycle-the-handle-waits-and-the-vouch-has-no-minter.md) D1 + D2. The hub's join relay now carries a signed application vouch in `<redacted>`, so the ADR 0260 live-record path is no longer dormant. `mintHubApplicationVouch` / `<redacted>` share one envelope with the login and logout tokens, and each verifier demands what the other two lack; `verifyHubAssertion` now refuses any `purpose`, closing the device-login replay. The jti is a chosen, separate single-use store; a vouch must carry a jti and an audience, and must name the login the request names, or the row is written unvouched (never refused); a vouched re-application takes over an unvouched pending row for its login. The vouch carries its login as `applicant_login`, so even an un-upgraded core refuses it as a login. `applicant_handle` (`core_254`) is identity, not record, and rides only past the consent floor.](0333-the-application-vouch-is-a-third-token-spent-once-and-bound-to-its-login.md) |
428
428
  | 0334 | [**The recruiting directory lists a hidden builder by name, and hide changes a row's shape, not whether it exists** ([task 1002291](https://cloudbongos.com/builders#/task/1002291), R15 — goal 1000110). Privacy spec D2 (owner-signed) lists an active, public, hidden account on the recruiter surface "name-only (if not opted out)"; the directory dropped it outright, making hide a second opt-out while Settings told that builder recruiters would see their name and picture. `GET /scouting` now answers two tiers: `builders` (ranked, `scoutingListableSql`, unchanged) and `name_only` (`scoutingNameOnlySql` = the same predicate with the hide term flipped, so the tiers partition the directory and only the opt-out removes anyone), projected to `github_login`, `display_name`, `avatar_url` by `scouting-name-only.js`. The existence floor stays (a private account is the archon-floor D3 sliver's, never the directory's); presence never depends on activity; the only order is the login; the predicate is pinned to that one reader and its three columns. The hall's `/people` renders the tier as its own panel, never ranked, found by name, never by a role filter. Rejected: fixing only the copy; trailing rows in the ranked ledger; listing private discoverable accounts by name.](0334-the-recruiting-directory-lists-a-hidden-builder-by-name.md) | privacy / recruiting / hall |
429
429
  | 0335 | [**An invite is a pre-approved row on the project's own instance, and `project.recruit` says who may send one** ([task 1002813](https://cloudbongos.com/builders#/task/1002813), R1 of the project admin console wave, goal 1000110 — BONGOS-V2). Supersedes ADR 0141 §5's "Invitations (deferred slice)" paragraph only. Formalises spike task 1002786's three decisions and reconciles them with six weeks of shipping: the instance invite route (task 1003044), the PATCH rescind (ADR 0201 D4.2) and the composed join door (ADR 0247) already exist, and the hub invite's dead end is now reachable from the creation wizard. **Decisions:** (D1) authority lives on the project's own instance, and the instance IS the project; the hub is identity, relay, echo, notification and links, and never writes a project's admission row; (D2) a recruit invite is an `access_requests` row with `status='invited'`, so signing in is the acceptance act and the gate is untouched; the row gains `kind ∈ (application, invite)` and `created_by` (the inviter, which a rescind would otherwise overwrite), reuses `note`, backfills exactly (`created_at = resolved_at`), and neither column ever enters an admission read; (D3) one new grantable atom, `project.recruit` (`system:false`, floor archon, widened per instance through the existing governance tab), guards the invite, the outstanding list, the invite rescind and the queue read, while `access_request.review` keeps the verdict; because inviting is admitting, a recruiter without the review atom may not approve or re-admit an APPLICANT through the invite route (409 to the queue); the hub sliver moves onto the atom, and the hub directory and hub invite KEEP `project.curate`, revising ADR 0210/0215/0248, since swapping the directory would raise a gate on a viewing surface (ADR 0217). **Task map:** R2 (task 1002814) = the migration, the invite writes and the sender list, still on `access_request.review`; R3 (task 1002815) = mint the atom, move the gates, the 409 clause, the sliver swap and the pins, and it lands after R2; R5 (task 1002817) is already delivered, so close it into task 1002331; task 1003362 folds into R3. Rejected: a new invitations table; the hub pending row as the invite; widening `access_request.review` instead of minting an atom; a per-project settings row for the floor; letting a recruiter's invite approve applications; `system:true`; swapping `project.curate` on the hub directory.](0335-an-invite-is-a-pre-approved-row-on-the-projects-own-instance.md) | governance / onboarding / platform-identity |
430
- | 0336 | [**A guild is a hub-side group whose members each consent twice, and its collective record is only ever the sum of what those members already show** ([task 1002299](https://cloudbongos.com/builders#/task/1002299), goal 1000110 — the R23 design spike for goal 1000045's C7/C8/C9). No platform guild existed (every "guild" in the tree is Discord's). Decisions: (1) three hub tables in `platform-identity`, in their own `guilds.js` (the 1,500-line ratchet), distinct from instance-local `goal_members`: guilds (slug, name, `visibility ∈ public|unlisted` with **no default**), members (`membership_kind ∈ owner|member`, never `role` per ADR 0174; a partial unique index holds exactly one owner; a per-member `shown_publicly`), and requests; (2) two consents to join, no self-join, through ADR 0202's connections lifecycle verbatim (silent decline, 90-day swallowed re-ask, 30-day read-time expiry); `accountActiveSql` floors the feature; an **unlisted guild 404s to everyone on the public read, members included**, and takes invites only, because a slug is guessable; (3) the public roster is `shown_publicly AND accountVisibleSql`, and widening a guild to public changes no member's flag but the flipping owner's, so consent given to a small audience is never reused for a large one; (4) **the privacy resolution, pending the owner's ratification:** a member is counted iff shown AND `rollupVisibleSql`, read-time, `no-store`, never stored, and N and M are both lengths of lists the reader can see. The guild record is therefore always a sum of numbers already on members' own public profiles, so nothing can be subtracted out. All-or-nothing is rejected (one member blanks everyone and hiding becomes costly), as is per-member consent to count hidden numbers (it breaks "hide_stats beats everything"). The trade-off: guilds of privacy-minded builders look smaller and rank lower. A union count of all members' projects is refused because it reveals shared private projects; (5) the namespace is `/g/<slug>`, `g` is reserved in the SEO spec's project-slug list, the slug is fixed in V1, and `tests/guild_namespace_reserved.mjs` pins the two documents together; (6) a guild engages a project only through the individual door: a guild application is each member's own vouched application, labelled through ADR 0260's live applicant-profile read (no new instance field); inviting a guild fans out ordinary individual invites to its public roster at that moment, **never a guild-level pre-approval** (that would let a guild owner decide admission to someone else's project); (7) the map ranks public guilds by size and counted works shipped, read-time, with a slug tiebreak, and R29 narrows to the visual. Scope changes for R24–R37 are tabled in the ADR, including R28's dependency on the abandoned R11 and R29/R31's archived orb goals.](0336-a-guilds-record-is-the-sum-of-what-its-members-already-show.md) | platform identity / privacy / social graph |
430
+ | 0336 | [**A guild is a hub-side group whose members each consent twice, and its collective record is only ever the sum of what those members already show** ([task 1002299](https://cloudbongos.com/builders#/task/1002299), goal 1000110 — the R23 design spike for goal 1000045's C7/C8/C9). No platform guild existed (every "guild" in the tree is Discord's). Decisions: (1) three hub tables in `platform-identity`, in their own `guilds.js` (the 1,500-line ratchet), distinct from instance-local `goal_members`: guilds (slug, name, `visibility ∈ public|unlisted` with **no default**), members (`membership_kind ∈ owner|member`, never `role` per ADR 0174; a partial unique index holds exactly one owner; a per-member `shown_publicly`), and requests; (2) two consents to join, no self-join, through ADR 0202's connections lifecycle verbatim (silent decline, 90-day swallowed re-ask, 30-day read-time expiry); `accountActiveSql` floors the feature; an **unlisted guild 404s to everyone on the public read, members included**, and takes invites only, because a slug is guessable; (3) the public roster is `shown_publicly AND accountVisibleSql`, and widening a guild to public changes no member's flag but the flipping owner's, so consent given to a small audience is never reused for a large one; (4) **the privacy resolution — NOT ratified; superseded by [ADR 0337](0337-the-hub-account-is-the-person-a-hall-account-is-their-seat-in-one-project.md) D4.4:** it proposed that a member is counted iff shown AND `rollupVisibleSql`, read-time, `no-store`, never stored, and N and M are both lengths of lists the reader can see. The guild record is therefore always a sum of numbers already on members' own public profiles, so nothing can be subtracted out. All-or-nothing is rejected (one member blanks everyone and hiding becomes costly), as is per-member consent to count hidden numbers (it breaks "hide_stats beats everything"). The trade-off: guilds of privacy-minded builders look smaller and rank lower. A union count of all members' projects is refused because it reveals shared private projects; (5) the namespace is `/g/<slug>`, `g` is reserved in the SEO spec's project-slug list, the slug is fixed in V1, and `tests/guild_namespace_reserved.mjs` pins the two documents together; (6) a guild engages a project only through the individual door: a guild application is each member's own vouched application, labelled through ADR 0260's live applicant-profile read (no new instance field); inviting a guild fans out ordinary individual invites to its public roster at that moment, **never a guild-level pre-approval** (that would let a guild owner decide admission to someone else's project); (7) the map ranks public guilds by size and counted works shipped, read-time, with a slug tiebreak, and R29 narrows to the visual. Scope changes for R24–R37 are tabled in the ADR, including R28's dependency on the abandoned R11 and R29/R31's archived orb goals.](0336-a-guilds-record-is-the-sum-of-what-its-members-already-show.md) | platform identity / privacy / social graph |
431
+ | 0337 | [**The hub account is the person, a hall account is their seat in one project, so anything that spans projects lives on the hub** ([task 1004255](https://cloudbongos.com/builders#/task/1004255), goal 1000110 — the owner's hub/hall re-scope, 2026-09-24/25). Extends [ADR 0141](<redacted>.md) from identity to placement: (1) a surface about the person across projects, or about more than one project, goes on the HUB (`public-landing`, `platform-identity`): people directory, community, guilds, connections, recruiting, platform-account settings; one about this project goes in the HALL (`hall-ui`); "Builders you may know" is on both; `status-ui` is the status dashboard, not the hall; (2) no authority moves, and hub actions still land as the project's own rows; (3) hall `/people` retires, ADR 0334's recruiting directory moves unchanged to the hub community page, `/roster` is the in-project directory; (4) owner calls: the hidden-builder recruiter panel is recruiter-only while public search is unchanged (amends community-tab spec D8); platform-account settings get a new hub page; **guild totals count hidden and private members by default, with a notice on the join card and a per-guild off switch (`counted_in_totals`), rounded to a 1–2–5 band, only once a guild has at least 3 of them, refreshed weekly; size stays the public roster length** (supersedes ADR 0336 D5; relaxes the privacy spec's "`hide_stats` beats everything" for guild totals only, for a member who was told), with the fake-account padding limit stated and accepted.](0337-the-hub-account-is-the-person-a-hall-account-is-their-seat-in-one-project.md) | platform identity / placement / privacy |
@@ -2521,5 +2521,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
2521
2521
  landed since 1.19.1019 with no explicit bump. run 36085437258. (task 1002620)
2522
2522
  1.19.1021 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2523
2523
  landed since 1.19.1020 with no explicit bump. run 36086047874. (task 1002620)
2524
+ 1.19.1022 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2525
+ landed since 1.19.1021 with no explicit bump. run 36176262855. (task 1002620)
2526
+ 1.19.1023 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
2527
+ landed since 1.19.1022 with no explicit bump. run 36178875275. (task 1002620)
2524
2528
  ---------------------------------------------------------------------------
2525
2529
  ```
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.1021",
3
+ "version": "1.19.1023",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.1021",
9
+ "version": "1.19.1023",
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.19.1021",
3
+ "version": "1.19.1023",
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/src/module-api.js CHANGED
@@ -71,7 +71,7 @@ const { responsibilityFor, ROLE_RESPONSIBILITIES } = require('./role-responsibil
71
71
  // there. scripts/gds/bump-version.js still rewrites the literal below; it appends
72
72
  // the entry to that file. Look for a version's history there, not here.
73
73
  // ---------------------------------------------------------------------------
74
- const CORE_VERSION = '1.19.1021'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
74
+ const CORE_VERSION = '1.19.1023'; // CI auto-patch carrier (ADR 0161); changelog: docs/module-api-changelog.md
75
75
 
76
76
  // A namespaced logger so a module's log lines are attributable + consistent.
77
77
  // Usage: const log = api.logger('dev-box'); log.info('mounted');