tink-harness 1.9.3 → 1.9.4

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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tink",
3
3
  "description": "A small harness layer for Claude Code and Codex.",
4
- "version": "1.9.3",
4
+ "version": "1.9.4",
5
5
  "author": {
6
6
  "name": "dotori"
7
7
  }
package/CHANGELOG.md CHANGED
@@ -6,6 +6,17 @@ All notable changes to Tink are tracked here.
6
6
 
7
7
  No unreleased changes yet.
8
8
 
9
+ ## [1.9.4] - 2026-06-10
10
+
11
+ ### Added
12
+
13
+ - Harness cards now sort by usage and open with a smooth expand animation showing richer details: last used, success/failure counts, context cost, co-used harness chips, score factors, safe next action, and evidence handles.
14
+ - Added an evaluation & maintenance history section to the Harnesses tab, fed by `.tink/maintenance/ledger.jsonl` (new `maintenance_events` field in the lifecycle summary).
15
+
16
+ ### Fixed
17
+
18
+ - The lifecycle generator now strips a UTF-8 BOM before parsing JSONL files, so the first ledger entry is no longer dropped.
19
+
9
20
  ## [1.9.3] - 2026-06-10
10
21
 
11
22
  ### Added
package/VERSIONING.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Versioning
2
2
 
3
- Current version: `1.9.3`
3
+ Current version: `1.9.4`
4
4
 
5
5
  Tink follows semver from `1.0.0` onward.
6
6
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tink-harness",
3
- "version": "1.9.3",
3
+ "version": "1.9.4",
4
4
  "description": "Self-growing harnesses for Claude Code and Codex.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -15,7 +15,7 @@ function readJson(filePath, fallback) {
15
15
 
16
16
  function readLines(filePath) {
17
17
  if (!fs.existsSync(filePath)) return [];
18
- return fs.readFileSync(filePath, 'utf8').split(/\r?\n/).filter(Boolean);
18
+ return fs.readFileSync(filePath, 'utf8').replace(/^/, '').split(/\r?\n/).filter(Boolean);
19
19
  }
20
20
 
