@bongos/core 1.19.473 → 1.19.475

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.bongos-core.json 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.473",
6
- "core_contract": "1.19.473",
7
- "source_commit": "f858b1895c3573e86927088c29b26f2ad38491a8",
5
+ "core_version": "1.19.475",
6
+ "core_contract": "1.19.475",
7
+ "source_commit": "00cdb7fdc1b1a2dac01579ebb0ac0b02285aa73f",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-02T15:11:50.486Z",
9
+ "built_at": "2026-09-02T16:14:31.482Z",
10
10
  "redaction": {
11
11
  "model": "docs-redacted+functional-verbatim",
12
- "docs_redacted": 411,
12
+ "docs_redacted": 412,
13
13
  "agent_docs_stubbed": 24,
14
- "functional_verbatim": 1834,
14
+ "functional_verbatim": 1838,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 2269,
20
- "tree_sha256": "75c680a966e595c6ecad7c239e057f8c023780495df07c5195c859e293942c0b",
19
+ "file_count": 2274,
20
+ "tree_sha256": "3c9dba8400c97d8d17c763124eed07bc653384f1c690ccff923085a0cea24820",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -1644,10 +1644,15 @@
1644
1644
  "mode": "0000644",
1645
1645
  "sha256": "19a6e49700fa63ef781c3fa9b96d868785995c4037b078543b354e45c6db823f"
1646
1646
  },
1647
+ {
1648
+ "path": "docs/adr/0228-the-reading-room-owes-a-reference-content-rules-not-a-second-archetype.md",
1649
+ "mode": "0000644",
1650
+ "sha256": "5dd1936d04cd259b2f56eea3c272ea35850d9272b68d9e314692dc58c04e4aed"
1651
+ },
1647
1652
  {
1648
1653
  "path": "docs/adr/README.md",
1649
1654
  "mode": "0000644",
1650
- "sha256": "93a49ca61bb1ea3e3bf9c3325bcae8b174278c8504fb1cc993c03d87bb6ba2f5"
1655
+ "sha256": "7957f1adf1329b402729e4b8ea14ea5279ef9b58b38ac5c6e711107909d13b8f"
1651
1656
  },
