@bongos/core 1.19.533 → 1.19.534

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.
Files changed (51) hide show
  1. package/.bongos-core.json +106 -46
  2. package/clients/bongos-client/index.d.ts +1 -1
  3. package/config/branding.neutral.json +3 -2
  4. package/docs/adr/0245-the-way-back-into-an-optional-step-is-that-step-s-own-answer-route.md +144 -0
  5. package/docs/adr/0246-catalog-membership-is-a-projection-not-a-federation-side-effect.md +96 -0
  6. package/docs/adr/0247-the-join-door-is-one-composed-answer-and-dark-matter-has-no-live-verdict-yet.md +202 -0
  7. package/docs/adr/README.md +4 -0
  8. package/docs/api/openapi.json +8 -1
  9. package/docs/api-reference.md +1 -1
  10. package/docs/copy-inventory.md +129 -126
  11. package/docs/copy-registry.json +189 -161
  12. package/docs/file-map.md +1 -0
  13. package/docs/module-api-changelog.md +14 -0
  14. package/modules/onboarding/module.json +1 -1
  15. package/modules/onboarding/routes/access-requests.js +21 -6
  16. package/modules/platform-identity/module.json +1 -1
  17. package/modules/platform-identity/project-catalog.js +202 -0
  18. package/modules/platform-identity/routes/my-projects.js +7 -2
  19. package/modules/platform-identity/routes/projects.js +6 -0
  20. package/modules/platform-identity/routes/sso.js +10 -0
  21. package/modules/provisioning/catalog-bridge.js +105 -0
  22. package/modules/provisioning/provisioning.js +28 -2
  23. package/modules/provisioning/routes/provisioning.js +45 -11
  24. package/modules/provisioning/tests/provisioning.mjs +11 -8
  25. package/modules/public-landing/public/assets/cosmos.css +15 -0
  26. package/modules/public-landing/public/projects.html +162 -3
  27. package/modules/ui-design/kit/serve.js +1 -0
  28. package/package-lock.json +2 -2
  29. package/package.json +1 -1
  30. package/scripts/gds/fitness.js +9 -0
  31. package/scripts/gds/provision-net.js +7 -0
  32. package/src/bongos/project-door.js +138 -0
  33. package/src/branding.js +8 -0
  34. package/src/module-api.js +12 -1
  35. package/tests/access_requests_invite.mjs +2 -2
  36. package/tests/application_lifecycle.mjs +1 -0
  37. package/tests/catalog_only_client.mjs +242 -0
  38. package/tests/currency_label.mjs +8 -5
  39. package/tests/module_api.mjs +1 -0
  40. package/tests/project_catalog_bridge.mjs +355 -0
  41. package/tests/project_catalog_routes.mjs +213 -0
  42. package/tests/project_join_door.mjs +166 -0
  43. package/tests/project_join_door_route.mjs +206 -0
  44. package/tests/project_modules_ui.mjs +318 -0
  45. package/tests/projects_hub.mjs +6 -1
  46. package/tests/projects_hub_pre_uat.mjs +2 -2
  47. package/tests/provision_settings_apply.mjs +6 -5
  48. package/tests/provisioning_module_selection.mjs +164 -3
  49. package/tests/provisioning_settings.mjs +10 -7
  50. package/tests/provisioning_settings_apply.mjs +3 -3
  51. package/tests/provisioning_settings_env.mjs +7 -4
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.533",
6
- "core_contract": "1.19.533",
7
- "source_commit": "03699f66aaf653282fcbb775d32389f301c822f8",
5
+ "core_version": "1.19.534",
6
+ "core_contract": "1.19.534",
7
+ "source_commit": "1eed1abe54c1a615fc751be7b5c5361ed183e701",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-04T14:01:59.226Z",
9
+ "built_at": "2026-09-04T14:43:41.890Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
- "docs_redacted": 436,
12
+ "docs_redacted": 439,
13
13
  "agent_docs_stubbed": 24,
14
- "functional_verbatim": 1979,
14
+ "functional_verbatim": 1988,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 2439,
20
- "tree_sha256": "2ab0086b311db79080f1d46f9e35341d02d01ed318907fcf85a11f4dd19beb38",
19
+ "file_count": 2451,
20
+ "tree_sha256": "1d0ba8602e73d9b01a0bf440739e615efce023edf6cd5db2a27e12021eac8fda",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -387,7 +387,7 @@
387
387
  {
388
388
  "path": "clients/bongos-client/index.d.ts",
389
389
  "mode": "0000644",
390
- "sha256": "ae23995907a851acd2b2dbae901280926e3041ce64e16407d47ed5ba998d7c74"
390
+ "sha256": "22ae4bdb9bf36dfc3ce19fe38e64e8dded029087dac16d3ea20ccdbe1d860788"
391
391
  },
