@andresmassello/uscha 1.40.1
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/LICENSE +21 -0
- package/README.md +104 -0
- package/bin/README.md +6 -0
- package/bin/uscha.js +28 -0
- package/package.json +38 -0
- package/uscha-kit/.claude/skills/uscha-adr-refine/SKILL.md +161 -0
- package/uscha-kit/.claude/skills/uscha-characterize/SKILL.md +124 -0
- package/uscha-kit/.claude/skills/uscha-devloop/SKILL.md +490 -0
- package/uscha-kit/.claude/skills/uscha-devloop/qa_ledger.py +5602 -0
- package/uscha-kit/.claude/skills/uscha-discovery/SKILL.md +161 -0
- package/uscha-kit/.claude/skills/uscha-mirador/SKILL.md +145 -0
- package/uscha-kit/.claude/skills/uscha-mirador/mirador-render.py +112 -0
- package/uscha-kit/.claude/skills/uscha-mirador/mirador-watch.ps1 +22 -0
- package/uscha-kit/.claude/skills/uscha-mirador/mirador-watch.sh +26 -0
- package/uscha-kit/.claude/skills/uscha-mirador/mirador.template.html +586 -0
- package/uscha-kit/.claude/skills/uscha-mirador/telemetry-extract.py +130 -0
- package/uscha-kit/.claude/skills/uscha-reverse-discovery/SKILL.md +116 -0
- package/uscha-kit/.claude/skills/uscha-rubric/SKILL.md +79 -0
- package/uscha-kit/.claude/skills/uscha-sysdoc/SKILL.md +88 -0
- package/uscha-kit/.claude-plugin/plugin.json +24 -0
- package/uscha-kit/.codex-plugin/plugin.json +37 -0
- package/uscha-kit/CHANGELOG-1.10.0.md +84 -0
- package/uscha-kit/CHANGELOG-1.11.0.md +67 -0
- package/uscha-kit/CHANGELOG-1.12.0.md +46 -0
- package/uscha-kit/CHANGELOG-1.13.0.md +33 -0
- package/uscha-kit/CHANGELOG-1.14.0.md +42 -0
- package/uscha-kit/CHANGELOG-1.15.0.md +58 -0
- package/uscha-kit/CHANGELOG-1.16.0.md +55 -0
- package/uscha-kit/CHANGELOG-1.17.0.md +44 -0
- package/uscha-kit/CHANGELOG-1.18.0.md +42 -0
- package/uscha-kit/CHANGELOG-1.19.0.md +41 -0
- package/uscha-kit/CHANGELOG-1.2.2.md +16 -0
- package/uscha-kit/CHANGELOG-1.2.3.md +20 -0
- package/uscha-kit/CHANGELOG-1.2.4.md +10 -0
- package/uscha-kit/CHANGELOG-1.2.5.md +23 -0
- package/uscha-kit/CHANGELOG-1.2.6.md +11 -0
- package/uscha-kit/CHANGELOG-1.2.7.md +15 -0
- package/uscha-kit/CHANGELOG-1.2.8.md +24 -0
- package/uscha-kit/CHANGELOG-1.2.9.md +4 -0
- package/uscha-kit/CHANGELOG-1.20.0.md +29 -0
- package/uscha-kit/CHANGELOG-1.21.0.md +33 -0
- package/uscha-kit/CHANGELOG-1.22.0.md +60 -0
- package/uscha-kit/CHANGELOG-1.23.0.md +75 -0
- package/uscha-kit/CHANGELOG-1.24.0.md +50 -0
- package/uscha-kit/CHANGELOG-1.25.0.md +55 -0
- package/uscha-kit/CHANGELOG-1.26.0.md +70 -0
- package/uscha-kit/CHANGELOG-1.27.0.md +45 -0
- package/uscha-kit/CHANGELOG-1.28.0.md +35 -0
- package/uscha-kit/CHANGELOG-1.29.0.md +20 -0
- package/uscha-kit/CHANGELOG-1.3.0.md +74 -0
- package/uscha-kit/CHANGELOG-1.30.0.md +46 -0
- package/uscha-kit/CHANGELOG-1.31.0.md +59 -0
- package/uscha-kit/CHANGELOG-1.32.0.md +50 -0
- package/uscha-kit/CHANGELOG-1.33.0.md +46 -0
- package/uscha-kit/CHANGELOG-1.34.0.md +55 -0
- package/uscha-kit/CHANGELOG-1.35.0.md +30 -0
- package/uscha-kit/CHANGELOG-1.36.0.md +33 -0
- package/uscha-kit/CHANGELOG-1.37.0.md +41 -0
- package/uscha-kit/CHANGELOG-1.38.0.md +11 -0
- package/uscha-kit/CHANGELOG-1.39.0.md +14 -0
- package/uscha-kit/CHANGELOG-1.4.0.md +68 -0
- package/uscha-kit/CHANGELOG-1.40.0.md +16 -0
- package/uscha-kit/CHANGELOG-1.40.1.md +11 -0
- package/uscha-kit/CHANGELOG-1.5.0.md +64 -0
- package/uscha-kit/CHANGELOG-1.6.0.md +57 -0
- package/uscha-kit/CHANGELOG-1.7.0.md +74 -0
- package/uscha-kit/CHANGELOG-1.8.0.md +46 -0
- package/uscha-kit/CHANGELOG-1.9.0.md +112 -0
- package/uscha-kit/LICENSE +21 -0
- package/uscha-kit/README.md +497 -0
- package/uscha-kit/VERSION +1 -0
- package/uscha-kit/WORKBENCH.md +178 -0
- package/uscha-kit/hooks/block-approved-writes.ps1 +46 -0
- package/uscha-kit/hooks/hooks.json +15 -0
- package/uscha-kit/install-uscha.py +344 -0
- package/uscha-kit/skills/uscha-adr-refine/SKILL.md +161 -0
- package/uscha-kit/skills/uscha-characterize/SKILL.md +124 -0
- package/uscha-kit/skills/uscha-devloop/SKILL.md +490 -0
- package/uscha-kit/skills/uscha-devloop/qa_ledger.py +5602 -0
- package/uscha-kit/skills/uscha-discovery/SKILL.md +161 -0
- package/uscha-kit/skills/uscha-mirador/SKILL.md +145 -0
- package/uscha-kit/skills/uscha-mirador/mirador-render.py +112 -0
- package/uscha-kit/skills/uscha-mirador/mirador-watch.ps1 +22 -0
- package/uscha-kit/skills/uscha-mirador/mirador-watch.sh +26 -0
- package/uscha-kit/skills/uscha-mirador/mirador.template.html +586 -0
- package/uscha-kit/skills/uscha-mirador/telemetry-extract.py +130 -0
- package/uscha-kit/skills/uscha-reverse-discovery/SKILL.md +116 -0
- package/uscha-kit/skills/uscha-rubric/SKILL.md +79 -0
- package/uscha-kit/skills/uscha-sysdoc/SKILL.md +88 -0
- package/uscha-kit/templates/.gitattributes +6 -0
- package/uscha-kit/templates/CLAUDE.md +56 -0
- package/uscha-kit/templates/CONSTITUTION.md +149 -0
- package/uscha-kit/templates/RUBRIC.md +38 -0
- package/uscha-kit/templates/docs/adr/README.md +19 -0
- package/uscha-kit/templates/rubric-grader-prompt.md +63 -0
- package/uscha-kit/tests/smoke-engine.sh +1739 -0
- package/uscha-kit/uscha.config.json +181 -0
- package/uscha-kit/workbench-doctor.sh +45 -0
|
@@ -0,0 +1,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
|
+
</style>
|
|
158
|
+
</head>
|
|
159
|
+
<body>
|
|
160
|
+
|
|
161
|
+
<header class="topbar"><div class="wrap">
|
|
162
|
+
<div class="brand"><span class="dot"></span>uscha<span class="cmd">/mirador</span></div>
|
|
163
|
+
<div class="meta"><b id="m-proj">—</b> · generated <b id="m-ts">—</b></div>
|
|
164
|
+
</div></header>
|
|
165
|
+
|
|
166
|
+
<main class="wrap">
|
|
167
|
+
|
|
168
|
+
<section class="hero">
|
|
169
|
+
<div class="projtitle" id="projtitle"></div>
|
|
170
|
+
<div class="verdict">
|
|
171
|
+
<span class="band" id="v-band">—</span>
|
|
172
|
+
<h1 id="v-title">—</h1>
|
|
173
|
+
<div class="sub" id="v-sub">—</div>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div class="trailcard">
|
|
177
|
+
<div class="cap">
|
|
178
|
+
<span>The trail — from idea to production</span>
|
|
179
|
+
<span class="legend">
|
|
180
|
+
<span><i style="background:var(--green)"></i>verified</span>
|
|
181
|
+
<span><i style="background:var(--amber)"></i>in progress</span>
|
|
182
|
+
<span><i style="background:var(--red)"></i>blocked</span>
|
|
183
|
+
<span><i style="background:#cfd5db"></i>to do</span>
|
|
184
|
+
</span>
|
|
185
|
+
</div>
|
|
186
|
+
<svg id="trail" viewBox="0 0 1060 300" role="img" aria-label="Project trail"></svg>
|
|
187
|
+
<div class="lapse">
|
|
188
|
+
<label for="scrub">Time-lapse</label>
|
|
189
|
+
<input type="range" id="scrub" min="0" max="0" value="0" step="1" aria-label="Replay the project's evolution">
|
|
190
|
+
<span class="asof">as of <b id="asof">—</b></span>
|
|
191
|
+
</div>
|
|
192
|
+
</div>
|
|
193
|
+
</section>
|
|
194
|
+
|
|
195
|
+
<section class="grid">
|
|
196
|
+
<div class="card col6">
|
|
197
|
+
<h2>Specifications <span class="n" id="spec-n">—</span></h2>
|
|
198
|
+
<div class="speclist" id="speclist"></div>
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
<div class="card col6">
|
|
202
|
+
<h2>ADRs <span class="n" id="adr-n">—</span></h2>
|
|
203
|
+
<div class="speclist" id="adrlist"></div>
|
|
204
|
+
</div>
|
|
205
|
+
|
|
206
|
+
<div class="card col6">
|
|
207
|
+
<h2>CONSTITUTION — invariants</h2>
|
|
208
|
+
<div class="inv" id="inv"></div>
|
|
209
|
+
</div>
|
|
210
|
+
|
|
211
|
+
<div class="card col6">
|
|
212
|
+
<h2>The 6 layers of truth</h2>
|
|
213
|
+
<div class="heat" id="heat"></div>
|
|
214
|
+
</div>
|
|
215
|
+
|
|
216
|
+
<div class="card col6">
|
|
217
|
+
<h2>QA loops <span class="n" id="qa-n">—</span></h2>
|
|
218
|
+
<div id="loops"></div>
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
<div class="card col6">
|
|
222
|
+
<h2>Execution policy <span class="n" id="exec-n">—</span></h2>
|
|
223
|
+
<div class="exec" id="exec"></div>
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
<div class="card col6">
|
|
227
|
+
<details class="subs">
|
|
228
|
+
<summary>Sub-scores (drill-down)</summary>
|
|
229
|
+
<div class="subrow" id="subrow"></div>
|
|
230
|
+
</details>
|
|
231
|
+
<p style="font-size:.8rem;color:var(--ink-mute);margin-top:.9rem;line-height:1.5">
|
|
232
|
+
The verdict above is all you need to look at. These roll up into it; they're here if you want to dig.
|
|
233
|
+
</p>
|
|
234
|
+
</div>
|
|
235
|
+
</section>
|
|
236
|
+
|
|
237
|
+
<section class="telemetry" id="telemetry" hidden>
|
|
238
|
+
<div class="tlabel">Claude Code · session telemetry <span>vendor-reported — narrated, not measured</span></div>
|
|
239
|
+
<div class="trow" id="trow"></div>
|
|
240
|
+
<div class="tmodels" id="tmodels"></div>
|
|
241
|
+
</section>
|
|
242
|
+
|
|
243
|
+
<div class="foot">
|
|
244
|
+
generated by <b>qa_ledger.py dashboard --json</b> — read-only, evidence rules · click a milestone to see its receipt<br>
|
|
245
|
+
the tool executes · the method governs · evidence decides · the human approves
|
|
246
|
+
</div>
|
|
247
|
+
</main>
|
|
248
|
+
|
|
249
|
+
<div class="scrim" id="scrim"></div>
|
|
250
|
+
<aside class="drawer" id="drawer" aria-hidden="true">
|
|
251
|
+
<div class="dh">
|
|
252
|
+
<div><div class="ey" id="d-ey">receipt</div><h3 id="d-title">—</h3><p id="d-desc">—</p></div>
|
|
253
|
+
<button class="x" id="d-close" aria-label="Close">✕</button>
|
|
254
|
+
</div>
|
|
255
|
+
<div class="receipt"><div class="rt">evidence captured by execution</div><pre id="d-pre"></pre></div>
|
|
256
|
+
</aside>
|
|
257
|
+
|
|
258
|
+
<script>
|
|
259
|
+
/* ==== DATA — sample output of `qa_ledger.py dashboard --json` (deterministic) ==== */
|
|
260
|
+
/*MIRADOR_DATA_START*/
|
|
261
|
+
const DATA = {
|
|
262
|
+
project: "Fiscal Platform",
|
|
263
|
+
generated: "2026-07-05 14:22 UTC",
|
|
264
|
+
readiness: { score:88, band:"CASI LISTO",
|
|
265
|
+
title:"Casi listo para release",
|
|
266
|
+
sub:"No sale todavía — <b>1 blocker: falta validar el rollback en producción</b> (capa Operacional en rojo)." },
|
|
267
|
+
subscores:[
|
|
268
|
+
{k:"simplicity",val:92,bd:"SIMPLE"},
|
|
269
|
+
{k:"waste",val:78,bd:"ACEPTABLE"},
|
|
270
|
+
{k:"rebuild",val:90,bd:"COVERS"},
|
|
271
|
+
{k:"golden",val:100,bd:"OK"},
|
|
272
|
+
{k:"coverage",val:81,bd:"81%"},
|
|
273
|
+
],
|
|
274
|
+
phases:[
|
|
275
|
+
{key:"idea", label:"Idea", phase:"00", status:"done", count:"", risk:0},
|
|
276
|
+
{key:"disc", label:"Discovery", phase:"01", status:"done", count:"seed + ready", risk:0},
|
|
277
|
+
{key:"spec", label:"SPEC", phase:"02", status:"done", count:"6/7 specs", risk:1},
|
|
278
|
+
{key:"adr", label:"ADR · CONST", phase:"03", status:"done", count:"4 ADR · 6 inv", risk:1},
|
|
279
|
+
{key:"build", label:"Build", phase:"04", status:"prog", count:"5/7 módulos", risk:2},
|
|
280
|
+
{key:"qa", label:"QA loop", phase:"05", status:"prog", count:"3 loops · 1 escalado", risk:2},
|
|
281
|
+
{key:"verify", label:"Verify", phase:"06", status:"block", count:"1 blocker", risk:2},
|
|
282
|
+
{key:"prod", label:"Producción", phase:"07", status:"todo", count:"human gate", risk:3},
|
|
283
|
+
],
|
|
284
|
+
execution_policy:{source:"sample",default:{tier:"standard",model:null,effort:"medium"},phases:{
|
|
285
|
+
idea:{phase:"idea",method:"Capture the intent",tier:"human",model:null,effort:"none"},
|
|
286
|
+
disc:{phase:"disc",method:"Shape discovery facts and risks",tier:"deep",model:null,effort:"high"},
|
|
287
|
+
spec:{phase:"spec",method:"Write testable SPEC/ACCEPTANCE",tier:"deep",model:null,effort:"high"},
|
|
288
|
+
adr:{phase:"adr",method:"Record decisions and invariants",tier:"deep",model:null,effort:"high"},
|
|
289
|
+
build:{phase:"build",method:"Implement the accepted plan",tier:"standard",model:null,effort:"medium"},
|
|
290
|
+
qa:{phase:"qa",method:"Run maker-not-checker QA",tier:"checker",model:null,effort:"high",uncorrelated:true},
|
|
291
|
+
verify:{phase:"verify",method:"Re-run measured release gates",tier:"standard",model:null,effort:"medium"},
|
|
292
|
+
prod:{phase:"prod",method:"Stop at the human gate",tier:"human",model:null,effort:"none"}
|
|
293
|
+
}},
|
|
294
|
+
specs:[
|
|
295
|
+
{id:"SPEC-001",t:"Alta de comprobante",status:"done",acc:100},
|
|
296
|
+
{id:"SPEC-002",t:"Numeración sin huecos",status:"done",acc:100},
|
|
297
|
+
{id:"SPEC-003",t:"Reintento idempotente",status:"done",acc:100},
|
|
298
|
+
{id:"SPEC-004",t:"Cierre de caja",status:"prog",acc:70},
|
|
299
|
+
{id:"SPEC-005",t:"Exportación fiscal",status:"prog",acc:55},
|
|
300
|
+
{id:"SPEC-006",t:"Rollback de despliegue",status:"block",acc:20},
|
|
301
|
+
{id:"SPEC-007",t:"Reporte de conciliación",status:"todo",acc:0},
|
|
302
|
+
],
|
|
303
|
+
adrs:[
|
|
304
|
+
{id:"ADR-001",t:"Almacenamiento append-only",status:"done"},
|
|
305
|
+
{id:"ADR-002",t:"Idempotencia por request-id",status:"done"},
|
|
306
|
+
{id:"ADR-003",t:"Cola de reintentos con backoff",status:"done"},
|
|
307
|
+
{id:"ADR-004",t:"Estrategia de rollback",status:"prog"},
|
|
308
|
+
],
|
|
309
|
+
inv:[
|
|
310
|
+
{name:"Seguridad",status:"ok"},{name:"Dominio",status:"ok"},
|
|
311
|
+
{name:"Simplicidad",status:"ok"},{name:"Reuso",status:"warn"},
|
|
312
|
+
{name:"Golden",status:"ok"},{name:"Tests efectivos",status:"ok"},
|
|
313
|
+
{name:"Integridad del gate",status:"ok"},
|
|
314
|
+
],
|
|
315
|
+
capas:[
|
|
316
|
+
{name:"Funcional",status:"ok",v:"cumple SPEC"},
|
|
317
|
+
{name:"Técnica",status:"ok",v:"tests + lint"},
|
|
318
|
+
{name:"Regresiva",status:"ok",v:"sin romper"},
|
|
319
|
+
{name:"Contractual",status:"warn",v:"1 schema drift"},
|
|
320
|
+
{name:"Seguridad",status:"ok",v:"0 críticos"},
|
|
321
|
+
{name:"Operacional",status:"miss",v:"rollback sin validar"},
|
|
322
|
+
],
|
|
323
|
+
loops:[
|
|
324
|
+
{mod:"pagos",iters:4,max:6,state:"converged"},
|
|
325
|
+
{mod:"ticket",iters:6,max:6,state:"oscillating"},
|
|
326
|
+
{mod:"auth",iters:2,max:6,state:"escalated"},
|
|
327
|
+
],
|
|
328
|
+
/* time-lapse: cada snapshot = readiness + hasta qué fase se había llegado */
|
|
329
|
+
snapshots:[
|
|
330
|
+
{date:"jun 18",readiness:12,reached:1},
|
|
331
|
+
{date:"jun 23",readiness:34,reached:2},
|
|
332
|
+
{date:"jun 27",readiness:51,reached:3},
|
|
333
|
+
{date:"jul 01",readiness:69,reached:4},
|
|
334
|
+
{date:"jul 03",readiness:80,reached:5},
|
|
335
|
+
{date:"jul 05",readiness:88,reached:7},
|
|
336
|
+
],
|
|
337
|
+
telemetry:{ source:"Claude Code", sessions:3, model:"claude-opus-4-8", effort:"high",
|
|
338
|
+
tokens_in:284000, tokens_out:47000, ms:5400000,
|
|
339
|
+
by_model:[{model:"claude-opus-4-8",tokens_in:240000,tokens_out:41000},{model:"claude-haiku-4-5",tokens_in:44000,tokens_out:6000}] },
|
|
340
|
+
evidence:{
|
|
341
|
+
verify:{ey:"gate · verify",title:"Verify — 1 blocker",desc:"Capa Operacional en rojo: el rollback no se validó en un entorno real.",
|
|
342
|
+
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"},
|
|
343
|
+
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.",
|
|
344
|
+
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>"},
|
|
345
|
+
build:{ey:"fase · build",title:"Build — 5/7 módulos",desc:"Construcción contra el plan, commit a commit. Dos módulos en curso.",
|
|
346
|
+
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)"},
|
|
347
|
+
qa:{ey:"fase · qa loop",title:"QA loop — 3 loops",desc:"Iteración maker≠checker por módulo hasta converger. Uno escaló a humano.",
|
|
348
|
+
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>"},
|
|
349
|
+
spec:{ey:"fase · spec",title:"SPEC — 6/7",desc:"Qué debe pasar, verificable. Una spec bloqueada (rollback).",
|
|
350
|
+
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%)"},
|
|
351
|
+
disc:{ey:"fase · discovery",title:"Discovery — ready",desc:"De la idea a la forma del sistema. Vos decidiste el alcance.",
|
|
352
|
+
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"},
|
|
353
|
+
adr:{ey:"fase · adr · constitution",title:"ADR · CONSTITUTION",desc:"Por qué esta forma, y qué nunca se viola.",
|
|
354
|
+
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>"},
|
|
355
|
+
idea:{ey:"origen",title:"Idea",desc:"El punto de partida. Traés la idea; el método la cruza.",
|
|
356
|
+
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.\""},
|
|
357
|
+
},
|
|
358
|
+
};
|
|
359
|
+
/*MIRADOR_DATA_END*/
|
|
360
|
+
|
|
361
|
+
/* ==== helpers ==== */
|
|
362
|
+
const $ = s => document.querySelector(s);
|
|
363
|
+
const statusColor = {done:"var(--green)",prog:"var(--amber)",block:"var(--red)",todo:"#cfd5db"};
|
|
364
|
+
const chipClass = {done:"st-done",prog:"st-prog",block:"st-block",todo:"st-todo"};
|
|
365
|
+
const chipText = {done:"done",prog:"in progress",block:"blocked",todo:"to do"};
|
|
366
|
+
|
|
367
|
+
/* ==== header + verdict ==== */
|
|
368
|
+
$("#m-proj").textContent = DATA.project;
|
|
369
|
+
$("#projtitle").textContent = DATA.project || "—";
|
|
370
|
+
$("#m-ts").textContent = DATA.generated;
|
|
371
|
+
const bandColors = {"CASI LISTO":["var(--amber-soft)","var(--amber)"],"LISTO":["var(--green-soft)","var(--green)"],"NO LISTO":["var(--red-soft)","var(--red-deep)"],
|
|
372
|
+
"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)"]};
|
|
373
|
+
function paintVerdict(score, band, title, sub){
|
|
374
|
+
const b=$("#v-band"); const c=bandColors[band]||["#eef1f4","var(--ink-mute)"];
|
|
375
|
+
b.textContent=band; b.style.background=c[0]; b.style.color=c[1];
|
|
376
|
+
$("#v-title").innerHTML = (title||"") + ' <span class="score">'+score+'/100</span>';
|
|
377
|
+
$("#v-sub").innerHTML = sub||"";
|
|
378
|
+
}
|
|
379
|
+
paintVerdict(DATA.readiness.score, DATA.readiness.band, DATA.readiness.title, DATA.readiness.sub);
|
|
380
|
+
|
|
381
|
+
/* ==== el sendero (SVG) ==== */
|
|
382
|
+
const VB={w:1060,h:300}, base=232, pad=70;
|
|
383
|
+
const riskY=[0,26,60,92]; // más riesgo = más alto (paso de montaña)
|
|
384
|
+
const N=DATA.phases.length;
|
|
385
|
+
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];}
|
|
386
|
+
function smoothPath(pts){
|
|
387
|
+
let d="M "+pts[0][0]+" "+pts[0][1];
|
|
388
|
+
for(let i=0;i<pts.length-1;i++){
|
|
389
|
+
const [x0,y0]=pts[i],[x1,y1]=pts[i+1];const mx=(x0+x1)/2;
|
|
390
|
+
d+=" C "+mx+" "+y0+" "+mx+" "+y1+" "+x1+" "+y1;
|
|
391
|
+
}
|
|
392
|
+
return d;
|
|
393
|
+
}
|
|
394
|
+
function renderTrail(activeReached){
|
|
395
|
+
const svg=$("#trail"); svg.innerHTML="";
|
|
396
|
+
const pts=DATA.phases.map((p,i)=>nodeXY(i,p.risk));
|
|
397
|
+
const ns="http://www.w3.org/2000/svg";
|
|
398
|
+
// terreno (silueta) + contornos suaves
|
|
399
|
+
const terr=document.createElementNS(ns,"path");
|
|
400
|
+
terr.setAttribute("d", smoothPath(pts)+" L "+pts[N-1][0]+" "+VB.h+" L "+pts[0][0]+" "+VB.h+" Z");
|
|
401
|
+
terr.setAttribute("fill","var(--terrain)"); terr.setAttribute("opacity",".4"); svg.appendChild(terr);
|
|
402
|
+
for(let k=1;k<=3;k++){
|
|
403
|
+
const c=document.createElementNS(ns,"path");
|
|
404
|
+
c.setAttribute("d", smoothPath(pts.map(([x,y])=>[x,y+k*14])));
|
|
405
|
+
c.setAttribute("fill","none");c.setAttribute("stroke","var(--terrain-line)");c.setAttribute("stroke-width","1");c.setAttribute("opacity",".5");
|
|
406
|
+
svg.appendChild(c);
|
|
407
|
+
}
|
|
408
|
+
// trail line
|
|
409
|
+
const trail=document.createElementNS(ns,"path");
|
|
410
|
+
trail.setAttribute("d",smoothPath(pts));trail.setAttribute("fill","none");
|
|
411
|
+
trail.setAttribute("stroke","var(--stone)");trail.setAttribute("stroke-width","3");
|
|
412
|
+
trail.setAttribute("stroke-dasharray","2 7");trail.setAttribute("stroke-linecap","round");trail.setAttribute("opacity",".8");
|
|
413
|
+
svg.appendChild(trail);
|
|
414
|
+
// mojones
|
|
415
|
+
DATA.phases.forEach((p,i)=>{
|
|
416
|
+
const [x,y]=pts[i];
|
|
417
|
+
// estado efectivo según time-lapse
|
|
418
|
+
let st=p.status;
|
|
419
|
+
if(activeReached!==null){
|
|
420
|
+
if(i<activeReached) st="done";
|
|
421
|
+
else if(i===activeReached) st=(p.status==="block"?"block":"prog");
|
|
422
|
+
else st="todo";
|
|
423
|
+
}
|
|
424
|
+
const g=document.createElementNS(ns,"g");
|
|
425
|
+
g.setAttribute("class","moj");g.setAttribute("tabindex","0");
|
|
426
|
+
g.setAttribute("role","button");
|
|
427
|
+
g.setAttribute("aria-label",p.label+" — "+chipText[st]);
|
|
428
|
+
g.dataset.key=p.key;
|
|
429
|
+
// halo estado
|
|
430
|
+
const halo=document.createElementNS(ns,"circle");
|
|
431
|
+
halo.setAttribute("cx",x);halo.setAttribute("cy",y);halo.setAttribute("r","18");
|
|
432
|
+
halo.setAttribute("fill",statusColor[st]);halo.setAttribute("opacity",".16");
|
|
433
|
+
if(st==="prog"||st==="block")halo.setAttribute("class","pulse");
|
|
434
|
+
g.appendChild(halo);
|
|
435
|
+
// piedra (mojón)
|
|
436
|
+
const stone=document.createElementNS(ns,"path");
|
|
437
|
+
const s=11;
|
|
438
|
+
stone.setAttribute("d",`M ${x-s} ${y+s} L ${x-s+2} ${y-s+3} Q ${x} ${y-s-3} ${x+s-2} ${y-s+3} L ${x+s} ${y+s} Z`);
|
|
439
|
+
stone.setAttribute("class","stone");
|
|
440
|
+
stone.setAttribute("fill", st==="todo"?"#eef1f4":"#fff");
|
|
441
|
+
stone.setAttribute("stroke",statusColor[st]);stone.setAttribute("stroke-width","2.5");
|
|
442
|
+
g.appendChild(stone);
|
|
443
|
+
// dot central
|
|
444
|
+
const dot=document.createElementNS(ns,"circle");
|
|
445
|
+
dot.setAttribute("cx",x);dot.setAttribute("cy",y);dot.setAttribute("r","3.4");
|
|
446
|
+
dot.setAttribute("fill",statusColor[st]);g.appendChild(dot);
|
|
447
|
+
// textos
|
|
448
|
+
const ph=document.createElementNS(ns,"text");
|
|
449
|
+
ph.setAttribute("x",x);ph.setAttribute("y",y-24);ph.setAttribute("text-anchor","middle");
|
|
450
|
+
ph.setAttribute("class","mphase");ph.textContent=p.phase;g.appendChild(ph);
|
|
451
|
+
const lab=document.createElementNS(ns,"text");
|
|
452
|
+
lab.setAttribute("x",x);lab.setAttribute("y",base+38);lab.setAttribute("text-anchor","middle");
|
|
453
|
+
lab.setAttribute("class","mlabel");lab.textContent=p.label;g.appendChild(lab);
|
|
454
|
+
if(p.count){
|
|
455
|
+
const cnt=document.createElementNS(ns,"text");
|
|
456
|
+
cnt.setAttribute("x",x);cnt.setAttribute("y",base+53);cnt.setAttribute("text-anchor","middle");
|
|
457
|
+
cnt.setAttribute("class","mcount");cnt.textContent=p.count;g.appendChild(cnt);
|
|
458
|
+
}
|
|
459
|
+
g.addEventListener("click",()=>openEvidence(p.key));
|
|
460
|
+
g.addEventListener("keydown",e=>{if(e.key==="Enter"||e.key===" "){e.preventDefault();openEvidence(p.key);}});
|
|
461
|
+
svg.appendChild(g);
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
/* ==== specs / adrs / inv / heat / loops / subs ==== */
|
|
466
|
+
function renderSpecs(){
|
|
467
|
+
$("#spec-n").textContent = DATA.specs.filter(s=>s.status==="done").length+"/"+DATA.specs.length;
|
|
468
|
+
$("#speclist").innerHTML = DATA.specs.map(s=>`
|
|
469
|
+
<div class="spec">
|
|
470
|
+
<span class="id">${s.id}</span>
|
|
471
|
+
<span class="t">${s.t}</span>
|
|
472
|
+
<span class="chip ${chipClass[s.status]}">${chipText[s.status]}</span>
|
|
473
|
+
<span class="acc">${s.acc}%</span>
|
|
474
|
+
</div>`).join("");
|
|
475
|
+
}
|
|
476
|
+
function adrLabel(a){
|
|
477
|
+
if(a.adr_status==="experiment"){
|
|
478
|
+
if(a.expired)return "experiment overdue";
|
|
479
|
+
if(a.experiment_valid===false)return "experiment invalid";
|
|
480
|
+
return "experiment";
|
|
481
|
+
}
|
|
482
|
+
if(a.adr_status)return a.adr_status;
|
|
483
|
+
return a.status==="done"?"accepted":a.status==="prog"?"proposed":chipText[a.status];
|
|
484
|
+
}
|
|
485
|
+
function renderAdrs(){
|
|
486
|
+
$("#adr-n").textContent = DATA.adrs.filter(a=>a.status==="done").length+"/"+DATA.adrs.length;
|
|
487
|
+
$("#adrlist").innerHTML = DATA.adrs.map(a=>`
|
|
488
|
+
<div class="spec">
|
|
489
|
+
<span class="id">${a.id}</span>
|
|
490
|
+
<span class="t">${a.t}</span>
|
|
491
|
+
<span class="chip ${chipClass[a.status]}">${adrLabel(a)}</span>
|
|
492
|
+
</div>`).join("");
|
|
493
|
+
}
|
|
494
|
+
function renderInv(){
|
|
495
|
+
$("#inv").innerHTML = DATA.inv.map(v=>`<span class="item ${v.status}"><i></i>${v.name}</span>`).join("");
|
|
496
|
+
}
|
|
497
|
+
function renderHeat(){
|
|
498
|
+
const cls={ok:"h-ok",warn:"h-warn",miss:"h-miss"};
|
|
499
|
+
$("#heat").innerHTML = DATA.capas.map(c=>`
|
|
500
|
+
<div class="cell ${cls[c.status]}"><div class="lab">${c.name}</div><div class="v">${c.v}</div></div>`).join("");
|
|
501
|
+
}
|
|
502
|
+
function renderLoops(){
|
|
503
|
+
$("#qa-n").textContent = DATA.loops.length;
|
|
504
|
+
const tag={converged:["st-done","converged"],oscillating:["st-prog","oscillating"],escalated:["st-block","escalated"],active:["st-prog","active"]};
|
|
505
|
+
$("#loops").innerHTML = DATA.loops.map(l=>{
|
|
506
|
+
const mx=l.max||l.iters||0;
|
|
507
|
+
let bars=""; for(let i=0;i<mx;i++)bars+=`<b class="${i<l.iters?'on':''}"></b>`;
|
|
508
|
+
const t=tag[l.state]||["st-todo","—"];
|
|
509
|
+
return `<div class="loop"><span class="mod">${l.mod}</span><span class="bar">${bars}</span><span class="tag ${t[0]}">${t[1]}</span></div>`;
|
|
510
|
+
}).join("");
|
|
511
|
+
}
|
|
512
|
+
function renderSubs(){
|
|
513
|
+
const band=v=>(v==null?"var(--ink-mute)":v>=85?"var(--green)":v>=65?"var(--amber)":"var(--red)");
|
|
514
|
+
$("#subrow").innerHTML = DATA.subscores.map(s=>`
|
|
515
|
+
<div class="s"><div class="k">${s.k}</div><div class="val" style="color:${band(s.val)}">${s.val==null?"—":s.val}</div><div class="bd">${s.bd==null?"":s.bd}</div></div>`).join("");
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function renderExecutionPolicy(){
|
|
519
|
+
const host=$("#exec"); if(!host)return;
|
|
520
|
+
const policy=DATA.execution_policy||{};
|
|
521
|
+
const phases=policy.phases||{};
|
|
522
|
+
const source=policy.source||"policy";
|
|
523
|
+
$("#exec-n").textContent=source.replace("config.defaults.","");
|
|
524
|
+
const rows=(DATA.phases||[]).map(p=>{
|
|
525
|
+
const e=p.execution||phases[p.key]||{};
|
|
526
|
+
const model=e.model||"default";
|
|
527
|
+
const tier=e.tier||"-";
|
|
528
|
+
const effort=e.effort||"-";
|
|
529
|
+
const method=e.method||"No execution policy declared for this phase.";
|
|
530
|
+
const unc=e.uncorrelated?`<span class="exchip warn">uncorrelated</span>`:"";
|
|
531
|
+
return `<div class="exrow"><div class="exphase">${p.phase||""} ${p.key}</div><div><div class="exmethod">${method}</div><div class="exmeta"><span class="exchip">tier=${tier}</span><span class="exchip">model=${model}</span><span class="exchip">effort=${effort}</span>${unc}</div></div></div>`;
|
|
532
|
+
}).join("");
|
|
533
|
+
host.innerHTML=rows||`<p style="color:var(--ink-mute);font-size:.82rem">No execution_policy in DATA.</p>`;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
function renderTelemetry(){
|
|
537
|
+
const t=DATA.telemetry, sec=$("#telemetry");
|
|
538
|
+
if(!t){ if(sec) sec.hidden=true; return; }
|
|
539
|
+
const fmtN=n=>n==null?"—":n>=1e6?(n/1e6).toFixed(1)+"M":n>=1e3?Math.round(n/1e3)+"k":String(n);
|
|
540
|
+
const fmtMs=ms=>{ if(ms==null)return "—"; const m=Math.round(ms/60000); return m>=60?(m/60).toFixed(1)+"h":m+"m"; };
|
|
541
|
+
const bm=t.by_model||[];
|
|
542
|
+
const short=m=>String(m).replace(/^claude-/,"");
|
|
543
|
+
const modelCell=bm.length?(bm.length+" model"+(bm.length>1?"s":"")):(t.model?short(t.model):"—");
|
|
544
|
+
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)]];
|
|
545
|
+
$("#trow").innerHTML=cells.map(([k,v])=>`<div class="tcell"><div class="tk">${k}</div><div class="tv">${v}</div></div>`).join("");
|
|
546
|
+
$("#tmodels").innerHTML = bm.length>1 ? bm.map(m=>`<div class="tmrow"><span class="tmn">${short(m.model)}</span><span class="tmv">${fmtN(m.tokens_in)} → ${fmtN(m.tokens_out)}</span></div>`).join("") : "";
|
|
547
|
+
if(sec) sec.hidden=false;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/* ==== evidence drawer ==== */
|
|
551
|
+
function openEvidence(key){
|
|
552
|
+
const e=DATA.evidence[key]; if(!e)return;
|
|
553
|
+
$("#d-ey").textContent=e.ey; $("#d-title").textContent=e.title; $("#d-desc").textContent=e.desc;
|
|
554
|
+
$("#d-pre").innerHTML=e.pre;
|
|
555
|
+
$("#drawer").classList.add("open"); $("#drawer").setAttribute("aria-hidden","false");
|
|
556
|
+
$("#scrim").classList.add("open");
|
|
557
|
+
}
|
|
558
|
+
function closeDrawer(){$("#drawer").classList.remove("open");$("#drawer").setAttribute("aria-hidden","true");$("#scrim").classList.remove("open");}
|
|
559
|
+
$("#d-close").addEventListener("click",closeDrawer);
|
|
560
|
+
$("#scrim").addEventListener("click",closeDrawer);
|
|
561
|
+
document.addEventListener("keydown",e=>{if(e.key==="Escape")closeDrawer();});
|
|
562
|
+
|
|
563
|
+
/* ==== time-lapse ==== */
|
|
564
|
+
const scrub=$("#scrub"); const snaps=DATA.snapshots||[];
|
|
565
|
+
if(snaps.length){ scrub.max=snaps.length-1; scrub.value=snaps.length-1; }
|
|
566
|
+
else { scrub.max=0; scrub.value=0; scrub.disabled=true; }
|
|
567
|
+
function applySnapshot(i){
|
|
568
|
+
const s=snaps[i]; if(!s) return;
|
|
569
|
+
$("#asof").textContent=s.date;
|
|
570
|
+
const isLatest = i===snaps.length-1;
|
|
571
|
+
if(isLatest){ paintVerdict(DATA.readiness.score,DATA.readiness.band,DATA.readiness.title,DATA.readiness.sub); renderTrail(null); }
|
|
572
|
+
else{
|
|
573
|
+
const band=s.readiness>=95?"READY":s.readiness>=50?"IN PROGRESS":"NOT READY";
|
|
574
|
+
paintVerdict(s.readiness,band,"History",'Reconstructed from the snapshot of <b>'+s.date+'</b> — the ledger keeps every run.');
|
|
575
|
+
renderTrail(s.reached);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
scrub.addEventListener("input",()=>applySnapshot(+scrub.value));
|
|
579
|
+
|
|
580
|
+
/* ==== init ==== */
|
|
581
|
+
renderSpecs(); renderAdrs(); renderInv(); renderHeat(); renderLoops(); renderSubs(); renderExecutionPolicy(); renderTelemetry();
|
|
582
|
+
$("#asof").textContent = snaps.length ? snaps[snaps.length-1].date : "no history yet (readiness --record)";
|
|
583
|
+
renderTrail(null);
|
|
584
|
+
</script>
|
|
585
|
+
</body>
|
|
586
|
+
</html>
|