@bongos/core 1.19.579 → 1.19.581

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 (40) hide show
  1. package/.bongos-core.json +75 -40
  2. package/bin/bongos.js +10 -1
  3. package/clients/bongos-client/README.md +1 -1
  4. package/clients/bongos-client/bongos-client.global.js +2 -0
  5. package/clients/bongos-client/index.cjs +2 -0
  6. package/clients/bongos-client/index.d.ts +4 -0
  7. package/clients/bongos-client/index.mjs +2 -0
  8. package/docs/adr/0260-the-application-is-the-consent-and-the-echo-is-the-gate.md +146 -0
  9. package/docs/adr/README.md +1 -0
  10. package/docs/api/openapi.json +73 -3
  11. package/docs/api-reference.md +3 -2
  12. package/docs/copy-inventory.md +21 -18
  13. package/docs/copy-registry.json +50 -23
  14. package/docs/file-map.md +2 -0
  15. package/docs/module-api-changelog.md +4 -0
  16. package/modules/dev-box/app/src/vendor/bongos-client.cjs +2 -0
  17. package/modules/hall-ui/public/oversight.css +7 -0
  18. package/modules/hall-ui/public/watch.js +75 -0
  19. package/modules/onboarding/routes/access-requests.js +56 -1
  20. package/modules/platform-identity/applicant-profile.js +158 -0
  21. package/modules/platform-identity/application-echo.js +50 -0
  22. package/modules/platform-identity/recruiter-sliver.js +19 -0
  23. package/modules/platform-identity/routes/sso.js +126 -0
  24. package/package-lock.json +2 -2
  25. package/package.json +1 -1
  26. package/scripts/gds/build-cli-package.js +8 -2
  27. package/scripts/gds/cli-lib.js +11 -3
  28. package/scripts/gds/recall.js +1 -1
  29. package/scripts/gds/start.js +39 -26
  30. package/scripts/gds/surface.js +89 -0
  31. package/src/bongos/auth-admission.js +61 -0
  32. package/src/bongos/auth.js +2 -1
  33. package/src/module-api.js +11 -1
  34. package/tests/adr_renumber_integrity.mjs +84 -0
  35. package/tests/applicant_profile_boundary.mjs +641 -0
  36. package/tests/applicant_profile_read_bounds.mjs +95 -0
  37. package/tests/cli_surface.mjs +146 -0
  38. package/tests/module_api.mjs +1 -0
  39. package/tests/recruiter_sliver_boundary.mjs +35 -2
  40. package/tests/watch_applications_queue.mjs +94 -0
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.579",
6
- "core_contract": "1.19.579",
7
- "source_commit": "8b206e9f473df316fc1981854a479d1ba509d76e",
5
+ "core_version": "1.19.581",
6
+ "core_contract": "1.19.581",
7
+ "source_commit": "03f7d02a4e669a2fa53f34e6375ebb6b7a046a27",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-07T17:52:17.407Z",
9
+ "built_at": "2026-09-07T20:15:39.494Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
- "docs_redacted": 452,
12
+ "docs_redacted": 453,
13
13
  "agent_docs_stubbed": 24,
14
- "functional_verbatim": 2047,
14
+ "functional_verbatim": 2053,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 2523,
20
- "tree_sha256": "bd13bef2ae141fa766ed5c8a67894be21c62263079037e763bc1533ff500afdc",
19
+ "file_count": 2530,
20
+ "tree_sha256": "5c954594f6fc90e5180384a00a4a9cb3258580a0f9df36d629dbf71731a1fd4e",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -367,17 +367,17 @@
367
367
  {
368
368
  "path": "bin/bongos.js",
369
369
  "mode": "0000644",
370
- "sha256": "36208a41a60e3a21356ca9f81abdff9203837ff024b3d15c88a26bee9cce371c"
370
+ "sha256": "d585fc874c0a45f5d02d5753025d4761ca9b817ae9e5c556891b5c5c444cffde"
371
371
  },
372
372
  {
373
373
  "path": "clients/bongos-client/README.md",
374
374
  "mode": "0000644",
375
- "sha256": "f94a07e3c135a5d6c41a2d16f722e86b990362996042dc2a5e05b2020f8cf376"
375
+ "sha256": "48a76d533215b8c0885fa77f59fc0e8915262db6b57029f6850692882e6e7455"
376
376
  },
377
377
  {
378
378
  "path": "clients/bongos-client/bongos-client.global.js",
379
379
  "mode": "0000644",
380
- "sha256": "06d1814637fe2898f0d93a3870726d53102d02feeae4611fcca80f195e2e26e4"
380
+ "sha256": "c7fe87ceb18e7e1c10e629d002fb6f322ee540606c3ec9f6d38741dac7a55e0f"
381
381
  },
