@bongos/core 1.20.21 → 1.20.22
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 +67 -47
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +4 -2
- package/clients/bongos-client/index.cjs +4 -2
- package/clients/bongos-client/index.d.ts +8 -4
- package/clients/bongos-client/index.mjs +4 -2
- package/docs/adr/0335-an-invite-is-a-pre-approved-row-on-the-projects-own-instance.md +1 -0
- package/docs/adr/0353-a-hub-invite-is-a-notice-of-the-projects-own-invite.md +70 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +137 -72
- package/docs/api-reference.md +6 -5
- package/docs/copy-inventory.md +196 -196
- package/docs/copy-registry.json +250 -250
- package/docs/module-api-changelog.md +2 -0
- package/docs/page-inventory.json +2 -1
- package/docs/page-readings.json +310 -306
- package/modules/hall-ui/public/watch.js +23 -0
- package/modules/onboarding/routes/access-requests.js +36 -6
- package/modules/platform-identity/invite-notices.js +61 -0
- package/modules/platform-identity/module.json +1 -1
- package/modules/platform-identity/platform-identity.js +12 -18
- package/modules/platform-identity/routes/my-projects.js +6 -15
- package/modules/platform-identity/routes/projects.js +22 -15
- package/modules/platform-identity/routes/sso-invites.js +82 -0
- package/modules/platform-identity/tests/platform-identity.mjs +14 -11
- package/modules/public-landing/public/assets/cosmos.css +5 -0
- package/modules/public-landing/public/projects.html +35 -29
- package/modules/public-landing/public/projects.states.json +1 -0
- package/modules/ui-design/kit/serve.js +9 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +6 -0
- package/src/bongos/auth-admission.js +40 -0
- package/src/bongos/auth.js +2 -1
- package/src/module-api.js +8 -1
- package/tests/access_requests_invite.mjs +39 -1
- package/tests/application_lifecycle.mjs +41 -1
- package/tests/catalog_only_client.mjs +5 -3
- package/tests/hub_invite_notices.mjs +261 -0
- package/tests/module_api.mjs +1 -0
- package/tests/my_projects_surface.mjs +11 -1
- package/tests/project_invite_ownership.mjs +12 -7
- package/tests/project_invite_ui.mjs +42 -45
- package/tests/watch_applications_queue.mjs +33 -2
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.20.
|
|
6
|
-
"core_contract": "1.20.
|
|
7
|
-
"source_commit": "
|
|
5
|
+
"core_version": "1.20.22",
|
|
6
|
+
"core_contract": "1.20.22",
|
|
7
|
+
"source_commit": "d79f16096f873c36c2bd963c04d552fa8300497f",
|
|
8
8
|
"source_ref": "HEAD",
|
|
9
|
-
"built_at": "2026-09-
|
|
9
|
+
"built_at": "2026-09-30T16:20:55.212Z",
|
|
10
10
|
"redaction": {
|
|
11
11
|
"model": "docs-redacted+functional-verbatim",
|
|
12
|
-
"docs_redacted":
|
|
12
|
+
"docs_redacted": 558,
|
|
13
13
|
"agent_docs_stubbed": 25,
|
|
14
|
-
"functional_verbatim":
|
|
14
|
+
"functional_verbatim": 2641,
|
|
15
15
|
"rules": 3,
|
|
16
16
|
"gate_literals": 3,
|
|
17
17
|
"gate": "passed"
|
|
18
18
|
},
|
|
19
|
-
"file_count":
|
|
20
|
-
"tree_sha256": "
|
|
19
|
+
"file_count": 3225,
|
|
20
|
+
"tree_sha256": "63917bb4f51c192d66139655bbfebd61e0f04299a9f09e5073420a970ee9e39a",
|
|
21
21
|
"files": [
|
|
22
22
|
{
|
|
23
23
|
"path": ".claude/skills/ask-for-help/SKILL.md",
|
|
@@ -327,12 +327,12 @@
|
|
|
327
327
|
{
|
|
328
328
|
"path": "clients/bongos-client/README.md",
|
|
329
329
|
"mode": "0000644",
|
|
330
|
-
"sha256": "
|
|
330
|
+
"sha256": "ff5585737cc618ca1a24c994a27411a6f4b60192d4904063771744788c0b8002"
|
|
331
331
|
},
|
|
332
332
|
{
|
|
333
333
|
"path": "clients/bongos-client/bongos-client.global.js",
|
|
334
334
|
"mode": "0000644",
|
|
335
|
-
"sha256": "
|
|
335
|
+
"sha256": "f0a11cd6519a8ba62cb645c8d50da768f00f7252562a8b7a2f6e089ae0040f5e"
|
|
336
336
|
},
|
|
337
337
|
{
|
|
338
338
|
"path": "clients/bongos-client/examples/hello-world.mjs",
|
|
@@ -342,17 +342,17 @@
|
|
|
342
342
|
{
|
|
343
343
|
"path": "clients/bongos-client/index.cjs",
|
|
344
344
|
"mode": "0000644",
|
|
345
|
-
"sha256": "
|
|
345
|
+
"sha256": "60673c698acad267f556a78b0e4d13b60d5357208a8772fe0d69cbd60b2b1c2c"
|
|
346
346
|
},
|
|
347
347
|
{
|
|
348
348
|
"path": "clients/bongos-client/index.d.ts",
|
|
349
349
|
"mode": "0000644",
|
|
350
|
-
"sha256": "
|
|
350
|
+
"sha256": "2697fd1139e28cf2bf4ca8a5d4218d22060399bb4050cfdaa9457eae6c639b79"
|
|
351
351
|
},
|
|
352
352
|
{
|
|
353
353
|
"path": "clients/bongos-client/index.mjs",
|
|
354
354
|
"mode": "0000644",
|
|
355
|
-
"sha256": "
|
|
355
|
+
"sha256": "37028d9008247d9afd900f38437e1f8e335c19633c55379864177033d4f1bc89"
|
|
356
356
|
},
|
|
357
357
|
{
|
|
358
358
|
"path": "clients/bongos-client/package.json",
|
|
@@ -2147,7 +2147,7 @@
|
|
|
2147
2147
|
{
|
|
2148
2148
|
"path": "docs/adr/0335-an-invite-is-a-pre-approved-row-on-the-projects-own-instance.md",
|
|
2149
2149
|
"mode": "0000644",
|
|
2150
|
-
"sha256": "
|
|
2150
|
+
"sha256": "f22adac7e27f2e83ece9311e44fca35edb0bd571440f108d28a9088775d0b93c"
|
|
2151
2151
|
},
|
|
2152
2152
|
{
|
|
2153
2153
|
"path": "docs/adr/0336-a-guilds-record-is-the-sum-of-what-its-members-already-show.md",
|
|
@@ -2234,20 +2234,25 @@
|
|
|
2234
2234
|
"mode": "0000644",
|
|
2235
2235
|
"sha256": "d74b4f6262d4f23fd67f73771bbe8187e9cb92fa321b83ffb33ef03cf96d1236"
|
|
2236
2236
|
},
|
|
2237
|
+
{
|
|
2238
|
+
"path": "docs/adr/0353-a-hub-invite-is-a-notice-of-the-projects-own-invite.md",
|
|
2239
|
+
"mode": "0000644",
|
|
2240
|
+
"sha256": "4445d9c306a22fb3b7e72467bedad262f4653c092bf4866f8a39410248da2724"
|
|
2241
|
+
},
|
|
2237
2242
|
{
|
|
2238
2243
|
"path": "docs/adr/README.md",
|
|
2239
2244
|
"mode": "0000644",
|
|
2240
|
-
"sha256": "
|
|
2245
|
+
"sha256": "6be10fb1925fd406729e08b4d604569b1123b7c5f6091300559df8532831ab69"
|
|
2241
2246
|
},
|
|
2242
2247
|
{
|
|
2243
2248
|
"path": "docs/api-reference.md",
|
|
2244
2249
|
"mode": "0000644",
|
|
2245
|
-
"sha256": "
|
|
2250
|
+
"sha256": "d6a19f9db87e4d936f317efe07e1a4dfaaa75c5a6797a4c3e4939634adec8494"
|
|
2246
2251
|
},
|
|
2247
2252
|
{
|
|
2248
2253
|
"path": "docs/api/openapi.json",
|
|
2249
2254
|
"mode": "0000644",
|
|
2250
|
-
"sha256": "
|
|
2255
|
+
"sha256": "f44cf9111baee003af84ab6c83503e97a4a723c4cb2ad59ad367cce75f8f717e"
|
|
2251
2256
|
},
|
|
2252
2257
|
{
|
|
2253
2258
|
"path": "docs/architecture.md",
|
|
@@ -2272,12 +2277,12 @@
|
|
|
2272
2277
|
{
|
|
2273
2278
|
"path": "docs/copy-inventory.md",
|
|
2274
2279
|
"mode": "0000644",
|
|
2275
|
-
"sha256": "
|
|
2280
|
+
"sha256": "5034d255bbdcdd29c52831ce069561494a5ad7cc028b5e5711a0ab7a95a73a2b"
|
|
2276
2281
|
},
|
|
2277
2282
|
{
|
|
2278
2283
|
"path": "docs/copy-registry.json",
|
|
2279
2284
|
"mode": "0000644",
|
|
2280
|
-
"sha256": "
|
|
2285
|
+
"sha256": "e75a8e11138b151ea81f378dde8f4e7fe395b44ba9369e7c98e865ff6516eeb4"
|
|
2281
2286
|
},
|
|
2282
2287
|
{
|
|
2283
2288
|
"path": "docs/design/apex-pass-2-direction.md",
|
|
@@ -2777,7 +2782,7 @@
|
|
|
2777
2782
|
{
|
|
2778
2783
|
"path": "docs/module-api-changelog.md",
|
|
2779
2784
|
"mode": "0000644",
|
|
2780
|
-
"sha256": "
|
|
2785
|
+
"sha256": "f721721d1ba698925f1aa8133bb3b5d6ab6c048be0183212c0a89b1ca1d132a0"
|
|
2781
2786
|
},
|
|
2782
2787
|
{
|
|
2783
2788
|
"path": "docs/modules-contract.md",
|
|
@@ -2862,12 +2867,12 @@
|
|
|
2862
2867
|
{
|
|
2863
2868
|
"path": "docs/page-inventory.json",
|
|
2864
2869
|
"mode": "0000644",
|
|
2865
|
-
"sha256": "
|
|
2870
|
+
"sha256": "7a607ea5f1d29bd078ddbb6bfa5bcfb68e689095ca47bf290194cc7578794ed9"
|
|
2866
2871
|
},
|
|
2867
2872
|
{
|
|
2868
2873
|
"path": "docs/page-readings.json",
|
|
2869
2874
|
"mode": "0000644",
|
|
2870
|
-
"sha256": "
|
|
2875
|
+
"sha256": "dd7227bb9daface49cfb1e2a8702cb81b95a5d19f0169038ddfd7a6c80e04225"
|
|
2871
2876
|
},
|
|
2872
2877
|
{
|
|
2873
2878
|
"path": "docs/project-context.template.md",
|
|
@@ -5717,7 +5722,7 @@
|
|
|
5717
5722
|
{
|
|
5718
5723
|
"path": "modules/hall-ui/public/watch.js",
|
|
5719
5724
|
"mode": "0000644",
|
|
5720
|
-
"sha256": "
|
|
5725
|
+
"sha256": "7a5ef4f34be6b0ab7f5b3fa34bf1666c17164a6331031cc908c2791109585b89"
|
|
5721
5726
|
},
|
|
5722
5727
|
{
|
|
5723
5728
|
"path": "modules/hall-ui/public/watch.states.json",
|
|
@@ -6807,7 +6812,7 @@
|
|
|
6807
6812
|
{
|
|
6808
6813
|
"path": "modules/onboarding/routes/access-requests.js",
|
|
6809
6814
|
"mode": "0000644",
|
|
6810
|
-
"sha256": "
|
|
6815
|
+
"sha256": "1eaac1f70a519ca5a0ea71bb56ab6d33b582f5ff002faf5811fa6c1700480057"
|
|
6811
6816
|
},
|
|
6812
6817
|
{
|
|
6813
6818
|
"path": "modules/onboarding/routes/onboarding.js",
|
|
@@ -6894,6 +6899,11 @@
|
|
|
6894
6899
|
"mode": "0000644",
|
|
6895
6900
|
"sha256": "2eb5577450d3bc1d67b7eab9a13256889aca945c6ac6a6a0ca919e195952b7a1"
|
|
6896
6901
|
},
|
|
6902
|
+
{
|
|
6903
|
+
"path": "modules/platform-identity/invite-notices.js",
|
|
6904
|
+
"mode": "0000644",
|
|
6905
|
+
"sha256": "1391ac415c073c88942f517b4467514142e15fb0fadb2e156c44db4e75ee0763"
|
|
6906
|
+
},
|
|
6897
6907
|
{
|
|
6898
6908
|
"path": "modules/platform-identity/invite-suggestions.js",
|
|
6899
6909
|
"mode": "0000644",
|
|
@@ -7037,7 +7047,7 @@
|
|
|
7037
7047
|
{
|
|
7038
7048
|
"path": "modules/platform-identity/module.json",
|
|
7039
7049
|
"mode": "0000644",
|
|
7040
|
-
"sha256": "
|
|
7050
|
+
"sha256": "c5efc01e3be9725d2f78d3cd5f89db68731c2958c627374fc005b5afb1fdc51f"
|
|
7041
7051
|
},
|
|
7042
7052
|
{
|
|
7043
7053
|
"path": "modules/platform-identity/origin-containment.js",
|
|
@@ -7047,7 +7057,7 @@
|
|
|
7047
7057
|
{
|
|
7048
7058
|
"path": "modules/platform-identity/platform-identity.js",
|
|
7049
7059
|
"mode": "0000644",
|
|
7050
|
-
"sha256": "
|
|
7060
|
+
"sha256": "9877fd84f74545d96473651cca9d00a76712507e76962c1891d3dd0c3d47e7b2"
|
|
7051
7061
|
},
|
|
7052
7062
|
{
|
|
7053
7063
|
"path": "modules/platform-identity/pollers/code-sweep.js",
|
|
@@ -7122,7 +7132,7 @@
|
|
|
7122
7132
|
{
|
|
7123
7133
|
"path": "modules/platform-identity/routes/my-projects.js",
|
|
7124
7134
|
"mode": "0000644",
|
|
7125
|
-
"sha256": "
|
|
7135
|
+
"sha256": "c9a3541f976aa872d4645dccb6101b5ca046a7ea963b1665e9cb6ed9d3796f5c"
|
|
7126
7136
|
},
|
|
7127
7137
|
{
|
|
7128
7138
|
"path": "modules/platform-identity/routes/profile.js",
|
|
@@ -7132,7 +7142,7 @@
|
|
|
7132
7142
|
{
|
|
7133
7143
|
"path": "modules/platform-identity/routes/projects.js",
|
|
7134
7144
|
"mode": "0000644",
|
|
7135
|
-
"sha256": "
|
|
7145
|
+
"sha256": "094c1d54e11794acb09066d0d95b18dbe9100f8b87a67cd7eb1f56be30026f8e"
|
|
7136
7146
|
},
|
|
7137
7147
|
{
|
|
7138
7148
|
"path": "modules/platform-identity/routes/public-profile.js",
|
|
@@ -7144,6 +7154,11 @@
|
|
|
7144
7154
|
"mode": "0000644",
|
|
7145
7155
|
"sha256": "3fbaaafe605fd891c449499ca85a716f9b8780bb1869f25b32162593ada8fedd"
|
|
7146
7156
|
},
|
|
7157
|
+
{
|
|
7158
|
+
"path": "modules/platform-identity/routes/sso-invites.js",
|
|
7159
|
+
"mode": "0000644",
|
|
7160
|
+
"sha256": "af4efb51c20e8cfc3827fbebb17918bcae08f4b07cab982005f52ec34c7c1978"
|
|
7161
|
+
},
|
|
7147
7162
|
{
|
|
7148
7163
|
"path": "modules/platform-identity/routes/sso.js",
|
|
7149
7164
|
"mode": "0000644",
|
|
@@ -7172,7 +7187,7 @@
|
|
|
7172
7187
|
{
|
|
7173
7188
|
"path": "modules/platform-identity/tests/platform-identity.mjs",
|
|
7174
7189
|
"mode": "0000644",
|
|
7175
|
-
"sha256": "
|
|
7190
|
+
"sha256": "d9164f9ae4188a313ce55ebc7c670531aa4e47128f85571aaa55730dbabd1dd3"
|
|
7176
7191
|
},
|
|
7177
7192
|
{
|
|
7178
7193
|
"path": "modules/platform-identity/ui/.gitkeep",
|
|
@@ -7787,7 +7802,7 @@
|
|
|
7787
7802
|
{
|
|
7788
7803
|
"path": "modules/public-landing/public/assets/cosmos.css",
|
|
7789
7804
|
"mode": "0000644",
|
|
7790
|
-
"sha256": "
|
|
7805
|
+
"sha256": "865e576b309a0ad4b160dc10ff8907e2a6210d2aaea7138155d70143caf7686c"
|
|
7791
7806
|
},
|
|
7792
7807
|
{
|
|
7793
7808
|
"path": "modules/public-landing/public/assets/verro-medallion.png",
|
|
@@ -8047,7 +8062,7 @@
|
|
|
8047
8062
|
{
|
|
8048
8063
|
"path": "modules/public-landing/public/projects.html",
|
|
8049
8064
|
"mode": "0000644",
|
|
8050
|
-
"sha256": "
|
|
8065
|
+
"sha256": "b2780277742b8f560dc431ce77498ffdefaf1a027ffde893caa472e59da512f3"
|
|
8051
8066
|
},
|
|
8052
8067
|
{
|
|
8053
8068
|
"path": "modules/public-landing/public/projects.probes.json",
|
|
@@ -8057,7 +8072,7 @@
|
|
|
8057
8072
|
{
|
|
8058
8073
|
"path": "modules/public-landing/public/projects.states.json",
|
|
8059
8074
|
"mode": "0000644",
|
|
8060
|
-
"sha256": "
|
|
8075
|
+
"sha256": "5ce71b5f4c9cf3b9c955bd6e86a8f9b2c8fc53a917955a57577272972ab31693"
|
|
8061
8076
|
},
|
|
8062
8077
|
{
|
|
8063
8078
|
"path": "modules/public-landing/public/terms.html",
|
|
@@ -8582,7 +8597,7 @@
|
|
|
8582
8597
|
{
|
|
8583
8598
|
"path": "modules/ui-design/kit/serve.js",
|
|
8584
8599
|
"mode": "0000644",
|
|
8585
|
-
"sha256": "
|
|
8600
|
+
"sha256": "9b74bbbdaeee790c88a0f6153ff12f8e7062442257542f308a4a9ab188bbbe2d"
|
|
8586
8601
|
},
|
|
8587
8602
|
{
|
|
8588
8603
|
"path": "modules/ui-design/kit/tells.js",
|
|
@@ -8892,12 +8907,12 @@
|
|
|
8892
8907
|
{
|
|
8893
8908
|
"path": "package-lock.json",
|
|
8894
8909
|
"mode": "0000644",
|
|
8895
|
-
"sha256": "
|
|
8910
|
+
"sha256": "a4c779e1257097ccfca882fff19eb4475df55ddcec186db467d2efa021e2f32d"
|
|
8896
8911
|
},
|
|
8897
8912
|
{
|
|
8898
8913
|
"path": "package.json",
|
|
8899
8914
|
"mode": "0000644",
|
|
8900
|
-
"sha256": "
|
|
8915
|
+
"sha256": "90060e42b238486c38227cafbb5d7026e8d4283a3b96508f1617d69f524cdefe"
|
|
8901
8916
|
},
|
|
8902
8917
|
{
|
|
8903
8918
|
"path": "public-docs/index.html",
|
|
@@ -8917,7 +8932,7 @@
|
|
|
8917
8932
|
{
|
|
8918
8933
|
"path": "release-notes.json",
|
|
8919
8934
|
"mode": "0000644",
|
|
8920
|
-
"sha256": "
|
|
8935
|
+
"sha256": "f92a265951d8ff9531bca0803411c8f1a156d3a034c3125a080cf9c65c66c3c5"
|
|
8921
8936
|
},
|
|
8922
8937
|
{
|
|
8923
8938
|
"path": "scripts/bongos-mcp.js",
|
|
@@ -10697,7 +10712,7 @@
|
|
|
10697
10712
|
{
|
|
10698
10713
|
"path": "src/bongos/auth-admission.js",
|
|
10699
10714
|
"mode": "0000644",
|
|
10700
|
-
"sha256": "
|
|
10715
|
+
"sha256": "5e8147efb07016de8648dc6d3dc09d437b27dfc768484bc0aa94582acb6774a2"
|
|
10701
10716
|
},
|
|
10702
10717
|
{
|
|
10703
10718
|
"path": "src/bongos/auth-config.js",
|
|
@@ -10722,7 +10737,7 @@
|
|
|
10722
10737
|
{
|
|
10723
10738
|
"path": "src/bongos/auth.js",
|
|
10724
10739
|
"mode": "0000644",
|
|
10725
|
-
"sha256": "
|
|
10740
|
+
"sha256": "fad15cdbd6652b2a699f48769baaaed0e59314ac6bef326e663d6c7b06b1eaae"
|
|
10726
10741
|
},
|
|
10727
10742
|
{
|
|
10728
10743
|
"path": "src/bongos/cascade-dispatch.js",
|
|
@@ -11032,7 +11047,7 @@
|
|
|
11032
11047
|
{
|
|
11033
11048
|
"path": "src/module-api.js",
|
|
11034
11049
|
"mode": "0000644",
|
|
11035
|
-
"sha256": "
|
|
11050
|
+
"sha256": "8d944dcbb15babff01a10b3805ccc2fbe79ff903067af22afde12a8a1447b580"
|
|
11036
11051
|
},
|
|
11037
11052
|
{
|
|
11038
11053
|
"path": "src/module-loader/catalog.js",
|
|
@@ -11107,7 +11122,7 @@
|
|
|
11107
11122
|
{
|
|
11108
11123
|
"path": "tests/access_requests_invite.mjs",
|
|
11109
11124
|
"mode": "0000644",
|
|
11110
|
-
"sha256": "
|
|
11125
|
+
"sha256": "1044d48504923ec03a8a08303fa12612b2fc02abed32d85be26bf8d5615807ad"
|
|
11111
11126
|
},
|
|
11112
11127
|
{
|
|
11113
11128
|
"path": "tests/accessibility_replay.mjs",
|
|
@@ -11327,7 +11342,7 @@
|
|
|
11327
11342
|
{
|
|
11328
11343
|
"path": "tests/application_lifecycle.mjs",
|
|
11329
11344
|
"mode": "0000644",
|
|
11330
|
-
"sha256": "
|
|
11345
|
+
"sha256": "d35db1b125eaee28d3e0af9c1354b36f589a74bca922b22b507fc29b48bb38c4"
|
|
11331
11346
|
},
|
|
11332
11347
|
{
|
|
11333
11348
|
"path": "tests/architect.mjs",
|
|
@@ -11702,7 +11717,7 @@
|
|
|
11702
11717
|
{
|
|
11703
11718
|
"path": "tests/catalog_only_client.mjs",
|
|
11704
11719
|
"mode": "0000644",
|
|
11705
|
-
"sha256": "
|
|
11720
|
+
"sha256": "ea7d5c8546966c84ce8831484355f059b7dc42558954090afa90e17d89165454"
|
|
11706
11721
|
},
|
|
11707
11722
|
{
|
|
11708
11723
|
"path": "tests/category_advisory_guard.mjs",
|
|
@@ -13349,6 +13364,11 @@
|
|
|
13349
13364
|
"mode": "0000644",
|
|
13350
13365
|
"sha256": "5daff8488df269752d2e5992e60a03bb75cf85a5b8d5fcaa15703b12a5bb6b94"
|
|
13351
13366
|
},
|
|
13367
|
+
{
|
|
13368
|
+
"path": "tests/hub_invite_notices.mjs",
|
|
13369
|
+
"mode": "0000644",
|
|
13370
|
+
"sha256": "c79b4101c944110166da2e158bada2192235897887b65bbb7b1f9fae3c61eb13"
|
|
13371
|
+
},
|
|
13352
13372
|
{
|
|
13353
13373
|
"path": "tests/hub_map_orb_states.mjs",
|
|
13354
13374
|
"mode": "0000644",
|
|
@@ -13957,7 +13977,7 @@
|
|
|
13957
13977
|
{
|
|
13958
13978
|
"path": "tests/module_api.mjs",
|
|
13959
13979
|
"mode": "0000644",
|
|
13960
|
-
"sha256": "
|
|
13980
|
+
"sha256": "be32afe74b2ecd47f136a824c9997b54274b72d41dfe5cd8d1e540e9a05f83f4"
|
|
13961
13981
|
},
|
|
13962
13982
|
{
|
|
13963
13983
|
"path": "tests/module_api_lazy.mjs",
|
|
@@ -14072,7 +14092,7 @@
|
|
|
14072
14092
|
{
|
|
14073
14093
|
"path": "tests/my_projects_surface.mjs",
|
|
14074
14094
|
"mode": "0000644",
|
|
14075
|
-
"sha256": "
|
|
14095
|
+
"sha256": "9055da573a4e210daf6d2910938df77f711f54a6701a33ebc8995ef11a95fc52"
|
|
14076
14096
|
},
|
|
14077
14097
|
{
|
|
14078
14098
|
"path": "tests/nav_permission_atoms.mjs",
|
|
@@ -14447,7 +14467,7 @@
|
|
|
14447
14467
|
{
|
|
14448
14468
|
"path": "tests/project_invite_ownership.mjs",
|
|
14449
14469
|
"mode": "0000644",
|
|
14450
|
-
"sha256": "
|
|
14470
|
+
"sha256": "bc77a25e814de321faae41780b83504bb4ce87a73bb944d0d231a301532e5364"
|
|
14451
14471
|
},
|
|
14452
14472
|
{
|
|
14453
14473
|
"path": "tests/project_invite_suggestions.mjs",
|
|
@@ -14457,7 +14477,7 @@
|
|
|
14457
14477
|
{
|
|
14458
14478
|
"path": "tests/project_invite_ui.mjs",
|
|
14459
14479
|
"mode": "0000644",
|
|
14460
|
-
"sha256": "
|
|
14480
|
+
"sha256": "2b6d62d93878b43896dbe17247e7879ac86c842d9300cdf34876c5010ab02d1f"
|
|
14461
14481
|
},
|
|
14462
14482
|
{
|
|
14463
14483
|
"path": "tests/project_join_door.mjs",
|
|
@@ -16047,7 +16067,7 @@
|
|
|
16047
16067
|
{
|
|
16048
16068
|
"path": "tests/watch_applications_queue.mjs",
|
|
16049
16069
|
"mode": "0000644",
|
|
16050
|
-
"sha256": "
|
|
16070
|
+
"sha256": "6f0dc184d552c4db4048894286cff96ade91e44c8070fae66fdeef324ba09a7d"
|
|
16051
16071
|
},
|
|
16052
16072
|
{
|
|
16053
16073
|
"path": "tests/watch_roster_self_row.mjs",
|
|
@@ -5,7 +5,7 @@ A **generated**, zero-dependency typed client for the Bongos API — produced fr
|
|
|
5
5
|
by hand; it regenerates when the spec changes, so it can never drift from the routes.
|
|
6
6
|
|
|
7
7
|
- API version: **v1** (served at `/api/bongos/v1`)
|
|
8
|
-
-
|
|
8
|
+
- 463 operations across 70 resource groups
|
|
9
9
|
|
|
10
10
|
## Use it from your project
|
|
11
11
|
|
|
@@ -729,8 +729,6 @@ function createClient(opts = {}) {
|
|
|
729
729
|
postMyProjectsClientIdLeave: (args) => request("POST", "/my-projects/{clientId}/leave", { hasBody: true }, args),
|
|
730
730
|
// GET /my-projects/invites — rank: any-builder — GET /my-projects/invites
|
|
731
731
|
getMyProjectsInvites: (args) => request("GET", "/my-projects/invites", { hasBody: false }, args),
|
|
732
|
-
// POST /my-projects/invites/{clientId}/accept — rank: any-builder — POST /my-projects/invites/:clientId/accept
|
|
733
|
-
postMyProjectsInvitesClientIdAccept: (args) => request("POST", "/my-projects/invites/{clientId}/accept", { hasBody: true }, args),
|
|
734
732
|
// POST /my-projects/invites/{clientId}/decline — rank: any-builder — POST /my-projects/invites/:clientId/decline
|
|
735
733
|
postMyProjectsInvitesClientIdDecline: (args) => request("POST", "/my-projects/invites/{clientId}/decline", { hasBody: true }, args),
|
|
736
734
|
// POST /my-projects/join — rank: any-builder — POST /my-projects/join
|
|
@@ -1015,6 +1013,10 @@ function createClient(opts = {}) {
|
|
|
1015
1013
|
postSsoDevicePoll: (args) => request("POST", "/sso/device/poll", { hasBody: true }, args),
|
|
1016
1014
|
// POST /sso/device/start — rank: public — POST /sso/device/start
|
|
1017
1015
|
postSsoDeviceStart: (args) => request("POST", "/sso/device/start", { hasBody: true }, args),
|
|
1016
|
+
// POST /sso/invites/clear — rank: public — POST /sso/invites/clear
|
|
1017
|
+
postSsoInvitesClear: (args) => request("POST", "/sso/invites/clear", { hasBody: true }, args),
|
|
1018
|
+
// POST /sso/invites/notify — rank: public — POST /sso/invites/notify
|
|
1019
|
+
postSsoInvitesNotify: (args) => request("POST", "/sso/invites/notify", { hasBody: true }, args),
|
|
1018
1020
|
// POST /sso/membership/check-in — rank: public — POST /sso/membership/check-in
|
|
1019
1021
|
postSsoMembershipCheckIn: (args) => request("POST", "/sso/membership/check-in", { hasBody: true }, args),
|
|
1020
1022
|
// GET /sso/pubkey — rank: public — GET /sso/pubkey
|
|
@@ -728,8 +728,6 @@ function createClient(opts = {}) {
|
|
|
728
728
|
postMyProjectsClientIdLeave: (args) => request("POST", "/my-projects/{clientId}/leave", { hasBody: true }, args),
|
|
729
729
|
// GET /my-projects/invites — rank: any-builder — GET /my-projects/invites
|
|
730
730
|
getMyProjectsInvites: (args) => request("GET", "/my-projects/invites", { hasBody: false }, args),
|
|
731
|
-
// POST /my-projects/invites/{clientId}/accept — rank: any-builder — POST /my-projects/invites/:clientId/accept
|
|
732
|
-
postMyProjectsInvitesClientIdAccept: (args) => request("POST", "/my-projects/invites/{clientId}/accept", { hasBody: true }, args),
|
|
733
731
|
// POST /my-projects/invites/{clientId}/decline — rank: any-builder — POST /my-projects/invites/:clientId/decline
|
|
734
732
|
postMyProjectsInvitesClientIdDecline: (args) => request("POST", "/my-projects/invites/{clientId}/decline", { hasBody: true }, args),
|
|
735
733
|
// POST /my-projects/join — rank: any-builder — POST /my-projects/join
|
|
@@ -1014,6 +1012,10 @@ function createClient(opts = {}) {
|
|
|
1014
1012
|
postSsoDevicePoll: (args) => request("POST", "/sso/device/poll", { hasBody: true }, args),
|
|
1015
1013
|
// POST /sso/device/start — rank: public — POST /sso/device/start
|
|
1016
1014
|
postSsoDeviceStart: (args) => request("POST", "/sso/device/start", { hasBody: true }, args),
|
|
1015
|
+
// POST /sso/invites/clear — rank: public — POST /sso/invites/clear
|
|
1016
|
+
postSsoInvitesClear: (args) => request("POST", "/sso/invites/clear", { hasBody: true }, args),
|
|
1017
|
+
// POST /sso/invites/notify — rank: public — POST /sso/invites/notify
|
|
1018
|
+
postSsoInvitesNotify: (args) => request("POST", "/sso/invites/notify", { hasBody: true }, args),
|
|
1017
1019
|
// POST /sso/membership/check-in — rank: public — POST /sso/membership/check-in
|
|
1018
1020
|
postSsoMembershipCheckIn: (args) => request("POST", "/sso/membership/check-in", { hasBody: true }, args),
|
|
1019
1021
|
// GET /sso/pubkey — rank: public — GET /sso/pubkey
|
|
@@ -382,13 +382,13 @@ export interface PostModulesKeyEnableResponse { ok: boolean; key: unknown; enabl
|
|
|
382
382
|
export interface PostModulesKeySubmitRequest { signed_by?: string }
|
|
383
383
|
export interface PostModulesKeySubmitResponse { ok: boolean; submission: unknown }
|
|
384
384
|
export interface PostMyProjectsClientIdLeaveResponse { ok: boolean; left: unknown }
|
|
385
|
-
export interface PostMyProjectsInvitesClientIdAcceptResponse { ok: boolean; accepted: boolean }
|
|
386
385
|
export interface PostMyProjectsInvitesClientIdDeclineResponse { ok: boolean; accepted: boolean }
|
|
387
386
|
export interface PostMyProjectsJoinRequest { origin: string; note?: string }
|
|
388
387
|
export interface PostMyProjectsJoinResponse { ok: boolean; status: unknown; project: unknown; message: unknown }
|
|
389
388
|
export interface PostNpmReleasePreviewRequest { version: string }
|
|
390
389
|
export interface PostOverrideRequestsIdDecideRequest { decision: string; note?: string }
|
|
391
390
|
export interface PostProjectsInviteRequest { client_id: string; github_login: string }
|
|
391
|
+
export interface PostProjectsInviteResponse { invited: boolean; invite_url: unknown }
|
|
392
392
|
export interface PostProjectsProjectIdSpecialitiesRequest { name: string; discipline: string; summary?: string; contract_md?: string; visibility?: string; sellable?: boolean; skills?: string[] }
|
|
393
393
|
export interface PostProjectsProjectIdSpecialitiesResponse { speciality: unknown }
|
|
394
394
|
export interface PostProjectsRequest { origin: string; name: string; tagline?: string; description?: string; art_url?: string; type?: string; template?: string }
|
|
@@ -443,6 +443,8 @@ export interface PostSsoApplicantProfileRequest { client_id: string; client_secr
|
|
|
443
443
|
export interface PostSsoApplicantProfileResponse { profile: unknown }
|
|
444
444
|
export interface PostSsoDevicePollRequest { device_code: string }
|
|
445
445
|
export interface PostSsoDeviceStartRequest { origin: string }
|
|
446
|
+
export interface PostSsoInvitesClearRequest { client_id: string; client_secret: string; github_login: string }
|
|
447
|
+
export interface PostSsoInvitesNotifyRequest { client_id: string; client_secret: string; github_login: string }
|
|
446
448
|
export interface PostSsoMembershipCheckInRequest { client_id: string; client_secret: string; github_id: StringifiableId; github_login: string; display_name?: string; avatar_url?: string; membership_kind?: string }
|
|
447
449
|
export interface PostSsoMembershipCheckInResponse { ok: boolean }
|
|
448
450
|
export interface PostSsoTokenRequest { client_id: string; client_secret: string; code: string; redirect_uri: string }
|
|
@@ -1156,8 +1158,6 @@ export interface BongosClient {
|
|
|
1156
1158
|
postMyProjectsClientIdLeave(args?: RequestArgs): Promise<PostMyProjectsClientIdLeaveResponse>;
|
|
1157
1159
|
/** GET /my-projects/invites — rank: any-builder */
|
|
1158
1160
|
getMyProjectsInvites(args?: RequestArgs): Promise<GetMyProjectsInvitesResponse>;
|
|
1159
|
-
/** POST /my-projects/invites/{clientId}/accept — rank: any-builder */
|
|
1160
|
-
postMyProjectsInvitesClientIdAccept(args?: RequestArgs): Promise<PostMyProjectsInvitesClientIdAcceptResponse>;
|
|
1161
1161
|
/** POST /my-projects/invites/{clientId}/decline — rank: any-builder */
|
|
1162
1162
|
postMyProjectsInvitesClientIdDecline(args?: RequestArgs): Promise<PostMyProjectsInvitesClientIdDeclineResponse>;
|
|
1163
1163
|
/** POST /my-projects/join — rank: any-builder */
|
|
@@ -1209,7 +1209,7 @@ export interface BongosClient {
|
|
|
1209
1209
|
/** GET /projects/featured — rank: public */
|
|
1210
1210
|
getProjectsFeatured(args?: RequestArgs): Promise<GetProjectsFeaturedResponse>;
|
|
1211
1211
|
/** POST /projects/invite — rank: metic+archon */
|
|
1212
|
-
postProjectsInvite(args: RequestArgs & { body: PostProjectsInviteRequest }): Promise<
|
|
1212
|
+
postProjectsInvite(args: RequestArgs & { body: PostProjectsInviteRequest }): Promise<PostProjectsInviteResponse>;
|
|
1213
1213
|
};
|
|
1214
1214
|
"provisioning": {
|
|
1215
1215
|
/** GET /provisioning/control-plane — rank: archon */
|
|
@@ -1442,6 +1442,10 @@ export interface BongosClient {
|
|
|
1442
1442
|
postSsoDevicePoll(args: RequestArgs & { body: PostSsoDevicePollRequest }): Promise<ApiResponse>;
|
|
1443
1443
|
/** POST /sso/device/start — rank: public */
|
|
1444
1444
|
postSsoDeviceStart(args: RequestArgs & { body: PostSsoDeviceStartRequest }): Promise<ApiResponse>;
|
|
1445
|
+
/** POST /sso/invites/clear — rank: public */
|
|
1446
|
+
postSsoInvitesClear(args: RequestArgs & { body: PostSsoInvitesClearRequest }): Promise<ApiResponse>;
|
|
1447
|
+
/** POST /sso/invites/notify — rank: public */
|
|
1448
|
+
postSsoInvitesNotify(args: RequestArgs & { body: PostSsoInvitesNotifyRequest }): Promise<ApiResponse>;
|
|
1445
1449
|
/** POST /sso/membership/check-in — rank: public */
|
|
1446
1450
|
postSsoMembershipCheckIn(args: RequestArgs & { body: PostSsoMembershipCheckInRequest }): Promise<PostSsoMembershipCheckInResponse>;
|
|
1447
1451
|
/** GET /sso/pubkey — rank: public */
|
|
@@ -725,8 +725,6 @@ export function createClient(opts = {}) {
|
|
|
725
725
|
postMyProjectsClientIdLeave: (args) => request("POST", "/my-projects/{clientId}/leave", { hasBody: true }, args),
|
|
726
726
|
// GET /my-projects/invites — rank: any-builder — GET /my-projects/invites
|
|
727
727
|
getMyProjectsInvites: (args) => request("GET", "/my-projects/invites", { hasBody: false }, args),
|
|
728
|
-
// POST /my-projects/invites/{clientId}/accept — rank: any-builder — POST /my-projects/invites/:clientId/accept
|
|
729
|
-
postMyProjectsInvitesClientIdAccept: (args) => request("POST", "/my-projects/invites/{clientId}/accept", { hasBody: true }, args),
|
|
730
728
|
// POST /my-projects/invites/{clientId}/decline — rank: any-builder — POST /my-projects/invites/:clientId/decline
|
|
731
729
|
postMyProjectsInvitesClientIdDecline: (args) => request("POST", "/my-projects/invites/{clientId}/decline", { hasBody: true }, args),
|
|
732
730
|
// POST /my-projects/join — rank: any-builder — POST /my-projects/join
|
|
@@ -1011,6 +1009,10 @@ export function createClient(opts = {}) {
|
|
|
1011
1009
|
postSsoDevicePoll: (args) => request("POST", "/sso/device/poll", { hasBody: true }, args),
|
|
1012
1010
|
// POST /sso/device/start — rank: public — POST /sso/device/start
|
|
1013
1011
|
postSsoDeviceStart: (args) => request("POST", "/sso/device/start", { hasBody: true }, args),
|
|
1012
|
+
// POST /sso/invites/clear — rank: public — POST /sso/invites/clear
|
|
1013
|
+
postSsoInvitesClear: (args) => request("POST", "/sso/invites/clear", { hasBody: true }, args),
|
|
1014
|
+
// POST /sso/invites/notify — rank: public — POST /sso/invites/notify
|
|
1015
|
+
postSsoInvitesNotify: (args) => request("POST", "/sso/invites/notify", { hasBody: true }, args),
|
|
1014
1016
|
// POST /sso/membership/check-in — rank: public — POST /sso/membership/check-in
|
|
1015
1017
|
postSsoMembershipCheckIn: (args) => request("POST", "/sso/membership/check-in", { hasBody: true }, args),
|
|
1016
1018
|
// GET /sso/pubkey — rank: public — GET /sso/pubkey
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
- **Date:** 2026-09-24
|
|
5
5
|
- **Task:** [task 1002813](https://cloudbongos.com/builders#/task/1002813) (R1 of the project admin console wave, goal 1000110 — BONGOS-V2)
|
|
6
6
|
- **Deciders:** the three decisions come from the spike ([task 1002786](https://cloudbongos.com/builders#/task/1002786), [`docs/specs/<redacted>.md`](../specs/<redacted>.md), 2026-08-14). The spike asked the owner for one sign-off: what "widenable to metics" means. The owner answered on blocker 1000122, and [ADR 0217](0217-rank-gates-inviting-not-viewing.md) D1 records the answer: a per-project invite floor that defaults to archon and is set by that project's own archons. This ADR does three things. It records the decisions, reconciles them with what has shipped in the six weeks since, and says what each build task still has to build.
|
|
7
|
+
- **Followed by:** [ADR 0353](0353-a-hub-invite-is-a-notice-of-the-projects-own-invite.md) — R6 (task 1002818): the hub's invite row became a notice the project writes, the hub-side accept was retired, and the wizard's invite step hands the owner to the project's hall. The "Until R6 lands" item below is closed by it.
|
|
7
8
|
- **Supersedes:** only the "Invitations (deferred slice)" paragraph of [ADR 0141](<redacted>.md) §5. The rest of ADR 0141 stands.
|
|
8
9
|
- **Revises:** [ADR 0210](0210-scouting-graduates-on-an-instance-asserted-owner-claim.md), [ADR 0215](0215-the-recruiter-sliver-is-a-ceiling-not-a-step.md) and [ADR 0248](0248-a-hub-project-s-owner-is-the-provisioning-row-carried-on-a-port.md) predicted that `project.recruit` would replace `project.curate` on the hub's recruiting doors. D3.6 revises that prediction.
|
|
9
10
|
- **Related:** [ADR 0016](0016-trust-boundary-server-enforced-permissions.md), [0108](0108-instance-composition-configurable-root.md), [0112](0112-private-goals-invitations-join-requests.md), [0151](0151-governance-permissions-as-atom-ranks-as-roles.md), [0157](0157-archon-is-rank-and-identity-only.md), [0182](0182-project-visibility-two-axes.md), [0194](0194-joinability-one-door-decision.md), [0201](0201-cross-project-application-vouched-row-live-record.md), [0247](0247-the-join-door-is-one-composed-answer-and-dark-matter-has-no-live-verdict-yet.md), [0328](0328-a-hosted-projects-authority-is-its-membership-row-not-its-owner-column.md)
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# ADR 0353 — A hub invite is a notice of the project's own invite, and signing in accepts it
|
|
2
|
+
|
|
3
|
+
- **Status:** accepted
|
|
4
|
+
- **Date:** 2026-09-30
|
|
5
|
+
- **Task:** [task 1002818](https://cloudbongos.com/builders#/task/1002818) (R6 of the project admin console wave, goal 1000110 — BONGOS-V2)
|
|
6
|
+
- **Builds on:** [ADR 0335](0335-an-invite-is-a-pre-approved-row-on-the-projects-own-instance.md) D1/D2 (authority lives on the project's own instance; an invite is a pre-approved `access_requests` row; signing in is the acceptance act)
|
|
7
|
+
- **Interacts with:** [ADR 0141](<redacted>.md) §3 (membership check-in), [ADR 0205](0205-federated-checkin-needs-the-hubs-own-signin-witness.md) (client credentials never mint a membership), [ADR 0352](0352-a-rollup-report-carries-its-as-of-in-a-header.md) (hub↔instance wire skew)
|
|
8
|
+
- **Decided with:** the owner chose the split in D3 on 2026-09-30, over building the signed-assertion relay in this task.
|
|
9
|
+
|
|
10
|
+
## Context
|
|
11
|
+
|
|
12
|
+
ADR 0335 left one dead end that real users could reach. The hub had its own invite, `POST /projects/invite`, called from the creation wizard's invite step and from a project's manage page. It wrote a `pending` row in `<redacted>`, and accepting it (`POST /my-projects/invites/:clientId/accept`) flipped that row to `member`. No project reads that table to decide who may enter. So the invitee "accepted" and then met the project's own sign-in gate, which knew nothing about them. A hosted project's join door defaults to `apply`, so this was the common case, not an edge case.
|
|
13
|
+
|
|
14
|
+
The project's own invite (`POST /access-requests/invite`, R2) does admit: it writes the `invited` row the sign-in gate reads. But nothing told the invitee it existed, and rescinding it left nothing to clean up on the hub because the hub had never heard of it.
|
|
15
|
+
|
|
16
|
+
The task's done-when: **no state where an accepted invite leads to a locked door.**
|
|
17
|
+
|
|
18
|
+
## Decision
|
|
19
|
+
|
|
20
|
+
### D1 — The hub's `pending` row is a NOTICE of the project's own invite
|
|
21
|
+
|
|
22
|
+
The row stays, and My Projects lists it as before, but only the project writes it now. Two new server-to-server routes, authenticated by `client_id` + `client_secret` like the membership check-in:
|
|
23
|
+
|
|
24
|
+
| Route | When the project calls it | What the hub does |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| `POST /sso/invites/notify` | every outcome of its invite route that leaves the login holding an `invited` row: a fresh invite, a pending application approved in place, and a re-invite of one already standing | `INSERT … 'pending' ON CONFLICT DO NOTHING` for the account that login resolves to |
|
|
27
|
+
| `POST /sso/invites/clear` | a dismissal after which the login holds no `invited` row | `DELETE` that `pending` row, and only a `pending` one |
|
|
28
|
+
|
|
29
|
+
Both calls touch a `pending` row only, so client credentials still cannot create, change or remove a membership (ADR 0205). A login with no hub account is a no-op, because there is nobody on the hub to notify; the project's row still admits them. **The answer is always `{ ok: true }` once the client authenticates**, whether or not anything was written, so a project cannot use the route to learn which GitHub logins have platform accounts.
|
|
30
|
+
|
|
31
|
+
The sender is core's `notifyHubOfInvite` (`src/bongos/auth-admission.js`). It sits in the kernel beside the other hub calls that hold the client secret, and modules reach it as a narrow doorway port. It is fire-and-forget, time-bounded and swallows every failure: an invite must never wait on the hub or fail because of it. Re-inviting is how a notice the hub missed is healed, since the hub's insert is idempotent.
|
|
32
|
+
|
|
33
|
+
Only a dismissal that leaves no `invited` row clears the notice. If two archons invited the same login and one rescinds, the other invite still admits, so the notice stays. A declined application also sends a clear. That is a no-op unless the hub still holds a pre-ADR-0353 row for that login, which would be a dead end anyway.
|
|
34
|
+
|
|
35
|
+
### D2 — Accepting is signing in to the project; the hub-side accept is retired
|
|
36
|
+
|
|
37
|
+
`POST /my-projects/invites/:clientId/accept` is deleted, and `resolveInvite` becomes the decline-only `removePendingInvite`. The hub page has not called accept since the invitations list came back (task 1003774): each invitation renders as **Open project →** with "Sign in there to accept." What was missing was the server half, so no caller (an old cached page, a script) can still flip a row and admit nobody.
|
|
38
|
+
|
|
39
|
+
Signing in closes the loop without any new code. The hub's own sign-in witness (`recordFederatedSignin`, stamped at `/sso/token` and `/sso/device/poll`) already turns a `pending` row into `member` when the invitee signs in to that project.
|
|
40
|
+
|
|
41
|
+
### D3 — The hub's own invite writes nothing; it hands the owner to the project's hall
|
|
42
|
+
|
|
43
|
+
`POST /projects/invite` keeps its owner-or-curator gate and its byte-identical 403, and it still resolves the catalog client. It no longer writes. It answers `200 { invited: false, invite_url }`: the project's hall invite box with the login filled in, `<origin>/builders/watch?invite=<login>`. A 200 rather than a refusal, because it is the answer every admitted caller gets, and a 4xx on that path would log a failed request in the owner's browser on every invite. Every instance already canonicalises `/builders/watch` to its hall (a one-host instance serves `/watch`, a two-host one redirects to `builders.<apex>/watch`, both keeping the query), so the hub needs no hall address of its own. The sign-in redirect carries the full URL, so the name survives signing in.
|
|
44
|
+
|
|
45
|
+
The wizard and manage-page widget keep their field, their suggestions and their Skip button. Each name becomes a line reading "not sent yet — finish it on your project's hall" with a **Send the invite →** link. It is a link the owner clicks, never a window opened for them: an open triggered by a network answer is blocked by browsers, and a surprise navigation would lose the wizard.
|
|
46
|
+
|
|
47
|
+
The hall's Watch page reads `?invite=` once, only if it is a valid GitHub username, and **only fills the box**. Sending stays the owner's click, because a link anyone can craft must never be the act that admits.
|
|
48
|
+
|
|
49
|
+
The owner chose this over building the relay ADR 0335 foresaw (see Alternatives) in this task.
|
|
50
|
+
|
|
51
|
+
### D4 — Wire skew (ADR 0352's rule)
|
|
52
|
+
|
|
53
|
+
The two notice routes are **new**. An older hub answers 404, the sender swallows it, and the invite still admits: only the notice is lost. A newer hub with an older instance simply receives no notices. Each route's body is validated strictly (three fields), so a field added later must ride a header, or ship on the hub and be released before any instance sends it.
|
|
54
|
+
|
|
55
|
+
## Consequences
|
|
56
|
+
|
|
57
|
+
- **The done-when holds for every invite created from now on.** A hub notice exists only because the project wrote an `invited` row, and a rescind that closes the door removes the notice. The hub's own invite creates no row at all.
|
|
58
|
+
- **Rows written before this change are untouched.** Pending rows the old hub invite wrote still render as **Open project →**, and on a door other than `open` they still lead to the project's gate. The invitee can decline them, and the project can clear one by inviting and then rescinding. Deleting them at deploy would need a data migration, and it would also delete notices that do work (on `open` doors). That is left to the owner (see the task's handoff), not done silently here.
|
|
59
|
+
- **The hub's invite now reaches people with no platform account.** The project's own invite takes any GitHub login, so the old `account_not_found` refusal is gone. Such an invitee sees no hub notice, because there is no account to show it to, but they are admitted when they sign in.
|
|
60
|
+
- **Three things still bypass the notice**, all by scope: the reviewer queue's approve (`PATCH` pending → invited), the Discord approval reaction, and R3's future recruiter rescind route. The first two are approvals of an application the applicant already knows about. R3 (task 1002815) must call the same port when it adds its rescind.
|
|
61
|
+
- **The 30-day invite expiry** (task 1002969) is read-time on the instance. An expired invite's hub notice stays until the invitee declines it or the project re-invites and rescinds. Task 1002969 owns closing that.
|
|
62
|
+
|
|
63
|
+
## Alternatives considered
|
|
64
|
+
|
|
65
|
+
- **The signed-assertion relay (ADR 0335's foreseen design).** The hub mints a new `purpose: 'invite'` token for the inviter, and a new token-authenticated route on the project checks that builder's own permission and writes the real row. It keeps the in-place form, but it adds an authentication path to every instance (security-sensitive), roughly doubles this task, needs a fallback for every older instance, and cannot work until the owner has signed in to a new project once (there is no local builder to check the permission against). The owner deferred it. D3's hand-off leaves room for it: if it is built, the route's `invited: false` becomes `true` for instances that support it.
|
|
66
|
+
- **Seed the hub row only when the project's door is `open`.** An owner who later closes the door strands every such notice in front of a locked door, and a self-hosted project's door is not known to the hub reliably.
|
|
67
|
+
- **Keep the hub-side accept, but have the instance read hub memberships at sign-in.** This puts a hub call inside the admission gate and moves a verdict onto the hub, which ADR 0335 D1 forbids.
|
|
68
|
+
- **A body field on the membership check-in instead of new routes.** The check-in body is validated strictly, so an older hub would 400 the whole check-in (ADR 0352), and a check-in is sent at sign-in, not at invite time.
|
|
69
|
+
|
|
70
|
+
Proof: `tests/hub_invite_notices.mjs` (the hub routes over the real domain functions: pending-only writes keyed on the authenticated client, the identical answer for an unknown login, the credential refusals, the three-field body, the hand-off link, and the sender), `tests/access_requests_invite.mjs` and `tests/application_lifecycle.mjs` (which invite outcomes notify and which dismissals clear), `tests/project_invite_ownership.mjs` and `tests/catalog_only_client.mjs` (the gate is unchanged and nothing is written), `tests/project_invite_ui.mjs` (the hand-off line, no non-http link, no window opened), `tests/watch_applications_queue.mjs` (the prefill fills and never sends), `tests/my_projects_surface.mjs` and `modules/platform-identity/tests/platform-identity.mjs` (the accept route and flip are gone).
|
package/docs/adr/README.md
CHANGED
|
@@ -471,3 +471,4 @@ These 20 numbers are each shared by exactly two files. They are **accepted histo
|
|
|
471
471
|
| 0350 | [**The hub holds a per-project WRITE deploy key, so a hosted project's upgraded pin always reaches its GitHub repo** ([task 1004291](https://cloudbongos.com/builders#/task/1004291), follow-up to task 1004065). Amends ADR 0176. The owner's token lasts one hour and the checkout's own key is read-only, so most hosted upgrades ended "GitHub was not updated". **D1:** a second deploy key per repo, registered `read_only: false` as `cloudbongos-pin-<slug>`, the pull key untouched. **D2:** kept outside every checkout in the runner's own directory (0700 dir, 0600 key, modes verified), unreadable by the project's account. **D3:** minted at standup and whenever a fresh owner token finds none, so reconnecting GitHub once is enough. **D4:** `pushUpgradePin` tries write key, then owner token, then origin, never forced. **D5:** revoked on teardown and disconnect. **D6:** a project on the shared app user is refused a key: "needs its own account first". **D7:** /deploy says reconnecting sets up lasting access.](0350-the-hub-holds-a-per-project-write-deploy-key-so-a-hosted-upgrade-reaches-github.md) | provisioning / security |
|
|
472
472
|
| 0351 | [**A criterion closes on a UAT, not on its linked tasks shipping** ([task 1004392](https://cloudbongos.com/builders#/task/1004392), goal 1000089 — working area 3). Supersedes ADR 0183's close rule. `wa7-government` had closed on six Board Room navigation tasks while nothing it describes was on the live site. **D1:** three checks — Code (linked tasks done, ≥1 shipped; automatic), Live, UAT — and the sweep closes a criterion only with a CURRENT sign-off (no older than the newest linked ship). **D2 (owner):** "Awaiting UAT" is a visible state. **D3 (owner):** one step; the signer is refused if they shipped linked work, the project owner excepted. **D4 (owner):** an mp4/webm up to 100 MB on the project's own server. **D5 (owner):** backend-only is set while a criterion is open and takes a recording-free sign-off. **D6 (owner):** Live is read via the `deploy.taskWhere` port where the site can tell, attested otherwise. **D7 (owner):** already-closed criteria stay closed ("Closed before UAT"). **D8:** `/satisfy` is a recorded override that needs a reason. **D9:** `/goal-uat` + `scripts/gds/uat.js`; the hall is task 1004402.](0351-a-criterion-closes-on-a-uat.md) | lifecycle / criteria / review |
|
|
473
473
|
| 0352 | [**A rollup report carries its "as of" in a header, and the hub keeps the newest** ([task 1004268](https://cloudbongos.com/builders#/task/1004268), goal 1000110). `POST /sso/activity/rollup` was last-writer-wins with no report timestamp, so a sign-in push read earlier and a ship push read later could land the older totals last. **D1:** the instance's read time rides a `Bongos-Report-As-Of` HEADER, not a body field, because the hub validates the body strictly and hub and instances upgrade independently: a body field from a newer instance would 400 on an older hub and lose the report, while an unknown header is ignored. **D2:** missing or unparseable = unstamped = last-writer-wins, never a 400. **D3:** the guard is a WHERE on the upsert's conflict branch (migration 026, `reported_as_of`, no backfill); a skipped update keeps the row lock, so the task 1004244 snapshot stays in step. **D4:** the stamp is compared only with the same client's, and clamped to the hub's `now()`.](0352-a-rollup-report-carries-its-as-of-in-a-header.md) | platform identity / federation |
|
|
474
|
+
| 0353 | [**A hub invite is a notice of the project's own invite, and signing in accepts it** ([task 1002818](https://cloudbongos.com/builders#/task/1002818), goal 1000110 — R6 of the admin console wave). The hub's own invite wrote a `pending` membership that no project reads, so an "accepted" invite met a locked door (new projects default to `apply`). **D1:** that row is now a NOTICE the PROJECT writes: its invite route calls `POST /sso/invites/notify` and a dismissal that leaves no invited row calls `/sso/invites/clear` (client credentials, pending rows only, always `{ ok: true }` so no account oracle); core's `notifyHubOfInvite` is fire-and-forget. **D2:** the hub-side accept route is deleted; signing in to the project accepts, and the hub's own sign-in witness turns the row into a membership. **D3 (owner):** `POST /projects/invite` writes nothing and answers `{ invited: false, invite_url }`, the project's hall invite link (`/builders/watch?invite=<login>`, which only pre-fills); the signed-assertion relay is deferred. **D4:** new routes, so an older hub 404s and only the notice is lost (ADR 0352).](0353-a-hub-invite-is-a-notice-of-the-projects-own-invite.md) | platform identity / federation |
|