1652
1657
  {
1653
1658
  "path": "docs/api-reference.md",
@@ -1682,12 +1687,12 @@
1682
1687
  {
1683
1688
  "path": "docs/copy-inventory.md",
1684
1689
  "mode": "0000644",
1685
- "sha256": "7726b8ee862ebd75d1a95fafa9a5fb13993b4ad79bacba17f6e401e950082be4"
1690
+ "sha256": "6d4b4090818929b5f80cf9f4511c12b8d907a62c71c02fe6427a8b298a18c8b3"
1686
1691
  },
1687
1692
  {
1688
1693
  "path": "docs/copy-registry.json",
1689
1694
  "mode": "0000644",
1690
- "sha256": "34b21e7e0c7135f4a20cc821174fc2e05ccfee4aa3691855197aa3356356e308"
1695
+ "sha256": "cd3c935bcdf78bf8681780b856f6c15d51298b89021ae1231271955fd933e9e4"
1691
1696
  },
1692
1697
  {
1693
1698
  "path": "docs/design/apex-pass-2-direction.md",
@@ -2137,7 +2142,7 @@
2137
2142
  {
2138
2143
  "path": "docs/module-api-changelog.md",
2139
2144
  "mode": "0000644",
2140
- "sha256": "f0ec801852ae00f83d8f1a14319f04a442a85541624b3c96e7c9606e80f3a046"
2145
+ "sha256": "043897a72d41dff093e021084fe46392f9d5820b3abcfdced8388310dd9a3186"
2141
2146
  },
2142
2147
  {
2143
2148
  "path": "docs/modules-contract.md",
@@ -4027,7 +4032,7 @@
4027
4032
  {
4028
4033
  "path": "modules/hall-ui/public/builders.js",
4029
4034
  "mode": "0000644",
4030
- "sha256": "e8a0e98de75cab1f595f7541c0732b032ea0060e1b56526c52f352fcbd4a26a3"
4035
+ "sha256": "3dfbefd08de01bc8215452dcc7583f8c7aa00770ad646185ae0bb47bb05184bd"
4031
4036
  },
4032
4037
  {
4033
4038
  "path": "modules/hall-ui/public/chime.wav",
@@ -4132,22 +4137,22 @@
4132
4137
  {
4133
4138
  "path": "modules/hall-ui/public/dom-utils.js",
4134
4139
  "mode": "0000644",
4135
- "sha256": "f0d0b87de9c678c3728c20eeb43818a39d35f2507b4b55ac58e2a4663ff2ceac"
4140
+ "sha256": "1a336eb7c5754f37281a6875806436db22560a9589b9ba7381f58202d5627968"
4136
4141
  },
4137
4142
  {
4138
4143
  "path": "modules/hall-ui/public/drachmae.html",
4139
4144
  "mode": "0000644",
4140
- "sha256": "f3432d954686034ed88ab1d1774810b9869f0e658e8b554ff0a96e1e522fcc4a"
4145
+ "sha256": "25cf34cf723e8ca2fb492f589fb3415e51aab0b2ab187bad77f2c270248c61b8"
4141
4146
  },
4142
4147
  {
4143
4148
  "path": "modules/hall-ui/public/drachmae.js",
4144
4149
  "mode": "0000644",
4145
- "sha256": "96f4c24eaf1eeeae3c86e1f1aff69306d3435ad631371124455d451933a7d698"
4150
+ "sha256": "7da97f075f12cb479491d43d5501b9e39aecfd36a250c066917c7381db292872"
4146
4151
  },
4147
4152
  {
4148
4153
  "path": "modules/hall-ui/public/drachmae.states.json",
4149
4154
  "mode": "0000644",
4150
- "sha256": "b87b1ab365598d2373e49518a21abb3d7e7a5802685d8e7c7708fb5a098272b3"
4155
+ "sha256": "bc27c8079a69a0d2a1cee6f376db0c86bb22b282818734e8795046cec898ed6b"
4151
4156
  },
4152
4157
  {
4153
4158
  "path": "modules/hall-ui/public/fanfare.wav",
@@ -4242,12 +4247,12 @@
4242
4247
  {
4243
4248
  "path": "modules/hall-ui/public/hall-render.js",
4244
4249
  "mode": "0000644",
4245
- "sha256": "5a09364d61b41b2cf7e970370ee993e41bbe85d0dbf5be89e979d436ac1e48a1"
4250
+ "sha256": "45c8ad11a4fcb4f9dee90bcf89e693f0d879ce2c15bbe3bdcf7d4cf0ef1e6d6f"
4246
4251
  },
4247
4252
  {
4248
4253
  "path": "modules/hall-ui/public/hall-widgets.js",
4249
4254
  "mode": "0000644",
4250
- "sha256": "ee838da0a5d076ab50b68129a35c9f5144f94a2a41ea7d9b8d14a22e4c8d1f28"
4255
+ "sha256": "8aa90c0bb15a19f13295da6225d270bbbdaf4b5336240f03cbc05abe192e87c0"
4251
4256
  },
4252
4257
  {
4253
4258
  "path": "modules/hall-ui/public/harbor.css",
@@ -4312,17 +4317,17 @@
4312
4317
  {
4313
4318
  "path": "modules/hall-ui/public/index.html",
4314
4319
  "mode": "0000644",
4315
- "sha256": "339e1fdbb5e87039c8196ebbf2aedc342111eccc9b2dc9509244559d010fffbd"
4320
+ "sha256": "f6f292816d5d5483b6b0fa2196821400ac87d7f5b4ea75b7746a44b9c0286b90"
4316
4321
  },
4317
4322
  {
4318
4323
  "path": "modules/hall-ui/public/index.states.json",
4319
4324
  "mode": "0000644",
4320
- "sha256": "8a9745eb41f5ee18f42a49be8aaf8dbdc28656767e570022fdf19737596c3628"
4325
+ "sha256": "ac0bd95920b845d11ba24bf3e75ac169c13820861ad73776a6b174a5386e8b5f"
4321
4326
  },
4322
4327
  {
4323
4328
  "path": "modules/hall-ui/public/md-reader.js",
4324
4329
  "mode": "0000644",
4325
- "sha256": "f36b6098ba3d47b098492786a5415d5d88db2e77f1f3da58cf9eba040044a4e2"
4330
+ "sha256": "ac9cc29bf8f480c7bd9560309a36a540e33421d65bfdbdbd2d24cd9808dc51d1"
4326
4331
  },
4327
4332
  {
4328
4333
  "path": "modules/hall-ui/public/modules.css",
@@ -4452,7 +4457,7 @@
4452
4457
  {
4453
4458
  "path": "modules/hall-ui/public/profile.js",
4454
4459
  "mode": "0000644",
4455
- "sha256": "6433253093196f674142979c881ea8dc735d17841aeb99ddbc0e69b621b4b7e0"
4460
+ "sha256": "79d43fd7b8ff8957cc6a28e87ba16e1da92f1642bb1e54e63957f9df9eb123bc"
4456
4461
  },
4457
4462
  {
4458
4463
  "path": "modules/hall-ui/public/ranks.html",
@@ -4497,7 +4502,22 @@
4497
4502
  {
4498
4503
  "path": "modules/hall-ui/public/room.css",
4499
4504
  "mode": "0000644",
4500
- "sha256": "ae7f02a8145e6e6f98f5428dcb48a1a82caec227af95a8c3a0c32e662af8167b"
4505
+ "sha256": "cdc1b17139a9752fb90c6866a7c2ec114d3255002176fd5a4812a1b1df9b4617"
4506
+ },
4507
+ {
4508
+ "path": "modules/hall-ui/public/roster.html",
4509
+ "mode": "0000644",
4510
+ "sha256": "4edbfb63eb08a0c0e96efec59f6559b62bc4bed24eaebe3ef4169382a8e1124c"
4511
+ },
4512
+ {
4513
+ "path": "modules/hall-ui/public/roster.js",
4514
+ "mode": "0000644",
4515
+ "sha256": "072dcdb71d6a0b4cb95dca627853273ece886bf431c923a314c72454188bad64"
4516
+ },
4517
+ {
4518
+ "path": "modules/hall-ui/public/roster.states.json",
4519
+ "mode": "0000644",
4520
+ "sha256": "ffd8233231152bee1c1815afc61e7422bdc3cc605cf5d8a5cd0609f9d04b4323"
4501
4521
  },
4502
4522
  {
4503
4523
  "path": "modules/hall-ui/public/sessions.css",
@@ -4542,7 +4562,7 @@
4542
4562
  {
4543
4563
  "path": "modules/hall-ui/public/shell.js",
4544
4564
  "mode": "0000644",
4545
- "sha256": "cef95769bb5a8629aa2a75733a776d2e398ef4ec1b65b1c28bfc6a2bb75f9dbe"
4565
+ "sha256": "1779bddc395a97fa0a5934838c483cfa8cc1b331dc354407521fa12c1a815727"
4546
4566
  },
4547
4567
  {
4548
4568
  "path": "modules/hall-ui/public/ship.wav",
@@ -6667,12 +6687,12 @@
6667
6687
  {
6668
6688
  "path": "package-lock.json",
6669
6689
  "mode": "0000644",
6670
- "sha256": "f7be3efd0df18000603a05ccfb7a2b2f11e6b7aee81298c66cbbd4820d51a191"
6690
+ "sha256": "8f099942973cfab3b26b34b06d18180b1b82cd7d8dfe60757da1da2995b58c42"
6671
6691
  },
6672
6692
  {
6673
6693
  "path": "package.json",
6674
6694
  "mode": "0000644",
6675
- "sha256": "34165e1371803af75314357a120c881daefc4611a3a3669bd0cb483b9d127893"
6695
+ "sha256": "139f26b54cd4779f2db95720a95ad1cb1aff793e1a0ec581162430321711e836"
6676
6696
  },
6677
6697
  {
6678
6698
  "path": "public-docs/index.html",
@@ -8327,7 +8347,7 @@
8327
8347
  {
8328
8348
  "path": "src/module-api.js",
8329
8349
  "mode": "0000644",
8330
- "sha256": "7b8c9a525a256e4c0428b2b600220ceb8b7c3665bbc1146da9e6bccc832994da"
8350
+ "sha256": "ede2310e38c62c4196d2a733f4919995e7780fca88e4d66fce5c959199be4df5"
8331
8351
  },
8332
8352
  {
8333
8353
  "path": "src/module-loader/catalog.js",
@@ -9722,7 +9742,7 @@
9722
9742
  {
9723
9743
  "path": "tests/hall_nav.mjs",
9724
9744
  "mode": "0000644",
9725
- "sha256": "3754e9c4880f96da1c0b77e4d81c1652b94c878872b663b3c421f5aef1bf0712"
9745
+ "sha256": "05ed6e312636cd1f4af9a26daf8c030c5892e2dc9f24ec6969fa224db5cf0525"
9726
9746
  },
9727
9747
  {
9728
9748
  "path": "tests/hall_oversight_world.mjs",
@@ -9732,7 +9752,7 @@
9732
9752
  {
9733
9753
  "path": "tests/hall_page_gate_map.mjs",
9734
9754
  "mode": "0000644",
9735
- "sha256": "a66273f4a3e376d0055afb0e41fac308e84eb30b2f468f8e36831f3aad662f98"
9755
+ "sha256": "d529685063de8c4aa6676abf86f1180bb9cd13d27a3c372d8a3d9cf078af1999"
9736
9756
  },
9737
9757
  {
9738
9758
  "path": "tests/hall_palette.mjs",
@@ -9754,6 +9774,11 @@
9754
9774
  "mode": "0000644",
9755
9775
  "sha256": "09a7aadf345c75208af035a0f6fad1ecfe10ff056b025d885361647d4e8127b9"
9756
9776
  },
9777
+ {
9778
+ "path": "tests/hall_reference_world.mjs",
9779
+ "mode": "0000644",
9780
+ "sha256": "efa4896db5902cdad99cbaa82787a39394a41753e0ff086d4e6582514178b1c0"
9781
+ },
9757
9782
  {
9758
9783
  "path": "tests/hall_roster_world.mjs",
9759
9784
  "mode": "0000644",
@@ -10617,7 +10642,7 @@
10617
10642
  {
10618
10643
  "path": "tests/rank_tier_single_source.mjs",
10619
10644
  "mode": "0000644",
10620
- "sha256": "572da54574a1f86c4cc7bf3ecc361941931ee71db5db0f8d0496d355e57f2871"
10645
+ "sha256": "7840efed42f6960ff25ad9fdd182f979049280566de4831973e5476ab5a6e8ed"
10621
10646
  },
10622
10647
  {
10623
10648
  "path": "tests/rate_limit_public.mjs",
@@ -0,0 +1,44 @@
1
+ # ADR 0228 — The reading room owes a reference its content rules, not a second archetype
2
+
3
+ **Status:** accepted · **Date:** 2026-09-02 · **Task:** [task 1003473](https://cloudbongos.com/builders#/task/1003473) · **Goal:** 1000081 (the builders' hall in the chrome world)
4
+
5
+ ## Context
6
+
7
+ Task 1003306 built the reading-room archetype (`modules/hall-ui/public/room.css`) for the primer: a progress hairline under the top bar, the room's name alone over the ground, a glass table of contents beside a `--bg-card` article card, numbered sections, and an acknowledge gate at the end. Task 1003307 found that drachmae (`/builders/drachmae`, the "how you get paid" doc) had been wearing half of that room since task 1472 — no `room.css`, no `room` class, unstyled loading states — and repaired the source without moving the screen.
8
+
9
+ What was left was a design question rather than a repair: the archetype was designed for ONE document and now serves two, and the second is not shaped like the first. The primer is a linear onboarding read with a gate at the end. Drachmae is a **reference**: people open it to answer one question about how they get paid, it carries a live table (the security bounty rates, fetched from `/public/bounty-table`), and it is scanned and deep-linked rather than read start to finish. The task asked what the room owes a reference, and whether that is a variant of `.page-head--room` and `room.css` or the same room with different content rules.
10
+
11
+ Rendering the page through the kit against the fixture harness before touching it found four things no floor measures:
12
+
13
+ - The shared markdown reader (`md-reader.js`) parsed headings to `###` only, so the doc's six `####` earning sources — the very questions a reader arrives with — rendered as paragraphs beginning with a literal `####`.
14
+ - The bold rule refused an inner `*`, so `**It's the API *list* price, deliberately**` showed its asterisks.
15
+ - The live rate card was appended as the LAST section, after the doc's own "See it / dig deeper" links: an appendix to a footer.
16
+ - A deep link into the page landed nowhere: the document arrives after `load`, so the browser's own fragment jump found nothing, and `:target` resolved against nothing.
17
+
18
+ One thing the task said to raise rather than decide: the mock's `.pane` carries a `--rule-soft` hairline while the shell's `.scroll` is `border: 1px solid transparent`, and `docs/design/hall-direction-v2.md` §6 says v3 has "no hairline edge". Changing `.scroll` repaints all 27 hall pages, so it is an owner decision. It was raised in task 1003470 and the owner closed it: **the transparent edge stands.** This task inherits that decision — the article, the skeleton and the failure note stay the shell's `.scroll`, and nothing is repainted.
19
+
20
+ ## Decision
21
+
22
+ **The same room, one body modifier, and the difference is content rules.** `drachmae.html` opts into `room room--reference`; `room.css` §8 is what the modifier switches on. Nothing about the composition changes — the head keeps `.page-head--room` (in a reading room the document *is* the page, and that is as true of a reference), the glass contents sit beside the card, the measure and the rhythm are the archetype's — because the room is right for both documents. What the room does for a reader who is *looking* rather than *reading* is:
23
+
24
+ 1. **The linear-read instruments are off.** No progress hairline (the page omits the markup; §1 stays for the primer, whose gate is fed by it), no section counters (`content: none` on the h2's and the contents' `::before`), no "About a five-minute read" (the sub line says what kind of document this is). `primer.css` is no longer loaded: it holds only the acknowledge gate.
25
+ 2. **The outline goes to h3 depth, nests, and lights the trail.** `drachmae.js` collects `h2[id], h3[id]` from the RENDERED article — so `*not*` and a link in a heading arrive as words, and the live section inserted below is one more heading in order — nests h3s under their part (`.primer-toc__sub`), and the scrollspy lights the entry AND its part (`.is-trail`). On a phone the collapsed toggle rides under the top bar (`position: sticky` at the room's 760px breakpoint, on the card's ground at 92% because light's glass has no blur and prose showed through the bare tint) and its middle (`#primer-toc-current`) names the part the reader is in.
26
+ 3. **Every heading is an address.** `.room-anchor` on every h2/h3/h4: the heading's own id as a real `<a href>`, a 24px box of its own, ALWAYS visible in the faint ink (an address is part of the content, not a hover secret, and a phone has no hover), the accent when it is the one the reader arrived at, copying the absolute link on click. The landing is the page's: `landOnHash()` scrolls to the target after the document, the live section AND the fonts (a swap after the jump moved the landing 300px short on a phone and past it on a desktop), and marks it `.is-target` until the next hash change hands the mark back to `:target`.
27
+ 4. **The live table is a section ahead of the closing links,** not an appendix after them: `insertBountySection` puts it before the h2 matching `/^see it/i`, and falls back to appending if the footer is retitled.
28
+
29
+ **The parser reaches h4, slugs a linked heading by its text, and renders nested emphasis.** `md-reader.js` is `#{1,4}` in both regexes (the heading and the paragraph break), the bold rule is `\*\*(.+?)\*\*`, and `slugify` strips a markdown link down to its text first so the copied address is `#<redacted>` rather than the target's path. The primer's markdown has no `####` and no linked heading, so its render is unchanged; `room.css` gives the h4 a rule (the body face at the body size, one weight step up) and the measure.
30
+
31
+ ## Alternatives rejected
32
+
33
+ - **A second archetype sheet (`reference.css`) or a `.page-head--reference` voice.** It would be `room.css` again with the counter deleted, and the head is the same voice for the same reason. The third scanned document (the ranks ladder, the API docs) takes the four rules by one body class.
34
+ - **Hover-only anchors,** the documentation-site default. A phone has no hover, and on a reference the addresses are content.
35
+ - **An accordion outline** that shows a part's h3s only while the reader is in it. Fourteen entries fit beside the card at 1440, and the sub-questions are what a reader is scanning FOR — hiding them defeats the outline.
36
+ - **Placing the live table inside the prose that describes it** (under the `####` bounty source). It would lose its outline entry and its deep link, which for a live schedule are the point.
37
+ - **Teaching the kit stub to serve the markdown** so the states file could pin the populated document under both bases. The kit's stub (`modules/ui-design/kit/serve.js`) is the ui-design module's; the states file documents that the populated render is the harness's job, as `primer.states.json` already does.
38
+
39
+ ## Consequences
40
+
41
+ - `tests/hall_reference_world.mjs` pins the modifier, the instruments' absence (and the primer's keeping them), the outline, the anchors, the landing order, the table's placement, the h4 rule, and RUNS the reader in a vm for the h4, the slug and the nested emphasis.
42
+ - The primer is untouched; `tests/hall_reading_world.mjs` still passes on it. The reader change reaches every document the hall renders through `OTBMdReader`; both changes only add rendering where a literal marker showed before.
43
+ - Measured through the kit against the harness: 24 renders + 2 reduced-motion passes, all clean, at 1440/390/320 in both modes, across `read`, `toc-open`, `jump` and `out`.
44
+ - Honest limits: no page has been used in a real browser by a person; the kit stub still renders the failure note for this page (documented in the states file); the `:target` mark on the initial landing is script-set, since the browser resolved `:target` before the heading existed.
@@ -313,6 +313,7 @@ This keeps the decision history honest and traceable.
313
313
  | 0224 | [**Module-owned skills reach the core checkout as untracked, self-ignored copies with a manifest: the self-copy guard stays, the dev path is a flag, and the file map skips what the module documents** ([task 1003330](https://cloudbongos.com/builders#/task/1003330) · goal 1000083). `materializeClaude`'s self-copy guard is right (a core copying itself is a no-op at best), but Claude Code discovers only `.claude/skills/` at the project root, so a builder IN the core checkout could not invoke any of the nine ui-design skills written there. Decisions: **D1** `node scripts/gds/claude-materialize.js --module-skills-only [--dry-run] [--clean]` calls `materializeModuleSkillsIntoCore`: the same roster, gate and slug rule as step 1b, verbatim copies (no bongosify: the checkout runs `node scripts/gds/…`), a core-owned name kept, a disabled module's excluded; the guard untouched. **D2** untracked and self-ignored: a generated `.claude/skills/.module-skills.json` manifest and a `.gitignore` that ignores itself, the manifest and every materialised dir; each run removes what the last manifest named then lands the current set (byte-identical on repeat; a dropped declaration disappears); `--clean` stops after the removal. **D3** `gen-file-map.js` and `tests/file_map.mjs` skip materialised names by the manifest (a module skill is documented by its module's README, not by the notes sidecar). **D4** init from a checkout that carries the copies still lands them through step 1b, transformed, and never the dotfiles. **D5** pinned end to end in `tests/claude_materialize.mjs`. Rejected: dropping the guard; committing the copies; symlinks or junctions; notes for dirs not in the repository.](<redacted>.md) | materialiser / skills |
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
+ | 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 |
316
317
  | 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 |
317
318
 
318
319
  > ⚠️ **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.