@bongos/core 1.19.475 → 1.19.477

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.475",
6
- "core_contract": "1.19.475",
7
- "source_commit": "00cdb7fdc1b1a2dac01579ebb0ac0b02285aa73f",
5
+ "core_version": "1.19.477",
6
+ "core_contract": "1.19.477",
7
+ "source_commit": "17d71953a32ddb4b8006ce2c0ed1d915b815ed1c",
8
8
  "source_ref": "HEAD",
9
- "built_at": "2026-09-02T16:14:31.482Z",
9
+ "built_at": "2026-09-02T17:36:21.154Z",
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
14
  "functional_verbatim": 1838,
15
15
  "rules": 3,
16
16
  "gate_literals": 3,
17
17
  "gate": "passed"
18
18
  },
19
- "file_count": 2274,
20
- "tree_sha256": "3c9dba8400c97d8d17c763124eed07bc653384f1c690ccff923085a0cea24820",
19
+ "file_count": 2275,
20
+ "tree_sha256": "dc88e652d6b7674e287e406b3397118f0e2af3e0346c2f234602da8ea24ff35b",
21
21
  "files": [
22
22
  {
23
23
  "path": ".claude/skills/blocker-review/SKILL.md",
@@ -252,7 +252,7 @@
252
252
  {
253
253
  "path": ".claude/skills/session-handoff/SKILL.md",
254
254
  "mode": "0000644",
255
- "sha256": "ceca875c997081dda2692a1319f78dded07383b9b1645914a68dac09020e2035"
255
+ "sha256": "ddce51865985491a71bf89f734de3589cf2bb4f1ee76ac828b0c3905844db75f"
256
256
  },
257
257
  {
258
258
  "path": ".claude/skills/status/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": "6d4b4090818929b5f80cf9f4511c12b8d907a62c71c02fe6427a8b298a18c8b3"
1695
+ "sha256": "504ab0bb1e8cd0c643a0aa27ea7e018278fc86d573291ccb654cc936488eb0dc"
1691
1696
  },
1692
1697
  {
1693
1698
  "path": "docs/copy-registry.json",
1694
1699
  "mode": "0000644",
1695
- "sha256": "cd3c935bcdf78bf8681780b856f6c15d51298b89021ae1231271955fd933e9e4"
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": "043897a72d41dff093e021084fe46392f9d5820b3abcfdced8388310dd9a3186"
2150
+ "sha256": "d04efaed57b08373781ce55244d916cca3302daecd5c6c5c3e7fb93bfcdcf950"
2146
2151
  },
2147
2152
  {
2148
2153
  "path": "docs/modules-contract.md",
@@ -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",
@@ -4572,12 +4577,12 @@
4572
4577
  {
4573
4578
  "path": "modules/hall-ui/public/style.css",
4574
4579
  "mode": "0000644",
4575
- "sha256": "9bb44b2d5ad45342ebee328430c793d0e48ce921362e87501002a5c73fbe71c9"
4580
+ "sha256": "8fb991f5806a8b94b28e5c32451593c20d70f640ec8407d9c3316c2ae76b1352"
4576
4581
  },
4577
4582
  {
4578
4583
  "path": "modules/hall-ui/public/task-detail.js",
4579
4584
  "mode": "0000644",
4580
- "sha256": "e549efef4a7453709830769b3f224811ee5f0a1342a362c05d5601df82e7e938"
4585
+ "sha256": "911cd948103fcebe94c0388bea01675b2dbb796b63a7d5ce176f70e0062912a2"
4581
4586
  },
4582
4587
  {
4583
4588
  "path": "modules/hall-ui/public/task-lib.js",
@@ -4592,17 +4597,17 @@
4592
4597
  {
4593
4598
  "path": "modules/hall-ui/public/task.html",
4594
4599
  "mode": "0000644",
4595
- "sha256": "6329e908e59610a668ac4ea06d2e8bf31ee3a753568cda219347b4db91fa47d9"
4600
+ "sha256": "7644447bc2ef50d21d185de51ec3fc06ca7f8ed78aba71b70658d3869f73563b"
4596
4601
  },
4597
4602
  {
4598
4603
  "path": "modules/hall-ui/public/task.js",
4599
4604
  "mode": "0000644",
4600
- "sha256": "d70be94aa8fa125e9ce681e07fd461773cce4228218cb232881301917b5a1e39"
4605
+ "sha256": "893cc2b1c621b464fc0e8b350b55e4d6930ca7b27125c85be0b8ad45275e9781"
4601
4606
  },
4602
4607
  {
4603
4608
  "path": "modules/hall-ui/public/task.states.json",
4604
4609
  "mode": "0000644",
4605
- "sha256": "0cdf5f1508ecb750f7476f76578c8d9eeda5410a0a22fb74c05d10baffe174ef"
4610
+ "sha256": "befd2edcf337712426f900ed7619ffc5a41b3559d34fb98d2dcaf7268e96b5ad"
4606
4611
  },
4607
4612
  {
4608
4613
  "path": "modules/hall-ui/public/triumph.wav",
@@ -4642,7 +4647,7 @@
4642
4647
  {
4643
4648
  "path": "modules/hall-ui/public/work.js",
4644
4649
  "mode": "0000644",
4645
- "sha256": "f40ca87f8656b44111d65ed4467dee06c23708c4d8daa34126d2ed0bc0c65029"
4650
+ "sha256": "7283ebe918bb1ab2eedf4f88e7ccfc8fa7ab79b790460c1886c89cabc2632f9c"
4646
4651
  },
4647
4652
  {
4648
4653
  "path": "modules/hall-ui/public/work.states.json",
@@ -6687,12 +6692,12 @@
6687
6692
  {
6688
6693
  "path": "package-lock.json",
6689
6694
  "mode": "0000644",
6690
- "sha256": "8f099942973cfab3b26b34b06d18180b1b82cd7d8dfe60757da1da2995b58c42"
6695
+ "sha256": "61a5372ee6a0262a905396aa54a6a72f423e509308213dcc24dcc02e78b225c8"
6691
6696
  },
6692
6697
  {
6693
6698
  "path": "package.json",
6694
6699
  "mode": "0000644",
6695
- "sha256": "139f26b54cd4779f2db95720a95ad1cb1aff793e1a0ec581162430321711e836"
6700
+ "sha256": "067fc518cdcae9e18db8cfe039dacef25a81586772c0c8c0105f8325f325ad34"
6696
6701
  },
6697
6702
  {
6698
6703
  "path": "public-docs/index.html",
@@ -8347,7 +8352,7 @@
8347
8352
  {
8348
8353
  "path": "src/module-api.js",
8349
8354
  "mode": "0000644",
8350
- "sha256": "ede2310e38c62c4196d2a733f4919995e7780fca88e4d66fce5c959199be4df5"
8355
+ "sha256": "80d23eb49ce424e573ed6bfed618610b58498eba45cc4756ca2739cc05831106"
8351
8356
  },
8352
8357
  {
8353
8358
  "path": "src/module-loader/catalog.js",
@@ -9722,7 +9727,7 @@
9722
9727
  {
9723
9728
  "path": "tests/hall_board_world.mjs",
9724
9729
  "mode": "0000644",
9725
- "sha256": "bb66cec6b745cd6e50efd8828d05a75aa68fbc50ed43d8a49988c1246bfd3324"
9730
+ "sha256": "f9f5365b5cd80f73d843508110af13bd8042e1f9249ae8b545db9bac64c6529a"
9726
9731
  },
9727
9732
  {
9728
9733
  "path": "tests/hall_catalog_world.mjs",
@@ -9772,7 +9777,7 @@
9772
9777
  {
9773
9778
  "path": "tests/hall_record_world.mjs",
9774
9779
  "mode": "0000644",
9775
- "sha256": "09a7aadf345c75208af035a0f6fad1ecfe10ff056b025d885361647d4e8127b9"
9780
+ "sha256": "b96e31d2de9be8b017e222815a63b3ad92cfc8f24dd5f2b3e9aac7931daeadc7"
9776
9781
  },
9777
9782
  {
9778
9783
  "path": "tests/hall_reference_world.mjs",
@@ -16,10 +16,10 @@ Use it at the end of a working session, or mid-session when the context is getti
16
16
 
17
17
  Emit **exactly one fenced code block** (so it's trivially copy-pasteable) containing the prompt below. Write it for a reader — and a future Claude session — that has **none** of this conversation's context: be specific, name files by path, and spell out task ids as hall links, never a bare `#NNN`.
18
18
 
19
- Keep it **short and curated** — a prompt, not a transcript. Include only what the next session needs to act. Use these sections, dropping any that are genuinely empty:
19
+ Keep it **short and curated** — a prompt, not a transcript. Include only what the next session needs to act. Target **under ~60 lines** inside the block: every line is context the fresh session pays for before it does anything. If it runs long, cut history first, then pointers — never the next steps or the first action. Use these sections, dropping any that are genuinely empty:
20
20
 
21
21
  1. **Goal / where things stand** — one or two lines: the overall objective and the current state.
22
- 2. **Done this session** — a few bullets of what actually landed, each tagged `verified-prod` | `verified-smoke` | `implemented-not-verified` so the next session trusts the right things.
22
+ 2. **Done this session** — optional, and capped: **at most three one-line bullets**, each tagged `verified-prod` | `verified-smoke` | `implemented-not-verified`, and only for a fact the next session must TRUST before it builds — a behaviour it will rely on, a state it must not redo. The record of what landed already exists twice (the dated file under `docs/session-logs/` and the ship notes in the ledger), so point at that file by path rather than repeating it here. Drop the section when nothing needs trusting.
23
23
  3. **Next steps** — the ordered, concrete actions to take next. Name the exact files, functions, and task ids (as hall links). Lead with the single most important one.
24
24
  4. **Manual checks to run first** — anything the next session should verify before building on this work (a test to run, a page to load, a status to confirm).
25
25
  5. **Gotchas / watch-outs** — traps you hit or foresee (a stale-tooling merge, a flaky step, a gated path, a decision still open).
@@ -28,7 +28,7 @@ Keep it **short and curated** — a prompt, not a transcript. Include only what
28
28
 
29
29
  ## How to compose it
30
30
 
31
- - **Ground it in this session.** Pull the "Done" + "Next steps" from what actually happened here — the claims you worked, the files you touched, the decisions you made. If you hold an active claim, name it and its status.
31
+ - **Ground it in this session.** Pull the "Next steps" (and the few "Done" facts worth trusting) from what actually happened here — the claims you worked, the files you touched, the decisions you made. If you hold an active claim, name it and its status. History belongs in the session log, not in the prompt.
32
32
  - **Mirror `docs/handoff-template.md`** for the section shape, but condense: a starter prompt is a fraction of a full session log.
33
33
  - **Self-contained.** Assume the fresh session starts cold. Every reference (file, task, ADR, URL) must be resolvable without this chat.
34
34
  - **Improvable.** If the builder edits the draft, keep their changes — this is their prompt to reuse and refine, not a fixed artifact.
@@ -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.