@bongos/core 1.19.454 → 1.19.456

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 (58) hide show
  1. package/.bongos-core.json +116 -51
  2. package/docs/adr/0047-discord-bugs-intake-anti-abuse.md +1 -0
  3. package/docs/copy-inventory.md +231 -228
  4. package/docs/copy-registry.json +352 -312
  5. package/docs/module-api-changelog.md +4 -0
  6. package/modules/discord/db.js +0 -3
  7. package/modules/discord/discord-bot.js +8 -1
  8. package/modules/discord/discord-bugs.js +133 -74
  9. package/modules/discord/module.json +1 -1
  10. package/modules/hall-ui/public/atlas.css +41 -32
  11. package/modules/hall-ui/public/atlas.html +8 -0
  12. package/modules/hall-ui/public/atlas.states.json +60 -0
  13. package/modules/hall-ui/public/collab.css +2 -2
  14. package/modules/hall-ui/public/collab.html +7 -1
  15. package/modules/hall-ui/public/collab.states.json +60 -0
  16. package/modules/hall-ui/public/copy-desk.css +21 -16
  17. package/modules/hall-ui/public/copy-desk.html +9 -1
  18. package/modules/hall-ui/public/copy-desk.states.json +59 -0
  19. package/modules/hall-ui/public/drachmae.html +38 -5
  20. package/modules/hall-ui/public/drachmae.js +20 -2
  21. package/modules/hall-ui/public/drachmae.states.json +64 -0
  22. package/modules/hall-ui/public/gate.css +20 -25
  23. package/modules/hall-ui/public/gate.html +9 -1
  24. package/modules/hall-ui/public/gate.states.json +60 -0
  25. package/modules/hall-ui/public/goals-lib.js +22 -5
  26. package/modules/hall-ui/public/goals-render.js +27 -5
  27. package/modules/hall-ui/public/goals.css +141 -60
  28. package/modules/hall-ui/public/goals.html +8 -1
  29. package/modules/hall-ui/public/goals.states.json +74 -0
  30. package/modules/hall-ui/public/government.css +35 -36
  31. package/modules/hall-ui/public/government.html +9 -1
  32. package/modules/hall-ui/public/government.states.json +59 -0
  33. package/modules/hall-ui/public/harbor.css +23 -26
  34. package/modules/hall-ui/public/harbor.html +9 -1
  35. package/modules/hall-ui/public/harbor.states.json +59 -0
  36. package/modules/hall-ui/public/modules.css +22 -21
  37. package/modules/hall-ui/public/modules.html +9 -1
  38. package/modules/hall-ui/public/modules.states.json +73 -0
  39. package/modules/hall-ui/public/people.html +7 -1
  40. package/modules/hall-ui/public/people.states.json +59 -0
  41. package/modules/hall-ui/public/scouting.html +7 -1
  42. package/modules/hall-ui/public/scouting.states.json +54 -0
  43. package/modules/hall-ui/public/sessions.css +1 -1
  44. package/modules/hall-ui/public/sessions.html +9 -1
  45. package/modules/hall-ui/public/sessions.states.json +58 -0
  46. package/modules/hall-ui/public/style.css +125 -5
  47. package/modules/hall-ui/public/watch.css +36 -39
  48. package/modules/hall-ui/public/watch.html +9 -1
  49. package/modules/hall-ui/public/watch.states.json +60 -0
  50. package/modules/lifecycle/goal-suggest.js +70 -2
  51. package/modules/lifecycle/lifecycle.js +7 -0
  52. package/package-lock.json +2 -2
  53. package/package.json +1 -1
  54. package/src/module-api.js +1 -1
  55. package/tests/discord_bugs.mjs +110 -31
  56. package/tests/filing_abuse_matrix.mjs +59 -20
  57. package/tests/goal_subtitle.mjs +14 -8
  58. package/tests/goal_suggest.mjs +107 -1
