@andresmassello/uscha 1.40.2 → 1.43.0
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/README.md +6 -6
- package/bin/uscha.js +19 -7
- package/package.json +1 -1
- package/uscha-kit/.claude/skills/uscha-adr-refine/SKILL.md +2 -2
- package/uscha-kit/.claude/skills/uscha-devloop/SKILL.md +3 -1
- package/uscha-kit/.claude/skills/uscha-devloop/qa_ledger.py +729 -155
- package/uscha-kit/.claude/skills/uscha-mirador/SKILL.md +22 -7
- package/uscha-kit/.claude/skills/uscha-mirador/mirador-render.py +44 -5
- package/uscha-kit/.claude/skills/uscha-mirador/mirador-watch.ps1 +1 -1
- package/uscha-kit/.claude/skills/uscha-mirador/mirador-watch.sh +1 -1
- package/uscha-kit/.claude/skills/uscha-mirador/mirador.template.html +666 -586
- package/uscha-kit/.claude-plugin/plugin.json +1 -1
- package/uscha-kit/.codex-plugin/plugin.json +1 -1
- package/uscha-kit/CHANGELOG-1.41.0.md +18 -0
- package/uscha-kit/CHANGELOG-1.41.1.md +53 -0
- package/uscha-kit/CHANGELOG-1.41.2.md +34 -0
- package/uscha-kit/CHANGELOG-1.41.3.md +30 -0
- package/uscha-kit/CHANGELOG-1.42.0.md +41 -0
- package/uscha-kit/CHANGELOG-1.43.0.md +37 -0
- package/uscha-kit/INSTALL.md +120 -101
- package/uscha-kit/README.md +24 -13
- package/uscha-kit/VERSION +1 -1
- package/uscha-kit/WORKBENCH.md +19 -5
- package/uscha-kit/hooks/block-approved-writes.py +25 -0
- package/uscha-kit/install-uscha.py +534 -267
- package/uscha-kit/skills/uscha-adr-refine/SKILL.md +2 -2
- package/uscha-kit/skills/uscha-devloop/SKILL.md +3 -1
- package/uscha-kit/skills/uscha-devloop/qa_ledger.py +729 -155
- package/uscha-kit/skills/uscha-mirador/SKILL.md +22 -7
- package/uscha-kit/skills/uscha-mirador/mirador-render.py +44 -5
- package/uscha-kit/skills/uscha-mirador/mirador-watch.ps1 +1 -1
- package/uscha-kit/skills/uscha-mirador/mirador-watch.sh +1 -1
- package/uscha-kit/skills/uscha-mirador/mirador.template.html +666 -586
- package/uscha-kit/templates/CONSTITUTION.md +4 -4
- package/uscha-kit/templates/docs/adr/README.md +19 -19
- package/uscha-kit/tests/ledger-integrity-regressions.py +136 -0
- package/uscha-kit/tests/smoke-engine.sh +1312 -29
- package/uscha-kit/uscha.config.json +1 -1
- package/uscha-kit/workbench-doctor.sh +47 -3
|
@@ -1,586 +1,666 @@
|
|
|
1
|
-
<!doctype html>
|
|
2
|
-
<html lang="es">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="utf-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
-
<title>Mirador — estado del proyecto · Uscha</title>
|
|
7
|
-
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
-
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
-
<link href="https://fonts.googleapis.com/css2?family=Inter+Tight:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
10
|
-
<style>
|
|
11
|
-
:root{
|
|
12
|
-
--bg:#f3f5f7; --paper:#ffffff; --ink:#14171c; --ink-soft:#3b424b; --ink-mute:#8b929b;
|
|
13
|
-
--red:#e30613; --red-deep:#b00410; --red-soft:#fff1f2;
|
|
14
|
-
--green:#1f9d57; --green-soft:#e9f7ef; --amber:#e0930c; --amber-soft:#fff5e1;
|
|
15
|
-
--terrain:#d9d2c4; --terrain-line:#c7bda9; --rule:#e6e9ee; --stone:#8b8578;
|
|
16
|
-
--sans:"Inter Tight",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
|
17
|
-
--mono:"JetBrains Mono",monospace;
|
|
18
|
-
}
|
|
19
|
-
*{box-sizing:border-box;margin:0;padding:0}
|
|
20
|
-
html{scroll-behavior:smooth}
|
|
21
|
-
body{background:var(--bg);color:var(--ink);font-family:var(--sans);line-height:1.5;-webkit-font-smoothing:antialiased}
|
|
22
|
-
body::before{content:"";position:fixed;inset:0;background-image:radial-gradient(circle at 1px 1px,rgba(0,0,0,.022) 1px,transparent 0);background-size:5px 5px;pointer-events:none;z-index:0}
|
|
23
|
-
.wrap{max-width:1140px;margin:0 auto;padding:0 clamp(1rem,4vw,2rem);position:relative;z-index:1}
|
|
24
|
-
|
|
25
|
-
/* topbar */
|
|
26
|
-
.topbar{background:var(--ink);color:#fff;height:46px;display:flex;align-items:center}
|
|
27
|
-
.topbar .wrap{display:flex;align-items:center;justify-content:space-between;width:100%}
|
|
28
|
-
.brand{display:flex;align-items:center;gap:.55rem;font-weight:800;letter-spacing:-.02em}
|
|
29
|
-
.brand .dot{width:11px;height:11px;border-radius:50%;background:var(--red);box-shadow:0 0 0 3px rgba(227,6,19,.25)}
|
|
30
|
-
.brand .cmd{font-family:var(--mono);font-weight:500;font-size:.78rem;color:#aeb4bd;background:#22262e;padding:2px 8px;border-radius:5px;margin-left:.4rem}
|
|
31
|
-
.topbar .meta{font-family:var(--mono);font-size:.72rem;color:#8b929b;letter-spacing:.04em}
|
|
32
|
-
.topbar .meta b{color:#cbd2dc;font-weight:500}
|
|
33
|
-
|
|
34
|
-
/* hero verdict + trail */
|
|
35
|
-
.hero{padding:clamp(1.4rem,4vw,2.4rem) 0 .6rem}
|
|
36
|
-
.verdict{display:flex;align-items:baseline;gap:1rem;flex-wrap:wrap;margin-bottom:.2rem}
|
|
37
|
-
.verdict .band{font-family:var(--mono);font-weight:700;font-size:.78rem;letter-spacing:.08em;padding:5px 12px;border-radius:7px}
|
|
38
|
-
.verdict h1{font-weight:800;font-size:clamp(1.5rem,3.4vw,2.3rem);letter-spacing:-.03em;line-height:1.08}
|
|
39
|
-
.verdict h1 .score{color:var(--ink-mute);font-weight:600;font-size:.62em}
|
|
40
|
-
.verdict .sub{width:100%;color:var(--ink-soft);font-size:1.02rem;margin-top:.15rem}
|
|
41
|
-
.verdict .sub b{color:var(--red-deep)}
|
|
42
|
-
|
|
43
|
-
.trailcard{background:var(--paper);border:1px solid var(--rule);border-radius:18px;padding:.4rem .4rem 0;margin-top:1rem;box-shadow:0 24px 50px -40px rgba(0,0,0,.35);overflow:hidden}
|
|
44
|
-
.trailcard .cap{display:flex;justify-content:space-between;align-items:center;padding:.7rem 1rem .1rem;font-family:var(--mono);font-size:.7rem;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-mute)}
|
|
45
|
-
.trailcard .cap .legend{display:flex;gap:.9rem}
|
|
46
|
-
.trailcard .cap .legend span{display:flex;align-items:center;gap:.32rem}
|
|
47
|
-
.trailcard .cap .legend i{width:9px;height:9px;border-radius:50%;display:inline-block}
|
|
48
|
-
svg{display:block;width:100%;height:auto}
|
|
49
|
-
.moj{cursor:pointer}
|
|
50
|
-
.moj:focus{outline:none}
|
|
51
|
-
.moj:focus .stone{stroke:var(--ink);stroke-width:2.5}
|
|
52
|
-
.moj .stone{transition:transform .15s}
|
|
53
|
-
.moj:hover .stone{transform:translateY(-2px)}
|
|
54
|
-
@media (prefers-reduced-motion: no-preference){
|
|
55
|
-
.pulse{animation:pulse 1.8s ease-in-out infinite}
|
|
56
|
-
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.35}}
|
|
57
|
-
}
|
|
58
|
-
.mlabel{font-family:var(--sans);font-weight:700;font-size:12.5px;fill:var(--ink)}
|
|
59
|
-
.mcount{font-family:var(--mono);font-size:10.5px;fill:var(--ink-mute)}
|
|
60
|
-
.mphase{font-family:var(--mono);font-size:9px;letter-spacing:.1em;fill:var(--ink-mute);text-transform:uppercase}
|
|
61
|
-
|
|
62
|
-
/* timelapse */
|
|
63
|
-
.lapse{display:flex;align-items:center;gap:1rem;padding:.7rem 1rem 1rem}
|
|
64
|
-
.lapse label{font-family:var(--mono);font-size:.7rem;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-mute);white-space:nowrap}
|
|
65
|
-
.lapse input[type=range]{flex:1;accent-color:var(--red)}
|
|
66
|
-
.lapse .asof{font-family:var(--mono);font-size:.76rem;color:var(--ink);white-space:nowrap;min-width:120px;text-align:right}
|
|
67
|
-
.lapse .asof b{color:var(--red-deep)}
|
|
68
|
-
|
|
69
|
-
/* panels */
|
|
70
|
-
.grid{display:grid;grid-template-columns:repeat(12,1fr);gap:1rem;margin:1.2rem 0}
|
|
71
|
-
.card{background:var(--paper);border:1px solid var(--rule);border-radius:14px;padding:1.1rem 1.15rem}
|
|
72
|
-
.card h2{font-size:.72rem;font-family:var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--ink-mute);margin-bottom:.8rem;display:flex;justify-content:space-between;align-items:center}
|
|
73
|
-
.card h2 .n{color:var(--ink);font-weight:700}
|
|
74
|
-
.col6{grid-column:span 6}.col4{grid-column:span 4}.col12{grid-column:span 12}
|
|
75
|
-
@media(max-width:820px){.col6,.col4{grid-column:span 12}}
|
|
76
|
-
|
|
77
|
-
.speclist{display:flex;flex-direction:column;gap:.5rem}
|
|
78
|
-
.spec{display:flex;align-items:center;gap:.7rem;font-size:.9rem}
|
|
79
|
-
.spec .id{font-family:var(--mono);font-size:.76rem;color:var(--ink-mute);min-width:74px}
|
|
80
|
-
.spec .t{flex:1;color:var(--ink-soft)}
|
|
81
|
-
.spec .chip{font-family:var(--mono);font-size:.66rem;font-weight:700;letter-spacing:.04em;padding:2px 8px;border-radius:20px}
|
|
82
|
-
.spec .acc{font-family:var(--mono);font-size:.72rem;color:var(--ink-mute);min-width:38px;text-align:right}
|
|
83
|
-
.st-done{background:var(--green-soft);color:var(--green)}
|
|
84
|
-
.st-prog{background:var(--amber-soft);color:var(--amber)}
|
|
85
|
-
.st-block{background:var(--red-soft);color:var(--red-deep)}
|
|
86
|
-
.st-todo{background:#eef1f4;color:var(--ink-mute)}
|
|
87
|
-
|
|
88
|
-
.inv{display:flex;flex-wrap:wrap;gap:.5rem}
|
|
89
|
-
.inv .item{display:flex;align-items:center;gap:.4rem;font-size:.82rem;background:#f7f9fb;border:1px solid var(--rule);border-radius:9px;padding:.4rem .6rem}
|
|
90
|
-
.inv .item i{width:8px;height:8px;border-radius:50%}
|
|
91
|
-
.ok i{background:var(--green)} .warn i{background:var(--amber)} .miss i{background:var(--red)}
|
|
92
|
-
.ok{color:var(--ink)} .warn{color:var(--ink)} .miss{color:var(--ink)}
|
|
93
|
-
|
|
94
|
-
.heat{display:grid;grid-template-columns:repeat(3,1fr);gap:.5rem}
|
|
95
|
-
.heat .cell{border-radius:10px;padding:.6rem;border:1px solid var(--rule)}
|
|
96
|
-
.heat .cell .lab{font-size:.78rem;font-weight:600}
|
|
97
|
-
.heat .cell .v{font-family:var(--mono);font-size:.68rem;margin-top:.15rem}
|
|
98
|
-
.h-ok{background:var(--green-soft)} .h-ok .v{color:var(--green)}
|
|
99
|
-
.h-warn{background:var(--amber-soft)} .h-warn .v{color:var(--amber)}
|
|
100
|
-
.h-miss{background:var(--red-soft)} .h-miss .v{color:var(--red-deep)}
|
|
101
|
-
|
|
102
|
-
.loop{display:flex;align-items:center;gap:.6rem;font-size:.86rem;margin-bottom:.55rem}
|
|
103
|
-
.loop .mod{font-family:var(--mono);font-size:.78rem;min-width:96px;color:var(--ink)}
|
|
104
|
-
.loop .bar{flex:1;display:flex;gap:3px}
|
|
105
|
-
.loop .bar b{height:14px;border-radius:2px;flex:1;background:#e7ebef}
|
|
106
|
-
.loop .bar b.on{background:var(--stone)}
|
|
107
|
-
.loop .tag{font-family:var(--mono);font-size:.64rem;font-weight:700;padding:2px 7px;border-radius:20px}
|
|
108
|
-
|
|
109
|
-
/* subscores collapsed */
|
|
110
|
-
.subs summary{cursor:pointer;font-family:var(--mono);font-size:.72rem;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-mute);list-style:none;display:flex;align-items:center;gap:.5rem}
|
|
111
|
-
.subs summary::-webkit-details-marker{display:none}
|
|
112
|
-
.subs summary::before{content:"▸";color:var(--red);transition:transform .15s}
|
|
113
|
-
.subs[open] summary::before{transform:rotate(90deg)}
|
|
114
|
-
.subrow{display:grid;grid-template-columns:repeat(5,1fr);gap:.6rem;margin-top:.8rem}
|
|
115
|
-
.subrow .s{text-align:center;background:#f7f9fb;border:1px solid var(--rule);border-radius:10px;padding:.6rem .3rem}
|
|
116
|
-
.subrow .s .k{font-family:var(--mono);font-size:.62rem;letter-spacing:.06em;color:var(--ink-mute);text-transform:uppercase}
|
|
117
|
-
.subrow .s .val{font-weight:800;font-size:1.25rem;margin-top:.15rem}
|
|
118
|
-
.subrow .s .bd{font-family:var(--mono);font-size:.6rem;color:var(--ink-mute)}
|
|
119
|
-
@media(max-width:560px){.subrow{grid-template-columns:repeat(2,1fr)}}
|
|
120
|
-
|
|
121
|
-
/* evidence drawer */
|
|
122
|
-
.drawer{position:fixed;top:0;right:0;height:100%;width:min(460px,92vw);background:var(--paper);border-left:1px solid var(--rule);box-shadow:-30px 0 60px -30px rgba(0,0,0,.4);transform:translateX(100%);transition:transform .28s ease;z-index:40;display:flex;flex-direction:column}
|
|
123
|
-
.drawer.open{transform:translateX(0)}
|
|
124
|
-
.drawer .dh{display:flex;justify-content:space-between;align-items:flex-start;gap:1rem;padding:1.2rem 1.3rem;border-bottom:1px solid var(--rule)}
|
|
125
|
-
.drawer .dh .ey{font-family:var(--mono);font-size:.66rem;letter-spacing:.14em;text-transform:uppercase;color:var(--red-deep)}
|
|
126
|
-
.drawer .dh h3{font-size:1.15rem;letter-spacing:-.02em;margin-top:.25rem}
|
|
127
|
-
.drawer .dh p{font-size:.85rem;color:var(--ink-soft);margin-top:.3rem}
|
|
128
|
-
.drawer .x{background:none;border:1px solid var(--rule);border-radius:8px;width:32px;height:32px;cursor:pointer;font-size:1.1rem;color:var(--ink-mute);flex-shrink:0}
|
|
129
|
-
.drawer .receipt{flex:1;overflow:auto;padding:1.1rem 1.3rem}
|
|
130
|
-
.drawer .receipt .rt{font-family:var(--mono);font-size:.7rem;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-mute);margin-bottom:.5rem}
|
|
131
|
-
.drawer pre{background:#14171c;color:#d7dbe0;border-radius:10px;padding:1rem;font-family:var(--mono);font-size:.76rem;line-height:1.55;overflow:auto;white-space:pre-wrap;word-break:break-word}
|
|
132
|
-
.drawer pre .g{color:#34d399}.drawer pre .r{color:#f87171}.drawer pre .a{color:#fbbf24}.drawer pre .m{color:#8b929b}
|
|
133
|
-
.scrim{position:fixed;inset:0;background:rgba(20,23,28,.35);opacity:0;pointer-events:none;transition:opacity .28s;z-index:39}
|
|
134
|
-
.scrim.open{opacity:1;pointer-events:auto}
|
|
135
|
-
|
|
136
|
-
.foot{padding:1.4rem 0 3rem;font-family:var(--mono);font-size:.68rem;color:var(--ink-mute);text-align:center;line-height:1.7}
|
|
137
|
-
.foot b{color:var(--ink-soft)}
|
|
138
|
-
.telemetry{margin:1.4rem 0 .2rem;border:1px dashed var(--rule);border-radius:14px;padding:.85rem 1.1rem;background:#fbfbfa}
|
|
139
|
-
.telemetry .tlabel{font-family:var(--mono);font-size:.68rem;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-mute);margin-bottom:.7rem;display:flex;gap:.6rem;align-items:baseline;flex-wrap:wrap}
|
|
140
|
-
.telemetry .tlabel span{font-size:.62rem;color:var(--amber);border:1px solid var(--amber);border-radius:6px;padding:.05rem .4rem;text-transform:none;letter-spacing:0}
|
|
141
|
-
.telemetry .trow{display:flex;gap:1.7rem;flex-wrap:wrap}
|
|
142
|
-
.telemetry .tcell .tk{font-size:.66rem;color:var(--ink-mute)}
|
|
143
|
-
.telemetry .tcell .tv{font-family:var(--mono);font-size:1.05rem;color:var(--ink-soft)}
|
|
144
|
-
.telemetry .tmodels{margin-top:.8rem;display:flex;flex-direction:column;gap:.35rem}
|
|
145
|
-
.telemetry .tmrow{display:flex;justify-content:space-between;gap:1rem;font-size:.78rem;border-top:1px solid var(--rule);padding-top:.35rem}
|
|
146
|
-
.telemetry .tmrow .tmn{font-family:var(--mono);color:var(--ink-mute)}
|
|
147
|
-
.telemetry .tmrow .tmv{font-family:var(--mono);color:var(--ink-soft)}
|
|
148
|
-
.exec{display:flex;flex-direction:column;gap:.55rem}
|
|
149
|
-
.exrow{display:grid;grid-template-columns:72px 1fr;gap:.75rem;border-top:1px solid var(--rule);padding:.55rem 0}
|
|
150
|
-
.exrow:first-child{border-top:0;padding-top:0}
|
|
151
|
-
.exphase{font-family:var(--mono);font-size:.7rem;color:var(--red-deep);letter-spacing:.06em;text-transform:uppercase}
|
|
152
|
-
.exmethod{font-size:.82rem;line-height:1.35;color:var(--ink-soft);margin-bottom:.25rem}
|
|
153
|
-
.exmeta{display:flex;gap:.35rem;flex-wrap:wrap}
|
|
154
|
-
.exchip{font-family:var(--mono);font-size:.63rem;color:var(--ink-mute);border:1px solid var(--rule);border-radius:6px;padding:.08rem .38rem;background:#fff}
|
|
155
|
-
.exchip.warn{color:var(--amber);border-color:var(--amber);background:#fff9ec}
|
|
156
|
-
.hero .projtitle{font-size:1.7rem;font-weight:700;color:var(--ink-soft);letter-spacing:-.01em;margin-bottom:.75rem;line-height:1.15}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
<div class="
|
|
197
|
-
<
|
|
198
|
-
<div class="
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
<div class="
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
<
|
|
213
|
-
<div class="
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
<
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
</
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
<div class="
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
<div
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
</
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
{
|
|
296
|
-
{
|
|
297
|
-
{
|
|
298
|
-
{
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
{
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
{
|
|
305
|
-
{
|
|
306
|
-
{
|
|
307
|
-
{
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
{
|
|
312
|
-
{
|
|
313
|
-
{
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
{
|
|
317
|
-
{
|
|
318
|
-
{
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
{
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
{
|
|
325
|
-
{
|
|
326
|
-
{
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
{
|
|
331
|
-
{
|
|
332
|
-
{
|
|
333
|
-
{
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
$("#
|
|
500
|
-
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
return
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
const
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
function
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
$("#
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
const
|
|
569
|
-
|
|
570
|
-
const
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="es">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>Mirador — estado del proyecto · Uscha</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter+Tight:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500;600&display=swap" rel="stylesheet">
|
|
10
|
+
<style>
|
|
11
|
+
:root{
|
|
12
|
+
--bg:#f3f5f7; --paper:#ffffff; --ink:#14171c; --ink-soft:#3b424b; --ink-mute:#8b929b;
|
|
13
|
+
--red:#e30613; --red-deep:#b00410; --red-soft:#fff1f2;
|
|
14
|
+
--green:#1f9d57; --green-soft:#e9f7ef; --amber:#e0930c; --amber-soft:#fff5e1;
|
|
15
|
+
--terrain:#d9d2c4; --terrain-line:#c7bda9; --rule:#e6e9ee; --stone:#8b8578;
|
|
16
|
+
--sans:"Inter Tight",-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;
|
|
17
|
+
--mono:"JetBrains Mono",monospace;
|
|
18
|
+
}
|
|
19
|
+
*{box-sizing:border-box;margin:0;padding:0}
|
|
20
|
+
html{scroll-behavior:smooth}
|
|
21
|
+
body{background:var(--bg);color:var(--ink);font-family:var(--sans);line-height:1.5;-webkit-font-smoothing:antialiased}
|
|
22
|
+
body::before{content:"";position:fixed;inset:0;background-image:radial-gradient(circle at 1px 1px,rgba(0,0,0,.022) 1px,transparent 0);background-size:5px 5px;pointer-events:none;z-index:0}
|
|
23
|
+
.wrap{max-width:1140px;margin:0 auto;padding:0 clamp(1rem,4vw,2rem);position:relative;z-index:1}
|
|
24
|
+
|
|
25
|
+
/* topbar */
|
|
26
|
+
.topbar{background:var(--ink);color:#fff;height:46px;display:flex;align-items:center}
|
|
27
|
+
.topbar .wrap{display:flex;align-items:center;justify-content:space-between;width:100%}
|
|
28
|
+
.brand{display:flex;align-items:center;gap:.55rem;font-weight:800;letter-spacing:-.02em}
|
|
29
|
+
.brand .dot{width:11px;height:11px;border-radius:50%;background:var(--red);box-shadow:0 0 0 3px rgba(227,6,19,.25)}
|
|
30
|
+
.brand .cmd{font-family:var(--mono);font-weight:500;font-size:.78rem;color:#aeb4bd;background:#22262e;padding:2px 8px;border-radius:5px;margin-left:.4rem}
|
|
31
|
+
.topbar .meta{font-family:var(--mono);font-size:.72rem;color:#8b929b;letter-spacing:.04em}
|
|
32
|
+
.topbar .meta b{color:#cbd2dc;font-weight:500}
|
|
33
|
+
|
|
34
|
+
/* hero verdict + trail */
|
|
35
|
+
.hero{padding:clamp(1.4rem,4vw,2.4rem) 0 .6rem}
|
|
36
|
+
.verdict{display:flex;align-items:baseline;gap:1rem;flex-wrap:wrap;margin-bottom:.2rem}
|
|
37
|
+
.verdict .band{font-family:var(--mono);font-weight:700;font-size:.78rem;letter-spacing:.08em;padding:5px 12px;border-radius:7px}
|
|
38
|
+
.verdict h1{font-weight:800;font-size:clamp(1.5rem,3.4vw,2.3rem);letter-spacing:-.03em;line-height:1.08}
|
|
39
|
+
.verdict h1 .score{color:var(--ink-mute);font-weight:600;font-size:.62em}
|
|
40
|
+
.verdict .sub{width:100%;color:var(--ink-soft);font-size:1.02rem;margin-top:.15rem}
|
|
41
|
+
.verdict .sub b{color:var(--red-deep)}
|
|
42
|
+
|
|
43
|
+
.trailcard{background:var(--paper);border:1px solid var(--rule);border-radius:18px;padding:.4rem .4rem 0;margin-top:1rem;box-shadow:0 24px 50px -40px rgba(0,0,0,.35);overflow:hidden}
|
|
44
|
+
.trailcard .cap{display:flex;justify-content:space-between;align-items:center;padding:.7rem 1rem .1rem;font-family:var(--mono);font-size:.7rem;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-mute)}
|
|
45
|
+
.trailcard .cap .legend{display:flex;gap:.9rem}
|
|
46
|
+
.trailcard .cap .legend span{display:flex;align-items:center;gap:.32rem}
|
|
47
|
+
.trailcard .cap .legend i{width:9px;height:9px;border-radius:50%;display:inline-block}
|
|
48
|
+
svg{display:block;width:100%;height:auto}
|
|
49
|
+
.moj{cursor:pointer}
|
|
50
|
+
.moj:focus{outline:none}
|
|
51
|
+
.moj:focus .stone{stroke:var(--ink);stroke-width:2.5}
|
|
52
|
+
.moj .stone{transition:transform .15s}
|
|
53
|
+
.moj:hover .stone{transform:translateY(-2px)}
|
|
54
|
+
@media (prefers-reduced-motion: no-preference){
|
|
55
|
+
.pulse{animation:pulse 1.8s ease-in-out infinite}
|
|
56
|
+
@keyframes pulse{0%,100%{opacity:1}50%{opacity:.35}}
|
|
57
|
+
}
|
|
58
|
+
.mlabel{font-family:var(--sans);font-weight:700;font-size:12.5px;fill:var(--ink)}
|
|
59
|
+
.mcount{font-family:var(--mono);font-size:10.5px;fill:var(--ink-mute)}
|
|
60
|
+
.mphase{font-family:var(--mono);font-size:9px;letter-spacing:.1em;fill:var(--ink-mute);text-transform:uppercase}
|
|
61
|
+
|
|
62
|
+
/* timelapse */
|
|
63
|
+
.lapse{display:flex;align-items:center;gap:1rem;padding:.7rem 1rem 1rem}
|
|
64
|
+
.lapse label{font-family:var(--mono);font-size:.7rem;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-mute);white-space:nowrap}
|
|
65
|
+
.lapse input[type=range]{flex:1;accent-color:var(--red)}
|
|
66
|
+
.lapse .asof{font-family:var(--mono);font-size:.76rem;color:var(--ink);white-space:nowrap;min-width:120px;text-align:right}
|
|
67
|
+
.lapse .asof b{color:var(--red-deep)}
|
|
68
|
+
|
|
69
|
+
/* panels */
|
|
70
|
+
.grid{display:grid;grid-template-columns:repeat(12,1fr);gap:1rem;margin:1.2rem 0}
|
|
71
|
+
.card{background:var(--paper);border:1px solid var(--rule);border-radius:14px;padding:1.1rem 1.15rem}
|
|
72
|
+
.card h2{font-size:.72rem;font-family:var(--mono);letter-spacing:.14em;text-transform:uppercase;color:var(--ink-mute);margin-bottom:.8rem;display:flex;justify-content:space-between;align-items:center}
|
|
73
|
+
.card h2 .n{color:var(--ink);font-weight:700}
|
|
74
|
+
.col6{grid-column:span 6}.col4{grid-column:span 4}.col12{grid-column:span 12}
|
|
75
|
+
@media(max-width:820px){.col6,.col4{grid-column:span 12}}
|
|
76
|
+
|
|
77
|
+
.speclist{display:flex;flex-direction:column;gap:.5rem}
|
|
78
|
+
.spec{display:flex;align-items:center;gap:.7rem;font-size:.9rem}
|
|
79
|
+
.spec .id{font-family:var(--mono);font-size:.76rem;color:var(--ink-mute);min-width:74px}
|
|
80
|
+
.spec .t{flex:1;color:var(--ink-soft)}
|
|
81
|
+
.spec .chip{font-family:var(--mono);font-size:.66rem;font-weight:700;letter-spacing:.04em;padding:2px 8px;border-radius:20px}
|
|
82
|
+
.spec .acc{font-family:var(--mono);font-size:.72rem;color:var(--ink-mute);min-width:38px;text-align:right}
|
|
83
|
+
.st-done{background:var(--green-soft);color:var(--green)}
|
|
84
|
+
.st-prog{background:var(--amber-soft);color:var(--amber)}
|
|
85
|
+
.st-block{background:var(--red-soft);color:var(--red-deep)}
|
|
86
|
+
.st-todo{background:#eef1f4;color:var(--ink-mute)}
|
|
87
|
+
|
|
88
|
+
.inv{display:flex;flex-wrap:wrap;gap:.5rem}
|
|
89
|
+
.inv .item{display:flex;align-items:center;gap:.4rem;font-size:.82rem;background:#f7f9fb;border:1px solid var(--rule);border-radius:9px;padding:.4rem .6rem}
|
|
90
|
+
.inv .item i{width:8px;height:8px;border-radius:50%}
|
|
91
|
+
.ok i{background:var(--green)} .warn i{background:var(--amber)} .miss i{background:var(--red)}
|
|
92
|
+
.ok{color:var(--ink)} .warn{color:var(--ink)} .miss{color:var(--ink)}
|
|
93
|
+
|
|
94
|
+
.heat{display:grid;grid-template-columns:repeat(3,1fr);gap:.5rem}
|
|
95
|
+
.heat .cell{border-radius:10px;padding:.6rem;border:1px solid var(--rule)}
|
|
96
|
+
.heat .cell .lab{font-size:.78rem;font-weight:600}
|
|
97
|
+
.heat .cell .v{font-family:var(--mono);font-size:.68rem;margin-top:.15rem}
|
|
98
|
+
.h-ok{background:var(--green-soft)} .h-ok .v{color:var(--green)}
|
|
99
|
+
.h-warn{background:var(--amber-soft)} .h-warn .v{color:var(--amber)}
|
|
100
|
+
.h-miss{background:var(--red-soft)} .h-miss .v{color:var(--red-deep)}
|
|
101
|
+
|
|
102
|
+
.loop{display:flex;align-items:center;gap:.6rem;font-size:.86rem;margin-bottom:.55rem}
|
|
103
|
+
.loop .mod{font-family:var(--mono);font-size:.78rem;min-width:96px;color:var(--ink)}
|
|
104
|
+
.loop .bar{flex:1;display:flex;gap:3px}
|
|
105
|
+
.loop .bar b{height:14px;border-radius:2px;flex:1;background:#e7ebef}
|
|
106
|
+
.loop .bar b.on{background:var(--stone)}
|
|
107
|
+
.loop .tag{font-family:var(--mono);font-size:.64rem;font-weight:700;padding:2px 7px;border-radius:20px}
|
|
108
|
+
|
|
109
|
+
/* subscores collapsed */
|
|
110
|
+
.subs summary{cursor:pointer;font-family:var(--mono);font-size:.72rem;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-mute);list-style:none;display:flex;align-items:center;gap:.5rem}
|
|
111
|
+
.subs summary::-webkit-details-marker{display:none}
|
|
112
|
+
.subs summary::before{content:"▸";color:var(--red);transition:transform .15s}
|
|
113
|
+
.subs[open] summary::before{transform:rotate(90deg)}
|
|
114
|
+
.subrow{display:grid;grid-template-columns:repeat(5,1fr);gap:.6rem;margin-top:.8rem}
|
|
115
|
+
.subrow .s{text-align:center;background:#f7f9fb;border:1px solid var(--rule);border-radius:10px;padding:.6rem .3rem}
|
|
116
|
+
.subrow .s .k{font-family:var(--mono);font-size:.62rem;letter-spacing:.06em;color:var(--ink-mute);text-transform:uppercase}
|
|
117
|
+
.subrow .s .val{font-weight:800;font-size:1.25rem;margin-top:.15rem}
|
|
118
|
+
.subrow .s .bd{font-family:var(--mono);font-size:.6rem;color:var(--ink-mute)}
|
|
119
|
+
@media(max-width:560px){.subrow{grid-template-columns:repeat(2,1fr)}}
|
|
120
|
+
|
|
121
|
+
/* evidence drawer */
|
|
122
|
+
.drawer{position:fixed;top:0;right:0;height:100%;width:min(460px,92vw);background:var(--paper);border-left:1px solid var(--rule);box-shadow:-30px 0 60px -30px rgba(0,0,0,.4);transform:translateX(100%);transition:transform .28s ease;z-index:40;display:flex;flex-direction:column}
|
|
123
|
+
.drawer.open{transform:translateX(0)}
|
|
124
|
+
.drawer .dh{display:flex;justify-content:space-between;align-items:flex-start;gap:1rem;padding:1.2rem 1.3rem;border-bottom:1px solid var(--rule)}
|
|
125
|
+
.drawer .dh .ey{font-family:var(--mono);font-size:.66rem;letter-spacing:.14em;text-transform:uppercase;color:var(--red-deep)}
|
|
126
|
+
.drawer .dh h3{font-size:1.15rem;letter-spacing:-.02em;margin-top:.25rem}
|
|
127
|
+
.drawer .dh p{font-size:.85rem;color:var(--ink-soft);margin-top:.3rem}
|
|
128
|
+
.drawer .x{background:none;border:1px solid var(--rule);border-radius:8px;width:32px;height:32px;cursor:pointer;font-size:1.1rem;color:var(--ink-mute);flex-shrink:0}
|
|
129
|
+
.drawer .receipt{flex:1;overflow:auto;padding:1.1rem 1.3rem}
|
|
130
|
+
.drawer .receipt .rt{font-family:var(--mono);font-size:.7rem;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-mute);margin-bottom:.5rem}
|
|
131
|
+
.drawer pre{background:#14171c;color:#d7dbe0;border-radius:10px;padding:1rem;font-family:var(--mono);font-size:.76rem;line-height:1.55;overflow:auto;white-space:pre-wrap;word-break:break-word}
|
|
132
|
+
.drawer pre .g{color:#34d399}.drawer pre .r{color:#f87171}.drawer pre .a{color:#fbbf24}.drawer pre .m{color:#8b929b}
|
|
133
|
+
.scrim{position:fixed;inset:0;background:rgba(20,23,28,.35);opacity:0;pointer-events:none;transition:opacity .28s;z-index:39}
|
|
134
|
+
.scrim.open{opacity:1;pointer-events:auto}
|
|
135
|
+
|
|
136
|
+
.foot{padding:1.4rem 0 3rem;font-family:var(--mono);font-size:.68rem;color:var(--ink-mute);text-align:center;line-height:1.7}
|
|
137
|
+
.foot b{color:var(--ink-soft)}
|
|
138
|
+
.telemetry{margin:1.4rem 0 .2rem;border:1px dashed var(--rule);border-radius:14px;padding:.85rem 1.1rem;background:#fbfbfa}
|
|
139
|
+
.telemetry .tlabel{font-family:var(--mono);font-size:.68rem;letter-spacing:.08em;text-transform:uppercase;color:var(--ink-mute);margin-bottom:.7rem;display:flex;gap:.6rem;align-items:baseline;flex-wrap:wrap}
|
|
140
|
+
.telemetry .tlabel span{font-size:.62rem;color:var(--amber);border:1px solid var(--amber);border-radius:6px;padding:.05rem .4rem;text-transform:none;letter-spacing:0}
|
|
141
|
+
.telemetry .trow{display:flex;gap:1.7rem;flex-wrap:wrap}
|
|
142
|
+
.telemetry .tcell .tk{font-size:.66rem;color:var(--ink-mute)}
|
|
143
|
+
.telemetry .tcell .tv{font-family:var(--mono);font-size:1.05rem;color:var(--ink-soft)}
|
|
144
|
+
.telemetry .tmodels{margin-top:.8rem;display:flex;flex-direction:column;gap:.35rem}
|
|
145
|
+
.telemetry .tmrow{display:flex;justify-content:space-between;gap:1rem;font-size:.78rem;border-top:1px solid var(--rule);padding-top:.35rem}
|
|
146
|
+
.telemetry .tmrow .tmn{font-family:var(--mono);color:var(--ink-mute)}
|
|
147
|
+
.telemetry .tmrow .tmv{font-family:var(--mono);color:var(--ink-soft)}
|
|
148
|
+
.exec{display:flex;flex-direction:column;gap:.55rem}
|
|
149
|
+
.exrow{display:grid;grid-template-columns:72px 1fr;gap:.75rem;border-top:1px solid var(--rule);padding:.55rem 0}
|
|
150
|
+
.exrow:first-child{border-top:0;padding-top:0}
|
|
151
|
+
.exphase{font-family:var(--mono);font-size:.7rem;color:var(--red-deep);letter-spacing:.06em;text-transform:uppercase}
|
|
152
|
+
.exmethod{font-size:.82rem;line-height:1.35;color:var(--ink-soft);margin-bottom:.25rem}
|
|
153
|
+
.exmeta{display:flex;gap:.35rem;flex-wrap:wrap}
|
|
154
|
+
.exchip{font-family:var(--mono);font-size:.63rem;color:var(--ink-mute);border:1px solid var(--rule);border-radius:6px;padding:.08rem .38rem;background:#fff}
|
|
155
|
+
.exchip.warn{color:var(--amber);border-color:var(--amber);background:#fff9ec}
|
|
156
|
+
.hero .projtitle{font-size:1.7rem;font-weight:700;color:var(--ink-soft);letter-spacing:-.01em;margin-bottom:.75rem;line-height:1.15}
|
|
157
|
+
|
|
158
|
+
/* status story — how it's going / what's blocking / what's next (all from measured DATA) */
|
|
159
|
+
.status{margin-top:1rem;background:var(--paper);border:1px solid var(--rule);border-radius:14px;box-shadow:0 24px 50px -44px rgba(0,0,0,.3)}
|
|
160
|
+
.srow{display:grid;grid-template-columns:132px 1fr;gap:1rem;align-items:start;padding:.85rem 1.15rem;border-top:1px solid var(--rule)}
|
|
161
|
+
.srow:first-child{border-top:0}
|
|
162
|
+
.srow .slab{font-family:var(--mono);font-size:.66rem;letter-spacing:.12em;text-transform:uppercase;color:var(--ink-mute);padding-top:.2rem}
|
|
163
|
+
.srow .sval{font-size:.94rem;color:var(--ink);min-width:0}
|
|
164
|
+
.srow .sval .muted{color:var(--ink-mute)}
|
|
165
|
+
.dimline{font-family:var(--mono);font-size:.82rem;color:var(--ink-soft);line-height:1.7;word-break:break-word}
|
|
166
|
+
.dimline b{color:var(--ink);font-weight:600}
|
|
167
|
+
.blockers{display:flex;flex-direction:column;gap:.35rem}
|
|
168
|
+
.blockers .bk{display:flex;align-items:baseline;gap:.5rem;line-height:1.4}
|
|
169
|
+
.blockers .bk i{color:var(--red-deep);font-style:normal;font-weight:800;flex-shrink:0}
|
|
170
|
+
.blockers .bk.ok i{color:var(--green)}
|
|
171
|
+
.blockers .bk.ok{color:var(--ink-soft)}
|
|
172
|
+
.next .nmethod{color:var(--ink);line-height:1.4}
|
|
173
|
+
.next .nmethod b{font-weight:700}
|
|
174
|
+
.next .nmeta{margin-top:.35rem;display:flex;gap:.35rem;flex-wrap:wrap}
|
|
175
|
+
.next .nmeta span{font-family:var(--mono);font-size:.63rem;color:var(--ink-mute);border:1px solid var(--rule);border-radius:6px;padding:.08rem .4rem;background:#fff}
|
|
176
|
+
.emptyhint{color:var(--ink-mute);font-size:.82rem;line-height:1.5}
|
|
177
|
+
</style>
|
|
178
|
+
</head>
|
|
179
|
+
<body>
|
|
180
|
+
|
|
181
|
+
<header class="topbar"><div class="wrap">
|
|
182
|
+
<div class="brand"><span class="dot"></span>uscha<span class="cmd">/mirador</span></div>
|
|
183
|
+
<div class="meta"><b id="m-proj">—</b> · generated <b id="m-ts">—</b></div>
|
|
184
|
+
</div></header>
|
|
185
|
+
|
|
186
|
+
<main class="wrap">
|
|
187
|
+
|
|
188
|
+
<section class="hero">
|
|
189
|
+
<div class="projtitle" id="projtitle"></div>
|
|
190
|
+
<div class="verdict">
|
|
191
|
+
<span class="band" id="v-band">—</span>
|
|
192
|
+
<h1 id="v-title">—</h1>
|
|
193
|
+
<div class="sub" id="v-sub">—</div>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<div class="status" id="status" hidden>
|
|
197
|
+
<div class="srow"><div class="slab">Cómo viene</div><div class="sval dimline" id="s-how"></div></div>
|
|
198
|
+
<div class="srow"><div class="slab">Qué lo traba</div><div class="sval blockers" id="s-block"></div></div>
|
|
199
|
+
<div class="srow"><div class="slab">Qué sigue</div><div class="sval next" id="s-next"></div></div>
|
|
200
|
+
</div>
|
|
201
|
+
|
|
202
|
+
<div class="trailcard">
|
|
203
|
+
<div class="cap">
|
|
204
|
+
<span>The trail — from idea to production</span>
|
|
205
|
+
<span class="legend">
|
|
206
|
+
<span><i style="background:var(--green)"></i>verified</span>
|
|
207
|
+
<span><i style="background:var(--amber)"></i>in progress</span>
|
|
208
|
+
<span><i style="background:var(--red)"></i>blocked</span>
|
|
209
|
+
<span><i style="background:#cfd5db"></i>to do</span>
|
|
210
|
+
</span>
|
|
211
|
+
</div>
|
|
212
|
+
<svg id="trail" viewBox="0 0 1060 300" role="img" aria-label="Project trail"></svg>
|
|
213
|
+
<div class="lapse">
|
|
214
|
+
<label for="scrub">Time-lapse</label>
|
|
215
|
+
<input type="range" id="scrub" min="0" max="0" value="0" step="1" aria-label="Replay the project's evolution">
|
|
216
|
+
<span class="asof">as of <b id="asof">—</b></span>
|
|
217
|
+
</div>
|
|
218
|
+
</div>
|
|
219
|
+
</section>
|
|
220
|
+
|
|
221
|
+
<section class="grid">
|
|
222
|
+
<div class="card col6">
|
|
223
|
+
<h2>Specifications <span class="n" id="spec-n">—</span></h2>
|
|
224
|
+
<div class="speclist" id="speclist"></div>
|
|
225
|
+
</div>
|
|
226
|
+
|
|
227
|
+
<div class="card col6">
|
|
228
|
+
<h2>ADRs <span class="n" id="adr-n">—</span></h2>
|
|
229
|
+
<div class="speclist" id="adrlist"></div>
|
|
230
|
+
</div>
|
|
231
|
+
|
|
232
|
+
<div class="card col6">
|
|
233
|
+
<h2>CONSTITUTION — invariants</h2>
|
|
234
|
+
<div class="inv" id="inv"></div>
|
|
235
|
+
</div>
|
|
236
|
+
|
|
237
|
+
<div class="card col6" id="card-heat">
|
|
238
|
+
<h2>The 6 layers of truth</h2>
|
|
239
|
+
<div class="heat" id="heat"></div>
|
|
240
|
+
</div>
|
|
241
|
+
|
|
242
|
+
<div class="card col6">
|
|
243
|
+
<h2>QA loops <span class="n" id="qa-n">—</span></h2>
|
|
244
|
+
<div id="loops"></div>
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
<div class="card col6">
|
|
248
|
+
<h2>Execution policy <span class="n" id="exec-n">—</span></h2>
|
|
249
|
+
<div class="exec" id="exec"></div>
|
|
250
|
+
</div>
|
|
251
|
+
|
|
252
|
+
<div class="card col6">
|
|
253
|
+
<details class="subs">
|
|
254
|
+
<summary>Sub-scores (drill-down)</summary>
|
|
255
|
+
<div class="subrow" id="subrow"></div>
|
|
256
|
+
</details>
|
|
257
|
+
<p style="font-size:.8rem;color:var(--ink-mute);margin-top:.9rem;line-height:1.5">
|
|
258
|
+
The verdict above is all you need to look at. These roll up into it; they're here if you want to dig.
|
|
259
|
+
</p>
|
|
260
|
+
</div>
|
|
261
|
+
</section>
|
|
262
|
+
|
|
263
|
+
<section class="telemetry" id="telemetry" hidden>
|
|
264
|
+
<div class="tlabel">Claude Code · session telemetry <span>vendor-reported — narrated, not measured</span></div>
|
|
265
|
+
<div class="trow" id="trow"></div>
|
|
266
|
+
<div class="tmodels" id="tmodels"></div>
|
|
267
|
+
</section>
|
|
268
|
+
|
|
269
|
+
<div class="foot">
|
|
270
|
+
generated by <b>qa_ledger.py dashboard --json</b> — read-only, evidence rules · click a milestone to see its receipt<br>
|
|
271
|
+
the tool executes · the method governs · evidence decides · the human approves
|
|
272
|
+
</div>
|
|
273
|
+
</main>
|
|
274
|
+
|
|
275
|
+
<div class="scrim" id="scrim"></div>
|
|
276
|
+
<aside class="drawer" id="drawer" aria-hidden="true">
|
|
277
|
+
<div class="dh">
|
|
278
|
+
<div><div class="ey" id="d-ey">receipt</div><h3 id="d-title">—</h3><p id="d-desc">—</p></div>
|
|
279
|
+
<button class="x" id="d-close" aria-label="Close">✕</button>
|
|
280
|
+
</div>
|
|
281
|
+
<div class="receipt"><div class="rt">evidence captured by execution</div><pre id="d-pre"></pre></div>
|
|
282
|
+
</aside>
|
|
283
|
+
|
|
284
|
+
<script>
|
|
285
|
+
/* ==== DATA — sample output of `qa_ledger.py dashboard --json` (deterministic) ==== */
|
|
286
|
+
/*MIRADOR_DATA_START*/
|
|
287
|
+
const DATA = {
|
|
288
|
+
project: "Fiscal Platform",
|
|
289
|
+
generated: "2026-07-05 14:22 UTC",
|
|
290
|
+
readiness: { score:88, band:"CASI LISTO",
|
|
291
|
+
title:"Casi listo para release",
|
|
292
|
+
sub:"No sale todavía — <b>1 blocker: falta validar el rollback en producción</b> (capa Operacional en rojo)." },
|
|
293
|
+
subscores:[
|
|
294
|
+
{k:"simplicity",val:92,bd:"SIMPLE"},
|
|
295
|
+
{k:"waste",val:78,bd:"ACEPTABLE"},
|
|
296
|
+
{k:"rebuild",val:90,bd:"COVERS"},
|
|
297
|
+
{k:"golden",val:100,bd:"OK"},
|
|
298
|
+
{k:"coverage",val:81,bd:"81%"},
|
|
299
|
+
],
|
|
300
|
+
phases:[
|
|
301
|
+
{key:"idea", label:"Idea", phase:"00", status:"done", count:"", risk:0},
|
|
302
|
+
{key:"disc", label:"Discovery", phase:"01", status:"done", count:"seed + ready", risk:0},
|
|
303
|
+
{key:"spec", label:"SPEC", phase:"02", status:"done", count:"6/7 specs", risk:1},
|
|
304
|
+
{key:"adr", label:"ADR · CONST", phase:"03", status:"done", count:"4 ADR · 6 inv", risk:1},
|
|
305
|
+
{key:"build", label:"Build", phase:"04", status:"prog", count:"5/7 módulos", risk:2},
|
|
306
|
+
{key:"qa", label:"QA loop", phase:"05", status:"prog", count:"3 loops · 1 escalado", risk:2},
|
|
307
|
+
{key:"verify", label:"Verify", phase:"06", status:"block", count:"1 blocker", risk:2},
|
|
308
|
+
{key:"prod", label:"Producción", phase:"07", status:"todo", count:"human gate", risk:3},
|
|
309
|
+
],
|
|
310
|
+
execution_policy:{source:"sample",default:{tier:"standard",model:null,effort:"medium"},phases:{
|
|
311
|
+
idea:{phase:"idea",method:"Capture the intent",tier:"human",model:null,effort:"none"},
|
|
312
|
+
disc:{phase:"disc",method:"Shape discovery facts and risks",tier:"deep",model:null,effort:"high"},
|
|
313
|
+
spec:{phase:"spec",method:"Write testable SPEC/ACCEPTANCE",tier:"deep",model:null,effort:"high"},
|
|
314
|
+
adr:{phase:"adr",method:"Record decisions and invariants",tier:"deep",model:null,effort:"high"},
|
|
315
|
+
build:{phase:"build",method:"Implement the accepted plan",tier:"standard",model:null,effort:"medium"},
|
|
316
|
+
qa:{phase:"qa",method:"Run maker-not-checker QA",tier:"checker",model:null,effort:"high",uncorrelated:true},
|
|
317
|
+
verify:{phase:"verify",method:"Re-run measured release gates",tier:"standard",model:null,effort:"medium"},
|
|
318
|
+
prod:{phase:"prod",method:"Stop at the human gate",tier:"human",model:null,effort:"none"}
|
|
319
|
+
}},
|
|
320
|
+
specs:[
|
|
321
|
+
{id:"SPEC-001",t:"Alta de comprobante",status:"done",acc:100},
|
|
322
|
+
{id:"SPEC-002",t:"Numeración sin huecos",status:"done",acc:100},
|
|
323
|
+
{id:"SPEC-003",t:"Reintento idempotente",status:"done",acc:100},
|
|
324
|
+
{id:"SPEC-004",t:"Cierre de caja",status:"prog",acc:70},
|
|
325
|
+
{id:"SPEC-005",t:"Exportación fiscal",status:"prog",acc:55},
|
|
326
|
+
{id:"SPEC-006",t:"Rollback de despliegue",status:"block",acc:20},
|
|
327
|
+
{id:"SPEC-007",t:"Reporte de conciliación",status:"todo",acc:0},
|
|
328
|
+
],
|
|
329
|
+
adrs:[
|
|
330
|
+
{id:"ADR-001",t:"Almacenamiento append-only",status:"done"},
|
|
331
|
+
{id:"ADR-002",t:"Idempotencia por request-id",status:"done"},
|
|
332
|
+
{id:"ADR-003",t:"Cola de reintentos con backoff",status:"done"},
|
|
333
|
+
{id:"ADR-004",t:"Estrategia de rollback",status:"prog"},
|
|
334
|
+
],
|
|
335
|
+
inv:[
|
|
336
|
+
{name:"Seguridad",status:"ok"},{name:"Dominio",status:"ok"},
|
|
337
|
+
{name:"Simplicidad",status:"ok"},{name:"Reuso",status:"warn"},
|
|
338
|
+
{name:"Golden",status:"ok"},{name:"Tests efectivos",status:"ok"},
|
|
339
|
+
{name:"Integridad del gate",status:"ok"},
|
|
340
|
+
],
|
|
341
|
+
capas:[
|
|
342
|
+
{name:"Funcional",status:"ok",v:"cumple SPEC"},
|
|
343
|
+
{name:"Técnica",status:"ok",v:"tests + lint"},
|
|
344
|
+
{name:"Regresiva",status:"ok",v:"sin romper"},
|
|
345
|
+
{name:"Contractual",status:"warn",v:"1 schema drift"},
|
|
346
|
+
{name:"Seguridad",status:"ok",v:"0 críticos"},
|
|
347
|
+
{name:"Operacional",status:"miss",v:"rollback sin validar"},
|
|
348
|
+
],
|
|
349
|
+
loops:[
|
|
350
|
+
{mod:"pagos",iters:4,max:6,state:"converged"},
|
|
351
|
+
{mod:"ticket",iters:6,max:6,state:"oscillating"},
|
|
352
|
+
{mod:"auth",iters:2,max:6,state:"escalated"},
|
|
353
|
+
],
|
|
354
|
+
/* time-lapse: cada snapshot = readiness + hasta qué fase se había llegado */
|
|
355
|
+
snapshots:[
|
|
356
|
+
{date:"jun 18",readiness:12,reached:1},
|
|
357
|
+
{date:"jun 23",readiness:34,reached:2},
|
|
358
|
+
{date:"jun 27",readiness:51,reached:3},
|
|
359
|
+
{date:"jul 01",readiness:69,reached:4},
|
|
360
|
+
{date:"jul 03",readiness:80,reached:5},
|
|
361
|
+
{date:"jul 05",readiness:88,reached:7},
|
|
362
|
+
],
|
|
363
|
+
telemetry:{ source:"Claude Code", sessions:3, model:"claude-opus-4-8", effort:"high",
|
|
364
|
+
tokens_in:284000, tokens_out:47000, ms:5400000,
|
|
365
|
+
by_model:[{model:"claude-opus-4-8",tokens_in:240000,tokens_out:41000},{model:"claude-haiku-4-5",tokens_in:44000,tokens_out:6000}] },
|
|
366
|
+
evidence:{
|
|
367
|
+
verify:{ey:"gate · verify",title:"Verify — 1 blocker",desc:"Capa Operacional en rojo: el rollback no se validó en un entorno real.",
|
|
368
|
+
pre:"<span class='m'>$ qa_ledger.py ingest-gate --layer operational</span>\nOPERATIONAL: <span class='r'>MISS</span>\n ! deploy rollback drill: <span class='r'>NOT RUN</span>\n ! SPEC-006 acceptance: 20% (< 100%)\n<span class='m'># readiness cap aplicado: operational MISS => no supera RELEASE</span>\n<span class='r'>BLOCKER</span> — se requiere drill de rollback + human approval"},
|
|
369
|
+
prod:{ey:"human gate",title:"Producción — requiere aprobación",desc:"La automatización nunca despliega sola. El paso a producción lo aprueba una persona.",
|
|
370
|
+
pre:"<span class='m'>estado:</span> BLOQUEADO por gate humano\n<span class='m'>requisitos previos:</span>\n <span class='g'>✓</span> tests verdes\n <span class='g'>✓</span> 0 findings críticos\n <span class='r'>✗</span> rollback validado\n <span class='r'>✗</span> aprobación humana\n<span class='m'># se habilita cuando readiness ≥ 95 y 0 blockers</span>"},
|
|
371
|
+
build:{ey:"fase · build",title:"Build — 5/7 módulos",desc:"Construcción contra el plan, commit a commit. Dos módulos en curso.",
|
|
372
|
+
pre:"<span class='m'>módulos:</span>\n <span class='g'>✓</span> pagos (SPEC-001..003)\n <span class='g'>✓</span> ticket\n <span class='g'>✓</span> promos\n <span class='a'>◐</span> cierre-caja (SPEC-004, 70%)\n <span class='a'>◐</span> export-fiscal(SPEC-005, 55%)\n <span class='r'>·</span> rollback (SPEC-006, 20%)\n <span class='m'>·</span> conciliación (SPEC-007, todo)"},
|
|
373
|
+
qa:{ey:"fase · qa loop",title:"QA loop — 3 loops",desc:"Iteración maker≠checker por módulo hasta converger. Uno escaló a humano.",
|
|
374
|
+
pre:"<span class='m'>$ qa_ledger.py converged / oscillation / escalate</span>\n pagos : <span class='g'>CONVERGED</span> (4 iters)\n ticket : <span class='a'>OSCILLATING</span> (6 iters, sin converger)\n auth : <span class='r'>ESCALATED</span> (2 iters → humano)\n<span class='m'># ticket alterna entre 2 estados: candidato a revisión humana</span>"},
|
|
375
|
+
spec:{ey:"fase · spec",title:"SPEC — 6/7",desc:"Qué debe pasar, verificable. Una spec bloqueada (rollback).",
|
|
376
|
+
pre:"<span class='m'>$ qa_ledger.py spec-check</span>\n 6/7 specs con acceptance criteria testeables\n <span class='g'>✓</span> SPEC-001..003 (100%)\n <span class='a'>◐</span> SPEC-004 (70%) · SPEC-005 (55%)\n <span class='r'>✗</span> SPEC-006 rollback (20%)"},
|
|
377
|
+
disc:{ey:"fase · discovery",title:"Discovery — ready",desc:"De la idea a la forma del sistema. Vos decidiste el alcance.",
|
|
378
|
+
pre:"<span class='m'>discovery/</span>\n <span class='g'>✓</span> seed document\n <span class='g'>✓</span> restricciones inviolables → CONSTITUTION\n <span class='g'>✓</span> perfil de riesgo asignado (fiscal = C+)\n <span class='g'>✓</span> READY — aprobado para SPEC"},
|
|
379
|
+
adr:{ey:"fase · adr · constitution",title:"ADR · CONSTITUTION",desc:"Por qué esta forma, y qué nunca se viola.",
|
|
380
|
+
pre:"<span class='m'>docs/adr/</span>\n <span class='g'>✓</span> ADR-001 append-only\n <span class='g'>✓</span> ADR-002 idempotencia\n <span class='g'>✓</span> ADR-003 reintentos\n <span class='a'>◐</span> ADR-004 rollback (proposed)\n<span class='m'>CONSTITUTION:</span> 6/7 invariantes OK · <span class='a'>Reuso: WARN</span>"},
|
|
381
|
+
idea:{ey:"origen",title:"Idea",desc:"El punto de partida. Traés la idea; el método la cruza.",
|
|
382
|
+
pre:"<span class='m'># el sendero arranca acá.</span>\n\"API fiscal que autoriza cada comprobante,\n sin numeración con huecos ni facturas duplicadas.\""},
|
|
383
|
+
},
|
|
384
|
+
};
|
|
385
|
+
/*MIRADOR_DATA_END*/
|
|
386
|
+
|
|
387
|
+
/* ==== helpers ==== */
|
|
388
|
+
const $ = s => document.querySelector(s);
|
|
389
|
+
const statusColor = {done:"var(--green)",prog:"var(--amber)",block:"var(--red)",todo:"#cfd5db"};
|
|
390
|
+
const chipClass = {done:"st-done",prog:"st-prog",block:"st-block",todo:"st-todo"};
|
|
391
|
+
const chipText = {done:"done",prog:"in progress",block:"blocked",todo:"to do"};
|
|
392
|
+
function node(tag, className, value){
|
|
393
|
+
const n=document.createElement(tag);
|
|
394
|
+
if(className)n.className=className;
|
|
395
|
+
if(value!==undefined&&value!==null)n.textContent=String(value);
|
|
396
|
+
return n;
|
|
397
|
+
}
|
|
398
|
+
function append(parent,...children){children.forEach(child=>parent.appendChild(child));return parent;}
|
|
399
|
+
function emptyHint(text){return node("p","emptyhint",text);}
|
|
400
|
+
function countOf(v){return Array.isArray(v)?v.length:(v||0);}
|
|
401
|
+
function renderEvidencePre(host, value){
|
|
402
|
+
host.replaceChildren();
|
|
403
|
+
const entities={"<":"<",">":">","&":"&",""":"\"","'":"'"};
|
|
404
|
+
const text=value==null?"":String(value),tokens=/<span class=(["'])([gram])\1>|<\/span>/gi;
|
|
405
|
+
const addText=(parent,part)=>parent.appendChild(document.createTextNode(part.replace(/&(lt|gt|amp|quot|#39);/g,m=>entities[m])));
|
|
406
|
+
let cursor=0,current=host,match;
|
|
407
|
+
while((match=tokens.exec(text))!==null){
|
|
408
|
+
addText(current,text.slice(cursor,match.index));
|
|
409
|
+
if(match[2]){const span=node("span",match[2]);current.appendChild(span);current=span;}
|
|
410
|
+
else if(current!==host)current=host;
|
|
411
|
+
cursor=tokens.lastIndex;
|
|
412
|
+
}
|
|
413
|
+
addText(current,text.slice(cursor));
|
|
414
|
+
}
|
|
415
|
+
/* ==== header + verdict ==== */
|
|
416
|
+
$("#m-proj").textContent = DATA.project;
|
|
417
|
+
$("#projtitle").textContent = DATA.project || "—";
|
|
418
|
+
$("#m-ts").textContent = DATA.generated;
|
|
419
|
+
const bandColors = {"CASI LISTO":["var(--amber-soft)","var(--amber)"],"LISTO":["var(--green-soft)","var(--green)"],"NO LISTO":["var(--red-soft)","var(--red-deep)"],
|
|
420
|
+
"READY":["var(--green-soft)","var(--green)"],"RELEASE CANDIDATE":["var(--amber-soft)","var(--amber)"],"IN PROGRESS":["var(--amber-soft)","var(--amber)"],"NOT READY":["var(--red-soft)","var(--red-deep)"]};
|
|
421
|
+
function paintVerdict(score, band, title, sub){
|
|
422
|
+
const b=$("#v-band"); const c=bandColors[band]||["#eef1f4","var(--ink-mute)"];
|
|
423
|
+
b.textContent=band; b.style.background=c[0]; b.style.color=c[1];
|
|
424
|
+
const titleHost=$("#v-title");
|
|
425
|
+
titleHost.replaceChildren(document.createTextNode(String(title||"")+" "),node("span","score",String(score)+"/100"));
|
|
426
|
+
$("#v-sub").textContent=sub||"";
|
|
427
|
+
}
|
|
428
|
+
paintVerdict(DATA.readiness.score, DATA.readiness.band, DATA.readiness.title, DATA.readiness.sub);
|
|
429
|
+
|
|
430
|
+
/* ==== el sendero (SVG) ==== */
|
|
431
|
+
const VB={w:1060,h:300}, base=232, pad=70;
|
|
432
|
+
const riskY=[0,26,60,92];
|
|
433
|
+
const N=DATA.phases.length;
|
|
434
|
+
function nodeXY(i,risk){const r=(riskY[risk]==null?0:riskY[risk]);const x=pad+i*((VB.w-2*pad)/(N-1));const y=base-r;return [x,y];}
|
|
435
|
+
function smoothPath(pts){
|
|
436
|
+
let d="M "+pts[0][0]+" "+pts[0][1];
|
|
437
|
+
for(let i=0;i<pts.length-1;i++){
|
|
438
|
+
const [x0,y0]=pts[i],[x1,y1]=pts[i+1];const mx=(x0+x1)/2;
|
|
439
|
+
d+=" C "+mx+" "+y0+" "+mx+" "+y1+" "+x1+" "+y1;
|
|
440
|
+
}
|
|
441
|
+
return d;
|
|
442
|
+
}
|
|
443
|
+
function renderTrail(activeReached){
|
|
444
|
+
const svg=$("#trail"); svg.replaceChildren();
|
|
445
|
+
const pts=DATA.phases.map((p,i)=>nodeXY(i,p.risk));
|
|
446
|
+
const ns="http://www.w3.org/2000/svg";
|
|
447
|
+
const terr=document.createElementNS(ns,"path");
|
|
448
|
+
terr.setAttribute("d", smoothPath(pts)+" L "+pts[N-1][0]+" "+VB.h+" L "+pts[0][0]+" "+VB.h+" Z");
|
|
449
|
+
terr.setAttribute("fill","var(--terrain)"); terr.setAttribute("opacity",".4"); svg.appendChild(terr);
|
|
450
|
+
for(let k=1;k<=3;k++){
|
|
451
|
+
const c=document.createElementNS(ns,"path");
|
|
452
|
+
c.setAttribute("d", smoothPath(pts.map(([x,y])=>[x,y+k*14])));
|
|
453
|
+
c.setAttribute("fill","none");c.setAttribute("stroke","var(--terrain-line)");c.setAttribute("stroke-width","1");c.setAttribute("opacity",".5");
|
|
454
|
+
svg.appendChild(c);
|
|
455
|
+
}
|
|
456
|
+
const trail=document.createElementNS(ns,"path");
|
|
457
|
+
trail.setAttribute("d",smoothPath(pts));trail.setAttribute("fill","none");
|
|
458
|
+
trail.setAttribute("stroke","var(--stone)");trail.setAttribute("stroke-width","3");
|
|
459
|
+
trail.setAttribute("stroke-dasharray","2 7");trail.setAttribute("stroke-linecap","round");trail.setAttribute("opacity",".8");
|
|
460
|
+
svg.appendChild(trail);
|
|
461
|
+
DATA.phases.forEach((p,i)=>{
|
|
462
|
+
const [x,y]=pts[i];
|
|
463
|
+
let st=p.status;
|
|
464
|
+
if(activeReached!==null){
|
|
465
|
+
if(i<activeReached) st="done";
|
|
466
|
+
else if(i===activeReached) st=(p.status==="block"?"block":"prog");
|
|
467
|
+
else st="todo";
|
|
468
|
+
}
|
|
469
|
+
const g=document.createElementNS(ns,"g");
|
|
470
|
+
g.setAttribute("class","moj");g.setAttribute("tabindex","0");g.setAttribute("role","button");
|
|
471
|
+
g.setAttribute("aria-label",p.label+" — "+chipText[st]);g.dataset.key=p.key;
|
|
472
|
+
const halo=document.createElementNS(ns,"circle");
|
|
473
|
+
halo.setAttribute("cx",x);halo.setAttribute("cy",y);halo.setAttribute("r","18");
|
|
474
|
+
halo.setAttribute("fill",statusColor[st]);halo.setAttribute("opacity",".16");
|
|
475
|
+
if(st==="prog"||st==="block")halo.setAttribute("class","pulse");
|
|
476
|
+
g.appendChild(halo);
|
|
477
|
+
const stone=document.createElementNS(ns,"path"); const size=11;
|
|
478
|
+
stone.setAttribute("d","M "+(x-size)+" "+(y+size)+" L "+(x-size+2)+" "+(y-size+3)+" Q "+x+" "+(y-size-3)+" "+(x+size-2)+" "+(y-size+3)+" L "+(x+size)+" "+(y+size)+" Z");
|
|
479
|
+
stone.setAttribute("class","stone");stone.setAttribute("fill",st==="todo"?"#eef1f4":"#fff");
|
|
480
|
+
stone.setAttribute("stroke",statusColor[st]);stone.setAttribute("stroke-width","2.5");g.appendChild(stone);
|
|
481
|
+
const dot=document.createElementNS(ns,"circle");
|
|
482
|
+
dot.setAttribute("cx",x);dot.setAttribute("cy",y);dot.setAttribute("r","3.4");dot.setAttribute("fill",statusColor[st]);g.appendChild(dot);
|
|
483
|
+
const ph=document.createElementNS(ns,"text");
|
|
484
|
+
ph.setAttribute("x",x);ph.setAttribute("y",y-24);ph.setAttribute("text-anchor","middle");ph.setAttribute("class","mphase");ph.textContent=p.phase;g.appendChild(ph);
|
|
485
|
+
const lab=document.createElementNS(ns,"text");
|
|
486
|
+
lab.setAttribute("x",x);lab.setAttribute("y",base+38);lab.setAttribute("text-anchor","middle");lab.setAttribute("class","mlabel");lab.textContent=p.label;g.appendChild(lab);
|
|
487
|
+
if(p.count){
|
|
488
|
+
const cnt=document.createElementNS(ns,"text");
|
|
489
|
+
cnt.setAttribute("x",x);cnt.setAttribute("y",base+53);cnt.setAttribute("text-anchor","middle");cnt.setAttribute("class","mcount");cnt.textContent=p.count;g.appendChild(cnt);
|
|
490
|
+
}
|
|
491
|
+
g.addEventListener("click",()=>openEvidence(p.key));
|
|
492
|
+
g.addEventListener("keydown",e=>{if(e.key==="Enter"||e.key===" "){e.preventDefault();openEvidence(p.key);}});
|
|
493
|
+
svg.appendChild(g);
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/* ==== specs / adrs / inv / heat / loops / subs ==== */
|
|
498
|
+
function renderSpecs(){
|
|
499
|
+
$("#spec-n").textContent=DATA.specs.filter(s=>s.status==="done").length+"/"+DATA.specs.length;
|
|
500
|
+
const host=$("#speclist");host.replaceChildren();
|
|
501
|
+
if(!DATA.specs.length){host.appendChild(emptyHint("sin specs medidas todavía — la fase SPEC las declara"));return;}
|
|
502
|
+
DATA.specs.forEach(s=>append(host,append(node("div","spec"),node("span","id",s.id),node("span","t",s.t),
|
|
503
|
+
node("span","chip "+(chipClass[s.status]||"st-todo"),chipText[s.status]||s.status),node("span","acc",String(s.acc)+"%"))));
|
|
504
|
+
}
|
|
505
|
+
function adrLabel(a){
|
|
506
|
+
if(a.adr_status==="experiment"){
|
|
507
|
+
if(a.expired)return "experiment overdue";
|
|
508
|
+
if(a.experiment_valid===false)return "experiment invalid";
|
|
509
|
+
return "experiment";
|
|
510
|
+
}
|
|
511
|
+
if(a.adr_status)return a.adr_status;
|
|
512
|
+
return a.status==="done"?"accepted":a.status==="prog"?"proposed":chipText[a.status];
|
|
513
|
+
}
|
|
514
|
+
function renderAdrs(){
|
|
515
|
+
$("#adr-n").textContent=DATA.adrs.filter(a=>a.status==="done").length+"/"+DATA.adrs.length;
|
|
516
|
+
const host=$("#adrlist");host.replaceChildren();
|
|
517
|
+
if(!DATA.adrs.length){host.appendChild(emptyHint("sin ADRs medidos todavía — la fase ADR·CONST los registra"));return;}
|
|
518
|
+
DATA.adrs.forEach(a=>append(host,append(node("div","spec"),node("span","id",a.id),node("span","t",a.t),
|
|
519
|
+
node("span","chip "+(chipClass[a.status]||"st-todo"),adrLabel(a)))));
|
|
520
|
+
}
|
|
521
|
+
function renderInv(){
|
|
522
|
+
const host=$("#inv");host.replaceChildren();
|
|
523
|
+
DATA.inv.forEach(v=>{const item=node("span","item "+(v.status||""));append(item,node("i"),document.createTextNode(String(v.name||"")));host.appendChild(item);});
|
|
524
|
+
}
|
|
525
|
+
function renderHeat(){
|
|
526
|
+
const cls={ok:"h-ok",warn:"h-warn",miss:"h-miss"},host=$("#heat");host.replaceChildren();
|
|
527
|
+
const card=$("#card-heat");
|
|
528
|
+
// the engine does not feed capas for a real project -> hide the whole card rather than show a dead grid
|
|
529
|
+
if(!(DATA.capas||[]).length){if(card)card.hidden=true;return;}
|
|
530
|
+
if(card)card.hidden=false;
|
|
531
|
+
DATA.capas.forEach(c=>append(host,append(node("div","cell "+(cls[c.status]||""),undefined),node("div","lab",c.name),node("div","v",c.v))));
|
|
532
|
+
}
|
|
533
|
+
function renderLoops(){
|
|
534
|
+
$("#qa-n").textContent=DATA.loops.length;
|
|
535
|
+
const tag={converged:["st-done","converged"],oscillating:["st-prog","oscillating"],escalated:["st-block","escalated"],active:["st-prog","active"]};
|
|
536
|
+
const host=$("#loops");host.replaceChildren();
|
|
537
|
+
if(!DATA.loops.length){host.appendChild(emptyHint("sin loops de QA todavía — arrancan en la fase QA loop"));return;}
|
|
538
|
+
DATA.loops.forEach(l=>{
|
|
539
|
+
const mx=l.max||l.iters||0,bars=node("span","bar");
|
|
540
|
+
for(let i=0;i<mx;i++)bars.appendChild(node("b",i<l.iters?"on":""));
|
|
541
|
+
const t=tag[l.state]||["st-todo","—"];
|
|
542
|
+
append(host,append(node("div","loop"),node("span","mod",l.mod),bars,node("span","tag "+t[0],t[1])));
|
|
543
|
+
});
|
|
544
|
+
}
|
|
545
|
+
function renderSubs(){
|
|
546
|
+
const band=v=>(v==null?"var(--ink-mute)":v>=85?"var(--green)":v>=65?"var(--amber)":"var(--red)");
|
|
547
|
+
const host=$("#subrow");host.replaceChildren();
|
|
548
|
+
DATA.subscores.forEach(s=>{const val=node("div","val",s.val==null?"—":s.val);val.style.color=band(s.val);
|
|
549
|
+
append(host,append(node("div","s"),node("div","k",s.k),val,node("div","bd",s.bd==null?"":s.bd)));});
|
|
550
|
+
}
|
|
551
|
+
function renderExecutionPolicy(){
|
|
552
|
+
const host=$("#exec");if(!host)return;host.replaceChildren();
|
|
553
|
+
const policy=DATA.execution_policy||{},phases=policy.phases||{},source=policy.source||"policy";
|
|
554
|
+
$("#exec-n").textContent=source.replace("config.defaults.","");
|
|
555
|
+
if(!(DATA.phases||[]).length){const empty=node("p",null,"No execution_policy in DATA.");empty.style.color="var(--ink-mute)";empty.style.fontSize=".82rem";host.appendChild(empty);return;}
|
|
556
|
+
(DATA.phases||[]).forEach(p=>{
|
|
557
|
+
const e=p.execution||phases[p.key]||{},meta=node("div","exmeta");
|
|
558
|
+
append(meta,node("span","exchip","tier="+(e.tier||"-")),node("span","exchip","model="+(e.model||"default")),
|
|
559
|
+
node("span","exchip","effort="+(e.effort||"-")));
|
|
560
|
+
if(e.uncorrelated)meta.appendChild(node("span","exchip warn","uncorrelated"));
|
|
561
|
+
append(host,append(node("div","exrow"),node("div","exphase",String(p.phase||"")+" "+String(p.key||"")),
|
|
562
|
+
append(node("div"),node("div","exmethod",e.method||"No execution policy declared for this phase."),meta)));
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
function renderTelemetry(){
|
|
566
|
+
const t=DATA.telemetry,sec=$("#telemetry");if(!t){if(sec)sec.hidden=true;return;}
|
|
567
|
+
const fmtN=n=>n==null?"—":n>=1e6?(n/1e6).toFixed(1)+"M":n>=1e3?Math.round(n/1e3)+"k":String(n);
|
|
568
|
+
const fmtMs=ms=>{if(ms==null)return "—";const m=Math.round(ms/60000);return m>=60?(m/60).toFixed(1)+"h":m+"m";};
|
|
569
|
+
const bm=t.by_model||[],short=m=>String(m).replace(/^claude-/,"");
|
|
570
|
+
const modelCell=bm.length?(bm.length+" model"+(bm.length>1?"s":"")):(t.model?short(t.model):"—");
|
|
571
|
+
const cells=[["tokens in",fmtN(t.tokens_in)],["tokens out",fmtN(t.tokens_out)],["wall time",fmtMs(t.ms)],["model",modelCell],["effort",t.effort||"—"],["sessions",t.sessions==null?"—":String(t.sessions)]];
|
|
572
|
+
const row=$("#trow");row.replaceChildren();cells.forEach(([k,v])=>append(row,append(node("div","tcell"),node("div","tk",k),node("div","tv",v))));
|
|
573
|
+
const models=$("#tmodels");models.replaceChildren();
|
|
574
|
+
if(bm.length>1)bm.forEach(m=>append(models,append(node("div","tmrow"),node("span","tmn",short(m.model)),node("span","tmv",fmtN(m.tokens_in)+" → "+fmtN(m.tokens_out)))));
|
|
575
|
+
if(sec)sec.hidden=false;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/* ==== status story: cómo viene / qué lo traba / qué sigue — all from measured DATA ==== */
|
|
579
|
+
function renderStatus(){
|
|
580
|
+
const sec=$("#status");if(!sec)return;
|
|
581
|
+
/* CÓMO VIENE — the measured dimensions, verbatim from the ledger's sub-scores */
|
|
582
|
+
const how=$("#s-how");how.replaceChildren();
|
|
583
|
+
const subs=DATA.subscores||[];
|
|
584
|
+
if(subs.length){
|
|
585
|
+
subs.forEach((s,i)=>{
|
|
586
|
+
if(i)how.appendChild(document.createTextNode(" · "));
|
|
587
|
+
how.appendChild(node("b",null,s.k));
|
|
588
|
+
how.appendChild(document.createTextNode(" "+(s.bd==null?(s.val==null?"—":String(s.val)):s.bd)));
|
|
589
|
+
});
|
|
590
|
+
}else how.appendChild(node("span","muted","sin dimensiones medidas todavía"));
|
|
591
|
+
/* QUÉ LO TRABA — hard blockers only, each traceable to a measured field */
|
|
592
|
+
const bl=$("#s-block");bl.replaceChildren();
|
|
593
|
+
const blockers=[];
|
|
594
|
+
const cap=DATA.readiness&&DATA.readiness.sub;
|
|
595
|
+
if(cap)blockers.push("tope de readiness — "+String(cap).replace(/<[^>]+>/g,"").trim());
|
|
596
|
+
subs.forEach(s=>{const bd=String(s.bd||"").toUpperCase();
|
|
597
|
+
if(bd.includes("FAIL")||bd.includes("MISS"))blockers.push("gate "+s.k+": "+s.bd);});
|
|
598
|
+
// NB: a failing gate already flips its CONSTITUTION invariant, so we do NOT also list the
|
|
599
|
+
// invariant here -- that would double-report one fact. Invariant status has its own card.
|
|
600
|
+
const di=DATA.discovery_intake||{};
|
|
601
|
+
if(countOf(di.production_findings))blockers.push(countOf(di.production_findings)+" hallazgo(s) de producción abiertos");
|
|
602
|
+
if(countOf(di.spec_doubts))blockers.push(countOf(di.spec_doubts)+" duda(s) de spec abiertas");
|
|
603
|
+
if(countOf(di.spec_change_requests))blockers.push(countOf(di.spec_change_requests)+" cambio(s) de spec pendientes");
|
|
604
|
+
const ex=DATA.adr_experiments||{};
|
|
605
|
+
if(ex.expired)blockers.push(ex.expired+" experimento(s) ADR vencidos");
|
|
606
|
+
if(ex.malformed)blockers.push(ex.malformed+" experimento(s) ADR malformados");
|
|
607
|
+
const seen=new Set(),uniq=blockers.filter(b=>!seen.has(b)&&seen.add(b));
|
|
608
|
+
if(uniq.length){
|
|
609
|
+
uniq.forEach(b=>{const row=node("div","bk");append(row,node("i",null,"▸"),document.createTextNode(b));bl.appendChild(row);});
|
|
610
|
+
}else{
|
|
611
|
+
const unmeasured=subs.filter(s=>s.val==null||s.val===0).map(s=>s.k);
|
|
612
|
+
const row=node("div","bk ok");
|
|
613
|
+
append(row,node("i",null,"✓"),document.createTextNode(
|
|
614
|
+
unmeasured.length?"nada bloquea — el score sube con evidencia medida en: "+unmeasured.join(", "):"nada bloquea"));
|
|
615
|
+
bl.appendChild(row);
|
|
616
|
+
}
|
|
617
|
+
/* QUÉ SIGUE — the active phase in the FSM + its method and execution hint */
|
|
618
|
+
const nx=$("#s-next");nx.replaceChildren();
|
|
619
|
+
const ph=DATA.phases||[];
|
|
620
|
+
let idx=ph.findIndex(p=>p.status==="prog"||p.status==="block");
|
|
621
|
+
if(idx<0)idx=ph.findIndex(p=>p.status==="todo");
|
|
622
|
+
if(idx<0)idx=ph.length-1;
|
|
623
|
+
const p=ph[idx];
|
|
624
|
+
if(p){
|
|
625
|
+
const pol=(DATA.execution_policy&&DATA.execution_policy.phases)||{};
|
|
626
|
+
const e=p.execution||pol[p.key]||{};
|
|
627
|
+
const head=node("div","nmethod");
|
|
628
|
+
head.appendChild(node("b",null,String(p.phase||"")+" "+String(p.label||p.key||"").toUpperCase()));
|
|
629
|
+
if(e.method)head.appendChild(document.createTextNode(" — "+e.method));
|
|
630
|
+
nx.appendChild(head);
|
|
631
|
+
const meta=node("div","nmeta");
|
|
632
|
+
append(meta,node("span",null,"tier="+(e.tier||"-")),node("span",null,"model="+(e.model||"default")),node("span",null,"effort="+(e.effort||"-")));
|
|
633
|
+
if(e.uncorrelated)meta.appendChild(node("span",null,"uncorrelated"));
|
|
634
|
+
nx.appendChild(meta);
|
|
635
|
+
}else nx.appendChild(node("span","muted","—"));
|
|
636
|
+
sec.hidden=false;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
/* ==== evidence drawer ==== */
|
|
640
|
+
function openEvidence(key){
|
|
641
|
+
const e=DATA.evidence[key];if(!e)return;
|
|
642
|
+
$("#d-ey").textContent=e.ey;$("#d-title").textContent=e.title;$("#d-desc").textContent=e.desc;renderEvidencePre($("#d-pre"),e.pre);
|
|
643
|
+
$("#drawer").classList.add("open");$("#drawer").setAttribute("aria-hidden","false");$("#scrim").classList.add("open");
|
|
644
|
+
}
|
|
645
|
+
function closeDrawer(){$("#drawer").classList.remove("open");$("#drawer").setAttribute("aria-hidden","true");$("#scrim").classList.remove("open");}
|
|
646
|
+
$("#d-close").addEventListener("click",closeDrawer);$("#scrim").addEventListener("click",closeDrawer);
|
|
647
|
+
document.addEventListener("keydown",e=>{if(e.key==="Escape")closeDrawer();});
|
|
648
|
+
|
|
649
|
+
/* ==== time-lapse ==== */
|
|
650
|
+
const scrub=$("#scrub"),snaps=DATA.snapshots||[];
|
|
651
|
+
if(snaps.length){scrub.max=snaps.length-1;scrub.value=snaps.length-1;}else{scrub.max=0;scrub.value=0;scrub.disabled=true;}
|
|
652
|
+
function applySnapshot(i){
|
|
653
|
+
const s=snaps[i];if(!s)return;$("#asof").textContent=s.date;
|
|
654
|
+
if(i===snaps.length-1){paintVerdict(DATA.readiness.score,DATA.readiness.band,DATA.readiness.title,DATA.readiness.sub);renderTrail(null);}
|
|
655
|
+
else{const band=s.readiness>=95?"READY":s.readiness>=50?"IN PROGRESS":"NOT READY";
|
|
656
|
+
paintVerdict(s.readiness,band,"History","Reconstructed from the snapshot of "+s.date+" — the ledger keeps every run.");renderTrail(s.reached);}
|
|
657
|
+
}
|
|
658
|
+
scrub.addEventListener("input",()=>applySnapshot(+scrub.value));
|
|
659
|
+
|
|
660
|
+
/* ==== init ==== */
|
|
661
|
+
renderStatus();renderSpecs();renderAdrs();renderInv();renderHeat();renderLoops();renderSubs();renderExecutionPolicy();renderTelemetry();
|
|
662
|
+
$("#asof").textContent=snaps.length?snaps[snaps.length-1].date:"no history yet (readiness --record)";
|
|
663
|
+
renderTrail(null);
|
|
664
|
+
</script>
|
|
665
|
+
</body>
|
|
666
|
+
</html>
|