@bongos/core 1.19.420 → 1.19.421
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 +68 -33
- package/docs/copy-inventory.md +282 -281
- package/docs/copy-registry.json +375 -366
- package/docs/design/mocks/hub/mine.html +377 -0
- package/docs/design/projects-hub-direction-v2.md +25 -1
- package/docs/file-map.md +1 -0
- package/docs/module-api-changelog.md +2 -0
- package/modules/hall-ui/public/settings.html +3 -2
- package/modules/hall-ui/public/settings.js +10 -6
- package/modules/platform-identity/account-visibility.js +94 -3
- package/modules/platform-identity/platform-identity.js +45 -50
- package/modules/platform-identity/routes/connections.js +3 -3
- package/modules/ui-design/kit/fixtures/featured-hub.json +1 -0
- package/modules/ui-design/kit/fixtures/provisioning-instances-empty.json +4 -0
- package/modules/ui-design/kit/fixtures/provisioning-instances.json +99 -0
- package/modules/ui-design/kit/lib.js +6 -2
- package/modules/ui-design/kit/probe.js +1 -1
- package/modules/ui-design/kit/render.js +1 -1
- package/modules/ui-design/kit/serve.js +63 -2
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/assets/cosmos.css +224 -18
- package/public-landing/projects-empty.states.json +49 -0
- package/public-landing/projects-floor.states.json +63 -0
- package/public-landing/projects.html +112 -51
- package/public-landing/projects.probes.json +32 -7
- package/public-landing/projects.states.json +10 -6
- package/src/module-api.js +1 -1
- package/tests/account_privacy_flags.mjs +17 -10
- package/tests/effective_visibility_predicate.mjs +891 -0
- package/tests/profile_route.mjs +22 -11
- package/tests/projects_hub.mjs +33 -5
- package/tests/projects_hub_pre_uat.mjs +18 -6
- package/tests/ui_design_kit.mjs +32 -2
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en" data-mode="dark">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
6
|
+
<title>Your projects and the manage page, in the world</title>
|
|
7
|
+
<link rel="icon" href="data:,">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
9
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
10
|
+
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400&family=Manrope:wght@300;400;500;600&display=swap">
|
|
11
|
+
<link rel="stylesheet" href="../../../../public-landing/assets/cosmos.css">
|
|
12
|
+
<style>
|
|
13
|
+
body{
|
|
14
|
+
/* The composition round for task 1003345 (docs/design/projects-hub-direction-v2.md §14). cosmos.css
|
|
15
|
+
carries the hub's contract, the planets (their crops and materials) and the fixed status
|
|
16
|
+
vocabulary; this sheet adds only the two views' composition in the world's grammar. Dark only
|
|
17
|
+
(ADR 0204). Every colour is a token. */
|
|
18
|
+
--orb-shadow:opacity(1);
|
|
19
|
+
}
|
|
20
|
+
/* ── the planet, docked on a row: the sky's object, relative instead of absolute ── */
|
|
21
|
+
.mock .worldOrb{position:relative;left:auto;top:auto;transform:none;display:block;cursor:default;}
|
|
22
|
+
.mock .worldOrb .disc{display:block;margin:0;}
|
|
23
|
+
.mock .worldOrb.ghost{opacity:.4;filter:saturate(.2);}
|
|
24
|
+
.mock .worldOrb.ashen{filter:saturate(.3) brightness(.78);}
|
|
25
|
+
.mock .worldOrb.dim{filter:saturate(.55) brightness(.92);}
|
|
26
|
+
|
|
27
|
+
/* ── the view: a column, a lowercase display title, one hairline field, hairline pills ── */
|
|
28
|
+
.view{max-width:1180px;margin:0 auto;padding:34px var(--gutter) 60px;}
|
|
29
|
+
.vHead{display:flex;align-items:baseline;gap:18px;flex-wrap:wrap;margin:0 0 6px;}
|
|
30
|
+
.vTitle{font-family:var(--font-display);font-weight:300;font-size:clamp(28px,3.4vw,44px);letter-spacing:-.035em;line-height:1.04;text-transform:lowercase;color:var(--ink);margin:0;}
|
|
31
|
+
.vCount{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint);}
|
|
32
|
+
.vLede{font-size:15.5px;line-height:1.5;color:var(--ink-soft);max-width:62ch;margin:0 0 22px;}
|
|
33
|
+
.hairField{display:flex;align-items:center;gap:12px;border-bottom:1px solid var(--rule);padding:10px 0;max-width:520px;}
|
|
34
|
+
.hairField svg{width:18px;height:18px;stroke:var(--ink-faint);fill:none;stroke-width:1.6;flex:none;}
|
|
35
|
+
.hairField input{flex:1;min-width:0;background:none;border:0;font-family:var(--font-body);font-size:15.5px;color:var(--ink);padding:4px 0;}
|
|
36
|
+
.hairField input::placeholder{color:var(--ink-faint);}
|
|
37
|
+
.hairField input:focus{outline:none;}
|
|
38
|
+
.hairField:focus-within{border-bottom-color:var(--ink);}
|
|
39
|
+
.hairField .count{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint);}
|
|
40
|
+
.toolRow{display:flex;align-items:center;justify-content:space-between;gap:18px;flex-wrap:wrap;margin:16px 0 8px;}
|
|
41
|
+
.hpills{display:flex;gap:8px;flex-wrap:wrap;}
|
|
42
|
+
.hpill{display:inline-flex;align-items:center;gap:8px;min-height:36px;padding:0 15px;border:1px solid var(--rule);border-radius:var(--radius-pill);background:none;font-family:var(--font-body);font-size:14.5px;color:var(--ink-soft);cursor:pointer;text-transform:lowercase;}
|
|
43
|
+
.hpill:hover{border-color:var(--ink-faint);color:var(--ink);}
|
|
44
|
+
.hpill[aria-pressed="true"]{background:var(--ink);color:var(--bg);border-color:var(--ink);}
|
|
45
|
+
.hpill .n{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);}
|
|
46
|
+
.hpill:not([aria-pressed="true"]) .n{color:var(--ink-faint);}
|
|
47
|
+
|
|
48
|
+
/* ── v1 · the ledger row: planet · name and chip · the facts · the rail · the doors ── */
|
|
49
|
+
.rows{list-style:none;margin:0;padding:0;}
|
|
50
|
+
.mrow{display:grid;grid-template-columns:72px minmax(200px,1.2fr) 1.3fr minmax(230px,1fr) auto;gap:22px;align-items:center;padding:22px 0;border-top:1px solid var(--rule-soft);}
|
|
51
|
+
.rows .mrow:last-child{border-bottom:1px solid var(--rule-soft);}
|
|
52
|
+
.mrow.gone .mname{color:var(--ink-soft);}
|
|
53
|
+
.who{display:grid;gap:8px;justify-items:start;min-width:0;}
|
|
54
|
+
.who .chip{white-space:normal;line-height:1.3;}
|
|
55
|
+
.mname{font-family:var(--font-body);font-weight:500;font-size:17px;letter-spacing:-.015em;color:var(--ink);}
|
|
56
|
+
.facts{display:grid;gap:5px;min-width:0;}
|
|
57
|
+
.fact{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint);overflow-wrap:anywhere;}
|
|
58
|
+
.fact a,.fact code{font-family:var(--font-mono);font-size:12.5px;letter-spacing:0;text-transform:none;color:var(--ink-soft);text-decoration:none;background:none;padding:0;}
|
|
59
|
+
.fact a,.ledger .v a,.backRow a{display:inline-block;padding:3px 0;min-height:24px;line-height:18px;}
|
|
60
|
+
.fact a:hover{color:var(--ink);text-decoration:underline;}
|
|
61
|
+
.fact.err{color:var(--err);text-transform:none;letter-spacing:0;font-family:var(--font-body);font-size:13.5px;line-height:1.45;}
|
|
62
|
+
.fact.soft{color:var(--ink-soft);text-transform:none;letter-spacing:0;font-family:var(--font-body);font-size:13.5px;line-height:1.45;}
|
|
63
|
+
/* the lifecycle rail, the strip's replacement: three hairlines, a 7px dot, one lit step (DESIGN.md) */
|
|
64
|
+
.rail{display:grid;grid-template-columns:repeat(3,1fr);gap:0 10px;list-style:none;margin:0;padding:0;}
|
|
65
|
+
.rail li{position:relative;border-top:1px solid var(--rule);padding:12px 0 0;font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint);line-height:1.3;}
|
|
66
|
+
.rail li::before{content:"";position:absolute;top:-4px;left:0;width:7px;height:7px;border-radius:50%;border:1px solid var(--rule);background:var(--bg);}
|
|
67
|
+
.rail li.done{border-top-color:var(--ink-soft);color:var(--ink-soft);}
|
|
68
|
+
.rail li.done::before{background:var(--ink-soft);border-color:var(--ink-soft);}
|
|
69
|
+
.rail li.now{border-top-color:var(--accent);color:var(--ink);}
|
|
70
|
+
.rail li.now::before{background:var(--accent);border-color:var(--accent);outline:4px solid color-mix(in srgb,var(--accent) 22%,transparent);}
|
|
71
|
+
/* verification states keep the world's fixed vocabulary: live is green, a wind-down is neutral */
|
|
72
|
+
.rail.isLive li.done{border-top-color:var(--ok);color:var(--ok);}
|
|
73
|
+
.rail.isLive li.done::before{background:var(--ok);border-color:var(--ok);}
|
|
74
|
+
.rail.flowDown li.now{border-top-color:var(--ink-soft);color:var(--ink-soft);}
|
|
75
|
+
.rail.flowDown li.now::before{background:var(--ink-soft);border-color:var(--ink-soft);outline:none;}
|
|
76
|
+
.railNote{grid-column:1/-1;margin:8px 0 0;font-family:var(--font-body);font-size:13px;line-height:1.45;color:var(--ink-soft);}
|
|
77
|
+
.railWrap{display:grid;gap:0;}
|
|
78
|
+
/* the doors: hairline pills, the accent only on the one that acts; the ender stays on the error ink */
|
|
79
|
+
.doors{display:flex;gap:8px;flex-wrap:wrap;justify-content:flex-end;}
|
|
80
|
+
.door{display:inline-flex;align-items:center;justify-content:center;min-height:40px;padding:0 18px;border:1px solid var(--rule);border-radius:var(--radius-pill);background:none;font-family:var(--font-body);font-size:14.5px;color:var(--ink);text-transform:lowercase;text-decoration:none;cursor:pointer;white-space:nowrap;}
|
|
81
|
+
.door:hover{border-color:var(--ink-faint);}
|
|
82
|
+
.door.primary{background:var(--accent);border-color:var(--accent);color:var(--accent-on);font-weight:500;}
|
|
83
|
+
.door.primary:hover{background:color-mix(in srgb,var(--accent) 88%,var(--ink));}
|
|
84
|
+
.door.danger{border-color:color-mix(in srgb,var(--err) 45%,transparent);color:var(--err);}
|
|
85
|
+
.door.danger:hover{border-color:var(--err);}
|
|
86
|
+
|
|
87
|
+
/* ── v2 · the poster row: the planet large, the name in the display voice, the rail full width ── */
|
|
88
|
+
.prow{display:grid;grid-template-columns:132px 1fr auto;gap:0 28px;align-items:start;padding:30px 0;border-top:1px solid var(--rule-soft);}
|
|
89
|
+
.prow .pname{font-family:var(--font-display);font-weight:300;font-size:30px;letter-spacing:-.035em;line-height:1.04;text-transform:lowercase;color:var(--ink);margin:0 0 8px;}
|
|
90
|
+
.prow .facts{margin-top:10px;}
|
|
91
|
+
.prow .railWrap{grid-column:2/4;margin-top:18px;}
|
|
92
|
+
/* ── v3 · the shelf: two up, the planet above the name ── */
|
|
93
|
+
.shelf{display:grid;grid-template-columns:repeat(2,1fr);gap:0 36px;}
|
|
94
|
+
.shelfCell{display:grid;gap:12px;justify-items:start;padding:26px 0;border-top:1px solid var(--rule-soft);}
|
|
95
|
+
.shelfCell .mname{font-size:19px;}
|
|
96
|
+
|
|
97
|
+
/* ── the manage page ── */
|
|
98
|
+
.backRow{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;}
|
|
99
|
+
.backRow a{color:var(--ink-faint);text-decoration:none;}
|
|
100
|
+
.backRow a:hover{color:var(--ink);}
|
|
101
|
+
.mHead{display:grid;grid-template-columns:auto 1fr;gap:0 26px;align-items:center;padding:22px 0 8px;}
|
|
102
|
+
.mHead .titleRow{display:flex;align-items:baseline;gap:16px;flex-wrap:wrap;}
|
|
103
|
+
.ledger{display:grid;grid-template-columns:172px 1fr;gap:0 22px;margin:14px 0 0;}
|
|
104
|
+
.ledger > *{border-top:1px solid var(--rule-soft);padding:13px 0;}
|
|
105
|
+
.ledger .k{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint);padding-top:16px;}
|
|
106
|
+
.ledger .v{font-size:15px;line-height:1.5;color:var(--ink);}
|
|
107
|
+
.ledger .v a,.ledger .v code{font-family:var(--font-mono);font-size:13.5px;color:var(--ink-soft);text-decoration:none;background:none;padding:0;}
|
|
108
|
+
.ledger .v a:hover{color:var(--ink);text-decoration:underline;}
|
|
109
|
+
.ledger .v small{display:block;font-size:13px;color:var(--ink-soft);}
|
|
110
|
+
.mSec{border-top:1px solid var(--rule);padding:30px 0 34px;display:grid;grid-template-columns:minmax(200px,300px) 1fr;gap:0 40px;}
|
|
111
|
+
.mSec h2{font-family:var(--font-display);font-weight:300;font-size:clamp(22px,2.4vw,28px);letter-spacing:-.03em;line-height:1.08;text-transform:lowercase;color:var(--ink);margin:0 0 8px;}
|
|
112
|
+
.mSec .why{font-size:13.5px;line-height:1.5;color:var(--ink-faint);max-width:34ch;margin:0;}
|
|
113
|
+
.mSec .body{display:grid;gap:16px;align-content:start;}
|
|
114
|
+
.mSec .body p{margin:0;font-size:15px;line-height:1.5;color:var(--ink-soft);max-width:62ch;}
|
|
115
|
+
/* a choice: hairline-separated cells, the chosen one carries the dot and the ink */
|
|
116
|
+
.choice{display:grid;grid-template-columns:repeat(3,1fr);border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);}
|
|
117
|
+
.choice label{display:grid;gap:6px;padding:16px 18px 18px;border-left:1px solid var(--rule-soft);cursor:pointer;color:var(--ink-soft);}
|
|
118
|
+
.choice label:first-child{border-left:0;padding-left:0;}
|
|
119
|
+
.choice .ch{display:flex;align-items:center;gap:10px;font-family:var(--font-body);font-weight:500;font-size:15px;color:var(--ink);}
|
|
120
|
+
.choice .ch::before{content:"";width:7px;height:7px;border-radius:50%;border:1px solid var(--rule);background:var(--bg);flex:none;}
|
|
121
|
+
.choice label.on .ch::before{background:var(--accent);border-color:var(--accent);outline:4px solid color-mix(in srgb,var(--accent) 22%,transparent);}
|
|
122
|
+
.choice .cb{font-size:13px;line-height:1.45;color:var(--ink-faint);}
|
|
123
|
+
.choice input{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);}
|
|
124
|
+
.runs{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint);}
|
|
125
|
+
.runs b{color:var(--ok);font-weight:400;}
|
|
126
|
+
/* the planet row: the settings slot the picker takes (task 1003347) */
|
|
127
|
+
.planetRow{display:grid;grid-template-columns:96px 1fr;gap:22px;align-items:center;}
|
|
128
|
+
.swatches{display:flex;gap:10px;}
|
|
129
|
+
.sw{width:26px;height:26px;border-radius:50%;border:1px solid var(--rule);background:var(--sw);}
|
|
130
|
+
.sw.on{outline:1px solid var(--ink);outline-offset:3px;}
|
|
131
|
+
.typed{display:grid;gap:10px;max-width:440px;}
|
|
132
|
+
.typed .hairField{max-width:none;}
|
|
133
|
+
|
|
134
|
+
/* ── the empty band: the Projects band's grammar, one door ── */
|
|
135
|
+
.band{background:var(--bg-card);border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);padding:58px var(--gutter) 62px;display:grid;gap:14px;justify-items:start;}
|
|
136
|
+
.band h2{font-family:var(--font-display);font-weight:300;font-size:clamp(30px,4vw,48px);letter-spacing:-.035em;line-height:1.02;text-transform:lowercase;color:var(--ink);margin:0;max-width:16ch;}
|
|
137
|
+
.band p{margin:0 0 8px;font-size:16px;line-height:1.5;color:var(--ink-soft);max-width:52ch;}
|
|
138
|
+
|
|
139
|
+
/* ── the sheet furniture ── */
|
|
140
|
+
.sheet{padding:34px var(--gutter) 60px;max-width:1180px;margin:0 auto;}
|
|
141
|
+
.sheet h2{font-family:var(--font-display);font-weight:300;font-size:clamp(26px,3.1vw,42px);letter-spacing:-.035em;line-height:1.06;color:var(--ink);margin:0 0 6px;}
|
|
142
|
+
.sheet > p{font-size:15.5px;line-height:1.5;color:var(--ink-soft);max-width:62ch;margin:0 0 26px;}
|
|
143
|
+
.rowLabel{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint);padding:18px 0 12px;border-top:1px solid var(--rule-soft);margin-top:30px;}
|
|
144
|
+
section[data-state]{display:none;}
|
|
145
|
+
body[data-state="list"] section[data-state="list"],
|
|
146
|
+
body[data-state="cards"] section[data-state="cards"],
|
|
147
|
+
body[data-state="manage"] section[data-state="manage"],
|
|
148
|
+
body[data-state="empty"] section[data-state="empty"]{display:block;}
|
|
149
|
+
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;}
|
|
150
|
+
.skip{position:absolute;left:12px;top:-60px;background:var(--bg-card);color:var(--ink);padding:11px 18px;border-radius:999px;}
|
|
151
|
+
.skip:focus{top:12px;}
|
|
152
|
+
.bar{height:var(--top-h);display:flex;align-items:center;padding:0 var(--gutter);border-bottom:1px solid var(--rule);font-family:var(--font-body);font-weight:500;font-size:17px;letter-spacing:-.015em;color:var(--ink);}
|
|
153
|
+
.mock .foot{padding:18px var(--gutter);font-size:14px;color:var(--ink-soft);text-align:center;}
|
|
154
|
+
a:focus-visible,button:focus-visible,label:focus-within{outline:2px solid var(--accent);outline-offset:3px;}
|
|
155
|
+
|
|
156
|
+
@media (max-width:900px){
|
|
157
|
+
.mrow{grid-template-columns:56px 1fr;gap:14px 16px;padding:20px 0;}
|
|
158
|
+
.mrow .worldOrb{--d:56px;}
|
|
159
|
+
.mrow .facts,.mrow .railWrap,.mrow .doors{grid-column:1/-1;}
|
|
160
|
+
.doors{justify-content:flex-start;}
|
|
161
|
+
.prow{grid-template-columns:88px 1fr;}
|
|
162
|
+
.prow .doors{grid-column:1/-1;justify-content:flex-start;margin-top:16px;}
|
|
163
|
+
.prow .railWrap{grid-column:1/-1;}
|
|
164
|
+
.shelf{grid-template-columns:1fr;}
|
|
165
|
+
.mHead{grid-template-columns:1fr;gap:14px;}
|
|
166
|
+
.ledger{grid-template-columns:1fr;gap:0;}
|
|
167
|
+
.ledger .k{border-top:1px solid var(--rule-soft);padding-bottom:0;}
|
|
168
|
+
.ledger .v{border-top:0;padding-top:6px;}
|
|
169
|
+
.mSec{grid-template-columns:1fr;gap:14px;}
|
|
170
|
+
.choice{grid-template-columns:1fr;}
|
|
171
|
+
.choice label{border-left:0;padding-left:0;border-top:1px solid var(--rule-soft);}
|
|
172
|
+
.choice label:first-child{border-top:0;}
|
|
173
|
+
.planetRow{grid-template-columns:72px 1fr;}
|
|
174
|
+
}
|
|
175
|
+
</style>
|
|
176
|
+
</head>
|
|
177
|
+
<body class="mock" data-state="list">
|
|
178
|
+
<!--
|
|
179
|
+
Your projects and the manage page, the composition round (task 1003345, goal 1000080).
|
|
180
|
+
?state=list the mine view, v1 "the ledger": planet, name and chip, the facts, the rail, the doors, per row
|
|
181
|
+
?state=cards the three row treatments side by side: v1 the ledger, v2 the poster row, v3 the shelf
|
|
182
|
+
?state=manage the manage page: the ledger of facts, the rail, the doors, hairline sections, the planet slot
|
|
183
|
+
?state=empty the mine view with no projects: a band of the Projects band's grammar
|
|
184
|
+
-->
|
|
185
|
+
<a class="skip" href="#main">Skip to content</a>
|
|
186
|
+
<header class="bar">Cloud Bongos</header>
|
|
187
|
+
<main id="main">
|
|
188
|
+
<h1 class="vh">Your projects and the manage page</h1>
|
|
189
|
+
|
|
190
|
+
<section data-state="list">
|
|
191
|
+
<div class="view">
|
|
192
|
+
<div class="vHead"><h2 class="vTitle">your projects</h2><span class="vCount">4 projects</span></div>
|
|
193
|
+
<p class="vLede">Every project you own: what it is, whether it is answering, and the controls that matter. Each row says what the platform last verified, and when.</p>
|
|
194
|
+
<div class="toolRow">
|
|
195
|
+
<label class="hairField"><svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="7"/><path d="M20 20l-3.5-3.5"/></svg><input type="text" placeholder="Search your projects by name, repo or address" aria-label="Search your projects"><span class="count">4</span></label>
|
|
196
|
+
<div class="hpills" role="group" aria-label="Filter by state">
|
|
197
|
+
<button type="button" class="hpill" aria-pressed="true">all <span class="n">4</span></button>
|
|
198
|
+
<button type="button" class="hpill" aria-pressed="false">live <span class="n">1</span></button>
|
|
199
|
+
<button type="button" class="hpill" aria-pressed="false">needs you <span class="n">1</span></button>
|
|
200
|
+
<button type="button" class="hpill" aria-pressed="false">working <span class="n">1</span></button>
|
|
201
|
+
<button type="button" class="hpill" aria-pressed="false">torn down <span class="n">1</span></button>
|
|
202
|
+
</div>
|
|
203
|
+
</div>
|
|
204
|
+
<ul class="rows">
|
|
205
|
+
<li class="mrow">
|
|
206
|
+
<span class="worldOrb" data-shape="pair" data-material="gold" data-style="wash" style="--acc:var(--terracotta);--d:64px"><span class="disc"></span></span>
|
|
207
|
+
<div class="who"><span class="mname">demo</span><span class="chip live"><span class="cdot"></span>live <span class="rec">checked 3m ago</span></span></div>
|
|
208
|
+
<div class="facts"><span class="fact">repo <a href="#">builder/demo</a></span><span class="fact">shared server</span><span class="fact">answering at <a href="#">demo.cloudbongos.com</a></span></div>
|
|
209
|
+
<div class="railWrap"><ul class="rail isLive"><li class="done">Request filed</li><li class="done">Standing it up</li><li class="done">Live</li></ul><p class="railNote">Up and answering since 1 Aug.</p></div>
|
|
210
|
+
<div class="doors"><a class="door" href="#">open site</a><a class="door" href="#">manage</a><button type="button" class="door danger">tear it down</button></div>
|
|
211
|
+
</li>
|
|
212
|
+
<li class="mrow">
|
|
213
|
+
<span class="worldOrb dim" data-shape="blob" data-material="frost" data-style="wash" style="--acc:var(--sea);--d:64px"><span class="disc"></span></span>
|
|
214
|
+
<div class="who"><span class="mname">harbor</span><span class="chip busy"><span class="cdot"></span>standing up</span></div>
|
|
215
|
+
<div class="facts"><span class="fact">repo <a href="#">builder/harbor</a></span><span class="fact">its own server</span><span class="fact soft">No address yet. It gets one once the instance is live.</span></div>
|
|
216
|
+
<div class="railWrap"><ul class="rail"><li class="done">Request filed</li><li class="now">Standing it up</li><li>Live</li></ul><p class="railNote">The platform is building it. This page updates on its own.</p></div>
|
|
217
|
+
<div class="doors"><a class="door" href="#">manage</a><button type="button" class="door danger">tear it down</button></div>
|
|
218
|
+
</li>
|
|
219
|
+
<li class="mrow">
|
|
220
|
+
<span class="worldOrb ashen" data-shape="bead" data-material="obsidian" data-style="tint" style="--acc:var(--gold-soft);--d:64px"><span class="disc"></span></span>
|
|
221
|
+
<div class="who"><span class="mname">tidepool</span><span class="chip bad"><span class="cdot"></span>snagged <span class="rec">since 28 Aug</span></span></div>
|
|
222
|
+
<div class="facts"><span class="fact">repo <a href="#">builder/tidepool</a></span><span class="fact">shared server, adopted repo</span><span class="fact err">The deploy could not read the repository: it is private, and the deploy key only reads public ones.</span></div>
|
|
223
|
+
<div class="railWrap"><ul class="rail"><li class="done">Request filed</li><li class="now">Standing it up</li><li>Live</li></ul><p class="railNote">Stopped here. Fix the cause, then try again.</p></div>
|
|
224
|
+
<div class="doors"><a class="door" href="#">manage</a><button type="button" class="door primary">try again</button><button type="button" class="door danger">tear it down</button></div>
|
|
225
|
+
</li>
|
|
226
|
+
<li class="mrow gone">
|
|
227
|
+
<span class="worldOrb ghost" data-shape="blob" data-material="chrome" data-style="tint" style="--acc:var(--sun-lit);--d:64px"><span class="disc"></span></span>
|
|
228
|
+
<div class="who"><span class="mname">old-lantern</span><span class="chip calm"><span class="cdot"></span>torn down <span class="rec">15 Aug</span></span></div>
|
|
229
|
+
<div class="facts"><span class="fact">repo <a href="#">builder/old-lantern</a></span><span class="fact">shared server</span><span class="fact soft">Its address, <code>old-lantern.cloudbongos.com</code>, stays reserved for this project.</span></div>
|
|
230
|
+
<div class="railWrap"><ul class="rail flowDown"><li class="done">Teardown queued</li><li class="done">Winding down</li><li class="now">Torn down</li></ul><p class="railNote">The repository is still yours, and its database is kept. Bringing it back takes one click.</p></div>
|
|
231
|
+
<div class="doors"><a class="door" href="#">manage</a><button type="button" class="door primary">bring it back</button></div>
|
|
232
|
+
</li>
|
|
233
|
+
</ul>
|
|
234
|
+
</div>
|
|
235
|
+
</section>
|
|
236
|
+
|
|
237
|
+
<section data-state="cards" class="sheet">
|
|
238
|
+
<h2>Three rows, one project</h2>
|
|
239
|
+
<p>The same live project in the three treatments. v1 keeps the row to the height of its planet and lines the facts up as a ledger; v2 gives the name the display voice and the rail the full width; v3 puts two projects side by side under their planets.</p>
|
|
240
|
+
<div class="rowLabel">v1, the ledger row (the list above)</div>
|
|
241
|
+
<ul class="rows">
|
|
242
|
+
<li class="mrow">
|
|
243
|
+
<span class="worldOrb" data-shape="pair" data-material="gold" data-style="wash" style="--acc:var(--terracotta);--d:64px"><span class="disc"></span></span>
|
|
244
|
+
<div class="who"><span class="mname">demo</span><span class="chip live"><span class="cdot"></span>live <span class="rec">checked 3m ago</span></span></div>
|
|
245
|
+
<div class="facts"><span class="fact">repo <a href="#">builder/demo</a></span><span class="fact">shared server</span><span class="fact">answering at <a href="#">demo.cloudbongos.com</a></span></div>
|
|
246
|
+
<div class="railWrap"><ul class="rail isLive"><li class="done">Request filed</li><li class="done">Standing it up</li><li class="done">Live</li></ul><p class="railNote">Up and answering since 1 Aug.</p></div>
|
|
247
|
+
<div class="doors"><a class="door" href="#">open site</a><a class="door" href="#">manage</a><button type="button" class="door danger">tear it down</button></div>
|
|
248
|
+
</li>
|
|
249
|
+
</ul>
|
|
250
|
+
<div class="rowLabel">v2, the poster row</div>
|
|
251
|
+
<div class="prow">
|
|
252
|
+
<span class="worldOrb" data-shape="pair" data-material="gold" data-style="wash" style="--acc:var(--terracotta);--d:112px"><span class="disc"></span></span>
|
|
253
|
+
<div>
|
|
254
|
+
<h3 class="pname">demo</h3>
|
|
255
|
+
<span class="chip live"><span class="cdot"></span>live <span class="rec">checked 3m ago</span></span>
|
|
256
|
+
<div class="facts"><span class="fact">repo <a href="#">builder/demo</a>, shared server</span><span class="fact">answering at <a href="#">demo.cloudbongos.com</a></span></div>
|
|
257
|
+
</div>
|
|
258
|
+
<div class="doors"><a class="door" href="#">open site</a><a class="door" href="#">manage</a><button type="button" class="door danger">tear it down</button></div>
|
|
259
|
+
<div class="railWrap"><ul class="rail isLive"><li class="done">Request filed</li><li class="done">Standing it up</li><li class="done">Live</li></ul><p class="railNote">Up and answering since 1 Aug.</p></div>
|
|
260
|
+
</div>
|
|
261
|
+
<div class="rowLabel">v3, the shelf</div>
|
|
262
|
+
<div class="shelf">
|
|
263
|
+
<div class="shelfCell">
|
|
264
|
+
<span class="worldOrb" data-shape="pair" data-material="gold" data-style="wash" style="--acc:var(--terracotta);--d:88px"><span class="disc"></span></span>
|
|
265
|
+
<span class="mname">demo</span>
|
|
266
|
+
<span class="chip live"><span class="cdot"></span>live <span class="rec">checked 3m ago</span></span>
|
|
267
|
+
<div class="facts"><span class="fact">repo <a href="#">builder/demo</a>, shared server</span><span class="fact">answering at <a href="#">demo.cloudbongos.com</a></span></div>
|
|
268
|
+
<div class="doors"><a class="door" href="#">open site</a><a class="door" href="#">manage</a></div>
|
|
269
|
+
</div>
|
|
270
|
+
<div class="shelfCell">
|
|
271
|
+
<span class="worldOrb dim" data-shape="blob" data-material="frost" data-style="wash" style="--acc:var(--sea);--d:88px"><span class="disc"></span></span>
|
|
272
|
+
<span class="mname">harbor</span>
|
|
273
|
+
<span class="chip busy"><span class="cdot"></span>standing up</span>
|
|
274
|
+
<div class="facts"><span class="fact">repo <a href="#">builder/harbor</a>, its own server</span><span class="fact soft">No address yet.</span></div>
|
|
275
|
+
<div class="doors"><a class="door" href="#">manage</a></div>
|
|
276
|
+
</div>
|
|
277
|
+
</div>
|
|
278
|
+
</section>
|
|
279
|
+
|
|
280
|
+
<section data-state="manage">
|
|
281
|
+
<div class="view">
|
|
282
|
+
<p class="backRow"><a href="#">your projects</a></p>
|
|
283
|
+
<div class="mHead">
|
|
284
|
+
<span class="worldOrb" data-shape="pair" data-material="gold" data-style="wash" style="--acc:var(--terracotta);--d:96px"><span class="disc"></span></span>
|
|
285
|
+
<div>
|
|
286
|
+
<div class="titleRow"><h2 class="vTitle">demo</h2><span class="chip live"><span class="cdot"></span>live <span class="rec">checked 3m ago</span></span></div>
|
|
287
|
+
<p class="vLede" style="margin:8px 0 0">Its address, its repository, what the platform last verified about it, and the controls you have today.</p>
|
|
288
|
+
</div>
|
|
289
|
+
</div>
|
|
290
|
+
<div class="ledger">
|
|
291
|
+
<span class="k">repository</span><span class="v"><a href="#">builder/demo</a></span>
|
|
292
|
+
<span class="k">server</span><span class="v">shared server</span>
|
|
293
|
+
<span class="k">address</span><span class="v">Answering at <a href="#">demo.cloudbongos.com</a><small>last answered 3 minutes ago</small></span>
|
|
294
|
+
<span class="k">created</span><span class="v">1 Aug 2026</span>
|
|
295
|
+
<span class="k">planet</span><span class="v">the bongo pair in brushed gold, washed terracotta<small>chosen by the walk. Pick your own below.</small></span>
|
|
296
|
+
</div>
|
|
297
|
+
<div class="railWrap" style="margin:22px 0 0;max-width:520px"><ul class="rail isLive"><li class="done">Request filed</li><li class="done">Standing it up</li><li class="done">Live</li></ul><p class="railNote">Up and answering since 1 Aug.</p></div>
|
|
298
|
+
<div class="doors" style="justify-content:flex-start;margin:22px 0 34px"><a class="door" href="#">open site</a><button type="button" class="door">restart it</button></div>
|
|
299
|
+
|
|
300
|
+
<div class="mSec">
|
|
301
|
+
<div><h2>repository visibility</h2><p class="why">Read from GitHub with your own credential. Public today; the private direction waits for the private-repo deploy fix.</p></div>
|
|
302
|
+
<div class="body">
|
|
303
|
+
<div class="choice">
|
|
304
|
+
<label class="on"><span class="ch">Public</span><span class="cb">Anyone can read the code on GitHub.</span></label>
|
|
305
|
+
<label><span class="ch">Private</span><span class="cb">Not yet: the deploy key only reads public repositories.</span></label>
|
|
306
|
+
</div>
|
|
307
|
+
</div>
|
|
308
|
+
</div>
|
|
309
|
+
<div class="mSec">
|
|
310
|
+
<div><h2>who can see it</h2><p class="why">The platform's request. What the project runs is read back from its own manifest.</p></div>
|
|
311
|
+
<div class="body">
|
|
312
|
+
<div class="choice">
|
|
313
|
+
<label class="on"><span class="ch">Everyone</span><span class="cb">Anyone can open this project's pages. Signing in is only needed to take part.</span></label>
|
|
314
|
+
<label><span class="ch">Only signed-in members</span><span class="cb">Visitors meet a sign-in door first. The project's pages open only to people signed in to it.</span></label>
|
|
315
|
+
</div>
|
|
316
|
+
<p class="runs">runs <b>everyone</b>, read from the project 2m ago</p>
|
|
317
|
+
</div>
|
|
318
|
+
</div>
|
|
319
|
+
<div class="mSec">
|
|
320
|
+
<div><h2>how people join</h2><p class="why">The membership door. A change reaches the live project by the restart the runner performs.</p></div>
|
|
321
|
+
<div class="body">
|
|
322
|
+
<div class="choice">
|
|
323
|
+
<label class="on"><span class="ch">Anyone can join</span><span class="cb">Whoever signs in with GitHub becomes a member right away, at the newcomer rank.</span></label>
|
|
324
|
+
<label><span class="ch">Apply to join</span><span class="cb">People ask to join from the project's hall; you approve each one, then they sign in.</span></label>
|
|
325
|
+
<label><span class="ch">Invite only</span><span class="cb">No one can apply. You invite people by GitHub username from the hall.</span></label>
|
|
326
|
+
</div>
|
|
327
|
+
<p class="runs">runs <b>anyone can join</b>, read from the project 2m ago</p>
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
330
|
+
<div class="mSec">
|
|
331
|
+
<div><h2>its planet</h2><p class="why">How this project shows on the sky, in your list and on its card. The choice lands here next (task 1003347).</p></div>
|
|
332
|
+
<div class="body">
|
|
333
|
+
<div class="planetRow">
|
|
334
|
+
<span class="worldOrb" data-shape="pair" data-material="gold" data-style="wash" style="--acc:var(--terracotta);--d:88px"><span class="disc"></span></span>
|
|
335
|
+
<div class="typed">
|
|
336
|
+
<span class="fact">material: brushed gold, washed</span>
|
|
337
|
+
<div class="swatches" aria-label="Colour"><span class="sw" style="--sw:var(--sea)"></span><span class="sw on" style="--sw:var(--terracotta)"></span><span class="sw" style="--sw:var(--gold-soft)"></span><span class="sw" style="--sw:var(--sun-lit)"></span><span class="sw" style="--sw:var(--accent)"></span></div>
|
|
338
|
+
<p style="margin:0;font-size:13.5px;color:var(--ink-faint)">Material, style and colour, and the card's backdrop, become your own choice here.</p>
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
341
|
+
</div>
|
|
342
|
+
</div>
|
|
343
|
+
<div class="mSec">
|
|
344
|
+
<div><h2>take it offline</h2><p class="why">Stops the server and its nightly backups. It stays in your list, marked torn down, and the repository is untouched.</p></div>
|
|
345
|
+
<div class="body">
|
|
346
|
+
<div class="typed">
|
|
347
|
+
<label class="hairField"><input type="text" placeholder="Type the project's name to confirm" aria-label="Type the project's name to confirm"></label>
|
|
348
|
+
<div class="doors" style="justify-content:flex-start"><button type="button" class="door danger">tear it down</button></div>
|
|
349
|
+
</div>
|
|
350
|
+
</div>
|
|
351
|
+
</div>
|
|
352
|
+
</div>
|
|
353
|
+
</section>
|
|
354
|
+
|
|
355
|
+
<section data-state="empty">
|
|
356
|
+
<div class="view">
|
|
357
|
+
<div class="vHead"><h2 class="vTitle">your projects</h2></div>
|
|
358
|
+
</div>
|
|
359
|
+
<div class="band">
|
|
360
|
+
<h2>nothing of yours in this sky yet</h2>
|
|
361
|
+
<p>Name a project, pick a home for its code, and it gets an address of its own here. It shows up in this list the moment you file it.</p>
|
|
362
|
+
<a class="door primary" href="#">create your first project</a>
|
|
363
|
+
</div>
|
|
364
|
+
</section>
|
|
365
|
+
</main>
|
|
366
|
+
<footer class="foot">A composition sheet for task 1003345. Nothing here is served.</footer>
|
|
367
|
+
<script>
|
|
368
|
+
/* The mock's state contract: read ?state= once, apply one state (list | cards | manage | empty).
|
|
369
|
+
No motion in script; nothing is built here. */
|
|
370
|
+
(function () {
|
|
371
|
+
var state = new URLSearchParams(location.search || location.hash.slice(1) || (window.__Q || '')).get('state') || 'list';
|
|
372
|
+
if (['list', 'cards', 'manage', 'empty'].indexOf(state) === -1) state = 'list';
|
|
373
|
+
document.body.setAttribute('data-state', state);
|
|
374
|
+
})();
|
|
375
|
+
</script>
|
|
376
|
+
</body>
|
|
377
|
+
</html>
|
|
@@ -183,4 +183,28 @@ Three mastheads, rendered dark and light at 1440 / 390 / 320 (ALL CLEAN) and mea
|
|
|
183
183
|
|
|
184
184
|
**What was built.** The template grammar grows from shape × style to shape × **material** × style: `<shape>-<style>` stays the chrome material's name (nine rows keep their meaning), `<shape>-<material>-<style>` names a new material with style `tint` / `wash` / `plain` — thirty-six names in migration `<redacted>`, `PROJECT_TEMPLATES` (every valid name, the `POST /projects` enum) and `PROJECT_TEMPLATE_WALK` (the hub's twelve-step default, each step changing shape, material and style — `ORB_WALK` in `projects.html`, `ORB_TEMPLATES` the full set). `cosmos.css` carries a plate token per material × shape, a two-attribute crop rule per plate measured from the cutout's alpha box (pinned by `tests/projects_hub.mjs`, which recomputes every rule from the boxes), and `[data-style="plain"]`. The orb carries `data-material` beside `data-shape` and `data-style`. The field's ground is `--field-plate` at `cover` under the page ground, drifting 2–3% over 90 s by transform (dead under reduced motion) — one token, so the pick is one line.
|
|
185
185
|
|
|
186
|
-
**The pick,
|
|
186
|
+
**The pick, closed (owner, 2026-08-29).** "backdrop and materials are good": the nebula stays. In the same note the owner asked for a project SETTING where the owner chooses the planet's material and colour, and where the project card's backdrop can be customised too — filed as task 1003347, behind this round's manage-page makeover (task 1003345) where the setting lives. Before that note: the owner was not in the session, so the backdrop shipped on the builder's recommendation — the nebula wash (its violet and bronze are the chrome's own colours; the dust plate's glow competes with the orbs; the starfield is plain; the deepfield's corner is heavier than asked) — with the other three beside it in `.impeccable/mocks/world-assets/` and on the sheet. Swapping is one token (`--field-plate`) plus a copy into `assets/world/`. The materials are not a pick: they ship as templates, and a curated `template` chooses one per project.
|
|
187
|
+
|
|
188
|
+
## 14. Your projects and the manage page: the composition round (task 1003345)
|
|
189
|
+
|
|
190
|
+
**Date:** 2026-08-29 · the owner's ask after the planets review: "the your projects page also needs a makeover as well as the create a project page". This is the YOUR PROJECTS half; the wizard is task 1003346.
|
|
191
|
+
|
|
192
|
+
**The round.** One sheet, `docs/design/mocks/hub/mine.html` (`?state=list|cards|manage|empty`; check-mock ALL RULES PASS; shot at 1440 and 390 through the kit's stub, ALL CLEAN): the mine view as **v1 the ledger row** (one row per project at the height of its planet: the planet as the project's own template object, the name and its verified chip, the facts as label-face lines, the progress strip as the lifecycle rail, the doors as hairline pills with the accent only on the one that acts; hairline search field; filter pills, the pressed one inverted to ink); the alternatives **v2 the poster row** (the name in the display voice, the rail full width) and **v3 the shelf** (two up under the planets); the manage page (the name in the display voice with its chip, a ledger of facts incl. the planet, the rail, the doors, the settings as hairline sections with the choice as hairline cells, an "its planet" section as the slot task 1003347 fills, the ender in the error ink with the name typed back); and the empty state as a band of the Projects band's grammar. The review page: https://claude.ai/code/artifact/9f21b382-fb16-4c10-a90b-8d2b38ad9288 (the owner picks the row treatment and the chip's time placement).
|
|
193
|
+
|
|
194
|
+
**What the kit gained for it (built this round).** An instances fixture (`modules/ui-design/kit/fixtures/provisioning-instances.json`: live, standing up, snagged, torn down; `$now-3m` stamps resolved per request) and the stub's per-id, settings and repo-visibility answers, so `in-mine`, `in-manage`, `in-manage-snag`, `in-manage-gone`, `in-manage-missing` and `out-manage` render through `projects.states.json`; a states file may alias a fixture (`stub.fixtures`), which is how `projects-empty.states.json` renders the empty state. The BEFORE render of the old views through those fixtures found 22 floor problems the build must clear: links in the facts, the address and the back row under 24px tall, the 17px search input, no visible `h1` on the manage-missing state.
|
|
195
|
+
|
|
196
|
+
**Not built yet.** The page itself waits for the pick (the round's method). The build: `cosmos.css` rules scoped to `#view-mine` / `#view-manage` (no `.card` ground, the row grid, the rail in place of `.strip`, the pills), `orbFor()` emitting the planet (`templateOf` for a catalog-matched instance, `slugTemplate` otherwise), `#mTitle` moved out of `#mMain` so every manage state has its `h1`, the chip's time under the name if the owner says so, the states' expectations widened, `tests/projects_hub.mjs` + `projects_hub_pre_uat.mjs` retargeted, the copy registry regenerated.
|
|
197
|
+
|
|
198
|
+
### 14.1 The build: v1 the ledger row (task 1003345)
|
|
199
|
+
|
|
200
|
+
**Date:** 2026-08-29 · the pick page carried no comment, and the owner's brief for the build session named the fallback in so many words ("default v1 the ledger row if unpicked"), so the build took it — **v1 the ledger row**, the chip's time kept inside the chip. The pick page stays up; v2 or v3 is a CSS round away.
|
|
201
|
+
|
|
202
|
+
**What was built.** Both views keep the markup the truth rules pin (`renderCard` / `orbFor` / `stripMarkup` shapes, the manage ids) and take the world's grammar by scope in `cosmos.css` (`#view-mine` / `#view-manage`): no `.card` ground; the view title in the display voice, lowercase; the search as one hairline field, the filters as hairline pills with the pressed one inverted to ink; one row per project on **fixed tracks** (`72px` the planet, `minmax(0,1.1fr) minmax(0,1.3fr) minmax(0,1fr) 232px` the name, the facts, the rail and the doors, no content minimum, so every row lays its columns on the same lines, task 1003348); `orbFor()` emits the project's planet, a `.worldOrb` with the template attributes, ghosted / ashen / dim by state, a link only when live (`planetOf`: the catalog's template and accent for a host-matched instance, the slug walk otherwise, and a catalog that lands after the view rendered repaints the rows and the manage head); the strip is drawn as the lifecycle rail (three hairlines, a 7px dot, one lit step, an outline halo, no plate, no connector, no pulse; live green, a wind-down neutral); the doors are hairline pills with the accent only on the one that acts and the ender on the error ink; the empty state is a band of the Projects band's grammar with its own title. The manage page: the page's one `h1` stands outside `#mMain` in a head with the planet (`--orb-scale:1.5`) and the chip, so the signed-out, not-found and loading states keep a visible heading (`#mName` inside it is what `renderManage` fills; the clause hides with `#mMain` by `:has`); the facts are a ledger of label · value rows on hairlines (repository, server, address, created, **planet** — the slot task 1003347 fills, "chosen for it by the platform" until a curated template says otherwise); the settings are hairline sections (`.mSec`: the title and its fold left, the body right) with the choice as hairline cells carrying the dot; the ender's typed-back name is a hairline field.
|
|
203
|
+
|
|
204
|
+
**Kept by the page's own truth rules, not the sheet.** A long-live project shows no rail (`stripFor` lights the landed moment only, task 1003243); a snagged or torn-down row carries its paragraph in place of a rail. The sheet drew rails on every row; the page does not invent a lifecycle it is not in.
|
|
205
|
+
|
|
206
|
+
**The kit.** The BEFORE render of the old views through the instances fixture found 33 floor problems (the 22 the round recorded plus the states' own gaps); AFTER: `render.js --page /projects` ALL CLEAN, `--states public-landing/projects-empty.states.json` ALL CLEAN, and a new sibling `projects-floor.states.json` ALL CLEAN. That sibling exists because the signed-in stub **owns** Demo (the instances fixture): an owned orb's plain click opens its manage view, never its card, so the all-null floor card renders on the hub's own catalog (`featured-hub.json` = the landing's three plus Tidewater, a null row nobody owns), aliased through `stub.fixtures` — the kit's rule that only a `<page>-<variant>.states.json` may alias a fixture. The sky's count in `in-map` is six (the landing's three, the platform's own, the two owned-only). The table's owned-row name link takes the 24px floor with the other carried surfaces.
|
|
207
|
+
|
|
208
|
+
**The flows, guarded.** The DONE WHEN keeps every manage flow green; there were no such probes, so this round wrote them: attach (the row's fold), restart (a fifth fixture row, Mooring, live but unreachable), teardown (the row's two presses and the manage page's typed-back ender), revive, retry and join drive the stub's receipts end to end (`serve.js` now answers the asks; `projects.probes.json`).
|
|
209
|
+
|
|
210
|
+
**Retargeted.** `tests/projects_hub.mjs` (the planet, the fixed tracks, the h1 outside `#mMain`, the `.mSec` cards; the sphere's glow gone) and `tests/projects_hub_pre_uat.mjs` (the manage ledger, `paintManagePlanet`, the docked planet's cursor); the copy registry regenerated.
|
package/docs/file-map.md
CHANGED
|
@@ -350,6 +350,7 @@ tests/
|
|
|
350
350
|
├── session_start_freshness.mjs ← (#1277, ADR 0067): the SessionStart freshness decision (decideFreshness) — pull ONLY on a clean `main` full clone strictly behind origin/main; warn in every other behind case; skip when up to date.
|
|
351
351
|
├── own_scope_guard.mjs ← (task 1003343, goal 1000045 criterion own-scope-held-by-a-test): the OWN-SCOPE structural guard, generalised out of memory_isolation.mjs. An explicit inventory of own-scoped route files; inside them the route's declared PATH decides — a path naming no builder must never read a subject from the request (direct, optional-chained, destructured, aliased, computed-key or header), a path that DOES name one needs a per-route exemption whose gate the guard verifies is present. Repo-wide backstop: any file mounting a `/me` route must be in the inventory. Source-grepping, so it runs in the DB-free `unit` lane.
|
|
352
352
|
├── account_existence_rate_limit.mjs ← (task 1003339, goal 1000045, ADR 0209): the two PUBLIC account-existence probes (onboarding GET /access-requests/status + core GET /auth/web/admission-status) share ONE per-IP budget. Counts to the limit on each route, proves the budget is shared in both directions and when alternated, moves Date.now to prove the window slides on the real mounted route, and proves a refused probe issues no query. DB-free; mutation-checked three ways.
|
|
353
|
+
├── effective_visibility_predicate.mjs ← (task 1002967, goal 1000045): the EFFECTIVE-VISIBILITY structural guard over `modules/platform-identity/account-visibility.js` (the file task 1002295 landed and this task ADOPTED rather than duplicating). Three layers — SQL and JS agree row-for-row over the privacy-spec D2 matrix; that file is the only one allowed to compare `profile_state`/`account_visibility`/`hide_stats` to a literal (per-occurrence exemptions, each with a reason and a checked obligation); and every `SELECT` on `platform_identity_accounts` is enumerated FROM SOURCE and must join a shared predicate, be declared non-public, or be a checked deferral to a named caller — so a read that does not exist yet (hub search, the sitemap) fails the build until its author classifies it. Behavioural half runs the real `/profiles/:handle` route over an in-memory accounts table filtered by each statement's own WHERE clause: private → 404 (never 403), byte-identical to stranger and owner alike. DB-free `unit` lane.
|
|
353
354
|
└── public_task_resolver.mjs ← (#604): the public `/public/tasks/:id` projection — title/value_summary exposed only for shipped|confirmed; withheld for in-flight so unshipped/security task names can't leak.
|
|
354
355
|
```
|
|
355
356
|
|
|
@@ -1277,5 +1277,7 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
1277
1277
|
landed since 1.19.418 with no explicit bump. run 33265382684. (task 1002620)
|
|
1278
1278
|
1.19.420 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1279
1279
|
landed since 1.19.419 with no explicit bump. run 33266034679. (task 1002620)
|
|
1280
|
+
1.19.421 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1281
|
+
landed since 1.19.420 with no explicit bump. run 33266605270. (task 1002620)
|
|
1280
1282
|
---------------------------------------------------------------------------
|
|
1281
1283
|
```
|
|
@@ -107,8 +107,9 @@
|
|
|
107
107
|
</p>
|
|
108
108
|
<div id="privacy-rows"></div>
|
|
109
109
|
<p class="settings-status" id="privacy-status" role="status" aria-live="polite"></p>
|
|
110
|
-
<p class="section-note">Your
|
|
111
|
-
|
|
110
|
+
<p class="section-note">Your profile page setting applies now — private already answers "not found"
|
|
111
|
+
everywhere your profile is read. Recruiter discoverability is saved to your
|
|
112
|
+
account and starts applying as the recruiting surfaces land.</p>
|
|
112
113
|
</section>
|
|
113
114
|
|
|
114
115
|
<!-- CRAFT — discipline preferences (GET /me, PATCH /me/disciplines). -->
|
|
@@ -596,11 +596,15 @@
|
|
|
596
596
|
// toggle is deliberately BINARY (spec D1: "one column, one settings toggle");
|
|
597
597
|
// resetting back to null stays an API affordance, not a third UI state.
|
|
598
598
|
//
|
|
599
|
-
// COPY RULE
|
|
600
|
-
//
|
|
601
|
-
//
|
|
602
|
-
//
|
|
603
|
-
//
|
|
599
|
+
// COPY RULE — a toggle may promise exactly what is enforced, and the two rows
|
|
600
|
+
// are at different stages, so they speak in different tenses (task 1002967).
|
|
601
|
+
// VISIBILITY is live: every public read joins the shared effective-visibility
|
|
602
|
+
// predicate, so private really does 404 — including to the owner, which the
|
|
603
|
+
// row must say out loud, because a page that vanishes for you too is
|
|
604
|
+
// surprising unless you are told where your own record went (here, Settings).
|
|
605
|
+
// DISCOVERABILITY is still stored-only: no recruiting surface reads it yet, so
|
|
606
|
+
// its row keeps the future tense until that lands. A toggle that claims
|
|
607
|
+
// protection it does not have is worse than no toggle.
|
|
604
608
|
function renderPrivacy(data) {
|
|
605
609
|
const wrap = $('#privacy-rows');
|
|
606
610
|
if (!wrap) return;
|
|
@@ -612,7 +616,7 @@
|
|
|
612
616
|
: 'They will see your name, your disciplines, and whether you have been active in the last 30 days — never your credits, works shipped, karma, or the projects you build on.';
|
|
613
617
|
wrap.innerHTML =
|
|
614
618
|
privacyRow('visibility', 'Public profile page', isPublic ? 'Public' : 'Private', isPublic,
|
|
615
|
-
'Public means anyone can open your profile page and find you by name. Private
|
|
619
|
+
'Public means anyone can open your profile page and find you by name. Private hides the page from everyone — including you: it answers "not found" to every visitor, and you read your own record here in Settings instead.', '') +
|
|
616
620
|
privacyRow('discoverable', 'Discoverable by recruiters', discoverable ? 'On' : 'Off', discoverable,
|
|
617
621
|
`Recruiters looking for builders will be able to find you. ${grant}`,
|
|
618
622
|
follows ? ' <span class="voices__default">· follows your profile setting</span>' : '');
|
|
@@ -14,10 +14,37 @@
|
|
|
14
14
|
// already at the file-size ratchet. So the definition lands here, alone and
|
|
15
15
|
// tiny, and 1002967 ADOPTS it rather than writing a second one.
|
|
16
16
|
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
17
|
+
// THREE AXES, THREE ANSWERS, ONE HOME (task 1002967 adopting this file). The
|
|
18
|
+
// axes stay independent — conflating them is the trap the privacy spec D1/D2
|
|
19
|
+
// exists to prevent — but every EFFECTIVE answer derived from them lives here,
|
|
20
|
+
// because an answer that lives anywhere else is a second variant waiting to
|
|
21
|
+
// drift:
|
|
22
|
+
//
|
|
23
|
+
// accountVisibleSql / isAccountVisible EXISTENCE — may the world see that
|
|
24
|
+
// this account exists? Every PUBLIC read floors on this.
|
|
25
|
+
// accountActiveSql / isAccountActive CONSENT — active, without the
|
|
26
|
+
// existence axis. The OWN-SCOPE floor (ADR 0202 D4 §2): a private account
|
|
27
|
+
// is still a real participant in its own scope.
|
|
28
|
+
// rollupVisibleSql / isRollupVisible NUMBERS — visible AND NOT
|
|
29
|
+
// hide_stats. ADR 0171 D2's predicate EXTENDED with the existence axis,
|
|
30
|
+
// and COMPOSED from accountVisibleSql rather than restated.
|
|
31
|
+
// isRecruiterDiscoverable REACH — the COALESCE, privacy
|
|
32
|
+
// spec D1. A reach predicate only: it never buys addressability.
|
|
33
|
+
//
|
|
34
|
+
// The rollup pair was originally left to "the rollup readers" to apply. Task
|
|
35
|
+
// 1002967 moved it here for the reason this file exists: there are two such
|
|
36
|
+
// readers (listScoutingRollup and getPublicProfileExtras), so leaving them to
|
|
37
|
+
// spell `AND NOT hide_stats` themselves is exactly the second-variant problem,
|
|
38
|
+
// one level down. hide_stats still suppresses NUMBERS and never existence —
|
|
39
|
+
// that is why it appears in rollupVisibleSql and in nothing above it.
|
|
40
|
+
//
|
|
19
41
|
// Not authority: an account being visible confers nothing (ADR 0016).
|
|
20
42
|
//
|
|
43
|
+
// tests/effective_visibility_predicate.mjs enforces that this file is the ONLY
|
|
44
|
+
// first-party server file allowed to compare these columns to a literal, and
|
|
45
|
+
// that every SELECT on platform_identity_accounts either joins one of these
|
|
46
|
+
// predicates or is declared non-public with a reason.
|
|
47
|
+
//
|
|
21
48
|
// Fail-closed: a missing/absent account row is invisible, never visible
|
|
22
49
|
// (ADR 0192 §6 / ADR 0194 §2).
|
|
23
50
|
'use strict';
|
|
@@ -55,4 +82,68 @@ function accountActiveSql(alias) {
|
|
|
55
82
|
return `(${alias ? `${alias}.` : ''}profile_state = 'active')`;
|
|
56
83
|
}
|
|
57
84
|
|
|
58
|
-
|
|
85
|
+
// The JS twin of accountActiveSql, for an own-scope reader that already holds
|
|
86
|
+
// the row (routes/connections.js). Fail-closed, same as isAccountVisible.
|
|
87
|
+
function isAccountActive(account) {
|
|
88
|
+
return !!account && account.profile_state === 'active';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// -------------------------------------------------------------------------
|
|
92
|
+
// The rollup axis: visible AND not hidden (task 1002967 — ADR 0171 D2 extended)
|
|
93
|
+
// -------------------------------------------------------------------------
|
|
94
|
+
|
|
95
|
+
// The predicate the two ROLLUP readers join — listScoutingRollup (/people,
|
|
96
|
+
// /scouting) and getPublicProfileExtras (the platform-identity.hub port behind
|
|
97
|
+
// core GET /builders/:id/profile). COMPOSED from accountVisibleSql, never
|
|
98
|
+
// restated, so changing what "visible" means moves this with it.
|
|
99
|
+
function rollupVisibleSql(alias) {
|
|
100
|
+
if (alias && !/^[A-Za-z_][A-Za-z0-9_]*$/.test(String(alias))) {
|
|
101
|
+
throw new Error('rollupVisibleSql: alias must be a bare SQL identifier');
|
|
102
|
+
}
|
|
103
|
+
return `(${accountVisibleSql(alias)} AND NOT ${alias ? `${alias}.` : ''}hide_stats)`;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// The JS twin of rollupVisibleSql, for a reader that already holds the row.
|
|
107
|
+
function isRollupVisible(account) {
|
|
108
|
+
return isAccountVisible(account) && !account.hide_stats;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// -------------------------------------------------------------------------
|
|
112
|
+
// The reach axis: recruiting discoverability (privacy spec D1)
|
|
113
|
+
// -------------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
// The two stored values of account_visibility. Mirrors the CHECK in
|
|
116
|
+
// migrations/platform_identity_012_account_privacy.sql.
|
|
117
|
+
const ACCOUNT_VISIBILITY = ['public', 'private'];
|
|
118
|
+
|
|
119
|
+
// isRecruiterDiscoverable(account) — THE effective-recruiter-reach predicate.
|
|
120
|
+
// The JS twin of COALESCE(recruiter_discoverable, account_visibility='public');
|
|
121
|
+
// nothing else may spell that COALESCE out. Moved here from platform-identity.js
|
|
122
|
+
// by task 1002967 so all three effective answers share one home.
|
|
123
|
+
//
|
|
124
|
+
// The tri-state is the point: NULL = follow visibility (opt-OUT for a public
|
|
125
|
+
// account, opt-IN for a private one), an explicit boolean overrides — an
|
|
126
|
+
// explicit true on a PRIVATE account is exactly the privacy spec's D2
|
|
127
|
+
// "private, discoverable" row, which the D3 recruiter sliver exists to serve.
|
|
128
|
+
//
|
|
129
|
+
// NOT a complete gate, and NEVER addressability: profile_state='active' still
|
|
130
|
+
// floors every recruiter surface, hide_stats still empties the sliver to
|
|
131
|
+
// name-only, and a discoverable private account is still absent from every
|
|
132
|
+
// public read. Fail-closed on a missing account.
|
|
133
|
+
function isRecruiterDiscoverable(account) {
|
|
134
|
+
if (!account) return false;
|
|
135
|
+
const explicit = account.recruiter_discoverable;
|
|
136
|
+
if (typeof explicit === 'boolean') return explicit;
|
|
137
|
+
return account.account_visibility === 'public';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
module.exports = {
|
|
141
|
+
isAccountVisible,
|
|
142
|
+
accountVisibleSql,
|
|
143
|
+
accountActiveSql,
|
|
144
|
+
isAccountActive,
|
|
145
|
+
rollupVisibleSql,
|
|
146
|
+
isRollupVisible,
|
|
147
|
+
ACCOUNT_VISIBILITY,
|
|
148
|
+
isRecruiterDiscoverable,
|
|
149
|
+
};
|