@bongos/core 1.19.474 → 1.19.476

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 (33) hide show
  1. package/.bongos-core.json +56 -36
  2. package/docs/adr/0229-the-title-is-the-door-and-the-record-exits-to-where-it-was-opened-from.md +38 -0
  3. package/docs/adr/README.md +1 -0
  4. package/docs/copy-inventory.md +255 -258
  5. package/docs/copy-registry.json +376 -414
  6. package/docs/module-api-changelog.md +4 -0
  7. package/modules/hall-ui/public/builders.js +13 -64
  8. package/modules/hall-ui/public/dom-utils.js +2 -1
  9. package/modules/hall-ui/public/goals-page.js +4 -3
  10. package/modules/hall-ui/public/goals-render.js +5 -3
  11. package/modules/hall-ui/public/hall-render.js +10 -142
  12. package/modules/hall-ui/public/hall-widgets.js +6 -16
  13. package/modules/hall-ui/public/index.html +0 -3
  14. package/modules/hall-ui/public/index.states.json +3 -2
  15. package/modules/hall-ui/public/profile.js +1 -1
  16. package/modules/hall-ui/public/roster.html +63 -0
  17. package/modules/hall-ui/public/roster.js +227 -0
  18. package/modules/hall-ui/public/roster.states.json +100 -0
  19. package/modules/hall-ui/public/shell.js +28 -4
  20. package/modules/hall-ui/public/style.css +20 -18
  21. package/modules/hall-ui/public/task-detail.js +16 -4
  22. package/modules/hall-ui/public/task.html +5 -3
  23. package/modules/hall-ui/public/task.js +58 -4
  24. package/modules/hall-ui/public/task.states.json +1 -0
  25. package/modules/hall-ui/public/work.js +10 -11
  26. package/package-lock.json +2 -2
  27. package/package.json +1 -1
  28. package/src/module-api.js +1 -1
  29. package/tests/hall_board_world.mjs +69 -0
  30. package/tests/hall_nav.mjs +36 -1
  31. package/tests/hall_page_gate_map.mjs +8 -0
  32. package/tests/hall_record_world.mjs +83 -4
  33. package/tests/rank_tier_single_source.mjs +8 -1
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.474",
6
- "core_contract": "1.19.474",
7
- "source_commit": "cbed4469409e30c69a32b07f647a33f1ad72764e",
5
+ "core_version": "1.19.476",
6
+ "core_contract": "1.19.476",
7
+ "source_commit": "631886be90fa74af3353170d0f5e351a5cf100c7",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-02T15:40:28.383Z",
9
+ "built_at": "2026-09-02T16:34:36.724Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
- "docs_redacted": 412,
12
+ "docs_redacted": 413,
13
13
  "agent_docs_stubbed": 24,
14
- "functional_verbatim": 1835,
14
+ "functional_verbatim": 1838,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 2271,
20
- "tree_sha256": "4f93b9431f1f6511a3839bfad51625dc009b96fcece556e986af904e9ab62d63",
19
+ "file_count": 2275,
20
+ "tree_sha256": "06f67b13e31a022107f61818ab5b0dbc693fcd5c8fd30d8f58e27e16e4b7a113",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -1649,10 +1649,15 @@
1649
1649
  "mode": "0000644",
1650
1650
  "sha256": "5dd1936d04cd259b2f56eea3c272ea35850d9272b68d9e314692dc58c04e4aed"
1651
1651
  },
1652
+ {
1653
+ "path": "docs/adr/0229-the-title-is-the-door-and-the-record-exits-to-where-it-was-opened-from.md",
1654
+ "mode": "0000644",
1655
+ "sha256": "9166e49d44a97a1bb99d5604fb19579eaa1f3a491026bcf84538ecd5c0494880"
1656
+ },
1652
1657
  {
1653
1658
  "path": "docs/adr/README.md",
1654
1659
  "mode": "0000644",
1655
- "sha256": "7957f1adf1329b402729e4b8ea14ea5279ef9b58b38ac5c6e711107909d13b8f"
1660
+ "sha256": "4a6c61e9643282e9754f845ce4afb245c9c76b1eb827fc8ad52b973e594bd950"
1656
1661
  },
