@bongos/core 1.19.428 → 1.19.429
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 +46 -26
- package/clients/bongos-client/index.d.ts +1 -1
- package/docs/adr/0214-canonical-profile-url-one-resolver-dark-until-the-page-exists.md +90 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +3 -2
- package/docs/copy-inventory.md +24 -23
- package/docs/copy-registry.json +37 -28
- package/docs/file-map.md +2 -1
- package/docs/module-api-changelog.md +2 -0
- package/modules/hall-ui/public/profile-canonical.js +137 -0
- package/modules/hall-ui/public/profile-connections.js +20 -9
- package/modules/hall-ui/public/profile.html +9 -0
- package/modules/hall-ui/public/profile.js +5 -0
- package/modules/platform-identity/canonical-profile.js +143 -0
- package/modules/platform-identity/routes/sso.js +7 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/bongos/routes/builders.js +24 -2
- package/src/bongos/serve-internal.js +28 -2
- package/src/module-api.js +1 -1
- package/tests/canonical_profile_url.mjs +628 -0
- package/tests/profile_connections_ui.mjs +38 -0
- package/tests/profile_route.mjs +30 -5
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.
|
|
6
|
-
"core_contract": "1.19.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.19.429",
|
|
6
|
+
"core_contract": "1.19.429",
|
|
7
|
+
"source_commit": "c9d9b31a6f80bb459bf490c45544f2edd344e056",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-08-29T20:
|
|
9
|
+
"built_at": "2026-08-29T20:59:30.558Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 364,
|
|
13
13
|
"agent_docs_stubbed": 23,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 1737,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 2124,
|
|
20
|
+
"tree_sha256": "e575e80b0436081de41f6f6833a4a4f94d9420d69cb3357a68a33f4330902fa1",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/blocker-review/SKILL.md",
|
|
@@ -372,7 +372,7 @@
|
|
|
372
372
|
{
|
|
373
373
|
"path": "clients/bongos-client/index.d.ts",
|
|
374
374
|
"mode": "0000644",
|
|
375
|
-
"sha256": "
|
|
375
|
+
"sha256": "0bd5a2773bde6bc126a6351d20f5ce30049d2a8bc9a8ba2154f74af9cb1401b2"
|
|
376
376
|
},
|
|
377
377
|
{
|
|
378
378
|
"path": "clients/bongos-client/index.mjs",
|
|
@@ -1564,10 +1564,15 @@
|
|
|
1564
1564
|
"mode": "0000644",
|
|
1565
1565
|
"sha256": "c3aba5879f639ea409d80bc8545085fc1892fe191868ac69c9be52507750f33a"
|
|
1566
1566
|
},
|
|
1567
|
+
{
|
|
1568
|
+
"path": "docs/adr/0214-canonical-profile-url-one-resolver-dark-until-the-page-exists.md",
|
|
1569
|
+
"mode": "0000644",
|
|
1570
|
+
"sha256": "b964e1674d615cb4525e94bb4cac63d4ac0813e3c1ea9236620bf030d7c53124"
|
|
1571
|
+
},
|
|
1567
1572
|
{
|
|
1568
1573
|
"path": "docs/adr/README.md",
|
|
1569
1574
|
"mode": "0000644",
|
|
1570
|
-
"sha256": "
|
|
1575
|
+
"sha256": "9b2d411decbc309773631064ed286ee0d134860ec688204ac45b827a28748d7a"
|
|
1571
1576
|
},
|
|
1572
1577
|
{
|
|
1573
1578
|
"path": "docs/api-reference.md",
|
|
@@ -1577,7 +1582,7 @@
|
|
|
1577
1582
|
{
|
|
1578
1583
|
"path": "docs/api/openapi.json",
|
|
1579
1584
|
"mode": "0000644",
|
|
1580
|
-
"sha256": "
|
|
1585
|
+
"sha256": "6b3d91999080e09913f97a766b097e26ff9ae941d4026ab7b025c6dc4a7f9996"
|
|
1581
1586
|
},
|
|
1582
1587
|
{
|
|
1583
1588
|
"path": "docs/architecture.md",
|
|
@@ -1602,12 +1607,12 @@
|
|
|
1602
1607
|
{
|
|
1603
1608
|
"path": "docs/copy-inventory.md",
|
|
1604
1609
|
"mode": "0000644",
|
|
1605
|
-
"sha256": "
|
|
1610
|
+
"sha256": "6192d6ab0115cd57a4dce5a61da2de1084ba6ccfa860356b8d54045ebd3609aa"
|
|
1606
1611
|
},
|
|
1607
1612
|
{
|
|
1608
1613
|
"path": "docs/copy-registry.json",
|
|
1609
1614
|
"mode": "0000644",
|
|
1610
|
-
"sha256": "
|
|
1615
|
+
"sha256": "9f52cecfbab533846f8e182676801940c94141c2fb6096b745e73aa0423688a9"
|
|
1611
1616
|
},
|
|
1612
1617
|
{
|
|
1613
1618
|
"path": "docs/design/apex-pass-2-direction.md",
|
|
@@ -1952,7 +1957,7 @@
|
|
|
1952
1957
|
{
|
|
1953
1958
|
"path": "docs/file-map.md",
|
|
1954
1959
|
"mode": "0000644",
|
|
1955
|
-
"sha256": "
|
|
1960
|
+
"sha256": "23121260ec5061645027d50609712217a619ea9ae8ad539cdeeb18e1cd552755"
|
|
1956
1961
|
},
|
|
1957
1962
|
{
|
|
1958
1963
|
"path": "docs/handoff-template.md",
|
|
@@ -1962,7 +1967,7 @@
|
|
|
1962
1967
|
{
|
|
1963
1968
|
"path": "docs/module-api-changelog.md",
|
|
1964
1969
|
"mode": "0000644",
|
|
1965
|
-
"sha256": "
|
|
1970
|
+
"sha256": "b827125881354448b4c3e19a16a6b0a27210386869d33a09fa0d488812745b6a"
|
|
1966
1971
|
},
|
|
1967
1972
|
{
|
|
1968
1973
|
"path": "docs/modules-contract.md",
|
|
@@ -4124,10 +4129,15 @@
|
|
|
4124
4129
|
"mode": "0000644",
|
|
4125
4130
|
"sha256": "40f6f44283aa7dba25a2c983d77b6e6f53f40eefef3394122d34f7f42fc5f676"
|
|
4126
4131
|
},
|
|
4132
|
+
{
|
|
4133
|
+
"path": "modules/hall-ui/public/profile-canonical.js",
|
|
4134
|
+
"mode": "0000644",
|
|
4135
|
+
"sha256": "4f010fd2a4e77447839eb77f5e294ea62c525fc47f392e916790f37078a92726"
|
|
4136
|
+
},
|
|
4127
4137
|
{
|
|
4128
4138
|
"path": "modules/hall-ui/public/profile-connections.js",
|
|
4129
4139
|
"mode": "0000644",
|
|
4130
|
-
"sha256": "
|
|
4140
|
+
"sha256": "1f52e92aea4583273d719538c453d4d11313f15d583536316c75ef4d625a8265"
|
|
4131
4141
|
},
|
|
4132
4142
|
{
|
|
4133
4143
|
"path": "modules/hall-ui/public/profile-nudge.js",
|
|
@@ -4137,12 +4147,12 @@
|
|
|
4137
4147
|
{
|
|
4138
4148
|
"path": "modules/hall-ui/public/profile.html",
|
|
4139
4149
|
"mode": "0000644",
|
|
4140
|
-
"sha256": "
|
|
4150
|
+
"sha256": "236e3da37f6e8b57525a7d0bc9cc07cea25db4a983770ed965409df8b9d5f655"
|
|
4141
4151
|
},
|
|
4142
4152
|
{
|
|
4143
4153
|
"path": "modules/hall-ui/public/profile.js",
|
|
4144
4154
|
"mode": "0000644",
|
|
4145
|
-
"sha256": "
|
|
4155
|
+
"sha256": "6433253093196f674142979c881ea8dc735d17841aeb99ddbc0e69b621b4b7e0"
|
|
4146
4156
|
},
|
|
4147
4157
|
{
|
|
4148
4158
|
"path": "modules/hall-ui/public/ranks.html",
|
|
@@ -4824,6 +4834,11 @@
|
|
|
4824
4834
|
"mode": "0000644",
|
|
4825
4835
|
"sha256": "3793f588d62eaa96a74da6bceb55043e8a9de35bc120dbe63f26bcc2b7e904da"
|
|
4826
4836
|
},
|
|
4837
|
+
{
|
|
4838
|
+
"path": "modules/platform-identity/canonical-profile.js",
|
|
4839
|
+
"mode": "0000644",
|
|
4840
|
+
"sha256": "2aa433ba293545a313972f88b1143d74551cb6ec4e06747906ff7e3c365f4e6a"
|
|
4841
|
+
},
|
|
4827
4842
|
{
|
|
4828
4843
|
"path": "modules/platform-identity/connections.js",
|
|
4829
4844
|
"mode": "0000644",
|
|
@@ -4987,7 +5002,7 @@
|
|
|
4987
5002
|
{
|
|
4988
5003
|
"path": "modules/platform-identity/routes/sso.js",
|
|
4989
5004
|
"mode": "0000644",
|
|
4990
|
-
"sha256": "
|
|
5005
|
+
"sha256": "dc3eaa8789a28a389ddeaac88f5cd514db8bf10705f430839f0bf164fc963674"
|
|
4991
5006
|
},
|
|
4992
5007
|
{
|
|
4993
5008
|
"path": "modules/platform-identity/scouting-authz.js",
|
|
@@ -5557,12 +5572,12 @@
|
|
|
5557
5572
|
{
|
|
5558
5573
|
"path": "package-lock.json",
|
|
5559
5574
|
"mode": "0000644",
|
|
5560
|
-
"sha256": "
|
|
5575
|
+
"sha256": "f2b5dbf384c0e497631f003718439033bab6eb4b1f3ded6d29b187a0d487418d"
|
|
5561
5576
|
},
|
|
5562
5577
|
{
|
|
5563
5578
|
"path": "package.json",
|
|
5564
5579
|
"mode": "0000644",
|
|
5565
|
-
"sha256": "
|
|
5580
|
+
"sha256": "42c9b8ace9c23611c7449eb129580b6c9b40eedb9e0ed9ac0c0a10fdf6c85408"
|
|
5566
5581
|
},
|
|
5567
5582
|
{
|
|
5568
5583
|
"path": "public-docs/index.html",
|
|
@@ -7587,7 +7602,7 @@
|
|
|
7587
7602
|
{
|
|
7588
7603
|
"path": "src/bongos/routes/builders.js",
|
|
7589
7604
|
"mode": "0000644",
|
|
7590
|
-
"sha256": "
|
|
7605
|
+
"sha256": "dc71324e4a1357f86b7e5a66c64ca5b38a2c79ef58c6d7a6f7337a23cfa1ee60"
|
|
7591
7606
|
},
|
|
7592
7607
|
{
|
|
7593
7608
|
"path": "src/bongos/routes/github.js",
|
|
@@ -7657,7 +7672,7 @@
|
|
|
7657
7672
|
{
|
|
7658
7673
|
"path": "src/bongos/serve-internal.js",
|
|
7659
7674
|
"mode": "0000644",
|
|
7660
|
-
"sha256": "
|
|
7675
|
+
"sha256": "a5036b24dbcd9d3f78de44781e60accb07ef62e458004d7e195af1c60f7e89f3"
|
|
7661
7676
|
},
|
|
7662
7677
|
{
|
|
7663
7678
|
"path": "src/bongos/stealth-gate.js",
|
|
@@ -7692,7 +7707,7 @@
|
|
|
7692
7707
|
{
|
|
7693
7708
|
"path": "src/module-api.js",
|
|
7694
7709
|
"mode": "0000644",
|
|
7695
|
-
"sha256": "
|
|
7710
|
+
"sha256": "9a399f369f1c857fda182086231cee242e797eb7ac8fc24fdacdc9bdf706943e"
|
|
7696
7711
|
},
|
|
7697
7712
|
{
|
|
7698
7713
|
"path": "src/module-loader/catalog.js",
|
|
@@ -8134,6 +8149,11 @@
|
|
|
8134
8149
|
"mode": "0000644",
|
|
8135
8150
|
"sha256": "0d7bfd5a02e0beefe5f4a4b60daa4d3dee57f8e893febfd366732b419a75678f"
|
|
8136
8151
|
},
|
|
8152
|
+
{
|
|
8153
|
+
"path": "tests/canonical_profile_url.mjs",
|
|
8154
|
+
"mode": "0000644",
|
|
8155
|
+
"sha256": "6f9d5c57d61aa3e1d9c6e8d7be7568d8a2a0d1d6832b8880ce3204a8b67f3a6c"
|
|
8156
|
+
},
|
|
8137
8157
|
{
|
|
8138
8158
|
"path": "tests/cap_newcomer_tasks.mjs",
|
|
8139
8159
|
"mode": "0000644",
|
|
@@ -9717,7 +9737,7 @@
|
|
|
9717
9737
|
{
|
|
9718
9738
|
"path": "tests/profile_connections_ui.mjs",
|
|
9719
9739
|
"mode": "0000644",
|
|
9720
|
-
"sha256": "
|
|
9740
|
+
"sha256": "1e6923437bcafbbb396d43202a4fe5c9d49dd9baef059d84aa2520bd64a195b6"
|
|
9721
9741
|
},
|
|
9722
9742
|
{
|
|
9723
9743
|
"path": "tests/profile_fields.mjs",
|
|
@@ -9742,7 +9762,7 @@
|
|
|
9742
9762
|
{
|
|
9743
9763
|
"path": "tests/profile_route.mjs",
|
|
9744
9764
|
"mode": "0000644",
|
|
9745
|
-
"sha256": "
|
|
9765
|
+
"sha256": "0189425a0430dbab84044bdca2ac99e4dde87db061b8226412aa2fc393bb3119"
|
|
9746
9766
|
},
|
|
9747
9767
|
{
|
|
9748
9768
|
"path": "tests/profile_ui_cross_project.mjs",
|
|
@@ -34,7 +34,7 @@ export interface GetBuildersDirectoryResponse { builders: unknown }
|
|
|
34
34
|
export interface GetBuildersIdActiveClaimsResponse { builder_id: unknown; claims: unknown; stale_hours: unknown }
|
|
35
35
|
export interface GetBuildersIdActivityResponse { builder_id: unknown; section: unknown; role: unknown; rows: unknown; total: unknown }
|
|
36
36
|
export interface GetBuildersIdOnboardingResponse { builder_id: unknown; onboarding: unknown }
|
|
37
|
-
export interface GetBuildersIdProfileResponse { builder: unknown; shipped_count: unknown; recent_ships: unknown; achievements: unknown; cross_project?: unknown }
|
|
37
|
+
export interface GetBuildersIdProfileResponse { builder: unknown; shipped_count: unknown; recent_ships: unknown; achievements: unknown; cross_project?: unknown; handle?: unknown }
|
|
38
38
|
export interface GetBuildersMePendingRankChangeResponse { change: unknown; greeting?: unknown }
|
|
39
39
|
export interface GetBuildersRosterResponse { builders: unknown; stale_hours: unknown }
|
|
40
40
|
export interface GetClosenessForMeResponse { closeness: unknown; count: unknown }
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# ADR 0214 — The canonical profile URL gets one resolver, and it stays dark until the page it names actually resolves
|
|
2
|
+
|
|
3
|
+
- **Status:** accepted
|
|
4
|
+
- **Date:** 2026-08-29
|
|
5
|
+
- **Task:** [task 1002804](https://cloudbongos.com/builders#/task/1002804) (P08, goal 1000045 — Public builder profiles, criterion C1)
|
|
6
|
+
- **Relates to:** ADR 0171 D1/D2/D8 (link out instead of inlining; `hide_stats` suppresses numbers, never existence; `github_id` is the join key and the handle is presentation), ADR 0202 D4 / ADR 0210 (one shared visibility predicate, never a second variant), ADR 0141 (identity federated, authority local), ADR 0083 (module boundary), ADR 0016 (identity is never authority), `docs/specs/<redacted>.md` Decision 1 + Decision 3, `docs/specs/<redacted>.md` D2 (404, never 403)
|
|
7
|
+
|
|
8
|
+
## Context
|
|
9
|
+
|
|
10
|
+
Profile-page spec Decision 1 names one canonical public profile with two render paths: `cloudbongos.com/u/<handle>` is canonical and server-rendered on the hub; `builders.<instance>/profile` stays the LOCAL view and emits `rel=canonical` to it; and the numeric `?id=` form "never dies" as a rename-proof fallback that redirects to the canonical handle URL. ADR 0171 D1 amended that: federated non-hub instances **link out** rather than inlining cross-project numbers, and named this task the owner of the federated render path — the visible link, the `rel=canonical` emission, and the numeric-id redirect.
|
|
11
|
+
|
|
12
|
+
Four facts about the tree, established before any of this was designed:
|
|
13
|
+
|
|
14
|
+
1. **`/u/<handle>` does not exist.** No route, no page, no hub search, no sitemap (proved by task 1002806, confirmed by task 1002296). The page is goal 1000048 R14 / [task 1002365](https://cloudbongos.com/builders#/task/1002365), which sits at `backlog`. So does R13 (cross-instance canonical), R12 (robots.txt) and R15 (sitemap). R01 — the ADR that *decides the SEO namespace* — is only `ready`, and R03 in the same goal proposes moving platform pages under `/bongos/…`. **The path string `/u/<handle>` is not merely unbuilt; its shape is not yet settled.**
|
|
15
|
+
2. **The READ behind that page already exists.** `GET /profiles/:handle` (`modules/platform-identity/routes/public-profile.js`, R05) resolves current-or-previous handle through `resolveHandle`, which floors on the shared `accountVisibleSql` predicate and answers 404 for unknown / provisional / private alike — never 403.
|
|
16
|
+
3. **`GET /builders/:id/profile` whitelists nine core columns and `handle` is not one of them.** Task 1002296 hit this and routed around it: `profile-connections.js` hands the connections API a `github_login` and uses the handle the response echoes back. That workaround has a hole its own header names — the rename window is a **single** `previous_handle` slot, so a builder who has renamed **twice** has a login that matches neither `handle` nor `previous_handle`, the degree call 404s, and the connections section silently never appears.
|
|
17
|
+
4. **No served page carries a hub origin.** `clientBranding()` deliberately omits `domains`; `applyBrandTokens()` stamps `{{publicOrigin}}` and `{{buildersOrigin}}` (this instance's own addresses) and nothing else. The only hub origin anywhere at runtime is `loadIdpConfig().origin` in `src/bongos/auth-config.js` — server-side auth wiring, never rendered. ADR 0171 D1 flagged exactly this gap.
|
|
18
|
+
|
|
19
|
+
## Decision
|
|
20
|
+
|
|
21
|
+
### D1 — One resolver owns the canonical URL, and it answers `null` while the page does not resolve
|
|
22
|
+
|
|
23
|
+
`modules/platform-identity/canonical-profile.js` is THE place that answers "what is this builder's canonical profile URL?". Everything that needs the answer — the `rel=canonical` link, the visible link-out, the numeric-id redirect — asks it and renders nothing when it answers `null`. One resolver, three consumers, so the three can never disagree about where a profile lives or about whether it is safe to point there.
|
|
24
|
+
|
|
25
|
+
`CANONICAL_PROFILE_PAGE_LIVE` is `false`. **Today the resolver answers `null` everywhere and all three surfaces are dark.**
|
|
26
|
+
|
|
27
|
+
**Why dark, and not "canonical ahead of the page".** Emitting a canonical at a URL that is planned but unbuilt is a normal SEO staging move when the target is imminent and its address is fixed. Neither holds here:
|
|
28
|
+
|
|
29
|
+
- **The target 404s.** A `rel=canonical` pointing at a 404 does not consolidate anything — a 404 cannot be indexed, so there is no signal to consolidate onto. What it *can* do is cost: the declaring page is the only one that works, and a canonical that names a non-indexable URL is a documented way to have a working page reported as a duplicate of nothing and dropped. We would be spending the one page that ranks to point at a page that does not exist.
|
|
30
|
+
- **The address is not settled.** R01 has not been written and R03 proposes relocating platform pages under `/bongos/…`. Baking `/u/<handle>` into every federated instance's served HTML now means every one of them advertises a path that a sibling task may still move — and a canonical is precisely the kind of claim that is expensive to retract, because crawlers cache it and users bookmark what it redirects them to.
|
|
31
|
+
- **There is no upside to being early.** Canonical consolidation begins when the target becomes indexable, not before. Emitting now buys nothing and risks the working page.
|
|
32
|
+
|
|
33
|
+
So the honest answer is: build the machinery, prove it, and **do not point at a 404**. The flip is one boolean in one file, and R14 cannot land the page without noticing it — `tests/canonical_profile_url.mjs` fails if a `/u/` page route appears anywhere in the tree while the flag is still `false`. The flag cannot silently lie in either direction.
|
|
34
|
+
|
|
35
|
+
**What must be true to flip it** (the checklist R13/R14 inherit, stated once so nobody has to re-derive it): the page is served at the path this file names; R01 has ratified that path; the page 404s (never 403) for absent / provisional / private, matching `GET /profiles/:handle`; and `noindex`-until-first-ship (P09) is in place so flipping the flag does not publish a farm of empty profiles.
|
|
36
|
+
|
|
37
|
+
### D2 — The hub origin becomes a server-side stamp, delivered as a `<meta>`, not a `<script>`
|
|
38
|
+
|
|
39
|
+
`applyBrandTokens()` gains `{{hubOrigin}}`. It resolves to the instance's **own** `publicOrigin` when `platform-identity` is enabled (that module *is* the hub flag — `src/platform-server.js`), to `loadIdpConfig().origin` on a federated instance, and to `''` on a vanilla one. This is the "server-side hub-origin stamp token" ADR 0171 D1 asked for, and it is the only way a served page can carry a correct cross-host canonical: `clientBranding()` cannot reach `domains`, and a baked value would make every instance advertise whoever authored the file (the fitness identity gate, task 1002748).
|
|
40
|
+
|
|
41
|
+
It is stamped as `<meta name="hub-origin" content="{{hubOrigin}}">`, following the `{{instanceBase}}` precedent — a meta attribute, never a script global. **The token is deliberately not written directly into a `href`**: `dropEmptyHrefLinks` only drops an href that is a *bare* origin token, so `href="{{hubOrigin}}/u/x"` on an instance with no hub would silently become the same-host relative URL `/u/x` — a canonical pointing at the instance itself. Reading the meta and refusing an empty value makes that failure impossible rather than merely unlikely.
|
|
42
|
+
|
|
43
|
+
### D3 — `handle` joins the numeric profile response under the EXISTENCE predicate, not the rollup one
|
|
44
|
+
|
|
45
|
+
`GET /builders/:id/profile` gains a top-level `handle`, attached only where the `platform-identity.hub` port resolves, sourced from a new port function `getPublicHandle(githubId)` that floors on `isAccountVisible` — the shared predicate, never a variant.
|
|
46
|
+
|
|
47
|
+
**Why the existence predicate and not the rollup one.** `account-visibility.js` is explicit that `hide_stats` suppresses NUMBERS and never existence, and `resolveHandle` already says the same out loud: hiding removes the rollup, not the page. A builder who hides their stats still has a public profile at `/u/<handle>` — it just carries no numbers. Gating the handle on `rollupVisibleSql` would deny a canonical URL and a connect button to exactly the people who chose a public profile without public numbers.
|
|
48
|
+
|
|
49
|
+
**This does not create a new inference.** It is true that `handle` present together with `cross_project: null` narrows the four indistinguishable rollup-null cases to "hidden". That pair is **already published, unauthenticated, on `/profiles/:handle`**, which answers `200 { account: { handle }, cross_project: null }` for exactly that account. The change re-presents on the numeric route a pair the handle route already serves; it does not widen what the platform discloses. The three cases ADR 0171 D2 requires to stay merged — no account, provisional, private — all still produce `handle` absent **and** `cross_project` null, byte-identical.
|
|
50
|
+
|
|
51
|
+
**What this closes today, with the flag still off:** fact 3 above. `profile-connections.js` can take the canonical handle straight from the profile response instead of guessing with a `github_login`, so a twice-renamed builder gets their connections section back.
|
|
52
|
+
|
|
53
|
+
### D4 — The numeric `?id=` form redirects on the hub and canonicalizes on a federated instance
|
|
54
|
+
|
|
55
|
+
The spec's two clauses — "the local view stays as-is" and "the numeric form redirects to the canonical URL" — read as a contradiction only until you ask *which instance*:
|
|
56
|
+
|
|
57
|
+
- **On the hub**, `builders.<apex>/profile?id=N` and `<apex>/u/<handle>` describe the same person from the same database. Two URLs for one page on one host is precisely the duplication Decision 1 exists to end, so the numeric form **redirects**.
|
|
58
|
+
- **On a federated instance**, `builders.foo.com/profile?id=N` shows *foo's* local rank, works and achievements. That is genuinely different content, so it **must not** redirect. It stays the local view, emits `rel=canonical`, and shows the visible link-out — ADR 0171 D1's federated render path exactly.
|
|
59
|
+
- **On a vanilla instance** nothing changes at all.
|
|
60
|
+
|
|
61
|
+
A page cannot both redirect to a URL and declare it canonical, and it does not have to: the two behaviours belong to different instances.
|
|
62
|
+
|
|
63
|
+
The hub redirect is a client-side `location.replace` (it replaces rather than pushes, so Back still leaves the site cleanly) issued after the profile read resolves the handle. It is not a true 301 and does not claim to be: the hall page is served from a boot-time in-memory HTML map with no per-request database access, and giving that path a DB read to serve a redirect is a real cost for a page that is not indexed today (there is no robots.txt yet — R12). **When R14 lands the server-rendered page it should serve the numeric form as a real 301 in its own namespace, using this same resolver**; that is written here so the follow-up is a decision already made rather than one rediscovered.
|
|
64
|
+
|
|
65
|
+
### D5 — The redirect is not an existence oracle
|
|
66
|
+
|
|
67
|
+
The hub redirect fires iff the profile response carries a `handle`, which it does iff `isAccountVisible` — the same predicate `/profiles/:handle` already floors on. Three properties make the fallback silent:
|
|
68
|
+
|
|
69
|
+
1. **The non-redirect branch is a working page, not an error.** No account, provisional, and private all render the ordinary local profile — the same markup, the same status, the same everything. There is no error shape to read a signal off.
|
|
70
|
+
2. **The three "no" cases are indistinguishable from each other.** `getPublicHandle` returns `null` for all three; the response omits the key identically. The redirect can only ever separate "publicly visible account" from "not that", never one *reason* from another.
|
|
71
|
+
3. **The one bit it does expose is already public.** "This builder has a publicly visible platform account" is exactly what an unauthenticated `GET /profiles/<handle>` answers with 200-vs-404 today. The redirect derives its target from a handle the response already carried; it adds no bit the caller could not already read.
|
|
72
|
+
|
|
73
|
+
And the redirect target is a handle, never a `github_id` — the join key stays internal, as `<redacted>` already insists.
|
|
74
|
+
|
|
75
|
+
### D6 — Publishing the handle map puts the numeric profile read under the EXISTING public read ceiling
|
|
76
|
+
|
|
77
|
+
`GET /builders/:id/profile` answered at line rate. That is structural rather than an oversight: `rateLimitMiddleware` returns early for every non-write method, and the one read limiter is mounted under `/public/*`, which this route is not (ADR 0209's finding, one route over). Tolerable while the route re-presented leaderboard data everyone can already read; not once it also carries `handle`, because an id walk then yields the **builder → canonical-profile-handle map**, one call per builder, for every publicly visible account. `modules/platform-identity/routes/public-profile.js` caps `/profiles/:handle` for exactly that mapping, with exactly that reasoning. Two routes publishing one map get one ceiling.
|
|
78
|
+
|
|
79
|
+
So the route mounts `publicReadRateLimitMiddleware` — **the shared instance** (120/60s per IP, the `/public/*` posture), not a new budget and not a private copy. ADR 0209's load-bearing point applies verbatim: two limiters configured identically are indistinguishable in a diff and hand an enumerator twice the budget, so the test asserts the shared instance **by object identity**.
|
|
80
|
+
|
|
81
|
+
**This required strengthening a guard, not weakening one.** `tests/profile_route.mjs` (R06.1) pinned the route as public by requiring the registration line to read `router.get(path, async (req, res) =>` — the handler and nothing else. Its own comment says what it is for: *"a future PR walling this behind `requireBuilder` would break 'another account can see it'"*. That intent is **auth**, and throttling is not authentication — `/public/*` is rate-limited and public. But a line-regex cannot tell the two apart, and it silently missed the more likely bypass anyway: a gate mounted with `router.use`. The guard now reads the **real router stack**, rejects any function exported by `../auth` on that route, rejects any router-level middleware at all, and allows exactly one named handler — the shared limiter — with its reason. Six mutations were run against it; the strengthened form catches all of them, including the two `router.use` forms the old regex let through.
|
|
82
|
+
|
|
83
|
+
## Consequences
|
|
84
|
+
|
|
85
|
+
- **Three surfaces ship dark and one ships live.** The canonical link, the link-out and the numeric redirect are all gated on one boolean that is `false`; the widened `handle` on the profile read is live immediately and closes the twice-renamed connect-button hole. A reviewer should expect exactly that asymmetry.
|
|
86
|
+
- **R13/R14 inherit a one-line flip and a checklist.** `CANONICAL_PROFILE_PAGE_LIVE` in `modules/platform-identity/canonical-profile.js`, with D1's four preconditions in its doc-comment and a test that fails if a `/u/` page route lands while it is still `false`.
|
|
87
|
+
- **A cross-version window exists and is bounded.** A federated instance running a core release where the flag is `true` while its hub is pinned to an older release would link out to a 404. The blast radius is a link and a canonical, not a broken page, and the flag ships in the same release as the page it names.
|
|
88
|
+
- **The link-out discloses that a builder has a public platform account** — the same bit D5 analyses, now visible on a federated instance's page rather than only via the handle route. Deliberate: that is what "link out to the canonical record" means.
|
|
89
|
+
- **`applyBrandTokens` grows a seventh token.** Its signature is now at seven injectable parameters; a further one should probably move the origins into an object rather than extend the list again.
|
|
90
|
+
- **Not decided here:** the sitemap and `noindex` rules (P09 / R15), the served page itself and its caching posture (R14), whether the numeric permalink on the hub should key on the core builder id or a platform-side identifier once the page exists (R14's call, with D5's rule binding either way), and whether federated instances ever inline cross-project numbers — ADR 0171 D1 names the pull-with-cache follow-up and it stays a separate task.
|
package/docs/adr/README.md
CHANGED
|
@@ -298,6 +298,7 @@ This keeps the decision history honest and traceable.
|
|
|
298
298
|
| 0209 | [**Two routes answering one question spend one budget; whether the answer itself collapses is the owner's call** ([task 1003339](https://cloudbongos.com/builders#/task/1003339), goal 1000045 — criterion `i-control-my-own-access-and-exposure`). `GET /access-requests/status` (public, returns `member|invited|pending|dismissed|none`) and `GET /auth/web/admission-status` (its bare-boolean twin) both answer "does this GitHub login have an account here?", and neither was throttled — `rateLimitMiddleware` returns early for every non-write method, and the one read limiter is mounted only under `/public/*`. Scripted at line rate either becomes a login→membership roster: who builds here, and confirmation that a named person is present. Decision: ship the ceiling as ONE budget — `accountExistenceReadRateLimit`, 120 reads/60s per IP over a single bucket, exported as one middleware INSTANCE rather than a factory and mounted on both routes, because two limiters each configured 120/60s look identical in a diff, pass any test that counts to the limit per route, and hand an enumerator 240/min by alternating. The onboarding module reaches it through the doorway, so `src/bongos/middleware/rate-limit.js` joins `KERNEL_FILES` on `logger.js`'s argument: authz decides WHO may ask, the ceiling decides HOW OFTEN, and the file imports nothing. Deliberately NOT decided here: collapsing the response so a non-subject caller cannot tell `pending`/`dismissed`/`none` from "not admitted" — the consumer sweep found route 1 has exactly ONE consumer (`scripts/gds/login.js`), which already treats `pending` and `none` identically, so collapsing costs exactly one thing: the only message in the codebase that tells a declined applicant they were declined. That is a judgement about what an applicant is owed, so it goes to the owner as a blocker with the evidence. Noted either way: collapsing does not close the oracle — `admitted:true` still means member-or-invited. Rejected: a separate limiter per route; a `createReadRateLimit` factory (two matching call sites get two buckets — the generality loses the property); vendoring a fourth copy of the sliding window; hardening only the smaller route; collapsing on my own judgement because "nothing much" reads it.](<redacted>.md) | security / rate limiting |
|
|
299
299
|
| 0207 | [**Building the connection graph: three departures from ADR 0202, and the three limits it is honest about** ([task 1002295](https://cloudbongos.com/builders#/task/1002295), goal 1000045 criterion C6 — the build of the connections wave). ADR 0202 decided the graph; building it surfaced three places where the decision's MECHANISM could not be written literally without losing the PROPERTY it was about, and three places where the shipped code cannot promise as much as a casual reading suggests. Departures: (1) **level-wise CTEs, not `WITH RECURSIVE`** — a recursive term can carry neither a per-level fan-out cap nor an ordered per-level truncation, which are the two things D3 actually decided, so a recursive build would have had a depth bound and no fan-out bound; a test asserts `RECURSIVE` appears in neither query. (2) **1st degree is an unbounded pair-index probe**, never a scan of the capped frontier — reading it off the `FANOUT_1`-truncated 1-hop reports "2nd" for someone in the viewer's own connections list once they pass 500 connections, and the rule this states is that **a cap may only ever make an answer less specific (`null`), never wrong**. (3) The shared visibility predicate lands in its own file, `modules/platform-identity/account-visibility.js` (`isAccountVisible` / `accountVisibleSql` / `accountActiveSql`), because task 1002967 had not shipped and `platform-identity.js` sat at the size ratchet with zero headroom — **1002967 adopts it rather than writing a second predicate**. Also decided: node visibility during traversal is viewer-relative and asymmetric on purpose (your OWN 1-hop floors on consent alone so a friend who goes private does not vanish from your list; every deeper hop clears the public predicate); cancel is a `DELETE` an ADDRESSEE may not use, so the 90-day cooldown cannot be dodged by calling a decline a delete; and the create response is `201 { status: 'pending' }` and nothing else, so a swallowed re-ask has nothing to fabricate. Limits stated rather than implied: a swallowed re-ask is silent in the RESPONSE and observable by its ABSENCE from the requester's outbox (the claim is narrowed, not the code — a decoy row would be a lie in the database to protect a lie in the API); the TARGET's 1-hop is capped too and that truncation is deliberately NOT reported, because saying so leaks one bit about a third party's degree; and the query COUNT is bounded while the frontier's WORK is O(sum of degree over the 1-hop), since `ORDER BY … LIMIT` materialises before it truncates — the revisit trigger is a measured p95, and the cheap next move is a `statement_timeout` returning the `null` D3 already chose. Deferred and named: a per-builder cap on OUTBOUND requests (the cooldown stops repeat asks to one person, not one builder asking a hundred — a real number is owner-signed), and a real-Postgres pass on the expire-then-insert write path, which CI cannot run.](<redacted>.md) | platform identity / social graph / privacy |
|
|
300
300
|
| 0212 | [**The connections section is built on the profile page that exists, and it addresses a builder by asking the API to name them** ([task 1002296](https://cloudbongos.com/builders#/task/1002296), R20 · goal 1000045 criterion C6 — the UI half of the connections wave). The profile-page spec puts section 6 on the canonical `cloudbongos.com/u/<handle>`, which [task 1002806](https://cloudbongos.com/builders#/task/1002806) proved does not exist (no route, no page, no search entry, no sitemap row) and which belongs to goal 1000048 R14. Decisions: (1) **section 6 lands in the reserved slot R04 left on the hall profile page** (`modules/hall-ui/public/profile.{html,js}`), as a separate `profile-connections.js` so neither the page script nor `platform-identity.js` grows against its line ratchet, and it follows the CROSS-PROJECT section’s contract exactly — the markup ships `hidden` and reveals only where the hub answered, so an instance without `platform-identity` never renders it. It does not pre-empt the canonical page, which inherits the same three own-scope calls. (2) **Addressing: the page hands the API the `github_login` it holds and uses the `handle` the response echoes back for every write** — core `GET /builders/:id/profile` whitelists nine columns and `handle` is not one of them, and widening that whitelist is task 1002804’s canonical-URL work. `findAccountByHandle` already resolves current-or-previous handle and the route answers with the canonical one, which is the same move the API built for a stale Connect link. It fails closed into the deliberately indistinguishable 404 (unknown / provisional / private stranger), so the ONE honest gap is stated rather than discovered: **a twice-renamed handle has no connect button on this surface**, and it disappears when `/u/<handle>` lands. (3) Three rendering rules, enforced by `tests/profile_connections_ui.mjs` (real router over a WHERE-derived fake pool on a real socket, real client above it): `degree: null` renders "Out of network" and never "not connected", with copy that is BYTE-IDENTICAL whether `truncated` was set — varying it would rebuild the ADR 0207 L2 side channel in words; a sent request claims exactly `201 { status: 'pending' }`, so a re-ask swallowed by the 90-day cooldown simply shows "Connect" again next load with no explanation anywhere; and every count is the length of the list beside it, with `seen_at` and `truncated` never reaching the screen. Rejected: waiting for the hub page (it would block C6’s proof on another goal); building `/u/<handle>` here (the spec says only one such page gets built); adding `handle` to the core profile whitelist or to `cross_project` (the latter is `null` under `hide_stats`, which is exactly where the edge must still show — ADR 0202 D4 §3); resolving through public `GET /profiles/:handle` (it 404s for a private account the viewer IS connected to). Deferred and named: a public per-builder connections list (there is deliberately no such route), removal from the other person’s profile, and sections 7–8.](<redacted>.md) | platform identity / social graph / hall UI |
|
|
301
|
+
| 0214 | [**The canonical profile URL gets one resolver, and it stays dark until the page it names actually resolves** ([task 1002804](https://cloudbongos.com/builders#/task/1002804), P08 · goal 1000045 criterion C1 — the canonical/redirect wiring). Profile-page spec Decision 1 wants `rel=canonical` on the instance, a link-out to the hub, and a numeric-id redirect; `/u/<handle>` does not exist (goal 1000048 R14 is `backlog`, and R01 — the ADR that fixes its PATH — is unwritten, while R03 proposes moving platform pages under `/bongos/…`). Decisions: (1) **one resolver** (`modules/platform-identity/canonical-profile.js`) answers where a profile lives, and it answers `null` while the page 404s — a canonical naming a non-indexable URL consolidates nothing and risks the one page that works, and the address is not even settled yet; one boolean flips all three surfaces together, and a test reds if a `/u/` route lands while it is still false. (2) **`{{hubOrigin}}`** joins `applyBrandTokens` (own `publicOrigin` on the hub, `loadIdpConfig().origin` when federated, `''` otherwise) — the server-side stamp ADR 0171 D1 asked for — delivered as a `<meta>`, never inside an `href`, because an empty value in an href degrades to a same-host URL claiming to be the hub. (3) **`handle` joins `GET /builders/:id/profile`** under the EXISTENCE predicate (`isAccountVisible`), not the rollup one: `hide_stats` suppresses numbers and never addressability, and the `handle`+`cross_project: null` pair is already published unauthenticated by `GET /profiles/:handle`. This closes ADR 0212’s stated gap — a twice-renamed builder now has a connect button. (4) **The hub redirects and a federated instance canonicalizes**, which is how Decision 1’s two apparently contradictory clauses reconcile: the hub serves the same person at both URLs, a federated instance serves genuinely different content. (5) **The redirect is not an existence oracle** — its non-redirect branch is the ordinary working page, absent/provisional/private are byte-identical, and the one bit it exposes is what `/profiles/:handle` already answers with 200-vs-404.](<redacted>.md) | platform identity / SEO / hall UI |
|
|
301
302
|
| 0210 | [**The recruiting directory graduates on an instance-asserted ownership claim, and the recruiting opt-out gates the directory and nothing else** ([task 1002290](https://cloudbongos.com/builders#/task/1002290), R14, goal 1000045 criterion C5 — "project owners, not only Archons, browse builder profiles to recruit"). Two independent problems wore one task id. The reach flag `recruiter_discoverable` protected nobody — task 1002967 said so, and the Settings row stayed in the future tense because of it. And "widen the Archon-only /scouting" was already stale (ADR 0157 lowered `project.curate` to metic) while hiding the real question: **a project owner is not a rank**, and someone who registers a project is an Archon on their OWN instance and usually a bare `xenos` on the hub where the directory lives, so no floor reaches them without reaching every stranger. **D1** "project owner" on the hub is `<redacted>.membership_kind = 'owner'` — written by the PROJECT itself at `POST /sso/membership/check-in` from its own first-admin config, and refused unless the hub's own `signin_verified_at` witness stands (ADR 0205) — and never `platform_identity_projects.owner_github_id`, the hub-curated column ADR 0201 D3 names and forbids; the prohibition is about a hub-held list consulted before an ADMISSION on someone else's box, and this is the hub reading the instance's own mirrored claim before a read of the hub's OWN directory. **D2** the two doors are an OR in `modules/platform-identity/scouting-authz.js` (the `goal-authz.js` owner-or-rank shape, kept out of `platform-identity.js` at its size ratchet): ownership is asked first and a "no" falls through to the UNMODIFIED `requirePermission`, so a refusal keeps its exact code, `required`/`held` and floors map — the widening can only admit; the atom is spelled in the ROUTE's middleware list, because a gate hidden in the helper would make `route-rank-check.js` and `gen-api-docs.js` record this privileged route as `any-builder`; and a broken ownership read falls closed onto the rank door rather than admitting or 500ing. No atom is minted — `project.recruit` belongs to the console wave (spike 1002786 R3). **D3** `PAGE_GATES` gains an optional `alsoAdmits` naming a function on the `platform-identity.hub` port, declared on `requireProjectCuratePage` alone and consulted only after the permission check refused, so the `/people` shell admits exactly the two audiences its data route does; absent port, absent function or a throw all deny. **D4** the reach COALESCE gets its SQL twin in `account-visibility.js` (its sole owner) and composes into `scoutingListableSql` = rollup AND reach, which `listScoutingRollup` joins — but reach is deliberately NOT folded into `rollupVisibleSql`, because the community spec D3 says the opt-out governs the recruiting surface only and folding it down would silently empty a future public leaderboard. **D5** no migration: both columns exist and migration 003's `(github_id)` index already serves the probe. **D6** the ownership query JOINs `platform_identity_sso_clients` on `status='active'` — a membership row never expires and nothing ages it out, so suspending the client (the lever that already stops a project signing anyone in) had to revoke the read it derives, or suspending a rogue project would leave its owner reading every builder on the platform forever. The task's own adversarial review also found the sole-owner drift guard was matching only the UNALIASED spelling of the reach COALESCE — i.e. not the one that ships — and two fail-open defaults (a prototype-reachable `alsoAdmits`, and an alias guard resting on template evaluation order); all three fixed and mutation-checked. ⚠ Honest limits recorded, not fixed: the hall NAV still hides People from a project owner (task 1003349, sequenced with R15 which owns that surface), the `touchMembership` write path stays as ADR 0205 left it, and `openapi.json` cannot express "or owner". ⚠ And DELEGATES do not reach the directory yet (blocker 1000122 + task 1003350, linked) — an instance reports `owner` for its first admin only, so the registrant half works and the delegate half needs the instance to report `owner` for any archon, which changes a cross-version wire contract and a shipped bucketing; filed as blocker 1000122, and nothing here changes when it is answered. Rejected: lowering the floor further (a floor low enough to admit a registrant excludes nobody); a `project.owner` atom (ADR 0201 D3 rejected it); minting `project.recruit` here; gating on `owner_github_id`; folding reach into the rollup predicate; widening `membershipKind()` on our own judgement; shipping the route without the page.](<redacted>.md) | platform identity / authz + privacy |
|
|
302
303
|
| 0213 | [**The platform T&C acceptance is the hub's own witness, written once, and consent becomes two columns** ([task 1002913](https://cloudbongos.com/builders#/task/1002913), goal 1000045). ADR 0141 §4 deferred consent for cross-project aggregation to "the platform T&C" and ADR 0171 D3 wrote its own definition around the moment that acceptance "can later be captured" - but no acceptance record existed, and while `/terms` DOES exist (task 1003251) it says nothing about work crossing between projects, so the public-profile premise ("already carries platform T&C consent") was untrue for every account. The scouting.js:6 pointer the task named had also been overwritten by ADR 0210, so the debt was going invisible rather than getting paid. Decision: `terms_accepted_at` + `terms_version` on `platform_identity_accounts` (migration 019), written ONLY by `upsertAccount`'s `activate` branch - one caller, /sso/authorize, where `activate: true` is a source literal and every field comes from the hub's resolved session (the ADR 0205 D3 property; a record a caller could assert is evidence of nothing). COALESCE makes it write-once, so the column keeps meaning WHEN consent was given. The consent axis in `account-visibility.js` becomes `profile_state = 'active' AND terms_accepted_at IS NOT NULL`, composed into every other predicate, which is what makes the tie structural rather than a promise. NO backfill (D4): existing active accounts re-accept on their next hub sign-in - ADR 0171 D3's own rule and P03's ratified rationale - and that visible consequence, plus the wording itself, went to the owner on blocker 1000123. Rejected: an acceptances TABLE (no event stream yet), a click-through gate (an interstitial in a redirect built to be invisible; additive later), backfilling (records a consent that never happened), deriving profile_state from the timestamp, and putting the predicate in `terms-acceptance.js` (a second owner of an effective-visibility answer, which ADR 0202 D4 forbids).](<redacted>.md) | platform identity / consent / privacy |
|
|
303
304
|
|
package/docs/api/openapi.json
CHANGED
|
@@ -2847,7 +2847,7 @@
|
|
|
2847
2847
|
"builders"
|
|
2848
2848
|
],
|
|
2849
2849
|
"summary": "GET /builders/:id/profile",
|
|
2850
|
-
"description": "GET /builders/:id/profile — the viewable builder profile page's read (task 1467, goal 25 \"bx-identity\"). Returns ONE builder's public portfolio: identity + rank + economy counters + shipped-work count + recent ships + unlocked achievements. Backs the shareable /builders/profile page. rank: public — every field returned here is ALREADY world-readable: the leaderboard (GET /public/leaderboard, no auth) exposes rank, total_credits, karma, and the top achievement ids; GET /builders/:id/onboarding is likewise an unauthenticated roster-visible read. This route is a per-builder re- presentation of the SAME already-public data, so a portfolio link is shareable without a sign-in wall (that IS the feature). It deliberately WHITELISTS the public column subset off the full builder row — the private columns getBuilderById also returns (anthropic_email / anthropic_user_id / monthly_budget_usd / system_role / box_blocked*) are NEVER spread into the JSON. A leak-prevention unit test (tests/profile_route.mjs) asserts their absence + the 404-on-unknown-id behaviour.\n\n**Rank:** `public` — No authentication — any caller.",
|
|
2850
|
+
"description": "GET /builders/:id/profile — the viewable builder profile page's read (task 1467, goal 25 \"bx-identity\"). Returns ONE builder's public portfolio: identity + rank + economy counters + shipped-work count + recent ships + unlocked achievements. Backs the shareable /builders/profile page. rank: public — every field returned here is ALREADY world-readable: the leaderboard (GET /public/leaderboard, no auth) exposes rank, total_credits, karma, and the top achievement ids; GET /builders/:id/onboarding is likewise an unauthenticated roster-visible read. This route is a per-builder re- presentation of the SAME already-public data, so a portfolio link is shareable without a sign-in wall (that IS the feature). It deliberately WHITELISTS the public column subset off the full builder row — the private columns getBuilderById also returns (anthropic_email / anthropic_user_id / monthly_budget_usd / system_role / box_blocked*) are NEVER spread into the JSON. A leak-prevention unit test (tests/profile_route.mjs) asserts their absence + the 404-on-unknown-id behaviour. THROTTLED since task 1002804. The global limiter meters WRITES only (rateLimitMiddleware returns early for GET) and the public read limiter is mounted under /public/*, which this is not — so this route answered at line rate. That was tolerable while it re-presented leaderboard data; it is not now that it also carries `handle`, because an id walk then yields the builder → canonical-profile-handle map in one call per builder. modules/platform-identity/routes/public-profile.js caps /profiles/:handle for exactly that mapping, so the two routes that publish it get the same ceiling. The EXISTING shared /public/* budget (120/60s per IP), not a new one — ADR 0209's rule that routes answering one question spend one budget.\n\n**Rank:** `public` — No authentication — any caller.",
|
|
2851
2851
|
"x-rank": "public",
|
|
2852
2852
|
"x-source": "src/bongos/routes/builders.js",
|
|
2853
2853
|
"parameters": [
|
|
@@ -15820,7 +15820,8 @@
|
|
|
15820
15820
|
"shipped_count": {},
|
|
15821
15821
|
"recent_ships": {},
|
|
15822
15822
|
"achievements": {},
|
|
15823
|
-
"cross_project": {}
|
|
15823
|
+
"cross_project": {},
|
|
15824
|
+
"handle": {}
|
|
15824
15825
|
},
|
|
15825
15826
|
"required": [
|
|
15826
15827
|
"builder",
|
package/docs/copy-inventory.md
CHANGED
|
@@ -4,13 +4,13 @@
|
|
|
4
4
|
> Deterministic on a given tree — regenerate and commit rather than hand-editing.
|
|
5
5
|
> Machine-readable twin: [`copy-registry.json`](copy-registry.json).
|
|
6
6
|
|
|
7
|
-
**
|
|
7
|
+
**1589 strings** across 3 surfaces · 1383 distinct pieces of copy · 1589 certain, 0 heuristic · 157 shown in more than one place.
|
|
8
8
|
|
|
9
9
|
## Surfaces
|
|
10
10
|
|
|
11
11
|
| surface | lives at | files | strings |
|
|
12
12
|
|---|---|---|---|
|
|
13
|
-
| `builders-hall` | `modules/hall-ui/public` |
|
|
13
|
+
| `builders-hall` | `modules/hall-ui/public` | 74 | 1165 |
|
|
14
14
|
| `status-dashboard` | `modules/status-ui/public` | 4 | 98 |
|
|
15
15
|
| `landing` | `public-landing` | 5 | 326 |
|
|
16
16
|
|
|
@@ -78,7 +78,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
78
78
|
| `c10806b50c61` | 2x | Claim task {…} | modules/hall-ui/public/goals-render.js:280, modules/hall-ui/public/work.js:417 |
|
|
79
79
|
| … | | _117 more in the JSON registry_ | |
|
|
80
80
|
|
|
81
|
-
## `builders-hall` —
|
|
81
|
+
## `builders-hall` — 1165 strings
|
|
82
82
|
|
|
83
83
|
| id | conf | text | where |
|
|
84
84
|
|---|---|---|---|
|
|
@@ -135,7 +135,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
135
135
|
| `6ca31dd5eb91` | certain | Acknowledge the primer | `modules/hall-ui/public/primer.html:65` |
|
|
136
136
|
| `4461b13f95c2` | certain | Actions apply at once. Closing stops and deletes the box; you can start a fresh one anytime. | `modules/hall-ui/public/settings.html:200` |
|
|
137
137
|
| `524188fe0078` | certain | Active sessions | `modules/hall-ui/public/settings.html:153` |
|
|
138
|
-
| `f6036360c90c` | certain | Activity, by craft | `modules/hall-ui/public/profile.html:
|
|
138
|
+
| `f6036360c90c` | certain | Activity, by craft | `modules/hall-ui/public/profile.html:89` |
|
|
139
139
|
| `b8476c677e7c` | certain | Add a note (optional) | `modules/hall-ui/public/my-projects.html:52` |
|
|
140
140
|
| `62eded408097` | certain | Add {…} | `modules/hall-ui/public/profile-nudge.js:68` |
|
|
141
141
|
| `7cf0163d4cde` | certain | Aggregate totals across every recorded work session. | `modules/hall-ui/public/sessions.html:35` |
|
|
@@ -193,7 +193,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
193
193
|
| `46b33550d52b` | certain | Bulk claim | `modules/hall-ui/public/goals.html:151` |
|
|
194
194
|
| `46b33550d52b` | certain | Bulk claim | `modules/hall-ui/public/work.html:121` |
|
|
195
195
|
| `6d6d626fc518` | certain | Bulk release | `modules/hall-ui/public/work.html:122` |
|
|
196
|
-
| `181cfa50b127` | certain | By the numbers | `modules/hall-ui/public/profile.html:
|
|
196
|
+
| `181cfa50b127` | certain | By the numbers | `modules/hall-ui/public/profile.html:42` |
|
|
197
197
|
| `a015a43bfc7f` | certain | CLI access | `modules/hall-ui/public/settings.html:135` |
|
|
198
198
|
| `25de6150fc93` | certain | Capabilities held by a machine identity, never by a person or a rank. Shown for completeness — nothing here ca | `modules/hall-ui/public/government.html:91` |
|
|
199
199
|
| `bdca1f219e6f` | certain | Cast objection | `modules/hall-ui/public/government.js:885` |
|
|
@@ -297,7 +297,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
297
297
|
| `8f597eabf4ac` | certain | Could not load the drachmae guide. | `modules/hall-ui/public/drachmae.js:162` |
|
|
298
298
|
| `b2147cc90e36` | certain | Could not load the idea template. Reload, or file from the terminal with capture.js. | `modules/hall-ui/public/ideas.js:438` |
|
|
299
299
|
| `6264ebc9ede0` | certain | Could not load the primer. | `modules/hall-ui/public/primer.js:620` |
|
|
300
|
-
| `7604af13c40e` | certain | Could not load the profile. | `modules/hall-ui/public/profile.js:
|
|
300
|
+
| `7604af13c40e` | certain | Could not load the profile. | `modules/hall-ui/public/profile.js:625` |
|
|
301
301
|
| `8b69e445592a` | certain | Could not load the question labels; showing the answers as stored. | `modules/hall-ui/public/idea.js:229` |
|
|
302
302
|
| `edd5bafbe075` | certain | Could not load the queue just now. | `modules/hall-ui/public/ideas.js:387` |
|
|
303
303
|
| `02bb0570996a` | certain | Could not load the ranks. | `modules/hall-ui/public/ranks.js:197` |
|
|
@@ -354,7 +354,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
354
354
|
| `e91a4d2e3e1c` | certain | Created rank {…}. | `modules/hall-ui/public/government.js:402` |
|
|
355
355
|
| `5968d13a9dbe` | certain | Criteria met — awaiting review | `modules/hall-ui/public/goals-render.js:143` |
|
|
356
356
|
| `4338cedb0c2c` | certain | Cross-project builders | `modules/hall-ui/public/scouting.html:31` |
|
|
357
|
-
| `a55e423263f0` | certain | Cross-project record | `modules/hall-ui/public/profile.html:
|
|
357
|
+
| `a55e423263f0` | certain | Cross-project record | `modules/hall-ui/public/profile.html:52` |
|
|
358
358
|
| `a55e423263f0` | certain | Cross-project record | `modules/hall-ui/public/settings.html:86` |
|
|
359
359
|
| `6744aed3d15e` | certain | Dated exploit-detail engagement reports — kept out of the repo (threat-model §6.3) and served only to Archons. | `modules/hall-ui/public/watch.html:121` |
|
|
360
360
|
| `7c6891813679` | certain | Default ({…}) | `modules/hall-ui/public/settings.js:65` |
|
|
@@ -399,7 +399,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
399
399
|
| `44f928d13699` | certain | Event sounds | `modules/hall-ui/public/settings.html:320` |
|
|
400
400
|
| `8690c6424585` | certain | Every builder in {{worldName}} — rank, {{currency}}, karma, and active claims. | `modules/hall-ui/public/watch.html:61` |
|
|
401
401
|
| `2ae1eb35611e` | certain | Every done-when criterion for this goal, with its full detail. | `modules/hall-ui/public/goals.html:186` |
|
|
402
|
-
| `8825f1d90dd1` | certain | Every idea this builder captured — promoted, discarded, merged, or still open. | `modules/hall-ui/public/profile.html:
|
|
402
|
+
| `8825f1d90dd1` | certain | Every idea this builder captured — promoted, discarded, merged, or still open. | `modules/hall-ui/public/profile.html:109` |
|
|
403
403
|
| `bc481184a282` | certain | Every place you are currently signed in — this browser, your terminal, any dev box. A session refreshes for an | `modules/hall-ui/public/settings.html:154` |
|
|
404
404
|
| `1e05b7fe42c8` | certain | Every project you build on across the platform. | `modules/hall-ui/public/my-projects.html:24` |
|
|
405
405
|
| `efafe5ce29ed` | certain | Every task linked to this criterion has shipped — a Metic can confirm it | `modules/hall-ui/public/goals-render.js:588` |
|
|
@@ -441,7 +441,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
441
441
|
| `e2a9e33abe60` | certain | Goal tasks | `modules/hall-ui/public/goals.html:112` |
|
|
442
442
|
| `e0bc28d10785` | certain | Goals could not be loaded. Refresh to try again. | `modules/hall-ui/public/goals-page.js:889` |
|
|
443
443
|
| `4517f2253d0f` | certain | Goals couldn’t be loaded just now. Refresh to try again. | `modules/hall-ui/public/goals.js:112` |
|
|
444
|
-
| `6a85997bda21` | certain | Goals created | `modules/hall-ui/public/profile.html:
|
|
444
|
+
| `6a85997bda21` | certain | Goals created | `modules/hall-ui/public/profile.html:101` |
|
|
445
445
|
| `2ed2cb8fbffd` | certain | Goals page | `modules/hall-ui/public/goals.js:94` |
|
|
446
446
|
| `320a0516a33e` | certain | Goals seeded — awaiting kickoff | `modules/hall-ui/public/roadmap.js:205` |
|
|
447
447
|
| `bba4caf837e9` | certain | Goals — {{worldName}} | `modules/hall-ui/public/goals.html:6` |
|
|
@@ -475,7 +475,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
475
475
|
| `ba82ec56f1e3` | certain | Idea — {{worldName}} | `modules/hall-ui/public/idea.html:6` |
|
|
476
476
|
| `ba82ec56f1e3` | certain | Idea — {{worldName}} | `modules/hall-ui/public/idea.html:6` |
|
|
477
477
|
| `e181637c5a5e` | certain | Ideas awaiting triage | `modules/hall-ui/public/builders.js:850` |
|
|
478
|
-
| `d4e21769ce1c` | certain | Ideas filed, and what became of them | `modules/hall-ui/public/profile.html:
|
|
478
|
+
| `d4e21769ce1c` | certain | Ideas filed, and what became of them | `modules/hall-ui/public/profile.html:108` |
|
|
479
479
|
| `379b20d548ad` | certain | Ideas whose author has said "I had the thought but not the full picture". Anyone can develop one — the author | `modules/hall-ui/public/ideas.html:33` |
|
|
480
480
|
| `ef224c9de7c8` | certain | Ideas — {{worldName}} | `modules/hall-ui/public/ideas.html:6` |
|
|
481
481
|
| `ef224c9de7c8` | certain | Ideas — {{worldName}} | `modules/hall-ui/public/ideas.html:6` |
|
|
@@ -528,7 +528,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
528
528
|
| `181555ce376f` | certain | Loading held pull requests… | `modules/hall-ui/public/gate.html:31` |
|
|
529
529
|
| `edf9b78b9544` | certain | Loading idea… | `modules/hall-ui/public/idea.html:28` |
|
|
530
530
|
| `691f74556a1d` | certain | Loading modules… | `modules/hall-ui/public/modules.html:23` |
|
|
531
|
-
| `2b5826012538` | certain | Loading profile… | `modules/hall-ui/public/profile.html:
|
|
531
|
+
| `2b5826012538` | certain | Loading profile… | `modules/hall-ui/public/profile.html:28` |
|
|
532
532
|
| `554b156a08d6` | certain | Loading sessions… | `modules/hall-ui/public/sessions.html:23` |
|
|
533
533
|
| `20d8552e13db` | certain | Loading tasks… | `modules/hall-ui/public/work.html:27` |
|
|
534
534
|
| `b1d84b4724d7` | certain | Loading task… | `modules/hall-ui/public/task.html:56` |
|
|
@@ -712,7 +712,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
712
712
|
| `06cbba1440b1` | certain | Rebase owed — new claims paused | `modules/hall-ui/public/hall-render.js:485` |
|
|
713
713
|
| `58fb6fe4b47e` | certain | Recent history could not be loaded right now. | `modules/hall-ui/public/work.js:1152` |
|
|
714
714
|
| `3c41dbf7c212` | certain | Recent ledger | `modules/hall-ui/public/watch.js:640` |
|
|
715
|
-
| `fbce2d89706b` | certain | Recent works | `modules/hall-ui/public/profile.html:
|
|
715
|
+
| `fbce2d89706b` | certain | Recent works | `modules/hall-ui/public/profile.html:66` |
|
|
716
716
|
| `0b2d1327608f` | certain | Recently shipped | `modules/hall-ui/public/hall-widgets.js:107` |
|
|
717
717
|
| `cd2bffe2e5b9` | certain | Recognize your peers’ finished work. A vote up (+) or down (−) shapes their karma after a one-week cooling per | `modules/hall-ui/public/hall-widgets.js:169` |
|
|
718
718
|
| `986ecda14881` | certain | Record your owner approval for | `modules/hall-ui/public/gate.js:87` |
|
|
@@ -739,8 +739,8 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
739
739
|
| `ab456f09c0f6` | certain | Request access | `modules/hall-ui/public/hall-render.js:405` |
|
|
740
740
|
| `967f6d1ac068` | certain | Request dev box | `modules/hall-ui/public/box-panel.js:357` |
|
|
741
741
|
| `967f6d1ac068` | certain | Request dev box | `modules/hall-ui/public/box-panel.js:421` |
|
|
742
|
-
| `820f47ce5f16` | certain | Request sent | `modules/hall-ui/public/profile-connections.js:
|
|
743
|
-
| `820f47ce5f16` | certain | Request sent | `modules/hall-ui/public/profile-connections.js:
|
|
742
|
+
| `820f47ce5f16` | certain | Request sent | `modules/hall-ui/public/profile-connections.js:86` |
|
|
743
|
+
| `820f47ce5f16` | certain | Request sent | `modules/hall-ui/public/profile-connections.js:134` |
|
|
744
744
|
| `e86e43684bce` | certain | Request sent — the owner or a manager will review it. | `modules/hall-ui/public/goals-render.js:999` |
|
|
745
745
|
| `88b36bd9368b` | certain | Request to join | `modules/hall-ui/public/my-projects.html:47` |
|
|
746
746
|
| `280f20e7ddf5` | certain | Request withdrawn. | `modules/hall-ui/public/goals-render.js:1015` |
|
|
@@ -776,6 +776,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
776
776
|
| `324af64c8b6e` | certain | See all {…} goals {…} | `modules/hall-ui/public/roadmap.js:116` |
|
|
777
777
|
| `324af64c8b6e` | certain | See all {…} goals {…} | `modules/hall-ui/public/roadmap.js:194` |
|
|
778
778
|
| `bed8f9758ac0` | certain | See how ranks work → | `modules/hall-ui/public/not-ready.html:32` |
|
|
779
|
+
| `a9c398651862` | certain | See the cross-project record | `modules/hall-ui/public/profile-canonical.js:114` |
|
|
779
780
|
| `17d5b5739f46` | certain | See the work board → | `modules/hall-ui/public/hall-render.js:845` |
|
|
780
781
|
| `e6afd2262c6e` | certain | See what's blocking | `modules/hall-ui/public/goals-render.js:148` |
|
|
781
782
|
| `75dc5463facc` | certain | Select at least 2 waiting tasks to copy a parallel-session prompt. | `modules/hall-ui/public/work.js:764` |
|
|
@@ -804,7 +805,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
804
805
|
| `838b17fa8d93` | certain | Shown on your public profile — it helps others know what you work on. | `modules/hall-ui/public/profile-nudge.js:69` |
|
|
805
806
|
| `2f12d46f96ca` | certain | Sign in from the app. | `modules/hall-ui/public/hall-render.js:650` |
|
|
806
807
|
| `19ab56cc3932` | certain | Sign in to filter to your goals | `modules/hall-ui/public/goals-page.js:866` |
|
|
807
|
-
| `969f95f183ca` | certain | Sign in to the platform once to activate your profile before connecting. | `modules/hall-ui/public/profile-connections.js:
|
|
808
|
+
| `969f95f183ca` | certain | Sign in to the platform once to activate your profile before connecting. | `modules/hall-ui/public/profile-connections.js:220` |
|
|
808
809
|
| `b057dfbf49c5` | certain | Sign in when Claude prompts you. | `modules/hall-ui/public/hall-render.js:650` |
|
|
809
810
|
| `e57ec24e13ef` | certain | Sign in with GitHub | `modules/hall-ui/public/hall-render.js:256` |
|
|
810
811
|
| `e57ec24e13ef` | certain | Sign in with GitHub | `modules/hall-ui/public/hall-render.js:327` |
|
|
@@ -818,7 +819,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
818
819
|
| `82df09a08476` | certain | So the Archon knows who you are | `modules/hall-ui/public/hall-render.js:275` |
|
|
819
820
|
| `7da6a848cd26` | certain | Someone helped | `modules/hall-ui/public/collab.js:65` |
|
|
820
821
|
| `867df58c9aaa` | certain | Something else | `modules/hall-ui/public/copy-desk.html:97` |
|
|
821
|
-
| `c377d8ed744b` | certain | Something went wrong. Try again. | `modules/hall-ui/public/profile-connections.js:
|
|
822
|
+
| `c377d8ed744b` | certain | Something went wrong. Try again. | `modules/hall-ui/public/profile-connections.js:275` |
|
|
822
823
|
| `7e4b5d31df51` | certain | Sort by karma (peer recognition + approvals) | `modules/hall-ui/public/hall-widgets.js:210` |
|
|
823
824
|
| `f8ca223d8864` | certain | Sort by rank | `modules/hall-ui/public/hall-widgets.js:208` |
|
|
824
825
|
| `d67798e25c67` | certain | Sort by tokens used across recorded sessions — efficiency is tokens ÷ credits (hover a row for tokens-per-drac | `modules/hall-ui/public/hall-widgets.js:211` |
|
|
@@ -852,7 +853,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
852
853
|
| `545c59c1deb7` | certain | That is the session you are using right now. Ending it signs you out of this page. | `modules/hall-ui/public/settings-sessions.js:74` |
|
|
853
854
|
| `4a8d1beb6f98` | certain | That project isn’t on the platform. | `modules/hall-ui/public/my-projects.js:145` |
|
|
854
855
|
| `c0e28f30524d` | certain | That project's own standing, as reported by that project. | `modules/hall-ui/public/profile.js:230` |
|
|
855
|
-
| `e99d4a811e92` | certain | That request is no longer available. | `modules/hall-ui/public/profile-connections.js:
|
|
856
|
+
| `e99d4a811e92` | certain | That request is no longer available. | `modules/hall-ui/public/profile-connections.js:270` |
|
|
856
857
|
| `50198c465225` | certain | The 16-value rank enum in three bands, which ranks are live, and the permissions each rank holds. | `modules/hall-ui/public/diagrams.html:68` |
|
|
857
858
|
| `1569b8838862` | certain | The Dev Box app now acts as | `modules/hall-ui/public/pair.js:40` |
|
|
858
859
|
| `47c47af5dd7f` | certain | The Repo Atlas regenerates itself on the server every time code ships. It looks like it hasn’t been generated | `modules/hall-ui/public/atlas.html:48` |
|
|
@@ -867,7 +868,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
867
868
|
| `9f45c63c9697` | certain | The kinds of work you prefer. Task suggestions lean toward your choices — pick one, several, or none. | `modules/hall-ui/public/settings.html:118` |
|
|
868
869
|
| `e1ce3766c6fe` | certain | The map has not been generated on this deploy yet. | `modules/hall-ui/public/atlas.js:320` |
|
|
869
870
|
| `a07074fdfac4` | certain | The map is being drawn | `modules/hall-ui/public/atlas.html:47` |
|
|
870
|
-
| `a0a4a7ecfa72` | certain | The most recent works this builder has shipped. | `modules/hall-ui/public/profile.html:
|
|
871
|
+
| `a0a4a7ecfa72` | certain | The most recent works this builder has shipped. | `modules/hall-ui/public/profile.html:67` |
|
|
871
872
|
| `381f29be7497` | certain | The most recently shipped tasks. | `modules/hall-ui/public/work.html:97` |
|
|
872
873
|
| `82479c539fca` | certain | The name shown beside your work — in the roster, the leaderboard, and the work feed. Your sign-in and the cred | `modules/hall-ui/public/settings.html:67` |
|
|
873
874
|
| `b88dd8b96b72` | certain | The next person to notice this sentence reads your note instead of re-litigating it. | `modules/hall-ui/public/copy-desk.html:122` |
|
|
@@ -892,7 +893,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
892
893
|
| `bcdbded93d4e` | certain | The work board failed to load. Reload to try again. | `modules/hall-ui/public/work.js:1402` |
|
|
893
894
|
| `25794c439321` | certain | Then copy the project onto your machine and run the one-shot bootstrap (it checks your setup, installs depende | `modules/hall-ui/public/hall-render.js:717` |
|
|
894
895
|
| `df316c3484de` | certain | Then drive it however you like: | `modules/hall-ui/public/hall-render.js:650` |
|
|
895
|
-
| `5151cf635644` | certain | They have asked to connect. | `modules/hall-ui/public/profile-connections.js:
|
|
896
|
+
| `5151cf635644` | certain | They have asked to connect. | `modules/hall-ui/public/profile-connections.js:203` |
|
|
896
897
|
| `6e46811189db` | certain | This PR is held by the gate-review check. | `modules/hall-ui/public/gate.js:113` |
|
|
897
898
|
| `ed313d96b419` | certain | This builder keeps their track record private. | `modules/hall-ui/public/profile.js:210` |
|
|
898
899
|
| `3f9e476e22db` | certain | This clears {…}. The gate check re-runs and the PR merges once it goes green. Your approval applies only to co | `modules/hall-ui/public/gate.js:87` |
|
|
@@ -948,7 +949,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
948
949
|
| `70dc106494cf` | certain | What is wrong with it, and what would better look like? | `modules/hall-ui/public/copy-desk.html:101` |
|
|
949
950
|
| `ae44a375e814` | certain | What it unlocks | `modules/hall-ui/public/ranks.js:109` |
|
|
950
951
|
| `00dc1f0f83e1` | certain | What kind of wrong? | `modules/hall-ui/public/copy-desk.html:89` |
|
|
951
|
-
| `5f2b989bbf6a` | certain | What this builder has done, seen one craft at a time. | `modules/hall-ui/public/profile.html:
|
|
952
|
+
| `5f2b989bbf6a` | certain | What this builder has done, seen one craft at a time. | `modules/hall-ui/public/profile.html:90` |
|
|
952
953
|
| `41b2aef03c03` | certain | What this project runs today | `modules/hall-ui/public/government.js:680` |
|
|
953
954
|
| `3dade88f0584` | certain | What your box would cost: | `modules/hall-ui/public/box-panel.js:235` |
|
|
954
955
|
| `b8743d16eab8` | certain | What {…} unlocks | `modules/hall-ui/public/hall-render.js:82` |
|
|
@@ -968,7 +969,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
968
969
|
| `c5fd76a9e03d` | certain | Won {…} | `modules/hall-ui/public/builders.js:482` |
|
|
969
970
|
| `c5fd76a9e03d` | certain | Won {…} | `modules/hall-ui/public/profile.js:115` |
|
|
970
971
|
| `520d139df9eb` | certain | Work is complete but the version is held — no tasks are being claimed here right now. | `modules/hall-ui/public/roadmap.js:223` |
|
|
971
|
-
| `0e6b8e8f26cc` | certain | Works completed | `modules/hall-ui/public/profile.html:
|
|
972
|
+
| `0e6b8e8f26cc` | certain | Works completed | `modules/hall-ui/public/profile.html:96` |
|
|
972
973
|
| `52e5fe3c6075` | certain | Write a Full Idea | `modules/hall-ui/public/ideas.html:43` |
|
|
973
974
|
| `7664f4373d1f` | certain | Write one | `modules/hall-ui/public/idea.js:243` |
|
|
974
975
|
| `c368b56ecf40` | certain | Xenos builders start with a focused set of pages — ship your first | `modules/hall-ui/public/not-ready.html:26` |
|
|
@@ -1166,7 +1167,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1166
1167
|
| `c5623d60015d` | certain | {…} Your standing is {…}. | `modules/hall-ui/public/hall-render.js:729` |
|
|
1167
1168
|
| `418b4031e9a4` | certain | {…} \u00b7 who votes, and how it {…} | `modules/hall-ui/public/government.js:963` |
|
|
1168
1169
|
| `a91e1e702d4d` | certain | {…} achieved, {…} in progress, {…} not started of {…} goals | `modules/hall-ui/public/roadmap.js:80` |
|
|
1169
|
-
| `0da0e247ff0f` | certain | {…} asking to connect | `modules/hall-ui/public/profile-connections.js:
|
|
1170
|
+
| `0da0e247ff0f` | certain | {…} asking to connect | `modules/hall-ui/public/profile-connections.js:136` |
|
|
1170
1171
|
| `9d160147b34a` | certain | {…} criteria · since {…} | `modules/hall-ui/public/roadmap.js:140` |
|
|
1171
1172
|
| `9d160147b34a` | certain | {…} criteria · since {…} | `modules/hall-ui/public/roadmap.js:163` |
|
|
1172
1173
|
| `78ba2bd4bc44` | certain | {…} criteria · started {…} | `modules/hall-ui/public/roadmap.js:223` |
|
|
@@ -1193,7 +1194,7 @@ Identical copy within one surface. Sometimes right (a repeated button), sometime
|
|
|
1193
1194
|
| `8e28c14b106d` | certain | {…} total | `modules/hall-ui/public/roadmap.js:108` |
|
|
1194
1195
|
| `44e2f95586e4` | certain | {…} will be able to open dev boxes again. | `modules/hall-ui/public/harbor.js:176` |
|
|
1195
1196
|
| `b2db97589086` | certain | {…} will not be able to open or wake a dev box until you unblock them. A box they already have running is left | `modules/hall-ui/public/harbor.js:146` |
|
|
1196
|
-
| `3ad8f32dd43b` | certain | {…} you have sent | `modules/hall-ui/public/profile-connections.js:
|
|
1197
|
+
| `3ad8f32dd43b` | certain | {…} you have sent | `modules/hall-ui/public/profile-connections.js:137` |
|
|
1197
1198
|
| `8186b562553a` | certain | {…} {…} waiting | `modules/hall-ui/public/work.js:356` |
|
|
1198
1199
|
| `0d4016a5531c` | certain | {…} {…}{…} | `modules/hall-ui/public/work.js:220` |
|
|
1199
1200
|
| `b3757ba3bb46` | certain | {…} · Stored encrypted; never shown again after saving. | `modules/hall-ui/public/settings.js:1036` |
|