21
21
  function listFiles(dirPath, suffix) {
@@ -517,6 +517,28 @@ function summarize(root) {
517
517
  item.candidate_score = scoreCandidate(item);
518
518
  }
519
519
  const harnessSummaries = [...summaries.values()].sort((a, b) => a.id.localeCompare(b.id));
520
+
521
+ const ledgerPath = path.join(root, '.tink/maintenance/ledger.jsonl');
522
+ const knownHarnessIds = [...summaries.keys()];
523
+ const maintenanceEvents = parseJsonl(ledgerPath)
524
+ .map((entry) => {
525
+ const refs = [...(Array.isArray(entry.files) ? entry.files : []), ...(Array.isArray(entry.evidence) ? entry.evidence : []), String(entry.op_id || '')];
526
+ const related = knownHarnessIds.filter((id) =>
527
+ refs.some((ref) => String(ref).includes(`${id}.md`) || String(ref).includes(`harness:${id}`) || String(ref).includes(`-${id}-`))
528
+ ).sort();
529
+ return {
530
+ timestamp: entry.timestamp || '',
531
+ op_id: entry.op_id || '',
532
+ type: entry.type || 'unknown',
533
+ files: (Array.isArray(entry.files) ? entry.files : []).slice(0, 8),
534
+ result: entry.result || 'unknown',
535
+ approval: entry.approval || '',
536
+ harnesses: related
537
+ };
538
+ })
539
+ .sort((a, b) => String(b.timestamp).localeCompare(String(a.timestamp)))
540
+ .slice(0, 60);
541
+
520
542
  return {
521
543
  generated_at: new Date().toISOString(),
522
544
  run_window: {
@@ -530,11 +552,13 @@ function summarize(root) {
530
552
  '.tink/memory/*.md',
531
553
  '.tink/runs/*.md',
532
554
  '.tink/maintenance/weave-queue.json',
533
- '.tink/maintenance/friction.jsonl'
555
+ '.tink/maintenance/friction.jsonl',
556
+ '.tink/maintenance/ledger.jsonl'
534
557
  ],
535
558
  harnesses: harnessSummaries,
536
559
  graph: buildGraph(harnessSummaries),
537
- timeline: buildTimeline(runs, root)
560
+ timeline: buildTimeline(runs, root),
561
+ maintenance_events: maintenanceEvents
538
562
  };
539
563
  }
540
564
 
@@ -130,6 +130,19 @@ const COPY = {
130
130
  viewAll: 'View all',
131
131
  confidenceShort: 'Confidence',
132
132
  routingHelp: 'When cast routes a task to a visible-thinking overlay harness.',
133
+ lastUsed: 'Last used',
134
+ successes: 'Successes',
135
+ failures: 'Failures',
136
+ contextCost: 'Context cost',
137
+ coUsedWith: 'Often used with',
138
+ safeNextAction: 'Safe next action',
139
+ scoreFactors: 'Score factors',
140
+ viewInGraph: 'View in graph',
141
+ historyEyebrow: 'HISTORY',
142
+ historyTitle: 'Evaluation & maintenance history',
143
+ historyHelp: 'Approved reusable-state changes from the maintenance ledger, newest first.',
144
+ historyEmpty: 'No ledger history yet.',
145
+ sortNote: 'Sorted by usage',
133
146
  groups: [
134
147
  ['keep', 'Healthy harnesses', 'Ready to keep using'],
135
148
  ['weave', 'Weave candidates', 'Worth improving next'],
@@ -174,6 +187,19 @@ COPY.ko = {
174
187
  viewAll: '전체 보기',
175
188
  confidenceShort: '신뢰도',
176
189
  routingHelp: 'cast가 생각 보조 overlay 하네스로 라우팅하는 기준입니다.',
190
+ lastUsed: '마지막 사용',
191
+ successes: '성공',
192
+ failures: '실패',
193
+ contextCost: '컨텍스트 비용',
194
+ coUsedWith: '함께 쓰인 하네스',
195
+ safeNextAction: '다음 안전 행동',
196
+ scoreFactors: '점수 요인',
197
+ viewInGraph: '그래프에서 보기',
198
+ historyEyebrow: '히스토리',
199
+ historyTitle: '평가·생성 히스토리',
200
+ historyHelp: '유지보수 장부에 기록된 승인 변경 이력을 최신순으로 보여줍니다.',
201
+ historyEmpty: '아직 장부 기록이 없습니다.',
202
+ sortNote: '사용량 순 정렬',
177
203
  navLabel: '탐색',
178
204
  operator: '작업자',
179
205
  online: 'Tink 온라인',
@@ -860,26 +886,81 @@ function renderSelectedPanel(harnesses, copy) {
860
886
  function renderHarness(item, copy) {
861
887
  const signals = item.signals || {};
862
888
  const score = Number(item.candidate_score?.total || 0);
889
+ const factors = (item.candidate_score?.factors || []).slice(0, 5);
890
+ const coUsed = (signals.co_used_with || []).slice(0, 5);
891
+ const reason = normalizeReason(item.reason, copy);
863
892
  return `
864
- <article class="harness-card ${recommendationClass(item.recommendation)}" data-harness-id="${escapeAttr(item.id)}" data-recommendation="${escapeAttr(item.recommendation || 'unknown')}" tabindex="0" role="button">
865
- <div>
866
- <p class="eyebrow">${escapeHtml(renderCopyValue(item.recommendation, copy))}</p>
867
- <h3>${escapeHtml(item.id)}</h3>
893
+ <article class="harness-card ${recommendationClass(item.recommendation)}" data-harness-id="${escapeAttr(item.id)}" data-recommendation="${escapeAttr(item.recommendation || 'unknown')}">
894
+ <button class="harness-summary" type="button" aria-expanded="false">
895
+ <div>
896
+ <p class="eyebrow">${escapeHtml(renderCopyValue(item.recommendation, copy))}</p>
897
+ <h3>${escapeHtml(item.id)}</h3>
898
+ </div>
899
+ <div class="harness-mini">
900
+ <span>${escapeHtml(copy.uses)} ${escapeHtml(signals.uses ?? 0)}</span>
901
+ <strong>${escapeHtml(score)}</strong>
902
+ </div>
903
+ <span class="chevron" aria-hidden="true"></span>
904
+ </button>
905
+ <div class="harness-detail">
906
+ <div class="harness-detail-inner">
907
+ ${reason ? `<p class="harness-reason">${escapeHtml(reason)}</p>` : ''}
908
+ <dl>
909
+ <div><dt>${escapeHtml(copy.lifecycleState)}</dt><dd>${escapeHtml(renderCopyValue(item.lifecycle_state, copy))}</dd></div>
910
+ <div><dt>${escapeHtml(copy.lastUsed || 'Last used')}</dt><dd>${escapeHtml(signals.last_used ? shortDate(signals.last_used) : renderCopyValue('', copy))}</dd></div>
911
+ <div><dt>${escapeHtml(copy.successes || 'Successes')}</dt><dd>${escapeHtml(signals.successes ?? 0)}</dd></div>
912
+ <div><dt>${escapeHtml(copy.failures || 'Failures')}</dt><dd>${escapeHtml(signals.failures ?? 0)}</dd></div>
913
+ <div><dt>${escapeHtml(copy.blocked)}</dt><dd>${escapeHtml(signals.blocked ?? 0)}</dd></div>
914
+ <div><dt>${escapeHtml(copy.contextCost || 'Context cost')}</dt><dd>${escapeHtml(renderCopyValue(signals.context_cost, copy))}</dd></div>
915
+ </dl>
916
+ ${coUsed.length ? `
917
+ <p class="detail-label">${escapeHtml(copy.coUsedWith || 'Often used with')}</p>
918
+ <div class="co-used-chips">${coUsed.map((related) => `<span class="co-chip">${escapeHtml(related.id)} ×${escapeHtml(related.count)}</span>`).join('')}</div>
919
+ ` : ''}
920
+ ${factors.length ? `
921
+ <p class="detail-label">${escapeHtml(copy.scoreFactors || 'Score factors')}</p>
922
+ <ul class="factor-list">${factors.map((factor) => `<li><span>${escapeHtml(factor.name)}</span><strong>${escapeHtml(factor.points ?? factor.value ?? '')}</strong></li>`).join('')}</ul>
923
+ ` : ''}
924
+ ${item.safe_next_action ? `
925
+ <p class="detail-label">${escapeHtml(copy.safeNextAction || 'Safe next action')}</p>
926
+ <p class="harness-next">${escapeHtml(item.safe_next_action)}</p>
927
+ ` : ''}
928
+ <p class="detail-label">${escapeHtml(copy.evidenceHandles)} (${escapeHtml(String((item.evidence_handles || []).length))})</p>
929
+ <ul class="evidence-list">${renderEvidence(item.evidence_handles, copy)}</ul>
930
+ <button class="link-button" type="button" data-select-harness="${escapeAttr(item.id)}">${escapeHtml(copy.viewInGraph || 'View in graph')} →</button>
931
+ </div>
868
932
  </div>
869
- <strong>${escapeHtml(score)}</strong>
870
- <dl>
871
- <div><dt>${escapeHtml(copy.lifecycleState)}</dt><dd>${escapeHtml(renderCopyValue(item.lifecycle_state, copy))}</dd></div>
872
- <div><dt>${escapeHtml(copy.uses)}</dt><dd>${escapeHtml(signals.uses ?? 0)}</dd></div>
873
- <div><dt>${escapeHtml(copy.blocked)}</dt><dd>${escapeHtml(signals.blocked ?? 0)}</dd></div>
874
- </dl>
875
- <details>
876
- <summary>${escapeHtml(copy.evidenceHandles)} (${escapeHtml(String((item.evidence_handles || []).length))})</summary>
877
- <ul>${renderEvidence(item.evidence_handles, copy)}</ul>
878
- </details>
879
933
  </article>
880
934
  `;
881
935
  }
882
936
 
937
+ function renderHistorySection(events = [], copy) {
938
+ const items = Array.isArray(events) ? events.slice(0, 30) : [];
939
+ return `
940
+ <section class="history-section">
941
+ <div class="panel-title">
942
+ <p class="eyebrow">${escapeHtml(copy.historyEyebrow || 'HISTORY')}</p>
943
+ <h2>${escapeHtml(copy.historyTitle || 'Evaluation & maintenance history')}</h2>
944
+ <p>${escapeHtml(copy.historyHelp || '')}</p>
945
+ </div>
946
+ ${items.length ? `
947
+ <ol class="history-feed">
948
+ ${items.map((event) => `
949
+ <li>
950
+ <span class="history-type ${escapeAttr(String(event.type || 'unknown').replace(/[^a-z0-9_-]/gi, '-'))}">${escapeHtml(event.type || 'unknown')}</span>
951
+ <div>
952
+ <strong>${escapeHtml(shortDate(event.timestamp))} · ${escapeHtml(event.result || '')}</strong>
953
+ ${event.harnesses?.length ? `<p class="history-harnesses">${event.harnesses.map((id) => escapeHtml(id)).join(', ')}</p>` : ''}
954
+ ${event.files?.length ? `<p class="history-files">${event.files.slice(0, 4).map((file) => `<code>${escapeHtml(normalizePath(file).replace(/^.*[\\/]/, ''))}</code>`).join(' ')}</p>` : ''}
955
+ </div>
956
+ </li>
957
+ `).join('')}
958
+ </ol>
959
+ ` : `<p class="empty-note">${escapeHtml(copy.historyEmpty || 'No ledger history yet.')}</p>`}
960
+ </section>
961
+ `;
962
+ }
963
+
883
964
  function renderGraphOverview(graph = {}, copy) {
884
965
  const stats = graphStats(graph);
885
966
  const nodeCounts = new Map(stats.nodeCounts);
@@ -1267,13 +1348,11 @@ function renderScript(harnesses, copy) {
1267
1348
  selectHarness(button.dataset.selectHarness);
1268
1349
  });
1269
1350
  });
1270
- cards.forEach((card) => {
1271
- card.addEventListener('click', () => selectHarness(card.dataset.harnessId));
1272
- card.addEventListener('keydown', (event) => {
1273
- if (event.key === 'Enter' || event.key === ' ') {
1274
- event.preventDefault();
1275
- selectHarness(card.dataset.harnessId);
1276
- }
1351
+ document.querySelectorAll('.harness-summary').forEach((button) => {
1352
+ button.addEventListener('click', () => {
1353
+ const card = button.closest('.harness-card');
1354
+ const expanded = card.classList.toggle('is-expanded');
1355
+ button.setAttribute('aria-expanded', expanded ? 'true' : 'false');
1277
1356
  });
1278
1357
  });
1279
1358
  document.querySelectorAll('[data-filter-rec]').forEach((button) => {
@@ -1999,24 +2078,26 @@ function renderStyles() {
1999
2078
 
2000
2079
  .harness-grid {
2001
2080
  display: grid;
2002
- grid-template-columns: repeat(2, minmax(0, 1fr));
2003
- gap: var(--space-3);
2081
+ grid-template-columns: repeat(3, minmax(0, 1fr));
2082
+ gap: var(--space-2);
2083
+ align-items: start;
2004
2084
  }
2005
2085
 
2006
2086
  .harness-card {
2007
- cursor: pointer;
2008
- transition: opacity 120ms ease, border-color 120ms ease;
2009
- display: grid;
2010
- gap: var(--space-2);
2087
+ transition: opacity 160ms ease, border-color 160ms ease;
2088
+ padding: 0;
2089
+ overflow: hidden;
2011
2090
  }
2012
2091
 
2013
2092
  .harness-card:hover,
2014
- .harness-card:focus-visible,
2015
- .harness-card.is-selected {
2093
+ .harness-card.is-selected,
2094
+ .harness-card.is-expanded {
2016
2095
  border-color: var(--border-hover);
2017
2096
  outline: none;
2018
2097
  }
2019
2098
 
2099
+ .harness-card.is-expanded { border-color: var(--border-strong); }
2100
+
2020
2101
  .harness-card.is-filtered-out { display: none; }
2021
2102
 
2022
2103
  .harness-card.keep { border-top: 2px solid var(--success); }
@@ -2025,32 +2106,206 @@ function renderStyles() {
2025
2106
  .harness-card.merge_candidate { border-top: 2px solid var(--accent); }
2026
2107
  .harness-card.observe { border-top: 2px solid var(--text-secondary); }
2027
2108
 
2028
- .harness-card > div { display: grid; gap: 4px; }
2109
+ .harness-summary {
2110
+ width: 100%;
2111
+ display: grid;
2112
+ grid-template-columns: 1fr auto 14px;
2113
+ align-items: center;
2114
+ gap: var(--space-2);
2115
+ padding: var(--space-3);
2116
+ border: 0;
2117
+ background: transparent;
2118
+ color: var(--text-primary);
2119
+ font-family: var(--font-ui);
2120
+ text-align: left;
2121
+ cursor: pointer;
2122
+ }
2123
+
2124
+ .harness-summary:hover { background: var(--bg-hover); }
2125
+
2126
+ .harness-summary .eyebrow { margin: 0 0 2px; font-size: 10px; }
2029
2127
 
2030
- .harness-card h3 {
2128
+ .harness-summary h3 {
2031
2129
  margin: 0;
2032
- font-size: 16px;
2130
+ font-size: 14px;
2033
2131
  line-height: 1.25;
2034
2132
  font-weight: 600;
2133
+ overflow-wrap: anywhere;
2035
2134
  }
2036
2135
 
2037
- .harness-card > strong {
2038
- margin: 0;
2039
- font-size: 24px;
2136
+ .harness-mini {
2137
+ display: grid;
2138
+ gap: 2px;
2139
+ justify-items: end;
2140
+ }
2141
+
2142
+ .harness-mini span {
2143
+ color: var(--text-secondary);
2144
+ font-size: 11px;
2145
+ white-space: nowrap;
2146
+ }
2147
+
2148
+ .harness-mini strong {
2149
+ font-size: 18px;
2040
2150
  line-height: 1;
2041
2151
  font-family: var(--font-mono);
2042
2152
  font-weight: 600;
2153
+ }
2154
+
2155
+ .chevron {
2156
+ width: 7px;
2157
+ height: 7px;
2158
+ border-right: 1.5px solid var(--text-secondary);
2159
+ border-bottom: 1.5px solid var(--text-secondary);
2160
+ transform: rotate(45deg);
2161
+ transition: transform 240ms ease;
2162
+ justify-self: center;
2163
+ }
2164
+
2165
+ .harness-card.is-expanded .chevron { transform: rotate(225deg); }
2166
+
2167
+ .harness-detail {
2168
+ display: grid;
2169
+ grid-template-rows: 0fr;
2170
+ transition: grid-template-rows 320ms cubic-bezier(0.2, 0.8, 0.2, 1);
2171
+ }
2172
+
2173
+ .harness-card.is-expanded .harness-detail { grid-template-rows: 1fr; }
2174
+
2175
+ .harness-detail-inner {
2176
+ overflow: hidden;
2177
+ min-height: 0;
2178
+ padding: 0 var(--space-3);
2179
+ display: grid;
2180
+ gap: var(--space-2);
2181
+ opacity: 0;
2182
+ transition: opacity 240ms ease 60ms, padding 320ms ease;
2183
+ }
2184
+
2185
+ .harness-card.is-expanded .harness-detail-inner {
2186
+ opacity: 1;
2187
+ padding: var(--space-1) var(--space-3) var(--space-3);
2188
+ }
2189
+
2190
+ .harness-reason {
2191
+ margin: 0;
2192
+ color: var(--text-secondary);
2193
+ font-size: 12px;
2194
+ line-height: 1.5;
2195
+ }
2196
+
2197
+ .detail-label {
2198
+ margin: var(--space-1) 0 0;
2199
+ color: var(--text-secondary);
2200
+ font-size: 10px;
2201
+ text-transform: uppercase;
2202
+ letter-spacing: 0.06em;
2203
+ }
2204
+
2205
+ .co-used-chips { display: flex; flex-wrap: wrap; gap: 6px; }
2206
+
2207
+ .co-chip {
2208
+ border: 1px solid var(--border-default);
2209
+ background: var(--bg-hover);
2210
+ border-radius: var(--radius-sm);
2211
+ padding: 2px 6px;
2212
+ font-size: 11px;
2213
+ font-family: var(--font-mono);
2214
+ color: var(--text-secondary);
2215
+ }
2216
+
2217
+ .factor-list {
2218
+ margin: 0;
2219
+ padding: 0;
2220
+ list-style: none;
2221
+ display: grid;
2222
+ gap: 4px;
2223
+ }
2224
+
2225
+ .factor-list li {
2226
+ display: flex;
2227
+ justify-content: space-between;
2228
+ gap: var(--space-2);
2229
+ font-size: 12px;
2230
+ color: var(--text-secondary);
2231
+ }
2232
+
2233
+ .factor-list strong { font-family: var(--font-mono); color: var(--text-primary); font-weight: 500; }
2234
+
2235
+ .harness-next {
2236
+ margin: 0;
2043
2237
  color: var(--text-primary);
2044
- justify-self: end;
2238
+ font-size: 12px;
2239
+ line-height: 1.5;
2045
2240
  }
2046
2241
 
2047
- .harness-card p {
2242
+ .evidence-list {
2048
2243
  margin: 0;
2244
+ padding-left: 16px;
2049
2245
  color: var(--text-secondary);
2050
- font-size: 13px;
2051
- line-height: 1.45;
2246
+ font-size: 11px;
2247
+ display: grid;
2248
+ gap: 3px;
2249
+ }
2250
+
2251
+ .harness-card .link-button { margin-top: var(--space-1); justify-self: start; }
2252
+
2253
+ .history-section {
2254
+ margin-top: var(--space-6);
2255
+ border-top: 1px solid var(--border-default);
2256
+ padding-top: var(--space-4);
2257
+ }
2258
+
2259
+ .history-feed {
2260
+ margin: var(--space-3) 0 0;
2261
+ padding: 0;
2262
+ list-style: none;
2263
+ display: grid;
2264
+ gap: var(--space-2);
2265
+ }
2266
+
2267
+ .history-feed li {
2268
+ display: grid;
2269
+ grid-template-columns: 110px 1fr;
2270
+ gap: var(--space-3);
2271
+ align-items: start;
2272
+ border: 1px solid var(--border-default);
2273
+ border-radius: var(--radius-md);
2274
+ background: var(--bg-card);
2275
+ padding: var(--space-2) var(--space-3);
2052
2276
  }
2053
2277
 
2278
+ .history-type {
2279
+ display: inline-block;
2280
+ font-family: var(--font-mono);
2281
+ font-size: 10px;
2282
+ text-transform: uppercase;
2283
+ letter-spacing: 0.05em;
2284
+ color: var(--text-secondary);
2285
+ border: 1px solid var(--border-default);
2286
+ border-radius: var(--radius-sm);
2287
+ padding: 3px 6px;
2288
+ margin-top: 2px;
2289
+ text-align: center;
2290
+ }
2291
+
2292
+ .history-type.memory { color: var(--accent-text); border-color: var(--accent-dim); }
2293
+ .history-type.weave { color: var(--warning); border-color: var(--warning-dim); }
2294
+ .history-type.frog { color: var(--danger); border-color: var(--danger-dim); }
2295
+ .history-type.harness-create,
2296
+ .history-type.harness-edit { color: var(--success); border-color: var(--success-dim); }
2297
+
2298
+ .history-feed strong { font-size: 12px; font-weight: 500; }
2299
+
2300
+ .history-harnesses {
2301
+ margin: 2px 0 0;
2302
+ color: var(--text-secondary);
2303
+ font-size: 11px;
2304
+ font-family: var(--font-mono);
2305
+ }
2306
+
2307
+ .history-files { margin: 4px 0 0; display: flex; flex-wrap: wrap; gap: 4px; }
2308
+
2054
2309
  .harness-card dl,
2055
2310
  .selected dl,
2056
2311
  .graph-overview dl {
@@ -2359,7 +2614,7 @@ function renderStyles() {
2359
2614
  }
2360
2615
  .hero-sidebar { width: 100%; }
2361
2616
  .project-strip-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
2362
- .harness-grid,
2617
+ .harness-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
2363
2618
  .stats-grid { grid-template-columns: 1fr; }
2364
2619
  .home-stats { grid-template-columns: repeat(3, minmax(0, 1fr)); }
2365
2620
  .home-columns { grid-template-columns: 1fr; }
@@ -2439,13 +2694,19 @@ function renderReport(summary) {
2439
2694
  <section class="page-head">
2440
2695
  <p class="eyebrow">${escapeHtml(copy.harnessesEyebrow || 'HARNESSES')}</p>
2441
2696
  <h1>${escapeHtml(copy.harnessCards)}</h1>
2442
- <p>${escapeHtml(copy.harnessesHelp || '')}</p>
2697
+ <p>${escapeHtml(copy.harnessesHelp || '')} · ${escapeHtml(copy.sortNote || 'Sorted by usage')}</p>
2443
2698
  </section>
2444
2699
  <section class="harness-section">
2445
2700
  <div class="harness-grid">
2446
- ${harnesses.map((item) => renderHarness(item, copy)).join('\n')}
2701
+ ${[...harnesses]
2702
+ .sort((a, b) =>
2703
+ Number(b.signals?.uses || 0) - Number(a.signals?.uses || 0) ||
2704
+ Number(b.candidate_score?.total || 0) - Number(a.candidate_score?.total || 0) ||
2705
+ a.id.localeCompare(b.id))
2706
+ .map((item) => renderHarness(item, copy)).join('\n')}
2447
2707
  </div>
2448
2708
  </section>
2709
+ ${renderHistorySection(summary.maintenance_events, copy)}
2449
2710
  </section>
2450
2711
  <section class="page" data-page="memory">
2451
2712
  ${renderMemoryPage(summary, copy)}