@bongos/core 1.19.426 → 1.19.428
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 +65 -30
- package/docs/adr/0212-connections-on-the-profile-surface-and-addressing.md +61 -0
- package/docs/adr/0213-platform-terms-acceptance-is-the-hubs-own-witness.md +71 -0
- package/docs/adr/README.md +2 -0
- package/docs/copy-inventory.md +34 -18
- package/docs/copy-registry.json +179 -25
- package/docs/file-map.md +2 -1
- package/docs/module-api-changelog.md +4 -0
- package/modules/hall-ui/public/profile-connections.js +304 -0
- package/modules/hall-ui/public/profile.html +16 -1
- package/modules/hall-ui/public/profile.js +5 -0
- package/modules/hall-ui/public/style.css +38 -0
- package/modules/platform-identity/account-visibility.js +31 -12
- package/modules/platform-identity/connections.js +4 -3
- package/modules/platform-identity/migrations/platform_identity_019_terms_acceptance.sql +47 -0
- package/modules/platform-identity/platform-identity.js +12 -10
- package/modules/platform-identity/routes/connections.js +4 -3
- package/modules/platform-identity/terms-acceptance.js +97 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/terms.html +8 -0
- package/scripts/hall-preview/server.js +3 -0
- package/src/module-api.js +1 -1
- package/tests/connections_api.mjs +13 -8
- package/tests/connections_ownership.mjs +6 -1
- package/tests/effective_visibility_predicate.mjs +89 -14
- package/tests/profile_connections_ui.mjs +889 -0
- package/tests/profile_privacy_owner_view.mjs +31 -4
- package/tests/profile_route.mjs +15 -8
- package/tests/terms_acceptance.mjs +573 -0
|
@@ -76,23 +76,28 @@ const GHOST = '5150'; // active · PRIVATE — the owner-renders subjec
|
|
|
76
76
|
const SHY = '6060'; // active · public · HIDDEN — the no-partial-numbers subject
|
|
77
77
|
const NOSY = '7777'; // active · public — the stranger at the side door
|
|
78
78
|
|
|
79
|
+
// ADR 0213 (task 1002913): consent is profile_state AND an acceptance of the
|
|
80
|
+
// platform terms, both written by the same statement — so a consented fixture
|
|
81
|
+
// carries both, and one carrying only the first is a row from before the terms.
|
|
82
|
+
const ACCEPTED_AT = '2026-08-29T00:00:00.000Z';
|
|
83
|
+
|
|
79
84
|
const ACCOUNTS = [
|
|
80
85
|
{ id: 1, github_id: OCTO, github_login: 'octo', display_name: 'Octo', avatar_url: null,
|
|
81
86
|
handle: 'octo', previous_handle: null, handle_changed_at: null, bio: null, links: null,
|
|
82
87
|
display_name_source: 'github', created_at: 'T0', updated_at: 'T0',
|
|
83
|
-
profile_state: 'active', account_visibility: 'public', hide_stats: false, recruiter_discoverable: null },
|
|
88
|
+
profile_state: 'active', terms_accepted_at: ACCEPTED_AT, account_visibility: 'public', hide_stats: false, recruiter_discoverable: null },
|
|
84
89
|
{ id: 2, github_id: GHOST, github_login: 'ghost', display_name: 'Ghost', avatar_url: null,
|
|
85
90
|
handle: 'ghost', previous_handle: null, handle_changed_at: null, bio: 'ghost-bio-515007', links: null,
|
|
86
91
|
display_name_source: 'github', created_at: 'T0', updated_at: 'T0',
|
|
87
|
-
profile_state: 'active', account_visibility: 'private', hide_stats: false, recruiter_discoverable: null },
|
|
92
|
+
profile_state: 'active', terms_accepted_at: ACCEPTED_AT, account_visibility: 'private', hide_stats: false, recruiter_discoverable: null },
|
|
88
93
|
{ id: 3, github_id: SHY, github_login: 'shy', display_name: 'Shy', avatar_url: null,
|
|
89
94
|
handle: 'shy', previous_handle: null, handle_changed_at: null, bio: null, links: null,
|
|
90
95
|
display_name_source: 'github', created_at: 'T0', updated_at: 'T0',
|
|
91
|
-
profile_state: 'active', account_visibility: 'public', hide_stats: true, recruiter_discoverable: null },
|
|
96
|
+
profile_state: 'active', terms_accepted_at: ACCEPTED_AT, account_visibility: 'public', hide_stats: true, recruiter_discoverable: null },
|
|
92
97
|
{ id: 4, github_id: NOSY, github_login: 'nosy', display_name: 'Nosy', avatar_url: null,
|
|
93
98
|
handle: 'nosy', previous_handle: null, handle_changed_at: null, bio: null, links: null,
|
|
94
99
|
display_name_source: 'github', created_at: 'T0', updated_at: 'T0',
|
|
95
|
-
profile_state: 'active', account_visibility: 'public', hide_stats: false, recruiter_discoverable: null },
|
|
100
|
+
profile_state: 'active', terms_accepted_at: ACCEPTED_AT, account_visibility: 'public', hide_stats: false, recruiter_discoverable: null },
|
|
96
101
|
];
|
|
97
102
|
|
|
98
103
|
// Every number and name the cross-project rollup can produce, per account.
|
|
@@ -135,6 +140,10 @@ function sentinelsOf(githubId) {
|
|
|
135
140
|
// anything else throws — a WHERE it cannot read must never degrade to no WHERE.
|
|
136
141
|
const KNOWN_COLUMNS = new Set(['profile_state', 'account_visibility', 'hide_stats', 'github_id', 'handle', 'previous_handle']);
|
|
137
142
|
|
|
143
|
+
// Columns this interpreter can answer an IS [NOT] NULL test on — a different
|
|
144
|
+
// syntax from the literal comparisons above, hence its own set (ADR 0213).
|
|
145
|
+
const KNOWN_NULL_TESTS = new Set(['terms_accepted_at']);
|
|
146
|
+
|
|
138
147
|
/**
|
|
139
148
|
* Build a row test from a statement's own WHERE text. Literal comparisons carry
|
|
140
149
|
* as much weight as placeholders here: the effective-visibility predicate is
|
|
@@ -144,8 +153,18 @@ const KNOWN_COLUMNS = new Set(['profile_state', 'account_visibility', 'hide_stat
|
|
|
144
153
|
function accountFilter(where, params) {
|
|
145
154
|
const tests = [];
|
|
146
155
|
const consumed = new Set();
|
|
156
|
+
// NULL-test terms actually turned into a filter, as opposed to column names
|
|
157
|
+
// merely recognised. The final sweep compares the two (ADR 0213).
|
|
158
|
+
const modelled = new Set();
|
|
147
159
|
|
|
148
160
|
if (/\bprofile_state = 'active'/.test(where)) tests.push((r) => r.profile_state === 'active');
|
|
161
|
+
// The ADR 0213 consent half. It is a NULL test, not a literal comparison, so
|
|
162
|
+
// neither sweep below would see it — an unmodelled IS NOT NULL degrades to no
|
|
163
|
+
// filter exactly like an unread $N, which is what those sweeps exist to refuse.
|
|
164
|
+
if (/\bterms_accepted_at IS NOT NULL/.test(where)) {
|
|
165
|
+
modelled.add('terms_accepted_at');
|
|
166
|
+
tests.push((r) => r.terms_accepted_at != null);
|
|
167
|
+
}
|
|
149
168
|
if (/\baccount_visibility = 'public'/.test(where)) tests.push((r) => r.account_visibility === 'public');
|
|
150
169
|
if (/\bNOT (?:[a-z_]+\.)?hide_stats\b/.test(where)) tests.push((r) => !r.hide_stats);
|
|
151
170
|
|
|
@@ -173,6 +192,14 @@ function accountFilter(where, params) {
|
|
|
173
192
|
throw new Error(`fake pool: unrecognised literal comparison on ${m[1]} in "${where}"`);
|
|
174
193
|
}
|
|
175
194
|
}
|
|
195
|
+
for (const m of where.matchAll(/\b([a-z_]+)\s+IS\s+(?:NOT\s+)?NULL\b/gi)) {
|
|
196
|
+
if (!KNOWN_NULL_TESTS.has(m[1])) {
|
|
197
|
+
throw new Error(`fake pool: unrecognised NULL test on ${m[1]} in "${where}"`);
|
|
198
|
+
}
|
|
199
|
+
if (!modelled.has(m[1])) {
|
|
200
|
+
throw new Error(`fake pool: NULL test on ${m[1]} in "${where}" was recognised but never MODELLED — the clause is present and this interpreter is ignoring it`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
176
203
|
return (r) => tests.every((fn) => fn(r));
|
|
177
204
|
}
|
|
178
205
|
|
package/tests/profile_route.mjs
CHANGED
|
@@ -354,25 +354,28 @@ const PROOF_PROJECTS = [
|
|
|
354
354
|
const sumOf = (k) => PROOF_PROJECTS.reduce((n, p) => n + p[k], 0);
|
|
355
355
|
|
|
356
356
|
// The one account row the reads consult. setAccountHideStats mutates it.
|
|
357
|
-
|
|
357
|
+
// ADR 0213 (task 1002913): consent is profile_state AND an acceptance of the
|
|
358
|
+
// platform terms, both stamped by the same upsertAccount branch.
|
|
359
|
+
const ACCEPTED_AT = '2026-08-29T00:00:00.000Z';
|
|
360
|
+
const hubAccount = { profile_state: 'active', terms_accepted_at: ACCEPTED_AT, account_visibility: 'public', hide_stats: false, handle: 'r06-handle' };
|
|
358
361
|
|
|
359
362
|
// The scouting "table": one visible builder plus the THREE that consent and
|
|
360
363
|
// visibility exclude (the private row joined at task 1002967 — the same axis,
|
|
361
364
|
// enforced by the same shared predicate).
|
|
362
365
|
const SCOUTING_TABLE = [
|
|
363
|
-
{ profile_state: 'active', account_visibility: 'public', hide_stats: false,
|
|
366
|
+
{ profile_state: 'active', terms_accepted_at: ACCEPTED_AT, account_visibility: 'public', hide_stats: false,
|
|
364
367
|
row: { github_id: '11', github_login: 'r06-visible', display_name: 'Visible', avatar_url: null,
|
|
365
368
|
project_count: 2, total_credits: 5, total_tasks_shipped: 1, total_karma: 0,
|
|
366
369
|
last_active: '2026-08-01T00:00:00.000Z', disciplines: ['engineer'] } },
|
|
367
|
-
{ profile_state: 'active', account_visibility: 'public', hide_stats: true,
|
|
370
|
+
{ profile_state: 'active', terms_accepted_at: ACCEPTED_AT, account_visibility: 'public', hide_stats: true,
|
|
368
371
|
row: { github_id: '12', github_login: HIDDEN_LOGIN, display_name: 'Hidden', avatar_url: null,
|
|
369
372
|
project_count: 7, total_credits: 88817, total_tasks_shipped: 6613, total_karma: 5519,
|
|
370
373
|
last_active: '2026-08-02T00:00:00.000Z', disciplines: ['ui'] } },
|
|
371
|
-
{ profile_state: 'provisional', account_visibility: 'public', hide_stats: false,
|
|
374
|
+
{ profile_state: 'provisional', terms_accepted_at: null, account_visibility: 'public', hide_stats: false,
|
|
372
375
|
row: { github_id: '13', github_login: PROV_LOGIN, display_name: 'Provisional', avatar_url: null,
|
|
373
376
|
project_count: 1, total_credits: 71713, total_tasks_shipped: 1, total_karma: 0,
|
|
374
377
|
last_active: '2026-08-03T00:00:00.000Z', disciplines: [] } },
|
|
375
|
-
{ profile_state: 'active', account_visibility: 'private', hide_stats: false,
|
|
378
|
+
{ profile_state: 'active', terms_accepted_at: ACCEPTED_AT, account_visibility: 'private', hide_stats: false,
|
|
376
379
|
row: { github_id: '14', github_login: PRIVATE_LOGIN, display_name: 'Private', avatar_url: null,
|
|
377
380
|
project_count: 3, total_credits: 63127, total_tasks_shipped: 4409, total_karma: 2287,
|
|
378
381
|
last_active: '2026-08-04T00:00:00.000Z', disciplines: ['engineer'] } },
|
|
@@ -389,6 +392,7 @@ const engine = {
|
|
|
389
392
|
if (/AS project_count/.test(sql)) {
|
|
390
393
|
let rows = SCOUTING_TABLE;
|
|
391
394
|
if (/pa\.profile_state = 'active'/.test(sql)) rows = rows.filter((r) => r.profile_state === 'active');
|
|
395
|
+
if (/pa\.terms_accepted_at IS NOT NULL/.test(sql)) rows = rows.filter((r) => r.terms_accepted_at != null);
|
|
392
396
|
if (/pa\.account_visibility = 'public'/.test(sql)) rows = rows.filter((r) => r.account_visibility === 'public');
|
|
393
397
|
if (/NOT pa\.hide_stats/.test(sql)) rows = rows.filter((r) => !r.hide_stats);
|
|
394
398
|
return { rows: rows.map((r) => ({ ...r.row })) };
|
|
@@ -398,10 +402,13 @@ const engine = {
|
|
|
398
402
|
hubAccount.hide_stats = !!params[4];
|
|
399
403
|
return { rows: [{ github_id: String(HUB_GITHUB_ID), github_login: 'jaxri', ...hubAccount }] };
|
|
400
404
|
}
|
|
401
|
-
// getPublicProfileExtras' consent read
|
|
402
|
-
//
|
|
403
|
-
|
|
405
|
+
// getPublicProfileExtras' consent read. Matched on the columns it must
|
|
406
|
+
// carry rather than on the exact SELECT list: pinning the full list meant
|
|
407
|
+
// that adding terms_accepted_at (ADR 0213) silently unhooked this branch and
|
|
408
|
+
// the read fell through to the wrong answer with every other test green.
|
|
409
|
+
if (/SELECT profile_state,.*hide_stats/.test(sql) && /FROM platform_identity_accounts/.test(sql)) {
|
|
404
410
|
return { rows: [{ profile_state: hubAccount.profile_state,
|
|
411
|
+
terms_accepted_at: hubAccount.terms_accepted_at,
|
|
405
412
|
account_visibility: hubAccount.account_visibility, hide_stats: hubAccount.hide_stats }] };
|
|
406
413
|
}
|
|
407
414
|
// getRollupForBuilder's per-project rows (the client_id → origin two-hop).
|