382
382
  {
383
383
  "path": "clients/bongos-client/examples/hello-world.mjs",
@@ -387,17 +387,17 @@
387
387
  {
388
388
  "path": "clients/bongos-client/index.cjs",
389
389
  "mode": "0000644",
390
- "sha256": "ee0e25848a938f675e7a2e6e0a48983308117ecafe93ca219f4f1df49cf7ea51"
390
+ "sha256": "d80b05be7a602fa825f5f45e5b7f2bc002473a4731e2530df19fa3043f0c07fc"
391
391
  },
392
392
  {
393
393
  "path": "clients/bongos-client/index.d.ts",
394
394
  "mode": "0000644",
395
- "sha256": "adaa20c8ce6bee76dc289f10929014382aa89d7373f94ddafc84f4c87eeb0ed4"
395
+ "sha256": "e5cfa78c545e60771166ae4118e18f5c7bcf911db649c115c2831a028de3562b"
396
396
  },
397
397
  {
398
398
  "path": "clients/bongos-client/index.mjs",
399
399
  "mode": "0000644",
400
- "sha256": "6a3e4df57180f3c182fbadabe94030d06f125373c9336a7a4160015f1df5b11e"
400
+ "sha256": "47ae66627693378c276031754aca717ecc01e8e5038c4cf98ed844e5c03ee524"
401
401
  },
402
402
  {
403
403
  "path": "clients/bongos-client/package.json",
@@ -1814,20 +1814,25 @@
1814
1814
  "mode": "0000644",
1815
1815
  "sha256": "772b9153d882f6bbd5000ee90b83e05d550ee4f04d4f5392db36e223d9a58c59"
1816
1816
  },
1817
+ {
1818
+ "path": "docs/adr/0260-the-application-is-the-consent-and-the-echo-is-the-gate.md",
1819
+ "mode": "0000644",
1820
+ "sha256": "fa04f8d71fff8186ae975a949c045ca7481bd0f22f395cb8c7397bbe03ddd449"
1821
+ },
1817
1822
  {
1818
1823
  "path": "docs/adr/README.md",
1819
1824
  "mode": "0000644",
1820
- "sha256": "bf52fa642d9a576a88f961b46fbb68352e3fd8dd79004440f5e4182e549039ed"
1825
+ "sha256": "1fa7dcf1456d26896e5cec77026ace0f08db8bed65527dee563cd7fdd2d07525"
1821
1826
  },
1822
1827
  {
1823
1828
  "path": "docs/api-reference.md",
1824
1829
  "mode": "0000644",
1825
- "sha256": "0a622847ab68980279beba2e1a54813b2393107144f88dd31228fe4dfd248b90"
1830
+ "sha256": "450613589b6b480ba2d6789731003794749c0dd972fcd8636380fa5814d68807"
1826
1831
  },
1827
1832
  {
1828
1833
  "path": "docs/api/openapi.json",
1829
1834
  "mode": "0000644",
1830
- "sha256": "60ca0b131c661df16fb993182d2085d0b04ea15a4d91bec4318c3413fb8dc311"
1835
+ "sha256": "c3a329219460b2508d51009ff8f77bdc4733f3a17ad2776553dc7bce483555c4"
1831
1836
  },
1832
1837
  {
1833
1838
  "path": "docs/architecture.md",
@@ -1852,12 +1857,12 @@
1852
1857
  {
1853
1858
  "path": "docs/copy-inventory.md",
1854
1859
  "mode": "0000644",
1855
- "sha256": "da770863626202ff2566de7f0a152eebc086662c74d86423794d378fca2d83e6"
1860
+ "sha256": "c4b6557f79a876b18cfc020ea4d45b8dfa419c9c37cab3fb2c629d85939bd75d"
1856
1861
  },
1857
1862
  {
1858
1863
  "path": "docs/copy-registry.json",
1859
1864
  "mode": "0000644",
1860
- "sha256": "423614f0519d1e94e605e439b53a2bac7a63c2ae8005b0021c71e6c81f38edfc"
1865
+ "sha256": "867399ce0002d6a1f509969544ad9589db6769352597471b495c66749271bb3c"
1861
1866
  },
1862
1867
  {
1863
1868
  "path": "docs/design/apex-pass-2-direction.md",
@@ -2692,7 +2697,7 @@
2692
2697
  {
2693
2698
  "path": "docs/file-map.md",
2694
2699
  "mode": "0000644",
2695
- "sha256": "f43e8946ecb5c96dc7af0bcee49010245df577eee62381701d2aef1c67455662"
2700
+ "sha256": "8b9c303b7cb35b420ff13c0a265fe2e505769c6d1625977eb85c0d077ff1ddf2"
2696
2701
  },
2697
2702
  {
2698
2703
  "path": "docs/handoff-template.md",
@@ -2702,7 +2707,7 @@
2702
2707
  {
2703
2708
  "path": "docs/module-api-changelog.md",
2704
2709
  "mode": "0000644",
2705
- "sha256": "cb7bb494b4b2ad905f1067b6f3c11e6331faa8beaad533c70d4a5009c456a807"
2710
+ "sha256": "fad24c9257355302696388602bba42f251696c20d4e2380f668339a823a84558"
2706
2711
  },
2707
2712
  {
2708
2713
  "path": "docs/modules-contract.md",
@@ -4102,7 +4107,7 @@
4102
4107
  {
4103
4108
  "path": "modules/dev-box/app/src/vendor/bongos-client.cjs",
4104
4109
  "mode": "0000644",
4105
- "sha256": "ee0e25848a938f675e7a2e6e0a48983308117ecafe93ca219f4f1df49cf7ea51"
4110
+ "sha256": "d80b05be7a602fa825f5f45e5b7f2bc002473a4731e2530df19fa3043f0c07fc"
4106
4111
  },
4107
4112
  {
4108
4113
  "path": "modules/dev-box/app/test/unit.mjs",
@@ -4992,7 +4997,7 @@
4992
4997
  {
4993
4998
  "path": "modules/hall-ui/public/oversight.css",
4994
4999
  "mode": "0000644",
4995
- "sha256": "9aa3fd3f662274571e049eb9931f8dd575eef527a624bc6a5d57102061f54936"
5000
+ "sha256": "13d46f29e77ba9ca30ab52de2e0413f2fcb68bfec44de032730d4d135530e4ba"
4996
5001
  },
4997
5002
  {
4998
5003
  "path": "modules/hall-ui/public/pair.css",
@@ -5312,7 +5317,7 @@
5312
5317
  {
5313
5318
  "path": "modules/hall-ui/public/watch.js",
5314
5319
  "mode": "0000644",
5315
- "sha256": "66a3fc7edf59112c1126c6c1c96244ac3cae1c0aea99f946c96b202d14f2cbac"
5320
+ "sha256": "058f25fcef75b3db5410ea7fa6b09515b0b7fddc64a7698c1a1c44e753c8a651"
5316
5321
  },
5317
5322
  {
5318
5323
  "path": "modules/hall-ui/public/watch.states.json",
@@ -5917,7 +5922,7 @@
5917
5922
  {
5918
5923
  "path": "modules/onboarding/routes/access-requests.js",
5919
5924
  "mode": "0000644",
5920
- "sha256": "ebebd2ce84727de2dff8411a6279aefb3792de51577a5ba04dc7d565cbce5494"
5925
+ "sha256": "3c66a404347ef1b93edcf5c63a902c9561b0a3f9bd9bc6c17e8a2a1f1b5f6707"
5921
5926
  },
5922
5927
  {
5923
5928
  "path": "modules/onboarding/routes/onboarding.js",
@@ -5934,10 +5939,15 @@
5934
5939
  "mode": "0000644",
5935
5940
  "sha256": "020279ae50f2f9e7a004fe571e5ca14493ad26bd22c395cc4805f759f5560b72"
5936
5941
  },
5942
+ {
5943
+ "path": "modules/platform-identity/applicant-profile.js",
5944
+ "mode": "0000644",
5945
+ "sha256": "12c1d00965d2d386480bdf94673ab6f140c8ee6ba44a5995a2d1b9593c012d20"
5946
+ },
5937
5947
  {
5938
5948
  "path": "modules/platform-identity/application-echo.js",
5939
5949
  "mode": "0000644",
5940
- "sha256": "3793f588d62eaa96a74da6bceb55043e8a9de35bc120dbe63f26bcc2b7e904da"
5950
+ "sha256": "8217660dc23b57ba0fb54d6375aca9aec190c7abb1d70f9d44d4e290da272c64"
5941
5951
  },
5942
5952
  {
5943
5953
  "path": "modules/platform-identity/canonical-profile.js",
@@ -6117,7 +6127,7 @@
6117
6127
  {
6118
6128
  "path": "modules/platform-identity/recruiter-sliver.js",
6119
6129
  "mode": "0000644",
6120
- "sha256": "e1385b83ac5bb82d13ffbe5070043c2fd0ca20385b3d62e8331ef917d9fca496"
6130
+ "sha256": "4f6854050e3c7459f698c550577fa97958a573d7f1981c9de4d29417af2669c0"
6121
6131
  },
6122
6132
  {
6123
6133
  "path": "modules/platform-identity/routes/connections.js",
@@ -6152,7 +6162,7 @@
6152
6162
  {
6153
6163
  "path": "modules/platform-identity/routes/sso.js",
6154
6164
  "mode": "0000644",
6155
- "sha256": "dbc8eeab6b1911b7fdc8b29e3d10871d355d2dead926a0742e9a816a72215dba"
6165
+ "sha256": "6921323c299eee14e2db75b2c1716e35c245f7c971e6aec57c2b339b106f4d87"
6156
6166
  },
6157
6167
  {
6158
6168
  "path": "modules/platform-identity/scouting-authz.js",
@@ -7562,12 +7572,12 @@
7562
7572
  {
7563
7573
  "path": "package-lock.json",
7564
7574
  "mode": "0000644",
7565
- "sha256": "ce40726e213f4a94fd3983d81c5919e4cc435fda1d666e7cf598fc2f963b8741"
7575
+ "sha256": "06ccc86510eed776b32a243487781c83fefcf48d33053f9f3a313e90b8364a49"
7566
7576
  },
7567
7577
  {
7568
7578
  "path": "package.json",
7569
7579
  "mode": "0000644",
7570
- "sha256": "6ce1fcd5c0378c8001b8249d8359d04ecfa5730e07dc3e21cda4952fcf1deeae"
7580
+ "sha256": "f145cacd4b2198eb4c6f6cd07a1ac30ebe653b3aaf34c7768cf0ad8dc0561db4"
7571
7581
  },
7572
7582
  {
7573
7583
  "path": "public-docs/index.html",
@@ -7767,7 +7777,7 @@
7767
7777
  {
7768
7778
  "path": "scripts/gds/build-cli-package.js",
7769
7779
  "mode": "0000644",
7770
- "sha256": "4b2428615e3c81c14a3b3144eca35fcb2c4523c4debef0f7d4596e5934857a6b"
7780
+ "sha256": "760dbc00a1d8ed5dfa2184e93f9106383bbb36c7fdd973ddfc67b7e03f3bc2f8"
7771
7781
  },
7772
7782
  {
7773
7783
  "path": "scripts/gds/bump-version.js",
@@ -7827,7 +7837,7 @@
7827
7837
  {
7828
7838
  "path": "scripts/gds/cli-lib.js",
7829
7839
  "mode": "0000644",
7830
- "sha256": "b08ad47b79417816a9f132e8149f78743eefb47b8824edf3380cdea23447e07f"
7840
+ "sha256": "beb9d466f7623cae12788f23d0e30fbf3a520fec769e416ce3ad6458b23ceb2b"
7831
7841
  },
7832
7842
  {
7833
7843
  "path": "scripts/gds/client-baseurl-guard.js",
@@ -8392,7 +8402,7 @@
8392
8402
  {
8393
8403
  "path": "scripts/gds/recall.js",
8394
8404
  "mode": "0000644",
8395
- "sha256": "8bb682eebeec8738082a2988f59697da6982d7c727e82352c169abc72a623b78"
8405
+ "sha256": "d0ad3b72fd0db0384f0d2212b50650e7e081beb62296ac3ea256047d6c3d8ec0"
8396
8406
  },
8397
8407
  {
8398
8408
  "path": "scripts/gds/redteam-patrol.js",
@@ -8802,7 +8812,7 @@
8802
8812
  {
8803
8813
  "path": "scripts/gds/start.js",
8804
8814
  "mode": "0000644",
8805
- "sha256": "81c60afdec1b7b05babe1eceaad38e0f27b932bef74e9dcb2cd3782c58524e30"
8815
+ "sha256": "061a477c618ca76aba4f3abfb2d22ad89ceb0bee46bac65b139ad8ee2f30413a"
8806
8816
  },
8807
8817
  {
8808
8818
  "path": "scripts/gds/status.js",
@@ -8814,6 +8824,11 @@
8814
8824
  "mode": "0000644",
8815
8825
  "sha256": "072c493cc549e16c6045abf5bf74a8cfe0004b45844c6f4fe9e846d55d88bf34"
8816
8826
  },
8827
+ {
8828
+ "path": "scripts/gds/surface.js",
8829
+ "mode": "0000644",
8830
+ "sha256": "cc730dc2d59f5d6f06bad92e9c2b69cb7835142deefd31a8dfdd58b300c38e30"
8831
+ },
8817
8832
  {
8818
8833
  "path": "scripts/gds/task.js",
8819
8834
  "mode": "0000644",
@@ -8982,7 +8997,7 @@
8982
8997
  {
8983
8998
  "path": "src/bongos/auth-admission.js",
8984
8999
  "mode": "0000644",
8985
- "sha256": "8dc4cc0ab956d387b8044c22e83fce883f2f0129ed27c03503f7348e9d4f00bb"
9000
+ "sha256": "aaa113f476a1d8196324379f8fd78aab22f577455fa0378bac3200b98e6a887f"
8986
9001
  },
8987
9002
  {
8988
9003
  "path": "src/bongos/auth-config.js",
@@ -9007,7 +9022,7 @@
9007
9022
  {
9008
9023
  "path": "src/bongos/auth.js",
9009
9024
  "mode": "0000644",
9010
- "sha256": "517e25a2ca9b9adafa993725802af9e7fc1708f0a5609102ddce593e82478ca2"
9025
+ "sha256": "eeb1831ab3d033598e0bcd57c30589cac583a1259c98b2dd49fbd9bb7725e11b"
9011
9026
  },
9012
9027
  {
9013
9028
  "path": "src/bongos/cascade-dispatch.js",
@@ -9272,7 +9287,7 @@
9272
9287
  {
9273
9288
  "path": "src/module-api.js",
9274
9289
  "mode": "0000644",
9275
- "sha256": "c1da85daf5425cfb3bbd7d8e29e6995e90ead6088565476973954ca366ef14f5"
9290
+ "sha256": "b928f49545fb1adb6ce26a620a73083be9d71257e6171a533d672190e86495a6"
9276
9291
  },
9277
9292
  {
9278
9293
  "path": "src/module-loader/catalog.js",
@@ -9364,6 +9379,11 @@
9364
9379
  "mode": "0000644",
9365
9380
  "sha256": "125edcec8c7d7536365b9675daa1fceb9fe072a30e2a4a68ee2911cd258a8c10"
9366
9381
  },
9382
+ {
9383
+ "path": "tests/adr_renumber_integrity.mjs",
9384
+ "mode": "0000644",
9385
+ "sha256": "bdc9d3678e12dee3f3252785ca82e06071421762c7146cbc1591d3fc2ec72180"
9386
+ },
9367
9387
  {
9368
9388
  "path": "tests/agents_spawn.mjs",
9369
9389
  "mode": "0000644",
@@ -9424,6 +9444,16 @@
9424
9444
  "mode": "0000644",
9425
9445
  "sha256": "ac84c8d8b1e2f54ef278506570ed657e68d2da4e136968b0df31b51f943af4bb"
9426
9446
  },
9447
+ {
9448
+ "path": "tests/applicant_profile_boundary.mjs",
9449
+ "mode": "0000644",
9450
+ "sha256": "beefe27f5f753185cf3513f6576e55ee44680760ff2d8eed4ced93d56ec92b77"
9451
+ },
9452
+ {
9453
+ "path": "tests/applicant_profile_read_bounds.mjs",
9454
+ "mode": "0000644",
9455
+ "sha256": "185fed67d8b3e65121af637e37392c21d4bcd892bb2b305704479f42ff5987c7"
9456
+ },
9427
9457
  {
9428
9458
  "path": "tests/application_echo.mjs",
9429
9459
  "mode": "0000644",
@@ -9859,6 +9889,11 @@
9859
9889
  "mode": "0000644",
9860
9890
  "sha256": "a57516ed9e904cf1c86fe99688415f834dcae1a19f01b22e986f199fa5473258"
9861
9891
  },
9892
+ {
9893
+ "path": "tests/cli_surface.mjs",
9894
+ "mode": "0000644",
9895
+ "sha256": "589ecc771ab18829049eabc6183939843072160e8476828e246eadd8ff6f0709"
9896
+ },
9862
9897
  {
9863
9898
  "path": "tests/cli_token_reissue.mjs",
9864
9899
  "mode": "0000644",
@@ -11292,7 +11327,7 @@
11292
11327
  {
11293
11328
  "path": "tests/module_api.mjs",
11294
11329
  "mode": "0000644",
11295
- "sha256": "39812c560bcd88e3b3a7938ed2447fbd4ab24b0f925e9662b375910bcbe7f1da"
11330
+ "sha256": "157d551b9a99320198f22d32ce335fa4166414dc1818d11ef23a0ea05ac80a76"
11296
11331
  },
11297
11332
  {
11298
11333
  "path": "tests/module_api_lazy.mjs",
@@ -11857,7 +11892,7 @@
11857
11892
  {
11858
11893
  "path": "tests/recruiter_sliver_boundary.mjs",
11859
11894
  "mode": "0000644",
11860
- "sha256": "cad269f556293e0caa234561f5764f90998a5d133b1e0147a5bbf22403c55e3a"
11895
+ "sha256": "a9ad552e7060898e5c94db8af29e63d7cad568294c240c48bef78859a0558ec6"
11861
11896
  },
11862
11897
  {
11863
11898
  "path": "tests/redteam_followup_kind.mjs",
@@ -12592,7 +12627,7 @@
12592
12627
  {
12593
12628
  "path": "tests/watch_applications_queue.mjs",
12594
12629
  "mode": "0000644",
12595
- "sha256": "b1f7df5aa40b89f8737c76d94def15c539514f7f439cca79a034bf37b922158d"
12630
+ "sha256": "52c5c056c33b0b2ee91076b29e24ea5b650a0148c603fdde5136e60d6c6e926a"
12596
12631
  },
12597
12632
  {
12598
12633
  "path": "tests/web_admission_status.mjs",
package/bin/bongos.js CHANGED
@@ -204,7 +204,16 @@ function main(argv) {
204
204
 
205
205
  if (COMPILED) return runBundled(verb, def, forwarded);
206
206
 
207
- const res = spawnSync(process.execPath, [scriptPath, ...forwarded], { stdio: 'inherit' });
207
+ // A person typed `bongos <verb>`, so this output is for a person: no `[otb-card]` directives
208
+ // addressed to a model, no `/builder-*` slash commands that exist only inside Claude Code
209
+ // (task 1003680, scripts/gds/surface.js). The skills and the card-delivery hook run
210
+ // `node scripts/gds/<verb>.js` DIRECTLY and never pass through here, so the agent card shape
211
+ // is untouched. `bongos exec` above is deliberately left alone — it is the agent's escape
212
+ // hatch into a core script.
213
+ const res = spawnSync(process.execPath, [scriptPath, ...forwarded], {
214
+ stdio: 'inherit',
215
+ env: { ...process.env, BONGOS_SURFACE: 'terminal' },
216
+ });
208
217
  if (res.error) {
209
218
  process.stderr.write(`bongos: failed to run "${verb}": ${res.error.message}\n`);
210
219
  return 1;
@@ -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
- - 358 operations across 55 resource groups
8
+ - 359 operations across 55 resource groups
9
9
 
10
10
  ## Install
11
11
 
@@ -793,6 +793,8 @@ function createClient(opts = {}) {
793
793
  "sso": {
794
794
  // POST /sso/activity/rollup — rank: public — POST /sso/activity/rollup
795
795
  postSsoActivityRollup: (args) => request("POST", "/sso/activity/rollup", { hasBody: true }, args),
796
+ // POST /sso/applicant-profile — rank: public — POST /sso/applicant-profile
797
+ postSsoApplicantProfile: (args) => request("POST", "/sso/applicant-profile", { hasBody: true }, args),
796
798
  // GET /sso/authorize — rank: public — GET /sso/authorize
797
799
  getSsoAuthorize: (args) => request("GET", "/sso/authorize", { hasBody: false }, args),
798
800
  // POST /sso/device/poll — rank: public — POST /sso/device/poll
@@ -792,6 +792,8 @@ function createClient(opts = {}) {
792
792
  "sso": {
793
793
  // POST /sso/activity/rollup — rank: public — POST /sso/activity/rollup
794
794
  postSsoActivityRollup: (args) => request("POST", "/sso/activity/rollup", { hasBody: true }, args),
795
+ // POST /sso/applicant-profile — rank: public — POST /sso/applicant-profile
796
+ postSsoApplicantProfile: (args) => request("POST", "/sso/applicant-profile", { hasBody: true }, args),
795
797
  // GET /sso/authorize — rank: public — GET /sso/authorize
796
798
  getSsoAuthorize: (args) => request("GET", "/sso/authorize", { hasBody: false }, args),
797
799
  // POST /sso/device/poll — rank: public — POST /sso/device/poll
@@ -355,6 +355,8 @@ export interface PostSessionsRequest { session_id: string; task_ids?: number[];
355
355
  export interface PostSessionsResponse { skipped?: boolean; reason?: unknown; session?: unknown; token_reward?: unknown }
356
356
  export interface PostSsoActivityRollupRequest { client_id: string; client_secret: string; github_id: StringifiableId; credits?: number; tasks_shipped?: number; karma?: number; rank?: string; disciplines?: unknown; recent_ships?: unknown }
357
357
  export interface PostSsoActivityRollupResponse { ok: boolean }
358
+ export interface PostSsoApplicantProfileRequest { client_id: string; client_secret: string; github_id: StringifiableId }
359
+ export interface PostSsoApplicantProfileResponse { profile: unknown }
358
360
  export interface PostSsoDevicePollRequest { device_code: string }
359
361
  export interface PostSsoDeviceStartRequest { origin: string }
360
362
  export interface PostSsoMembershipCheckInRequest { client_id: string; client_secret: string; github_id: StringifiableId; github_login: string; display_name?: string; avatar_url?: string; membership_kind?: string; role?: string }
@@ -1119,6 +1121,8 @@ export interface BongosClient {
1119
1121
  "sso": {
1120
1122
  /** POST /sso/activity/rollup — rank: public */
1121
1123
  postSsoActivityRollup(args: RequestArgs & { body: PostSsoActivityRollupRequest }): Promise<PostSsoActivityRollupResponse>;
1124
+ /** POST /sso/applicant-profile — rank: public */
1125
+ postSsoApplicantProfile(args: RequestArgs & { body: PostSsoApplicantProfileRequest }): Promise<PostSsoApplicantProfileResponse>;
1122
1126
  /** GET /sso/authorize — rank: public */
1123
1127
  getSsoAuthorize(args?: RequestArgs): Promise<ApiResponse>;
1124
1128
  /** POST /sso/device/poll — rank: public */
@@ -789,6 +789,8 @@ export function createClient(opts = {}) {
789
789
  "sso": {
790
790
  // POST /sso/activity/rollup — rank: public — POST /sso/activity/rollup
791
791
  postSsoActivityRollup: (args) => request("POST", "/sso/activity/rollup", { hasBody: true }, args),
792
+ // POST /sso/applicant-profile — rank: public — POST /sso/applicant-profile
793
+ postSsoApplicantProfile: (args) => request("POST", "/sso/applicant-profile", { hasBody: true }, args),
792
794
  // GET /sso/authorize — rank: public — GET /sso/authorize
793
795
  getSsoAuthorize: (args) => request("GET", "/sso/authorize", { hasBody: false }, args),
794
796
  // POST /sso/device/poll — rank: public — POST /sso/device/poll
@@ -0,0 +1,146 @@
1
+ # ADR 0260 — An application IS the consent, and the hub's own echo is the gate: the D7 applicant view
2
+
3
+ - **Status:** accepted
4
+ - **Date:** 2026-09-03
5
+ - **Task:** [task 1002972](https://cloudbongos.com/builders#/task/1002972) (PV7, goal 1000045 — criterion C4)
6
+ - **Relates to:** `docs/specs/<redacted>.md` **D7** (owner-signed 2026-08-16), [ADR 0215](<redacted>.md) (the D3 sliver this reuses), [ADR 0205](<redacted>.md) + security report 1000027 (client credentials name a project, never a person), [ADR 0201](<redacted>.md) D1.2/D1.3 (the vouch mark and the statusless echo), [ADR 0171](<redacted>.md) D4/D5 (indistinguishable nulls, no-store), [ADR 0016](<redacted>.md) (descriptive, never authority), [ADR 0083](<redacted>.md) (the doorway)
7
+
8
+ ## Context
9
+
10
+ Spec D7 asks for one thing in one sentence: a reviewer looking at an application
11
+ sees **what the applicant's own profile rules would already show them**, plus the
12
+ applicant's note, **evaluated live at review time**. Public account → the public
13
+ profile view. Private account → the D3 sliver. No new disclosure class.
14
+
15
+ Two halves of that already existed when this task opened. The applicant's note
16
+ reaches the project's queue through the join relay (task 1002330,
17
+ `relayApplicationNote`). The D3 projection exists and is proved
18
+ ([ADR 0215](<redacted>.md)). What did not
19
+ exist was the **rule** — which of the two views an applicant gets — and the
20
+ **path** by which a project's reviewer queue could ask the hub for it.
21
+
22
+ Three things about that path are not obvious, and each could have been got wrong
23
+ in a way that looked like a simplification.
24
+
25
+ ## Decision
26
+
27
+ ### D1 — The rule is one port composing the two EXISTING views, never a third shape
28
+
29
+ `modules/platform-identity/applicant-profile.js` answers
30
+ `null | { view:'public', account, cross_project } | { view:'sliver', sliver }`.
31
+
32
+ The public branch's rollup half is `getPublicProfileExtras` — the very port the
33
+ canonical `/u/<handle>` page calls. The private branch's projection is
34
+ `sliverShapeFor`, exported from `recruiter-sliver.js` — the very function the
35
+ recruiting route serializes. **Neither branch builds its own object**, because a
36
+ third five-key lookalike *is* the new disclosure class D7 forbids: free to drift
37
+ from the original field by field, and uncovered by the whitelist proof over it.
38
+
39
+ `tests/applicant_profile_boundary.mjs` asserts both key sets against the
40
+ **modules' own exported lists**, never against copies kept in the test.
41
+
42
+ ### D2 — The APPLICATION is the consent, not the recruiting opt-in
43
+
44
+ This is the one place the applicant view differs from the recruiter sliver, and
45
+ it is the decision most likely to be "tidied" away later.
46
+
47
+ `getRecruiterSliver` floors on `recruiterSliverVisibleSql` = consent **and
48
+ recruiting reach**, because a recruiter browsing a directory was invited by
49
+ nobody. An applicant has done something a browsing recruiter has not — spec D7
50
+ says it in those words: **"applying is an explicit act"**.
51
+
52
+ So routing this port through `getRecruiterSliver` would be wrong *in the
53
+ direction that looks safe*. `recruiter_discoverable` defaults to **false** for a
54
+ private account, so a private builder who deliberately applied would show their
55
+ reviewer **nothing at all**, and criterion C4 would be satisfied by an empty box.
56
+ The reach opt-out governs the *recruiting surface* — `account-visibility.js` says
57
+ exactly that of `scoutingListableSql` — and was never a gate on a door the
58
+ account itself knocked on.
59
+
60
+ **What the swap does NOT relax**, and this is what keeps it from meaning
61
+ "applying opts you into everything":
62
+
63
+ - `accountActiveSql` still floors it — `profile_state='active'` **and**
64
+ `terms_accepted_at IS NOT NULL`. A provisional account and one that never
65
+ accepted the platform terms disclose nothing, applying or not.
66
+ - `hide_stats` is untouched in both branches. Applying is consent to be **seen**,
67
+ never consent to be **counted**.
68
+ - The existence axis stays off, for ADR 0215 D1's reason: the private account is
69
+ precisely this rule's subject.
70
+
71
+ The proof asserts the swap **in both directions on the same account** — `PRIVOUT`
72
+ gets an applicant view and gets `null` from `getRecruiterSliver` — so the
73
+ tempting refactor cannot pass quietly.
74
+
75
+ ### D3 — The gate is the hub's own echo, because client credentials name a project and not a person
76
+
77
+ `POST /sso/applicant-profile` is authenticated by `client_id` + `client_secret`,
78
+ like its two siblings. **That authentication is not sufficient on its own**, and
79
+ this is the whole security argument for the route.
80
+
81
+ [ADR 0205](<redacted>.md) / security
82
+ report 1000027 is the lesson `/sso/membership/check-in` and `/sso/activity/rollup`
83
+ both had to learn: credentials prove *which project* is calling and say **nothing
84
+ about who the `github_id` in the body is**. Without a per-subject witness, this
85
+ route is a **bulk disclosure oracle** over every account on the platform —
86
+ private ones included, since D7's private branch is exactly the disclosure the
87
+ existence axis normally withholds. That is strictly worse than the rollup hole it
88
+ would mirror.
89
+
90
+ The witness is `platform_identity_application_echoes` — the statusless
91
+ "you applied to this project on this date" row from
92
+ [ADR 0201](<redacted>.md) D1.3. It is
93
+ the right witness because **the hub writes it itself**, in its own join relay,
94
+ only after the project answered the relayed application with a success. A client
95
+ cannot assert itself into that table. `applicationBacksProfileRead` is the exact
96
+ analogue of `membershipBacksRollup`: a boolean, and the narrowest cross-account
97
+ read the table has — the caller must already name both the account and the
98
+ project, and there is no listing by `client_id`, so a project learns nothing
99
+ about who applied that it was not already told at its own door.
100
+
101
+ It reuses the **same 30-day window**, and that is load-bearing rather than tidy:
102
+ when the echo stops matching, the view stops rendering, because the platform has
103
+ stopped vouching for the link between that queue row and that account.
104
+
105
+ Refusal is **404, never 403** — a 403 would confirm the `github_id` names a real
106
+ account. And the gate runs **before any account is read**: the proof asserts that
107
+ on the statement log, not on the response, because a gate that refuses *after*
108
+ reading has already done the disclosure work.
109
+
110
+ ### D4 — LIVE means nothing is stored, at either end
111
+
112
+ Nothing about the applicant is written to the `access_requests` row and nothing is
113
+ cached on the project. The profile is resolved on **every render**
114
+ (`attachApplicantProfiles`), so an applicant who hides their stats, goes private,
115
+ or loses their account between applying and being reviewed is answered by the next
116
+ read of the queue — criterion C2's "immediately". A snapshot taken at apply time
117
+ would keep publishing a consent that had since been withdrawn, for as long as the
118
+ request sat in the queue. The hub sends `Cache-Control: no-store`; the project
119
+ keeps nothing.
120
+
121
+ ### D5 — The queue keys on the VOUCH, never on `github_login`
122
+
123
+ `attachApplicantProfiles` resolves only rows carrying `applicant_github_id` — the
124
+ hub's own assertion about who filed the request (`core_230`). `POST /access-requests`
125
+ is the one **public unauthenticated write** on the GDS, so `github_login` is a
126
+ string anybody may type. Resolving a cross-project record against an unvouched
127
+ login is precisely the impersonation surface the vouch exists to close: file a
128
+ request naming a well-known builder, let the owner read that builder's excellent
129
+ record, and walk in. An unvouched row gets `profile: null`, always.
130
+
131
+ ### D6 — The credential stays in core; the module gets a narrow port
132
+
133
+ The hub call lives in `src/bongos/auth-admission.js` beside the two other calls
134
+ that hold the hub client secret, and `loadIdpConfig` stays **off** the doorway —
135
+ putting it there would hand every module the credential that speaks for the whole
136
+ project. `<redacted>(githubId)` is published instead: one
137
+ `github_id` in, one already-bounded view out. The module can neither widen the
138
+ request nor re-address it.
139
+
140
+ ## Consequences
141
+
142
+ - **The path is complete but DORMANT until R09 ([task 1002285](https://cloudbongos.com/builders#/task/1002285)) ships the vouch writer.** Nothing in the tree writes `applicant_github_id` today, so every queue row currently resolves `profile: null` and renders exactly as it did before. This is stated rather than glossed: the feature is wired end to end and proved, and it lights up on the day R09 lands its writer — the sequencing R08's own migration comment predicted. 1002972's declared dependency was PV3 alone; **the real dependency on R09 was missing from the graph.**
143
+ - The reviewer queue degrades rather than breaks: a hub outage, a self-hosted instance with no hub, and an applicant with no echo all render a row with its login, note and vouch pill and no profile block.
144
+ - `MAX_LIVE_PROFILE_READS = 25` caps per-render hub round-trips against a 200-row queue. It bites on vouched rows in list order, so the rows a reviewer acts on carry the profile. Raising it buys detail and costs latency; it changes nothing about what may be disclosed.
145
+ - `recruiter-sliver.js` now has a second first-party reader. The sole-reader guard in `tests/recruiter_sliver_boundary.mjs` was widened with a stated reason **and strengthened**: every listed reader is now checked to not *branch* on a sliver field, so the property the guard protects — descriptive data is never consulted as authority — is enforced rather than trusted.
146
+ - **Still not built:** the apply UI's *"your application shares: …"* disclosure line D7 names. There is no apply UI anywhere — `POST /my-projects/join` has no browser caller — so the sentence has no page to live on. It belongs with whichever task builds that surface, and is filed rather than silently dropped.
@@ -351,3 +351,4 @@ This keeps the decision history honest and traceable.
351
351
  | 0257 | [**Auth resolves before the hall mounts anything, and a widget's boot read may never navigate** ([task 1003673](https://cloudbongos.com/builders#/task/1003673) · goal 1000063 — *The front door*). An invite-only instance could not admit its FIRST builder, and the symptom lied about where the fault was: an owner saw an empty Access-requests queue and no approve button, because **signing in does not file a request** — only the landing's *Request access* form does, and that form was unreachable. A signed-out visitor to `/builders` was bounced to GitHub, refused as a first-timer ("request access first"), and pointed back at `/builders` to be bounced again. Reproduced against `main`, not just an old pin. `builders.js` was already careful — `/me` is read `softAuth` and a 401 there renders the landing — but `DOMContentLoaded` called `mountHallWidgets()` FIRST, synchronously, and `goals.js`'s mount-time read of `/goals` goes through a `getJSON` with no `softAuth`, which answers 401 by NAVIGATING. The widget's read raced `renderLanding()` and won. **Decision: the front door settles which page this is before anything else runs** — `boot()` reads `/me` (soft), then either renders the landing and stops (nothing mounts, nothing else fetches) or mounts the hall, handing the resolved payload to `loadAll()` so the page still asks once. Widgets are member surfaces (`renderLanding` hid them all after the fact anyway), so not mounting them while signed out removes the CLASS rather than the one instance of it that was found; **a widget's own boot read may never navigate** is the belt beside it (`goals.js` reads soft and renders nothing without a session). A 401 during boot is not an instruction to go and sign in — only a user action is. `tests/hall_landing_boot.mjs` executes the real `builders.js` in a DOM stub and was confirmed to FAIL against the pre-fix file. Rejected: patching `goals.js` alone (it was merely first); making the 401 redirect soft everywhere (an expired session mid-visit SHOULD be sent to sign in — the line is boot vs user action); open enrollment as the cure (removes the gate [ADR 0050](<redacted>.md) chose deliberately instead of repairing the door).](<redacted>.md) | hall-ui / admission / front door |
352
352
  | 0258 | [**The public CLI is a generated client-only package, and its file list is proven by running it** ([task 1003679](https://cloudbongos.com/builders#/task/1003679) · goal 1000054 — *A newcomer can build without the UI*). The core ships private as `@bongos/core` ([ADR 0108](<redacted>.md)), so a newcomer with no credential can install NOTHING and the web hall is the only way in — the owner's words: "how am I supposed to easily take on tasks as a new builder? I should be able to do everything without the UI." Open task 1002025 proposed publishing the core, which ships the SERVER and bypasses [ADR 0099](<redacted>.md)'s redaction pipeline (dormant until ~2026-11). **Decision: generate a separate public `@cloudbongos/cli` from the core and do not publish the core.** Four load-bearing parts. (1) SCOPED NAME, because npm shares one namespace between org names and unscoped packages — the bare `cloudbongos` is unpublishable *precisely because the owner owns that org*, which npm reports as "invalid" and reads like the name is taken; and a package name cannot be created on the npm website at all (it exists on first publish, which is why "Add Existing Package" answered `Forbidden`). (2) THE FILE LIST IS DECLARED AND PROVEN BY BEHAVIOUR, never computed — a static require-closure CANNOT answer this, because `src/module-api.js` is the doorway a module may only import ([ADR 0083](<redacted>.md)) and it *names* every kernel capability, so a static walk sees 33 `src/` files from `start.js` alone where runtime resolves **one** (`api-prefix.js`, which imports nothing) — the same trap as task 1003677's unsatisfiable done-when. So the test packs the tarball, installs it into an empty dir with NO repo, and runs every verb: it may fail for want of a session, never for a missing file. That caught two real holes on its first strengthened run — `clients/bongos-client/index.mjs`, reached by a dynamic `import()` no `require()` walk can see, and a hand-listed `files` array that dropped `clients/` and produced a tarball which installed cleanly then died on first use; both are now build-time refusals, and the `files` array is derived from the manifest. (3) `claim`/`ship`/`dev`/`serve`/`module`/`upgrade`/`onboard`/`doctor`/`exec`/`package-core` are ABSENT and the CLI says WHY plus the next step (`bongos shell` → a cloud box with the full CLI) — a bare `unknown command` teaches nothing, which is the exact failure being fixed. The supported journey is `login` → `start` → `shell`. (4) A REDACTION GATE fails the build closed on non-loopback IPv4 (RFC 5737 doc ranges exempt), token/key shapes, and every domain + owner login readable from the instance's own `config/branding.json` — needles come from host config so the core carries no instance identity ([ADR 0062 §7](<redacted>.md)); `cloudbongos.com` is allowlisted as public by design. No `repository` field while the core repo is private (it would 404 for every user and publish the owner's login for nothing). 31 files, one dependency (`undici`), version independent of the core's ([ADR 0161](<redacted>.md)). `@bongos/client` is VENDORED, not depended on — a public package depending on a private one is uninstallable. **The owner runs `npm publish`; a builder must not.** Task 1002025 is superseded. Rejected: publishing the core; deriving from a static closure (structurally impossible past the doorway); waiting for the mirror (dormant, and this is a build product not a source release); an unscoped name; depending on `@bongos/client`; a degraded `claim`; shipping all of `src/` to make lazy getters safe.](<redacted>.md) | cli / distribution / public surface |
353
353
  | 0259 | [**A project’s departure from the public list is public, and the copy says so** ([task 1003672](https://cloudbongos.com/builders#/task/1003672) · goal 1000046 — *Project creation*). Decides the leak [ADR 0255](<redacted>.md) §6.2 deliberately filed rather than implied fixed. The public projects feed publishes a named list plus a count of anonymous rows, so when a project flips to stealth its named row LEAVES the identified block while the count RISES BY ONE — *“Mercury is gone, and there is one more black hole”* names it, and the moment, from a single diff of two snapshots. **Everything [ADR 0182](<redacted>.md) D2a does is defeated by it**, because the identity was never carried ON the anonymous row (`STEALTH_ANONYMOUS_KEEPS` is an allow-list precisely so a column added next year is anonymous by default) — it is carried by the ABSENCE of the named row that used to sit beside it. **Larger than the channel 0255 closed**, which needed repeated sampling and correlation; this one is exact and needs two reads. No `ORDER BY` change touches it: it is inherent in publishing a named list at all, so every fix is a product decision with a visible cost and the owner’s call, not a builder’s. **Decision: accept it, and make the copy say so.** Two reasons it is the right end of the trade and not merely the cheapest — (1) it is ALREADY the honest reading of the existing promise, since D2a made existence and count public *by design*, and a count that is public and truthful is a count whose CHANGES are public; (2) both alternatives damage the control they defend. A **delay window** means the owner clicks the privacy setting and is not private yet *without knowing it*, converting an information leak into a false belief — strictly worse, and the wrong direction for a privacy control. **Decoys** spend the count’s truthfulness (it is truthful today) on a guarantee that is still only probabilistic, and a decoy scheme needs some way to tell padding from real rows — the exact stable per-project key D2a forbids. The promise stealth actually keeps is about the project’s CONTENTS, not the TRANSITION: an anonymous row stays anonymous (no name, tagline, art, origin, or key surviving to the next snapshot); what is not private is stepping off a published list readers may keep copies of. So the copy — which read *“its name, and everything else about it, stay yours”*, every clause true and the paragraph as a whole overselling, because a reader takes it to cover the switch — now names the boundary in BOTH places an owner meets the setting (the choice’s blurb and the what-changes fold), in the plain register 0182’s interview fixed for this card. **A privacy control that oversells is worse than one that admits a limit**, the same standard 0255 §6 set for itself. **No behaviour changes** in `projects-feed.js` — the feed, the redaction allow-list and the keyed rotating permutation are decided FINISHED, not insufficient. `tests/projects_hub.mjs` pins the sentence in both surfaces (the copy IS the fix, so a future edit tightening the blurb must not quietly drop it) and re-applies the no-space-metaphor ban to it. Carries 0255 §6.3’s smaller residual so both live in one place: within one 24h epoch the permutation is stable, so same-day snapshots tell black holes apart (not name them) — accepted on the same reasoning, since the fix is a per-request permutation, which breaks pagination exactly as `ORDER BY random()` was rejected for. Guards recorded for any future attempt: no stable per-project key on an anonymous row, and no anonymous ordering an observer can predict or correlate. Rejected: the delay; decoys; dropping the named list (protects a privacy nobody asked for at the cost of the feature); and saying nothing, which is the failure mode this closes.](<redacted>.md) | platform-identity / privacy boundary / copy honesty |
354
+ | 0260 | [**An application IS the consent, and the hub’s own echo is the gate** ([task 1002972](https://cloudbongos.com/builders#/task/1002972) · goal 1000045, criterion C4). Privacy spec **D7** asks that a reviewer see what the applicant’s OWN profile rules would already show, evaluated LIVE at review time. The rule is ONE port composing the two EXISTING views — `getPublicProfileExtras` for a public account, `recruiter-sliver`’s own `sliverShapeFor` for a private one — because a third five-key lookalike **is** the new disclosure class D7 forbids. **The one deliberate difference from the D3 sliver:** `getRecruiterSliver` floors on recruiting REACH, and reusing it verbatim would be wrong in the direction that looks safe — `recruiter_discoverable` defaults to FALSE for a private account, so a private builder who deliberately applied would show their reviewer NOTHING and C4 would be satisfied by an empty box. D7 settles it: *“applying is an explicit act”*. The swap relaxes REACH only; `accountActiveSql` (active **and** terms accepted) and `hide_stats` still floor it, and the proof asserts both directions on the same account so the tempting refactor cannot pass quietly. **The federated route’s gate is the whole security story** ([ADR 0205](<redacted>.md) / security report 1000027): client credentials name a PROJECT and never a person, so `POST /sso/applicant-profile` authenticated alone is a bulk disclosure oracle over every account on the platform, private ones included. `applicationBacksProfileRead` demands a HUB-WRITTEN echo for exactly (this account, this client) inside the same 30-day window — a row only the hub’s own join relay creates — refusing **404, never 403**, and BEFORE any account is read (asserted on the statement log, because a gate that refuses after reading has already done the disclosure work). LIVE means nothing is stored at either end; the queue keys on the VOUCH and never on `github_login` (the one PUBLIC unauthenticated write makes an unvouched login an impersonation surface); and the hub client secret stays in core behind a narrow doorway port. **The path is complete but DORMANT until R09 ([task 1002285](https://cloudbongos.com/builders#/task/1002285)) ships the vouch writer** — nothing writes `applicant_github_id` today, and that dependency was missing from 1002972’s graph. Rejected: routing the port through `getRecruiterSliver`, a snapshot at apply time, resolving by `github_login`, and putting `loadIdpConfig` on the doorway.](<redacted>.md) | platform identity / privacy |