@@ -0,0 +1,60 @@
1
+ {
2
+ "_": "Collab — the two-sided ask board. ASKED OF YOU comes first because it is the half where someone is waiting, and the empty state of that half is the page's most common render. Written with the re-skin (task 1003307) so this page is MEASURED at three widths in both modes rather than looked at once.",
3
+ "page": "/builders/collab",
4
+ "surface": "hall-ui",
5
+ "stub": {
6
+ "prefix": "/builders/"
7
+ },
8
+ "modeQuery": false,
9
+ "_feeds": "The kit's own stub carries no feed for this page, so against it the page renders its EMPTY and SKELETON states — which is the point: those are states the floors must hold in. Against the hall-preview harness the same states render populated. Every declared 404 below is a fixture the harness does not carry, named rather than hidden.",
10
+ "ignoreRequests": [
11
+ "/api/(gds|bongos)/me$",
12
+ "/api/(gds|bongos)/me/",
13
+ "/api/(gds|bongos)/goals",
14
+ "/api/(gds|bongos)/inbox/",
15
+ "/api/(gds|bongos)/versions",
16
+ "/api/(gds|bongos)/collab",
17
+ "/api/(gds|bongos)/connections",
18
+ "/api/(gds|bongos)/v1/help-requests"
19
+ ],
20
+ "ignoreConsole": [
21
+ "/api/(gds|bongos)/",
22
+ "\\b401\\b",
23
+ "\\b403\\b",
24
+ "\\b404\\b",
25
+ "\\b501\\b",
26
+ "Unauthorized",
27
+ "Forbidden",
28
+ "Not Found",
29
+ "Not Implemented",
30
+ "load failed"
31
+ ],
32
+ "states": {
33
+ "board": {
34
+ "auth": true,
35
+ "actions": [
36
+ [
37
+ "wait",
38
+ 700
39
+ ]
40
+ ],
41
+ "expect": {
42
+ "visible": [
43
+ "#cb-for-me"
44
+ ]
45
+ }
46
+ },
47
+ "out": {
48
+ "auth": false
49
+ }
50
+ },
51
+ "reducedMotion": {
52
+ "auth": true,
53
+ "actions": [
54
+ [
55
+ "wait",
56
+ 600
57
+ ]
58
+ ]
59
+ }
60
+ }
@@ -17,18 +17,20 @@
17
17
  flex-direction: column;
18
18
  gap: 0.15rem;
19
19
  padding: 0.75rem 0.9rem;
20
- border: 1px solid var(--rule-soft);
21
- border-radius: 0.5rem;
22
- background: var(--surface-2);
20
+ border: var(--line) solid var(--rule-soft);
21
+ border-radius: var(--radius-sm);
22
+ background: var(--glass);
23
23
  }
24
24
  .cd-stat__n { font-size: 1.6rem; font-weight: 700; line-height: 1.1; }
25
25
  .cd-stat__l { font-size: 0.8rem; opacity: 0.7; }
26
26
 
27
27
  .cd-note { font-size: 0.85rem; opacity: 0.75; line-height: 1.5; }
28
+ /* 4px, not --radius-sm: a 10px radius on a 16px-tall inline span reads as a
29
+ pill, and the tier has no smaller token. Deliberate, not missed. */
28
30
  .cd-note code {
29
31
  padding: 0.05rem 0.3rem;
30
32
  border-radius: 0.25rem;
31
- background: var(--surface-2);
33
+ background: var(--glass);
32
34
  font-size: 0.95em;
33
35
  }
34
36
 
@@ -40,8 +42,8 @@
40
42
  .cd-dialog__input,
