@bongos/core 1.19.448 → 1.19.450

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 (42) hide show
  1. package/.bongos-core.json +96 -36
  2. package/docs/copy-inventory.md +121 -114
  3. package/docs/copy-registry.json +377 -331
  4. package/docs/file-map.md +1 -1
  5. package/docs/module-api-changelog.md +4 -0
  6. package/modules/hall-ui/public/diagram-viewer.css +51 -34
  7. package/modules/hall-ui/public/diagram-viewer.js +4 -1
  8. package/modules/hall-ui/public/diagrams.css +45 -0
  9. package/modules/hall-ui/public/diagrams.html +12 -20
  10. package/modules/hall-ui/public/diagrams.states.json +28 -0
  11. package/modules/hall-ui/public/idea.css +138 -71
  12. package/modules/hall-ui/public/idea.html +13 -3
  13. package/modules/hall-ui/public/idea.js +17 -4
  14. package/modules/hall-ui/public/idea.states.json +14 -0
  15. package/modules/hall-ui/public/ideas.css +148 -107
  16. package/modules/hall-ui/public/ideas.html +15 -5
  17. package/modules/hall-ui/public/ideas.states.json +15 -0
  18. package/modules/hall-ui/public/primer.css +92 -400
  19. package/modules/hall-ui/public/primer.html +68 -26
  20. package/modules/hall-ui/public/primer.js +107 -308
  21. package/modules/hall-ui/public/primer.states.json +38 -0
  22. package/modules/hall-ui/public/ranks.html +9 -1
  23. package/modules/hall-ui/public/ranks.states.json +1 -1
  24. package/modules/hall-ui/public/record.css +324 -0
  25. package/modules/hall-ui/public/roadmap.css +152 -86
  26. package/modules/hall-ui/public/roadmap.html +6 -1
  27. package/modules/hall-ui/public/roadmap.states.json +31 -0
  28. package/modules/hall-ui/public/room.css +441 -0
  29. package/modules/hall-ui/public/style.css +269 -63
  30. package/modules/hall-ui/public/task.css +71 -0
  31. package/modules/hall-ui/public/task.html +15 -32
  32. package/modules/hall-ui/public/task.js +142 -39
  33. package/modules/hall-ui/public/task.states.json +14 -0
  34. package/modules/hall-ui/public/work.css +4 -15
  35. package/modules/ui-design/kit/serve.js +7 -1
  36. package/package-lock.json +2 -2
  37. package/package.json +1 -1
  38. package/scripts/hall-preview/server.js +34 -0
  39. package/src/module-api.js +1 -1
  40. package/tests/hall_audit.mjs +18 -11
  41. package/tests/hall_reading_world.mjs +430 -0
  42. package/tests/hall_record_world.mjs +412 -0
package/docs/file-map.md CHANGED
@@ -127,7 +127,7 @@
127
127
  │ ├── sessions/ ← CORE-DOMAIN carve (BV1.R82, default-on): the session_records corpus + data plane (upload, cross-builder search/deep-dive), the BFG read-the-room scorer, session-pulse/stale-claim helpers. Registers no port — reached via HTTP POST to its own /sessions route; resolves the economy `reward` port for the cost-plus token reward. CLAUDE.md, module.json, routes/sessions.js, bfg.js, pulse.js.
128
128
  │ ├── autonomy/ ← CORE-DOMAIN carve (BV1.R83, default-on): the MAS autonomy surfaces — the $0 deterministic autonomy gate (System 3, inert until armed), the conductor/architect CLIs, and the in-process routine timers (incl. the weekly peer-vote-tally poller). Registers no port; resolves the lifecycle `tallyPeerVotes` doorway. CLAUDE.md, module.json, routes/autonomy.js, autonomy.js.
129
129
  │ ├── status-ui/ ← CORE-DOMAIN WEB-SURFACE carve (BV1.R84, default-on): the public status dashboard at status.<apex>, moved from public-status/. NO routes/db/port — declares contributes.webSurfaces:[{host:"status.",dir:"public"}], served by the loader web-surface seam (moduleWebSurfaces → serve-internal). CLAUDE.md, module.json, public/{index.html,status.js,style.css,cursors/,snapshots/}.