1657
1662
  {
1658
1663
  "path": "docs/api-reference.md",
@@ -1687,12 +1692,12 @@
1687
1692
  {
1688
1693
  "path": "docs/copy-inventory.md",
1689
1694
  "mode": "0000644",
1690
- "sha256": "aeebc3c4c680301220b1d6109608e04bff778041934da47a4eb81c7a63f66141"
1695
+ "sha256": "504ab0bb1e8cd0c643a0aa27ea7e018278fc86d573291ccb654cc936488eb0dc"
1691
1696
  },
1692
1697
  {
1693
1698
  "path": "docs/copy-registry.json",
1694
1699
  "mode": "0000644",
1695
- "sha256": "a56f2f9079612548b0c1232afc214e1f94c3a6f861bf3657ec5d4c6f897a25ed"
1700
+ "sha256": "e8b3759291fa7f9853bb901e230dcfeb75d5f8e8f4908983f65c2a82c36ae46f"
1696
1701
  },
1697
1702
  {
1698
1703
  "path": "docs/design/apex-pass-2-direction.md",
@@ -2142,7 +2147,7 @@
2142
2147
  {
2143
2148
  "path": "docs/module-api-changelog.md",
2144
2149
  "mode": "0000644",
2145
- "sha256": "0e5739856bba05e57c75990fa302988e3e9b3f6d4ec8cc47cbcdd975daebad32"
2150
+ "sha256": "e8957fb9ba93280d840032cdfd55ac8e8aaaf8fc7bc7a80df4e1e78a3cc9bcd1"
2146
2151
  },
2147
2152
  {
2148
2153
  "path": "docs/modules-contract.md",
@@ -4032,7 +4037,7 @@
4032
4037
  {
4033
4038
  "path": "modules/hall-ui/public/builders.js",
4034
4039
  "mode": "0000644",
4035
- "sha256": "e8a0e98de75cab1f595f7541c0732b032ea0060e1b56526c52f352fcbd4a26a3"
4040
+ "sha256": "3dfbefd08de01bc8215452dcc7583f8c7aa00770ad646185ae0bb47bb05184bd"
4036
4041
  },
4037
4042
  {
4038
4043
  "path": "modules/hall-ui/public/chime.wav",
@@ -4137,7 +4142,7 @@
4137
4142
  {
4138
4143
  "path": "modules/hall-ui/public/dom-utils.js",
4139
4144
  "mode": "0000644",
4140
- "sha256": "f0d0b87de9c678c3728c20eeb43818a39d35f2507b4b55ac58e2a4663ff2ceac"
4145
+ "sha256": "1a336eb7c5754f37281a6875806436db22560a9589b9ba7381f58202d5627968"
4141
4146
  },
4142
4147
  {
4143
4148
  "path": "modules/hall-ui/public/drachmae.html",
@@ -4192,12 +4197,12 @@
4192
4197
  {
4193
4198
  "path": "modules/hall-ui/public/goals-page.js",
4194
4199
  "mode": "0000644",
4195
- "sha256": "aed3d6f14dc40d4ff6b07f186bc34e6d96f6ba158250b0447fc71514f6bc4980"
4200
+ "sha256": "fb606d5d1e2fb4ea6c7f8d6b36cc8bc9a877e91748db06d15b05b72e5140e8eb"
4196
4201
  },
4197
4202
  {
4198
4203
  "path": "modules/hall-ui/public/goals-render.js",
4199
4204
  "mode": "0000644",
4200
- "sha256": "6c3ad030507152756dd84c0c06080a3a073a62943bc300cb44b202da5554c5a1"
4205
+ "sha256": "93f189853cab9851c737496435d08df2921e61187fa9c5ee015c9d314e83ad87"
4201
4206
  },
4202
4207
  {
4203
4208
  "path": "modules/hall-ui/public/goals.css",
@@ -4247,12 +4252,12 @@
4247
4252
  {
4248
4253
  "path": "modules/hall-ui/public/hall-render.js",
4249
4254
  "mode": "0000644",
4250
- "sha256": "5a09364d61b41b2cf7e970370ee993e41bbe85d0dbf5be89e979d436ac1e48a1"
4255
+ "sha256": "45c8ad11a4fcb4f9dee90bcf89e693f0d879ce2c15bbe3bdcf7d4cf0ef1e6d6f"
4251
4256
  },
4252
4257
  {
4253
4258
  "path": "modules/hall-ui/public/hall-widgets.js",
4254
4259
  "mode": "0000644",
4255
- "sha256": "ee838da0a5d076ab50b68129a35c9f5144f94a2a41ea7d9b8d14a22e4c8d1f28"
4260
+ "sha256": "8aa90c0bb15a19f13295da6225d270bbbdaf4b5336240f03cbc05abe192e87c0"
4256
4261
  },
4257
4262
  {
4258
4263
  "path": "modules/hall-ui/public/harbor.css",
@@ -4317,12 +4322,12 @@
4317
4322
  {
4318
4323
  "path": "modules/hall-ui/public/index.html",
4319
4324
  "mode": "0000644",
4320
- "sha256": "339e1fdbb5e87039c8196ebbf2aedc342111eccc9b2dc9509244559d010fffbd"
4325
+ "sha256": "f6f292816d5d5483b6b0fa2196821400ac87d7f5b4ea75b7746a44b9c0286b90"
4321
4326
  },
4322
4327
  {
4323
4328
  "path": "modules/hall-ui/public/index.states.json",
4324
4329
  "mode": "0000644",
4325
- "sha256": "8a9745eb41f5ee18f42a49be8aaf8dbdc28656767e570022fdf19737596c3628"
4330
+ "sha256": "ac0bd95920b845d11ba24bf3e75ac169c13820861ad73776a6b174a5386e8b5f"
4326
4331
  },
4327
4332
  {
4328
4333
  "path": "modules/hall-ui/public/md-reader.js",
@@ -4457,7 +4462,7 @@
4457
4462
  {
4458
4463
  "path": "modules/hall-ui/public/profile.js",
4459
4464
  "mode": "0000644",
4460
- "sha256": "6433253093196f674142979c881ea8dc735d17841aeb99ddbc0e69b621b4b7e0"
4465
+ "sha256": "79d43fd7b8ff8957cc6a28e87ba16e1da92f1642bb1e54e63957f9df9eb123bc"
4461
4466
  },
4462
4467
  {
4463
4468
  "path": "modules/hall-ui/public/ranks.html",
@@ -4504,6 +4509,21 @@
4504
4509
  "mode": "0000644",
4505
4510
  "sha256": "cdc1b17139a9752fb90c6866a7c2ec114d3255002176fd5a4812a1b1df9b4617"
4506
4511
  },
4512
+ {
4513
+ "path": "modules/hall-ui/public/roster.html",
4514
+ "mode": "0000644",
4515
+ "sha256": "4edbfb63eb08a0c0e96efec59f6559b62bc4bed24eaebe3ef4169382a8e1124c"
4516
+ },
4517
+ {
4518
+ "path": "modules/hall-ui/public/roster.js",
4519
+ "mode": "0000644",
4520
+ "sha256": "072dcdb71d6a0b4cb95dca627853273ece886bf431c923a314c72454188bad64"
4521
+ },
4522
+ {
4523
+ "path": "modules/hall-ui/public/roster.states.json",
4524
+ "mode": "0000644",
4525
+ "sha256": "ffd8233231152bee1c1815afc61e7422bdc3cc605cf5d8a5cd0609f9d04b4323"
4526
+ },
4507
4527
  {
4508
4528
  "path": "modules/hall-ui/public/sessions.css",
4509
4529
  "mode": "0000644",
@@ -4547,7 +4567,7 @@
4547
4567
  {
4548
4568
  "path": "modules/hall-ui/public/shell.js",
4549
4569
  "mode": "0000644",
4550
- "sha256": "cef95769bb5a8629aa2a75733a776d2e398ef4ec1b65b1c28bfc6a2bb75f9dbe"
4570
+ "sha256": "1779bddc395a97fa0a5934838c483cfa8cc1b331dc354407521fa12c1a815727"
4551
4571
  },
4552
4572
  {
4553
4573
  "path": "modules/hall-ui/public/ship.wav",
@@ -4557,12 +4577,12 @@
4557
4577
  {
4558
4578
  "path": "modules/hall-ui/public/style.css",
4559
4579
  "mode": "0000644",
4560
- "sha256": "9bb44b2d5ad45342ebee328430c793d0e48ce921362e87501002a5c73fbe71c9"
4580
+ "sha256": "8fb991f5806a8b94b28e5c32451593c20d70f640ec8407d9c3316c2ae76b1352"
4561
4581
  },
4562
4582
  {
4563
4583
  "path": "modules/hall-ui/public/task-detail.js",
4564
4584
  "mode": "0000644",
4565
- "sha256": "e549efef4a7453709830769b3f224811ee5f0a1342a362c05d5601df82e7e938"
4585
+ "sha256": "911cd948103fcebe94c0388bea01675b2dbb796b63a7d5ce176f70e0062912a2"
4566
4586
  },
4567
4587
  {
4568
4588
  "path": "modules/hall-ui/public/task-lib.js",
@@ -4577,17 +4597,17 @@
4577
4597
  {
4578
4598
  "path": "modules/hall-ui/public/task.html",
4579
4599
  "mode": "0000644",
4580
- "sha256": "6329e908e59610a668ac4ea06d2e8bf31ee3a753568cda219347b4db91fa47d9"
4600
+ "sha256": "7644447bc2ef50d21d185de51ec3fc06ca7f8ed78aba71b70658d3869f73563b"
4581
4601
  },
4582
4602
  {
4583
4603
  "path": "modules/hall-ui/public/task.js",
4584
4604
  "mode": "0000644",
4585
- "sha256": "d70be94aa8fa125e9ce681e07fd461773cce4228218cb232881301917b5a1e39"
4605
+ "sha256": "893cc2b1c621b464fc0e8b350b55e4d6930ca7b27125c85be0b8ad45275e9781"
4586
4606
  },
4587
4607
  {
4588
4608
  "path": "modules/hall-ui/public/task.states.json",
4589
4609
  "mode": "0000644",
4590
- "sha256": "0cdf5f1508ecb750f7476f76578c8d9eeda5410a0a22fb74c05d10baffe174ef"
4610
+ "sha256": "befd2edcf337712426f900ed7619ffc5a41b3559d34fb98d2dcaf7268e96b5ad"
4591
4611
  },
4592
4612
  {
4593
4613
  "path": "modules/hall-ui/public/triumph.wav",
@@ -4627,7 +4647,7 @@
4627
4647
  {
4628
4648
  "path": "modules/hall-ui/public/work.js",
4629
4649
  "mode": "0000644",
4630
- "sha256": "f40ca87f8656b44111d65ed4467dee06c23708c4d8daa34126d2ed0bc0c65029"
4650
+ "sha256": "7283ebe918bb1ab2eedf4f88e7ccfc8fa7ab79b790460c1886c89cabc2632f9c"
4631
4651
  },
4632
4652
  {
4633
4653
  "path": "modules/hall-ui/public/work.states.json",
@@ -6672,12 +6692,12 @@
6672
6692
  {
6673
6693
  "path": "package-lock.json",
6674
6694
  "mode": "0000644",
6675
- "sha256": "7ac1efdfd6206b8eed68fe2b06c4020c2e93652369dc9c43d5ef5b8615c09adb"
6695
+ "sha256": "66d0470b8bbef92b19fb646d6f7d03a07dbccdc43159a4a10cbfa7ef9edf234e"
6676
6696
  },
6677
6697
  {
6678
6698
  "path": "package.json",
6679
6699
  "mode": "0000644",
6680
- "sha256": "644b5140f039216491e355536933459469d0fd23b26c2a3c45b2368d8c2e0462"
6700
+ "sha256": "eeae6c47ef2d23504ee84eaf0681717f9f78aee6e87ebd02af794e235b9127d7"
6681
6701
  },
6682
6702
  {
6683
6703
  "path": "public-docs/index.html",
@@ -8332,7 +8352,7 @@
8332
8352
  {
8333
8353
  "path": "src/module-api.js",
8334
8354
  "mode": "0000644",
8335
- "sha256": "9e3d4023c04a2851d2af8f0ddcb41d9883ffbdf06d865c6b2890fa6ebdd5e9e6"
8355
+ "sha256": "e679812470446860395dcbc799ef21b0459c86f89e954ed9d50b4428f26be6ff"
8336
8356
  },
8337
8357
  {
8338
8358
  "path": "src/module-loader/catalog.js",
@@ -9707,7 +9727,7 @@
9707
9727
  {
9708
9728
  "path": "tests/hall_board_world.mjs",
9709
9729
  "mode": "0000644",
9710
- "sha256": "bb66cec6b745cd6e50efd8828d05a75aa68fbc50ed43d8a49988c1246bfd3324"
9730
+ "sha256": "f9f5365b5cd80f73d843508110af13bd8042e1f9249ae8b545db9bac64c6529a"
9711
9731
  },
9712
9732
  {
9713
9733
  "path": "tests/hall_catalog_world.mjs",
@@ -9727,7 +9747,7 @@
9727
9747
  {
9728
9748
  "path": "tests/hall_nav.mjs",
9729
9749
  "mode": "0000644",
9730
- "sha256": "3754e9c4880f96da1c0b77e4d81c1652b94c878872b663b3c421f5aef1bf0712"
9750
+ "sha256": "05ed6e312636cd1f4af9a26daf8c030c5892e2dc9f24ec6969fa224db5cf0525"
9731
9751
  },
9732
9752
  {
9733
9753
  "path": "tests/hall_oversight_world.mjs",
@@ -9737,7 +9757,7 @@
9737
9757
  {
9738
9758
  "path": "tests/hall_page_gate_map.mjs",
9739
9759
  "mode": "0000644",
9740
- "sha256": "a66273f4a3e376d0055afb0e41fac308e84eb30b2f468f8e36831f3aad662f98"
9760
+ "sha256": "d529685063de8c4aa6676abf86f1180bb9cd13d27a3c372d8a3d9cf078af1999"
9741
9761
  },
9742
9762
  {
9743
9763
  "path": "tests/hall_palette.mjs",
@@ -9757,7 +9777,7 @@
9757
9777
  {
9758
9778
  "path": "tests/hall_record_world.mjs",
9759
9779
  "mode": "0000644",
9760
- "sha256": "09a7aadf345c75208af035a0f6fad1ecfe10ff056b025d885361647d4e8127b9"
9780
+ "sha256": "b96e31d2de9be8b017e222815a63b3ad92cfc8f24dd5f2b3e9aac7931daeadc7"
9761
9781
  },
9762
9782
  {
9763
9783
  "path": "tests/hall_reference_world.mjs",
@@ -10627,7 +10647,7 @@
10627
10647
  {
10628
10648
  "path": "tests/rank_tier_single_source.mjs",
10629
10649
  "mode": "0000644",
10630
- "sha256": "572da54574a1f86c4cc7bf3ecc361941931ee71db5db0f8d0496d355e57f2871"
10650
+ "sha256": "7840efed42f6960ff25ad9fdd182f979049280566de4831973e5476ab5a6e8ed"
10631
10651
  },
10632
10652
  {
10633
10653
  "path": "tests/rate_limit_public.mjs",
@@ -0,0 +1,38 @@
1
+ # ADR 0229 — The title is the door into a task record, and the record exits to where it was opened from
2
+
3
+ **Status:** accepted · **Date:** 2026-09-02 · **Task:** [task 1003468](https://cloudbongos.com/builders#/task/1003468) · **Goal:** 1000081 (the builders' hall in the chrome world) · **Supersedes in part:** the "only way in" half of task 1869's card decision (task 1759's primary interaction stands)
4
+
5
+ ## Context
6
+
7
+ The owner, working the live hall on 2026-09-01, reported two halves of one journey. Into a record: *"you cant click into tasks that are todo they just get selected, you can only click into completed tasks it seems like."* Out of it: *"when you click into a completed task from a goal the only option from the ui is to go back to the tasks page even if you came from the goal. It should route you to the page where you clicked into a task card."*
8
+
9
+ Both were shipped behaviour, not re-skin regressions. Tasks 1759 and 1869 made multi-select the primary interaction on a claimable card so a builder can bulk-claim, and added `.task__open`, a 26×26 button holding a 12px glyph in `--ink-faint`, as **the only way** from a claimable card into the record. A read-only card (active, backlog) opened on a body click; the Completed ledger's rows always opened. So the owner's read was exact: the completed ones open, the to-do ones select. The owner of the instance not finding the glyph is the strongest evidence an affordance can fail on.
10
+
11
+ On the way out, `task.js` hardcoded `Open the work board` in three renders (the record, not-found, and the failed read) and `task.html`'s crumb a fourth (`Back to tasks` → `/work`). No referrer, no origin parameter, no history: a builder who walked goal → task had only the browser's own Back, and the two failure renders sent them somewhere they had never been.
12
+
13
+ ## Decision
14
+
15
+ **The title is the door.** `.task__title` carries a real link, `.task__link`, on both surfaces that render the card — the work board (`work.js`) and the goal detail To-do pane (`goals-render.js`). This is the convention the rest of the hall already uses (`.gr-title` on the goals list, the ledger rows), so it needs no teaching. The card body still toggles selection, the checkbox still does what it does, bulk claim is untouched: both click handlers `return` on the link and let the browser navigate (a middle-click opens a tab, which a button never gave). Once a control, the title owes the 24px floor on both axes; it is bought with the recipe `style.css` already uses for `.scroll__lede a` and `.gr-title` — inline-block, padding, and an equal negative margin that hands the room back so the card's line does not re-space. Ink at rest, accent underline on hover: sixty accent titles would take the board over.
16
+
17
+ **The ⤢ glyph goes.** The task allowed it to stay or go; with the title carrying the job it is a second door to the same room, one the owner never found, and a second control per card on a board whose point is scanning. Removed from both renderers, both handlers, and the sheet.
18
+
19
+ **The record learns where it came from.** The link a list writes carries the origin as a query parameter — `task-detail.js`'s new `OTBTaskDetail.href(id)` appends `?from=<pathname + search + hash>`, and `open(id)` (the backlog rows, the goal ledger rows, the read-only cards) navigates to the same URL. A query parameter, not in-memory state, because the origin must survive a reload and a copied link; and the hash rides inside it, because a goal lives at `#/goal/N` and `document.referrer` never carries a fragment. `task.js` resolves the exit in one place, in this order: `?from=` → a same-origin referrer → the work board. The crumb is pointed before the fetch, so the way back exists in every state; the actions row of all three renders uses the same target; the label is said the way the sidebar says it (*back to goal 1000081*, *back to the work board*, *back to sessions*).
20
+
21
+ **A value is honoured only as a path on this host.** One leading slash and not two (`//host` is another origin), no backslash (browsers read `/\host` as `//host`), no whitespace, never `/api/` or `/lib/` (the sign-in round trip's referrer is the auth callback), never this same record (a loop is not a way back), and at most 2048 characters. Anything else falls through to the next source. This is an `href`, not a redirect, so the blast radius of a bad value was a wrong link; the allowlist keeps it from ever being an off-host one.
22
+
23
+ ## Consequences
24
+
25
+ - From the goal detail To-do pane, clicking a claimable task's title opens its record; the card body still toggles selection and bulk claim still works. The same on the work board.
26
+ - A record opened from a goal offers a way back to that goal; one opened from the board offers the board on the tab it was opened from; one opened from any other hall page offers that page via the referrer; one opened cold offers the work board.
27
+ - The four hardcoded work-board exits are gone; `/work` remains only as the fallback in `task.html`'s static crumb and in `task.js`'s last resort.
28
+ - `docs/copy-inventory.md` / `docs/copy-registry.json` regenerated: `Open the work board` (3×) left the inventory; `Back to tasks` stays as the static fallback label.
29
+ - Pinned by seven new tests in `tests/hall_record_world.mjs` §10 (the record run in a vm with a query and a referrer, including the hostile values above) and four in `tests/hall_board_world.mjs` §7 (the link on both renderers, both handlers letting it through, the floor recipe, and `task-detail.js` run in a vm to prove the link carries path + query + hash on both hosts). `task.states.json` gained a `from-goal` state so the longer crumb label is measured.
30
+
31
+ ## Rejected
32
+
33
+ - **Making the card body open the record.** Deletes bulk claim, which is the reason the card behaves as it does; 1759/1869 remain right about the primary interaction.
34
+ - **Keeping the glyph beside the title link.** Two doors to one room, and a control per card that the person who owns the instance did not find.
35
+ - **In-memory origin (`sessionStorage`, a global).** Lost on reload and on a copied link, which is exactly when a builder needs the way back.
36
+ - **`document.referrer` as the primary source.** It drops the fragment, so a goal (`/goals#/goal/N`) would read as *goals*; it is the fallback for links written without `?from=`.
37
+ - **`history.back()`.** Not a link (no middle-click, no hover target), wrong after a reload, and it cannot be labelled with where it goes.
38
+ - **A `<base>`-style rewrite or server-side referrer handling.** The hall is static markup plus page scripts; the origin is known to the page that writes the link, and nowhere else.
@@ -314,6 +314,7 @@ This keeps the decision history honest and traceable.
314
314
  | 0225 | [**Privacy suppresses cross-project surfaces only: a private builder still appears on their own project's roster** ([task 1003443](https://cloudbongos.com/builders#/task/1003443) · goal 1000075). The accepted privacy spec defined its two flags inconsistently for one case: `hide_stats` suppresses "everything derived from the CROSS-PROJECT rollup" (qualified, so an in-project leaderboard is out of scope) while `account_visibility: private` said "absent from search, LEADERBOARDS, sitemap" (unqualified, so it read as covering the in-project one too) — and PV2 demands "ONE shared predicate used by every public read: … leaderboards …" while `modules/economy/credits.js`'s `leaderboard()` is a bare `SELECT … FROM builders` with no join and no predicate. Read one way that is an unshipped PV2 violation in main; read the other it is correct, and nothing settled which. **Owner decision (2026-08-30): privacy suppresses CROSS-PROJECT surfaces only.** A `private` builder is absent from `/u/<handle>`, platform search, the sitemap, Explore and the recruiting reach, and still appears on the roster of a project they build on — a project's record of its own work is the project's business. Two consequences are the point of recording it: `credits.leaderboard()` is **correct as written** (adding a predicate there would be a regression, not a fix — exactly what a literal PV2 reading would have done), and PV2's "leaderboards" means the cross-project ones. The spec was amended rather than annotated: the D1 row and both `active · public` matrix rows now say *cross-project*, the two `active · private` rows drop "absent everywhere" (the file's most misreadable phrase) for "absent from every cross-project surface", PV2's ship-table row is qualified, and the boundary lands as the **third rule** under D2 beside "`hide_stats` beats everything". Rejected: making `private` hide a builder from projects they build on (it reads as more protective and breaks the ledger — a roster that omits contributors cannot show who built the thing, and credits would accrue to someone invisible on the surface explaining them); and leaving it as a dated spec note (it pins shipped code as deliberately predicate-free, which is an ADR's job).](<redacted>.md) | privacy / spec |
315
315
  | 0227 | [**A task network map is refused; the goal graph is the map, and search answers findability** ([task 1003282](https://cloudbongos.com/builders#/task/1003282) · goal 1000072, criterion `legibility-project-map`, which gates itself on this spike). Measured across a swept density range (p=2%–40% of tasks carrying a dependency): a node-link view of ~1,357 tasks is confetti, not structure, and densifying it draws forty goals badly while claiming to show a thousand tasks — a confident picture of nothing is the opposite of the legibility this goal serves. Decision: NO whole-corpus task map at any density; the readable surface is the ~40-node GOAL graph as the entry point with a per-goal DEPENDENCY FOREST as the drill-down, queried when a goal is opened rather than zoomed out of a global map. Every edge a map could draw already exists in the DB and none needs a new table — `task_dependencies` (020), `tasks.parent_task_id`, `tasks.goal_id`, `task_criteria`→`done_when_criteria`, `idea_inbox.promoted_to_task_id`, `idea_inbox.merged_into_idea_id` (`core_195`), `tasks.version_id` (membership, never an edge). **Findability (goal 1000027) answers the owner’s question more cheaply than a map**: the ask splits into “find what I was brainstorming”, which `/recall` (ADR 0060) plus the two lineage columns already answers end to end and which wants a text LINEAGE TRAIL — one thing and its ancestry — not a picture; and “see the shape of the portfolio”, the only half a graph earns, which is about ~40 goals and not about tasks. So the trail outranks the map and is sequenced first. Accessibility: the TABLE is the primary surface and the graph the enhancement (not an `aria-label` on an SVG blob, not a toggle hiding the accessible path), no sideways scroll on the page (the graph scrolls inside its own container), and category is text as well as colour since the ADR 0192 set is not colour-distinguishable at forty nodes. Ranked list: (1) measure the real density against the live DB — it GATES and may cancel the rest, (2) the lineage trail, (3) the goal graph, (4) the per-goal forest. Rejected: view A (every task); view D (densified); a prototype as the output (the criterion forbade it — `spike-task-map.js` is a measuring instrument under `docs/research/`, wired to no route); and building before measuring. Honest limit: the corpus is SYNTHETIC (the live DB was unreachable), so the verdict is robust across the sweep but the specific numbers are model output — which is why item 1 is a measurement against the real DB.](<redacted>.md) | GDS / legibility + hall |
316
316
  | 0228 | [**The reading room owes a reference its content rules, not a second archetype** ([task 1003473](https://cloudbongos.com/builders#/task/1003473) · goal 1000081). The archetype task 1003306 built for the primer — a linear read with a gate — now serves drachmae, a REFERENCE people open to answer one question about how they get paid. Decision: the same room with ONE body modifier, `room--reference`, switching on content rules in `room.css` §8 rather than a second sheet or a `.page-head--reference` voice: the linear-read instruments are off (no progress hairline, no section counters, no read time; `primer.css`, which holds only the acknowledge gate, is no longer loaded), the outline goes to h3 depth read from the rendered DOM and lights the entry and its part, every h2/h3/h4 is an always-visible address (`.room-anchor`) that copies its link, a deep link is landed by the page after the document, the live section and the fonts (a swap moved the browser's own landing 300px, and `:target` had resolved against nothing), the collapsed phone outline rides under the top bar naming the part, and the live bounty table is a section ahead of the closing links rather than an appendix after them. Found by rendering before touching anything and fixed in the shared reader: `md-reader.js` stopped at `###`, so the doc's six earning sources rendered as paragraphs with a literal `####`; the bold rule refused an inner `*`; a linked heading slugged its target's path into the address. Rejected: a second archetype sheet, hover-only anchors (a phone has no hover), an accordion outline (the sub-questions are what a reader scans for), placing the live table inside the prose (it would lose its outline entry), and teaching the kit stub to serve markdown (the ui-design module's). Pinned by `tests/hall_reference_world.mjs`, four of whose tests run the reader in a vm.](<redacted>.md) | hall / design |
317
+ | 0229 | [**The title is the door into a task record, and the record exits to where it was opened from** ([task 1003468](https://cloudbongos.com/builders#/task/1003468) · goal 1000081). The owner, working the live hall: a to-do card "just gets selected", and a record opened from a goal "only offers the tasks page". Both were shipped behaviour: tasks 1759/1869 made multi-select the primary card interaction and left a 26px `--ink-faint` ⤢ glyph as the only way from a claimable card into the record, and `task.js` hardcoded `Open the work board` in three renders with `task.html`'s crumb a fourth. Decision: `.task__title` carries a real link (`.task__link`) on both surfaces that render the card — the convention `.gr-title` and the ledger rows already use — bought up to the 24px floor with the inline-block + padding + equal-negative-margin recipe so the card does not re-space; the card body still toggles selection and bulk claim is untouched; the glyph goes (a second door to the same room, one the owner never found). The link carries WHERE it was opened from as `?from=<path+query+hash>` (`OTBTaskDetail.href`), a query param because the origin must survive a reload and a copied link and because a referrer drops the `#/goal/N` fragment; `task.js` resolves the exit once — `?from=` → same-origin referrer → the work board — points the crumb before the fetch so the way back exists in every state, and labels it the way the sidebar does. A value is honoured only as a path on this host: one leading slash, no backslash, no whitespace, never `/api/`, never this same record. Rejected: opening on the card body (deletes bulk claim), keeping the glyph beside the link, in-memory origin, referrer-first, `history.back()`. Pinned by seven vm tests in `tests/hall_record_world.mjs` §10 and four in `tests/hall_board_world.mjs` §7.](<redacted>.md) | hall / design |
317
318
  | 0226 | [**One stale-claim timer; reporting degrades where enforcement refuses** ([task 1003476](https://cloudbongos.com/builders#/task/1003476), goal 1000072, criterion `legibility-sort-and-staleness`). "How long may a claim go silent" had FOUR answers and none of them was the project's: the sweeper that actually releases a claim read `GDS_STALE_CLAIM_HOURS` (else 6h); `GET /builders/roster` and `GET /builders/:id/active-claims` each hardcoded 24h in byte-identical blocks; and the two `session_pulses` helpers defaulted to 30 minutes. The 6-vs-24 pair is the harm: the roster is the screen an Archon opens to decide whether to intervene, and for **eighteen hours** after the sweeper had released a claim, stamped its session log `[auto-released:stale]` and returned the task to `ready`, that screen still showed it healthy — while the roster's own comment claimed to "mirror" the sweeper, true of the definition and false of the number. Decision: ONE resolver, `src/stale-timer.js` (a KERNEL file, the `instance-config.js`/`branding.js` category — host config a module reads, never hardcodes), reached by modules through the doorway and by core routes and the CLI directly. Precedence: explicit override (`--threshold-hours`, `?stale_hours=` — a LENS, labelled `query`, never a reconfiguration) → `<PREFIX>_STALE_CLAIM_HOURS` via `instance-config.resolveEnv`, whose legacy prefix chain still honours the live `GDS_STALE_CLAIM_HOURS` so **nothing in a running deployment has to change** (and which is the channel ADR 0190's runner already patches into `web.env`) → **6**, the SWEEP's historical number and deliberately not the roster's 24, because the sweep is the surface that acts and unifying on 24 would have made every project three times more patient with a dead claim under cover of a refactor. Bound 1..720h — the range `/builders/roster` has published since V3.R14; one timer, one bound. Every surface that prints the timer prints its PROVENANCE (`stale_hours_source`, the sweep header, the auto-release note): a number without it is exactly what let 6 and 24 coexist unnoticed. **The fail posture is asymmetric and that is the decision**: the resolver reports an unusable value rather than throwing, then a surface that only READS degrades to the default and names the bad var (500-ing the roster an Archon opened to find the problem would hide the problem), while the SWEEP refuses to run at all (it releases other people's claims and stamps their logs; no reload undoes that) — a surface that only reads may guess and say so, a surface that acts may not guess. Both routes now share one `staleHoursFor(req, res)`; two copies of a literal is how the drift happened twice inside one file. Rejected: a new `config/stale.json` tier (the env route already IS the per-project channel); making 24 the shared default; throwing from the resolver (forces a second reader — the original bug); folding in the boards' 14/30/60-DAY task buckets (a different question — days, about a task, a browsing lens with no enforcement behind it); a DB row + settings UI (a reasonable next step, but the defect was four numbers, not a missing knob — a row added before the readers agreed would have been a fifth).](<redacted>.md) | GDS / claims + ops |
318
319
 
319
320
  > ⚠️ **Numbering collisions are now machine-enforced, not narrated.** Twenty numbers are shared by two ADRs each — assigned in parallel sessions before anything checked. The files keep their filenames (renumbering would break every existing citation, and a number once assigned is never reused), and this table disambiguates each pair as `NNNN-a` / `NNNN-b`. The authoritative list is `LEGACY_DUPLICATE_ADRS` in [`scripts/gds/adr-namespace.js`](../../scripts/gds/adr-namespace.js), frozen by exact filename: a **new** collision — or a third file joining a legacy number, or a rename of either half — hard-fails the `unit` CI gate, as does an index row that points at no file or an ADR with no row. The list may only shrink. This paragraph used to narrate the collisions one by one and had fallen eight behind reality; see [ADR 0195](<redacted>.md) for why the check exists and why the pairs are grandfathered rather than renumbered.