41
43
  .cd-dialog__text {
42
44
  padding: 0.4rem 0.6rem;
43
- border: 1px solid var(--rule-soft);
44
- border-radius: 0.35rem;
45
+ border: var(--line) solid var(--rule-soft);
46
+ border-radius: var(--radius-sm);
45
47
  background: var(--bg-deep);
46
48
  color: inherit;
47
49
  font: inherit;
@@ -56,9 +58,9 @@
56
58
 
57
59
  .cd-card {
58
60
  padding: 0.85rem 1rem;
59
- border: 1px solid var(--rule-soft);
60
- border-radius: 0.5rem;
61
- background: var(--surface-2);
61
+ border: var(--line) solid var(--rule-soft);
62
+ border-radius: var(--radius-sm);
63
+ background: var(--glass);
62
64
  }
63
65
  .cd-card--slim { padding: 0.6rem 0.8rem; }
64
66
  .cd-card__head { display: flex; flex-wrap: wrap; gap: 0.4rem; align-items: center; margin-bottom: 0.4rem; }
@@ -70,23 +72,23 @@
70
72
  leading and trailing space in a string is often exactly what is wrong. */
71
73
  .cd-quote {
72
74
  quotes: '"' '"';
73
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
75
+ font-family: var(--font-mono);
74
76
  font-size: 0.9em;
75
77
  white-space: pre-wrap;
76
78
  word-break: break-word;
77
79
  }
78
- .cd-where { font-size: 0.8rem; opacity: 0.6; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
80
+ .cd-where { font-size: 0.8rem; opacity: 0.6; font-family: var(--font-mono); }
79
81
  .cd-byline { font-size: 0.8rem; opacity: 0.65; margin-left: auto; }
80
82
  .cd-reason { margin: 0.3rem 0 0; line-height: 1.55; }
81
83
  .cd-snapshot-label { font-size: 0.8rem; opacity: 0.6; }
82
84
  .cd-closed { margin: 0.5rem 0 0; font-size: 0.85rem; opacity: 0.8; line-height: 1.5; }
83
- .cd-places { margin: 0.3rem 0 0; padding-left: 1.1rem; font-size: 0.8rem; opacity: 0.65; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
85
+ .cd-places { margin: 0.3rem 0 0; padding-left: 1.1rem; font-size: 0.8rem; opacity: 0.65; font-family: var(--font-mono); }
84
86
 
85
87
  .cd-tag {
86
88
  display: inline-block;
87
89
  padding: 0.1rem 0.45rem;
88
- border: 1px solid var(--rule);
89
- border-radius: 999px;
90
+ border: var(--line) solid var(--rule);
91
+ border-radius: var(--radius-pill);
90
92
  font-size: 0.72rem;
91
93
  letter-spacing: 0.02em;
92
94
  opacity: 0.85;
@@ -103,11 +105,14 @@
103
105
  .cd-dialog {
104
106
  width: min(34rem, 92vw);
105
107
  padding: 1.25rem;
106
- border: 1px solid var(--rule);
107
- border-radius: 0.6rem;
108
+ border: var(--line) solid var(--rule);
109
+ border-radius: var(--radius);
108
110
  background: var(--bg-card);
109
111
  color: inherit;
110
112
  }
113
+ /* The one literal this sheet keeps. Pure black with alpha used as a SCRIM is
114
+ the single exception the Fifteen Rule names, and a backdrop cannot take a
115
+ ground token: it has to darken whatever is behind it in either mode. */
111
116
  .cd-dialog::backdrop { background: rgba(0, 0, 0, 0.55); }
112
117
  .cd-dialog__h { margin: 0 0 0.5rem; font-size: 1.1rem; }
113
118
  .cd-dialog__target { margin: 0 0 0.9rem; line-height: 1.5; }
@@ -19,7 +19,15 @@
19
19
  <div class="app-body">
20
20
  <header class="app-topbar" id="app-topbar"></header>
21
21
  <div class="app-content">
22
- <div class="page-head"><p class="page-head__sub" id="cd-sub">Loading the copy desk…</p></div>
22
+ <!-- task 1003307: the board page head. Copy desk shipped with NO <h1> at all
23
+ (one-header-main-h1 measured 0 on every render), so the re-skin
24
+ carries the structural fix rather than porting the hole. The heading
25
+ is the LABEL voice, not a display line: on a working surface the
26
+ page's own name is the least interesting thing on it. -->
27
+ <div class="page-head page-head--board">
28
+ <h1 class="page-head__h">Copy desk</h1>
29
+ <p class="page-head__sub" id="cd-sub">Loading the copy desk…</p>
30
+ </div>
23
31
 
24
32
  <main id="hall-main">
25
33
  <!-- The counts. Deliberately four numbers and not a dashboard: open work,
@@ -0,0 +1,59 @@
1
+ {
2
+ "_": "Copy desk — the one-voice review queue. Its confirm dialog is a real <dialog>, so its ::backdrop is the sheet's single sanctioned literal (black with alpha as a scrim). Written with the re-skin (task 1003307) so this page is MEASURED at three widths in both modes rather than looked at once.",
3
+ "page": "/builders/copy-desk",
4
+ "surface": "hall-ui",
5
+ "stub": {
6
+ "prefix": "/builders/"
7
+ },
8
+ "modeQuery": false,
9
+ "_feeds": "The kit's own stub carries no feed for this page, so against it the page renders its EMPTY and SKELETON states — which is the point: those are states the floors must hold in. Against the hall-preview harness the same states render populated. Every declared 404 below is a fixture the harness does not carry, named rather than hidden.",
10
+ "ignoreRequests": [
11
+ "/api/(gds|bongos)/me$",
12
+ "/api/(gds|bongos)/me/",
13
+ "/api/(gds|bongos)/goals",
14
+ "/api/(gds|bongos)/inbox/",
15
+ "/api/(gds|bongos)/versions",
16
+ "/api/(gds|bongos)/copy",
17
+ "/api/(gds|bongos)/security/"
18
+ ],
19
+ "ignoreConsole": [
20
+ "/api/(gds|bongos)/",
21
+ "\\b401\\b",
22
+ "\\b403\\b",
23
+ "\\b404\\b",
24
+ "\\b501\\b",
25
+ "Unauthorized",
26
+ "Forbidden",
27
+ "Not Found",
28
+ "Not Implemented",
29
+ "load failed"
30
+ ],
31
+ "states": {
32
+ "queue": {
33
+ "auth": true,
34
+ "actions": [
35
+ [
36
+ "wait",
37
+ 700
38
+ ]
39
+ ],
40
+ "expect": {
41
+ "visible": [
42
+ "#cd-summary-scroll"
43
+ ]
44
+ }
45
+ },
46
+ "out": {
47
+ "auth": false
48
+ }
49
+ },
50
+ "reducedMotion": {
51
+ "auth": true,
52
+ "actions": [
53
+ [
54
+ "wait",
55
+ 600
56
+ ]
57
+ ]
58
+ }
59
+ }
@@ -9,9 +9,15 @@
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
+ <!-- task 1003307: the reading-room archetype's own sheet. drachmae reuses the
13
+ primer's layout and loaded primer.css for it, but never room.css — so it
14
+ had the room's DOM and none of the room's world: no reading measure, no
15
+ prose-link 24px floor, no table wrapper. Loading it is the fix; the order
16
+ matches primer.html (room first, the page's own sheet on top). -->
17
+ <link rel="stylesheet" href="/builders/room.css">
12
18
  <link rel="stylesheet" href="/builders/primer.css?v=2026-07-02-shell1">
13
19
  </head>
14
- <body class="primer-body" data-page="drachmae" data-page-title="Drachmae &amp; karma">
20
+ <body class="room primer-body" data-page="drachmae" data-page-title="Drachmae &amp; karma">
15
21
 
16
22
  <!-- Reading-progress bar — fills as the reader scrolls through the article. -->
17
23
  <div class="primer-progress" aria-hidden="true"><div class="primer-progress__bar" id="primer-progress-bar"></div></div>
@@ -23,7 +29,13 @@
23
29
  <header class="app-topbar" id="app-topbar"></header>
24
30
  <div class="app-content">
25
31
 
26
- <div class="page-head">
32
+ <!-- task 1003307: the reading-room head. drachmae shares the primer's
33
+ .primer-layout and is read as a document, so it takes .page-head--room
34
+ (task 1003306's archetype) rather than the board's label voice — and
35
+ it gets the <h1> it never had. -->
36
+ <div class="page-head page-head--room">
37
+ <p class="page-head__eyebrow">Docs</p>
38
+ <h1 class="page-head__h">Drachmae &amp; karma</h1>
27
39
  <p class="page-head__sub">How you get paid, and how you earn trust — the reward model in plain language. About a five-minute read.</p>
28
40
  </div>
29
41
 
@@ -45,9 +57,30 @@
45
57
  </aside>
46
58
 
47
59
  <!-- Article column — markdown rendered here. -->
48
- <article class="primer-article" id="primer-article" aria-busy="true">
49
- <div class="primer-loading">Loading the drachmae guide…</div>
50
- </article>
60
+ <!-- The room's loading shape: skeleton lines in the article's own
61
+ measure, not a sentence. (.primer-loading, which used to sit in the
62
+ article, is defined in no stylesheet and rendered unstyled.) -->
63
+ <div class="primer-skeleton scroll" id="primer-skeleton" aria-hidden="true">
64
+ <span class="primer-sk primer-skeleton__h"></span>
65
+ <div class="primer-skeleton__p">
66
+ <span class="primer-sk"></span>
67
+ <span class="primer-sk"></span>
68
+ <span class="primer-sk"></span>
69
+ </div>
70
+ <div class="primer-skeleton__rule"></div>
71
+ <div class="primer-skeleton__p">
72
+ <span class="primer-sk"></span>
73
+ <span class="primer-sk"></span>
74
+ </div>
75
+ </div>
76
+
77
+ <article class="primer-article scroll" id="primer-article" aria-busy="true" hidden></article>
78
+
79
+ <div class="primer-note primer-note--error scroll" id="primer-error" hidden>
80
+ <p class="primer-note__label">The guide did not load</p>
81
+ <p class="primer-note__body">The markdown behind this page could not be fetched. Reload to try again, or read it in the repo.</p>
82
+ <p class="primer-note__acts"><a class="btn btn--ghost" href="/builders">Back to home</a></p>
83
+ </div>
51
84
 
52
85
  </div>
53
86
  </main>
@@ -146,9 +146,21 @@
146
146
  });
147
147
  }
148
148
 
149
+ // The three panels the article can resolve into (the primer's show(), which
150
+ // this page's DOM has mirrored since task 1472 and now finally matches).
151
+ function show(which) {
152
+ for (const [id, on] of [['#primer-skeleton', which === 'loading'],
153
+ ['#primer-article', which === 'article'],
154
+ ['#primer-error', which === 'error']]) {
155
+ const el = $(id);
156
+ if (el) el.hidden = !on;
157
+ }
158
+ }
159
+
149
160
  async function boot() {
150
161
  const article = $('#primer-article');
151
162
  if (!article || !window.OTBMdReader) return;
163
+ show('loading');
152
164
  let md;
153
165
  try {
154
166
  const [mdRes] = await Promise.all([
@@ -159,8 +171,8 @@
159
171
  md = await mdRes.text();
160
172
  } catch (err) {
161
173
  console.error('[drachmae] failed to load doc', err);
162
- article.innerHTML = `<p class="primer-loading">Could not load the drachmae guide. <a href="/builders">Go back</a> and try again.</p>`;
163
174
  article.removeAttribute('aria-busy');
175
+ show('error');
164
176
  return;
165
177
  }
166
178
  const resolveLinkHref = window.OTBMdReader.makeResolveLinkHref({
@@ -169,9 +181,15 @@
169
181
  getTreeBase: () => repoTreeBase,
170
182
  diagramsHref: '/builders/diagrams',
171
183
  });
172
- const { html, headings } = window.OTBMdReader.parseMarkdown(md, resolveLinkHref);
184
+ const parsed = window.OTBMdReader.parseMarkdown(md, resolveLinkHref);
185
+ // The doc's leading "# …" is the same page name .page-head--room already
186
+ // renders. Two <h1>s fails one-header-main-h1, and the page head is the one
187
+ // that exists before the fetch and survives its failure (task 1003307).
188
+ const html = parsed.html.replace(/^\s*<h1\b[^>]*>[\s\S]*?<\/h1>\s*/, '');
189
+ const headings = parsed.headings.filter((h) => h.level > 1);
173
190
  article.innerHTML = html;
174
191
  article.removeAttribute('aria-busy');
192
+ show('article');
175
193
  // Awaited before the TOC is built so the rate card gets a TOC entry and a
176
194
  // scrollspy target; the article itself is already on screen by now.
177
195
  const bountyHeading = await appendBountySection(article);
@@ -0,0 +1,64 @@
1
+ {
2
+ "_": "Drachmae & karma — a READING ROOM, not a board: it shares the primer .primer-layout and takes .page-head--room. Public (no page gate), so the signed-out render is the one most readers get. Written with the re-skin (task 1003307) so this page is MEASURED at three widths in both modes rather than looked at once.",
3
+ "page": "/builders/drachmae",
4
+ "surface": "hall-ui",
5
+ "stub": {
6
+ "prefix": "/builders/"
7
+ },
8
+ "modeQuery": false,
9
+ "_feeds": "The kit's own stub carries no feed for this page, so against it the page renders its EMPTY and SKELETON states — which is the point: those are states the floors must hold in. Against the hall-preview harness the same states render populated. Every declared 404 below is a fixture the harness does not carry, named rather than hidden.",
10
+ "ignoreRequests": [
11
+ "/api/(gds|bongos)/me$",
12
+ "/api/(gds|bongos)/me/",
13
+ "/api/(gds|bongos)/goals",
14
+ "/api/(gds|bongos)/inbox/",
15
+ "/api/(gds|bongos)/versions"
16
+ ],
17
+ "ignoreConsole": [
18
+ "/api/(gds|bongos)/",
19
+ "\\b401\\b",
20
+ "\\b403\\b",
21
+ "\\b404\\b",
22
+ "\\b501\\b",
23
+ "Unauthorized",
24
+ "Forbidden",
25
+ "Not Found",
26
+ "Not Implemented",
27
+ "load failed"
28
+ ],
29
+ "states": {
30
+ "read": {
31
+ "auth": true,
32
+ "actions": [
33
+ [
34
+ "wait",
35
+ 600
36
+ ]
37
+ ],
38
+ "expect": {
39
+ "visible": [
40
+ "#primer-article",
41
+ "#primer-toc"
42
+ ]
43
+ }
44
+ },
45
+ "out": {
46
+ "auth": false,
47
+ "actions": [
48
+ [
49
+ "wait",
50
+ 600
51
+ ]
52
+ ]
53
+ }
54
+ },
55
+ "reducedMotion": {
56
+ "auth": true,
57
+ "actions": [
58
+ [
59
+ "wait",
60
+ 600
61
+ ]
62
+ ]
63
+ }
64
+ }
@@ -13,8 +13,8 @@
13
13
  margin: 6px 0 22px;
14
14
  }
15
15
  .gate-stat {
16
- border: 1px solid var(--border); border-radius: var(--radius-sm);
17
- background: var(--surface); padding: 12px 14px;
16
+ border: var(--line) solid var(--rule-soft); border-radius: var(--radius-sm);
17
+ background: var(--bg-card); padding: 12px 14px;
18
18
  }
19
19
  .gate-stat__label {
20
20
  font-size: 11.5px; font-weight: 600;
@@ -28,8 +28,8 @@
28
28
  .gate-list { display: flex; flex-direction: column; gap: 16px; }
29
29
 
30
30
  .gate-crate {
31
- border: 1px solid var(--border); border-radius: var(--radius);
32
- background: var(--surface); overflow: hidden;
31
+ border: var(--line) solid var(--rule-soft); border-radius: var(--radius);
32
+ background: var(--bg-card); overflow: hidden;
33
33
  }
34
34
  /* Verdict band along the top, painted by the held-verdict. */
35
35
  .gate-crate__seal { height: 4px; background-color: var(--danger); }
@@ -40,45 +40,40 @@
40
40
  .gate-crate__title a { color: inherit; text-decoration: none; }
41
41
  .gate-crate__title a:hover { text-decoration: underline; }
42
42
  .gate-crate__meta { color: var(--ink-soft); font-size: 12.5px; }
43
- .gate-crate__meta a { color: var(--accent-text); }
43
+ .gate-crate__meta a { color: var(--accent-ink); }
44
44
 
45
- .gate-pill {
46
- font-size: 11px; font-weight: 600;
47
- letter-spacing: 0.04em; text-transform: uppercase;
48
- padding: 2px 8px; border-radius: 999px; white-space: nowrap;
49
- border: 1px solid var(--border); color: var(--ink-soft); background: var(--surface-2);
50
- }
51
- .gate-pill--escalate { background: var(--danger-weak); border-color: color-mix(in srgb, var(--danger) 30%, transparent); color: var(--danger); }
52
- .gate-pill--needs_trust { background: var(--warn-weak); border-color: color-mix(in srgb, var(--warn) 30%, transparent); color: var(--warn); }
53
- .gate-pill--trusted { background: var(--info-weak); border-color: color-mix(in srgb, var(--info) 30%, transparent); color: var(--info); }
45
+ /* Base: the shell's .fact-pill (task 1003307). States are hairlines, not fills. */
46
+ .gate-pill--escalate { color: var(--danger); }
47
+ .gate-pill--needs_trust { color: var(--warn); }
48
+ .gate-pill--trusted { color: var(--info); }
54
49
 
55
50
  .gate-reasons { list-style: none; padding: 0; margin: 0; }
56
51
  .gate-reasons li {
57
- padding: 6px 0; border-bottom: 1px solid var(--border);
52
+ padding: 6px 0; border-bottom: var(--line) solid var(--rule-soft);
58
53
  font-size: 13px; color: var(--ink);
59
54
  }
60
55
  .gate-reasons li:last-child { border-bottom: 0; }
61
56
  .gate-reasons code {
62
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px;
63
- color: var(--accent-text); background: var(--surface-2);
57
+ font-family: var(--font-mono); font-size: 12px;
58
+ color: var(--accent-ink); background: var(--glass);
64
59
  padding: 1px 5px; border-radius: 4px;
65
60
  }
66
61
  .gate-reasons .floor { color: var(--danger); font-weight: 600; }
67
62
 
68
63
  .gate-files { font-size: 12.5px; color: var(--ink-soft); }
69
- .gate-files code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
64
+ .gate-files code { font-family: var(--font-mono); }
70
65
 
71
66
  .gate-actions { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; margin-top: 2px; }
72
67
  .gate-btn {
73
68
  font: inherit; font-size: 13px; font-weight: 600;
74
- padding: 7px 16px; border: 1px solid var(--accent);
75
- background: var(--accent); color: #fff;
69
+ padding: 7px 16px; border: var(--line) solid var(--accent);
70
+ background: var(--accent); color: var(--accent-on);
76
71
  border-radius: var(--radius-sm); cursor: pointer;
77
72
  }
78
- .gate-btn:hover:not(:disabled) { background: var(--accent-deep); border-color: var(--accent-deep); }
73
+ .gate-btn:hover:not(:disabled) { background: var(--accent-lift); border-color: var(--accent-lift); }
79
74
  .gate-btn:disabled { opacity: 0.45; cursor: default; }
80
- .gate-btn--ghost { background: var(--surface); color: var(--ink); border-color: var(--border-strong); }
81
- .gate-btn--ghost:hover:not(:disabled) { background: var(--surface-2); border-color: var(--border-strong); }
75
+ .gate-btn--ghost { background: var(--bg-card); color: var(--ink); border-color: var(--rule); }
76
+ .gate-btn--ghost:hover:not(:disabled) { background: var(--glass); border-color: var(--rule); }
82
77
  .gate-approved-note { color: var(--ok); font-size: 13px; }
83
78
 
84
79
  .gate-empty, .gate-sealed { color: var(--ink-soft); padding: 16px 0; }
@@ -87,10 +82,10 @@
87
82
  /* Lightweight confirm modal (mirrors harbor). */
88
83
  .gate-modal-bg {
89
84
  position: fixed; inset: 0; background: color-mix(in srgb, var(--ink) 40%, transparent);
90
- display: flex; align-items: center; justify-content: center; z-index: 50; padding: 20px;
85
+ display: flex; align-items: center; justify-content: center; z-index: var(--z-modal); padding: 20px;
91
86
  }
92
87
  .gate-modal {
93
- background: var(--surface); border: 1px solid var(--border-strong); border-radius: var(--radius);
88
+ background: var(--bg-card); border: var(--line) solid var(--rule); border-radius: var(--radius);
94
89
  max-width: 480px; width: 100%; padding: 20px 22px; box-shadow: var(--shadow-pop);
95
90
  }
96
91
  .gate-modal h2 { color: var(--ink); margin: 0 0 8px; font-size: 17px; font-weight: 600; }
@@ -19,7 +19,15 @@
19
19
  <div class="app-body">
20
20
  <header class="app-topbar" id="app-topbar"></header>
21
21
  <div class="app-content">
22
- <div class="page-head"><p class="page-head__sub" id="gate-sub">Loading…</p></div>
22
+ <!-- task 1003307: the board page head. Gate shipped with NO <h1> at all
23
+ (one-header-main-h1 measured 0 on every render), so the re-skin
24
+ carries the structural fix rather than porting the hole. The heading
25
+ is the LABEL voice, not a display line: on a working surface the
26
+ page's own name is the least interesting thing on it. -->
27
+ <div class="page-head page-head--board">
28
+ <h1 class="page-head__h">Gate</h1>
29
+ <p class="page-head__sub" id="gate-sub">Loading…</p>
30
+ </div>
23
31
 
24
32
  <main id="hall-main">
25
33
  <p class="gate-sealed" id="gate-sealed" hidden>Archons only.</p>
@@ -0,0 +1,60 @@
1
+ {
2
+ "_": "Gate — the access-request queue behind requireGovernmentPage. Same confirm-modal component as harbor, which is why the two had guessed two different z-indexes for it. Written with the re-skin (task 1003307) so this page is MEASURED at three widths in both modes rather than looked at once.",
3
+ "page": "/builders/gate",
4
+ "surface": "hall-ui",
5
+ "stub": {
6
+ "prefix": "/builders/"
7
+ },
8
+ "modeQuery": false,
9
+ "_feeds": "The kit's own stub carries no feed for this page, so against it the page renders its EMPTY and SKELETON states — which is the point: those are states the floors must hold in. Against the hall-preview harness the same states render populated. Every declared 404 below is a fixture the harness does not carry, named rather than hidden.",
10
+ "ignoreRequests": [
11
+ "/api/(gds|bongos)/me$",
12
+ "/api/(gds|bongos)/me/",
13
+ "/api/(gds|bongos)/goals",
14
+ "/api/(gds|bongos)/inbox/",
15
+ "/api/(gds|bongos)/versions",
16
+ "/api/(gds|bongos)/gate-approvals",
17
+ "/api/(gds|bongos)/builders",
18
+ "/api/(gds|bongos)/access-requests"
19
+ ],
20
+ "ignoreConsole": [
21
+ "/api/(gds|bongos)/",
22
+ "\\b401\\b",
23
+ "\\b403\\b",
24
+ "\\b404\\b",
25
+ "\\b501\\b",
26
+ "Unauthorized",
27
+ "Forbidden",
28
+ "Not Found",
29
+ "Not Implemented",
30
+ "load failed"
31
+ ],
32
+ "states": {
33
+ "queue": {
34
+ "auth": true,
35
+ "actions": [
36
+ [
37
+ "wait",
38
+ 700
39
+ ]
40
+ ],
41
+ "expect": {
42
+ "visible": [
43
+ "#gate-body"
44
+ ]
45
+ }
46
+ },
47
+ "out": {
48
+ "auth": false
49
+ }
50
+ },
51
+ "reducedMotion": {
52
+ "auth": true,
53
+ "actions": [
54
+ [
55
+ "wait",
56
+ 600
57
+ ]
58
+ ]
59
+ }
60
+ }
@@ -90,15 +90,32 @@
90
90
  // goals.html loads task-lib.js before this file (the render-family order).
91
91
  const { msOf, taskActivityMs } = window.OTBTaskLib;
92
92
 
93
- // task 1003224 — on a LIST, a goal's subtitle is a TOOLTIP, never a second
94
- // visible line. The board's job there is to let someone find a goal by name;
95
- // rendering every explanation inline is what led 29 of 38 titles to carry
96
- // their own after a dash. Escaped because it lands inside an attribute.
93
+ // task 1003224 made a goal's subtitle a TOOLTIP on a list, because 29 of 38
94
+ // titles already carried their own explanation after a dash and a row would
95
+ // then say the same thing twice.
96
+ //
97
+ // Task 1003307 REVERSES that for the row, on the owner's call, against the
98
+ // approved archetype (docs/design/mocks/hall/goals/v3.html, .goal__sub). Two
99
+ // things changed under 1003224's reasoning: the dash-in-title rate fell to 17
100
+ // of 72, and 35 of those 72 goals now carry a real subtitle that a tooltip
101
+ // hides from anyone reading — and hides COMPLETELY from anyone on a touch
102
+ // screen, where there is no hover. A duplicated explanation is a copy-desk
103
+ // fix; an invisible one is not fixable at all.
104
+ //
105
+ // subtitleAttr survives for the CARD view, which has no room for a line.
97
106
  function subtitleAttr(g) {
98
107
  const t = (g && g.subtitle ? String(g.subtitle) : '').trim();
99
108
  return t ? ` title="${window.OTB.escapeHtml(t)}"` : '';
100
109
  }
101
110
 
111
+ // The row's visible subtitle. Empty string when the goal has none, so the row
112
+ // collapses to title + facts rather than reserving a blank line — the card's
113
+ // "every region hides on null" grammar, applied to a row.
114
+ function subtitleLine(g) {
115
+ const t = (g && g.subtitle ? String(g.subtitle) : '').trim();
116
+ return t ? `<p class="gr-sub">${window.OTB.escapeHtml(t)}</p>` : '';
117
+ }
118
+
102
119
  // task 1003224 — the three-layer prose rules for a goal's detail head, kept
103
120
  // here rather than in goals-page.js because that file is 500 lines over the
104
121
  // excess-lines ratchet and this one exists for exactly that reason. It WRITES
@@ -120,6 +137,6 @@
120
137
  ring, memberName,
121
138
  readinessOf, EMPTY_ROLLUP, goalRollLine,
122
139
  matchesTodoSearch, msOf, taskActivityMs,
123
- subtitleAttr, applyGoalProse,
140
+ subtitleAttr, subtitleLine, applyGoalProse,
124
141
  };
125
142
  })();