130
- │ ├── hall-ui/ ← CORE-DOMAIN WEB-SURFACE carve (BV1.R85, default-on): the authenticated builders' hall at builders.<apex>, moved from public-builders/ (~13k LOC, the largest). NO routes/db/port — declares contributes.webSurfaces:[{host:"builders.",dir:"public"}] for discovery, but UNLIKE status-ui keeps its DEDICATED serving block in serve-internal (the /builders shim + canonical redirects + citizen/Archon page gates + asset-stamping + hall-widget injection), partitioned out of the generic web-surface loop and served from BUILDERS_DIR. CLAUDE.md, module.json, public/{index.html,builders.js,style.css,work,watch,harbor,gate,pair,settings,ranks,sessions,primer,diagrams,atlas,...}.{html,js},cursors/,chime.wav.
130
+ │ ├── hall-ui/ ← CORE-DOMAIN WEB-SURFACE carve (BV1.R85, default-on): the authenticated builders' hall at builders.<apex>, moved from public-builders/ (~13k LOC, the largest). NO routes/db/port — declares contributes.webSurfaces:[{host:"builders.",dir:"public"}] for discovery, but UNLIKE status-ui keeps its DEDICATED serving block in serve-internal (the /builders shim + canonical redirects + citizen/Archon page gates + asset-stamping + hall-widget injection), partitioned out of the generic web-surface loop and served from BUILDERS_DIR. CLAUDE.md, module.json, public/{index.html,builders.js,style.css,work,watch,harbor,gate,pair,settings,ranks,sessions,primer,diagrams,atlas,...}.{html,js},cursors/,chime.wav. Two ARCHETYPE sheets sit beside the per-page ones: `record.css` (the task + idea records, task 1003305) and `room.css` (the READING ROOMS — the primer + the diagrams index, task 1003306: the reading measure and rhythm, the table of contents, the progress hairline and the framed figure diagram-viewer.js upgrades on both pages). A page sheet then holds only what one page has and the other does not — `primer.css` the acknowledge gate, `diagrams.css` the legend strip.
131
131
  │ ├── lifecycle/ ← CORE-DOMAIN carve (BV1.R86, default-on): the build state machine — tasks · claims · versions · done-when · goals · dependencies · claim→ship→grade→credit · github-push + merge-lock + conflict-resolve + publish-reconciler. The LAST, most-coupled carve; registers the `lifecycle` kernel port (createTask, classifyTaskKind, tallyPeerVotes, work-tracking reads). CLAUDE.md, module.json, routes/{tasks,claims,versions,done-when,goals,dependencies,gate-approvals,analytics}.js, lifecycle.js, github-push.js, merge-lock.js, conflict-resolve.js, publish-reconciler.js, task-visuals.js + task-visual-db.js + routes/visuals.js (the optional ship-time visual, task 1003109 — the route file is NOT named task-*.js on purpose; see the module CLAUDE.md).