392
392
  {
393
393
  "path": "clients/bongos-client/index.mjs",
@@ -402,7 +402,7 @@
402
402
  {
403
403
  "path": "config/branding.neutral.json",
404
404
  "mode": "0000644",
405
- "sha256": "c33b285d6a88d38eb15e062e0da845d29ec2aa7e6bfc5a4d321a3a49e0b7b2f9"
405
+ "sha256": "af8b7eea98085e2ec1365e3e4bdaaffe35df17e753c8891265b3839dc2a8d42b"
406
406
  },
407
407
  {
408
408
  "path": "config/design-tokens.neutral.json",
@@ -1734,20 +1734,35 @@
1734
1734
  "mode": "0000644",
1735
1735
  "sha256": "a115ba411ec945223b0de36514fcbfb253205b79c2125c232081f35af3ec9a45"
1736
1736
  },
1737
+ {
1738
+ "path": "docs/adr/0245-the-way-back-into-an-optional-step-is-that-step-s-own-answer-route.md",
1739
+ "mode": "0000644",
1740
+ "sha256": "eb4e39132264f6a4515276b470d6d431ccc3b38f3f14927080d7bc67e3aba57e"
1741
+ },
1742
+ {
1743
+ "path": "docs/adr/0246-catalog-membership-is-a-projection-not-a-federation-side-effect.md",
1744
+ "mode": "0000644",
1745
+ "sha256": "4440a267f36bb1b22815a0fd192dd96f4dbd236ec5c73e108f9462a31f2a7ad9"
1746
+ },
1747
+ {
1748
+ "path": "docs/adr/0247-the-join-door-is-one-composed-answer-and-dark-matter-has-no-live-verdict-yet.md",
1749
+ "mode": "0000644",
1750
+ "sha256": "f46ee74d7b83ad4422e1832fdba68a8b9ea0deccb425ab3083d4c8fbfd06eb02"
1751
+ },
1737
1752
  {
1738
1753
  "path": "docs/adr/README.md",
1739
1754
  "mode": "0000644",
1740
- "sha256": "b34cab20b46a17a183ce54b2aed2bb09f03c55c47c7f6300b43fc32c86a79421"
1755
+ "sha256": "d1bb4772b43b56cf1efa16fa99f255535bd736910c4c0ead94249f19862c7152"
1741
1756
  },
1742
1757
  {
1743
1758
  "path": "docs/api-reference.md",
1744
1759
  "mode": "0000644",
1745
- "sha256": "b137673ee16cd6f7d32b0b24d12b6c8e844a2e6e415a8118a4d458c382e5724f"
1760
+ "sha256": "44555316c4fd8655bd48185820784f8a351ddc1a874e6d905c38865c330e6be1"
1746
1761
  },
1747
1762
  {
1748
1763
  "path": "docs/api/openapi.json",
1749
1764
  "mode": "0000644",
1750
- "sha256": "46e8c08e1e7b5bfa6fe2e709308a466f3764ed320702c93dbe74d5523bb1d74b"
1765
+ "sha256": "59f85c4841c3e884ea88d03a8385b37d104e0b0bcf561c87412bc30912490831"
1751
1766
  },
1752
1767
  {
1753
1768
  "path": "docs/architecture.md",
@@ -1772,12 +1787,12 @@
1772
1787
  {
1773
1788
  "path": "docs/copy-inventory.md",
1774
1789
  "mode": "0000644",
1775
- "sha256": "b42faf85d36a5411a0a1227ca1dd52c7d1f0f741332f03f2fb66d5fa90bc3ea0"
1790
+ "sha256": "63719647c69dd90bafa957a9e24b772324b49a09c8e615cc4bc6eac65996e540"
1776
1791
  },
1777
1792
  {
1778
1793
  "path": "docs/copy-registry.json",
1779
1794
  "mode": "0000644",
1780
- "sha256": "f1a02c1bad4249ae04fc7c0758c5e6efd2df2e647be3d89c531a3daeb5281826"
1795
+ "sha256": "c951cedd4efe1050dadb15f8bda65e0bd1e6f59aa775e2a407c9b676c27a7d5d"
1781
1796
  },
1782
1797
  {
1783
1798
  "path": "docs/design/apex-pass-2-direction.md",
@@ -2612,7 +2627,7 @@
2612
2627
  {
2613
2628
  "path": "docs/file-map.md",
2614
2629
  "mode": "0000644",
2615
- "sha256": "c7f035d61e8a45f2354256ae5fbbc16794ce6108ab3b5b712b054ade924df4ea"
2630
+ "sha256": "6085fdda80f0c961067a044b890358fa148415638854029e63f764e262594aeb"
2616
2631
  },
2617
2632
  {
2618
2633
  "path": "docs/handoff-template.md",
@@ -2622,7 +2637,7 @@
2622
2637
  {
2623
2638
  "path": "docs/module-api-changelog.md",
2624
2639
  "mode": "0000644",
2625
- "sha256": "4b74bef8f5b808fa3c7990b8fd727c26be2c929d64805e071489f1ca40a7f339"
2640
+ "sha256": "93a1f84761363805e0950743fa35a6b755957ef1a8621baa51705ba70f83ddd4"
2626
2641
  },
2627
2642
  {
2628
2643
  "path": "docs/modules-contract.md",
@@ -5712,7 +5727,7 @@
5712
5727
  {
5713
5728
  "path": "modules/onboarding/module.json",
5714
5729
  "mode": "0000644",
5715
- "sha256": "36800847c9cc9d239bb4d0ec910b35b547f8c67e5db5cb1cd5aa63d801e4bf02"
5730
+ "sha256": "6d27c15d955095fb278db240d7e68da87abbdab14b4dd10f98dfd0753746a3c1"
5716
5731
  },
5717
5732
  {
5718
5733
  "path": "modules/onboarding/newcomer-restock.js",
@@ -5737,7 +5752,7 @@
5737
5752
  {
5738
5753
  "path": "modules/onboarding/routes/access-requests.js",
5739
5754
  "mode": "0000644",
5740
- "sha256": "41729b14813c7f9d7ed93d8e9cc272f8ec6a97c610318ac5cb0fa2ff870a74c0"
5755
+ "sha256": "ebebd2ce84727de2dff8411a6279aefb3792de51577a5ba04dc7d565cbce5494"
5741
5756
  },
5742
5757
  {
5743
5758
  "path": "modules/onboarding/routes/onboarding.js",
@@ -5877,7 +5892,7 @@
5877
5892
  {
5878
5893
  "path": "modules/platform-identity/module.json",
5879
5894
  "mode": "0000644",
5880
- "sha256": "ea0ef93145343658eb46421ebc435181fe7233c35be7bc73a7cac229f53f6c08"
5895
+ "sha256": "b798cbc2c6f4106c4eb94c9f7219ad1643bfb5ec9449e0384bf87b3f7ecccc30"
5881
5896
  },
5882
5897
  {
5883
5898
  "path": "modules/platform-identity/platform-identity.js",
@@ -5889,6 +5904,11 @@
5889
5904
  "mode": "0000644",
5890
5905
  "sha256": "b6eea95dbe66a3126fc2c73d93237cf38dd648a803e910eb88fbd5c780ccef62"
5891
5906
  },
5907
+ {
5908
+ "path": "modules/platform-identity/project-catalog.js",
5909
+ "mode": "0000644",
5910
+ "sha256": "2f83a6c37f3cabb5ef04d20f0d6e92a39218528bad214a5e61e07eb3bba1468e"
5911
+ },
5892
5912
  {
5893
5913
  "path": "modules/platform-identity/project-stats.js",
5894
5914
  "mode": "0000644",
@@ -5917,7 +5937,7 @@
5917
5937
  {
5918
5938
  "path": "modules/platform-identity/routes/my-projects.js",
5919
5939
  "mode": "0000644",
5920
- "sha256": "9f3a833a455c14ea535910ec09d86a2c5bfb5419678274cb5d6ea8d790dd7883"
5940
+ "sha256": "70148631797a5b75a033861f696bf43535f81e8d1f44284a1d935b741a0f6304"
5921
5941
  },
5922
5942
  {
5923
5943
  "path": "modules/platform-identity/routes/profile.js",
@@ -5927,7 +5947,7 @@
5927
5947
  {
5928
5948
  "path": "modules/platform-identity/routes/projects.js",
5929
5949
  "mode": "0000644",
5930
- "sha256": "ec297f39a3d7f23b05b24ea6837bb9bfd07a12f99734788e9332769c59acc7b7"
5950
+ "sha256": "f90fbe23065310a09769bc46fdb0703e460f402d5a4e0cc490eba70cf67965ea"
5931
5951
  },
5932
5952
  {
5933
5953
  "path": "modules/platform-identity/routes/public-profile.js",
@@ -5942,7 +5962,7 @@
5942
5962
  {
5943
5963
  "path": "modules/platform-identity/routes/sso.js",
5944
5964
  "mode": "0000644",
5945
- "sha256": "dc3eaa8789a28a389ddeaac88f5cd514db8bf10705f430839f0bf164fc963674"
5965
+ "sha256": "6f89a384bfe46027ac330324b933255c2d55764b53df0c06571ecfbce6b98c23"
5946
5966
  },
5947
5967
  {
5948
5968
  "path": "modules/platform-identity/scouting-authz.js",
@@ -5969,6 +5989,11 @@
5969
5989
  "mode": "0000644",
5970
5990
  "sha256": "7cb9dac9cb87b679e1ae50e4112b3a7928a38ebb9902c8947a74d17bec4d9009"
5971
5991
  },
5992
+ {
5993
+ "path": "modules/provisioning/catalog-bridge.js",
5994
+ "mode": "0000644",
5995
+ "sha256": "35a3c70ebe49b37a18044d6756d97b9caea87917a9fdbe0198fa05be25f5457a"
5996
+ },
5972
5997
  {
5973
5998
  "path": "modules/provisioning/migrations/provisioning_001_tables.sql",
5974
5999
  "mode": "0000644",
@@ -6057,7 +6082,7 @@
6057
6082
  {
6058
6083
  "path": "modules/provisioning/provisioning.js",
6059
6084
  "mode": "0000644",
6060
- "sha256": "8745258599d655b34204e5878b413308ae48a012b0306bde3297e98e452b3686"
6085
+ "sha256": "d3d376a0aafc52dda55d9d2f2dcdc3d5baf9370a766cd4328cd00b314e897c99"
6061
6086
  },
6062
6087
  {
6063
6088
  "path": "modules/provisioning/rate-limit.js",
@@ -6077,7 +6102,7 @@
6077
6102
  {
6078
6103
  "path": "modules/provisioning/routes/provisioning.js",
6079
6104
  "mode": "0000644",
6080
- "sha256": "e08199bc9146bd3122ca038637a0e390b9f5e636a102fa290dba414cf6cafdc1"
6105
+ "sha256": "3528d699cb9c0f396eeda2e9fc26eb9b89c02e0b0ca34beeb20fd84c832f5241"
6081
6106
  },
6082
6107
  {
6083
6108
  "path": "modules/provisioning/starter-bundles.js",
@@ -6092,7 +6117,7 @@
6092
6117
  {
6093
6118
  "path": "modules/provisioning/tests/provisioning.mjs",
6094
6119
  "mode": "0000644",
6095
- "sha256": "03e4ba2a5625ac321a95392c91b8e90cc6f7e235dc9a0c4bbcb742cbd459b083"
6120
+ "sha256": "4dceb82f4dbeb39128c66d666df0642aacb38988f7c6d01c55f6515bcd8ad798"
6096
6121
  },
6097
6122
  {
6098
6123
  "path": "modules/provisioning/ui/.gitkeep",
@@ -6332,7 +6357,7 @@
6332
6357
  {
6333
6358
  "path": "modules/public-landing/public/assets/cosmos.css",
6334
6359
  "mode": "0000644",
6335
- "sha256": "f10975893b3522accc6592246af0b07645c8d910c7637ecc5a006064a0988057"
6360
+ "sha256": "8da9101c633ea363802a4ac2831fe55d182308573a923bec50b4ce00e4f8254b"
6336
6361
  },
6337
6362
  {
6338
6363
  "path": "modules/public-landing/public/assets/verro-medallion.png",
@@ -6592,7 +6617,7 @@
6592
6617
  {
6593
6618
  "path": "modules/public-landing/public/projects.html",
6594
6619
  "mode": "0000644",
6595
- "sha256": "16d728f41b1fc8b72fb1ac74d6b97b8bb0709f8a490e660062d21a964b52a4f1"
6620
+ "sha256": "456100c301325c4d303061a00804fa67f7dfc7d4494b15aea190641541db6531"
6596
6621
  },
6597
6622
  {
6598
6623
  "path": "modules/public-landing/public/projects.probes.json",
@@ -7027,7 +7052,7 @@
7027
7052
  {
7028
7053
  "path": "modules/ui-design/kit/serve.js",
7029
7054
  "mode": "0000644",
7030
- "sha256": "4c52462cf61559e9a424107398eca58aa56b3783338827dda6110200fcdfd00a"
7055
+ "sha256": "e0d76468a01c6e76c0f106714b31bc42f6b3cb12cf2b73a5739253fab1b45b90"
7031
7056
  },
7032
7057
  {
7033
7058
  "path": "modules/ui-design/kit/tells.js",
@@ -7337,12 +7362,12 @@
7337
7362
  {
7338
7363
  "path": "package-lock.json",
7339
7364
  "mode": "0000644",
7340
- "sha256": "427e2b1d9b7e871c4e28163f4acd078751e2e43c88561fcefd85083721126292"
7365
+ "sha256": "468fe9079df95bf33112f63df0b3e72ce952db87b121b4127156e6c615eb2bbb"
7341
7366
  },
7342
7367
  {
7343
7368
  "path": "package.json",
7344
7369
  "mode": "0000644",
7345
- "sha256": "7e9fdd8898ce17821b9724972161530d4fb126cd7ec164a02dc290be89a41ae6"
7370
+ "sha256": "ac5e20798f6621e686013476b57d4034f1ce36d5562e684e7f3c6bee0747c759"
7346
7371
  },
7347
7372
  {
7348
7373
  "path": "public-docs/index.html",
@@ -7777,7 +7802,7 @@
7777
7802
  {
7778
7803
  "path": "scripts/gds/fitness.js",
7779
7804
  "mode": "0000644",
7780
- "sha256": "c6a11cbffff99b725d60dff415622ff3ade8e9c56ede14edc01828ebb80d8415"
7805
+ "sha256": "172f890f2a4297d287008f0ba11bc04fc801ea21a52728855967b178e2875373"
7781
7806
  },
7782
7807
  {
7783
7808
  "path": "scripts/gds/gate-review.js",
@@ -8097,7 +8122,7 @@
8097
8122
  {
8098
8123
  "path": "scripts/gds/provision-net.js",
8099
8124
  "mode": "0000644",
8100
- "sha256": "01b6ef89cbef4d898934c1802dec416c6085f0dde5ae49012bd2fa7a14bb9589"
8125
+ "sha256": "86c308f807a27f448a1f90f4257e74238c177b885468b95b89d379c7249366fa"
8101
8126
  },
8102
8127
  {
8103
8128
  "path": "scripts/gds/provision-repo.js",
@@ -8869,6 +8894,11 @@
8869
8894
  "mode": "0000644",
8870
8895
  "sha256": "c9edcf9cb1f027ca122388beabae488d2b771b9e7aee2cd12fbdb48ea6f0ded8"
8871
8896
  },
8897
+ {
8898
+ "path": "src/bongos/project-door.js",
8899
+ "mode": "0000644",
8900
+ "sha256": "045c0edceb0f71c3b488471ffb3a1c20c58014d0592c9ac44572245a7eb08960"
8901
+ },
8872
8902
  {
8873
8903
  "path": "src/bongos/project-settings.js",
8874
8904
  "mode": "0000644",
@@ -9002,7 +9032,7 @@
9002
9032
  {
9003
9033
  "path": "src/branding.js",
9004
9034
  "mode": "0000644",
9005
- "sha256": "e603fa0fabe63b7150f649db1ecaa50ef0908a803396e808cf212f4378cbb053"
9035
+ "sha256": "8885ee12599cdfdaf9bdb073185c83a5532bdc46d07edaeab42e36ccdc12f8e1"
9006
9036
  },
9007
9037
  {
9008
9038
  "path": "src/build-info.js",
@@ -9022,7 +9052,7 @@
9022
9052
  {
9023
9053
  "path": "src/module-api.js",
9024
9054
  "mode": "0000644",
9025
- "sha256": "12cf837247460eee2eafd0b97bcbaf4472a0dcea13cd87b116952f4b8f2b75fb"
9055
+ "sha256": "e97c60a63ed6befae3e842e86f97cf592bb58f45cdbe488d13cb0f5e576c75b0"
9026
9056
  },
9027
9057
  {
9028
9058
  "path": "src/module-loader/catalog.js",
@@ -9077,7 +9107,7 @@
9077
9107
  {
9078
9108
  "path": "tests/access_requests_invite.mjs",
9079
9109
  "mode": "0000644",
9080
- "sha256": "4d8435e7f8b949715ee40843beddc1868105edd9c67b269cfa8f510a192e9212"
9110
+ "sha256": "7229451a9a9cb74049b7fea808458423d8a2754c213e6974593811cd1b4322b1"
9081
9111
  },
9082
9112
  {
9083
9113
  "path": "tests/account_existence_rate_limit.mjs",
@@ -9177,7 +9207,7 @@
9177
9207
  {
9178
9208
  "path": "tests/application_lifecycle.mjs",
9179
9209
  "mode": "0000644",
9180
- "sha256": "ac8128aa0834f420ee744e057cd2baa01569611052d199dba9e5d28986ff1166"
9210
+ "sha256": "249c97a172d0b77dae22dc561dc7c02dd48b636956df296647d62d7c6e213727"
9181
9211
  },
9182
9212
  {
9183
9213
  "path": "tests/architect.mjs",
@@ -9504,6 +9534,11 @@
9504
9534
  "mode": "0000644",
9505
9535
  "sha256": "de9d8f8c1daab1e48122febd16699ac519890b6e1296edb7966d835434851786"
9506
9536
  },
9537
+ {
9538
+ "path": "tests/catalog_only_client.mjs",
9539
+ "mode": "0000644",
9540
+ "sha256": "2eb721978e016514ef53cf5083dbbc5d38a2db57cd75786fdd6b6a12e5488dff"
9541
+ },
9507
9542
  {
9508
9543
  "path": "tests/category_advisory_guard.mjs",
9509
9544
  "mode": "0000644",
@@ -9727,7 +9762,7 @@
9727
9762
  {
9728
9763
  "path": "tests/currency_label.mjs",
9729
9764
  "mode": "0000644",
9730
- "sha256": "09a937b9a9550b77659c0e60b3bc44c45378fdb3545c510d7579d4711518a733"
9765
+ "sha256": "b88f7ab55948f0bfbf0e6982cc96465ff2c34d76b143f40b86f933c323b8ff1d"
9731
9766
  },
9732
9767
  {
9733
9768
  "path": "tests/cutover_codemod.mjs",
@@ -10962,7 +10997,7 @@
10962
10997
  {
10963
10998
  "path": "tests/module_api.mjs",
10964
10999
  "mode": "0000644",
10965
- "sha256": "839cbdc51d53f6719671ff1f2caa431d52a8b76636cd7973350000510510779b"
11000
+ "sha256": "c7eadd36ae57c2f8976bf30bbd4c5d6ac3db0c1a8f67319860b3e4f85b5b5bc1"
10966
11001
  },
10967
11002
  {
10968
11003
  "path": "tests/module_catalog.mjs",
@@ -11234,11 +11269,36 @@
11234
11269
  "mode": "0000644",
11235
11270
  "sha256": "6a7a223a678b56333befaed017429e5b9d8a81e4405efae72a23355902517d9d"
11236
11271
  },
11272
+ {
11273
+ "path": "tests/project_catalog_bridge.mjs",
11274
+ "mode": "0000644",
11275
+ "sha256": "f4c023348054679885b8aefb8dcb700d23f3a67316e898f02e7cb0ecda75e9ff"
11276
+ },
11277
+ {
11278
+ "path": "tests/project_catalog_routes.mjs",
11279
+ "mode": "0000644",
11280
+ "sha256": "5918d1ce7c106e5d7baa9cb989d52f3e82ac2aaf45a89f9a9bb154066106dccc"
11281
+ },
11237
11282
  {
11238
11283
  "path": "tests/project_door_ui.mjs",
11239
11284
  "mode": "0000644",
11240
11285
  "sha256": "29f59ab64b231a7ec5d17a3e784d451f75d80c19efd4cb6a523d263e8f88b581"
11241
11286
  },
11287
+ {
11288
+ "path": "tests/project_join_door.mjs",
11289
+ "mode": "0000644",
11290
+ "sha256": "c0313968f5c6b07061443cfdbb0d928ba3067fb118cdd6fcf5892788189d22f7"
11291
+ },
11292
+ {
11293
+ "path": "tests/project_join_door_route.mjs",
11294
+ "mode": "0000644",
11295
+ "sha256": "9da8c3416b4d06f82fdf8254aec216d8e7a7368b697f151f5d58f1c286f86639"
11296
+ },
11297
+ {
11298
+ "path": "tests/project_modules_ui.mjs",
11299
+ "mode": "0000644",
11300
+ "sha256": "e728a4c1d13ea3a54910d0a706522a780db138783202a09f300900b8c661ae2e"
11301
+ },
11242
11302
  {
11243
11303
  "path": "tests/project_stats.mjs",
11244
11304
  "mode": "0000644",
@@ -11252,7 +11312,7 @@
11252
11312
  {
11253
11313
  "path": "tests/projects_hub.mjs",
11254
11314
  "mode": "0000644",
11255
- "sha256": "edcc3d0cc2b9d5e5c44a33b43f98353001ff22222bf2f34c2c3dd28f04f66989"
11315
+ "sha256": "2ef099cea46b1ffa0841d1845dedf00de047de4f9c6844baaabdcd317a5eebac"
11256
11316
  },
11257
11317
  {
11258
11318
  "path": "tests/projects_hub_module_picker.mjs",
@@ -11262,7 +11322,7 @@
11262
11322
  {
11263
11323
  "path": "tests/projects_hub_pre_uat.mjs",
11264
11324
  "mode": "0000644",
11265
- "sha256": "fef872ed80c7efdbf37e0672067f555c76aa17368a515afb99b1fa7e7ca3c4b4"
11325
+ "sha256": "294ecd72fa9e1c67cba9c2411c57447e459426682586c099adcd93b606530def"
11266
11326
  },
11267
11327
  {
11268
11328
  "path": "tests/promote_goal_id.mjs",
@@ -11287,7 +11347,7 @@
11287
11347
  {
11288
11348
  "path": "tests/provision_settings_apply.mjs",
11289
11349
  "mode": "0000644",
11290
- "sha256": "9a04386b3047177bb55b6d392db88764f06f92e5c0b20c16ed9e0f8ce520b07d"
11350
+ "sha256": "9a09eb9bd47ff36a991b7208d0bada1fb91e3f8f03fe36a3ac4b5549b56b48ca"
11291
11351
  },
11292
11352
  {
11293
11353
  "path": "tests/provisioning_callback_page.mjs",
@@ -11312,7 +11372,7 @@
11312
11372
  {
11313
11373
  "path": "tests/provisioning_module_selection.mjs",
11314
11374
  "mode": "0000644",
11315
- "sha256": "4a27e1641d949f261a8ba1b72816a8b077fc8a55862c4eef4091b453b7cfd45c"
11375
+ "sha256": "a8cb68c1f21a7c6547ef382b4641b80c1c9e46a2aad415f75dbfde36224079fd"
11316
11376
  },
11317
11377
  {
11318
11378
  "path": "tests/provisioning_onboard_mode.mjs",
@@ -11332,17 +11392,17 @@
11332
11392
  {
11333
11393
  "path": "tests/provisioning_settings.mjs",
11334
11394
  "mode": "0000644",
11335
- "sha256": "6f78a45d0c68a0da780d40c602f23bca3d48a80524218d4d7d311b2f8574dbee"
11395
+ "sha256": "a7ec1145f695ed066c322bbd88f5f1dd3f3b47620ca4314e57860876a7394fe7"
11336
11396
  },
11337
11397
  {
11338
11398
  "path": "tests/provisioning_settings_apply.mjs",
11339
11399
  "mode": "0000644",
11340
- "sha256": "c453bab3768953257be481cb8df700530f964e167fdca073cf4ae69892930a79"
11400
+ "sha256": "7fc80fc5576aba1dd10c0416899ade5fa2c9380ea8c1a995aaa99745aa56c29c"
11341
11401
  },
11342
11402
  {
11343
11403
  "path": "tests/provisioning_settings_env.mjs",
11344
11404
  "mode": "0000644",
11345
- "sha256": "4f51ea07fe3d80d52d6998547b028bec0a97267210faeffd75d9a47f7b5643e8"
11405
+ "sha256": "a941c67c8b5640bdbacc0b5078fadacc79d1332befeb40bc3ebe82cd36764552"
11346
11406
  },
11347
11407
  {
11348
11408
  "path": "tests/provisioning_slug_available.mjs",
@@ -171,7 +171,7 @@ export interface PatchMeSkillPrefsResponse { prefs: unknown; effective: unknown;
171
171
  export interface PatchMeSoundPrefsResponse { prefs: unknown; effective: unknown; effective_map: unknown; volume: unknown; defaults: unknown; known_sounds: unknown }
172
172
  export interface PatchProjectsIdFeaturedRequest { featured: boolean }
173
173
  export interface PatchProjectsIdFeaturedResponse { project: unknown }
174
- export interface PatchProvisioningInstancesIdDetailRequest { description?: string; detail?: Record<string, unknown> }
174
+ export interface PatchProvisioningInstancesIdDetailRequest { description?: string; detail?: Record<string, unknown>; modules?: string[] }
175
175
  export interface PatchProvisioningInstancesIdDetailResponse { ok: boolean; description: unknown; detail: unknown; options: unknown; publish: unknown; modules: unknown }
176
176
  export interface PatchProvisioningInstancesIdSettingsResponse { ok: boolean; saved: unknown; planet: unknown; push: unknown }
177
177
  export interface PatchTaskRecommendationsIdRequest { status: "declined" | "withdrawn" }
@@ -27,10 +27,11 @@
27
27
  "idp": false
28
28
  },
29
29
  "project": {
30
- "_note": "Per-project policy the PLATFORM decides and this instance OBEYS (task 1003139, goal 1000058). Stored on the platform's provisioning_instances.settings; written here as <PREFIX>_PLATFORM_VISIBILITY / <PREFIX>_JOINABILITY / <PREFIX>_VISIBILITY by the provisioning runner, which re-writes web.env and restarts the service when the owner changes one. The defaults are TODAY's behavior, so an instance that never receives a push — a self-hosted one, or one provisioned before this channel — keeps behaving exactly as it does now. platformVisibility (who may READ this instance's own pages): public | gated. joinability (HOW someone becomes a member): open | apply | invite_only. visibility (the join door, ADR 0182 D1 — how this project stands on the platform's map): public | private | stealth; it does NOT decide whether the project is on the map, publishability does.",
30
+ "_note": "Per-project policy the PLATFORM decides and this instance OBEYS (task 1003139, goal 1000058). Stored on the platform's provisioning_instances.settings; written here as <PREFIX>_PLATFORM_VISIBILITY / <PREFIX>_JOINABILITY / <PREFIX>_VISIBILITY by the provisioning runner, which re-writes web.env and restarts the service when the owner changes one. The defaults are TODAY's behavior, so an instance that never receives a push — a self-hosted one, or one provisioned before this channel — keeps behaving exactly as it does now. platformVisibility (who may READ this instance's own pages): public | gated. joinability (HOW someone becomes a member): open | apply | invite_only. visibility (the join door, ADR 0182 D1 — how this project stands on the platform's map): public | private | stealth; it does NOT decide whether the project is on the map, publishability does. joinGrant (what a PUBLIC join GRANTS): view | apply | full — the ceiling joinability may only narrow (ADR 0182 D6 / ADR 0245); `full` narrows nothing, which is why it is the default.",
31
31
  "platformVisibility": "public",
32
32
  "joinability": "apply",
33
- "visibility": "public"
33
+ "visibility": "public",
34
+ "joinGrant": "full"
34
35
  },
35
36
  "repo": {
36
37
  "owner": "",
@@ -0,0 +1,144 @@
1
+ # 0245 — The way back into an optional step is that step's own answer route
2
+
3
+ - **Status:** Accepted
4
+ - **Date:** 2026-09-04
5
+ - **Tasks:** [#1002342](https://cloudbongos.com/builders#/task/1002342) (this decision — BV1.R23)
6
+ - **Goal:** [#1000046](https://cloudbongos.com/builders#/goal/1000046) — Project creation, criterion
7
+ `proj-module-select` ("during setup a default set of modules is pre-selected…")
8
+ - **Related:** [ADR 0240](<redacted>.md)
9
+ (R20 — the nullable `modules` column, the three-valued contract, and §6's "R23 … needs to know whether
10
+ an owner has ever been asked"), [ADR 0237](<redacted>.md)
11
+ (R19 — the bundles and the `OPTIONAL_MODULES` catalog this surface renders),
12
+ [ADR 0243](<redacted>.md) (R22 — the
13
+ team-shape adjustment inside the fallback, and why `PATCH …/detail` already answered with `modules`),
14
+ [ADR 0238](<redacted>.md) (R05 — the manage view, and its
15
+ finding that "the project's own hall settings" is a page that does not exist),
16
+ [ADR 0111](<redacted>.md) (the enqueue-only web tier)
17
+
18
+ ## 1. The problem
19
+
20
+ R20 shipped a module picker as step 4 of the create wizard and made it skippable.
21
+ The route file said out loud what that left behind:
22
+
23
+ > …there is no mutation route yet (R23, task 1002342 is "add modules later"), so
24
+ > the receipt says the answer was kept rather than naming a way back that does
25
+ > not exist.
26
+
27
+ So a project's module set was **write-once at creation**. An owner who skipped the
28
+ picker, or picked wrong, had no way to change it — which is the same dead end
29
+ `PATCH /provisioning/instances/:id/detail` was built to close for the About step
30
+ one panel earlier. An optional step is a supported default only while there is a
31
+ way back; without one, "optional" quietly means "permanent".
32
+
33
+ Two questions had to be answered, and the second follows from the first.
34
+
35
+ ## 2. Not a new route — the same route the step beside it uses
36
+
37
+ The obvious move is a new `PATCH /provisioning/instances/:id/modules`. It is the
38
+ wrong one, and the reason is that the wizard's two optional steps are the same
39
+ kind of thing:
40
+
41
+ | | About (step 3) | Modules (step 4) |
42
+ |---|---|---|
43
+ | asked | optionally, at creation | optionally, at creation |
44
+ | stored | `description` + `detail` on the platform row | `modules` on the platform row |
45
+ | reaches the running instance | no | no |
46
+ | way back | `PATCH …/detail` | **this** |
47
+
48
+ `PATCH …/detail` is already *"answer (or re-answer) the wizard's optional creation
49
+ steps after creation"* — the name is narrower than the job. It already carries the
50
+ ownership gate (`instanceAccessDenied`: own-scoped, Archon override, 404 with no
51
+ existence leak), already records an event with an `api:self` / `api:archon:<id>`
52
+ actor, and — since [ADR 0243](<redacted>.md)
53
+ — **already answers with `effectiveModules(updated)`**, because a `team_shape`
54
+ answer moves a bundle-sourced project's modules. The route was already reporting
55
+ this value; all that was missing was the ability to set it.
56
+
57
+ A second route would have duplicated the gate, the actor rule, the 404 shape and
58
+ the receipt, for one column on a row the first route already writes. Duplicated
59
+ authorization is the kind that drifts.
60
+
61
+ **Not the settings channel**, for [ADR 0240](<redacted>.md)
62
+ §3's reason, restated: a setting is *a policy the running instance obeys* — it
63
+ rides `web.env`, queues a `settings-apply` intent and restarts the service. A
64
+ module selection is a declaration held on the platform row until a catalog row
65
+ materializes. Nothing is pushed and nothing restarts, and the surface says so
66
+ rather than borrowing its neighbours' "your project will restart" receipt.
67
+
68
+ ## 3. The skip state is not writable
69
+
70
+ `modules` is three-valued (ADR 0240 §2): `NULL` = never asked, `[]` = declined
71
+ everything, `[…]` = chosen. A later edit must be able to reach the second and
72
+ third, and **must not be able to reach the first**.
73
+
74
+ So only an **array** is a change. An omitted key, or an explicit `null`, leaves the
75
+ column alone; a body carrying nothing but a null `modules` is still the existing
76
+ `bad_detail` "nothing to change". That is a deliberate asymmetry rather than an
77
+ oversight: `NULL` is a claim about the owner's *history* — that they were never
78
+ asked — and a write that could re-assert it would let an answered project become
79
+ indistinguishable from an unasked one, permanently, which is the exact collapse
80
+ ADR 0240 refused `NOT NULL DEFAULT '[]'` to prevent.
81
+
82
+ `modules` also **replaces** where `detail` merges. The `detail = detail || $n::jsonb`
83
+ precedent exists because a form may send one interview answer without resending
84
+ the rest; a module selection has no keys to merge, and a merging write would make
85
+ *removal* impossible — the half of "add modules later" an owner needs most.
86
+
87
+ The always-on core stays unremovable by construction, not by a guard bolted on
88
+ here: `badModuleSelection` refuses a core key by name (`"memory" is always on for
89
+ every project — it is not one of the modules to choose`), `normalizeModuleSelection`
90
+ filters to the opt-in catalog on the way to storage, and `effectiveModules` prepends
91
+ the whole core on the way out. Three independent places, none of which this route
92
+ had to add.
93
+
94
+ ## 4. The surface is the manage page, because the hall page is still gone
95
+
96
+ ADR 0182 D5 pointed R05 at "the project's own hall settings"; ADR 0238 found that
97
+ page does not exist — `settings.html` is per-*builder*, and the hall's my-projects
98
+ page was deleted by task 1003308. Nothing has changed since, so the extras band
99
+ lands where its three sibling settings already live: `/projects?view=manage&id=N`.
100
+
101
+ Two reads, one of them new. The **current set** rides in on the instance itself —
102
+ `publicInstance` resolves `effectiveModules(row)`, so a skipped picker reads as its
103
+ own starter bundle and this page never re-derives that rule, which is the one way
104
+ two surfaces come to name different modules for one project. The **catalog** is the
105
+ same public `GET /provisioning/starter-bundles` the wizard's panel reads.
106
+
107
+ **A toggle is the save.** Every sibling section on that page has a Save button
108
+ because each is one choice with one value; the extras band is a list of independent
109
+ facts, where a Save button only adds a way to lose one. The write sends the whole
110
+ list in catalog order (what the column stores), and the band repaints from the
111
+ platform's answer rather than from what was sent — so it can never show a set the
112
+ server did not accept, and a refusal puts it back to what the row still holds.
113
+
114
+ ## 5. Consequences
115
+
116
+ - **`modules_ignored` now names the way back.** A create re-request that carries a
117
+ changed `modules` still refuses to apply it, but its receipt points at this route
118
+ — the `detail_ignored` shape, for the `detail_ignored` reason.
119
+ - **The wizard's own `?view=manage` links carry their id now.** The done panel's
120
+ in-page router matched `view=(\w+)` and dropped the query, so every
121
+ `projectPageLink` on that panel landed on a manage page with no project. Fixed in
122
+ passing because criterion B's pointer would have been the fifth link to do it.
123
+ - **Still not an input to the runner** (ADR 0240 §6). Changing the set changes a
124
+ declaration on the platform row; turning it into the child instance's own
125
+ `config/modules.json` remains separate work. The surface's copy says so — "nothing
126
+ restarts" is true today and would be a lie the moment that work lands, so it is
127
+ worth re-reading then.
128
+ - **A module selection is still not a publish precondition** (ADR 0182 D4).
129
+
130
+ ## 6. Rejected
131
+
132
+ - **A new `PATCH …/modules` route** — a second copy of one ownership gate, one actor
133
+ rule and one 404 shape, for a column the existing route's own UPDATE already
134
+ reaches.
135
+ - **The settings channel** — would queue a `settings-apply` and restart a running
136
+ instance to deliver a value the instance does not read (ADR 0240 §3).
137
+ - **Letting `modules: null` clear the column** — reachable, coherent-looking, and it
138
+ destroys the only fact that says whether an owner was ever asked.
139
+ - **Merging the list like `detail`** — makes removing a module impossible.
140
+ - **A Save button under the band** — one more state to hold, and its failure mode is
141
+ an owner who toggles, leaves, and loses the change.
142
+ - **Rendering the always-on core as disabled rows** — ADR 0240's rejection, and it
143
+ holds here for the same reason: a disabled control reads as a choice the owner
144
+ failed to make.