132
132
  │ ├── copy-desk/ ← CORE-DOMAIN module (task 1003113 / R02 of goal 1000074, default-on): the COPY half of the design contract — ADR 0081 gave design TOKENS a repo-owned source of truth, text had none. Owns the FLAG (anyone in any role marks a string or a whole surface as needing an artist's attention, with a reason required in BOTH the route and a CHECK constraint) and the QUEUE an artist reads it from. READS R01's committed registry (docs/copy-registry.json, generated by scripts/gds/copy-inventory.js) and never writes it. NO LIVE CMS is the hard non-goal: no column here could hold replacement text, and copy_no_cms.mjs pins that. Key is `copy-desk`, not `copy` — the branding contract already owns a `copy` block, so the bare word reds the "core carries no module code" fitness check. CLAUDE.md, module.json, registry.js, flags.js, routes/copy-desk.js, migrations/copy_desk_001_flags.sql, tests/{copy_flags,copy_queue,copy_no_cms}.mjs. Its hall surface lives with hall-ui (public/copy-desk.{html,js,css}), per the one-key-per-web-surface precedent.
133
133
  │ ├── ui-design/ ← CORE-DOMAIN module (task 1003324 / ADR 0197, default-on): the design capability every builder on every instance gets — ships METHOD, never a world. Absorbs ADR 0081's layer: adapters/{claude-design,figma}/index.js (moved from src/ui/adapters/), scripts/{design-sync,figma-design-sync,validate-design}.js (moved from scripts/gds/, which keeps thin shims), docs/design-contract.md (moved from docs/). Declaration-only: contributes.skills design (the /design playbook, new) + design-sync + figma-design-sync; no routes/port/migrations, so disabling it touches no served surface. CLAUDE.md separates the platform FLOORS (the Fifteen Rule, the dark twins, no page :root, the 24px floor, AA in both modes, the kill switch) from the instance's WORLD (its packs + DESIGN.md). kit/ (task 1003320) is the look-before-you-ship kit: lib.js (the floors, the states/probes contracts, the action + expectation vocabularies, the WCAG maths, the stub spawner), serve.js (any surface through the doorway's real transforms + fixtures/), render.js (states × 1440/390/320 × dark/light, audited), probe.js (PASS/FAIL contracts), check-mock.js (platform rules + the instance's DESIGN.md taste bans), contrast.js + png.js; a page's <page>.states.json / <page>.probes.json sit BESIDE the surface (modules/public-landing/public/, modules/hall-ui/public/). Recipe: docs/recipes/ui-look-before-you-ship.md. skills/ (task 1003321 / ADR 0198) is the vendoring policy: policy.json (licence allowlist, verdicts that may land, sidecar fields, preamble markers), PREAMBLE.md (the platform preamble every vendored SKILL.md opens with), README.md (the roster — both origins scanned dangerous on 2026-08-28, landed as rebuild specs; nothing vendored — and the landing recipe); a landable copy goes under skills/vendor/<name>/ with PROVENANCE.md + LICENSE and is materialised into an instance's .claude/skills/ by claude-materialize.js step 1b. Reserved home styles/ (1003323) with a README. config/design-tokens.* + design-sources.json STAY in config/ as the host boundary; public-landing keeps its own key (first customer, not owner).
@@ -1333,5 +1333,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
1333
1333
  landed since 1.19.446 with no explicit bump. run 33345047021. (task 1002620)
1334
1334
  1.19.448 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1335
1335
  landed since 1.19.447 with no explicit bump. run 33348050270. (task 1002620)
1336
+ 1.19.449 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1337
+ landed since 1.19.448 with no explicit bump. run 33406714743. (task 1002620)
1338
+ 1.19.450 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
1339
+ landed since 1.19.449 with no explicit bump. run 33414986466. (task 1002620)
1336
1340
  ---------------------------------------------------------------------------
1337
1341
  ```
@@ -1,17 +1,30 @@
1
1
  /* diagram-viewer.css — pannable / zoomable canvas for the onboarding diagrams
2
- (task 1059). Replaces the static PNG click-to-zoom lightbox on
3
- /builders/diagrams and the primer: each diagram's vector SVG loads inline
4
- into a canvas you DRAG to pan and SCROLL to zoom (Figma-style), with a
5
- toolbar and a fullscreen mode. Themed via the shared design tokens from
6
- style.css (with neutral fallbacks). */
2
+ (task 1059; rebuilt into the chrome world by task 1003306). Replaces the
3
+ static PNG click-to-zoom lightbox on /builders/diagrams and the primer: each
4
+ diagram's vector SVG loads inline into a canvas you DRAG to pan and SCROLL to
5
+ zoom (Figma-style), with a toolbar and a fullscreen mode.
6
+
7
+ THE FALLBACKS ARE GONE, and that is a fix rather than a tidy-up. This sheet
8
+ carried eleven `var(--x, LITERAL)` pairs whose tokens all exist and always
9
+ have — so the literal never rendered, and all it did was encode drift: the
10
+ `--ink` fallback said #2a2a2a where the sheet ships #17181a, and a reader
11
+ comparing the two would have believed the wrong one. A fallback for a token
12
+ that cannot be missing is a second, stale source of truth.
13
+
14
+ Likewise the two raw box-shadows (v3's panels separate by TONE, never a drawn
15
+ box and never a shadow), the hand-written `z-index: 9999` (now
16
+ --z-diagram-full, named in the shell's ladder), and the scroll lock, which
17
+ reached for `body` where the shell's own drawer locks on the root element.
18
+
19
+ `--line` is a WIDTH; the hairline's colour is --rule / --rule-soft. No compat
20
+ alias (--surface / --border / …) appears here. */
7
21
 
8
22
  .dgv {
9
23
  position: relative;
10
24
  margin: 0 auto;
11
- background: var(--surface, #fff);
12
- border: 1px solid var(--border, rgba(0, 0, 0, 0.15));
13
- border-radius: var(--radius-sm, 8px);
14
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.06);
25
+ background: var(--bg-card);
26
+ border: var(--line) solid var(--rule-soft);
27
+ border-radius: var(--radius);
15
28
  overflow: hidden;
16
29
  }
17
30
 
@@ -24,8 +37,8 @@
24
37
  overflow: hidden;
25
38
  /* faint dotted "drawing board" texture, like a design tool */
26
39
  background:
27
- radial-gradient(circle at center, color-mix(in srgb, var(--ink, #000) 5%, transparent) 1px, transparent 1.4px) 0 0 / 22px 22px,
28
- var(--surface, #fff);
40
+ radial-gradient(circle at center, color-mix(in srgb, var(--ink) 5%, transparent) 1px, transparent 1.4px) 0 0 / 22px 22px,
41
+ var(--bg-card);
29
42
  cursor: grab;
30
43
  /* Inline: allow vertical page scroll through the canvas (touch) so the reader
31
44
  is never trapped scrolling past a diagram; a mouse drag still pans and a
@@ -35,7 +48,7 @@
35
48
  }
36
49
  .dgv.is-fullscreen .dgv__stage { touch-action: none; }
37
50
  .dgv__stage.is-panning { cursor: grabbing; }
38
- .dgv__stage:focus-visible { box-shadow: inset 0 0 0 2px var(--accent, #4f63e7); }
51
+ .dgv__stage:focus-visible { box-shadow: inset 0 0 0 2px var(--focus); }
39
52
 
40
53
  /* The transform layer — translate()+scale() is set here by the script. */
41
54
  .dgv__pan {
@@ -48,7 +61,8 @@
48
61
  .dgv__pan.is-smooth { transition: transform 0.16s ease-out; } /* button zoom / fit only */
49
62
  .dgv__pan svg { display: block; }
50
63
 
51
- /* Floating control bar — bottom-right of the canvas. */
64
+ /* Floating control bar — bottom-right of the canvas. Glass, like every other
65
+ control the world floats over content. */
52
66
  .dgv__toolbar {
53
67
  position: absolute;
54
68
  right: 10px;
@@ -57,10 +71,9 @@
57
71
  align-items: center;
58
72
  gap: 2px;
59
73
  padding: 3px;
60
- background: color-mix(in srgb, var(--surface, #fff) 92%, transparent);
61
- border: 1px solid var(--border, rgba(0, 0, 0, 0.15));
62
- border-radius: var(--radius-sm, 8px);
63
- box-shadow: 0 1px 6px rgba(0, 0, 0, 0.12);
74
+ background: color-mix(in srgb, var(--bg-card) 92%, transparent);
75
+ border: var(--line) solid var(--glass-edge);
76
+ border-radius: var(--radius-pill);
64
77
  -webkit-backdrop-filter: blur(2px);
65
78
  backdrop-filter: blur(2px);
66
79
  user-select: none;
@@ -70,55 +83,59 @@
70
83
  -webkit-appearance: none;
71
84
  border: 0;
72
85
  background: transparent;
73
- font: 600 14px/1 var(--font-body, system-ui, sans-serif);
74
- color: var(--ink, #2a2a2a);
86
+ font: 500 14px/1 var(--font-body);
87
+ color: var(--ink-soft);
75
88
  min-width: 30px;
76
89
  height: 30px;
77
90
  padding: 0 8px;
78
- border-radius: 6px;
91
+ border-radius: var(--radius-pill);
79
92
  cursor: pointer;
80
93
  display: inline-flex;
81
94
  align-items: center;
82
95
  justify-content: center;
83
96
  }
84
- .dgv__btn:hover { background: color-mix(in srgb, var(--ink, #000) 7%, transparent); }
85
- .dgv__btn:focus-visible { outline: 2px solid var(--accent, #4f63e7); outline-offset: 1px; }
97
+ .dgv__btn:hover { background: var(--glass); color: var(--ink); }
98
+ .dgv__btn:focus-visible { outline: 2px solid var(--focus); outline-offset: 1px; }
86
99
  .dgv__btn--wide { min-width: 56px; font-variant-numeric: tabular-nums; }
87
- .dgv__sep { width: 1px; height: 18px; background: var(--border, rgba(0, 0, 0, 0.12)); margin: 0 3px; }
100
+ .dgv__sep { width: var(--line); height: 18px; background: var(--rule-soft); margin: 0 3px; }
88
101
 
89
102
  /* Hint pill — top-left; fades the moment the reader interacts. */
90
103
  .dgv__hint {
91
104
  position: absolute;
92
105
  top: 10px;
93
106
  left: 10px;
94
- font: 500 11px/1 var(--font-body, system-ui, sans-serif);
95
- letter-spacing: 0.04em;
96
- color: var(--ink-soft, #555);
97
- background: color-mix(in srgb, var(--surface, #fff) 88%, transparent);
98
- border: 1px solid var(--border, rgba(0, 0, 0, 0.1));
99
- border-radius: 999px;
107
+ font: 400 var(--label-size)/1 var(--label-face);
108
+ letter-spacing: var(--label-space);
109
+ color: var(--ink-faint);
110
+ background: color-mix(in srgb, var(--bg-card) 88%, transparent);
111
+ border: var(--line) solid var(--glass-edge);
112
+ border-radius: var(--radius-pill);
100
113
  padding: 5px 10px;
101
114
  pointer-events: none;
102
115
  transition: opacity 0.4s ease;
103
116
  }
104
117
  .dgv.is-touched .dgv__hint { opacity: 0; }
105
118
 
106
- /* Fullscreen — promote the whole .dgv to a fixed overlay. */
119
+ /* Fullscreen — promote the whole .dgv to a fixed overlay. It covers the shell
120
+ entirely, so it is the top of the z ladder. */
107
121
  .dgv.is-fullscreen {
108
122
  position: fixed;
109
123
  inset: 0;
110
- z-index: 9999;
124
+ z-index: var(--z-diagram-full);
111
125
  margin: 0;
112
126
  border: 0;
113
127
  border-radius: 0;
114
- background: color-mix(in srgb, var(--ink, #1a1d23) 96%, transparent);
128
+ background: var(--bg);
115
129
  }
116
130
  .dgv.is-fullscreen .dgv__stage {
117
131
  height: 100vh;
118
132
  height: 100dvh;
119
- background-color: var(--surface, #fff);
133
+ background-color: var(--bg-card);
120
134
  }
121
- body.dgv-fullscreen-open { overflow: hidden; }
135
+ /* The scroll lock goes on the ROOT element, which is where the shell's own
136
+ drawer puts it (html.drawer-open in style.css). Two overlays locking the same
137
+ page on two different elements is how one of them stops working. */
138
+ html.dgv-fullscreen-open { overflow: hidden; }
122
139
 
123
140
  @media (prefers-reduced-motion: reduce) {
124
141
  .dgv__pan.is-smooth { transition: none; }
@@ -177,7 +177,10 @@
177
177
 
178
178
  function toggleFullscreen(ctx) {
179
179
  const on = ctx.dgv.classList.toggle('is-fullscreen');
180
- document.body.classList.toggle('dgv-fullscreen-open', on);
180
+ // The lock goes on the ROOT element — where the shell's own drawer puts its
181
+ // one (html.drawer-open). Two overlays locking the same page on two
182
+ // different elements is how one of them stops working.
183
+ document.documentElement.classList.toggle('dgv-fullscreen-open', on);
181
184
  fullscreen = on ? ctx : null;
182
185
  const b = ctx.toolbar.querySelector('[data-act="full"]');
183
186
  if (b) b.title = on ? 'Exit fullscreen (Esc)' : 'Fullscreen';
@@ -0,0 +1,45 @@
1
+ /* diagrams.css — what the DIAGRAMS index has that the other reading rooms do
2
+ not (task 1003306, goal 1000081).
3
+
4
+ The room — the framed figure, the reading measure, the prose rhythm — is the
5
+ shared archetype in room.css, which this page loads first. What is left here
6
+ is the legend strip: the three-state key (live / planned / reserved) that
7
+ tells a reader how to read the five generated diagrams. It is genuinely this
8
+ page's alone, so it is this page's sheet.
9
+
10
+ Promoted out of a 16-line inline <style> block, which sat one line over the
11
+ hall's default budget with its own tests/hall_audit.mjs exception. The
12
+ exception retires with it, and the hall now carries no substantive inline
13
+ style at all.
14
+
15
+ The diagrams themselves are GENERATED (scripts/gds/gen-diagrams.js writes the
16
+ .svg/.png pair and assertions.json in lockstep) — never hand-edit a .mmd. */
17
+
18
+ /* The key sits above the figures on the ground of its own panel, wrapping to a
19
+ column on a phone rather than scrolling sideways. */
20
+ .legend {
21
+ display: flex;
22
+ flex-wrap: wrap;
23
+ gap: var(--sp-xs) var(--sp-md);
24
+ margin: 0;
25
+ }
26
+ .legend__item {
27
+ display: flex;
28
+ align-items: center;
29
+ gap: 8px;
30
+ min-height: 24px;
31
+ font-size: 13.5px;
32
+ color: var(--ink-soft);
33
+ }
34
+ /* The three states are the hall's fixed status vocabulary, not accent-derived:
35
+ a diagram's "planned" must not be the same salmon as a claim button. */
36
+ .legend__swatch {
37
+ font-family: var(--label-face);
38
+ font-size: var(--label-size);
39
+ letter-spacing: var(--label-space);
40
+ font-weight: 400;
41
+ white-space: nowrap;
42
+ }
43
+ .legend__swatch--live { color: var(--ok); }
44
+ .legend__swatch--planned { color: var(--warn); }
45
+ .legend__swatch--reserved { color: var(--ink-faint); }
@@ -9,27 +9,14 @@
9
9
  <link rel="preconnect" href="https://fonts.googleapis.com">
10
10
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
11
  <link rel="stylesheet" href="/builders/style.css?v=2026-07-02-shell1">
12
+ <link rel="stylesheet" href="/builders/room.css">
13
+ <link rel="stylesheet" href="/builders/diagrams.css">
12
14
  <link rel="stylesheet" href="/builders/diagram-viewer.css?v=2026-06-14-diagviewer-1">
13
- <style>
14
- /* Page-local styles for the diagrams page — the legend strip and figure
15
- frames. Everything derives from the shared design tokens so instance
16
- packs reskin this page along with the rest of the hall. */
17
- .legend { display: flex; flex-wrap: wrap; gap: 1rem 2rem; justify-content: center; margin: 0 0 1rem; padding: 1rem; }
18
- .legend__item { display: flex; align-items: center; gap: .5rem; font-size: .95rem; }
19
- .legend__swatch { font-weight: 700; }
20
- .legend__swatch--live { color: var(--ok); }
21
- .legend__swatch--reserved { color: var(--ink-faint); }
22
- .legend__swatch--planned { color: var(--warn); }
23
- figure.diagram { margin: 0 0 2.5rem; text-align: center; }
24
- figure.diagram img {
25
- max-width: 100%; height: auto; border: 1px solid var(--border);
26
- border-radius: var(--radius-sm); background: var(--surface); padding: .5rem;
27
- box-shadow: 0 2px 10px rgba(0, 0, 0, .06);
28
- }
29
- figure.diagram figcaption { margin-top: .6rem; font-size: .9rem; color: var(--ink-soft); }
30
- </style>
31
15
  </head>
32
- <body data-page="diagrams" data-page-title="Diagrams">
16
+ <!-- `room` opts this page into the reading-room archetype's composition tokens
17
+ (room.css) — the framed figure here is the same object the primer inlines,
18
+ and diagram-viewer.js upgrades both with one selector. -->
19
+ <body class="room" data-page="diagrams" data-page-title="Diagrams">
33
20
 
34
21
  <div class="app">
35
22
  <aside class="app-sidebar" id="app-sidebar" aria-label="Primary"></aside>
@@ -37,7 +24,12 @@
37
24
  <div class="app-body">
38
25
  <header class="app-topbar" id="app-topbar"></header>
39
26
  <div class="app-content">
40
- <div class="page-head">
27
+ <!-- The page had no <h1> at all before task 1003306, so it failed
28
+ one-header-main-h1 on every render. The reading room's head: the
29
+ label voice on the eyebrow, the room's name in the display face. -->
30
+ <div class="page-head page-head--room">
31
+ <p class="page-head__eyebrow">Docs</p>
32
+ <h1 class="page-head__h">How the system works</h1>
41
33
  <p class="page-head__sub">Five diagrams that show, at a glance, how work flows through {{worldName}}'s development system. <em>Drag to pan, scroll to zoom</em> — or open any diagram fullscreen.</p>
42
34
  </div>
43
35
 
@@ -0,0 +1,28 @@
1
+ {
2
+ "_": "The hall's diagrams index through modules/ui-design/kit/render.js (task 1003306). Five generated figures on one page is the hall's widest content, so the horizontal-overflow floor at 320 is the one this file exists for.",
3
+ "page": "/builders/diagrams",
4
+ "surface": "hall-ui",
5
+ "stub": { "prefix": "/builders/" },
6
+ "modeQuery": false,
7
+ "_feeds": "The diagrams are generated PNG/SVG pairs written by scripts/gds/gen-diagrams.js and served from /builders/diagrams/*; the kit's stub has neither those nor the hall's feeds, so both are declared. diagram-viewer.js upgrades each <img> into a pan/zoom canvas only once its .svg fetch succeeds — under the stub it does not, so what renders here is the graceful-degradation path: the framed figure with a static image in it. That IS the state to measure, because it is what a reader gets whenever the vector is unavailable.",
8
+ "ignoreRequests": [
9
+ "/api/(gds|bongos)/me$",
10
+ "/api/(gds|bongos)/me/",
11
+ "/api/(gds|bongos)/goals",
12
+ "/api/(gds|bongos)/inbox/",
13
+ "/builders/diagrams/"
14
+ ],
15
+ "ignoreConsole": [
16
+ "/api/(gds|bongos)/",
17
+ "\\b401\\b",
18
+ "\\b404\\b",
19
+ "Unauthorized",
20
+ "Not Found",
21
+ "load failed"
22
+ ],
23
+ "states": {
24
+ "in": { "auth": true, "expect": { "visible": ["#hall-main"] } },
25
+ "out": { "auth": false }
26
+ },
27
+ "reducedMotion": { "auth": true }
28
+ }
@@ -1,89 +1,156 @@
1
- /* modules/hall-ui/public/idea.css — the idea-detail page (task 1002929 / R153
2
- of goal 1000062).
1
+ /* idea.css — what the IDEA record has that the task record does not
2
+ (task 1002929 / R153 of goal 1000062; re-skinned into the chrome world by
3
+ task 1003305, goal 1000081).
3
4
 
4
- Promoted out of an inline <style> because tests/hall_audit.mjs budgets an inline
5
- block at 15 lines and this is ~60: past that the documented pattern (task 2176)
6
- is a per-page stylesheet. The exceptions in that budget map are grandfathered or
7
- deliberate; a brand-new page has no business claiming one.
5
+ The shared composition — the way back, the kind eyebrow, the title, the
6
+ sections, the honest failure heading — is record.css, which idea.html loads
7
+ first. What is left here is the four shapes only an idea has: its grade badge,
8
+ its attribution pair, its completeness readout, and the author-only mark
9
+ control.
8
10
 
9
- Built on the hall's tokens. No --line/--panel: neither is defined in style.css,
10
- so a fallback is all you would ever get and both assume a dark ground the hall
11
- does not have by default (--bg is #f5f6f8). */
11
+ Retired with the re-skin: `.idea-detail__back` (the archetype's `.rec-back`),
12
+ `.ig-head` and `.ig-title` (the archetype's `.rec-kind` and `.rec-title`), and
13
+ every compat alias this sheet used to spend — --surface-2 → --glass,
14
+ --border → --rule-soft, --accent-weak/--accent-text → --accent-tint/--accent-ink.
15
+ The aliases still EXIST for the pages nobody has re-skinned, which is exactly
16
+ why spending one here would be legal CSS that quietly opts this sheet back out
17
+ of the world. `--line` is a WIDTH; the hairline's colour is --rule/--rule-soft. */
12
18
 
13
- /* Page-local, like task.html (task 751) — this page adds a handful of shapes
14
- and reuses the hall's tokens for everything else. No --line/--panel here:
15
- neither is defined in style.css, so a fallback is all you would ever get
16
- and both assume a dark ground the hall does not have by default. */
17
-
18
- .idea-detail__back { font-size: 13px; color: var(--ink-soft); text-decoration: none; display: inline-block; margin-bottom: 10px; }
19
- .idea-detail__back:hover { color: var(--ink); text-decoration: underline; }
20
-
21
- /* The grade is the first thing to read: it decides what the rest of the page
22
- even contains. Three states, distinguished by colour AND by word — a badge
23
- that only differs by hue is unreadable to a chunk of readers. */
24
- .ig-grade {
25
- display: inline-flex; align-items: center; gap: 6px; height: 22px; padding: 0 10px;
26
- border-radius: 999px; font-size: 12px; font-weight: 600;
27
- background: var(--surface-2); border: 1px solid var(--border); color: var(--ink-soft);
28
- }
29
- .ig-grade--full { background: var(--ok-weak); color: var(--ok); border-color: color-mix(in srgb, var(--ok) 30%, transparent); }
30
- .ig-grade--spark { background: var(--warn-weak); color: var(--warn); border-color: color-mix(in srgb, var(--warn) 30%, transparent); }
31
- .ig-grade--quick { background: var(--accent-weak); color: var(--accent-text); border-color: color-mix(in srgb, var(--accent) 30%, transparent); }
32
-
33
- .ig-head { display: flex; flex-wrap: wrap; gap: 8px 10px; align-items: center; margin-bottom: 4px; }
34
- .ig-title { margin: 0; font-size: 1.15rem; text-wrap: balance; }
19
+ /* ---- the grade badge is NOT here ------------------------------------------
20
+ It is the archetype's `.rec-pill`, spent by idea.js's gradeBadge() through
21
+ GRADE_TONE. The badge is the first thing to read — it decides what the rest of
22
+ the page even contains — and it is distinguished by colour AND by word,
23
+ because a badge that differs only by hue is unreadable to a chunk of readers.
24
+ But a grade CLASSIFIES the record, which is the same job the task record's
25
+ status pill does, so it is the same component and not a lookalike: this sheet
26
+ carried a near-verbatim copy of `.rec-pill` down to the token triples, which
27
+ is a hundred lines of the archetype waiting to drift the first time one of the
28
+ two was touched. That is precisely what record.css exists to prevent. */
35
29
 
30
+ /* ---- the status line ----------------------------------------------------- */
36
31
  .ig-meta {
37
- font-size: 13px; color: var(--ink-soft); margin: 0 0 16px;
38
- display: flex; flex-wrap: wrap; gap: 4px 14px; align-items: baseline;
32
+ display: flex;
33
+ flex-wrap: wrap;
34
+ gap: 4px 16px;
35
+ align-items: baseline;
36
+ margin: 0 0 var(--sp-md);
37
+ font-size: 14px;
38
+ color: var(--ink-soft);
39
+ font-variant-numeric: tabular-nums;
40
+ }
41
+ .ig-meta strong { color: var(--ink); font-weight: 500; }
42
+ .ig-meta a {
43
+ display: inline-block;
44
+ min-height: 24px;
45
+ padding: 3px 0;
46
+ line-height: 18px;
47
+ color: var(--ink);
48
+ border-bottom: var(--line) solid var(--rule);
39
49
  }
40
- .ig-meta strong { color: var(--ink); font-weight: 600; }
50
+ .ig-meta a:hover { border-bottom-color: var(--ink); text-decoration: none; }
41
51
 
42
- /* Attribution is its own block, not a line in the meta run: on a developed
43
- idea it carries TWO people and the whole point of R146 was that both
44
- survive. Burying that in a comma list is how one of them gets lost. */
45
- .ig-credit {
46
- display: grid; gap: 6px;
47
- padding: 12px 14px; margin: 0 0 18px;
48
- border: 1px solid var(--border); border-radius: var(--radius-sm);
49
- background: var(--surface-2); font-size: 13px;
52
+ /* ---- attribution --------------------------------------------------------- */
53
+ /* Its own block, not a line in the meta run: a developed idea carries TWO people
54
+ and the whole point of R146 was that both survive. Burying that in a comma
55
+ list is how one of them gets lost. v1's bare ledger inside a v3 panel — a
56
+ hairline on top, air, and the first row drops both — rather than the boxed
57
+ well it used to be, because in v3 tone does the separation and a bordered card
58
+ inside a card is the treatment the owner's pick removed. */
59
+ .ig-credit { display: grid; margin: 0 0 var(--sp-md); }
60
+ .ig-credit__row {
61
+ display: flex;
62
+ flex-wrap: wrap;
63
+ gap: 4px 12px;
64
+ align-items: baseline;
65
+ border-top: var(--line) solid var(--rule-soft);
66
+ padding: 11px 0;
67
+ font-size: 14px;
50
68
  }
51
- .ig-credit__row { display: flex; flex-wrap: wrap; gap: 4px 8px; align-items: baseline; }
52
- .ig-credit__who { color: var(--ink); font-weight: 600; }
69
+ .ig-credit__row:first-child { border-top: 0; padding-top: 0; }
70
+ .ig-credit__who { color: var(--ink); font-weight: 500; }
53
71
  .ig-credit__role { color: var(--ink-faint); }
54
72
 
55
- /* The five answers, laid out to be READ: one column, generous leading, a
56
- measure that stops around 70 characters. */
57
- .ig-answers { display: grid; gap: 18px; margin: 4px 0 0; }
58
- .ig-answer__q { margin: 0 0 4px; font-size: 0.92rem; font-weight: 600; }
59
- .ig-answer__a { margin: 0; max-width: 70ch; line-height: 1.65; white-space: pre-wrap; }
73
+ /* ---- the five answers ---------------------------------------------------- */
74
+ /* Laid out to be READ: one column, generous leading, and the measured reading
75
+ width. `--measure` is 48ch MEASURED to render ~60 characters — the old `70ch`
76
+ here rendered closer to 88, because a `ch` is the zero glyph's width and
77
+ Manrope's zero is much wider than its average lowercase. */
78
+ .ig-answers { display: grid; gap: var(--sp-md); margin: var(--sp-md) 0 0; }
79
+ .ig-answer__q {
80
+ margin: 0 0 8px;
81
+ font-family: var(--label-face);
82
+ font-size: var(--label-size);
83
+ font-weight: 400;
84
+ letter-spacing: var(--label-space);
85
+ text-transform: var(--label-case);
86
+ color: var(--ink-faint);
87
+ }
88
+ .ig-answer__a {
89
+ margin: 0;
90
+ max-width: var(--measure);
91
+ font-size: 15px;
92
+ line-height: 1.65;
93
+ color: var(--ink-soft);
94
+ white-space: pre-wrap;
95
+ }
60
96
  .ig-answer__a[data-empty="1"] { color: var(--ink-faint); font-style: italic; }
61
97
 
62
- .ig-score { display: flex; align-items: center; gap: 10px; margin: 0 0 6px; }
63
- .ig-score__track { width: 140px; height: 8px; border-radius: 4px; background: var(--border); overflow: hidden; }
64
- .ig-score__fill { height: 100%; border-radius: 4px; background: var(--accent); }
98
+ /* ---- the completeness readout -------------------------------------------- */
99
+ /* A READOUT, never an input: every number in it comes from the server's scorer.
100
+ The three bands describe the TEXT, not a verdict — the pass bar lives in the
101
+ economy module and this bar deliberately does not know it — which is why they
102
+ are the state colours and not the accent. */
103
+ .ig-score { display: flex; align-items: center; gap: 12px; margin: 0 0 var(--sp-xs); }
104
+ .ig-score__track { width: 140px; height: 6px; border-radius: 3px; background: var(--skeleton); overflow: hidden; }
105
+ .ig-score__fill { display: block; height: 100%; border-radius: 3px; background: var(--info); }
65
106
  .ig-score__fill[data-band="full"] { background: var(--ok); }
66
107
  .ig-score__fill[data-band="part"] { background: var(--warn); }
67
108
  .ig-score__fill[data-band="thin"] { background: var(--danger); }
68
- .ig-score__n { font-size: 13px; font-variant-numeric: tabular-nums; color: var(--ink-soft); }
69
-
70
- .ig-body { max-width: 70ch; line-height: 1.65; white-space: pre-wrap; margin: 4px 0 0; }
71
- .ig-empty { color: var(--ink-faint); font-size: 0.9rem; }
72
- .ig-note { font-size: 0.82rem; color: var(--ink-faint); line-height: 1.55; margin: 14px 0 0; }
73
- .ig-sub { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--border); }
74
- .ig-sub__h { margin: 0 0 8px; font-size: 0.95rem; }
109
+ .ig-score__n {
110
+ font-family: var(--label-face);
111
+ font-size: var(--label-size);
112
+ letter-spacing: var(--label-space);
113
+ text-transform: var(--label-case);
114
+ color: var(--ink-faint);
115
+ font-variant-numeric: tabular-nums;
116
+ }
75
117
 
76
- /* ---- the mark control (task 1002930 / R154) -----------------------------
77
- Author-only, and rendered as its own block rather than a button in a toolbar:
78
- offering your idea up for someone else to develop is a decision, and the one
79
- sentence explaining what it means to you is the part that makes it one. */
80
- .ig-mark {
81
- margin-top: 20px;
82
- padding: 14px;
83
- border: 1px solid var(--border);
84
- border-radius: var(--radius-sm);
85
- background: var(--surface-2);
118
+ /* ---- the prose ----------------------------------------------------------- */
119
+ .ig-body {
120
+ margin: var(--sp-xs) 0 0;
121
+ max-width: var(--measure);
122
+ font-size: 15px;
123
+ line-height: 1.65;
124
+ color: var(--ink-soft);
125
+ white-space: pre-wrap;
86
126
  }
87
- .ig-mark__lede { margin: 0 0 10px; font-size: 13px; color: var(--ink-soft); max-width: 62ch; line-height: 1.55; }
88
- .ig-mark__err { margin: 8px 0 0; font-size: 13px; color: var(--danger); }
127
+ .ig-body[data-empty="1"] { color: var(--ink-faint); font-style: italic; }
128
+ .ig-empty { margin: 0; font-size: 14px; color: var(--ink-faint); }
129
+ .ig-note {
130
+ margin: var(--sp-md) 0 0;
131
+ max-width: var(--measure);
132
+ font-size: 13.5px;
133
+ line-height: 1.55;
134
+ color: var(--ink-faint);
135
+ }
136
+ .ig-note a { display: inline-block; min-height: 24px; padding: 3px 4px; margin: 0 -4px; line-height: 18px; }
137
+ .ig-sub { margin-top: var(--sp-lg); padding-top: var(--sp-sm); border-top: var(--line) solid var(--rule-soft); }
138
+ .ig-sub__h {
139
+ margin: 0;
140
+ font-family: var(--label-face);
141
+ font-size: var(--label-size);
142
+ font-weight: 400;
143
+ letter-spacing: var(--label-space);
144
+ text-transform: var(--label-case);
145
+ color: var(--ink-faint);
146
+ }
147
+
148
+ /* ---- the mark control (task 1002930 / R154) ------------------------------ */
149
+ /* Author-only, and its own block rather than a button in a toolbar: offering
150
+ your idea up for someone else to develop is a decision, and the one sentence
151
+ explaining what it means to you is the part that makes it one. It is the page's
152
+ one live thing, so `.btn` — the accent door — is warm here and nowhere else. */
153
+ .ig-mark { margin-top: var(--sp-lg); padding-top: var(--sp-sm); border-top: var(--line) solid var(--rule-soft); }
154
+ .ig-mark__lede { margin: 0 0 var(--sp-sm); max-width: var(--measure); font-size: 14px; line-height: 1.55; color: var(--ink-soft); }
155
+ .ig-mark__err { margin: var(--sp-xs) 0 0; font-size: 13.5px; color: var(--danger); }
89
156
  .ig-mark__err:empty { display: none; }
@@ -9,6 +9,9 @@
9
9
  <link rel="preconnect" href="https://fonts.googleapis.com">
10
10
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
11
  <link rel="stylesheet" href="/builders/style.css?v=2026-07-02-shell1">
12
+ <!-- The record archetype, shared with task.html (task 1003305). idea.css below
13
+ carries only what an idea record has and a task record does not. -->
14
+ <link rel="stylesheet" href="/builders/record.css">
12
15
  <link rel="stylesheet" href="/builders/idea.css">
13
16
  </head>
14
17
  <body data-page="idea" data-page-title="Idea">
@@ -23,14 +26,21 @@
23
26
  <!-- This page is deliberately not in the nav (it is always reached from a
24
27
  link), so a deep-linked visitor needs a way back before the body loads
25
28
  — the task.html precedent. -->
26
- <a class="idea-detail__back" href="/ideas">← Back to Ideas</a>
27
- <p class="page-head__sub" id="idea-sub">Loading idea…</p>
29
+ <div class="rec-crumb">
30
+ <a class="rec-back" href="/ideas">
31
+ <svg class="rec-back__ic" viewBox="0 0 16 16" aria-hidden="true"><path d="M9.4 3.6 5 8l4.4 4.4"/><path d="M5 8h7.2"/></svg>Back to ideas</a>
32
+ <p class="rec-sub" id="idea-sub">Loading idea</p>
33
+ </div>
28
34
  </div>
29
35
 
30
36
  <main id="hall-main">
31
37
  <section class="scroll" id="idea-scroll" aria-labelledby="idea-h">
32
38
  <div id="idea-body">
33
- <p class="deeplink-card__title" id="idea-h">Loading…</p>
39
+ <!-- The h1 is present in EVERY state, this one included: the
40
+ one-header-main-h1 floor measures the rendered DOM, and a page
41
+ whose heading only arrives with the fetch has none while it
42
+ loads, and none at all when the read fails. -->
43
+ <h1 class="rec-big" id="idea-h">Loading the idea</h1>
34
44
  </div>
35
45
  </section>
36
46
  </main>