stackvitals 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (465) hide show
  1. mergescout/__init__.py +1 -0
  2. mergescout/archive/__init__.py +0 -0
  3. mergescout/archive/__main__.py +30 -0
  4. mergescout/archive/convert.py +83 -0
  5. mergescout/archive/events.py +55 -0
  6. mergescout/archive/manifest.py +45 -0
  7. mergescout/archive/stream.py +83 -0
  8. mergescout/bench/__init__.py +1 -0
  9. mergescout/bench/__main__.py +135 -0
  10. mergescout/bench/ci.py +257 -0
  11. mergescout/bench/deploys.py +115 -0
  12. mergescout/bench/matched.py +22 -0
  13. mergescout/bench/prior.py +42 -0
  14. mergescout/bench/report.py +230 -0
  15. mergescout/bench/shared.py +112 -0
  16. mergescout/bench/stars.py +57 -0
  17. mergescout/cli.py +89 -0
  18. mergescout/config.py +35 -0
  19. mergescout/crawl/__init__.py +0 -0
  20. mergescout/crawl/__main__.py +97 -0
  21. mergescout/crawl/chain.py +34 -0
  22. mergescout/crawl/fetch.py +146 -0
  23. mergescout/crawl/outcomes.py +69 -0
  24. mergescout/crawl/queries.py +250 -0
  25. mergescout/crawl/runner.py +176 -0
  26. mergescout/crawl/seed.py +77 -0
  27. mergescout/crawl/state.py +169 -0
  28. mergescout/crawl/writer.py +87 -0
  29. mergescout/discover/__init__.py +0 -0
  30. mergescout/discover/__main__.py +58 -0
  31. mergescout/discover/sweep.py +286 -0
  32. mergescout/github/__init__.py +0 -0
  33. mergescout/github/graphql.py +202 -0
  34. mergescout/github/identities.py +35 -0
  35. mergescout/github/releases.py +137 -0
  36. mergescout/labels.py +20 -0
  37. mergescout/mcp_server.py +228 -0
  38. mergescout/measure/__init__.py +0 -0
  39. mergescout/measure/__main__.py +75 -0
  40. mergescout/measure/api_cost.py +91 -0
  41. mergescout/measure/count_repos.py +30 -0
  42. mergescout/measure/jsonl.py +14 -0
  43. mergescout/measure/report.py +56 -0
  44. mergescout/measure/sample.py +37 -0
  45. mergescout/owners.py +22 -0
  46. mergescout/setup_check.py +64 -0
  47. mergescout/signals/__init__.py +0 -0
  48. mergescout/signals/__main__.py +96 -0
  49. mergescout/signals/bump_ledger.py +258 -0
  50. mergescout/signals/bumps.py +104 -0
  51. mergescout/signals/check.py +141 -0
  52. mergescout/signals/community_bench.py +149 -0
  53. mergescout/signals/delivery_bench.py +103 -0
  54. mergescout/signals/deps_map.py +261 -0
  55. mergescout/signals/deps_trends.py +117 -0
  56. mergescout/signals/evidence.py +94 -0
  57. mergescout/signals/exploits.py +43 -0
  58. mergescout/signals/flow_bench.py +258 -0
  59. mergescout/signals/identity.py +73 -0
  60. mergescout/signals/issues.py +76 -0
  61. mergescout/signals/labels.py +79 -0
  62. mergescout/signals/osv.py +232 -0
  63. mergescout/signals/owners_watch.py +80 -0
  64. mergescout/signals/repo_stats.py +96 -0
  65. mergescout/signals/rules.py +70 -0
  66. mergescout/signals/silver.py +33 -0
  67. mergescout/signals/top_packages.py +26 -0
  68. mergescout/signals/triage.py +71 -0
  69. mergescout/signals/triage_math.py +120 -0
  70. mergescout/signals/walls.py +91 -0
  71. mergescout/snapshot/__init__.py +0 -0
  72. mergescout/snapshot/__main__.py +215 -0
  73. mergescout/snapshot/build.py +147 -0
  74. mergescout/snapshot/publish.py +70 -0
  75. mergescout/snapshot/schema.py +34 -0
  76. mergescout/snapshot/score.py +30 -0
  77. mergescout/store/__init__.py +0 -0
  78. mergescout/store/__main__.py +46 -0
  79. mergescout/store/sync.py +148 -0
  80. mergescout/timeutil.py +11 -0
  81. mergescout/tool/__init__.py +0 -0
  82. mergescout/tool/action_pack.py +62 -0
  83. mergescout/tool/live.py +201 -0
  84. mergescout/tool/queries.py +293 -0
  85. mergescout/tool/refs.py +31 -0
  86. mergescout/tool/store.py +129 -0
  87. mergescout/tool/tracking.py +83 -0
  88. stackvitals/__init__.py +2 -0
  89. stackvitals/__main__.py +4 -0
  90. stackvitals/ci/__init__.py +1 -0
  91. stackvitals/ci/action.py +574 -0
  92. stackvitals/ci/analyze.py +577 -0
  93. stackvitals/ci/api.py +218 -0
  94. stackvitals/ci/automation.py +212 -0
  95. stackvitals/ci/bench.py +195 -0
  96. stackvitals/ci/burnin.py +102 -0
  97. stackvitals/ci/cli.py +570 -0
  98. stackvitals/ci/collect.py +239 -0
  99. stackvitals/ci/cost.py +107 -0
  100. stackvitals/ci/coverage.py +155 -0
  101. stackvitals/ci/feedback.py +699 -0
  102. stackvitals/ci/fingerprint.py +220 -0
  103. stackvitals/ci/gitlab.py +121 -0
  104. stackvitals/ci/gitlab_action.py +125 -0
  105. stackvitals/ci/impact.py +125 -0
  106. stackvitals/ci/investigate.py +59 -0
  107. stackvitals/ci/kinds.py +52 -0
  108. stackvitals/ci/monitors.py +105 -0
  109. stackvitals/ci/owners.py +116 -0
  110. stackvitals/ci/quarantine_runtime.py +90 -0
  111. stackvitals/ci/report.py +435 -0
  112. stackvitals/ci/retry.py +67 -0
  113. stackvitals/ci/selfheal.py +102 -0
  114. stackvitals/ci/shards.py +120 -0
  115. stackvitals/ci/share.py +59 -0
  116. stackvitals/ci/store.py +93 -0
  117. stackvitals/ci/testreports.py +351 -0
  118. stackvitals/ci/tickets.py +106 -0
  119. stackvitals/ci/track_record.json +11 -0
  120. stackvitals/ci/triage.py +130 -0
  121. stackvitals/ci/triggers.py +147 -0
  122. stackvitals/ci/upload.py +85 -0
  123. stackvitals/ci/validate.py +91 -0
  124. stackvitals/cli.py +111 -0
  125. stackvitals/codeowners.py +69 -0
  126. stackvitals/community/__init__.py +0 -0
  127. stackvitals/community/action.py +140 -0
  128. stackvitals/community/affiliation.py +51 -0
  129. stackvitals/community/analyze.py +507 -0
  130. stackvitals/community/build.py +56 -0
  131. stackvitals/community/chaoss.py +70 -0
  132. stackvitals/community/cli.py +79 -0
  133. stackvitals/community/collect.py +240 -0
  134. stackvitals/community/context.py +54 -0
  135. stackvitals/community/report.py +250 -0
  136. stackvitals/connectors/__init__.py +244 -0
  137. stackvitals/connectors/ai_spend.py +190 -0
  138. stackvitals/connectors/base.py +82 -0
  139. stackvitals/connectors/check.py +686 -0
  140. stackvitals/connectors/copilot.py +104 -0
  141. stackvitals/connectors/discord.py +103 -0
  142. stackvitals/connectors/discourse.py +117 -0
  143. stackvitals/connectors/incident_io.py +106 -0
  144. stackvitals/connectors/jira.py +210 -0
  145. stackvitals/connectors/linear.py +140 -0
  146. stackvitals/connectors/pagerduty.py +96 -0
  147. stackvitals/connectors/slack.py +110 -0
  148. stackvitals/connectors/sonar.py +77 -0
  149. stackvitals/connectors/store.py +120 -0
  150. stackvitals/connectors/tracker.py +108 -0
  151. stackvitals/connectors/writeback.py +105 -0
  152. stackvitals/contribute/__init__.py +1 -0
  153. stackvitals/contribute/brief.py +297 -0
  154. stackvitals/contribute/cli.py +64 -0
  155. stackvitals/core/__init__.py +1 -0
  156. stackvitals/core/actors.py +103 -0
  157. stackvitals/core/agent_ledger.py +120 -0
  158. stackvitals/core/agreements.py +95 -0
  159. stackvitals/core/ai_outcomes.py +276 -0
  160. stackvitals/core/alerts.py +224 -0
  161. stackvitals/core/appdata.py +112 -0
  162. stackvitals/core/bucket.py +190 -0
  163. stackvitals/core/bugs.py +43 -0
  164. stackvitals/core/calendar.py +34 -0
  165. stackvitals/core/capex.py +57 -0
  166. stackvitals/core/change_risk.py +181 -0
  167. stackvitals/core/changes.py +133 -0
  168. stackvitals/core/chat.py +143 -0
  169. stackvitals/core/config.py +373 -0
  170. stackvitals/core/dataflow.py +168 -0
  171. stackvitals/core/decisions.py +275 -0
  172. stackvitals/core/delivery.py +954 -0
  173. stackvitals/core/delivery_peers.py +98 -0
  174. stackvitals/core/delivery_report.py +429 -0
  175. stackvitals/core/deliverycli.py +147 -0
  176. stackvitals/core/devlake_dora.sql +485 -0
  177. stackvitals/core/digest.py +333 -0
  178. stackvitals/core/explaincli.py +107 -0
  179. stackvitals/core/explore.py +296 -0
  180. stackvitals/core/export.py +342 -0
  181. stackvitals/core/exportcli.py +117 -0
  182. stackvitals/core/findings.py +350 -0
  183. stackvitals/core/health.py +94 -0
  184. stackvitals/core/identity.py +82 -0
  185. stackvitals/core/insights.py +365 -0
  186. stackvitals/core/insightscli.py +43 -0
  187. stackvitals/core/installdoc.py +69 -0
  188. stackvitals/core/investment.py +187 -0
  189. stackvitals/core/joins.py +43 -0
  190. stackvitals/core/llmsdoc.py +40 -0
  191. stackvitals/core/merge_queue.py +70 -0
  192. stackvitals/core/metrics.py +109 -0
  193. stackvitals/core/model.py +285 -0
  194. stackvitals/core/projectcli.py +66 -0
  195. stackvitals/core/propose.py +86 -0
  196. stackvitals/core/pulse.py +85 -0
  197. stackvitals/core/recorded.py +145 -0
  198. stackvitals/core/releases.py +39 -0
  199. stackvitals/core/requests.py +528 -0
  200. stackvitals/core/resync.py +35 -0
  201. stackvitals/core/schema.py +205 -0
  202. stackvitals/core/services.py +203 -0
  203. stackvitals/core/settings.py +96 -0
  204. stackvitals/core/sqlcli.py +111 -0
  205. stackvitals/core/stats.py +222 -0
  206. stackvitals/core/store.py +125 -0
  207. stackvitals/core/summary.py +104 -0
  208. stackvitals/core/time.py +29 -0
  209. stackvitals/core/tomledit.py +149 -0
  210. stackvitals/core/uicli.py +138 -0
  211. stackvitals/core/webapp.py +79 -0
  212. stackvitals/core/what_changed.py +76 -0
  213. stackvitals/core/workspace.py +194 -0
  214. stackvitals/deps/__init__.py +1 -0
  215. stackvitals/deps/action.py +299 -0
  216. stackvitals/deps/actions_vulns.py +89 -0
  217. stackvitals/deps/cli.py +77 -0
  218. stackvitals/deps/dossier.py +341 -0
  219. stackvitals/deps/evidence.py +44 -0
  220. stackvitals/deps/fixplan.py +40 -0
  221. stackvitals/deps/guarddog.py +32 -0
  222. stackvitals/deps/hygiene.py +47 -0
  223. stackvitals/deps/imports.py +113 -0
  224. stackvitals/deps/lockfiles.py +198 -0
  225. stackvitals/deps/lookalike.py +80 -0
  226. stackvitals/deps/reach.py +59 -0
  227. stackvitals/deps/release.py +110 -0
  228. stackvitals/deps/runtimes.py +161 -0
  229. stackvitals/deps/supply.py +454 -0
  230. stackvitals/deps/vulns.py +588 -0
  231. stackvitals/editors/__init__.py +40 -0
  232. stackvitals/editors/vscode/extension.js +101 -0
  233. stackvitals/editors/vscode/marks.js +92 -0
  234. stackvitals/editors/vscode/package.json +26 -0
  235. stackvitals/flow/__init__.py +1 -0
  236. stackvitals/flow/action.py +178 -0
  237. stackvitals/flow/analyze.py +418 -0
  238. stackvitals/flow/automate.py +72 -0
  239. stackvitals/flow/build.py +46 -0
  240. stackvitals/flow/cli.py +93 -0
  241. stackvitals/flow/code_hosts.py +146 -0
  242. stackvitals/flow/collect.py +451 -0
  243. stackvitals/flow/gitlab_mr.py +75 -0
  244. stackvitals/flow/knowledge.py +202 -0
  245. stackvitals/flow/peers.py +90 -0
  246. stackvitals/flow/preflight.py +210 -0
  247. stackvitals/flow/report.py +253 -0
  248. stackvitals/flow/rules.py +135 -0
  249. stackvitals/flow/store.py +108 -0
  250. stackvitals/flow/survival.py +84 -0
  251. stackvitals/flow/triage.py +62 -0
  252. stackvitals/mcp_server.py +670 -0
  253. stackvitals/portal/__init__.py +0 -0
  254. stackvitals/portal/action.py +205 -0
  255. stackvitals/portal/batch.py +80 -0
  256. stackvitals/portal/campaigns.py +104 -0
  257. stackvitals/portal/catalog.py +104 -0
  258. stackvitals/portal/checks.py +63 -0
  259. stackvitals/portal/cli.py +172 -0
  260. stackvitals/portal/descriptor.py +105 -0
  261. stackvitals/portal/export.py +43 -0
  262. stackvitals/portal/github.py +48 -0
  263. stackvitals/portal/golden.py +95 -0
  264. stackvitals/portal/history.py +30 -0
  265. stackvitals/portal/inventory.py +264 -0
  266. stackvitals/portal/owners.py +78 -0
  267. stackvitals/portal/pages.py +153 -0
  268. stackvitals/portal/rollup.py +32 -0
  269. stackvitals/portal/scorecard.py +366 -0
  270. stackvitals/security/__init__.py +2 -0
  271. stackvitals/security/probes.py +154 -0
  272. stackvitals/security/repo_checks.py +132 -0
  273. stackvitals/security/workflows.py +111 -0
  274. stackvitals/shell.py +107 -0
  275. stackvitals/site.py +51 -0
  276. stackvitals/web/app/LICENSES.md +428 -0
  277. stackvitals/web/app/boot.js +1 -0
  278. stackvitals/web/app/chunks/AddRepo-KRPZXWCY.js +1 -0
  279. stackvitals/web/app/chunks/SetupRunning-XWFQAKRZ.js +1 -0
  280. stackvitals/web/app/chunks/accuracy-data-OQTDXI7Y.js +1 -0
  281. stackvitals/web/app/chunks/agents-data-BAL6QAOF.js +11 -0
  282. stackvitals/web/app/chunks/chunk-24LFKFZ6.js +1 -0
  283. stackvitals/web/app/chunks/chunk-3G4SQNUC.js +1 -0
  284. stackvitals/web/app/chunks/chunk-3IXWEOIJ.js +154 -0
  285. stackvitals/web/app/chunks/chunk-3ROQFHPV.js +1 -0
  286. stackvitals/web/app/chunks/chunk-45PGAQRO.js +8 -0
  287. stackvitals/web/app/chunks/chunk-4H53OYLG.js +1 -0
  288. stackvitals/web/app/chunks/chunk-4LUI7O5P.js +1 -0
  289. stackvitals/web/app/chunks/chunk-4SQPQ5TB.js +1 -0
  290. stackvitals/web/app/chunks/chunk-4WKQTWUC.js +2 -0
  291. stackvitals/web/app/chunks/chunk-576YHDEK.js +8 -0
  292. stackvitals/web/app/chunks/chunk-6BDBXA6V.js +1 -0
  293. stackvitals/web/app/chunks/chunk-6RLVD2DT.js +1 -0
  294. stackvitals/web/app/chunks/chunk-763NH7PP.js +1 -0
  295. stackvitals/web/app/chunks/chunk-76WAVKUG.js +1 -0
  296. stackvitals/web/app/chunks/chunk-7II2BD5F.js +1 -0
  297. stackvitals/web/app/chunks/chunk-7TJCBOAY.js +1 -0
  298. stackvitals/web/app/chunks/chunk-ASYNLVOP.js +1 -0
  299. stackvitals/web/app/chunks/chunk-CTKDYZMG.js +1 -0
  300. stackvitals/web/app/chunks/chunk-CVB26NMC.js +1 -0
  301. stackvitals/web/app/chunks/chunk-G7NGB5UX.js +1 -0
  302. stackvitals/web/app/chunks/chunk-GPVLJRZD.js +1 -0
  303. stackvitals/web/app/chunks/chunk-GQYFN4PE.js +75 -0
  304. stackvitals/web/app/chunks/chunk-GT4EAKAO.js +2 -0
  305. stackvitals/web/app/chunks/chunk-IYMBFPCV.js +1 -0
  306. stackvitals/web/app/chunks/chunk-LAAGCPU3.js +1 -0
  307. stackvitals/web/app/chunks/chunk-MAPYGK3P.js +1 -0
  308. stackvitals/web/app/chunks/chunk-P5TXDD45.js +1 -0
  309. stackvitals/web/app/chunks/chunk-PABICGBL.js +1 -0
  310. stackvitals/web/app/chunks/chunk-RPXHAG6F.js +1 -0
  311. stackvitals/web/app/chunks/chunk-RQZPSJ2Y.js +1 -0
  312. stackvitals/web/app/chunks/chunk-S2ZBMEVA.js +1 -0
  313. stackvitals/web/app/chunks/chunk-S654BBEP.js +6 -0
  314. stackvitals/web/app/chunks/chunk-T7QVTNXF.js +1 -0
  315. stackvitals/web/app/chunks/chunk-THFUOCPC.js +273 -0
  316. stackvitals/web/app/chunks/chunk-TO4VVRZ7.js +1 -0
  317. stackvitals/web/app/chunks/chunk-VQ5YXO54.js +1 -0
  318. stackvitals/web/app/chunks/chunk-VWKSMFWJ.js +1 -0
  319. stackvitals/web/app/chunks/chunk-Y6LUXDM6.js +1 -0
  320. stackvitals/web/app/chunks/chunk-YTANGYYH.js +1 -0
  321. stackvitals/web/app/chunks/connect-KONYWYTT.js +1 -0
  322. stackvitals/web/app/chunks/connections-data-2DYS4UVW.js +32 -0
  323. stackvitals/web/app/chunks/logos-N5IINUAM.js +1 -0
  324. stackvitals/web/app/chunks/more-KMJMJQD3.js +10 -0
  325. stackvitals/web/app/chunks/more-PC6FVS3P.js +1 -0
  326. stackvitals/web/app/chunks/more-RCIVCUDE.js +2 -0
  327. stackvitals/web/app/chunks/more-ZKKRY6NO.css +1 -0
  328. stackvitals/web/app/chunks/sealbox-35YL4EUH.js +3 -0
  329. stackvitals/web/app/chunks/session-DYYTOE2K.js +1 -0
  330. stackvitals/web/app/chunks/settingsApply-2HRJEZYM.js +1 -0
  331. stackvitals/web/app/chunks/settingsReview-GX2FZQ4M.js +3 -0
  332. stackvitals/web/app/chunks/uPlot.esm-OIWT3AHJ.js +3 -0
  333. stackvitals/web/app/demo/ci/impact.json +1 -0
  334. stackvitals/web/app/demo/ci/quarantine.json +1 -0
  335. stackvitals/web/app/demo/ci/tickets.json +1 -0
  336. stackvitals/web/app/demo/mobile/alerts.json +1 -0
  337. stackvitals/web/app/demo/mobile/app.json +1 -0
  338. stackvitals/web/app/demo/mobile/community.json +1 -0
  339. stackvitals/web/app/demo/mobile/connectors.json +1 -0
  340. stackvitals/web/app/demo/mobile/data.json +1 -0
  341. stackvitals/web/app/demo/mobile/decisions.json +1 -0
  342. stackvitals/web/app/demo/mobile/delivery.json +1 -0
  343. stackvitals/web/app/demo/mobile/deps.json +1 -0
  344. stackvitals/web/app/demo/mobile/flow.json +1 -0
  345. stackvitals/web/app/demo/mobile/health.json +1 -0
  346. stackvitals/web/app/demo/mobile/insights.json +1 -0
  347. stackvitals/web/app/demo/mobile/pulse.json +1 -0
  348. stackvitals/web/app/demo/mobile/security.json +1 -0
  349. stackvitals/web/app/demo/mobile/summary/ci_jobs.jsonl +5 -0
  350. stackvitals/web/app/demo/mobile/summary/devlake_project_pr_metrics.jsonl +14 -0
  351. stackvitals/web/app/demo/mobile/summary/devlake_pull_requests.jsonl +14 -0
  352. stackvitals/web/app/demo/mobile/summary/manifest.json +1 -0
  353. stackvitals/web/app/demo/mobile/summary/prs.jsonl +19 -0
  354. stackvitals/web/app/demo/mobile/summary/tests.jsonl +1 -0
  355. stackvitals/web/app/demo/mobile/supply.json +1 -0
  356. stackvitals/web/app/demo/mobile/vulns.json +1 -0
  357. stackvitals/web/app/demo/payments-api/alerts.json +1 -0
  358. stackvitals/web/app/demo/payments-api/app.json +1 -0
  359. stackvitals/web/app/demo/payments-api/community.json +1 -0
  360. stackvitals/web/app/demo/payments-api/connectors.json +1 -0
  361. stackvitals/web/app/demo/payments-api/data.json +1 -0
  362. stackvitals/web/app/demo/payments-api/decisions.json +1 -0
  363. stackvitals/web/app/demo/payments-api/delivery.json +1 -0
  364. stackvitals/web/app/demo/payments-api/deps.json +1 -0
  365. stackvitals/web/app/demo/payments-api/flow.json +1 -0
  366. stackvitals/web/app/demo/payments-api/health.json +1 -0
  367. stackvitals/web/app/demo/payments-api/insights.json +1 -0
  368. stackvitals/web/app/demo/payments-api/pulse.json +1 -0
  369. stackvitals/web/app/demo/payments-api/security.json +1 -0
  370. stackvitals/web/app/demo/payments-api/summary/ci_jobs.jsonl +5 -0
  371. stackvitals/web/app/demo/payments-api/summary/devlake_project_pr_metrics.jsonl +14 -0
  372. stackvitals/web/app/demo/payments-api/summary/devlake_pull_requests.jsonl +14 -0
  373. stackvitals/web/app/demo/payments-api/summary/manifest.json +1 -0
  374. stackvitals/web/app/demo/payments-api/summary/prs.jsonl +22 -0
  375. stackvitals/web/app/demo/payments-api/summary/tests.jsonl +3 -0
  376. stackvitals/web/app/demo/payments-api/supply.json +1 -0
  377. stackvitals/web/app/demo/payments-api/vulns.json +1 -0
  378. stackvitals/web/app/demo/portal.json +1 -0
  379. stackvitals/web/app/demo/settings.json +1 -0
  380. stackvitals/web/app/demo/web-app/alerts.json +1 -0
  381. stackvitals/web/app/demo/web-app/app.json +1 -0
  382. stackvitals/web/app/demo/web-app/community.json +1 -0
  383. stackvitals/web/app/demo/web-app/connectors.json +1 -0
  384. stackvitals/web/app/demo/web-app/data.json +1 -0
  385. stackvitals/web/app/demo/web-app/decisions.json +1 -0
  386. stackvitals/web/app/demo/web-app/delivery.json +1 -0
  387. stackvitals/web/app/demo/web-app/deps.json +1 -0
  388. stackvitals/web/app/demo/web-app/flow.json +1 -0
  389. stackvitals/web/app/demo/web-app/health.json +1 -0
  390. stackvitals/web/app/demo/web-app/insights.json +1 -0
  391. stackvitals/web/app/demo/web-app/pulse.json +1 -0
  392. stackvitals/web/app/demo/web-app/security.json +1 -0
  393. stackvitals/web/app/demo/web-app/summary/ci_jobs.jsonl +5 -0
  394. stackvitals/web/app/demo/web-app/summary/devlake_project_pr_metrics.jsonl +14 -0
  395. stackvitals/web/app/demo/web-app/summary/devlake_pull_requests.jsonl +14 -0
  396. stackvitals/web/app/demo/web-app/summary/manifest.json +1 -0
  397. stackvitals/web/app/demo/web-app/summary/prs.jsonl +18 -0
  398. stackvitals/web/app/demo/web-app/summary/tests.jsonl +2 -0
  399. stackvitals/web/app/demo/web-app/supply.json +1 -0
  400. stackvitals/web/app/demo/web-app/vulns.json +1 -0
  401. stackvitals/web/app/fonts/plex-mono-400.woff2 +0 -0
  402. stackvitals/web/app/fonts/plex-sans-400.woff2 +0 -0
  403. stackvitals/web/app/fonts/plex-sans-600.woff2 +0 -0
  404. stackvitals/web/app/fonts/plex-sans-condensed-700.woff2 +0 -0
  405. stackvitals/web/app/help/accuracy.json +1 -0
  406. stackvitals/web/app/help/connectors.json +1 -0
  407. stackvitals/web/app/help/getting-started.json +1 -0
  408. stackvitals/web/app/help/index.json +1 -0
  409. stackvitals/web/app/help/modules.json +1 -0
  410. stackvitals/web/app/help/privacy-and-security.json +1 -0
  411. stackvitals/web/app/help/reference.json +1 -0
  412. stackvitals/web/app/main.css +1 -0
  413. stackvitals/web/app/main.js +1 -0
  414. stackvitals/web/app/pages/about.css +1 -0
  415. stackvitals/web/app/pages/about.js +1 -0
  416. stackvitals/web/app/pages/activity.css +1 -0
  417. stackvitals/web/app/pages/activity.js +1 -0
  418. stackvitals/web/app/pages/agents.css +1 -0
  419. stackvitals/web/app/pages/agents.js +2 -0
  420. stackvitals/web/app/pages/ci.css +1 -0
  421. stackvitals/web/app/pages/ci.js +1 -0
  422. stackvitals/web/app/pages/community.css +1 -0
  423. stackvitals/web/app/pages/community.js +1 -0
  424. stackvitals/web/app/pages/connections.css +1 -0
  425. stackvitals/web/app/pages/connections.js +6 -0
  426. stackvitals/web/app/pages/delivery.css +1 -0
  427. stackvitals/web/app/pages/delivery.js +45 -0
  428. stackvitals/web/app/pages/deps.css +1 -0
  429. stackvitals/web/app/pages/deps.js +7 -0
  430. stackvitals/web/app/pages/explore.css +1 -0
  431. stackvitals/web/app/pages/explore.js +7 -0
  432. stackvitals/web/app/pages/health.css +1 -0
  433. stackvitals/web/app/pages/health.js +1 -0
  434. stackvitals/web/app/pages/help.css +1 -0
  435. stackvitals/web/app/pages/help.js +6 -0
  436. stackvitals/web/app/pages/insights.css +1 -0
  437. stackvitals/web/app/pages/insights.js +1 -0
  438. stackvitals/web/app/pages/investment.css +1 -0
  439. stackvitals/web/app/pages/investment.js +3 -0
  440. stackvitals/web/app/pages/overview.css +1 -0
  441. stackvitals/web/app/pages/overview.js +1 -0
  442. stackvitals/web/app/pages/portal.css +1 -0
  443. stackvitals/web/app/pages/portal.js +51 -0
  444. stackvitals/web/app/pages/prs.css +1 -0
  445. stackvitals/web/app/pages/prs.js +4 -0
  446. stackvitals/web/app/pages/settings.css +1 -0
  447. stackvitals/web/app/pages/settings.js +1 -0
  448. stackvitals/web/app/pages/setup.css +1 -0
  449. stackvitals/web/app/pages/setup.js +2 -0
  450. stackvitals/web/app/pages/standing.css +1 -0
  451. stackvitals/web/app/pages/standing.js +1 -0
  452. stackvitals/web/app/pages/welcome-sheet.js +1 -0
  453. stackvitals/web/app/pages/welcome.js +1 -0
  454. stackvitals/web/app/settings-workflow.json +1 -0
  455. stackvitals/web/index.html +34 -0
  456. stackvitals/web/site/index.html +108 -0
  457. stackvitals/web/site/integrations.html +15 -0
  458. stackvitals/web/site/setup.html +15 -0
  459. stackvitals/web/site/site.css +68 -0
  460. stackvitals-0.1.0.dist-info/METADATA +134 -0
  461. stackvitals-0.1.0.dist-info/RECORD +465 -0
  462. stackvitals-0.1.0.dist-info/WHEEL +4 -0
  463. stackvitals-0.1.0.dist-info/entry_points.txt +3 -0
  464. stackvitals-0.1.0.dist-info/licenses/LICENSE +202 -0
  465. stackvitals-0.1.0.dist-info/licenses/NOTICE +16 -0
@@ -0,0 +1 @@
1
+ {"enterprise":"StackVitals has no server and no account: it runs in your GitHub Actions with your tokens and keeps everything in your\nrepos. So the controls an enterprise asks a vendor for are GitHub's own, applied to StackVitals like to any workflow.\n\n| What you need | Where it comes from | What to set |\n|---|---|---|\n| Single sign-on | Your GitHub organization's SAML or OIDC SSO | Nothing extra: people reach the app and the data through GitHub. A personal token a person pastes into the app must be SSO-authorized like any other. |\n| Who can see what | Repository permissions | The data branch and the app have the repo's visibility. Private repos' pages stay private (Enterprise Cloud private Pages) or aren't published at all (`pages: artifact`). The Portal leaves private repos out of every page a public portal repo writes. |\n| Who can change what | Repository roles | Settings changes need write access to the repo, decisions triage access, golden paths write access to the portal repo and to the target repo; the workflow starts only for owners, members and collaborators ([threat-model.md](threat-model.md)). |\n| Audit log | GitHub's audit log, plus StackVitals' own | Every decision and settings change is appended to `decisions/log.ndjson` on the data branch with who, when, why and from where; the app's Activity page shows it. GitHub's audit log records the workflow runs and token use. |\n| Data residency | Where your GitHub data lives | StackVitals' history is files in your repo, so it stays wherever your GitHub Enterprise data resides. What leaves the repo, and how to keep it all inside GitHub, is on [privacy.md](privacy.md). |\n| Secrets | GitHub Actions secrets | Keys live in your repo's or org's secrets and reach only their own service's host ([privacy.md](privacy.md)). |\n| Supply chain | Pinned actions, signed releases | Every action StackVitals' own workflows use is pinned to a commit; pin the StackVitals Action itself to a release's commit SHA rather than `@v0`. The release workflow signs each release with Sigstore and adds an SBOM and build provenance ([SECURITY.md](../SECURITY.md)). |\n| Uptime and support | GitHub Actions' availability | There is no StackVitals service to go down: a missed run is caught up by the next one. There is no paid SLA; report problems as issues, and security problems privately ([SECURITY.md](../SECURITY.md)). |\n| Certifications (SOC 2, ISO 27001) | GitHub's | StackVitals has no service of its own, so GitHub's certifications cover where the data lives. By default the dependency check sends package names to deps.dev and the registries; `[deps] outside_lookups = false` stops that ([privacy.md](privacy.md)). The code is open to review. |\n\n## Rate limits on Enterprise Cloud\n\nThe workflow token allows 15,000 requests an hour on Enterprise Cloud (1,000 elsewhere). The busiest public repos need\nunder 40% of the smaller limit for CI Health ([bench/accuracy/platform](../bench/accuracy/platform/README.md)).\n","privacy":"StackVitals runs in your own workflow and keeps its history in your repo. There is no StackVitals server: nothing is sent to us. This page lists everything a run (or the app) sends anywhere, built from the code's own list, and a test fails if the code gains an outside host this page doesn't name.\n\n| Feature | Goes to | What is sent | When | To turn it off |\n|---|---|---|---|---|\n| Every module (reading your repo, writing its results) | api.github.com, github.com | Requests with the workflow's own token: it reads runs, logs, pull requests, issues and files of this repo, and writes the stackvitals-data branch, Pages, and the comments, labels and issues your switches allow. | every run | Stays inside GitHub; remove a module from `modules` to stop it. |\n| Delivery from Bitbucket or Azure DevOps (command line) | api.bitbucket.org, dev.azure.com | Read-only requests for the repo you name, with BITBUCKET_TOKEN or AZURE_DEVOPS_TOKEN when set; nothing about a GitHub repo. | only when you run `stackvitals delivery --bitbucket` or `--azure` | Don't run it. |\n| SonarQube or SonarCloud code quality | sonarcloud.io | The Sonar project key in [connectors.sonar] (and the SONAR_TOKEN when set), to read its quality gate and measures. | once a day, when a project is set | Leave project under [connectors.sonar] empty. |\n| Security checks: the OpenSSF Best Practices badge | www.bestpractices.dev | A public repo's address, to look up its badge level; a private repo's is never sent. | once a week | Remove `deps` from `modules`. |\n| Benchmarks from public repos | github.com | A download of the crawl's benchmark files from stackvitals/stackvitals's releases; nothing about your repo is sent. | every run, cached a day | Nothing to turn off: it is a download. |\n| Shared benchmark figures (opt-in) | github.com | An issue on kaushik-yadav/merge-scout, filed by a person from a link in the run summary: this repo's rounded CI figures, its language and star tier, a random id kept in the data branch, and its name only with `name = true`. It is public once filed, and shows the account that filed it. | only when someone files the link | `[benchmarks] share = false` (the default); close the issue to withdraw it. |\n| Dependency licences and Scorecards | api.deps.dev, api.scorecard.dev | The name and version of every package in the dependency graph (to deps.dev; that includes the names of private or internal packages the graph lists) and the public source repos behind them (to OpenSSF Scorecard); never this repo's name. | weekly, Dependencies module | `[deps] outside_lookups = false`. |\n| Known vulnerabilities in every lockfile | github.com, api.osv.dev, api.deps.dev, osv-vulnerabilities.storage.googleapis.com | A download of the pinned OSV-Scanner from its GitHub release (checked against its SHA-256); then the name and version of every package in the repo's lockfiles to OSV (and, for manifests without exact versions, to deps.dev to resolve them). With outside lookups off, OSV's public databases are downloaded instead and no package name is sent. | after CI runs and on the schedule, at most every 6 hours unless a lockfile changed; Dependencies module | `[deps] outside_lookups = false` (scans offline). |\n| Release checks on changed packages | registry.npmjs.org, pypi.org, api.deps.dev | The name and the old and new versions of each npm or PyPI package a pull request adds or upgrades (at most 30), to read when it was released, its install scripts, dependencies and build provenance. | pull requests that change a lockfile | `[deps] outside_lookups = false`. |\n| Runtime versions | endoflife.date | Only the runtime's name (nodejs, python, php, ruby or go), to read its release lines and support dates; never the repo or its versions. | weekly, Dependencies module | `[deps] outside_lookups = false`. |\n| GuardDog code check (opt-in) | pypi.org, files.pythonhosted.org, registry.npmjs.org | Downloads of GuardDog itself and of each npm or PyPI package version a pull request adds or upgrades (at most 10), whose code it reads on the runner; nothing about the repo is sent. | pull requests that change a lockfile, only with `[deps] guarddog = true` | Off by default. |\n| History in a bucket (opt-in) | the s3-endpoint you set | Every file of the data branch (history, pages, decisions), signed with your bucket key, to your own bucket. | every run, only with `storage: s3://...` | The default, `storage: git`. |\n| Look-alike package check | api.npmjs.org, pypistats.org | Only the name of a package a pull request adds whose name is one edit from a popular one, to read its weekly downloads. | pull requests that change a lockfile | `[deps] outside_lookups = false` (names are still checked). |\n| Model triage of unexplained CI failures | api.anthropic.com, api.groq.com, api.mistral.ai, api.openai.com, generativelanguage.googleapis.com, the `url` you set under [ci.triage] | The failing step's log excerpt (bounded) and the changed files' names, with your key, to the provider you choose or the endpoint you set; at most 3 calls a run. | off until `[ci.triage]` and a key are set; a private repo also needs `allow_private = true` | Off by default. |\n| Issue trackers and incident tools | *.atlassian.net, api.linear.app, api.pagerduty.com, api.incident.io | Requests with your key that read issues, sprints and incidents; with write-back on, a comment linking the merged pull request (and a status move). | only with a key in the workflow; write-back only with `write_back = true` | Remove the key. |\n| A Discourse forum | your forum's host | Read requests for its public topics, with no key; only times and flags are kept, never usernames or text. | only with url under [connectors.discourse] | Remove the url. |\n| Slack and Discord support channels | slack.com, discord.com | Read requests with your bot token for the channels you name; only times and flags are kept, never text or user ids. | only with a slack-token or discord-token | Remove the token. |\n| Copilot usage reports | api.github.com, the report file host GitHub names | Requests with the reports token; the report file is fetched without it. | only with the Copilot token | Remove the token. |\n| AI spend | api.anthropic.com, api.openai.com, api.cursor.com | Read-only cost and usage report requests, each with that provider's own admin key; nothing about the repo. | only with an AI spend admin key | Remove the key. |\n| Chat alerts and the weekly digest | your webhook's host (Slack, Teams, Discord, other) | Alert and digest text: metric names and values, test names, this repo's name, links to its pages. | only with a webhook secret | Remove the webhook. |\n| Test connection | each connector's own host (above), your webhook's host, the s3-endpoint you set, your model provider, your GitLab host (`CI_API_V4_URL`) | One cheap call per connector asked for, with its own key: it reads one item, never writes to a tracker. A chat webhook gets one labelled test message (\"StackVitals connection test\"), never retried; a bucket gets one small object (`.stackvitals-check`) written and deleted under the prefix; model triage calls the provider's model list; GitLab reads the project and the token's own scopes, at `CI_API_V4_URL` for a self-hosted GitLab. The results go in docs/connectors.json; on a public repo without account, channel, team or bucket names. | only when someone with write access presses Test connection, comments `/stackvitals check`, or runs `stackvitals connectors check` | Don't run it. |\n| Exports (DevLake, CTRF, Postgres, OpenTelemetry) | api.github.com | Reads with your token; the files are written on your machine and sent nowhere. | only when you run `stackvitals export` | Don't run it. |\n| Portal Scorecard checks | api.scorecard.dev | The names of the organisation's public repos; private repos are never sent. | weekly, Portal module | Leave the Portal module out. |\n| GitLab CI | gitlab.com | Requests with your GitLab token for one project's pipelines and jobs. The GitLab job also writes the project's stackvitals-data branch, its Pages and one merge request note, and downloads StackVitals from github.com and its packages from PyPI. | when you run `stackvitals ci gitlab`, or each pipeline once you include the GitLab job | Don't run or include it. |\n| The app in a viewer's browser | your Pages site, api.github.com, raw.githubusercontent.com, avatars.githubusercontent.com, api.linear.app, your sign-in relay (optional), cdn.jsdelivr.net, cdnjs.cloudflare.com | The viewer's browser loads the app from your Pages site (its code, font and docs are all there). With a GitHub token or sign-in, the app calls api.github.com with it to read repos and data branches, set up the workflow, save settings and decisions, and save secrets sealed in the browser; workflow files and data are also read from raw.githubusercontent.com, and the account picture from avatars.githubusercontent.com. Testing a Linear key from the page sends it only to api.linear.app. Sign in with GitHub, when the site was built with a relay, sends the one-time code (and later the refresh token) to that relay, which swaps it for a token and keeps nothing. Explore loads DuckDB from jsDelivr; printable pages load Chart.js from cdnjs. Nothing goes to a server of ours. | when someone opens the app | Keep Pages private, or don't publish them; don't connect GitHub in the app. |\n\n## Nothing leaves GitHub\n\nLeave model triage, connectors and webhooks unset (they are, by default), and set `[deps] outside_lookups = false`. Runs then talk to GitHub only: your repo, and the benchmark download from public releases. A private repo's Pages are private on plans that allow it; otherwise publish none and read the job summary and the data branch.\n\n## What each token can reach\n\n- **The workflow token** (`GITHUB_TOKEN`): this repo only, with the permissions the workflow file grants.\n- **An optional personal or App token** (`stackvitals-token`): whatever it was granted; the Portal's App is read-only.\n- **Tracker and incident keys:** only their own service's host; the Jira key goes only to an `*.atlassian.net` site.\n- **A model key:** its provider's host, or the `url` you set under `[ci.triage]`.\n","threat-model":"StackVitals runs inside your own GitHub Actions with your repo's tokens, reads text that strangers write (pull request\ntitles, test names, logs, issues, comments) and writes back to GitHub (comments, labels, issues, the data branch). This\npage lists what could go wrong, what stops it, and the committed test that fails if the protection breaks\n(`tests/test_flow_*.py`; a test below that no longer exists fails `test_the_threat_model_names_only_tests_that_exist`).\nWhat leaves the repo is listed separately in [privacy.md](privacy.md); how to report a problem is in\n[SECURITY.md](../SECURITY.md).\n\n## Untrusted input\n\n| Threat | What stops it | Test |\n|---|---|---|\n| A fork PR's test or job name pings people or plants a link in a comment, issue or chat message | Names are code-formatted, `@` and links are neutralised, chat payloads are escaped | `test_real_logs_keep_to_the_failing_step_and_comments_cannot_ping_or_link`, `test_a_flaky_test_name_never_becomes_a_chat_link_or_ping`, `test_preflight_note_cannot_ping_people_or_make_links` |\n| A job or PR name injects markup into the pages | Every page escapes repo text | `test_dashboard_and_summary_escape_job_names_from_untrusted_workflows`, `test_pr_titles_from_strangers_are_escaped` |\n| A test report is a zip or XML bomb | Size limits, in-memory reads, defusedxml | `test_hostile_xml_is_refused_without_crashing`, `test_oversized_artifacts_are_skipped_not_downloaded`, `test_every_report_format_reads_into_one_shape_and_hostile_xml_is_refused` |\n| A failing log tells a model (triage, or an agent using the MCP server) what to do | CI text is fenced and labelled as data | `test_the_ai_prompt_fences_ci_text_so_an_agent_never_obeys_it` |\n| A fork PR's test report claims a real test passed on retry, so it gets called known flaky, re-run or quarantined | Fork runs' flaky and coverage claims are dropped; their failures still show on their own PR | `test_a_fork_report_cannot_mark_a_test_flaky_or_set_coverage` |\n| A pull request's own lockfile or manifest misleads or breaks the dependency check | The PR's code is never run, files are parsed with limits, the check never fails the PR | `test_the_pr_check_cannot_be_fooled_or_broken_by_the_prs_own_files` |\n\n## Fork pull requests and `workflow_run`\n\n| Threat | What stops it | Test |\n|---|---|---|\n| A fork PR's workflow runs with write tokens | StackVitals starts from `workflow_run` after CI, in the base repo's context, and reads the fork's results as data: it never checks out or runs the PR's code | `test_fork_runs_join_the_pr_that_was_open_on_that_branch_at_the_time`, `test_a_fork_pr_is_found_and_the_comment_is_edited_not_reposted` |\n| A fork PR gets a deps comment or a write | The lockfile check comments only on same-repo PRs | `test_the_pr_check_reads_lockfile_changes_and_never_comments_on_forks` |\n\n**Triggers.** The workflow `stackvitals ci init` writes starts on `workflow_run` (after your CI finishes), a schedule,\n`workflow_dispatch`, a push that changes its settings or workflow file, `issues` and `issue_comment` (commands and app\nrequests, checked against the sender's permission), and `pull_request` only when a lockfile changes. It never uses\n`pull_request_target`. On a fork's pull request GitHub gives that `pull_request` run a read-only token and no secrets,\nand StackVitals reads the lockfiles through the API without checking out or running the PR's code. The Portal's\nworkflow starts on a schedule, `workflow_dispatch`, `issues` and `issue_comment` (only `/stackvitals approve` from\nowners, members and collaborators starts a runner; the job checks both people's write access, refuses self-approval,\nand runs only the request text the approval's code names).\n\n| Threat | What stops it | Test |\n|---|---|---|\n| A workflow runs the base repo's token and secrets on a fork's code (`pull_request_target`) | No workflow StackVitals writes or ships uses that trigger | `test_no_workflow_uses_pull_request_target` |\n\n## Who can change things\n\n| Threat | What stops it | Test |\n|---|---|---|\n| Anyone opening an issue or commenting changes settings or decisions | The workflow starts only for owners, members and collaborators; the job checks write access for settings and triage access for decisions | `test_a_request_changes_settings_only_for_someone_with_write_access`, `test_a_comment_command_is_logged_and_reaches_the_quarantine_list` |\n| A golden path writes to a repo its requester can't | The org App checks the requester's write access to the target repo first | `test_a_golden_path_never_writes_to_a_repo_the_requester_cannot_write_to` |\n\n## Keys and tokens\n\n| Threat | What stops it | Test |\n|---|---|---|\n| A tracker key is sent to a host an attacker names in settings | The Jira site must be `*.atlassian.net`, redirects aren't followed, keys go only to their service's own host. The one exception is the triage key: it goes to the https endpoint set in `[ci.triage] url` in the repo's own settings file, which only people with write access can change | `test_the_jira_key_can_only_go_to_an_atlassian_site_even_through_an_app_request`, `test_the_copilot_key_never_reaches_the_report_file_host` |\n| A refused key's text ends up in a page or summary | Errors say what was refused, never the key | `test_a_refused_key_is_said_without_the_key_and_keeps_what_was_pulled` |\n| A workflow runs code from a moved tag | Every action StackVitals' own workflows and the Action use is pinned to a commit (the Action itself is yours to pin; see below) | `test_every_action_is_pinned_to_a_commit` |\n| A new upload to PyPI changes what the Action runs with your token and secrets | The Action installs from its `uv.lock`: exact versions with hashes, never the newest, and only the 11 packages it needs, not the 37 of a full install with the MCP server | `test_the_action_installs_its_locked_dependencies` |\n| A compromised build tool changes a release before it is signed | The build job is read-only with a pinned build backend and no cache; a separate job, behind the `release` environment, signs and publishes | `test_releases_build_read_only_and_sign_in_a_separate_job` |\n\n## The app in the browser\n\n| Threat | What stops it | Test |\n|---|---|---|\n| The app's page runs code from elsewhere | Everything is bundled into the data branch; the page allows only its own scripts (`script-src 'self'`) and talks only to GitHub's API and raw files, other repos' Pages sites (`*.github.io`, to read a public repo's published data), Linear's API and the optional sign-in relay; images load only from itself and GitHub's avatar host (`avatars.githubusercontent.com`) | `test_the_app_ships_with_its_libraries_licences_and_no_remote_code` |\n| A GitHub token pasted in the app outlives the visit | It stays in the tab's session storage and is gone when the tab closes, unless the person ticks \"Remember on this device\"; Sign out removes it. It goes only to `api.github.com`. On an `<owner>.github.io` Pages address, every Pages site of that owner shares one origin, so another of the owner's Pages sites opened in the same tab can read the tab's token (\"Remember\" is off there for the same reason) | |\n| The sign-in relay keeps or leaks tokens | The relay is optional and stateless: it only swaps GitHub's one-time code (with PKCE and a state check) or a refresh token for a token and returns it; it stores nothing and serves only the app's own address | `nothing is logged`, `unknown origins, missing PKCE, foreign redirects and odd grants never reach GitHub` (worker/relay) |\n| A connector secret saved from the app is seen on the way | The value is sealed in the browser (libsodium sealed box) with the repo's own Actions public key; only the sealed value reaches GitHub, and the app never reads a secret back | `a sealed secret opens with the repo's private key and holds the exact value` (web/test) |\n\n## Private data on public pages\n\n| Threat | What stops it | Test |\n|---|---|---|\n| A public repo's data branch keeps private tracker titles, links or sprint names | Titles, links and sprint names are dropped for public repos | `test_a_public_repo_never_keeps_tracker_titles_links_or_sprint_names` |\n| A public portal shows private repos | Private repos are left out of every page and file | `test_private_repos_never_reach_a_public_page_and_forks_are_not_judged` |\n| Code starts sending data to a new host without the privacy page saying so | Every outside host in the code must be on the privacy page | `test_the_privacy_page_names_every_outside_host_the_code_can_reach` |\n| Dependency names leave a private repo | `[deps] outside_lookups = false` keeps every name in the repo | `test_with_outside_lookups_off_no_dependency_name_leaves_the_repo` |\n\n## Losing or corrupting history\n\n| Threat | What stops it | Test |\n|---|---|---|\n| Two runs overwrite each other's history | Pushes retry on top of the other run | `test_overlapping_runs_converge_without_losing_either_side` |\n| A run dies half way and leaves partial data | Nothing is written until the commit; the next run catches up | `test_a_run_that_dies_before_its_commit_leaves_nothing_and_the_next_run_catches_up` |\n| A write to Jira or Linear repeats when a push is retried | Write-back is planned before the commit and sent once after it | `test_write_back_sends_after_the_commit_once_and_never_floods_old_issues` |\n\n## Not covered\n\n- Your own workflow file: StackVitals can't stop someone with write access from editing it.\n- The StackVitals Action reference in your workflow: `@v0` moves with each release. To fix exactly what runs, pin it to a\n release's commit SHA, as you would any action.\n- GitHub itself, the runners and the actions it pins.\n- What a model provider does with what triage sends it (off by default; [privacy.md](privacy.md)).\n"}
@@ -0,0 +1 @@
1
+ {"architecture":"StackVitals has no hosted backend. Everything happens in three places you already have: a GitHub Actions job in your repo, a\nbranch of your repo, and the reader's browser.\n\n```\n your CI runs ──► StackVitals job (GitHub Actions, your token)\n │ reads: Actions runs and logs, pull requests, issues, deployments, test reports\n │ reads (optional, your keys): Jira, Linear, PagerDuty, incident.io, Copilot, Slack, Discord\n ▼\n stackvitals-data branch (one commit per job)\n ├─ ci/ flow/ community/ deps/ portal/ connectors/ history, one file per day\n ├─ summary/ tables the app and Explore read\n ├─ decisions/log.ndjson every decision and settings change, who and why\n └─ docs/ the app (static files) ──► GitHub Pages, a 7-day artifact, or `stackvitals ui`\n │\n ├─► pull request comment and verdict check, issues, chat alerts (after the commit)\n └─► MCP server for AI agents (reads the same branch, or GitHub live)\n\n cross-repo evidence (built once a day by the crawler, published as data, never your private data):\n peers for benchmarks, upgrade outcomes for packages, maintainers' merge habits for Contribute\n```\n\n## One job, one commit\n\nEach run of the Action opens one shallow checkout of the `stackvitals-data` branch, runs every module named in\n`modules:` against it, keeps house (retention, the size cap) and commits once\n([core/workspace.py](../src/stackvitals/core/workspace.py)). If another run pushed first, the push is rejected and\nevery step runs again on the fresh data, so overlapping runs converge without losing either side. What talks to people\n(comments, issues, chat) runs only after the commit, so a refused comment never loses data and a re-run never posts\ntwice.\n\nA module that fails is reported in the job summary; the others still run and save. A run that runs out of API budget\nsaves what it read and says what it left; the next run carries on.\n\n## Storage\n\nHistory is plain files: one gzipped NDJSON file per day per stream, with retention per stream (CI days 180 days,\ntransitions and closed pull requests 400, Copilot 730) and a size cap (250 MB by default) that drops the oldest days\nfirst ([scaling.md](scaling.md)). Busy public repos store about 1 KB per CI run. An opt-in squash rewrites the branch's\ngit history to one commit every 1,000 commits; the files stay byte for byte.\n\nNothing in the branch is a secret: tokens and keys live in Actions secrets, and on a public repo, tracker titles,\nlinks and people's affiliations are never written ([privacy.md](privacy.md)).\n\n## The app\n\nThe app is static: Preact and TypeScript bundled ahead of time (`web/`, built into the package) and copied into the\nbranch's `docs/` folder as `index.html` and `app/`, with a content security policy that allows only its own scripts. Explore and the printable module pages are separate pages\nthat load DuckDB and Chart.js from public CDNs at pinned versions. It reads the JSON tables the job wrote. Settings and\ndecisions made in the app are sent as GitHub issues or comments that the next job applies, or, with the person's own\nGitHub token, committed or run straight from the browser; either way GitHub checks the sender's permissions and the\nchange is recorded in `decisions/log.ndjson`. Explore loads DuckDB in the browser\n(from jsDelivr) to run SQL over the same tables.\n\n## Rules and their evidence\n\nThe verdicts and headline numbers come from rules listed in [bench/accuracy/registry.json](../bench/accuracy/registry.json):\nwhat it was tuned on, the held-out data it was checked on, the bar it had to meet and every change since\n([rules.md](rules.md)). Each card in `bench/accuracy/` holds the method and the results;\n[accuracy.md](accuracy.md) is generated from them, and a test fails when a page and its data disagree.\n\n## Cross-repo evidence\n\nWhat a single repo can't know (how similar repos compare, whether an upgrade broke other projects, how a maintainer\nreviews outsiders) comes from a crawl of public repos (a list of about 125,000, about 90,000 of them sampled for CI benchmarks) run by\nthe project's own scheduled workflows\nin `deploy/crawler/`. It publishes aggregates only. Your job reads them; it never sends your data back.\n\n## Code map\n\n| Folder | What it holds |\n|---|---|\n| `src/stackvitals/ci` | CI Health: collecting runs and test reports, flake detection, the PR verdict, automation |\n| `src/stackvitals/flow` | Team Flow: who acts next, review stages, preflight |\n| `src/stackvitals/core` | the shared job (workspace, store, settings, decisions), Delivery and DORA, Insights, Explore, the app's data |\n| `src/stackvitals/deps` | Dependency Steward |\n| `src/stackvitals/community` | Community health |\n| `src/stackvitals/portal` | Portal: catalog, owners, scorecards, campaigns, golden paths, the org view |\n| `src/stackvitals/connectors` | Jira, Linear, PagerDuty, incident.io, Copilot, Slack, Discord, Discourse |\n| `src/stackvitals/web` | the app and the site |\n| `src/stackvitals/mcp_server.py` | the MCP server |\n| `src/mergescout` | Contribute (MergeScout) |\n| `bench/accuracy` | accuracy cards, the rule registry and the generators for the accuracy pages |\n| `worker/` | optional Cloudflare Worker for instant alerts |\n| `deploy/crawler` | the crawler's workflows |\n","roadmap":"What comes next, in rough order. Dates are when the data allows a result, not promises. Suggestions are welcome as\nissues; the ones backed by a real repo's problem move up.\n\n## Checks already running, read on a date\n\n- **Does the issue finder pick issues outsiders get merged?** Its ranking was frozen and its picks recorded before the\n outcomes existed. Read from 2027-03-20, when the attempt and merge windows have closed\n ([bench/accuracy/contribute](../bench/accuracy/contribute/README.md)).\n- **Do agents answer older CI failures better with StackVitals?** On failures inside GitHub's 90-day log retention it\n added nothing measurable. The test that matters is on failures whose logs GitHub has deleted; it runs once enough of\n the recorded failures have passed GitHub's 90-day retention ([bench/accuracy/agents](../bench/accuracy/agents/README.md)).\n\n## Accuracy\n\n- A person's audit of a sample of every card's labels; today two model families label and no person has checked.\n- Status precision for flaky CI: re-running pinned commits to confirm flakes directly, not only from history.\n- Deployment detection for shipping workflows with plain names (docker, pypi, CD), the weak side of today's rule.\n\n## Coverage\n\n- Bors and other merge bots outside GitHub's merge queue; monorepo task runners (Nx, Turborepo, Bazel) that skip\n unchanged tests; GitHub Enterprise Server. Each is untested today ([limitations.md](limitations.md#failure-modes)).\n- Dependency dossiers beyond npm and PyPI, and dependency paths from poetry and pip lockfiles.\n- GitLab merge requests, deployments and incidents (CI already runs inside GitLab).\n\n## Not planned\n\n- A hosted service or an account: the suite stays in your repo.\n- Productivity scores for individuals. Where StackVitals names people (an owner, who acts next on a pull request), it\n is to route work, not to rate them.\n","settings":"Put these in `.github/stackvitals.toml` on your default branch. A merged change applies on the next run (the workflow\nruns when the file is pushed). A missing file means the defaults; a value StackVitals can't use keeps its\ndefault and is named in the job summary. The app's Settings page edits the same file.\n\n\n## [ci.monitors.pass_on_retry]\n\n| Key | Default | What it does |\n|---|---|---|\n| `min_events` | `2` | Fail-then-pass events with no code change before a test or job is flaky. |\n| `quiet_days` | `14` days | Days without a flake before the pass-on-retry monitor lets go. |\n| `quiet_runs` | `10` runs | Clean runs in a row before a flaky test counts as fixed. |\n\n## [ci.monitors.failure_rate]\n\n| Key | Default | What it does |\n|---|---|---|\n| `enabled` | `true` | Watch each test's failure share on the default branch. |\n| `window_runs` | `50` runs | Latest default-branch runs the failure rate looks at. |\n| `min_runs` | `20` runs | Runs needed before the failure-rate monitor can fire. |\n| `activate` | `0.15` | Failure share that turns the monitor on. |\n| `resolve` | `0.05` | The monitor stays on while the failure share is above this. |\n\n## [ci.monitors.broken]\n\n| Key | Default | What it does |\n|---|---|---|\n| `streak` | `3` runs | Default-branch runs in a row that all failed: the test keeps failing (broken). |\n| `window_runs` | `5` runs | Latest runs the broken monitor looks at. |\n| `activate` | `0.8` | Failure share in that window that marks a test broken. |\n| `resolve` | `0.5` | A broken test stays broken while its failure share is at least this. |\n\n## [ci.monitors.slow]\n\n| Key | Default | What it does |\n|---|---|---|\n| `p50_ms` | `5000` ms | Label a test slow when its median passing time is above this. |\n\n## [ci.monitors.timeout_inflation]\n\n| Key | Default | What it does |\n|---|---|---|\n| `ratio` | `2.0` x | Label a test when its median failure time is at least this many times its P95 pass time. |\n| `min_failures` | `3` | Failures needed before that label shows. |\n\n## [ci.mute]\n\n| Key | Default | What it does |\n|---|---|---|\n| `\"test id or pattern\"` | `{} (none)` | Test ids or glob patterns muted until a date: still measured, but no alert and no new issue. |\n\n## [ci.quarantine]\n\n| Key | Default | What it does |\n|---|---|---|\n| `always` | `[] (none)` | Test ids always on the quarantine list, flaky or not. |\n| `never` | `[] (none)` | Test ids never quarantined, even when flaky. |\n| `max_days` | `0` | Days a flaky test may stay in quarantine on its own before it fails builds again and is listed as overdue for a fix; 0 never ends it. Tests someone quarantined by hand stay. |\n\n## [ci.coverage]\n\n| Key | Default | What it does |\n|---|---|---|\n| `min_patch` | `0.0` | The verdict check fails when fewer of a pull request's added lines ran than this (from the coverage report CI uploads); 0 is off. |\n\n## [ci.triage]\n\n| Key | Default | What it does |\n|---|---|---|\n| `provider` | `\"off\"` | A model reads the log of up to 3 failures the history can't decide and says whether they look like the change, beside the verdict (never in it); needs the triage-key input. Setups for each provider: docs/triage.md (gemini has a free tier). |\n| `model` | `\"\"` | The model to ask (empty: the provider's suggested small model). |\n| `url` | `\"\"` | Another OpenAI-compatible endpoint (https) instead of the provider's own. |\n| `allow_private` | `false` | Send a private repo's failing logs too (off: free tiers may keep what they're sent). |\n\n## [delivery]\n\n| Key | Default | What it does |\n|---|---|---|\n| `deploy_workflow` | `\"(?i)(deploy|release|publish|push-image)\"` | Workflow names that count as deployments when the repo has no production deployments. |\n| `production` | `[\"production\", \"prod\", \"live\"]` | GitHub deployment environments that count as production (matched on the first word). |\n| `incident_labels` | `[\"incident\", \"outage\", \"sev1\", \"sev-1\", \"sev2\", \"sev-2\", \"p0\"]` | Issue labels that mark an incident. |\n| `hotfix_labels` | `[\"hotfix\"]` | Pull request labels that mark a hotfix (counted as an incident). |\n| `source` | `\"auto\"` | Where deployments come from; auto tries deployments, deploy workflows, then releases; merges counts every merged pull request as one (only when chosen: it is a poor guess for most repos). |\n| `fix_patterns` | `[] (none)` | Regular expressions on a merged pull request's title or branch that mark it as a fix, with one group naming the pull request it fixes, like ^fix.*#([0-9]+) or ^hotfix/([0-9]+)-; the incident blames the deployment that shipped that pull request. |\n| `incident_sources` | `[] (none)` | Where incidents come from: any of issues, reverts, hotfixes, fixes, pager, tracker, recorded; empty keeps the automatic choice (a pager tool's alone when connected, else all of GitHub's). |\n| `production_branches` | `[] (none)` | Branches besides the default one that ship to production (globs, like production or release/*): deploy workflows on them count, and so do pull requests merged into them. |\n| `ignore_reverts_of` | `[] (none)` | Reverts that are not incidents: of pull requests touching only these paths (globs, like docs/**) or by these authors (like dependabot[bot]). |\n| `dora_report` | `\"2023\"` | The DORA report whose tiers DevLake's dashboard uses. |\n\n## [investment]\n\n| Key | Default | What it does |\n|---|---|---|\n| `labels` | `{} (none)` | Your own labels for each kind of work (feature, fix, maintenance, security, docs), like maintenance = [\"area: platform\"] under [investment.labels]; checked before the common ones, for where the work went. |\n\n## [rules]\n\n| Key | Default | What it does |\n|---|---|---|\n| `rules` | `[] (none)` | PR rules as code: each [[rules]] has a name, a when (author, paths, lines_over, risk, labels, first_time) and a then (label, reviewers, comment); dry_run = true only reports. Each action happens once per pull request; the Pull Requests page shows what each rule matched before and now. |\n\n## [calendar]\n\n| Key | Default | What it does |\n|---|---|---|\n| `weekends` | `[] (none)` | Days the team doesn't work, like saturday and sunday; left out of review phases and cycle time (DORA's DevLake-matching numbers stay in calendar time, with a working-time lead time beside them). |\n| `holidays` | `[] (none)` | Dates the team doesn't work, like 2026-12-25; left out the same way. |\n\n## [services]\n\n| Key | Default | What it does |\n|---|---|---|\n| `services` | `{} (none)` | Services inside this repo (a monorepo): under [services.<name>], paths is a list of CODEOWNERS-style patterns (like [\"services/api/\", \"libs/shared/**\"]) and deploy an optional pattern matched against each deployment's environment or workflow name. Delivery then shows review phases per service, and DORA per service for those with a deploy pattern; a pull request touching two services counts in both. |\n\n## [identity]\n\n| Key | Default | What it does |\n|---|---|---|\n| `bots` | `[] (none)` | Logins that are bots even if GitHub says otherwise. |\n| `people` | `[] (none)` | Logins that are people even if they look like bots. |\n| `aliases` | `{} (none)` | Old login to current login, so one person's history stays together. A key can also be a tool's id (`jira:<account id>`, `linear:<user id>`, `slack:<member id>`, `discord:<user id>`) or an email, so one person counts once across tools. |\n| `teams` | `{} (none)` | Team name to its members' logins, for numbers by team. |\n| `knowledge_names` | `false` | On a public repo, name the person behind each area in the knowledge map (folders one person knows, authors gone quiet). Off by default: public pages show the areas only. Private repos always show names. |\n\n## [alerts]\n\n| Key | Default | What it does |\n|---|---|---|\n| `pickup_p75_hours` | `4.0` h | Alert when 3 in 4 PRs wait longer than this for a first comment or review (last 28 days); 0 turns it off. |\n| `review_p75_hours` | `14.0` h | Alert when 3 in 4 reviews take longer than this; 0 turns it off. |\n| `cycle_p75_hours` | `72.0` h | Alert when 3 in 4 PRs take longer than this from first commit to merge; 0 turns it off. |\n| `change_failure_rate` | `0.15` | Alert when more than this share of deployments led to an incident; 0 turns it off. |\n| `channels` | `[\"webhook\"]` | Where alerts go: the webhook from the workflow, and/or one issue in this repo. |\n\n## [automation]\n\n| Key | Default | What it does |\n|---|---|---|\n| `pr_comments` | `the workflow input` | One comment on failed PR runs saying which failures are known flakes. |\n| `rerun_flaky` | `the workflow input` | Re-run failed jobs once when every failure is a known flake (needs actions: write). |\n| `quarantine` | `the workflow input` | Keep the quarantine list for the stackvitals ci quarantine command. |\n| `issues` | `the workflow input` | One issue per flaky test or job, closed once fixed (needs issues: write). |\n| `mention_owners` | `the workflow input` | @-mention code owners in those issues (off: owners are named without a notification). |\n| `assign_owners` | `the workflow input` | Assign a new flaky-test issue to its first code owner who is a person. |\n| `preflight_comments` | `the workflow input` | One note on new outside PRs that miss what merged PRs here usually include. |\n| `deps_comments` | `the workflow input` | One comment on same-repo PRs listing changed packages and their verdicts. |\n| `review_requests` | `false` | Off until turned on: request the top suggested reviewer (who knows the folders, who reviewed the author's PRs, eased by load) on ready PRs nobody was asked to review; once per PR. Needs pull-requests: write. |\n| `fix_requests` | `false` | Off until turned on: one issue per failure that breaks the default branch every run, closed with proof once fixed. Needs issues: write. |\n| `fix_by_copilot` | `false` | With fix_requests: assign each new one to Copilot's coding agent. |\n| `size_labels` | `false` | Off until turned on: a size/XS to size/XL label on each open PR by lines changed (under 10, 100, 500, 1000). |\n| `docs_label` | `false` | Off until turned on: label PRs that only change docs (Markdown, docs/, README) as low risk: docs only; never approves. |\n| `label_stuck` | `the workflow input` | Label PRs whose asked-for review has not started after 3 days, and unlabel them later. |\n| `verdict_check` | `the workflow input` | A \"StackVitals verdict\" check on pull requests: one required check that waits for every workflow on the newest commit and passes once CI is green (and patch coverage holds); known flakes are re-run, never let through (with rerun_flaky, automatically). Needs checks: write. On a private repo also set the repository variable STACKVITALS_EVERY_CI_RUN to true, so passing CI runs start a job. |\n| `verdict_strict` | `the workflow input` | With the verdict check: when a failure the comment called likely from this change goes green on a re-run of the same code, the check stays red until someone with triage access comments /stackvitals accept rerun on the pull request. Off by default (a green re-run passes). |\n\n## [agreements]\n\n| Key | Default | What it does |\n|---|---|---|\n| `review_within_hours` | `0` h | Agreement: every PR gets a first comment or review within this many hours; 0 is off. |\n| `merge_within_days` | `0` days | Agreement: PRs merge within this many days of being ready; 0 is off. |\n| `max_pr_lines` | `0` lines | Agreement: PRs stay under this many changed lines; 0 is off. |\n| `linked_issue` | `false` | Agreement: every PR links the issue it solves. |\n| `review_before_merge` | `false` | Agreement: nothing merges without a review. |\n\n## [digest]\n\n| Key | Default | What it does |\n|---|---|---|\n| `enabled` | `true` | Post a weekly digest: what changed, what to fix first, decisions ending soon. |\n| `pulse` | `true` | With the issue digest, three pulse questions (flow, friction, satisfaction) answered by reaction; next week's digest shows only the counts (GitHub shows reactions to anyone who can see the issue; on a public repo only the team's own people count). |\n| `day` | `\"monday\"` | The day the digest goes out (the first scheduled run after the hour). |\n| `hour` | `6` UTC | The hour, in UTC, from which the digest may go out on that day. |\n| `channels` | `[\"issue\"]` | Where the digest goes: one pinned issue in this repo, and/or the webhook. |\n\n## [deps.policy]\n\n| Key | Default | What it does |\n|---|---|---|\n| `min_scorecard` | `the workflow input` | Projects behind the dependencies scoring under this OpenSSF Scorecard (0 to 10) are listed on the supply page, unless a decision accepted the package. Empty is off. |\n| `max_severity` | `the workflow input` | The pull request check flags a changed package with an open advisory rated above this (low, moderate, high or critical). Empty is off. |\n\n## [deps]\n\n| Key | Default | What it does |\n|---|---|---|\n| `outside_lookups` | `true` | Look dependencies' public package names up at deps.dev, OpenSSF Scorecard, OSV, npm/PyPI release data, endoflife.date and the registries' download counts (docs/privacy.md); off scans with downloaded OSV databases, skips release checks, runtimes and GuardDog, and keeps every dependency name in the repo. |\n| `guarddog` | `false` | Off by default: the pull request check also runs DataDog's GuardDog on each npm or PyPI package version it adds or upgrades (at most 10), which downloads the package and looks for malicious code (install hooks, obfuscation, data sent out); a finding is a reason to look first. Needs outside_lookups on; Linux runners only. |\n\n## [deps.licenses]\n\n| Key | Default | What it does |\n|---|---|---|\n| `deny` | `[] (none)` | SPDX licence ids the repo's dependencies must not use (* allowed, like GPL-3.0*); a package offering an allowed choice is fine. Empty is off. |\n\n## [benchmarks]\n\n| Key | Default | What it does |\n|---|---|---|\n| `share` | `false` | Off by default: each CI run's summary offers a link that files this repo's rounded CI figures (pushes, time to green, first-try share; no names, no code) as an issue on StackVitals' public repo, so private repos get peer groups once 10+ of a kind have shared. Nothing is sent unless a person opens the link and files it. |\n| `name` | `false` | With benchmarks.share on: include this repo's name in the shared figures. Off shares them without it. |\n\n## [community]\n\n| Key | Default | What it does |\n|---|---|---|\n| `capacity` | `\"\"` | Who maintains the project and how much time they have, in your words (like \"two volunteers, evenings and weekends\"); shown on the Community page so the numbers are read against it. |\n| `norms` | `\"\"` | How the project works with people, in your words (like \"issues are triaged on Mondays; ideas go to Discussions first\"); shown on the Community page. |\n\n## [connectors.jira]\n\n| Key | Default | What it does |\n|---|---|---|\n| `url` | `\"\"` | Your Jira Cloud site, like https://acme.atlassian.net. The API token goes in the JIRA_API_TOKEN secret, never here. |\n| `email` | `\"\"` | The Jira account's email, when it is not in the JIRA_EMAIL secret. |\n| `scope_jql` | `\"\"` | JQL for the issues to read, like project in (WEB, API); empty reads every issue the account can see. |\n| `incident_jql` | `\"\"` | JQL for the issues that are incidents, like issuetype = Incident; they count in DORA's change failure rate and recovery. |\n| `key_expires` | `\"\"` | When the Jira API token expires (a year at most); a warning shows 14 days before. |\n| `write_back` | `false` | Comment on the Jira issue a merged pull request names (the account must be able to comment); off by default. |\n| `done_transition` | `\"\"` | With write-back on, the transition to move that issue through, like Done; empty only comments. |\n| `flaky_tickets` | `false` | Open a Jira issue for each test that turns flaky, in ticket_project, and comment and move it to done_transition (or Done) once fixed; the key must be able to write. |\n| `ticket_project` | `\"\"` | The Jira project key flaky-test issues go in, like WEB. |\n| `team_field` | `\"\"` | A Jira field that names each issue's team (its name, like Team, or its id, like customfield_10001): done issues are counted per team. Private repos only. |\n| `severity_field` | `\"\"` | A Jira field that gives each incident's severity (name or id), shown with the incident. |\n\n## [connectors.linear]\n\n| Key | Default | What it does |\n|---|---|---|\n| `teams` | `[] (none)` | Linear team keys to read, like ENG; empty reads every team the key can see. |\n| `incident_labels` | `[\"incident\"]` | Linear labels that mark an incident. |\n| `key_expires` | `\"\"` | When the Linear key expires, if it does; a warning shows 14 days before. |\n| `write_back` | `false` | Comment on the Linear issue a merged pull request names (the key needs write access); off by default. |\n| `done_state` | `\"\"` | With write-back on, the team's workflow state to move that issue to, like Done; empty only comments. |\n| `flaky_tickets` | `false` | Open a Linear issue for each test that turns flaky, in ticket_team, and comment and move it to done_state (or Done) once fixed; the key must be able to write. |\n| `ticket_team` | `\"\"` | The Linear team key flaky-test issues go in, like ENG. |\n\n## [connectors.discourse]\n\n| Key | Default | What it does |\n|---|---|---|\n| `url` | `\"\"` | The project's Discourse forum, like https://discuss.example.org; its public topics are read with no key (times and flags only, never text or usernames). |\n| `team` | `[] (none)` | Forum usernames whose replies count as the project's (forum staff always do). |\n| `groups` | `[] (none)` | Forum groups whose members' replies count as the project's. |\n\n## [connectors.slack]\n\n| Key | Default | What it does |\n|---|---|---|\n| `channels` | `[] (none)` | Slack support channel ids to read (the bot from the slack-token input must be in each); only times and flags are kept. |\n| `team` | `[] (none)` | Slack user ids whose replies count as the project's. |\n| `key_expires` | `\"\"` | When the Slack bot token expires, if it does; a warning shows 14 days before. |\n\n## [connectors.discord]\n\n| Key | Default | What it does |\n|---|---|---|\n| `channels` | `[] (none)` | Discord channel ids to read (the bot from the discord-token input needs View Channel and Read Message History); only times and flags are kept. |\n| `team` | `[] (none)` | Discord user ids whose replies count as the project's. |\n| `key_expires` | `\"\"` | When the Discord bot token expires, if it does; a warning shows 14 days before. |\n\n## [connectors.pagerduty]\n\n| Key | Default | What it does |\n|---|---|---|\n| `services` | `[] (none)` | PagerDuty service ids whose incidents count; empty counts every service the key can see. |\n| `urgencies` | `[\"high\"]` | Which PagerDuty urgencies count as production incidents. |\n| `key_expires` | `\"\"` | When the PagerDuty key expires, if it does; a warning shows 14 days before. |\n\n## [connectors.incident_io]\n\n| Key | Default | What it does |\n|---|---|---|\n| `incident_types` | `[] (none)` | incident.io incident types that count, like Platform; empty counts every type (test and tutorial incidents never count). |\n| `key_expires` | `\"\"` | When the incident.io key expires, if it does; a warning shows 14 days before. |\n\n## [connectors.copilot]\n\n| Key | Default | What it does |\n|---|---|---|\n| `org` | `\"\"` | The organization whose Copilot usage reports to keep; empty means this repo's owner. |\n| `key_expires` | `\"\"` | When the Copilot reports token expires, if it does; a warning shows 14 days before. |\n\n## [connectors.sonar]\n\n| Key | Default | What it does |\n|---|---|---|\n| `url` | `\"https://sonarcloud.io\"` | SonarCloud (https://sonarcloud.io) or your SonarQube server. |\n| `project` | `\"\"` | The Sonar project key, like myorg_myrepo; its quality gate and measures are read each day. A private project or SonarQube server needs the SONAR_TOKEN secret. |\n| `key_expires` | `\"\"` | When the Sonar token expires, if one is used; a warning shows 14 days before. |\n\n## [connectors.ai_spend]\n\n| Key | Default | What it does |\n|---|---|---|\n| `key_expires` | `\"\"` | When the soonest of the AI spend admin keys (Anthropic, OpenAI, Cursor) expires, if it does; a warning shows 14 days before. |\n\nExample:\n\n```toml\n[alerts]\npickup_p75_hours = 6\nchannels = [\"webhook\", \"issue\"]\n\n[ci.mute]\n\"tests/test_api.py::test_slow_network\" = 2026-12-31\n\n[automation]\nissues = true\n```\n"}
@@ -0,0 +1 @@
1
+ .uplot,.uplot *,.uplot *:before,.uplot *:after{box-sizing:border-box}.uplot{font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";line-height:1.5;width:min-content}.u-title{text-align:center;font-size:18px;font-weight:700}.u-wrap{position:relative;user-select:none}.u-over,.u-under{position:absolute}.u-under{overflow:hidden}.uplot canvas{display:block;position:relative;width:100%;height:100%}.u-axis{position:absolute}.u-legend{font-size:14px;margin:auto;text-align:center}.u-inline{display:block}.u-inline *{display:inline-block}.u-inline tr{margin-right:16px}.u-legend th{font-weight:600}.u-legend th>*{vertical-align:middle;display:inline-block}.u-legend .u-marker{width:1em;height:1em;margin-right:4px;background-clip:padding-box!important}.u-inline.u-live th:after{content:":";vertical-align:middle}.u-inline:not(.u-live) .u-value{display:none}.u-series>*{padding:4px}.u-series th{cursor:pointer}.u-legend .u-off>*{opacity:.3}.u-select{background:#00000012;position:absolute;pointer-events:none}.u-cursor-x,.u-cursor-y{position:absolute;left:0;top:0;pointer-events:none;will-change:transform}.u-hz .u-cursor-x,.u-vt .u-cursor-y{height:100%;border-right:1px dashed #607D8B}.u-hz .u-cursor-y,.u-vt .u-cursor-x{width:100%;border-bottom:1px dashed #607D8B}.u-cursor-pt{position:absolute;top:0;left:0;border-radius:50%;border:0 solid;pointer-events:none;will-change:transform;background-clip:padding-box!important}.u-axis.u-off,.u-select.u-off,.u-cursor-x.u-off,.u-cursor-y.u-off,.u-cursor-pt.u-off{display:none}:root{--bg: #F3F4F6;--sidebar: #ECEEF1;--surface: #FFFFFF;--surface-2: #F6F7F9;--field: #EEF0F3;--ink: #1B1F24;--ink-2: #4F5763;--ink-3: #626A76;--line: #E0E3E8;--line-strong: #C9CED6;--accent: #2160C4;--accent-ink: #FFFFFF;--accent-soft: #E5EDFA;--good: #146F40;--good-soft: #E3F2E9;--good-solid: #146F40;--good-solid-ink: #FFFFFF;--bad: #B8322A;--bad-soft: #FBE9E7;--bad-solid: #B8322A;--bad-solid-ink: #FFFFFF;--watch: #8A4F06;--watch-soft: #FAEFDD;--neutral-soft: #E7E9ED;--neutral-ink: #3C434D;--scrim: rgba(20, 26, 33, .36);--shadow-1: 0 1px 2px rgba(22, 30, 40, .06);--shadow-pop: 0 18px 50px rgba(22, 30, 40, .18), 0 2px 6px rgba(22, 30, 40, .08);--focus: #2160C4;--code-bg: #1E242C;--code-ink: #E6EBF1;--code-dim: #9AA5B1;--code-line: #2E3640;--logo-tile: #2160C4;--logo-fg: #FFFFFF;--logo-acc: #7CF0B6;--font: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;--mono: ui-monospace, "SF Mono", "Cascadia Mono", Consolas, monospace;--spring: cubic-bezier(.32, .72, 0, 1);--spring-pop: cubic-bezier(.34, 1.4, .64, 1);--ease-out: cubic-bezier(.22, 1, .36, 1);--dur-1: .22s;--dur-2: .32s;--dur-3: .42s;--r-lg: 20px;--r-md: 14px;--r-sm: 10px;--side-w: 252px;--top-h: 56px;--gutter: 40px;color-scheme:light}@media(prefers-color-scheme:dark){:root:not([data-theme=light]){--bg: #0E141B;--sidebar: #111922;--surface: #17202A;--surface-2: #1C2631;--field: #212C38;--ink: #E9EEF3;--ink-2: #AEB8C4;--ink-3: #94A0AD;--line: #26313D;--line-strong: #364352;--accent: #79AEFF;--accent-ink: #0B1A30;--accent-soft: #1B2B42;--good: #4CC487;--good-soft: #14291E;--good-solid: #3DB878;--good-solid-ink: #062012;--bad: #FF8578;--bad-soft: #331A19;--bad-solid: #F06A5D;--bad-solid-ink: #2A0703;--watch: #F0B45C;--watch-soft: #2D2416;--neutral-soft: #26313D;--neutral-ink: #D5DCE4;--scrim: rgba(0, 0, 0, .55);--shadow-1: 0 0 0 1px rgba(255, 255, 255, .04);--shadow-pop: 0 18px 50px rgba(0, 0, 0, .55), 0 0 0 1px rgba(255, 255, 255, .07);--focus: #79AEFF;--code-bg: #0B1016;--code-line: #1F2A35;--logo-tile: #2F6FD8;color-scheme:dark}}:root[data-theme=dark]{--bg: #0E141B;--sidebar: #111922;--surface: #17202A;--surface-2: #1C2631;--field: #212C38;--ink: #E9EEF3;--ink-2: #AEB8C4;--ink-3: #94A0AD;--line: #26313D;--line-strong: #364352;--accent: #79AEFF;--accent-ink: #0B1A30;--accent-soft: #1B2B42;--good: #4CC487;--good-soft: #14291E;--good-solid: #3DB878;--good-solid-ink: #062012;--bad: #FF8578;--bad-soft: #331A19;--bad-solid: #F06A5D;--bad-solid-ink: #2A0703;--watch: #F0B45C;--watch-soft: #2D2416;--neutral-soft: #26313D;--neutral-ink: #D5DCE4;--scrim: rgba(0, 0, 0, .55);--shadow-1: 0 0 0 1px rgba(255, 255, 255, .04);--shadow-pop: 0 18px 50px rgba(0, 0, 0, .55), 0 0 0 1px rgba(255, 255, 255, .07);--focus: #79AEFF;--code-bg: #0B1016;--code-line: #1F2A35;--logo-tile: #2F6FD8;color-scheme:dark}@media(prefers-contrast:more){:root{--ink-2: var(--ink);--ink-3: #3A414B;--line: #9AA2AD;--line-strong: #6B7480;--shadow-1: 0 0 0 1px var(--line-strong)}:root[data-theme=dark]{--ink-3: #C9D1DA;--line: #5B6774;--line-strong: #8794A2}}@media(prefers-contrast:more)and (prefers-color-scheme:dark){:root:not([data-theme=light]){--ink-3: #C9D1DA;--line: #5B6774;--line-strong: #8794A2}}@media(prefers-reduced-motion:reduce){:root{--dur-1: 1ms;--dur-2: 1ms;--dur-3: 1ms}*,*:before,*:after{animation-duration:1ms!important;animation-delay:0ms!important;transition-duration:1ms!important;transition-delay:0ms!important;scroll-behavior:auto!important}}:root.calm{--dur-1: 1ms;--dur-2: 1ms;--dur-3: 1ms}:root.calm *,:root.calm *:before,:root.calm *:after{animation-duration:1ms!important;animation-delay:0ms!important;transition-duration:1ms!important;transition-delay:0ms!important}*,*:before,*:after{box-sizing:border-box}html{-webkit-text-size-adjust:100%;text-size-adjust:100%;background:var(--bg)}@media(min-width:821px){html{scrollbar-gutter:stable}}html.is-locked{overflow:hidden}html.is-theming *,html.is-theming *:before,html.is-theming *:after{transition:background-color var(--dur-3) var(--ease-out),border-color var(--dur-3) var(--ease-out),color var(--dur-2) var(--ease-out)!important}body{margin:0;min-height:100vh;min-height:100dvh;font-family:var(--font);font-size:15px;line-height:1.5;color:var(--ink);background:var(--bg);-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;overflow-x:hidden}h1,h2,h3,h4,p{margin:0}a{color:var(--accent);text-underline-offset:2px}a:hover{text-decoration-thickness:2px}button{font:inherit;color:inherit;background:none;border:0;padding:0;cursor:pointer;-webkit-tap-highlight-color:transparent}button:disabled{cursor:default}a{-webkit-tap-highlight-color:transparent}input,select,textarea{font:inherit;color:inherit}code,pre{font-family:var(--mono)}img,svg{display:block}.ic{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;flex:none;display:inline-block}.num{font-variant-numeric:tabular-nums;font-feature-settings:"tnum" 1}:focus{outline:none}:focus-visible{outline:2.5px solid var(--focus);outline-offset:2px;border-radius:10px}.sr{position:absolute!important;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap}.muted{color:var(--ink-2)}.small{font-size:13px}.mono-soft{font-family:var(--mono);font-size:.9em;overflow-wrap:anywhere}code{font-size:.88em;background:var(--neutral-soft);color:var(--ink);padding:1px 6px;border-radius:6px;overflow-wrap:anywhere}.code-inline{display:inline-block;max-width:100%;white-space:pre-wrap}.press{transition:transform var(--dur-1) var(--ease-out),background-color var(--dur-1),color var(--dur-1),box-shadow var(--dur-1),border-color var(--dur-1),opacity var(--dur-1)}.press:active:not(:disabled){transform:scale(.965)}.skip{position:fixed;left:12px;top:-60px;z-index:200;padding:10px 16px;border-radius:12px;background:var(--ink);color:var(--bg);font-weight:700;text-decoration:none;transition:top var(--dur-1)}.skip:focus{top:12px}.splash{position:fixed;inset:0;z-index:300;display:grid;place-items:center;background:var(--bg);transition:opacity var(--dur-3) var(--ease-out),visibility 0s var(--dur-3)}.splash.is-gone{opacity:0;visibility:hidden}.splash-mark{width:64px;height:64px;display:block;animation:splash 1.4s var(--ease-out) infinite alternate}.splash-mark svg{width:100%;height:100%}@keyframes splash{0%{transform:scale(.92);opacity:.75}to{transform:scale(1);opacity:1}}.app{min-height:100vh;min-height:100dvh}.sidebar{position:fixed;inset:0 auto 0 0;width:var(--side-w);z-index:30;display:flex;flex-direction:column;gap:6px;padding:14px 12px 12px;background:var(--sidebar);border-right:1px solid var(--line);overflow-y:auto;overscroll-behavior:contain;scrollbar-width:thin}.side-brand{padding:2px 2px 10px}.brand{display:inline-flex;align-items:center;gap:10px;padding:6px 10px 6px 6px;border-radius:14px;font-weight:800;font-size:17px;letter-spacing:-.015em}.brand:hover{background:color-mix(in srgb,var(--ink) 6%,transparent)}.logo-mark{display:inline-block;flex:none;border-radius:26%;animation:logoIn var(--dur-3) var(--spring-pop) backwards}.logo-mark svg{width:100%;height:100%}@keyframes logoIn{0%{opacity:0;transform:scale(.7) rotate(-8deg)}}.side-nav{display:flex;flex-direction:column;gap:10px}.nav-group{display:flex;flex-direction:column;gap:1px}.nav-group-label{font-size:13px;font-weight:600;color:var(--ink-3);padding:6px 12px 4px}.nav-item{display:flex;align-items:center;gap:11px;padding:8px 12px;border-radius:11px;color:var(--ink-2);font-weight:600;font-size:14.5px;text-decoration:none;min-height:38px}.nav-item:hover{background:color-mix(in srgb,var(--ink) 6%,transparent);color:var(--ink)}.nav-item[aria-current=page]{background:var(--surface);color:var(--ink);box-shadow:var(--shadow-1),0 1px 2px #0000000d}.nav-item[aria-current=page] .ic{color:var(--accent)}.nav-item.is-off .nav-label{opacity:.7}.nav-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.badge{min-width:22px;padding:1px 7px;border-radius:99px;font-size:12px;font-weight:750;text-align:center;line-height:18px}.badge-bad{background:var(--bad-soft);color:var(--bad)}.badge-watch{background:var(--watch-soft);color:var(--watch)}.badge-accent{background:var(--accent-soft);color:var(--accent)}.side-foot{margin-top:auto;padding-top:10px;border-top:1px solid var(--line)}.side-health{display:flex;align-items:center;gap:10px;width:100%;text-align:left;padding:8px 10px;border-radius:12px;font-size:13px;color:var(--ink-2)}.side-health:hover{background:color-mix(in srgb,var(--ink) 6%,transparent);color:var(--ink)}.side-health b{display:block;color:var(--ink);font-weight:650}.side-health small{display:block;font-size:12px;color:var(--ink-3)}.side-demo{font-size:12px;color:var(--ink-3);padding:4px 10px 0}@media(max-height:900px){.sidebar{overscroll-behavior:auto;padding-top:8px}.side-brand{padding-bottom:2px}.side-nav{gap:4px}.nav-group-label{padding:4px 12px 2px}.nav-item{min-height:31px;padding:5px 12px}.side-health small,.side-demo{display:none}}.dot{width:9px;height:9px;border-radius:50%;flex:none;display:inline-block}.dot-good{background:var(--good)}.dot-watch{background:var(--watch)}.dot-bad{background:var(--bad)}.main-col{margin-left:var(--side-w);min-width:0}.topbar{position:sticky;top:0;z-index:20;height:var(--top-h);display:flex;align-items:center;gap:12px;padding:0 var(--gutter);background:transparent;border-bottom:1px solid transparent;transition:background-color var(--dur-2) var(--ease-out),border-color var(--dur-2) var(--ease-out)}.topbar.is-compact{background:color-mix(in srgb,var(--bg) 84%,transparent);-webkit-backdrop-filter:saturate(1.6) blur(16px);backdrop-filter:saturate(1.6) blur(16px);border-bottom-color:var(--line)}.topbar-logo{display:none}.topbar-title{flex:1;min-width:0;font-weight:750;font-size:16px;letter-spacing:-.01em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;opacity:0;transform:translateY(6px);transition:opacity var(--dur-2) var(--ease-out),transform var(--dur-3) var(--spring)}.topbar.is-compact .topbar-title{opacity:1;transform:none}.profile{margin-left:auto;display:inline-flex;align-items:center;gap:9px;padding:4px 10px 4px 4px;border-radius:99px;max-width:320px;min-height:40px;background:var(--surface);border:1px solid var(--line);font-weight:650;font-size:14px;box-shadow:var(--shadow-1)}.profile:hover{border-color:var(--line-strong)}.profile-name{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.profile-chev{width:16px;height:16px;color:var(--ink-3)}.repo-av{display:inline-grid;place-items:center;flex:none;border-radius:30%;font-weight:800;color:#fff;background:hsl(var(--hue, 212) 58% 42%)}main{display:block;max-width:1240px;margin:0 auto;padding:4px var(--gutter) 96px;min-width:0}main:focus{outline:none}.page-enter{animation:pageIn var(--dur-3) var(--spring) backwards}@keyframes pageIn{0%{opacity:0;transform:translateY(10px)}}.demo-banner{display:flex;align-items:center;gap:10px;margin:0 0 12px;padding:9px 14px;border-radius:14px;background:var(--accent-soft);color:var(--ink);font-size:13.5px;font-weight:600}.demo-banner .ic{color:var(--accent);width:18px;height:18px}.tabbar{display:none}.more-group+.more-group{margin-top:14px}.more-label{font-size:13px;font-weight:700;color:var(--ink-2);margin:0 4px 8px}.more-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:8px}.more-item{position:relative;display:flex;align-items:center;gap:10px;padding:12px;border-radius:14px;background:var(--surface-2);color:var(--ink);font-weight:650;font-size:14px;text-decoration:none;min-height:48px}.more-item[aria-current=page]{background:var(--accent-soft);color:var(--accent)}.more-item .badge{margin-left:auto}.logo-hint{font-size:13px;color:var(--ink-2);padding:6px 8px 10px}.logo-grid{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));gap:6px}@media(max-width:640px){.logo-grid{grid-template-columns:repeat(3,minmax(0,1fr))}}.logo-opt{display:flex;flex-direction:column;align-items:center;gap:4px;padding:12px 6px 10px;border-radius:14px;border:1.5px solid transparent;text-align:center}.logo-opt:hover{background:color-mix(in srgb,var(--ink) 5%,transparent)}.logo-opt[aria-checked=true]{border-color:var(--accent);background:var(--accent-soft)}.logo-name{font-weight:700;font-size:13.5px}.logo-note{font-size:11.5px;color:var(--ink-2);line-height:1.3}.appearance{padding:4px 6px}.pref-row{display:flex;align-items:center;justify-content:space-between;gap:12px;padding:8px 6px;font-weight:600;font-size:14px}@media(max-width:1100px){:root{--gutter: 28px}}@media(max-width:820px){:root{--gutter: 16px}.sidebar{display:none}.main-col{margin-left:0}.topbar{padding:0 12px 0 10px;padding-top:env(safe-area-inset-top);height:calc(var(--top-h) + env(safe-area-inset-top))}.topbar-logo{display:block}.topbar-logo .brand{padding:4px}.profile{padding:3px;gap:0}.profile-name,.profile-chev{display:none}main{padding:0 var(--gutter) calc(104px + env(safe-area-inset-bottom))}.tabbar{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));position:fixed;left:0;right:0;bottom:0;z-index:30;padding:6px 6px calc(6px + env(safe-area-inset-bottom));background:color-mix(in srgb,var(--surface) 88%,transparent);-webkit-backdrop-filter:saturate(1.6) blur(18px);backdrop-filter:saturate(1.6) blur(18px);border-top:1px solid var(--line)}.tab{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:2px;min-height:50px;padding:4px 0;border-radius:12px;font-size:11px;font-weight:650;color:var(--ink-2);text-decoration:none}.tab .ic{width:24px;height:24px}.tab[aria-current=page]{color:var(--accent)}.tab span{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}}.page{padding-top:10px;min-width:0}.page-head{display:flex;align-items:flex-end;gap:16px;flex-wrap:wrap;margin-bottom:18px}.page-titles{flex:1 1 320px;min-width:0}.large-title{font-size:34px;line-height:1.12;font-weight:800;letter-spacing:-.025em;overflow-wrap:anywhere}.page-sub{margin-top:6px;color:var(--ink-2);font-size:15px;font-weight:500;max-width:75ch;display:flex;align-items:center;gap:6px;flex-wrap:wrap}.page-tools{display:flex;gap:8px;flex-wrap:wrap}.section{margin-top:34px;min-width:0}.section-head{display:flex;align-items:flex-end;gap:12px;flex-wrap:wrap;margin:0 2px 12px;padding-bottom:10px;border-bottom:1px solid var(--line)}.section-titles{flex:1 1 260px;min-width:0}.section-title{font-size:20px;line-height:1.25;font-weight:750;letter-spacing:-.015em;display:flex;align-items:center;gap:8px}.section-desc{margin-top:3px;color:var(--ink-2);font-size:14px;max-width:72ch}.section-action{display:flex;gap:8px;align-items:center}.section-body{min-width:0;display:flex;flex-direction:column;gap:12px}.sub-h{font-size:16px;font-weight:700;margin:10px 2px 2px;display:flex;gap:8px;align-items:baseline;flex-wrap:wrap}.columns{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 28px;align-items:start}.columns-lead{grid-template-columns:minmax(0,1.25fr) minmax(0,1fr);gap:16px 24px}.columns>.section{margin-top:34px}@media(max-width:1100px){.columns,.columns-lead{grid-template-columns:minmax(0,1fr)}}.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);box-shadow:var(--shadow-1);min-width:0}.card-pad{padding:18px}.card.tone-accent{background:var(--accent-soft);border-color:transparent}.glist{list-style:none;margin:0;padding:4px 0}.glist-item{position:relative}.glist-item+.glist-item:before{content:"";position:absolute;top:0;left:16px;right:16px;height:1px;background:var(--line)}.row{display:flex;align-items:center;gap:12px;padding:12px 16px;min-height:54px;width:100%;text-align:left;color:inherit;text-decoration:none;flex-wrap:wrap}.row-action{border-radius:14px}.row-action:hover{background:color-mix(in srgb,var(--ink) 4%,transparent)}.row.is-selected{background:var(--accent-soft)}.row-icon{width:34px;height:34px;border-radius:10px;display:grid;place-items:center;background:var(--surface-2);color:var(--ink-2);flex:none}.row-lead{flex:none;display:flex}.row-text{flex:1 1 180px;min-width:0;display:flex;flex-direction:column;gap:1px}.row-title{font-weight:650;font-size:15px;overflow-wrap:anywhere}.row-title.mono{font-family:var(--mono);font-size:13.5px;font-weight:600}.row-sub{color:var(--ink-2);font-size:13px;overflow-wrap:anywhere}.row-trail{flex:none;display:flex;align-items:center;gap:8px;margin-left:auto}.row-chev{width:18px;height:18px;color:var(--ink-3)}.row-detail{padding:0 16px 14px}.more-row{display:flex;justify-content:center}.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));overflow:hidden}.stat{padding:15px 18px;box-shadow:-1px 0 0 var(--line),0 -1px 0 var(--line);min-width:0}.stat-label{font-size:13px;color:var(--ink-2);font-weight:600;display:flex;align-items:center;gap:6px}.stat-value{font-size:26px;font-weight:800;letter-spacing:-.02em;margin:2px 0;line-height:1.2;overflow-wrap:anywhere}.stat-note{font-size:13px;color:var(--ink-2)}.kv{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:8px 16px;margin:0;font-size:14px}.kv dt{color:var(--ink-2);font-weight:650}.kv dd{margin:0;min-width:0;overflow-wrap:anywhere}@media(max-width:480px){.kv{grid-template-columns:minmax(0,1fr);gap:2px}.kv dd{margin-bottom:8px}}.note{display:flex;gap:10px;align-items:flex-start;padding:11px 14px;border-radius:14px;font-size:14px;background:var(--surface-2);color:var(--ink);margin:0 0 12px}.note .ic{margin-top:1px;width:19px;height:19px}.note-bad{background:var(--bad-soft)}.note-bad .ic{color:var(--bad)}.note-watch{background:var(--watch-soft)}.note-watch .ic{color:var(--watch)}.note-good{background:var(--good-soft)}.note-good .ic{color:var(--good)}.note-accent{background:var(--accent-soft)}.note-accent .ic{color:var(--accent)}.plan-list{margin:0;padding-left:20px;color:var(--ink-2);display:flex;flex-direction:column;gap:4px}.btn{display:inline-flex;align-items:center;justify-content:center;gap:7px;border-radius:99px;padding:8px 16px;min-height:40px;font-weight:700;font-size:14px;line-height:1.2;text-decoration:none;white-space:nowrap;position:relative;flex:none}.btn .ic{width:18px;height:18px}.btn-filled{background:var(--accent);color:var(--accent-ink)}.btn-filled:hover{background:color-mix(in srgb,var(--accent) 88%,var(--ink))}.btn-tinted{background:color-mix(in srgb,var(--accent) 13%,var(--surface));color:color-mix(in srgb,var(--accent) 82%,var(--ink))}.btn-tinted:hover{background:color-mix(in srgb,var(--accent) 20%,var(--surface))}.btn-plain{background:var(--neutral-soft);color:var(--ink)}.btn-plain:hover{background:color-mix(in srgb,var(--ink) 12%,var(--surface))}.btn-danger{background:var(--bad-soft);color:var(--bad)}.btn-sm{padding:5px 12px;min-height:32px;font-size:13px}.btn-sm .ic{width:16px;height:16px}.btn-lg{padding:12px 22px;min-height:48px;font-size:15.5px}.btn-wide{width:100%}.btn-icon{padding:8px;aspect-ratio:1}.btn:disabled{opacity:.5}.btn.is-loading{opacity:.85}.spin{width:16px;height:16px;border-radius:50%;border:2.2px solid color-mix(in srgb,currentColor 25%,transparent);border-top-color:currentColor;animation:spin .8s linear infinite;flex:none}@keyframes spin{to{transform:rotate(360deg)}}@media(pointer:coarse){.btn{min-height:44px}.btn-sm{min-height:36px}}.seg{position:relative;display:inline-flex;background:color-mix(in srgb,var(--ink) 7%,transparent);border-radius:12px;padding:3px;max-width:100%}.seg-scroll{overflow-x:auto;scrollbar-width:none;overscroll-behavior-x:contain}.seg-scroll::-webkit-scrollbar{display:none}.seg-thumb{position:absolute;top:3px;bottom:3px;left:0;border-radius:9px;background:var(--surface);box-shadow:0 1px 3px #00000024,0 0 0 .5px #0000000a;pointer-events:none}.seg-thumb.is-ready{transition:transform var(--dur-3) var(--spring),width var(--dur-3) var(--spring)}:root[data-theme=dark] .seg-thumb{background:#35424f}@media(prefers-color-scheme:dark){:root:not([data-theme=light]) .seg-thumb{background:#35424f}}.seg-opt{position:relative;z-index:1;padding:6px 14px;min-height:34px;font-weight:700;font-size:13.5px;color:var(--ink-2);border-radius:9px;white-space:nowrap;flex:none;display:inline-flex;align-items:center;gap:6px;transition:color var(--dur-1)}.seg-opt[aria-checked=true]{color:var(--ink)}.seg-opt:focus-visible{outline-offset:-2px}.seg-sm .seg-opt{padding:4px 10px;min-height:30px;font-size:13px}.seg-count{font-size:12px;font-weight:700;padding:0 6px;border-radius:99px;background:color-mix(in srgb,var(--ink) 9%,transparent)}.switch{width:51px;height:31px;border-radius:99px;background:color-mix(in srgb,var(--ink) 18%,transparent);position:relative;flex:none;transition:background-color var(--dur-2) var(--ease-out)}.switch-knob{position:absolute;top:2px;left:2px;width:27px;height:27px;border-radius:50%;background:#fff;box-shadow:0 2px 5px #00000040;transition:transform var(--dur-3) var(--spring)}.switch[aria-checked=true]{background:var(--good-solid)}.switch[aria-checked=true] .switch-knob{transform:translate(20px)}.stepper{display:inline-flex;align-items:center;background:var(--field);border-radius:12px;padding:2px}.stepper button{width:34px;height:34px;border-radius:10px;display:grid;place-items:center;color:var(--ink-2);font-size:19px;font-weight:700}.stepper button:hover:not(:disabled){background:color-mix(in srgb,var(--ink) 8%,transparent);color:var(--ink)}.stepper button:disabled{opacity:.35}.stepper input{width:60px;border:0;background:none;text-align:center;font-weight:750;font-variant-numeric:tabular-nums;padding:0;-moz-appearance:textfield;appearance:textfield}.stepper input::-webkit-inner-spin-button,.stepper input::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}.stepper:focus-within{box-shadow:0 0 0 2px var(--focus)}.stepper-unit{font-size:13px;color:var(--ink-2);font-weight:650;padding-right:4px}.form{display:flex;flex-direction:column;gap:14px}.field{display:flex;flex-direction:column;gap:6px;min-width:0}.field-label{font-weight:650;font-size:14px}.field-hint{font-size:13px;color:var(--ink-2)}.field-error{font-size:13.5px;color:var(--bad);font-weight:600;display:flex;gap:6px;align-items:center}.field-error .ic{width:16px;height:16px}.input{width:100%;min-width:0;font-size:15px;color:var(--ink);background:var(--field);border:1.5px solid transparent;border-radius:12px;padding:10px 12px;min-height:42px;transition:border-color var(--dur-1),background-color var(--dur-1)}.input::placeholder{color:var(--ink-3)}.input:focus{outline:none;border-color:var(--focus);background:var(--surface)}.input.mono{font-family:var(--mono);font-size:14px}.has-error .input{border-color:var(--bad)}.input-unit{display:inline-flex;align-items:center;gap:8px}.input-unit .input{width:110px}.unit{color:var(--ink-2);font-size:13.5px;font-weight:650}.select{position:relative;display:inline-flex;min-width:0;max-width:100%}.select .input{appearance:none;-webkit-appearance:none;padding-right:36px;cursor:pointer}.select .ic{position:absolute;right:10px;top:50%;transform:translateY(-50%);width:18px;height:18px;pointer-events:none;color:var(--ink-2)}.chips{display:flex;flex-wrap:wrap;gap:6px;align-items:center;padding:6px;border-radius:12px;background:var(--field);border:1.5px solid transparent}.chips:focus-within{border-color:var(--focus);background:var(--surface)}.chip{display:inline-flex;align-items:center;gap:4px;padding:3px 4px 3px 11px;border-radius:99px;background:var(--surface);border:1px solid var(--line);font-size:13.5px;font-weight:650}.chip-x{width:22px;height:22px;border-radius:50%;display:grid;place-items:center;color:var(--ink-2)}.chip-x:hover{background:var(--neutral-soft);color:var(--ink)}.chip-x .ic{width:12px;height:12px}.chip-input{flex:1 1 100px;min-width:80px;border:0;background:none;padding:5px 6px;font-size:14px}.inline-form{display:flex;gap:8px;flex-wrap:wrap;align-items:center}.inline-form .input{flex:1 1 220px;width:auto}.pill{display:inline-flex;align-items:center;gap:5px;border-radius:99px;padding:3px 10px 3px 8px;font-size:12.5px;font-weight:700;white-space:nowrap;line-height:1.35}.pill .ic{width:13px;height:13px;stroke-width:2.4}.pill-good{background:var(--good-soft);color:var(--good)}.pill-bad{background:var(--bad-soft);color:var(--bad)}.pill-watch{background:var(--watch-soft);color:var(--watch)}.pill-neutral{background:var(--neutral-soft);color:var(--neutral-ink)}.pill-accent{background:var(--accent-soft);color:var(--accent)}.pill.is-solid.pill-good{background:var(--good-solid);color:var(--good-solid-ink)}.pill.is-solid.pill-bad{background:var(--bad-solid);color:var(--bad-solid-ink)}.change{display:inline-flex;align-items:center;justify-content:center;gap:3px;border-radius:99px;padding:2px 9px 2px 6px;font-size:13px;font-weight:750;white-space:nowrap;min-width:66px}.change .ic{width:13px;height:13px;stroke-width:2.8}.change-good{background:var(--good-solid);color:var(--good-solid-ink)}.change-bad{background:var(--bad-solid);color:var(--bad-solid-ink)}.change-neutral{background:var(--neutral-soft);color:var(--neutral-ink)}.info-btn{width:24px;height:24px;border-radius:50%;display:inline-grid;place-items:center;color:var(--ink-2);flex:none;vertical-align:middle}.info-btn>.ic{width:18px;height:18px;stroke-width:1.9}.info-btn:hover{color:var(--accent)}@media(pointer:coarse){.info-btn{width:30px;height:30px}}.info-body{font-size:16px;line-height:1.55}.facts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px;margin:16px 0 0}.fact{background:var(--surface-2);border-radius:14px;padding:10px 13px}.fact dt{font-size:12.5px;font-weight:650;color:var(--ink-2)}.fact dd{margin:0;font-weight:800;font-size:17px;overflow-wrap:anywhere}.code{border-radius:14px;background:var(--code-bg);color:var(--code-ink);overflow:hidden}.code-head{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:6px 8px 6px 14px;border-bottom:1px solid var(--code-line);font-size:12.5px;font-weight:650;color:var(--code-dim)}.code pre{margin:0;padding:12px 14px;font:500 13px/1.6 var(--mono);overflow:auto;max-height:360px}.code pre code{background:none;padding:0;color:inherit;font-size:inherit}.code .btn-tinted,.cmd .btn-tinted{background:#ffffff1a;color:var(--code-ink)}.cmd{display:flex;align-items:flex-start;gap:8px;background:var(--code-bg);color:var(--code-ink);border-radius:12px;padding:5px 5px 5px 14px;min-width:0}.cmd code{flex:1;min-width:0;background:none;color:inherit;padding:6px 0;font-size:13px;line-height:1.5;white-space:pre-wrap;overflow-wrap:anywhere}.cmd .copy,.cmd .btn{flex:none}.cmd-note{font-size:12.5px;color:var(--ink-2);margin:4px 2px 0}.code>.cmd-note{color:var(--code-dim);margin:0;padding:8px 14px 10px;border-top:1px solid var(--code-line)}.code>.cmd-note a,.code>.cmd-note code{color:var(--code-ink)}.commands{display:flex;flex-direction:column;gap:10px;margin-top:8px}.empty{display:flex;flex-direction:column;align-items:center;text-align:center;gap:8px;padding:28px 20px}.empty-icon{width:46px;height:46px;border-radius:50%;display:grid;place-items:center;background:var(--surface-2);color:var(--ink-2)}.empty-title{font-weight:750;font-size:16px}.empty-body{color:var(--ink-2);font-size:14px;max-width:52ch}.empty-action{display:flex;gap:8px;flex-wrap:wrap;justify-content:center;margin-top:6px}.skeleton{display:flex;flex-direction:column;gap:10px;margin-top:16px}.sk{border-radius:10px;background:linear-gradient(90deg,var(--neutral-soft) 25%,color-mix(in srgb,var(--neutral-soft) 50%,var(--surface)) 50%,var(--neutral-soft) 75%);background-size:200% 100%;animation:shimmer 1.4s linear infinite}.sk-title{height:38px;width:240px;margin-top:10px}.sk-sub{height:16px;width:360px;max-width:80%;margin-top:10px}.sk-line{height:14px}.sk-chart{height:260px;border-radius:18px}@keyframes shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}.toast{position:fixed;left:50%;bottom:28px;z-index:90;display:flex;align-items:center;gap:8px;max-width:calc(100vw - 32px);padding:10px 16px;border-radius:99px;background:var(--ink);color:var(--bg);font-weight:650;font-size:14px;box-shadow:var(--shadow-pop);pointer-events:none;transform:translate(-50%,24px);opacity:0;transition:transform var(--dur-3) var(--spring-pop),opacity var(--dur-1)}.toast.is-open{transform:translate(-50%);opacity:1}.toast .ic{width:18px;height:18px;stroke-width:2.4}@media(max-width:820px){.toast{bottom:calc(86px + env(safe-area-inset-bottom))}}.layer{position:relative;z-index:50}.overlay{position:fixed;inset:0;z-index:50}.scrim{position:absolute;inset:0;background:var(--scrim);opacity:0;transition:opacity var(--dur-2) var(--ease-out)}.overlay.is-open .scrim{opacity:1}.sheet{position:absolute;left:50%;top:50%;width:min(540px,calc(100vw - 32px));max-height:calc(100dvh - 48px);display:flex;flex-direction:column;background:var(--surface);border-radius:22px;box-shadow:var(--shadow-pop);outline:none;transform:translate(-50%,-46%) scale(.97);opacity:0;transition:transform var(--dur-3) var(--spring),opacity var(--dur-1) var(--ease-out)}.sheet-sm{width:min(460px,calc(100vw - 32px))}.sheet-lg{width:min(820px,calc(100vw - 32px))}.overlay.is-open .sheet{transform:translate(-50%,-50%);opacity:1}.sheet-head{display:flex;align-items:flex-start;gap:12px;padding:20px 18px 8px 22px;touch-action:none}.grabber{display:none}.sheet-titles{flex:1;min-width:0}.sheet-title{font-size:21px;font-weight:800;letter-spacing:-.02em;overflow-wrap:anywhere}.sheet-desc{color:var(--ink-2);font-size:14px;margin-top:3px;overflow-wrap:anywhere}.close-x{width:34px;height:34px;border-radius:50%;display:grid;place-items:center;background:var(--neutral-soft);color:var(--ink-2);flex:none}.close-x:hover{color:var(--ink)}.close-x .ic{width:18px;height:18px}.sheet-content{padding:8px 22px 20px;overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch;min-height:0}.sheet-foot{display:flex;justify-content:flex-end;gap:8px;flex-wrap:wrap;padding:12px 18px 18px;border-top:1px solid var(--line)}.sheet-h3{font-size:16px;font-weight:750;margin:18px 0 8px}.sheet .glist{box-shadow:none}@media(max-width:640px){.sheet,.sheet-sm,.sheet-lg{inset:auto 0 0;width:100%;max-height:90dvh;border-radius:22px 22px 0 0;transform:translateY(100%);opacity:1}.overlay.is-open .sheet{transform:translateY(0)}.sheet.is-dragging{transition:none}.sheet-head{position:relative;padding:20px 14px 6px 18px}.grabber{display:block;position:absolute;top:7px;left:50%;width:40px;height:5px;margin-left:-20px;border-radius:99px;background:var(--line-strong)}.sheet-content{padding:6px 18px 18px}.sheet-foot{padding:10px 14px calc(12px + env(safe-area-inset-bottom))}.sheet-foot .btn{flex:1 1 auto}}.popover{position:fixed;z-index:55;background:var(--surface);border-radius:18px;box-shadow:var(--shadow-pop);padding:8px;overflow-y:auto;overscroll-behavior:contain;transform-origin:top right;transform:translateY(-6px) scale(.97);opacity:0;transition:transform var(--dur-2) var(--spring),opacity var(--dur-1) var(--ease-out)}.popover:focus{outline:none}.popover.from-start{transform-origin:top left}.popover.is-up{transform-origin:bottom right;transform:translateY(6px) scale(.97)}.popover.is-open{transform:none;opacity:1}.menu{display:flex;flex-direction:column;gap:2px}.menu-label{font-size:12.5px;font-weight:700;color:var(--ink-2);padding:8px 10px 4px}.menu-sep{height:1px;background:var(--line);margin:6px 8px}.menu-item{display:flex;align-items:center;gap:12px;width:100%;text-align:left;padding:8px 10px;border-radius:12px;min-height:44px}.menu-item:hover,.menu-item:focus-visible{background:color-mix(in srgb,var(--ink) 6%,transparent)}.menu-item[aria-checked=true]{background:var(--accent-soft)}.menu-item.is-danger{color:var(--bad)}.menu-icon{width:34px;height:34px;border-radius:10px;display:grid;place-items:center;background:var(--accent-soft);color:var(--accent);flex:none}.menu-text{flex:1;min-width:0}.menu-text b{display:block;font-size:14.5px;font-weight:650;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.menu-text small{display:block;font-size:12.5px;color:var(--ink-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.menu-check{color:var(--accent);width:18px;height:18px;stroke-width:2.6}.table-wrap{overflow:hidden}.table-tools{display:flex;gap:8px;flex-wrap:wrap;align-items:center;padding:10px 12px;border-bottom:1px solid var(--line)}.search-field{position:relative;flex:1 1 200px;min-width:0}.search-field .ic{position:absolute;left:11px;top:50%;transform:translateY(-50%);width:17px;height:17px;color:var(--ink-3);pointer-events:none}.search-field .input{padding-left:36px;min-height:38px}.table-tools .select .input{min-height:38px;font-size:14px}.table-count{color:var(--ink-2);font-size:13px;font-weight:600}.table-scroll{overflow-x:auto;overscroll-behavior-x:contain}.table{width:100%;border-collapse:collapse;font-size:14px}.table th{text-align:left;font-size:12.5px;color:var(--ink-2);font-weight:700;padding:0;background:var(--surface-2);white-space:nowrap}.table th.is-num{text-align:right}.th-btn{display:inline-flex;align-items:center;gap:3px;padding:9px 14px;width:100%;font-weight:700;color:inherit;justify-content:inherit}.table th.is-num .th-btn{justify-content:flex-end}.th-btn .ic{width:14px;height:14px;opacity:.35}.th-btn .ic.is-on{opacity:1;color:var(--accent)}.table td{padding:11px 14px;border-top:1px solid var(--line);vertical-align:middle;overflow-wrap:break-word}.table td.is-num{text-align:right;white-space:nowrap}.table td.is-nowrap{white-space:nowrap}.table tbody tr:hover{background:color-mix(in srgb,var(--ink) 2.5%,transparent)}.table-more{display:flex;justify-content:center;padding:10px;border-top:1px solid var(--line)}@media(max-width:640px){.table thead{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}.table,.table tbody,.table tr,.table td{display:block;width:100%}.table tr{padding:10px 14px;border-top:1px solid var(--line);display:flex;flex-direction:column}.table td>*{justify-self:start}.table tbody tr:first-child{border-top:0}.table td{border:0;padding:3px 0;display:grid;grid-template-columns:minmax(96px,38%) minmax(0,1fr);gap:10px;align-items:center}.table td:before{content:attr(data-label);color:var(--ink-2);font-size:12.5px;font-weight:650}.table td.is-num{text-align:left}.table td,.table td.is-nowrap{white-space:normal;overflow-wrap:anywhere}.table td.is-primary{display:block;order:-1;font-weight:650;font-size:15px;padding-bottom:6px}.table td.is-primary:before{display:none}}.hstrip{display:flex;align-items:center;gap:10px;flex-wrap:wrap;padding:9px 10px 9px 14px;border-radius:16px;background:var(--surface);border:1px solid var(--line);font-size:14px;color:var(--ink-2)}.hstrip-text{flex:1 1 240px;min-width:0}.hstrip b{color:var(--ink)}.hstrip.tone-bad{background:var(--bad-soft);border-color:transparent}.hstrip.tone-watch{background:var(--watch-soft);border-color:transparent}.health-top{display:flex;align-items:center;gap:14px;padding:4px 0 14px}.health-top b{display:block;font-size:16px}.health-top small{color:var(--ink-2);font-size:13.5px}.light{width:44px;height:44px;border-radius:14px;display:grid;place-items:center;flex:none}.light .ic{width:24px;height:24px;stroke-width:2.2}.tone-good .light{background:var(--good-soft);color:var(--good)}.tone-watch .light{background:var(--watch-soft);color:var(--watch)}.tone-bad .light{background:var(--bad-soft);color:var(--bad)}.health-top+.kv,.glist+.kv{margin-top:14px}.send{display:flex;flex-direction:column;gap:12px;margin-top:16px;padding-top:14px;border-top:1px solid var(--line)}.send .kv{font-size:13px}.send-actions{display:flex;gap:8px;flex-wrap:wrap}.token-box p{font-size:14px;margin-bottom:10px}.hero{min-width:0}.hero-head{display:flex;align-items:flex-end;gap:16px;flex-wrap:wrap}.hero-figure{flex:1 1 280px;min-width:0}.hero-name{display:flex;align-items:center;gap:8px;font-size:15.5px;font-weight:700;color:var(--ink-2)}.hero-num{font-size:60px;line-height:1.04;font-weight:800;letter-spacing:-.04em;margin:4px 0 6px;white-space:nowrap}.hero-num small{font-size:.45em;font-weight:700;letter-spacing:-.01em;color:var(--ink-2);margin-left:3px}.hero-meta{display:flex;align-items:center;gap:10px;flex-wrap:wrap;min-height:28px}.hero-date{color:var(--ink-2);font-weight:600;font-size:14px}.hero-meaning{margin-top:10px;font-size:15px;max-width:62ch;min-height:1.5em}.hero-tools{padding-bottom:4px}.hero-note{margin-top:18px;color:var(--ink-2);font-size:14px;padding:24px;border-radius:16px;background:var(--surface-2);text-align:center}.scrub{position:relative;margin:12px -4px 0;touch-action:pan-y;border-radius:14px}.scrub:focus-visible{outline-offset:4px}.scrub>.sk{position:absolute;inset:0}.scrub-plot{position:relative}.scrub .u-legend{display:none}.scrub .u-cursor-x{border-right:1.5px solid var(--ink-3)!important;opacity:.8}.scrub .u-cursor-pt{border-radius:50%;box-shadow:0 1px 4px #00000040}.scrub-flag{position:absolute;top:0;transform:translate(-50%);padding:3px 9px;border-radius:8px;background:var(--ink);color:var(--bg);font-size:12.5px;font-weight:700;pointer-events:none;opacity:0;transition:opacity .12s;white-space:nowrap;z-index:3}.scrub-flag.is-on{opacity:1}.spark{overflow:visible;flex:none}.spark-base{stroke:var(--ink-3);stroke-width:1;stroke-dasharray:2 3;opacity:.7;fill:none}.spark-line{fill:none;stroke:var(--ink-2);stroke-width:2;stroke-linejoin:round;stroke-linecap:round;stroke-dasharray:1;stroke-dashoffset:0;animation:draw var(--dur-3) var(--ease-out) backwards;animation-duration:.7s}.spark-good .spark-line{stroke:var(--good)}.spark-bad .spark-line{stroke:var(--bad)}@keyframes draw{0%{stroke-dashoffset:1}}.watch{list-style:none;margin:0;padding:4px 0}.watch-row{position:relative;display:flex;align-items:center;padding-right:12px}.watch-row+.watch-row:before{content:"";position:absolute;top:0;left:16px;right:16px;height:1px;background:var(--line)}.watch-main{flex:1;min-width:0;display:grid;grid-template-columns:minmax(0,1fr) 84px 108px;align-items:center;gap:12px;padding:12px 8px 12px 16px;text-align:left;border-radius:14px;margin:2px 6px 2px 4px;min-height:60px}button.watch-main:hover{background:color-mix(in srgb,var(--ink) 4%,transparent)}.watch-row.is-selected .watch-main{background:var(--accent-soft)}.watch-name{min-width:0}.watch-name b{display:block;font-size:15px;font-weight:700;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.watch-name small{display:block;color:var(--ink-2);font-size:12.5px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.watch-val{display:flex;flex-direction:column;align-items:flex-end;gap:4px}.watch-val b{font-size:16px;font-weight:750;white-space:nowrap}.watch-trail{display:flex;justify-content:flex-start}.bars{display:flex;align-items:stretch;gap:10px;padding:8px 4px 0;border-radius:14px}.bar{flex:1;min-width:0;display:flex;flex-direction:column;align-items:center;gap:6px;cursor:default}.bar-value{font-size:13px;font-weight:700;color:var(--ink-2)}.bar-track{flex:1;width:100%;max-width:64px;display:flex;align-items:flex-end;border-radius:10px;background:color-mix(in srgb,var(--ink) 4%,transparent)}.bar-fill{display:block;width:100%;border-radius:10px;transform-origin:bottom;animation:barGrow var(--dur-3) var(--spring) backwards;animation-duration:.6s;transition:opacity var(--dur-1)}.bar-fill.tone-accent{background:var(--accent)}.bar-fill.tone-good{background:var(--good)}.bar-fill.tone-bad{background:var(--bad)}.bar-fill.tone-watch{background:var(--watch)}.bar-fill.tone-neutral{background:var(--ink-2)}.bars:has(.bar.is-on) .bar:not(.is-on) .bar-fill{opacity:.45}.bar-label{font-size:12.5px;font-weight:650;color:var(--ink-2);text-align:center;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:100%}@keyframes barGrow{0%{transform:scaleY(0)}}@media(max-width:820px){.hero-num{font-size:48px}.watch-main{grid-template-columns:minmax(0,1fr) 60px 96px;gap:10px;padding:11px 4px 11px 12px}.watch-main .spark{width:60px}.watch-row{padding-right:8px}.watch-name b,.watch-name small{white-space:normal;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2}}@media(max-width:380px){.hero-num{font-size:42px}.watch-main{grid-template-columns:minmax(0,1fr) 46px 88px;gap:8px}.watch-main .spark{width:46px}}@font-face{font-family:IBM Plex Sans;font-style:normal;font-weight:100 500;font-display:swap;src:url(fonts/plex-sans-400.woff2) format("woff2")}@font-face{font-family:IBM Plex Sans;font-style:normal;font-weight:600 900;font-display:swap;src:url(fonts/plex-sans-600.woff2) format("woff2")}@font-face{font-family:IBM Plex Sans Condensed;font-style:normal;font-weight:100 900;font-display:swap;src:url(fonts/plex-sans-condensed-700.woff2) format("woff2")}@font-face{font-family:IBM Plex Mono;font-style:normal;font-weight:100 900;font-display:swap;src:url(fonts/plex-mono-400.woff2) format("woff2")}:root[data-design=sheet]{--bg: #FFFFFF;--sidebar: #F5F5F4;--surface: #FFFFFF;--surface-2: #F5F5F4;--field: #F5F5F4;--ink: #111111;--ink-2: #474747;--ink-3: #5A5A5A;--line: #DCDCDA;--line-strong: #A8A8A6;--accent: #2160C4;--accent-ink: #FFFFFF;--accent-soft: #EAF0FA;--good: #0A7A47;--good-soft: #E6F2EC;--good-solid: #7CF0B6;--good-solid-ink: #000000;--bad: #C4262D;--bad-soft: #FCEAEA;--bad-solid: #E5484D;--bad-solid-ink: #000000;--watch: #985C00;--watch-soft: #FDF3E0;--neutral-soft: #EDEDEB;--neutral-ink: #3A3A3A;--scrim: rgba(0, 0, 0, .4);--shadow-1: none;--shadow-pop: 0 0 0 1px #111111;--focus: #2160C4;--code-bg: #141414;--code-ink: #F2F2F2;--code-dim: #B5B5B5;--code-line: #333333;--font: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;--display: "IBM Plex Sans Condensed", "IBM Plex Sans", system-ui, sans-serif;--mono: "IBM Plex Mono", ui-monospace, "SF Mono", "Cascadia Mono", Consolas, monospace;--r-lg: 2px;--r-md: 2px;--r-sm: 2px;--frame: #111111;--strip: #F0F0EE;--sv-blue: #2160C4;--sv-blue-ink: #2160C4;--sv-mint: #7CF0B6;--sv-amber: #F2A20C;--sv-coral: #E5484D;--sv-yellow: #FFC53D;--sv-yellow-ink: #8A5A00;--sv-ease: cubic-bezier(.6, 0, .2, 1);--mod: var(--ink);--mark-good: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-7 -7 14 14'%3E%3Crect x='-5.5' y='-5.5' width='11' height='11' fill='%237CF0B6' stroke='%23000'/%3E%3Cpath d='M-3 0l2 2.2 4-4.4' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");--mark-watch: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-7 -7 14 14'%3E%3Ccircle r='6.5' fill='%23F2A20C' stroke='%23000'/%3E%3Cpath d='M0 -3.8V0H2.8' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");--mark-bad: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-7 -7 14 14'%3E%3Cpath d='M0 -6.5 6.5 5H-6.5z' fill='%23E5484D' stroke='%23000' stroke-linejoin='round'/%3E%3C/svg%3E");--chart-1: #2160C4;--chart-2: #111111;--chart-3: #8A5A00;--chart-font: 400 13px "IBM Plex Sans", system-ui, sans-serif;--chart-area: flat;color-scheme:light}@media(prefers-color-scheme:dark){:root[data-design=sheet]:not([data-theme=light]){--bg: #0B0B0B;--sidebar: #141414;--surface: #141414;--surface-2: #1C1C1C;--field: #1C1C1C;--ink: #F2F2F2;--ink-2: #C2C2C2;--ink-3: #A8A8A8;--line: #2C2C2C;--line-strong: #555555;--accent: #7AA5F0;--accent-ink: #000000;--accent-soft: #14213A;--good: #7CF0B6;--good-soft: #1B2B23;--bad: #FF7B7F;--bad-soft: #2E1A1B;--watch: #F2A20C;--watch-soft: #2C2312;--neutral-soft: #262626;--neutral-ink: #D6D6D6;--focus: #7AA5F0;--scrim: rgba(0, 0, 0, .7);--shadow-pop: 0 0 0 1px #F2F2F2;--code-bg: #000000;--code-ink: #F2F2F2;--code-dim: #B5B5B5;--code-line: #2C2C2C;--frame: #8A8A8A;--strip: #1E1E1E;--sv-blue: #2F6FD8;--sv-blue-ink: #7AA5F0;--sv-yellow-ink: #FFC53D;--chart-1: #4A82E0;--chart-2: #F2F2F2;--chart-3: #C08A00;--dot-edge: #FFFFFF;--mark-good: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-7 -7 14 14'%3E%3Crect x='-5.5' y='-5.5' width='11' height='11' fill='%237CF0B6' stroke='%23FFF'/%3E%3Cpath d='M-3 0l2 2.2 4-4.4' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");--mark-watch: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-7 -7 14 14'%3E%3Ccircle r='6.5' fill='%23F2A20C' stroke='%23FFF'/%3E%3Cpath d='M0 -3.8V0H2.8' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");--mark-bad: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-7 -7 14 14'%3E%3Cpath d='M0 -6.5 6.5 5H-6.5z' fill='%23E5484D' stroke='%23FFF' stroke-linejoin='round'/%3E%3C/svg%3E");color-scheme:dark}}:root[data-design=sheet][data-theme=dark]{--bg: #0B0B0B;--sidebar: #141414;--surface: #141414;--surface-2: #1C1C1C;--field: #1C1C1C;--ink: #F2F2F2;--ink-2: #C2C2C2;--ink-3: #A8A8A8;--line: #2C2C2C;--line-strong: #555555;--accent: #7AA5F0;--accent-ink: #000000;--accent-soft: #14213A;--good: #7CF0B6;--good-soft: #1B2B23;--bad: #FF7B7F;--bad-soft: #2E1A1B;--watch: #F2A20C;--watch-soft: #2C2312;--neutral-soft: #262626;--neutral-ink: #D6D6D6;--focus: #7AA5F0;--scrim: rgba(0, 0, 0, .7);--shadow-pop: 0 0 0 1px #F2F2F2;--code-bg: #000000;--code-ink: #F2F2F2;--code-dim: #B5B5B5;--code-line: #2C2C2C;--frame: #8A8A8A;--strip: #1E1E1E;--sv-blue: #2F6FD8;--sv-blue-ink: #7AA5F0;--sv-yellow-ink: #FFC53D;--chart-1: #4A82E0;--chart-2: #F2F2F2;--chart-3: #C08A00;--dot-edge: #FFFFFF;--mark-good: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-7 -7 14 14'%3E%3Crect x='-5.5' y='-5.5' width='11' height='11' fill='%237CF0B6' stroke='%23FFF'/%3E%3Cpath d='M-3 0l2 2.2 4-4.4' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");--mark-watch: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-7 -7 14 14'%3E%3Ccircle r='6.5' fill='%23F2A20C' stroke='%23FFF'/%3E%3Cpath d='M0 -3.8V0H2.8' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");--mark-bad: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-7 -7 14 14'%3E%3Cpath d='M0 -6.5 6.5 5H-6.5z' fill='%23E5484D' stroke='%23FFF' stroke-linejoin='round'/%3E%3C/svg%3E");color-scheme:dark}@media(prefers-contrast:more){:root[data-design=sheet]{--ink-2: var(--ink);--ink-3: var(--ink);--line: var(--line-strong)}}:root[data-design=sheet]:is([data-page=ci],[data-page=investment],[data-page=portal],[data-page=standing],[data-page=explore]){--mod: var(--sv-blue)}:root[data-design=sheet]:is([data-page=prs],[data-page=community]){--mod: var(--sv-mint)}:root[data-design=sheet][data-page=delivery]{--mod: var(--sv-yellow)}:root[data-design=sheet][data-page=deps]{--mod: var(--sv-coral)}[data-design=sheet] *,[data-design=sheet] *:before,[data-design=sheet] *:after{border-radius:2px!important;box-shadow:none!important;text-shadow:none!important;-webkit-backdrop-filter:none!important;backdrop-filter:none!important}[data-design=sheet] :is(.spin,.info-btn,.u-cursor-pt,.sv-round){border-radius:50%!important}[data-design=sheet] body{font-size:16px;line-height:1.55;font-synthesis:none}[data-design=sheet] :focus-visible{outline:2px solid var(--focus);outline-offset:2px}[data-design=sheet] .num{font-variant-numeric:tabular-nums}[data-design=sheet] code{background:var(--surface-2);border:1px solid var(--line);padding:0 4px;font-size:.88em}[data-design=sheet] :is(pre code,.cmd code){border:0;padding:0;background:none}[data-design=sheet] .press:active:not(:disabled){transform:translateY(1px)}[data-design=sheet] .muted{color:var(--ink-2)}[data-design=sheet] .small{font-size:14px}[data-design=sheet] .skip{background:var(--ink);color:var(--bg);font-weight:600}@keyframes svFade{0%{opacity:0}}@keyframes svReveal{0%{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0 0 0 0)}}@keyframes svPulse{to{opacity:.45}}[data-design=sheet] .sv-frame{border:1px solid var(--line-strong)}[data-design=sheet] .sv-panel{background:var(--surface);border:1.5px solid var(--frame)}[data-design=sheet] .sv-panel-good{border-top:5px solid var(--sv-mint)}[data-design=sheet] .sv-panel-watch{border-top:5px solid var(--sv-amber)}[data-design=sheet] .sv-panel-bad{border-top:5px solid var(--sv-coral)}[data-design=sheet] .sv-strip{display:flex;align-items:center;justify-content:space-between;gap:10px;min-height:42px;padding:9px 14px;background:var(--strip);border-bottom:1px solid var(--line-strong);font-size:15.5px;font-weight:700;color:var(--ink)}[data-design=sheet] .sv-label{font:700 13.5px/1.3 var(--font);letter-spacing:.04em;text-transform:uppercase;color:var(--ink-2)}[data-design=sheet] .sv-num{font-family:var(--display);font-weight:700;line-height:1;letter-spacing:-.01em;font-variant-numeric:tabular-nums}[data-design=sheet] .sv-dh{display:inline-block;font-weight:700;border-bottom:3px solid var(--mod);padding-bottom:3px}[data-design=sheet] .sv-dim{font:400 14px/1.45 var(--font);color:var(--ink-2)}[data-design=sheet] .sv-mark{display:inline-block;width:13px;height:13px;flex:none;vertical-align:-2px;background:center / contain no-repeat}[data-design=sheet] .sv-mark-good{background-image:var(--mark-good)}[data-design=sheet] .sv-mark-watch{background-image:var(--mark-watch)}[data-design=sheet] .sv-mark-bad{background-image:var(--mark-bad)}[data-design=sheet] .sv-mark-neutral{width:10px;height:10px;border:1.5px solid currentColor}[data-design=sheet] .sv-mark-accent{width:10px;height:10px;background:var(--sv-blue)}[data-design=sheet] .sv-dimline{position:relative;display:flex;align-items:center;gap:12px;height:20px;margin:0 0 12px;border-inline:1px solid var(--ink);font:600 14px/1 var(--font);color:var(--ink-2);white-space:nowrap}[data-design=sheet] .sv-dimline:before,[data-design=sheet] .sv-dimline:after{content:"";flex:1;min-width:16px;height:11px;background:linear-gradient(var(--ink),var(--ink)) center / 100% 1px no-repeat}[data-design=sheet] .sv-dimline>span{overflow:hidden;text-overflow:ellipsis;line-height:20px}[data-design=sheet] .sv-dimline>span:before,[data-design=sheet] .sv-dimline>span:after{content:"";position:absolute;top:calc(50% - 6px);width:1px;height:12px;background:var(--ink);transform:rotate(45deg)}[data-design=sheet] .sv-dimline>span:before{left:0}[data-design=sheet] .sv-dimline>span:after{right:0}[data-design=sheet] .page-enter{animation:svFade var(--dur-3) var(--sv-ease) backwards}[data-design=sheet] .page-head{position:relative;margin-bottom:20px;padding-bottom:14px;border-bottom:2px solid var(--ink)}[data-design=sheet] .page-head:after{content:"";position:absolute;left:0;bottom:-2px;width:64px;height:5px;background:var(--mod);pointer-events:none}[data-design=sheet] .large-title{font-family:var(--display);font-weight:700;font-size:clamp(36px,3.8vw,46px);line-height:1.04;letter-spacing:-.01em}[data-design=sheet] .page-sub{margin-top:8px;font-size:15.5px;font-weight:400;color:var(--ink-2)}[data-design=sheet] .section{margin-top:40px}[data-design=sheet] .section-head{border-bottom:1px solid var(--line-strong);padding-bottom:10px;margin:0 0 14px}[data-design=sheet] .section-title{font-weight:700;font-size:21px;letter-spacing:-.01em}[data-design=sheet] .section-desc{margin-top:4px;font-size:15px;color:var(--ink-2)}[data-design=sheet] .sub-h{font-weight:700;font-size:17px}[data-design=sheet] .columns>.section{margin-top:40px}[data-design=sheet] .card{background:var(--surface);border:1px solid var(--line-strong)}[data-design=sheet] .card.tone-accent{background:var(--accent-soft);border-color:var(--sv-blue)}[data-design=sheet] .card:is(.tone-good,.tone-watch,.tone-bad){position:relative;background:var(--surface);border-color:var(--line-strong);border-top:5px solid var(--tone-c)}[data-design=sheet] .card:is(.tone-good,.tone-watch,.tone-bad):after{content:"";position:absolute;top:10px;right:10px;width:13px;height:13px;background:var(--tone-m) center / contain no-repeat;pointer-events:none}[data-design=sheet] .card.tone-good{--tone-c: var(--sv-mint);--tone-m: var(--mark-good)}[data-design=sheet] .card.tone-watch{--tone-c: var(--sv-amber);--tone-m: var(--mark-watch)}[data-design=sheet] .card.tone-bad{--tone-c: var(--sv-coral);--tone-m: var(--mark-bad)}[data-design=sheet] .glist-item+.glist-item:before{left:0;right:0}[data-design=sheet] .row-action:hover{background:color-mix(in srgb,var(--ink) 5%,transparent)}[data-design=sheet] .row.is-selected{background:var(--accent-soft);border-left:4px solid var(--sv-blue);padding-left:12px}[data-design=sheet] .row-icon{background:var(--surface-2);border:1px solid var(--line);color:var(--ink)}[data-design=sheet] .row-title{font-weight:600;font-size:15.5px}[data-design=sheet] .row-title.mono{font-size:14px;font-weight:400}[data-design=sheet] .row-sub{font-size:14.5px;color:var(--ink-2)}[data-design=sheet] .stat{border-right:1px solid var(--line);border-bottom:1px solid var(--line);margin:0 -1px -1px 0;padding:16px 18px}[data-design=sheet] .stat-label{font-size:14px;font-weight:600;color:var(--ink-2)}[data-design=sheet] .stat-value{font-family:var(--display);font-weight:700;font-size:32px;letter-spacing:0;line-height:1.1;margin:4px 0}[data-design=sheet] .stat-note{font-size:14px;color:var(--ink-2)}[data-design=sheet] .kv{font-size:15px}[data-design=sheet] .kv dt,[data-design=sheet] .field-label{font-weight:600}[data-design=sheet] .note{background:var(--surface);border:1px solid var(--line-strong);font-size:15px}[data-design=sheet] .note-bad{background:var(--bad-soft);border-color:var(--sv-coral)}[data-design=sheet] .note-watch{background:var(--watch-soft);border-color:var(--sv-amber)}[data-design=sheet] .note-good{background:var(--good-soft);border-color:color-mix(in srgb,var(--good) 45%,transparent)}[data-design=sheet] .note-accent{background:var(--accent-soft);border-color:color-mix(in srgb,var(--sv-blue) 45%,transparent)}[data-design=sheet] .facts .fact{background:var(--surface-2);border:1px solid var(--line)}[data-design=sheet] .fact dd{font-family:var(--display);font-weight:700;font-size:20px}[data-design=sheet] .info-btn{color:var(--ink-2)}[data-design=sheet] .info-btn:hover{color:var(--accent)}[data-design=sheet] .btn{border:1.5px solid transparent;font-weight:600;font-size:15px;padding:7px 16px}[data-design=sheet] .btn-sm{font-size:14px;padding:4px 12px}[data-design=sheet] .btn-icon{padding:8px}[data-design=sheet] .btn-filled{background:var(--sv-blue);border-color:var(--sv-blue);color:#fff}[data-design=sheet] .btn-filled:hover{background:color-mix(in srgb,var(--sv-blue) 85%,#000)}[data-design=sheet] .btn-tinted{background:transparent;border-color:var(--sv-blue);color:var(--accent)}[data-design=sheet] .btn-tinted:hover{background:var(--accent-soft)}[data-design=sheet] .btn-plain{background:transparent;border-color:var(--line-strong);color:var(--ink)}[data-design=sheet] .btn-plain:hover{background:var(--surface-2);border-color:var(--ink)}[data-design=sheet] .btn-danger{background:transparent;border-color:var(--bad);color:var(--bad)}[data-design=sheet] .btn-danger:hover{background:var(--bad-soft)}[data-design=sheet] :is(.code,.cmd) .btn-tinted{background:transparent;border-color:#8a8a8a;color:var(--code-ink)}[data-design=sheet] :is(.code,.cmd) .btn-tinted:hover{background:#ffffff1a}[data-design=sheet] .seg{background:transparent;border:1.5px solid var(--ink);padding:0}[data-design=sheet] .seg-thumb,:root[data-design=sheet][data-theme=dark] .seg-thumb{top:0;bottom:0;background:var(--ink)}@media(prefers-color-scheme:dark){:root[data-design=sheet]:not([data-theme=light]) .seg-thumb{background:var(--ink)}}[data-design=sheet] .seg-opt{font-weight:600;font-size:14.5px;color:var(--ink)}[data-design=sheet] .seg-opt[aria-checked=true]{color:var(--bg)}[data-design=sheet] .seg:has(.seg-thumb:not(.is-ready)) .seg-opt[aria-checked=true]{background:var(--ink)}[data-design=sheet] .seg-count{background:none;font-weight:700;padding:0;font-variant-numeric:tabular-nums}[data-design=sheet] .switch{background:transparent;border:1.5px solid var(--ink)}[data-design=sheet] .switch-knob{background:var(--ink);top:3px;left:3px;width:22px;height:22px}[data-design=sheet] .switch[aria-checked=true]{background:var(--sv-mint)}[data-design=sheet] .switch[aria-checked=true] .switch-knob{background:#000}[data-design=sheet] .switch:disabled{opacity:.45}[data-design=sheet] .stepper{background:var(--surface);border:1.5px solid var(--line-strong)}[data-design=sheet] .stepper:focus-within{border-color:var(--focus)}[data-design=sheet] .stepper input{font-variant-numeric:tabular-nums;font-weight:600}[data-design=sheet] .input,[data-design=sheet] .chips{background:var(--surface);border:1.5px solid var(--line-strong)}[data-design=sheet] .input:focus,[data-design=sheet] .chips:focus-within{background:var(--surface);border-color:var(--focus)}[data-design=sheet] .input::placeholder{color:var(--ink-3)}[data-design=sheet] .has-error .input{border-color:var(--bad)}[data-design=sheet] .field-error{font-weight:600}[data-design=sheet] .chip{background:var(--surface-2);border:1px solid var(--line-strong);font-size:14px;font-weight:600}[data-design=sheet] .chip-x:hover{background:none;color:var(--bad)}[data-design=sheet] .pill{background:none;padding:0;gap:7px;font-size:14.5px;font-weight:700;line-height:1.3;color:var(--ink)}[data-design=sheet] .pill .ic{display:none}[data-design=sheet] .pill:before{content:"";width:13px;height:13px;flex:none;background:center / contain no-repeat}[data-design=sheet] .pill-good:before{background-image:var(--mark-good)}[data-design=sheet] .pill-watch:before{background-image:var(--mark-watch)}[data-design=sheet] .pill-bad:before{background-image:var(--mark-bad)}[data-design=sheet] .pill-neutral{color:var(--ink-2)}[data-design=sheet] .pill-neutral:before{width:10px;height:10px;border:1.5px solid currentColor}[data-design=sheet] .pill-accent{color:var(--accent)}[data-design=sheet] .pill-accent:before{width:10px;height:10px;background:var(--sv-blue)}[data-design=sheet] .pill.is-solid{background:none}[data-design=sheet] .pill.is-solid.pill-good{color:var(--good)}[data-design=sheet] .pill.is-solid.pill-bad{color:var(--bad)}[data-design=sheet] .change{background:none;padding:0;min-width:0;font-size:14px;font-weight:700;font-variant-numeric:tabular-nums}[data-design=sheet] .change-good{background:none;color:var(--good)}[data-design=sheet] .change-bad{background:none;color:var(--bad)}[data-design=sheet] .change-neutral{background:none;color:var(--ink-2)}[data-design=sheet] .badge{display:inline-flex;align-items:center;gap:5px;background:none;padding:0;min-width:0;font-size:14px;font-weight:700;line-height:18px}[data-design=sheet] .badge:before{content:"";width:12px;height:12px;flex:none;background:center / contain no-repeat}[data-design=sheet] .badge-bad{background:none;color:var(--bad)}[data-design=sheet] .badge-bad:before{background-image:var(--mark-bad)}[data-design=sheet] .badge-watch{background:none;color:var(--watch)}[data-design=sheet] .badge-watch:before{background-image:var(--mark-watch)}[data-design=sheet] .badge-accent{background:none;color:var(--accent)}[data-design=sheet] .badge-accent:before{width:8px;height:8px;background:var(--sv-blue)}[data-design=sheet] .dot{width:13px;height:13px;background:center / contain no-repeat}[data-design=sheet] .dot-good{background-color:transparent;background-image:var(--mark-good)}[data-design=sheet] .dot-watch{background-color:transparent;background-image:var(--mark-watch)}[data-design=sheet] .dot-bad{background-color:transparent;background-image:var(--mark-bad)}[data-design=sheet] .code,[data-design=sheet] .cmd{background:var(--code-bg);color:var(--code-ink);border:1px solid var(--line-strong)}[data-design=sheet] .code-head{font-size:13.5px;font-weight:600}[data-design=sheet] .code pre{font:400 13.5px/1.6 var(--mono)}[data-design=sheet] .empty-icon{background:var(--surface-2);border:1px solid var(--line-strong);color:var(--ink-2)}[data-design=sheet] .empty-title{font-weight:700;font-size:17px}[data-design=sheet] .empty-body{font-size:15px}[data-design=sheet] .sk{background:var(--line);animation:svPulse 1.2s ease-in-out infinite alternate}[data-design=sheet] .toast{font-weight:600}[data-design=sheet] .sheet,[data-design=sheet] .popover{background:var(--surface);border:1.5px solid var(--ink)}[data-design=sheet] .sheet-title{font-family:var(--display);font-weight:700;font-size:26px;letter-spacing:0}[data-design=sheet] .sheet-h3{font-weight:700}[data-design=sheet] .close-x{background:var(--surface-2);border:1px solid var(--line-strong);color:var(--ink)}[data-design=sheet] .grabber{background:var(--ink-3)}[data-design=sheet] .sheet-foot{border-top-color:var(--line-strong)}[data-design=sheet] .menu-label{font-size:13.5px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--ink-2)}[data-design=sheet] .menu-sep{background:var(--line-strong)}[data-design=sheet] .menu-item:hover,[data-design=sheet] .menu-item:focus-visible{background:color-mix(in srgb,var(--ink) 6%,transparent)}[data-design=sheet] .menu-item[aria-checked=true]{background:var(--accent-soft)}[data-design=sheet] .menu-icon{background:var(--surface-2);border:1px solid var(--line);color:var(--ink)}[data-design=sheet] .menu-text b{font-weight:700;font-size:15px}[data-design=sheet] .menu-text small{font-size:13.5px;color:var(--ink-2)}[data-design=sheet] .table{font-size:15px}[data-design=sheet] .table th{background:var(--surface-2);border-bottom:1.5px solid var(--ink);font-size:14px;font-weight:700;color:var(--ink)}[data-design=sheet] .th-btn{font-weight:700}[data-design=sheet] .table td.is-num{font-variant-numeric:tabular-nums}[data-design=sheet] .table tbody tr:hover{background:color-mix(in srgb,var(--ink) 3%,transparent)}[data-design=sheet] .table-tools{border-bottom-color:var(--line-strong)}[data-design=sheet] .table-count{font-size:14px;color:var(--ink-2)}[data-design=sheet] .table .pill{white-space:normal;text-align:left}@media(min-width:641px){[data-design=sheet] .table th{white-space:normal;vertical-align:bottom}[data-design=sheet] .th-btn{padding:9px 10px}[data-design=sheet] .table td{padding:10px;overflow-wrap:anywhere}[data-design=sheet] .table td.is-num{white-space:nowrap;overflow-wrap:normal}[data-design=sheet] .table td.is-primary{white-space:normal}[data-design=sheet] .table td .btn{white-space:normal;text-align:left}}[data-design=sheet] .hstrip{background:var(--surface-2);border:1px solid var(--line-strong);font-size:15px;color:var(--ink-2)}[data-design=sheet] .hstrip.tone-bad{background:var(--bad-soft);border-color:var(--sv-coral)}[data-design=sheet] .hstrip.tone-watch{background:var(--watch-soft);border-color:var(--sv-amber)}[data-design=sheet] :is(.tone-good,.tone-watch,.tone-bad) .light{background:var(--surface);border:1px solid var(--line-strong)}[data-design=sheet] .send{border-top-color:var(--line-strong)}[data-design=sheet] .sidebar{border-right:1px solid var(--line-strong)}[data-design=sheet] .brand{font-weight:700;font-size:18px;letter-spacing:-.005em}[data-design=sheet] .brand:hover{background:none}[data-design=sheet] .nav-group-label{font-size:13.5px;font-weight:700;color:var(--ink-2);letter-spacing:.04em;text-transform:uppercase;padding:8px 12px 4px}[data-design=sheet] .nav-item{position:relative;font-size:15.5px;font-weight:400;color:var(--ink)}[data-design=sheet] .nav-item .ic{color:var(--ink-2);stroke-width:1.7}[data-design=sheet] .nav-item:hover{background:color-mix(in srgb,var(--ink) 6%,transparent)}[data-design=sheet] .nav-item[aria-current=page]{background:var(--bg);color:var(--ink);font-weight:700;outline:1px solid var(--line-strong);outline-offset:-1px}[data-design=sheet] .nav-item[aria-current=page]:before{content:"";position:absolute;left:0;top:0;bottom:0;width:4px;background:var(--sv-blue)}[data-design=sheet] .nav-item[aria-current=page] .ic{color:var(--accent)}[data-design=sheet] .side-foot{border-top:1px solid var(--line-strong)}[data-design=sheet] .side-health{font-size:14px;color:var(--ink-2)}[data-design=sheet] .side-health:hover{background:color-mix(in srgb,var(--ink) 5%,transparent)}[data-design=sheet] .side-health b{font-weight:700;color:var(--ink)}[data-design=sheet] .side-health small,[data-design=sheet] .side-demo{font-size:13px;color:var(--ink-2)}[data-design=sheet] .topbar.is-compact{background:var(--bg);border-bottom-color:var(--frame)}[data-design=sheet] .topbar-title{font-family:var(--display);font-weight:700;font-size:19px}[data-design=sheet] .profile{background:var(--bg);border:1px solid var(--line-strong);font-weight:600;font-size:15px}[data-design=sheet] .profile:hover{border-color:var(--ink)}[data-design=sheet] .repo-av{background:var(--ink);color:var(--bg);font-weight:700}[data-design=sheet] .demo-banner{background:var(--accent-soft);border:1px solid color-mix(in srgb,var(--sv-blue) 40%,transparent);font-weight:400;font-size:15px}[data-design=sheet] .more-label{font-size:13.5px;font-weight:700;text-transform:uppercase;letter-spacing:.04em;color:var(--ink-2)}[data-design=sheet] .more-item{background:var(--surface);border:1px solid var(--line-strong);font-size:15px;font-weight:600}[data-design=sheet] .more-item[aria-current=page]{background:var(--surface);color:var(--ink);border-color:var(--ink);border-left:4px solid var(--sv-blue)}[data-design=sheet] .logo-opt[aria-checked=true]{background:var(--accent-soft);border-color:var(--sv-blue)}[data-design=sheet] .logo-opt:hover{background:color-mix(in srgb,var(--ink) 5%,transparent)}[data-design=sheet] .logo-name,[data-design=sheet] .pref-row{font-weight:600}@media(max-width:820px){[data-design=sheet] .tabbar{background:var(--bg);border-top:1.5px solid var(--frame)}[data-design=sheet] .tab{font-size:12px;font-weight:600;color:var(--ink-2)}[data-design=sheet] .tab[aria-current=page]{color:var(--accent)}[data-design=sheet] .large-title{font-size:38px}}[data-design=sheet] .hero-name{font-size:16px;font-weight:700;color:var(--ink)}[data-design=sheet] .hero-num{font-family:var(--display);font-weight:700;font-size:68px;letter-spacing:-.01em}[data-design=sheet] .hero-num small{font-family:var(--font);font-weight:600;letter-spacing:0}[data-design=sheet] .hero-date{font-size:15px;font-weight:400}[data-design=sheet] .hero-meaning{font-size:16px}[data-design=sheet] .hero-note{background:var(--surface-2);border:1px solid var(--line)}[data-design=sheet] .scrub .u-cursor-x{border-right:1px solid var(--ink)!important;opacity:1}[data-design=sheet] .scrub-flag{font-size:13.5px;font-weight:600}[data-design=sheet] .spark-line{stroke:var(--ink);stroke-width:1.5}[data-design=sheet] .spark-good .spark-line{stroke:var(--good)}[data-design=sheet] .spark-bad .spark-line{stroke:var(--bad)}[data-design=sheet] .watch-row+.watch-row:before{left:0;right:0}[data-design=sheet] .watch-main{margin:0;border-left:4px solid transparent}[data-design=sheet] button.watch-main:hover{background:color-mix(in srgb,var(--ink) 5%,transparent)}[data-design=sheet] .watch-row.is-selected .watch-main{background:var(--accent-soft);border-left-color:var(--sv-blue)}[data-design=sheet] .watch-name b{font-size:15.5px;font-weight:700}[data-design=sheet] .watch-name small{font-size:14px;color:var(--ink-2)}[data-design=sheet] .watch-val b{font-family:var(--display);font-weight:700;font-size:21px}[data-design=sheet] .bar-track{background:var(--surface-2)}[data-design=sheet] .bar-value{font-size:14px;font-weight:600;color:var(--ink);font-variant-numeric:tabular-nums}[data-design=sheet] .bar-label{font-size:14px;color:var(--ink-2);white-space:normal;line-height:1.2}@media(max-width:640px){[data-design=sheet] .bars{gap:3px;padding-inline:0}[data-design=sheet] .bar-label{font-size:13px;margin-inline:-1.5px;max-width:calc(100% + 3px)}}[data-design=sheet] .bar-fill.tone-accent{background:var(--sv-blue)}[data-design=sheet] .bar-fill.tone-good{background:var(--sv-mint)}[data-design=sheet] .bar-fill.tone-bad{background:var(--sv-coral)}[data-design=sheet] .bar-fill.tone-watch{background:var(--sv-amber)}[data-design=sheet] .bar-fill.tone-neutral{background:var(--ink)}@media(max-width:820px){[data-design=sheet] .hero-num{font-size:54px}}@media(max-width:380px){[data-design=sheet] .hero-num{font-size:44px}}[data-design=sheet] .seg-count,[data-design=sheet] .cmd-note,[data-design=sheet] .field-hint,[data-design=sheet] .code-inline,[data-design=sheet] .mono-soft{font-size:14px}
@@ -0,0 +1 @@
1
+ import{a as M}from"./chunks/chunk-Y6LUXDM6.js";import{a as Y,d as Q}from"./chunks/chunk-7TJCBOAY.js";import{c as z}from"./chunks/chunk-4SQPQ5TB.js";import{a as X,b as T}from"./chunks/chunk-763NH7PP.js";import{c as G,g as K}from"./chunks/chunk-6BDBXA6V.js";import"./chunks/chunk-MAPYGK3P.js";import{e as J}from"./chunks/chunk-4WKQTWUC.js";import{o as q,q as C,s as E}from"./chunks/chunk-VQ5YXO54.js";import{a as W}from"./chunks/chunk-76WAVKUG.js";import"./chunks/chunk-VWKSMFWJ.js";import{G as U,P as R,R as P,S as V,T as A,a as S,b as x,c as b,d as g,f as F,i as v,w as e,x as w,z as N}from"./chunks/chunk-GT4EAKAO.js";import{d as h}from"./chunks/chunk-G7NGB5UX.js";h();h();h();function O({withName:o}){let[t,s]=b(!1),i=F(null),u=v(G).id,[r,l]=b(null);return g(()=>{t&&!r&&import("./chunks/logos-N5IINUAM.js").then(a=>l(a.LOGOS)).catch(()=>l([]))},[t]),e(S,{children:[e("button",{ref:i,type:"button",class:"brand press","aria-haspopup":"dialog","aria-expanded":t,onClick:()=>s(!t),"aria-label":"StackVitals. Choose the logo",children:[e(M,{}),o?e("span",{class:"brand-name",children:"StackVitals"}):null]}),e(Y,{open:t,onClose:()=>s(!1),anchor:i,label:"Choose a logo",align:"start",width:560,sheetOnPhone:!0,children:[e("p",{class:"logo-hint",children:"Pick the mark you like; it changes the sidebar, the browser tab and the splash."}),e("div",{class:"logo-grid",role:"radiogroup","aria-label":"Logos",onKeyDown:a=>{let c=[...a.currentTarget.querySelectorAll("button")],y=c.indexOf(document.activeElement),m=getComputedStyle(a.currentTarget).gridTemplateColumns.split(" ").length,n={ArrowRight:1,ArrowLeft:-1,ArrowDown:m,ArrowUp:-m}[a.key];n&&y>=0&&(a.preventDefault(),c[Math.max(0,Math.min(c.length-1,y+n))]?.focus())},children:(r||[]).map(a=>e("button",{type:"button",role:"radio","aria-checked":a.id===u,class:"logo-opt press",onClick:()=>{K(a),V(`Logo set to ${a.name}`)},title:a.note,children:[e(M,{def:a,size:44}),e("span",{class:"logo-name",children:a.name}),e("span",{class:"logo-note",children:a.note})]}))})]})]})}h();var p=(o,t)=>o?.facts?.[t]??0,k=[{id:"overview",label:"Overview",icon:"overview",group:"Health",load:()=>import("./pages/overview.js")},{id:"insights",label:"Insights",icon:"insights",group:"Health",module:"insights",load:()=>import("./pages/insights.js"),badge:o=>p(o,"findings")?{n:p(o,"findings"),tone:"accent",label:"findings to fix"}:null},{id:"ci",label:"CI Health",short:"CI",icon:"ci",group:"Health",module:"ci",load:()=>import("./pages/ci.js"),badge:o=>p(o,"ci_flaky")+p(o,"ci_broken")?{n:p(o,"ci_flaky")+p(o,"ci_broken"),tone:"bad",label:"flaky or failing"}:null},{id:"prs",label:"Pull Requests",short:"PRs",icon:"prs",group:"Health",module:"flow",load:()=>import("./pages/prs.js"),badge:o=>p(o,"stale_prs")?{n:p(o,"stale_prs"),tone:"watch",label:"stale"}:null},{id:"delivery",label:"Delivery",icon:"delivery",group:"Health",module:"delivery",load:()=>import("./pages/delivery.js")},{id:"community",label:"Community",icon:"community",group:"Health",module:"community",load:()=>import("./pages/community.js"),badge:o=>p(o,"newcomers_waiting")?{n:p(o,"newcomers_waiting"),tone:"watch",label:"newcomers waiting"}:null},{id:"deps",label:"Dependencies",short:"Deps",icon:"deps",group:"Health",module:"deps|vulns|supply",load:()=>import("./pages/deps.js"),badge:o=>p(o,"vulns_serious")?{n:p(o,"vulns_serious"),tone:"bad",label:"serious alerts"}:null},{id:"investment",label:"Investment",short:"Invest",icon:"investment",group:"Organisation",module:"delivery",load:()=>import("./pages/investment.js")},{id:"portal",label:"Portal",icon:"portal",group:"Organisation",module:"portal",load:()=>import("./pages/portal.js")},{id:"standing",label:"Where you stand",short:"Standing",icon:"insights",group:"Organisation",load:()=>import("./pages/standing.js")},{id:"explore",label:"Explore",icon:"explore",group:"Organisation",load:()=>import("./pages/explore.js")},{id:"activity",label:"Activity",icon:"activity",group:"Team",load:()=>import("./pages/activity.js")},{id:"health",label:"Data health",short:"Health",icon:"clock",group:"Team",load:()=>import("./pages/health.js")},{id:"settings",label:"Settings",icon:"settings",group:"Team",load:()=>import("./pages/settings.js")},{id:"setup",label:"Setup",icon:"setup",group:"Install",load:()=>import("./pages/setup.js")},{id:"connections",label:"Connections",short:"Connect",icon:"connections",group:"Install",load:()=>import("./pages/connections.js")},{id:"agents",label:"Agents",icon:"agents",group:"Install",load:()=>import("./pages/agents.js")},{id:"help",label:"Help",icon:"help",group:"Install",load:()=>import("./pages/help.js")},{id:"about",label:"About",icon:"about",group:"Install",load:()=>import("./pages/about.js")}],I=["overview","insights","ci","prs"],B=o=>k.find(t=>t.id===o);var oe=new Set((document.querySelector('meta[name="sv-page-css"]')?.content||"").split(",").filter(Boolean)),$=new Map,Z=document.querySelector('script[type="module"][src]')?.src||location.href,te=new URL(Z,location.href).search;function j(o){return oe.has(o)?($.has(o)||$.set(o,new Promise(t=>{let s=Object.assign(document.createElement("link"),{rel:"stylesheet",href:new URL(`pages/${o}.css`,Z).href+te});s.onload=s.onerror=()=>t(),document.head.appendChild(s)})),$.get(o)):Promise.resolve()}var _=new Map,L=null;function ne(){let[,o]=b(0);return g(()=>{L||Promise.all([import("./pages/welcome.js"),j("welcome")]).then(([t])=>{L=t.default,o(1)})},[]),L?e(L,{}):e(P,{})}function ae({r:o,def:t,app:s}){let[,i]=b(0),[u,r]=b(null),l=v(E);if(g(()=>{if(!t||_.has(t.id))return;let c=!0;return r(null),Promise.all([t.load(),j(t.id)]).then(([y])=>{_.set(t.id,y.default),c&&i(m=>m+1)},()=>{c&&r(t.id)}),()=>{c=!1}},[t?.id]),!t)return e("div",{class:"page",children:e(R,{icon:"search",title:"This page doesn't exist.",action:e(N,{href:"#/",variant:"filled",children:"Go to Overview"})})});if(u===t.id)return e("div",{class:"page",children:e(R,{icon:"warn",title:"This part of the app didn't load.",children:"Reload the page; if it keeps failing, the files on this site may be from an older run."})});let a=_.get(t.id);return a?e("div",{class:"page-enter",children:e(a,{app:s,route:o})},`${l.id}|${o.page}|${o.rest.join("/")}`):e(P,{})}function se({def:o,active:t,app:s}){let i=o.badge?.(s),u=o.module&&s&&!o.module.split("|").some(r=>s.modules?.[r]);return e("a",{class:"nav-item press"+(u?" is-off":""),href:T(o.id),"aria-current":t?"page":void 0,title:u?`${o.label}: not set up for this repo`:void 0,children:[e(w,{name:o.icon}),e("span",{class:"nav-label",children:o.label}),i?e("span",{class:`badge badge-${i.tone} num`,"aria-label":`${i.n} ${i.label}`,children:i.n}):null]})}function ee(){let o=v(X),t=J(),s=v(E),i=v(W),[u,r]=b(!1),l=B(o.page);g(()=>{let n=document.getElementById("splash");n&&(n.classList.add("is-gone"),setTimeout(()=>n.remove(),450))},[]),g(()=>{r(!1)},[o.page]);let a=[...new Set(k.map(n=>n.group))],c=!I.includes(o.page),y=v(q),m=!C&&(y||s.id==="here"&&t===null);return g(()=>{document.documentElement.dataset.page=m?"welcome":o.page},[m,o.page]),g(()=>{document.title=m?"Welcome \xB7 StackVitals":t===void 0?"StackVitals":`${l?.label||"Not found"} \xB7 ${s.label} \xB7 StackVitals`},[m,t===void 0,l?.id,s.label]),m?e(S,{children:[e(ne,{}),e(A,{})]}):e("div",{class:"app",children:[e("a",{class:"skip",href:"#main",onClick:n=>{n.preventDefault(),document.getElementById("main")?.focus()},children:"Skip to content"}),e("aside",{class:"sidebar","aria-label":"Sections",children:[e("div",{class:"side-brand",children:e(O,{withName:!0})}),e("nav",{class:"side-nav","aria-label":"Sections",children:a.map(n=>e("div",{class:"nav-group",role:"group","aria-label":n,children:[e("div",{class:"nav-group-label","aria-hidden":"true",children:n}),k.filter(d=>d.group===n).map(d=>e(se,{def:d,active:d.id===o.page,app:t??null}))]}))}),e("div",{class:"side-foot",children:[e(z,{app:t}),C?e("p",{class:"side-demo",children:"Demo data for three example repos."}):null]})]}),e("div",{class:"main-col",children:[e("header",{class:"topbar"+(i.compact?" is-compact":""),children:[e("span",{class:"topbar-logo",children:e(O,{})}),e("span",{class:"topbar-title","aria-hidden":!i.compact,children:i.title}),e(Q,{})]}),e("main",{id:"main",tabIndex:-1,children:[C?e("div",{class:"demo-banner",children:[e(w,{name:"play"}),e("span",{children:["You are looking at demo data. ",e("a",{href:location.pathname,children:"Open your own repo"})]})]}):null,t===void 0?e(P,{}):e(ae,{r:o,def:l,app:t})]})]}),e("nav",{class:"tabbar","aria-label":"Sections",children:[I.map(n=>{let d=B(n);return e("a",{class:"tab press",href:T(n),"aria-current":o.page===n?"page":void 0,children:[e(w,{name:d.icon}),e("span",{children:d.short||d.label})]})}),e("button",{type:"button",class:"tab press","aria-haspopup":"dialog","aria-expanded":u,"aria-current":c?"page":void 0,onClick:()=>r(!0),children:[e(w,{name:c&&l?l.icon:"more"}),e("span",{children:c&&l?(l.short||l.label).split(" ")[0]:"More"})]})]}),e(U,{open:u,onClose:()=>r(!1),title:"More",size:"sm",children:a.map(n=>{let d=k.filter(f=>f.group===n&&!I.includes(f.id));return d.length?e("div",{class:"more-group",children:[e("h3",{class:"more-label",children:n}),e("div",{class:"more-grid",children:d.map(f=>{let H=f.badge?.(t??null);return e("a",{class:"more-item press",href:T(f.id),"aria-current":f.id===o.page?"page":void 0,onClick:()=>r(!1),children:[e(w,{name:f.icon}),e("span",{children:f.label}),H?e("span",{class:`badge badge-${H.tone} num`,children:H.n}):null]})})})]}):null})}),e(A,{})]})}x(e(ee,{}),document.getElementById("root"));
@@ -0,0 +1 @@
1
+ .pipeline{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px}.pipe-node{position:relative;display:flex;flex-direction:column;gap:6px;padding:16px;border-radius:var(--r-md);background:var(--surface);border:1px solid var(--line);min-width:0;animation:pageIn var(--dur-3) var(--spring) backwards}.pipe-node b{font-size:16px}.pipe-text{color:var(--ink-2);font-size:13.5px;line-height:1.45;overflow-wrap:anywhere}.pipe-icon{width:36px;height:36px;border-radius:10px;display:grid;place-items:center;background:var(--accent-soft);color:var(--accent)}.pipe-icon .ic{width:20px;height:20px}.packet{position:absolute;top:34px;right:-12px;width:10px;height:10px;border-radius:50%;background:var(--accent);z-index:1;animation:packet 2.4s ease-in-out infinite}@keyframes packet{0%{opacity:0;transform:translate(-10px)}30%,70%{opacity:1}to{opacity:0;transform:translate(10px)}}@media(prefers-reduced-motion:reduce){.packet{animation:none;opacity:.8}}.about-steps{margin:0 0 12px;padding-left:22px;display:flex;flex-direction:column;gap:8px;font-size:14.5px;overflow-wrap:anywhere}.about-btn{margin-top:12px}@media(max-width:900px){.pipeline{grid-template-columns:minmax(0,1fr);gap:12px}.packet{inset:auto auto -11px 32px;animation-name:packetDown}@keyframes packetDown{0%{opacity:0;transform:translateY(-8px)}30%,70%{opacity:1}to{opacity:0;transform:translateY(8px)}}}.ab-lede{display:grid;grid-template-columns:minmax(0,1.6fr) minmax(0,1fr);gap:18px 28px;align-items:center}.ab-lede-text{margin:0;font-size:17px;line-height:1.55;max-width:64ch}.ab-cells{margin:0;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px 16px}.ab-cells dt{font-size:12.5px;font-weight:650;color:var(--ink-2)}.ab-cells dd{margin:2px 0 0;font-size:15.5px;font-weight:700;overflow-wrap:anywhere}@media(max-width:900px){.ab-lede{grid-template-columns:minmax(0,1fr)}}.pipe-step{font-size:12.5px;font-weight:700;color:var(--ink-2);letter-spacing:.02em}.ab-facts{margin:0;display:grid;grid-template-columns:minmax(120px,190px) minmax(0,1fr)}.ab-facts>div{display:contents}.ab-facts dt,.ab-facts dd{margin:0;padding:10px 0;border-top:1px solid var(--line);font-size:14.5px;overflow-wrap:anywhere;min-width:0}.ab-facts>div:first-child :is(dt,dd){border-top:0;padding-top:0}.ab-facts dt{font-weight:650;color:var(--ink-2);padding-right:14px}@media(max-width:560px){.ab-facts{grid-template-columns:minmax(0,1fr)}.ab-facts dd{border-top:0;padding-top:0}}.ab-mod{font-weight:650}.ab-from{color:var(--ink-2)}.ab-result{font-weight:750;white-space:nowrap}.ab-weak{display:inline-flex;gap:8px;align-items:flex-start}.ab-weak .sv-mark{margin-top:4px}.ab-detail{margin-top:22px;display:flex;flex-direction:column;gap:12px;scroll-margin-top:calc(var(--top-h, 56px) + 16px)}.ab-detail-head{display:flex;align-items:flex-end;justify-content:space-between;gap:10px 16px;flex-wrap:wrap}.ab-detail-head .field{flex:0 1 360px;min-width:0;margin:0}.ab-h3{margin:0;font-size:17px;font-weight:750}.ab-detail>.ab-facts{padding:14px 16px;background:var(--surface);border:1px solid var(--line);border-radius:var(--r-md)}.ab-foot{display:flex}[data-design=sheet][data-page=about] .pipeline{gap:32px;counter-reset:pipe}[data-design=sheet][data-page=about] .pipe-node{counter-increment:pipe;background:var(--surface);border:1.5px solid var(--frame);padding:18px;gap:8px;animation:svFade var(--dur-3) var(--sv-ease) backwards}[data-design=sheet][data-page=about] .pipe-node:before{content:counter(pipe);position:absolute;top:14px;right:14px;width:28px;height:28px;border:1.5px solid var(--ink);border-radius:50%!important;display:grid;place-items:center;font:700 14.5px/1 var(--font)}[data-design=sheet][data-page=about] .pipe-node b{font-weight:700;font-size:18px}[data-design=sheet][data-page=about] .pipe-text{font-size:15px;color:var(--ink-2)}[data-design=sheet][data-page=about] .pipe-icon{width:38px;height:38px;background:var(--surface-2);border:1.5px solid var(--ink);color:var(--ink)}[data-design=sheet][data-page=about] .pipe-icon .ic{width:20px;height:20px;stroke-width:1.7}[data-design=sheet][data-page=about] .packet{inset:50% -33px auto auto;width:32px;height:9px;margin-top:-4.5px;background:none;opacity:1;transform-origin:left center;animation:svPipeX .55s var(--sv-ease) .5s backwards}[data-design=sheet][data-page=about] .packet:before{content:"";position:absolute;left:0;right:8px;top:3.5px;height:2px;background:var(--sv-blue)}[data-design=sheet][data-page=about] .packet:after{content:"";position:absolute;right:0;top:0;width:9px;height:9px;background:var(--sv-blue);clip-path:polygon(0 0,100% 50%,0 100%)}@keyframes svPipeX{0%{transform:scaleX(0)}}@keyframes svPipeY{0%{transform:scaleY(0)}}[data-design=sheet][data-page=about] .about-steps{list-style:none;padding:0;gap:0;counter-reset:rm;font-size:15.5px}[data-design=sheet][data-page=about] .about-steps li{counter-increment:rm;position:relative;padding:10px 0 10px 40px}[data-design=sheet][data-page=about] .about-steps li+li{border-top:1px solid var(--line)}[data-design=sheet][data-page=about] .about-steps li:first-child{padding-top:0}[data-design=sheet][data-page=about] .about-steps li:before{content:counter(rm);position:absolute;left:0;top:8px;width:28px;height:28px;border:1.5px solid var(--ink);border-radius:50%!important;display:grid;place-items:center;font:700 14.5px/1 var(--font)}[data-design=sheet][data-page=about] .about-steps li:first-child:before{top:-2px}@media(max-width:900px){[data-design=sheet][data-page=about] .pipeline{gap:32px}[data-design=sheet][data-page=about] .packet{inset:auto auto -33px 32px;width:9px;height:32px;margin:0;transform-origin:center top;animation-name:svPipeY}[data-design=sheet][data-page=about] .packet:before{inset:0 auto 8px 3.5px;width:2px;height:auto}[data-design=sheet][data-page=about] .packet:after{top:auto;bottom:0;clip-path:polygon(0 0,100% 0,50% 100%)}}@media(prefers-reduced-motion:reduce){[data-design=sheet][data-page=about] :is(.pipe-node,.packet){animation:none}}[data-design=sheet][data-page=about] .ab-lede{background:var(--surface);border:1.5px solid var(--frame)}[data-design=sheet][data-page=about] .ab-lede-text{font-size:18px}[data-design=sheet][data-page=about] .ab-cells{gap:0;border:1px solid var(--line-strong)}[data-design=sheet][data-page=about] .ab-cells>div{padding:10px 14px;border-bottom:1px solid var(--line)}[data-design=sheet][data-page=about] .ab-cells>div:nth-child(odd){border-right:1px solid var(--line)}[data-design=sheet][data-page=about] .ab-cells>div:nth-last-child(-n+2){border-bottom:0}[data-design=sheet][data-page=about] .ab-cells dt{font:700 13.5px/1.3 var(--font);letter-spacing:.04em;text-transform:uppercase;color:var(--ink-2)}[data-design=sheet][data-page=about] .ab-cells dd{font-size:17px;font-weight:700}[data-design=sheet][data-page=about] .ab-cells dd code{font-size:15px;border:0;background:none;padding:0;font-weight:400}[data-design=sheet][data-page=about] .pipe-step{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}[data-design=sheet][data-page=about] .ab-facts dt{font-weight:600;color:var(--ink);font-size:15px}[data-design=sheet][data-page=about] .ab-facts dd{font-size:15.5px}[data-design=sheet][data-page=about] .ab-result{font-weight:700}[data-design=sheet][data-page=about] .ab-mod{font-weight:600}[data-design=sheet][data-page=about] .ab-h3{font-weight:700;font-size:18px}[data-design=sheet][data-page=about] .ab-detail>.ab-facts{border:1.5px solid var(--frame)}[data-design=sheet][data-page=about] .table-wrap{border:1.5px solid var(--frame)}[data-design=sheet][data-page=about] .card:is(.ab-lede,.card-pad){border:1.5px solid var(--frame)}
@@ -0,0 +1 @@
1
+ import{b as W}from"../chunks/chunk-763NH7PP.js";import{a as l}from"../chunks/chunk-6RLVD2DT.js";import{b as P,c as s,d as w,e as n}from"../chunks/chunk-76WAVKUG.js";import{d as A,h as I}from"../chunks/chunk-VWKSMFWJ.js";import{L as m,O as S,Q as C,a as d,c as f,d as v,w as e,x,z as b}from"../chunks/chunk-GT4EAKAO.js";import{d as g}from"../chunks/chunk-G7NGB5UX.js";g();g();var D=[{id:"ci",name:"CI Health",page:"ci",answers:"Which tests and jobs are flaky, failing or slow, and what CI costs.",from:"Your CI runs, jobs, steps and test reports (JUnit, CTRF, Jest, Go, TRX)."},{id:"prs",name:"Pull Requests",page:"prs",answers:"Who has to act next on each pull request, and how long reviews wait.",from:"Pull requests, reviews, comments and CODEOWNERS."},{id:"delivery",name:"Delivery",page:"delivery",answers:"DORA's metrics, cycle time in four phases and stuck pull requests.",from:"Merged pull requests, deployments and commit statuses; incidents from a pager you connect."},{id:"insights",name:"Insights",page:"insights",answers:"What to fix first, ranked by the hours of waiting at stake.",from:"The other modules' history, combined."},{id:"community",name:"Community",page:"community",answers:"Whether newcomers get a first answer, stay, and who carries the work.",from:"Issues and pull requests from first-time contributors, stars and traffic."},{id:"deps",name:"Dependencies",page:"deps",answers:"Known vulnerabilities, waiting security updates, licences and supply chain.",from:"Your lockfiles scanned with OSV-Scanner; package names looked up on deps.dev, Scorecard and the registries."},{id:"investment",name:"Investment",page:"investment",answers:"Where merged work went each month: features, fixes, maintenance.",from:"Merged pull requests, sorted by label, linked issue type, title or the files changed."},{id:"portal",name:"Portal",page:"portal",answers:"What an org owns, who owns it and how each repo scores.",from:"Every repo in the org, CODEOWNERS, teams and catalog-info.yaml files."},{id:"standing",name:"Where you stand",page:"standing",answers:"How this repo compares with similar public repos.",from:"A daily crawl of about 90,000 public repos, grouped by language and size."},{id:"explore",name:"Explore",page:"explore",answers:"Any question the pages don't answer, in SQL.",from:"The summary tables every module writes: one row per pull request, job and test."}];function R(){return e(l,{rows:D,name:"modules",caption:"What StackVitals measures",search:!1,columns:[{key:"name",label:"Module",primary:!0,render:a=>e("a",{class:"ab-mod",href:W(a.page),children:a.name})},{key:"answers",label:"What it answers"},{key:"from",label:"Where the data comes from",render:a=>e("span",{class:"ab-from",children:a.from})}]})}function z(){let[a,r]=f(null),[i,p]=f("");if(v(()=>{import("../chunks/accuracy-data-OQTDXI7Y.js").then(t=>r(t.CARDS))},[]),!a)return e(n,{children:e(C,{lines:4})});let h=a.map(t=>({id:t.id,title:t.title,check:t.rows[0]?.[0]||"",result:t.rows[0]?.[1]||"",n:t.rows.length,labeller:t.labeller,weak:t.weak,link:t.link})),u=[{key:"title",label:"Rule",primary:!0,render:t=>e("a",{class:"ab-mod",id:`acc-${t.id}`,href:"#acc-rows",onClick:c=>{c.preventDefault(),p(t.id),document.getElementById("acc-rows")?.scrollIntoView({behavior:"smooth",block:"start"})},children:t.title})},{key:"check",label:"Headline check"},{key:"result",label:"Result",num:!0,render:t=>e("b",{class:"ab-result",children:t.result})},{key:"n",label:"Checks",num:!0},{key:"weak",label:"Weak spot",render:t=>e("span",{class:"ab-weak",children:[e("span",{class:"sv-mark sv-mark-watch","aria-hidden":"true"}),t.weak]})}],o=a.find(t=>t.id===i)||a[0];return e(d,{children:[e(l,{rows:h,columns:u,name:"accuracy",caption:"How accurate each rule is",search:!1}),e("div",{class:"ab-detail",id:"acc-rows",children:[e("div",{class:"ab-detail-head",children:[e("h3",{class:"ab-h3",children:"Every check behind one rule"}),e(A,{label:"Rule",children:t=>e(I,{id:t,value:o.id,onChange:p,options:a.map(c=>({value:c.id,label:c.title}))})})]}),e("dl",{class:"ab-facts",children:[e("div",{children:[e("dt",{children:"What it claims"}),e("dd",{children:o.claim})]}),e("div",{children:[e("dt",{children:"Labels"}),e("dd",{children:o.labeller})]}),e("div",{children:[e("dt",{children:"Weak spot"}),e("dd",{children:o.weak})]})]}),e(l,{rows:o.rows.map(([t,c,T],q)=>({id:`${o.id}-${q}`,check:t,result:c,note:T})),name:`accuracy-${o.id}`,caption:`${o.title}: every check`,search:!1,columns:[{key:"check",label:"Check",primary:!0},{key:"result",label:"Result",num:!0,render:t=>e("b",{class:"ab-result",children:t.result})},{key:"note",label:"Range and notes",render:t=>t.note||e("span",{class:"muted",children:"\u2013"})}]}),e("div",{class:"ab-foot",children:e(b,{size:"sm",variant:"tinted",href:o.link,external:!0,iconEnd:"external",children:"Method and labels"})})]})]})}var M=()=>["localhost","127.0.0.1","[::1]"].includes(location.hostname),N=[["contents: write","Saves history to the stackvitals-data branch; changes .github/stackvitals.toml when someone with write access asks.","Nothing is saved."],["actions: read","Reads your CI runs, their jobs and test reports.","CI health stays empty."],["actions: write","Re-runs a job once when only known flakes failed (only with rerun flaky on).","Known flakes are not re-run."],["checks: read / write","Sees which pull requests have failing checks; posts the StackVitals verdict check (only when it's on).","No verdict check."],["statuses, deployments: read","Reads commit statuses and production deployments for delivery.","Delivery guesses deployments from workflow names."],["issues: write","The weekly digest, answers to /stackvitals commands, one issue per flaky test (when on).","No digest, no answers on GitHub."],["pull-requests: write","One comment on a pull request whose CI failed, edited in place (when on).","No comments on pull requests."]].map(([a,r,i])=>({id:a,perm:a,why:r,without:i})),H=[["prs","GitHub",()=>"Your CI runs, pull requests, issues and deployments."],["bolt","The workflow",()=>"Runs in your Actions after every CI run and every 3 hours; reads with the workflow's own token, about a minute."],["lock","stackvitals-data",a=>`A branch in ${a}: every number with its history, every decision with who, why and until when.`],["overview","This page",()=>M()?"Served on your machine by stackvitals ui from a copy of that branch; it keeps working offline.":/\.github\.io$/.test(location.hostname)?"Served by GitHub Pages from that branch. Static files only; nothing runs on a server.":"Static files that read that branch from GitHub; nothing runs on a server."]],y=({rows:a})=>e("dl",{class:"ab-facts",children:a.map(([r,i])=>e("div",{children:[e("dt",{children:r}),e("dd",{children:i})]}))}),k=a=>e("code",{class:"code-inline",children:a}),O=[["Quickstart","quickstart","Set it up in about five minutes."],["Privacy","privacy","What each token can reach and what leaves your repo, file by file."],["Methods","methods","How every number is counted, with its ranges."],["Accuracy","accuracy","Each rule replayed on public history."],["Limitations","limitations","What it can't tell you yet."],["Questions people ask","faq","Cost, data, and how it differs from other tools."]];function G({app:a}){let r=a?.repo||"your repo";return e(P,{title:"About StackVitals",subtitle:"What it is, how it works, what it measures and how far to trust it.",children:[e(s,{title:"What it is",description:"In two sentences.",children:e(n,{class:"ab-lede",children:[e("p",{class:"ab-lede-text",children:"StackVitals measures how your team ships: flaky tests, waiting reviews, delivery speed, dependencies and newcomers, each number with its history and sample size. It runs as a workflow inside your own repo and keeps everything on a branch there, so there is no StackVitals server and no account."}),e("dl",{class:"ab-cells",children:[e("div",{children:[e("dt",{children:"Runs in"}),e("dd",{children:"Your GitHub Actions"})]}),e("div",{children:[e("dt",{children:"Keeps data in"}),e("dd",{children:e("code",{class:"code-inline",children:"stackvitals-data"})})]}),e("div",{children:[e("dt",{children:"Server"}),e("dd",{children:"None"})]}),e("div",{children:[e("dt",{children:"Price"}),e("dd",{children:"Free, MIT licence"})]})]})]})}),e(s,{title:"How it works",description:"Four steps, in order, all inside your repo.",children:e("ol",{class:"pipeline","aria-label":"How data moves",children:H.map(([i,p,h],u)=>e("li",{class:"pipe-node",style:{animationDelay:`${u*70}ms`},children:[e("span",{class:"pipe-step",children:["Step ",u+1]}),e("span",{class:"pipe-icon",children:e(x,{name:i})}),e("b",{children:p}),e("span",{class:"pipe-text",children:h(r)}),u<H.length-1?e("i",{class:"packet","aria-hidden":"true"}):null]}))})}),e(s,{title:"What it measures",description:"One module per question. Open a module to see it for this repo.",children:e(R,{})}),e(s,{title:"How accurate it is",description:"Each rule checked on public data. Pick a rule's name to read every check behind it. A model's labels say so until a person audits them.",info:e(m,{info:{title:"Accuracy",doc:"accuracy.md",body:"Each card replays a rule on public history and reports what it got right, with ranges and the weak spot it still has."}}),children:e(z,{})}),e(w,{children:[e(s,{title:"What it costs and needs",description:"Free; it uses your own Actions minutes.",info:e(m,{info:{title:"Cost",doc:"faq.md",body:"MIT-licensed, no paid tier, no account and no StackVitals-hosted backend. Optional integrations use services and keys you choose."}}),children:e(n,{children:e(y,{rows:[["Price","Free. No paid tier, no account."],["Actions minutes","About a minute per run on a small repo, after each CI run and every 3 hours. Free on public repos; private repos use the plan's minutes."],["Needs",e(d,{children:["A GitHub repo with Actions on, and the workflow file (",e("a",{href:"#/setup",children:"Setup"})," adds it)."]})],["Optional","GitHub Pages to open this app on a public repo; a token to apply changes from the app; keys for the connectors you add."]]})})}),e(s,{title:"Privacy in short",description:"Your data stays in your repo.",info:e(m,{info:{title:"Privacy",doc:"privacy.md",body:"What each token can reach and what leaves your repo, file by file."}}),children:e(n,{children:e(y,{rows:[["Stored",e(d,{children:["In ",r,"'s ",k("stackvitals-data")," branch; git history is the audit log."]})],["Your code","Never sent anywhere."],["Leaves GitHub",e(d,{children:["Package names, for the dependency check (off with ",k("[deps] outside_lookups = false"),"); the chat webhook you set."]})],["Public pages",e(d,{children:["A GitHub Pages site is public, even for a private repo. View private repos with ",k(`stackvitals ui ${a?.repo||"owner/repo"}`),"."]})],["Tokens pasted here","Kept in this page's memory (or this tab's storage, if you tick keep, until the tab closes); sent only to api.github.com."]]})})})]}),e(s,{title:"How the numbers are counted",description:"Sample sizes always, guesses never.",info:e(m,{info:{title:"Methods",doc:"methods.md",body:"Wilson ranges for shares, order-statistic ranges for medians, Kaplan-Meier for waiting times and false-alarm control for weekly changes."}}),children:e(n,{children:e(y,{rows:[["Sample sizes",'Every number carries one; below the minimum it says "not enough data" instead of guessing.'],["DORA","Counted the way Apache DevLake's dashboards count it, from your own deployments."],["Peer comparisons","From a daily crawl of public repos, grouped by language and size."],["People","No person is ranked or timed on a public page."]]})})}),e(s,{title:"What each permission is for",description:"And what stops working without it.",children:e(l,{name:"permissions",caption:"Permissions",search:!1,rows:N,columns:[{key:"perm",label:"Permission",primary:!0,render:i=>e("span",{class:"mono-soft",children:i.perm})},{key:"why",label:"Why"},{key:"without",label:"Without it",render:i=>e("span",{class:"muted",children:i.without})}]})}),e(w,{children:[e(s,{title:"Remove StackVitals",description:"Three steps; nothing is left behind.",children:e(n,{children:[e("ol",{class:"about-steps",children:[e("li",{children:["Delete ",e("code",{class:"code-inline",children:[".github/workflows/",a?.links?.workflow||"stackvitals.yml"]}),"."]}),e("li",{children:["Delete the ",e("code",{class:"code-inline",children:"stackvitals-data"})," branch."]}),e("li",{children:["Turn GitHub Pages off, or point it elsewhere, in ",a?e("a",{href:`https://github.com/${a.repo}/settings/pages`,target:"_blank",rel:"noopener noreferrer",children:"Settings, Pages"}):"Settings, Pages","."]})]}),a?e(S,{inline:!0,code:"git push origin --delete stackvitals-data"}):null]})}),e(s,{title:"Report a security problem",description:"Privately, to the maintainers.",children:e(n,{children:[e("p",{children:'Use the Security tab of the repository that hosts the StackVitals Action and choose "Report a vulnerability"; it reaches the maintainers privately.'}),e("div",{class:"about-btn",children:e(b,{variant:"tinted",href:"https://github.com/stackvitals/stackvitals/security/advisories/new",external:!0,iconEnd:"external",children:"Report a vulnerability"})})]})})]}),e(s,{title:"Read more",description:"The guides behind this page, in Help.",children:e(l,{name:"about-links",caption:"Read more",search:!1,rows:O.map(([i,p,h])=>({id:p,title:i,what:h})),columns:[{key:"title",label:"Guide",primary:!0,render:i=>e("a",{class:"ab-mod",href:`#/help/${i.id}`,children:i.title})},{key:"what",label:"What's in it"}]})})]})}export{G as default};
@@ -0,0 +1 @@
1
+ .timeline{list-style:none;margin:0;padding:6px 0}.tl{display:grid;grid-template-columns:16px minmax(0,1fr);gap:12px;padding:10px 16px;position:relative;animation:pageIn var(--dur-2) var(--spring) backwards}.tl:not(:last-child):after{content:"";position:absolute;left:23px;top:28px;bottom:-10px;width:2px;background:var(--line)}.tl-dot{width:14px;height:14px;border-radius:50%;margin-top:4px;background:var(--ink-2);box-shadow:0 0 0 3px var(--surface);position:relative;z-index:1}.tl-accent .tl-dot{background:var(--accent)}.tl-good .tl-dot{background:var(--good)}.tl-line{font-size:15px;overflow-wrap:anywhere}.tl-meta{color:var(--ink-2);font-size:13px;margin-top:2px;overflow-wrap:anywhere}[data-design=sheet][data-page=activity] .timeline{padding:4px 0}[data-design=sheet][data-page=activity] .tl{grid-template-columns:14px minmax(0,1fr);gap:14px;padding:13px 18px;animation:svFade var(--dur-3) var(--sv-ease) backwards}[data-design=sheet][data-page=activity] .tl+.tl{border-top:1px solid var(--line)}[data-design=sheet][data-page=activity] .tl:not(:last-child):after{left:24px;top:32px;bottom:-14px;width:1.5px;background:var(--line-strong)}[data-design=sheet][data-page=activity] .tl-dot{width:13px;height:13px;margin-top:5px;background:var(--surface);border:1.5px solid var(--ink-2)}[data-design=sheet][data-page=activity] .tl-accent .tl-dot{width:11px;height:11px;margin:6px 1px 0;background:var(--sv-blue);border:0}[data-design=sheet][data-page=activity] .tl-good .tl-dot{border:0;background:var(--surface) var(--mark-good) center / contain no-repeat}[data-design=sheet][data-page=activity] .tl-line{font-size:16px;line-height:1.5}[data-design=sheet][data-page=activity] .tl-line b{font-weight:700}[data-design=sheet][data-page=activity] .tl-line .mono-soft{font-family:var(--mono);font-size:14px;color:var(--ink-2)}[data-design=sheet][data-page=activity] .tl-meta{font:400 14.5px/1.45 var(--font);color:var(--ink-2);margin-top:4px}@media(prefers-reduced-motion:reduce){[data-design=sheet][data-page=activity] .tl{animation:none}}
@@ -0,0 +1 @@
1
+ import{i as I}from"../chunks/chunk-S654BBEP.js";import{g as A}from"../chunks/chunk-CVB26NMC.js";import"../chunks/chunk-S2ZBMEVA.js";import"../chunks/chunk-ASYNLVOP.js";import{a as E}from"../chunks/chunk-6RLVD2DT.js";import"../chunks/chunk-45PGAQRO.js";import{d as $,g as P}from"../chunks/chunk-4WKQTWUC.js";import"../chunks/chunk-VQ5YXO54.js";import{b as C,c as p,e as g,f as q,g as z,h as F,i as c}from"../chunks/chunk-76WAVKUG.js";import"../chunks/chunk-VWKSMFWJ.js";import{I as u,L as w,O as S,P as h,R as D,a as y,n as k,q as m,w as t,z as x}from"../chunks/chunk-GT4EAKAO.js";import{d as b}from"../chunks/chunk-G7NGB5UX.js";b();var L={quarantine:"unquarantine",mute:"unmute",acknowledge:"reopen",snooze:"reopen",exclude:"include",accept:"reopen"},O=["unmute","unquarantine","reopen","include"],U=n=>n.verb==="set"?"changed":n.verb;function W(n,l){let s=n.kind==="setting"&&n.reason?.match(/^(.*) to (.*)$/),d=s?(l||[]).find(i=>i.key===n.id):null;if(!s||!d)return n.reason;let o=i=>{let r;try{r=JSON.parse(i)}catch{r=i}return I(r??d.default,d.kind,d.unit)};return`${o(s[1])} to ${o(s[2])}`}var N=n=>O.includes(n.verb)?"good":n.kind==="setting"?"neutral":"accent";function B({app:n}){let l=$({d:"decisions.json",s:"settings.json"});if(!l.ready)return t(D,{});let s=l.d?.state||{},d=l.s?.settings,o=(l.d?.log||[]).map(e=>{let a=s[`${e.kind}:${e.id}`],f=a&&a.verb===e.verb&&(!e.at||!a.at||a.at===e.at),v={...e,until:e.until??(f?a.until:null),reason:e.reason??(f?a.reason:null)};return{...v,reason:W(v,d)}}),i=Object.values(s).filter(P).sort((e,a)=>(e.until?0:1)-(a.until?0:1)||String(e.until||"").localeCompare(String(a.until||""))),r=e=>i.filter(a=>e.includes(a.verb)).length;return t(C,{title:"Activity",subtitle:"Decisions and settings changes made on GitHub or from this app, newest first.",children:[t(p,{title:"At a glance",description:"What still changes something today.",info:t(w,{info:{title:"Decisions",doc:"app.md#making-a-decision",body:t(y,{children:"A decision is in force until it ends or someone undoes it. Unmutes, reopens and confirmations only undo or rate one, so they are not counted. Git history of the data branch keeps the same record."})}}),children:t(F,{children:[t(c,{label:"Decisions in force",value:i.length}),t(c,{label:"Quarantined",value:r(["quarantine"])}),t(c,{label:"Muted or acknowledged",value:r(["mute","acknowledge"])}),t(c,{label:"Excluded pull requests",value:r(["exclude"])}),t(c,{label:"Settings changes",value:o.filter(e=>e.kind==="setting").length,note:"in the log"})]})}),t(p,{title:"In force",description:"Ending soon first. Undo one in a click; the workflow applies it and logs who and why.",action:t(u,{tone:i.length?"accent":"good",children:i.length?k(i.length,"decision"):"None"}),children:i.length&&n?t(q,{label:"Decisions in force",children:i.map(e=>t(z,{mono:!0,title:`${e.kind}:${e.id}`,leading:t(u,{tone:"accent",icon:"clock",children:e.verb}),subtitle:`${e.until?`until ${e.until}`:"no end date"}, by ${e.by}${e.reason?`: ${e.reason}`:""}`,trailing:L[e.verb]&&e.kind!=="rerun"?t(A,{app:n,kind:e.kind,id:e.id,verbs:[L[e.verb]],label:"Undo"}):null}))}):t(g,{children:t(h,{icon:"check",title:"Nothing is in force.",children:"Quarantines, mutes, snoozes and accepted risks show here with their end date."})})}),o.length?t(p,{title:"Timeline",description:`The newest ${Math.min(o.length,12)}.`,children:t(g,{pad:!1,children:t("ol",{class:"timeline",children:o.slice(0,12).map(e=>t("li",{class:`tl tl-${N(e)}`,children:[t("span",{class:"tl-dot","aria-hidden":"true"}),t("div",{class:"tl-body",children:[t("p",{class:"tl-line",children:[t("b",{children:e.by})," ",U(e)," ",t("span",{class:"mono-soft",children:[e.kind,":",e.id]}),e.until?` until ${e.until}`:""]}),t("p",{class:"tl-meta",children:[t("span",{title:e.at||"",children:m(e.at)})," \xB7 ",e.reason||"no reason given"," \xB7 from ",e.source]})]})]}))})})}):null,t(p,{title:"Log",description:"Every decision. Filter by what was done, on what kind of item, or by who.",children:t(E,{name:"activity",caption:"Decisions log",rows:o,columns:[{key:"at",label:"When",raw:e=>e.at||"",render:e=>t("span",{title:e.at||"",children:m(e.at)})},{key:"verb",label:"Did",filter:!0,render:e=>t(u,{tone:N(e),icon:null,children:e.verb})},{key:"kind",label:"On",primary:!0,filter:!0,render:e=>t("span",{class:"mono-soft",children:[e.kind,":",e.id]})},{key:"by",label:"Who",filter:!0},{key:"until",label:"Until",render:e=>e.until||"\u2013"},{key:"reason",label:"Why",render:e=>t("span",{class:"muted",children:e.reason||"\u2013"})},{key:"source",label:"Where"}],empty:t(g,{children:[t(h,{icon:"activity",title:"No decisions yet.",action:t(x,{variant:"tinted",href:"#/ci",children:"Open CI Health"}),children:"Type a command in a comment, or use the Decide buttons in this app; every decision lands here."}),t(S,{inline:!0,code:"/stackvitals mute test:<id> for 7d because <reason>"})]})})})]})}export{B as default};
@@ -0,0 +1 @@
1
+ .ag-hint{margin:0 0 12px;font-size:13.5px;color:var(--ink-2)}.ag-cards{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px;align-items:stretch}@media(max-width:1100px){.ag-cards{grid-template-columns:minmax(0,1fr)}}.ag-card{padding:0;display:flex;flex-direction:column;min-width:0;overflow:hidden}.ag-strip{display:flex;align-items:center;justify-content:space-between;gap:8px 12px;flex-wrap:wrap;padding:10px 16px;min-height:46px;background:var(--surface-2);border-bottom:1px solid var(--line)}.ag-strip h3{margin:0;font-size:16px;font-weight:750}.ag-file{font-size:13px;color:var(--ink-2);min-width:0;overflow-wrap:anywhere}.ag-body{padding:14px 16px 16px;display:flex;flex-direction:column;gap:14px;flex:1;min-width:0}.ag-steps{list-style:none;margin:0;padding:0;counter-reset:ag;display:flex;flex-direction:column;gap:0;font-size:14.5px}.ag-steps li{counter-increment:ag;position:relative;padding:7px 0 7px 36px;overflow-wrap:anywhere;min-width:0}.ag-steps li+li{border-top:1px solid var(--line)}.ag-steps li:before{content:counter(ag);position:absolute;left:0;top:6px;width:24px;height:24px;border-radius:50%;display:grid;place-items:center;background:var(--accent-soft);color:var(--accent);font:700 13px/1 var(--font)}.ag-steps q,.ag-q{quotes:"\201c" "\201d"}.ag-id{font-family:var(--mono);font-size:.92em;overflow-wrap:anywhere}.ag-code{min-width:0;margin-top:auto}.ag-code .code,.ag-code pre{margin:0}.ag-why{margin:0;font-size:14px;color:var(--ink-2)}.ag-tool-name{font-weight:700}@media(min-width:641px){.ag-tool-name{white-space:nowrap;overflow-wrap:normal}}.ag-q{color:var(--ink-2)}.ag-state{display:inline-flex;align-items:center;gap:7px;white-space:nowrap;font-weight:600}.ag-skill-name{display:flex;flex-direction:column;gap:2px}.ag-skill-name code{font-size:12.5px;color:var(--ink-2)}.section-body>.ag-code{margin-bottom:12px}.section-body>.ag-code pre code{white-space:pre}.ag-editor .ag-strip .seg{max-width:100%}.ag-facts{margin:0;display:grid;grid-template-columns:minmax(140px,220px) minmax(0,1fr)}.ag-facts>div{display:contents}.ag-facts dt,.ag-facts dd{margin:0;padding:10px 0;border-top:1px solid var(--line);font-size:14.5px}.ag-facts>div:first-child :is(dt,dd){border-top:0;padding-top:0}.ag-facts dt{font-weight:650;color:var(--ink-2);padding-right:16px}.ag-how-foot{margin-top:10px}@media(max-width:560px){.ag-facts{grid-template-columns:minmax(0,1fr)}.ag-facts dd{border-top:0;padding-top:0}}[data-design=sheet][data-page=agents] .ag-hint{font:400 15px/1.5 var(--font);color:var(--ink-2)}[data-design=sheet][data-page=agents] .ag-card{background:var(--surface);border:1.5px solid var(--frame)}[data-design=sheet][data-page=agents] .ag-strip{background:var(--strip);border-bottom:1px solid var(--line-strong)}[data-design=sheet][data-page=agents] .ag-strip h3{font-size:17px;font-weight:700}[data-design=sheet][data-page=agents] .ag-file{font-size:14px}[data-design=sheet][data-page=agents] .ag-steps{font-size:15.5px}[data-design=sheet][data-page=agents] .ag-steps li{padding-left:40px}[data-design=sheet][data-page=agents] .ag-steps li:before{width:26px;height:26px;top:5px;background:none;border:1.5px solid var(--ink);color:var(--ink);border-radius:50%!important;font:700 14px/1 var(--font)}[data-design=sheet][data-page=agents] .ag-id{border:0;background:none;padding:0;font-size:.9em}[data-design=sheet][data-page=agents] .ag-why{font-size:15px;padding:8px 12px;background:var(--surface-2);border-left:4px solid var(--line-strong)}[data-design=sheet][data-page=agents] .ag-tool-name{font-weight:400;font-size:14.5px}[data-design=sheet][data-page=agents] .ag-skill-name code{font-size:14px}[data-design=sheet][data-page=agents] .ag-skill-name b{font-weight:600}[data-design=sheet][data-page=agents] .table-wrap{border:1.5px solid var(--frame)}[data-design=sheet][data-page=agents] .ag-how{background:var(--surface);border:1.5px solid var(--frame)}[data-design=sheet][data-page=agents] .ag-facts dt{font-weight:600;color:var(--ink);font-size:15px}[data-design=sheet][data-page=agents] .ag-facts dd{font-size:15.5px}[data-design=sheet][data-page=agents] .ag-facts :is(dt,dd){border-top-color:var(--line)}
@@ -0,0 +1,2 @@
1
+ import{a as v}from"../chunks/chunk-6RLVD2DT.js";import{b as C,c as l,e as S}from"../chunks/chunk-76WAVKUG.js";import{a as m}from"../chunks/chunk-VWKSMFWJ.js";import{H as w,N as y,O as c,R as b,a as s,c as d,d as x,j as u,w as e,z as k}from"../chunks/chunk-GT4EAKAO.js";import{d as g}from"../chunks/chunk-G7NGB5UX.js";g();g();var I=e("a",{href:"https://docs.astral.sh/uv/",target:"_blank",rel:"noopener noreferrer",children:"uv"}),O={claude:{add:e(s,{children:"In a terminal, in your repo's folder, run the command below."}),load:e(s,{children:["Restart Claude Code; ",e("code",{class:"ag-id",children:"/mcp"})," lists ",e("b",{children:"stackvitals"}),"."]})},cursor:{add:e(s,{children:["Paste it into ",e("code",{class:"ag-id",children:".cursor/mcp.json"})," in the project, or ",e("code",{class:"ag-id",children:"~/.cursor/mcp.json"})," for every project."]}),load:e(s,{children:["Reload Cursor; its MCP settings list ",e("b",{children:"stackvitals"}),"."]})},vscode:{add:e(s,{children:["Paste it into ",e("code",{class:"ag-id",children:".vscode/mcp.json"})," in the workspace."]}),load:e(s,{children:["Open Copilot Chat in agent mode; its tools list ",e("b",{children:"stackvitals"}),"."]})},codex:{add:e(s,{children:["Add it to ",e("code",{class:"ag-id",children:"~/.codex/config.toml"}),"."]}),load:e(s,{children:"Start a new Codex session."})},desktop:{add:e(s,{children:["In Claude Desktop: Settings, Developer, Edit config. Paste it into ",e("code",{class:"ag-id",children:"claude_desktop_config.json"})," and save."]}),load:e(s,{children:"Restart the app."})},windsurf:{add:e(s,{children:["Add it to Cascade's MCP servers, in ",e("code",{class:"ag-id",children:"~/.codeium/windsurf/mcp_config.json"}),"."]}),load:e(s,{children:"Refresh Cascade's MCP servers."})}};function T({d:i,client:t,all:n}){let a=i.CLIENTS.find(p=>p.id===t),o=O[t];return e("article",{class:"ag-card card",children:[e("header",{class:"ag-strip",children:[e("h3",{children:a.label}),e("span",{class:"ag-file",children:a.file==="Terminal"?"One command":e("code",{class:"ag-id",children:a.file})})]}),e("div",{class:"ag-body",children:[e("ol",{class:"ag-steps",children:[e("li",{children:["Install ",I," once, if you don't have it."]}),e("li",{children:o.add}),e("li",{children:o.load}),e("li",{children:["Check it: ask ",e("q",{children:"Is test_upload a known flake in this repo?"})]})]}),e("div",{class:"ag-code",children:e(c,{code:i.config(t,n),title:a.file==="Terminal"?"Command":a.file})})]})]})}function z({d:i}){let t=i.GROUPS.flatMap(a=>a.tools.map(o=>({id:o.name,name:o.name,area:a.title,icon:a.icon,does:o.does,ask:o.ask,loads:i.CORE.includes(o.name)?"Core":"All tools"})));return e(v,{rows:t,columns:[{key:"name",label:"Tool",primary:!0,nowrap:!0,render:a=>e("code",{class:"ag-id ag-tool-name",children:a.name})},{key:"area",label:"Area",filter:!0,nowrap:!0},{key:"does",label:"What it answers"},{key:"ask",label:"Example question",render:a=>e("q",{class:"ag-q",children:a.ask})},{key:"loads",label:"Loaded with",filter:!0,render:a=>e("span",{class:"ag-state",children:[e("span",{class:"sv-mark "+(a.loads==="Core"?"sv-mark-accent":"sv-mark-neutral"),"aria-hidden":"true"}),a.loads]})}],name:"agent-tools",caption:"Tools your agent can use",limit:60})}function R({d:i,to:t}){let n=i.SKILLS.map(o=>({id:o.id,title:o.title,does:o.does,cmd:i.skillCommand(o.id,t)})),a=n.map(o=>o.cmd).join(` && \\
2
+ `);return e(s,{children:[e("div",{class:"ag-code",children:e(c,{code:a,title:`All ${n.length} skills, for ${t==="claude"?"Claude Code":"Codex"}`})}),e(v,{rows:n,name:"agent-skills",caption:"Skills",search:!1,columns:[{key:"title",label:"Skill",primary:!0,render:o=>e("span",{class:"ag-skill-name",children:[e("b",{children:o.title}),e("code",{class:"ag-id",children:o.id})]})},{key:"does",label:"When your agent uses it"},{key:"cmd",label:"Install one",nowrap:!0,render:o=>e(y,{text:o.cmd,small:!0,label:"Copy command"})}]})]})}function A({repo:i}){let[t,n]=d("vscode"),a={vscode:"VS Code",cursor:"Cursor",vscodium:"VSCodium"}[t];return e("article",{class:"ag-card card ag-editor",children:[e("header",{class:"ag-strip",children:[e("h3",{children:"Flaky-test marker"}),e(m,{label:"Editor",size:"sm",value:t,onChange:n,options:[{value:"vscode",label:"VS Code"},{value:"cursor",label:"Cursor"},{value:"vscodium",label:"VSCodium"}]})]}),e("div",{class:"ag-body",children:[e("p",{class:"ag-why",children:["From your machine: it installs into ",a," on your computer, so this page can't do it for you."]}),e("ol",{class:"ag-steps",children:[e("li",{children:"In a terminal, run the command below."}),e("li",{children:["Reload ",a,"."]}),e("li",{children:["Open a test file in ",i,"; known flaky tests are marked where they're defined."]}),e("li",{children:["To fetch the newest, run ",e("q",{children:"StackVitals: Refresh flaky tests"})," from the command palette."]})]}),e(c,{inline:!0,code:`uvx stackvitals editor ${t}`})]})]})}var D=[["Runs on","Your computer, started by your agent through uvx."],["Reads","The repo's stackvitals-data branch, or a local copy read from GitHub with your token."],["Sends to StackVitals","Nothing: there is no StackVitals server."],["Every answer says","When its numbers are from and where they came from."]];function V({app:i}){let[t,n]=d(null),[a,o]=d(u.get("agentTools","all")),[p,P]=d("claude");if(x(()=>{import("../chunks/agents-data-BAL6QAOF.js").then(n,()=>n(null))},[]),!t)return e(b,{});let f=t.GROUPS.reduce((r,h)=>r+h.tools.length,0),E=i?.repo||"your repo";return e(C,{title:"Agents",subtitle:"Give your coding agent this repo's history: known flakes, how changes get merged, who should review.",children:[e(l,{title:"Connect your agent",description:"One MCP server for every client. Your agent runs on your computer, so its config lives there: follow the steps on your agent's card and copy its config.",action:e(m,{label:"Tools in the config",size:"sm",value:a,onChange:r=>{o(r),u.set("agentTools",r)},options:[{value:"core",label:`Core (${t.CORE.length})`},{value:"all",label:`All tools (${f})`}]}),children:[e("p",{class:"ag-hint",children:a==="all"?`Every tool (${f}), as the skills and the MCP registry entry use.`:`The ${t.CORE.length} everyday tools keep the agent's context small; the rest load with --tools all.`}),e("div",{class:"ag-cards",children:t.CLIENTS.map(r=>e(T,{d:t,client:r.id,all:a==="all"}))})]}),e(l,{title:"What your agent can ask",description:`${f} tools, ${t.CORE.length} of them core. Each answers from kept history and comparisons across repos. Filter by area or by how it loads.`,children:e(z,{d:t})}),e(l,{title:"Skills",description:"Ready-made instructions that tell your agent when to ask StackVitals. They use all tools. Install all at once, or one by one.",action:e(m,{label:"Install skills for",size:"sm",value:p,onChange:P,options:[{value:"claude",label:"Claude Code"},{value:"codex",label:"Codex"}]}),children:e(R,{d:t,to:p})}),e(l,{title:"Editor",description:"Marks known flaky tests right in your test files, from the repo's stackvitals-data branch.",children:e(A,{repo:E})}),e(l,{title:"How it works",description:"What the server reads and where it runs.",children:e(S,{class:"ag-how",children:[e("dl",{class:"ag-facts",children:D.map(([r,h])=>e("div",{children:[e("dt",{children:r}),e("dd",{children:h})]}))}),e("div",{class:"ag-how-foot",children:e(k,{size:"sm",variant:"plain",href:w+"modules.md",external:!0,iconEnd:"external",children:"Learn more"})})]})})]})}export{V as default};
@@ -0,0 +1 @@
1
+ .tabs-row{display:flex;min-width:0}.tabs-row .seg{max-width:100%}.tab-panel{display:flex;flex-direction:column;gap:20px;animation:pageIn var(--dur-2) var(--spring) backwards;min-width:0}[data-page=ci] .section-body>.columns{row-gap:20px}[data-page=ci] .section-body>.ci-tbl,[data-page=ci] .section-body>.ci-panel{margin-top:8px}.ci-tbl{min-width:0}.ci-tbl-h{display:flex;gap:4px 12px;align-items:baseline;flex-wrap:wrap;padding:10px 14px;background:var(--surface);border:1px solid var(--line);border-bottom:0;border-radius:var(--r-lg) var(--r-lg) 0 0}.ci-tbl-h h3{margin:0;font-size:15.5px;font-weight:700}.ci-tbl-note{font-size:13.5px;color:var(--ink-2)}.ci-tbl-act{margin-left:auto}.ci-tbl>.table-wrap,.ci-tbl>.card{border-top-left-radius:0;border-top-right-radius:0}@media(min-width:641px){.ci-tbl .table :is(th,td)+:is(th,td){border-left:1px solid var(--line)}}.ci-panel{display:flex;flex-direction:column;min-width:0;background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden}.ci-panel-h{display:flex;gap:8px 14px;align-items:center;justify-content:space-between;flex-wrap:wrap;padding:10px 16px;background:var(--surface-2);border-bottom:1px solid var(--line)}.ci-panel-h h3{margin:0;font-size:15.5px;font-weight:700}.ci-panel-b{flex:1;display:flex;flex-direction:column;gap:12px;padding:14px 16px;min-width:0}.ci-bar{display:flex;gap:8px;align-items:center;flex-wrap:wrap;padding:10px 16px;border-top:1px solid var(--line);background:var(--surface-2)}.ci-pair{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:16px;align-items:stretch}@media(max-width:900px){.ci-pair{grid-template-columns:minmax(0,1fr)}}.ci-facts{display:grid;grid-template-columns:minmax(120px,max-content) minmax(0,1fr);gap:0;margin:0;font-size:14.5px}.ci-fact{display:contents}.ci-fact dt{padding:7px 16px 7px 0;font-weight:650;color:var(--ink-2)}.ci-fact dd{margin:0;padding:7px 0;min-width:0;overflow-wrap:anywhere}.ci-fact+.ci-fact>*{border-top:1px solid var(--line)}@media(max-width:520px){.ci-facts{grid-template-columns:minmax(0,1fr)}.ci-fact dt{padding:8px 0 0}.ci-fact dd{padding:2px 0 8px}.ci-fact+.ci-fact>dd{border-top:0}}.ci-say{display:flex;gap:8px;align-items:baseline;margin:0;font-size:14.5px;color:var(--ink-2)}.ci-cap{display:inline-block}.ci-cap:first-letter{text-transform:uppercase}.ci-id{font-family:var(--mono);font-size:.9em;overflow-wrap:anywhere}.ci-dec{display:flex;gap:6px;align-items:baseline;margin-top:3px;font-size:13.5px;color:var(--ink-2)}.ci-cell-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:3px}.ci-landed{margin:0;padding:0;list-style:none;display:flex;flex-wrap:wrap;gap:4px 14px}.ci-cmds{display:flex;flex-direction:column;gap:8px}.ci-setup{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.1fr);background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg)}.ci-setup-strip{grid-column:1 / -1;display:flex;justify-content:space-between;gap:10px;flex-wrap:wrap;padding:12px 18px;border-bottom:1px solid var(--line);font-weight:700}.ci-setup-strip span+span{font-weight:600;color:var(--ink-2);font-size:14px;overflow-wrap:anywhere}.ci-setup-form{padding:18px;min-width:0}.ci-setup-out{display:flex;flex-direction:column;gap:16px;padding:18px;min-width:0;border-left:1px solid var(--line)}.ci-setup-h{margin:0;font-weight:700;font-size:15px}.ci-feats{margin:0;padding:0;border:0;display:flex;flex-direction:column;gap:0;min-width:0}.ci-feats legend{padding:0;margin-bottom:6px}.ci-feat{display:grid;grid-template-columns:auto minmax(0,1fr);gap:12px;align-items:start;padding:10px 0;border-top:1px solid var(--line)}.ci-feat-text{display:flex;flex-direction:column;gap:2px;min-width:0}.ci-feat-text b{font-size:15px;font-weight:700}.ci-feat-text span{font-size:14px;color:var(--ink)}.ci-feat-text small{font-size:13.5px;color:var(--ink-2)}.ci-step{margin:6px 0 0}.ci-steppers{display:flex;flex-wrap:wrap;gap:14px 28px}.ci-steppers .field{flex:1 1 200px}.ci-jobs-now ul{margin:6px 0 0;padding:0;list-style:none;display:flex;flex-direction:column;gap:4px;font-size:14px}.ci-jobs-now code{overflow-wrap:anywhere}.ci-add{display:flex;flex-direction:column;gap:12px}.ci-add-paths{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:12px}.ci-add-path{display:flex;flex-direction:column;align-items:flex-start;gap:8px;padding:14px;border:1px solid var(--line);border-radius:var(--r-md);min-width:0}.ci-add-k{margin:0;font-size:13px;font-weight:700;color:var(--ink-2)}.ci-add-note{margin:0;font-size:13.5px;color:var(--ink-2)}.ci-add-more{display:flex;gap:10px 14px;align-items:center;flex-wrap:wrap}.ci-add-save{font-size:13.5px;color:var(--ink-2);overflow-wrap:anywhere}.ci-token{padding:14px;border:1px solid var(--line);border-radius:var(--r-md);background:var(--surface-2);display:flex;flex-direction:column;gap:10px;font-size:14px}.ci-token p{margin:0}.ci-yaml summary,.ci-manual summary{cursor:pointer;font-weight:650;font-size:14.5px;padding:6px 0}.ci-yaml .code pre{max-height:420px;overflow:auto}.ci-todo{margin:0;padding:12px 16px 12px 32px;display:flex;flex-direction:column;gap:8px;font-size:14px;border:1px solid var(--line);border-top:0;border-radius:0 0 var(--r-lg) var(--r-lg);background:var(--surface)}.ci-manual-list{display:grid;grid-template-columns:minmax(0,1fr);gap:12px;margin-top:8px}.ci-manual-list h4{margin:0 0 4px;font-size:14.5px;font-weight:700}.ci-manual .cmd code{overflow-wrap:anywhere}@media(max-width:1000px){.ci-setup{grid-template-columns:minmax(0,1fr)}.ci-setup-out{border-left:0;border-top:1px solid var(--line)}}@media(max-width:560px){.ci-add-paths{grid-template-columns:minmax(0,1fr)}.ci-setup-form,.ci-setup-out{padding:16px 14px}}[data-design=sheet][data-page=ci] .tab-panel{animation-name:svFade;animation-timing-function:var(--sv-ease);gap:24px}[data-design=sheet][data-page=ci] .ci-tbl-h{background:var(--strip);border:1.5px solid var(--frame);border-bottom:1px solid var(--line-strong);min-height:42px;padding:9px 14px}[data-design=sheet][data-page=ci] .ci-tbl-h h3{font-size:16px;font-weight:700}[data-design=sheet][data-page=ci] .ci-tbl-note{font-size:14px}[data-design=sheet][data-page=ci] .ci-tbl>:is(.table-wrap,.card){border:1.5px solid var(--frame);border-top:0}@media(min-width:641px){[data-design=sheet][data-page=ci] .ci-tbl .table :is(th,td)+:is(th,td){border-left:1px solid var(--line-strong)}[data-design=sheet][data-page=ci] .ci-tbl .table td{padding:10px 12px}[data-design=sheet][data-page=ci] .ci-tbl .th-btn{padding:9px 12px}[data-design=sheet][data-page=ci] .ci-tbl .table td{overflow-wrap:break-word}[data-design=sheet][data-page=ci] .ci-tbl .table td .ci-id{overflow-wrap:anywhere}[data-design=sheet][data-page=ci] .ci-tbl .table td .btn{white-space:nowrap}}[data-design=sheet][data-page=ci] .ci-panel{border:1.5px solid var(--frame)}[data-design=sheet][data-page=ci] .ci-panel-h{min-height:42px;padding:9px 14px;background:var(--strip);border-bottom:1px solid var(--line-strong)}[data-design=sheet][data-page=ci] .ci-panel-h h3{font-size:16px}[data-design=sheet][data-page=ci] .ci-panel-b{padding:14px}[data-design=sheet][data-page=ci] .ci-bar{padding:10px 14px;background:var(--strip);border-top:1px solid var(--line-strong)}[data-design=sheet][data-page=ci] .ci-facts{font-size:15px}[data-design=sheet][data-page=ci] .ci-fact dt{font-size:14px;font-weight:600}[data-design=sheet][data-page=ci] .ci-fact+.ci-fact>*{border-top-color:var(--line-strong)}[data-design=sheet][data-page=ci] .ci-say{font-size:15px}[data-design=sheet][data-page=ci] .ci-id{font:400 14px/1.45 var(--mono);color:var(--sv-blue-ink)}[data-design=sheet][data-page=ci] span.ci-id{color:var(--ink)}[data-design=sheet][data-page=ci] .ci-dec,[data-design=sheet][data-page=ci] .ci-cell-list{font-size:14px}[data-design=sheet][data-page=ci] .ci-landed a{display:inline-block;background:var(--surface-2);border:1px solid var(--line-strong);padding:2px 8px;font:400 14px var(--mono);text-decoration:none;color:var(--accent)}[data-design=sheet][data-page=ci] .ci-landed a:hover{border-color:var(--sv-blue);background:var(--accent-soft)}[data-design=sheet][data-page=ci] .page:has(.ci-glance) .large-title{font:400 clamp(22px,2.6vw,32px)/1.25 var(--mono);letter-spacing:0;overflow-wrap:anywhere;max-width:100%}[data-design=sheet][data-page=ci] .ci-glance .stat-value{font-size:28px;overflow-wrap:anywhere}[data-design=sheet][data-page=ci] .ci-glance .stat-value .pill{font-family:var(--font);font-size:16px}[data-design=sheet][data-page=ci] .ci-setup{border:1.5px solid var(--frame)}[data-design=sheet][data-page=ci] .ci-setup-strip{min-height:42px;padding:9px 14px;background:var(--strip);border-bottom:1px solid var(--line-strong);font-size:15.5px}[data-design=sheet][data-page=ci] .ci-setup-strip span+span{font-size:15px}[data-design=sheet][data-page=ci] .ci-setup-out{border-left:1px solid var(--line-strong)}[data-design=sheet][data-page=ci] .ci-setup-h,[data-design=sheet][data-page=ci] .ci-add-k{font-size:14px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:var(--ink-2)}[data-design=sheet][data-page=ci] .ci-feat{border-top-color:var(--line-strong);padding:12px 0}[data-design=sheet][data-page=ci] .ci-feat-text b{font-size:15.5px}[data-design=sheet][data-page=ci] .ci-feat-text span{font-size:15px}[data-design=sheet][data-page=ci] .ci-feat-text small{font-size:14px}[data-design=sheet][data-page=ci] .ci-add-path{border:1px solid var(--line-strong)}[data-design=sheet][data-page=ci] .ci-add-note,[data-design=sheet][data-page=ci] .ci-add-save{font-size:14px}[data-design=sheet][data-page=ci] .ci-token{border-color:var(--line-strong);font-size:15px}[data-design=sheet][data-page=ci] .ci-yaml summary,[data-design=sheet][data-page=ci] .ci-manual summary{font-weight:600;font-size:15px}[data-design=sheet][data-page=ci] .ci-todo{border:1.5px solid var(--frame);border-top:0;font-size:15px}[data-design=sheet][data-page=ci] .ci-manual-list h4{font-size:15px}@media(max-width:1000px){[data-design=sheet][data-page=ci] .ci-setup-out{border-left:0;border-top:1px solid var(--line-strong)}}.ci-add-path .btn{max-width:100%;white-space:normal;text-align:left}#ci-setup{scroll-margin-top:calc(var(--top-h, 56px) + 16px)}
@@ -0,0 +1 @@
1
+ import{a as g,b as V,c as X,d as be,e as ye,f as we}from"../chunks/chunk-CTKDYZMG.js";import{a as de}from"../chunks/chunk-YTANGYYH.js";import{a as R,b as ge}from"../chunks/chunk-4LUI7O5P.js";import{g as Q,h as fe}from"../chunks/chunk-CVB26NMC.js";import"../chunks/chunk-S2ZBMEVA.js";import"../chunks/chunk-ASYNLVOP.js";import{a as he}from"../chunks/chunk-IYMBFPCV.js";import{c as le,d as oe}from"../chunks/chunk-763NH7PP.js";import{a as O,b as G,c as pe,d as ce,e as ue}from"../chunks/chunk-3G4SQNUC.js";import{a as H}from"../chunks/chunk-LAAGCPU3.js";import"../chunks/chunk-24LFKFZ6.js";import"../chunks/chunk-6BDBXA6V.js";import"../chunks/chunk-MAPYGK3P.js";import{a as m}from"../chunks/chunk-6RLVD2DT.js";import{a as me}from"../chunks/chunk-45PGAQRO.js";import{d as ie,g as W}from"../chunks/chunk-4WKQTWUC.js";import"../chunks/chunk-VQ5YXO54.js";import{b as P,c as _,d as U,e as C,h as D,i as o}from"../chunks/chunk-76WAVKUG.js";import{a as re}from"../chunks/chunk-VWKSMFWJ.js";import{I,J as M,N as ae,P as w,Q as ne,R as se,a as h,c as J,d as q,l as y,m as b,o as x,q as A,r as S,s as Z,w as e,x as ee,y as te,z as v}from"../chunks/chunk-GT4EAKAO.js";import{d as Y}from"../chunks/chunk-G7NGB5UX.js";Y();var _e=(s,a)=>`https://github.com/${s.repo}${a}`,z=s=>s.charAt(0).toUpperCase()+s.slice(1),$e=s=>s?`${z(s.verb)} by ${s.by}${s.until?` until ${s.until}`:""}${s.reason?`: ${s.reason}`:""}`:null;function Se(s,a){let i=a?.state||{};if(!s||!Object.keys(i).length)return s;let l=(n,d)=>W(i[n])?i[n]:d&&W(d)?d:null;return{...s,tests:(s.tests||[]).map(n=>({...n,decision:l(`test:${n.id}`,n.decision)})),jobs:(s.jobs||[]).map(n=>({...n,decision:l(`job:${R(n)}`,n.decision)}))}}var ke=null;function L({name:s,...a}){let[i,l]=J(null);if(q(()=>{(ke||(ke=import("../chunks/more-KMJMJQD3.js"))).then(l)},[]),!i)return s==="Signals"||s==="Causes"?null:e(ne,{lines:3});let n=i[s];return e(n,{...a})}var Ce={timing:"a wait or timeout ran out",network:"a host didn't answer",infrastructure:"the runner itself",concurrency:"a race between threads","async wait":"checked before it was ready","shared state":"left over from another test",randomness:"a random seed",ordering:"an order that isn't guaranteed",time:"time zones or the clock","resource leak":"files or sockets left open",io:"a file on disk","floating point":"numeric rounding"},xe={broken:0,flaky:1,suspect:2,fixed:3,ok:4};async function Re(s,a){let i=a.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(-60).replace(/^-+|-+$/g,"");try{let l=await crypto.subtle.digest("SHA-1",new TextEncoder().encode(a)),n=[...new Uint8Array(l)].map(d=>d.toString(16).padStart(2,"0")).join("").slice(0,8);return`history/${s}-${i}-${n}.html`}catch{return null}}function je({app:s,ci:a,kind:i,id:l}){let n=i==="test"?(a.tests||[]).find(c=>c.id===l):(a.jobs||[]).find(c=>R(c)===l),[d,j]=J(null),[r,k]=J(!1);if(q(()=>{n&&Re(i,i==="test"?n.id:`${n.workflow}|${n.job}`).then(j)},[l]),!n)return e(P,{title:"Not found",subtitle:`No ${i} called ${l} in the last ${a.window_days} days.`,children:e(w,{icon:"search",title:"Nothing by that name",action:e(v,{variant:"filled",href:"#/ci",children:"Back to CI Health"})})});let p=n,$=me(i,i==="test"?n.id:l),T=(n.evidence||[]).map(c=>({...c,url:_e(s,`/actions/runs/${c.run}/attempts/${c.att}`)})),u=n.decision,F=[!!n.monitors?.length&&["Monitors",z(n.monitors.join(", ").replace(/_/g," "))],!!n.labels?.length&&["Labels",z(n.labels.join(", ").replace(/_/g," "))],!!n.message&&["Fails with",e("code",{class:"code-inline",children:n.message})],!!n.top_step&&["Most failing step",n.top_step],p.flake_steps?.length>1&&["Steps it failed in",p.flake_steps.join(", ")],!!p.flake&&["Looks like",e(h,{children:[z(p.flake)," ",e("span",{class:"muted",children:["(",Ce[p.flake]||"from its usual error",")"]})]})],!!n.owners?.length&&["Owners",n.owners.join(", ")],!!n.workflow&&["Workflow",e("span",{class:"mono-soft",children:n.workflow})]];return e(P,{title:l,subtitle:`${i==="test"?"Test":"Job"} in ${n.workflow||"?"}, last ${a.window_days} days.`,toolbar:e(h,{children:[e(v,{variant:"plain",icon:"left",href:"#/ci",children:"CI Health"}),d&&n.state!=="ok"?e(v,{variant:"tinted",href:d,iconEnd:"external",children:"Week by week"}):null]}),children:[e(_,{title:"At a glance",class:"ci-glance",children:e(D,{children:[e(o,{label:"State",value:e(M,{value:n.state})}),n.state==="broken"?e(o,{label:"Failed",value:`${n.failures??"\u2013"} of ${n.runs}`,note:"runs, the same code failing again"}):null,n.flake_events?e(h,{children:[e(o,{label:"Flaked",value:`${n.flake_events} of ${n.runs}`,note:"runs"}),e(o,{label:"Flake rate",value:b(n.flake_rate),note:`likely ${b(n.rate_low)} to ${b(n.rate_high)}`}),e(o,{label:"Last flake",value:x(n.last_flake),note:A(n.last_flake)}),Object.keys(n.variants||{}).length?e(o,{label:"Flakes came from",value:Object.keys(n.variants||{})[0],note:Object.entries(n.variants||{}).map(([c,B])=>`${c}: ${B}`).join(", ")}):null]}):null,n.since?e(o,{label:"In this state since",value:x(n.since)}):null,p.muted_until?e(o,{label:"No alerts until",value:p.muted_until>="9999"?"reopened":x(p.muted_until),note:"still measured"}):null,n.retry_minutes!==void 0?e(o,{label:"Lost to re-runs",value:`${Math.round(n.retry_minutes)} min`}):null,n.prs_affected?.length?e(o,{label:"Pull requests hit",value:n.prs_affected.length}):null]})}),e(_,{title:"Why it is marked and what to do",description:"What marks it, then the decision on it; the workflow applies a decision and logs who, when and why.",children:e("div",{class:"ci-pair",children:[e(V,{title:"Why it is marked",state:e(M,{value:n.state}),children:F.some(Boolean)?e(X,{rows:F}):e("p",{class:"ci-say",children:[e("span",{class:"sv-mark sv-mark-good","aria-hidden":"true"}),"Nothing marks it right now."]})}),e(V,{title:"Decision",state:u?e(I,{tone:"accent",icon:"check",children:z(u.verb)}):e(I,{tone:"neutral",icon:null,children:"None yet"}),bar:e(h,{children:[e(Q,{app:s,kind:i,id:i==="test"?n.id:l,label:"Make a decision",verbs:i==="test"?["quarantine","mute","acknowledge","unquarantine","unmute","reopen","confirm","dispute"]:["mute","acknowledge","unmute","reopen","confirm","dispute"]}),e(v,{size:"sm",variant:"plain",expanded:r,onClick:()=>k(!r),iconEnd:r?"up":"down",children:"As a comment"})]}),children:[u?e(X,{rows:[["What",z(u.verb)],["Who",u.by||"\u2013"],["Until",u.until?x(u.until):"Reopened by hand"],["Why",u.reason||"No reason given"],!!u.at&&["Decided",x(u.at)]]}):e("p",{class:"ci-say",children:"Quarantine it so it can't fail builds, mute its alerts for a while, or acknowledge it for good."}),r?e("div",{class:"ci-cmds",children:[e("p",{class:"muted small",children:"Type one of these in any issue or pull request comment:"}),e(fe,{list:[...i==="test"?[{text:`/stackvitals quarantine ${$} for 14d because `,note:"Your test command passes while only quarantined tests fail."}]:[],{text:`/stackvitals mute ${$} for 7d because `,note:"Still measured; no alert and no new issue."},{text:`/stackvitals acknowledge ${$} because `,note:"The team lives with it until someone reopens it."},{text:`/stackvitals dispute ${$} because `,note:"StackVitals called this wrong: it counts against its accuracy here and changes nothing else."}]})]}):null]})]})}),p.began?e(_,{title:"When it began",description:"Runs before its first flake against runs since, so you can tell whether a change started it.",children:e(L,{name:"Began",b:p.began,app:s})}):null,e(_,{title:"Evidence",description:"Each run where it failed and then passed, or flipped on the same code.",children:e(g,{title:"Evidence runs",note:`${T.length} run${T.length===1?"":"s"}`,children:e(m,{name:"evidence",caption:"Evidence runs",rows:T,columns:[{key:"run",label:"Run",primary:!0,render:c=>e("a",{href:te(c.url),target:"_blank",rel:"noopener noreferrer",children:c.run})},{key:"att",label:"Attempt",num:!0},{key:"kind",label:"What happened",raw:c=>c.kind==="retry"?"Failed, then passed on a re-run":c.kind==="flip"?"Passed and failed on the same code":c.kind}],empty:e(C,{children:e(w,{icon:"search",title:"No runs kept for this one."})})})})})]})}function Fe({app:s,cov:a,days:i}){let[l,n]=O();if(!a)return e(C,{children:e(w,{icon:"ci",title:"No coverage reports found.",children:"Upload Cobertura (coverage.xml), LCOV (lcov.info) or JaCoCo XML in an artifact whose name says coverage; each run's total and each pull request's patch coverage show here."})});let d=a.latest,j={id:"coverage",name:"Line coverage",sub:"Default branch, daily",kind:"share",better:"up",period:"day",points:(a.trend||[]).map(([r,k])=>({label:Z(r),value:k})),meaning:r=>`${b(r)} of lines ran in the default branch's tests.`};return e(h,{children:[e(D,{children:[e(o,{label:"Line coverage, default branch",value:d?b(d.pct):"\u2013",note:d?`${d.covered} of ${d.lines} lines, ${x(d.at)}`:"no default-branch report yet"}),e(o,{label:`Change, last ${i} days`,value:a.change===null||a.change===void 0?"\u2013":`${a.change>=0?"+":""}${(a.change*100).toFixed(1)} pts`}),e(o,{label:"Pull requests measured",value:(a.prs||[]).length,note:"patch coverage: added lines that ran"})]}),j.points.length?e(C,{children:e(G,{metric:j,ranges:l,setRange:n,height:240})}):null,e(g,{title:"Patch coverage by pull request",note:"Added lines that ran in the pull request's tests.",children:e(m,{name:"patch-coverage",caption:"Patch coverage by pull request",rows:a.prs||[],columns:[{key:"pr",label:"Pull request",primary:!0,render:r=>e("a",{href:_e(s,`/pull/${r.pr}`),target:"_blank",rel:"noopener noreferrer",children:["#",r.pr]})},{key:"pct",label:"Added lines that ran",num:!0,render:r=>r.lines?b(r.pct):"\u2013"},{key:"lines",label:"Lines",num:!0,render:r=>r.lines?`${r.covered} of ${r.lines}`:"no code lines"},{key:"missed",label:"Not run",raw:r=>Object.keys(r.missed||{}).length,render:r=>Object.keys(r.missed||{}).length?e("ul",{class:"ci-cell-list",children:Object.entries(r.missed||{}).slice(0,3).map(([k,p])=>e("li",{children:[e("span",{class:"mono-soft",children:S(k,60)})," ",e("span",{class:"muted",children:["lines ",p.slice(0,8).join(", "),p.length>8?"\u2026":""]})]}))}):"\u2013"},{key:"at",label:"Measured",render:r=>A(r.at)}],empty:e(C,{children:e(w,{icon:"prs",title:"No pull request has a coverage report yet."})})})})]})}var ve=["flaky","groups","all","slow","inventory","coverage"];function ze({app:s,route:a}){de();let i=ie({ci:"data.json",dec:"decisions.json"}),[l,n]=O(),[d,j]=J(null);if(!s)return e(P,{title:"CI Health",subtitle:"Flaky and failing tests and jobs.",children:e(w,{icon:"setup",title:"No StackVitals data here yet",action:e(v,{variant:"filled",href:"#/setup",children:"Set it up"})})});if(!i.ready)return e(se,{});let r=Se(i.ci,i.dec);return r?a.rest[0]==="test"||a.rest[0]==="job"?e(je,{app:s,ci:r,kind:a.rest[0],id:a.rest.slice(1).join("/")}):e(Ie,{app:s,ci:r,route:a,ranges:l,setRange:n,pick:d,setPick:j}):e(P,{title:"CI Health",subtitle:"Flaky and failing tests and jobs, first-try green and time to green.",children:e(w,{icon:"ci",title:"CI Health hasn't run here yet.",action:e(v,{variant:"filled",href:"#/settings",icon:"settings",children:"Turn it on"}),children:["Add ",e("code",{children:"ci"})," to the workflow's modules; the next run fills this page."]})})}var Te=s=>e(h,{children:[e("a",{href:s.href,class:"ci-id",children:S(s.name,90)}),s.decision?e("div",{class:"ci-dec",children:[e("span",{class:"sv-mark sv-mark-accent","aria-hidden":"true"}),$e(s.decision)]}):null]});function Ie({app:s,ci:a,route:i,ranges:l,setRange:n,pick:d,setPick:j}){let r=ve.includes(i.query.get("tab")||"")?i.query.get("tab"):"flaky",k=[...(a.tests||[]).map(t=>({...t,kind:"test",name:t.id,href:`#/ci/test/${encodeURIComponent(t.id)}`})),...(a.jobs||[]).map(t=>({...t,kind:"job",name:R(t),href:`#/ci/job/${encodeURIComponent(R(t))}`}))],p=k.filter(t=>t.state!=="ok"&&t.state!==void 0).sort((t,f)=>(xe[t.state]??9)-(xe[f.state]??9)||(f.flake_events||0)-(t.flake_events||0)),$=ge(a).filter(t=>t.points.length),T=$.find(t=>t.id===d)||$[0],u=Object.entries(a.minutes||{}),F=a.quarantine_expired||[],c=[{key:"state",label:"State",filter:!0,render:t=>e(M,{value:t.state})},{key:"kind",label:"Kind",filter:!0,render:t=>z(t.kind)},{key:"name",label:"Name",primary:!0,render:Te},{key:"flake_events",label:"Flakes",num:!0},{key:"flake",label:"Likely cause",filter:!0,raw:t=>t.flake||"",render:t=>t.flake?e("span",{title:Ce[t.flake],children:z(t.flake)}):"\u2013"},{key:"runs",label:"Runs",num:!0},{key:"flake_rate",label:"Rate",num:!0,render:t=>b(t.flake_rate)},{key:"last_flake",label:"Last seen",raw:t=>t.last_flake||"",render:t=>t.last_flake?A(t.last_flake):"\u2013"}],B={key:"decide",label:"Action",nowrap:!0,raw:()=>"",render:t=>(t.state==="flaky"||t.state==="broken")&&!t.decision?e(Q,{app:s,kind:t.kind,id:t.name,verbs:t.kind==="test"?["quarantine","mute","acknowledge"]:["mute","acknowledge"]}):e(v,{size:"sm",variant:"plain",href:t.href,iconEnd:"right",children:"Open"})},N={flaky:p.length,groups:(a.failure_groups||[]).length,all:k.length,slow:(a.slow_tests||[]).length,inventory:(a.new_tests||[]).length+(a.skipped_tests||[]).length,coverage:null},K={flaky:"Flaky and failing",groups:"Failure groups",all:"Everything",slow:"Slowest",inventory:"New and skipped",coverage:"Coverage"};return e(P,{title:"CI Health",subtitle:`Flaky and failing tests and jobs over the last ${a.window_days} days, from ${(a.runs??0).toLocaleString("en-US")} runs. A flake failed and then passed with no code change.`,toolbar:e(h,{children:[s.modules?.ci?.page?e(v,{variant:"plain",href:s.modules.ci.page,iconEnd:"external",children:"Printable page"}):null,e(v,{variant:"tinted",onClick:()=>we("ci-setup"),iconEnd:"down",children:"Set up in your CI"})]}),children:[F.length?e("div",{class:"note note-watch",children:[e(ee,{name:"warn"}),e("span",{children:[F.length," flaky test",F.length===1?"":"s"," stayed in quarantine past the limit you set and fail builds again until fixed: ",F.slice(0,5).join(", "),F.length>5?" and more":"","."]})]}):null,T?e(_,{title:"Trends",description:"Week by week. Drag across the chart, or focus it and use the arrow keys.",children:e(U,{ratio:"lead",children:[e(C,{children:e(G,{metric:T,ranges:l,setRange:n})}),e(ce,{label:"CI metrics",items:$.map(t=>pe(t,l[t.period]||12)),selected:T.id,onSelect:j,tones:ue($,l)})]})}):null,e(_,{title:"Key numbers",description:`Last ${a.window_days} days, and the runs left out of the flake counts.`,children:[e(D,{children:[e(o,{label:"Green on the first try",value:b(a.pushes?.first_try_share),note:a.pushes?e(h,{children:[`${a.pushes.pushes} pushes${a.pushes.first_try_range?`, likely ${b(a.pushes.first_try_range[0])} to ${b(a.pushes.first_try_range[1])}`:""}`,e("br",{}),e(he,{kind:"not_first_try",ev:a.pushes,repo:s?.repo})]}):null}),e(o,{label:"Push to all checks done",value:y(a.pushes?.median_s),note:a.pushes?`${a.pushes.median_range_s?`likely ${y(a.pushes.median_range_s[0])} to ${y(a.pushes.median_range_s[1])}; `:""}90th percentile ${y(a.pushes.p90_s)}`:null}),e(o,{label:"Runs re-run",value:b(a.rerun_share)}),e(o,{label:"Queue wait",value:y(a.queue?.median_s),note:a.queue?`95th percentile ${y(a.queue.p95_s)}`:null}),e(o,{label:"Runner minutes",value:u.reduce((t,[,f])=>t+f,0).toLocaleString("en-US"),note:`${a.window_days} days${u.filter(([,t])=>t).length?`: ${u.filter(([,t])=>t).map(([t,f])=>`${t} ${f}`).join(", ")}`:""}`})]}),e(L,{name:"Signals",ci:a})]}),a.changes||a.verdict_feedback?e(_,{title:"This week",description:"Against the four weeks before, and how often your team agreed with the calls.",children:[a.changes?e(be,{changes:a.changes}):null,a.verdict_feedback?e(ye,{fb:a.verdict_feedback}):null]}):null,e(_,{title:"Tests and jobs",description:p.some(t=>t.state==="flaky")?`Decide on a flaky item here, from its page, or with /stackvitals in any comment. ${p.filter(t=>t.decision).length} decided.`:"Everything CI ran, grouped the ways you'll look for it.",children:[e("div",{class:"tabs-row",children:e(re,{label:"Show",scroll:!0,value:r,onChange:t=>le(oe(i,"tab",t==="flaky"?null:t)),options:ve.map(t=>({value:t,label:e(h,{children:[K[t],N[t]!==null?e("span",{class:"seg-count num",children:N[t]}):null]}),aria:`${K[t]}${N[t]!==null?`, ${N[t]}`:""}`}))})}),e("div",{class:"tab-panel",children:[r==="flaky"?e(h,{children:[e(g,{title:"Flaky and failing",note:"Failing first, then by flakes.",children:e(m,{name:"flaky",caption:"Flaky and failing",rows:p,columns:[...c,B],empty:e(C,{children:e(w,{icon:"check",title:"Nothing is flaky or failing.",children:a.has_test_reports?"":"Upload test reports (JUnit, CTRF, Jest, Vitest, Go, TRX) as artifacts to see single tests too."})})})}),e(L,{name:"Causes",rows:p})]}):null,r==="all"?e(g,{title:"Every test and job",note:`${k.length} in the last ${a.window_days} days`,children:e(m,{name:"ci-items",caption:"Every test and job",rows:k,columns:c})}):null,r==="groups"?e(g,{title:"Failure groups",note:"Failures with the same error and code location.",children:e(m,{name:"failure-groups",caption:"Failure groups",rows:a.failure_groups||[],columns:[{key:"type",label:"Error",primary:!0},{key:"where",label:"Where",render:t=>e("span",{class:"mono-soft",children:S(t.where,60)})},{key:"message",label:"Message",render:t=>S(t.message,100)},{key:"tests",label:"Tests",num:!0},{key:"failures",label:"Failures",num:!0},{key:"last",label:"Last",render:t=>x(t.last)},{key:"top_tests",label:"Tests it hits most",raw:t=>(t.top_tests||[]).length,render:t=>e("ul",{class:"ci-cell-list",children:(t.top_tests||[]).slice(0,3).map(f=>e("li",{children:e("a",{class:"mono-soft",href:`#/ci/test/${encodeURIComponent(f)}`,children:S(f,50)})}))})},{key:"prompt",label:"For an AI agent",raw:()=>"",render:t=>t.prompt?e(ae,{text:t.prompt,label:"Copy prompt",small:!0}):"\u2013"}],empty:e(C,{children:e(w,{icon:"check",title:"No failures share a cause.",children:"Failures with the same root cause and code location are grouped here."})})})}):null,r==="slow"?e(h,{children:[e(g,{title:"Slowest tests",note:"Median and 95th percentile time per test.",children:e(m,{name:"slow-tests",caption:"Slowest tests",rows:a.slow_tests||[],initialSort:{key:"median_ms",desc:!0},columns:[{key:"id",label:"Test",primary:!0,render:t=>e("a",{class:"ci-id",href:`#/ci/test/${encodeURIComponent(t.id)}`,children:S(t.id,70)})},{key:"median_ms",label:"Median",num:!0,render:t=>y(t.median_ms/1e3)},{key:"p95_ms",label:"95th percentile",num:!0,render:t=>y(t.p95_ms/1e3)},{key:"slower",label:"This week",raw:t=>t.slower?.pct??null,render:t=>t.slower?e(I,{tone:"watch",children:[t.slower.pct,"% slower"]}):"\u2013"},{key:"trend",label:"Daily median",raw:()=>"",render:t=>e(H,{values:(t.trend||[]).map(f=>f[1]),label:`Daily median time of ${t.id}`})},{key:"samples",label:"Samples",num:!0}]})}),e(g,{title:"Slowest jobs",children:e(m,{name:"slow-jobs",caption:"Slowest jobs",rows:(a.jobs||[]).filter(t=>t.median_s),initialSort:{key:"median_s",desc:!0},columns:[{key:"job",label:"Job",primary:!0,raw:t=>R(t),render:t=>e("a",{class:"ci-id",href:`#/ci/job/${encodeURIComponent(R(t))}`,children:R(t)})},{key:"median_s",label:"Median",num:!0,render:t=>y(t.median_s)},{key:"p95_s",label:"95th percentile",num:!0,render:t=>y(t.p95_s)},{key:"slower",label:"This week",raw:t=>t.slower?.pct??null,render:t=>t.slower?e(I,{tone:"watch",children:[t.slower.pct,"% slower"]}):"\u2013"},{key:"trend",label:"Daily median",raw:()=>"",render:t=>e(H,{values:(t.trend||[]).map(f=>f[1]),label:`Daily median time of ${R(t)}`})}]})})]}):null,r==="inventory"?e(h,{children:[e(g,{title:"New tests",note:"Last 14 days.",children:e(m,{name:"new-tests",caption:"New tests",rows:a.new_tests||[],columns:[{key:"state",label:"State",filter:!0,render:t=>e(M,{value:t.state})},{key:"id",label:"Test",primary:!0,render:t=>e("a",{class:"ci-id",href:`#/ci/test/${encodeURIComponent(t.id)}`,children:S(t.id,90)})},{key:"first",label:"First seen",render:t=>x(t.first)},{key:"runs",label:"Runs since",num:!0},{key:"failures",label:"Failed",num:!0},{key:"flake_events",label:"Flaked",num:!0}],empty:e(C,{children:e(w,{icon:"ci",title:"No new tests in the last 14 days.",children:a.has_test_reports?"Tests are named here the first time a report shows them.":"Upload test reports (JUnit, CTRF, Jest, Vitest, Go, TRX) as artifacts to track tests."})})})}),e(g,{title:"Skipped on the default branch now",note:"Since as far back as the stored reports go.",children:e(m,{name:"skipped-tests",caption:"Skipped tests",rows:a.skipped_tests||[],columns:[{key:"id",label:"Test",primary:!0,render:t=>e("span",{class:"ci-id",children:S(t.id,90)})},{key:"new",label:"Newly skipped",raw:t=>t.new?"yes":"no",render:t=>t.new?e(I,{tone:"watch",children:"Newly skipped"}):"\u2013"},{key:"since",label:"Skipped since",render:t=>x(t.since)},{key:"days",label:"Days",num:!0},{key:"suites",label:"Jobs",num:!0}],empty:e(C,{children:e(w,{icon:"check",title:"Nothing is skipped.",children:"Skipped tests are named once a run with this version reads their reports."})})})})]}):null,r==="coverage"?e(Fe,{app:s,cov:a.coverage,days:a.window_days??30}):null]},r)]}),a.cost?e(Pe,{c:a.cost}):null,e(L,{name:"Quarantine",ci:a}),e(L,{name:"Impact",app:s,ci:a}),e(L,{name:"Setup",app:s,ci:a}),a.benchmark?e("p",{class:"ci-say",children:[e("span",{class:"sv-mark sv-mark-neutral","aria-hidden":"true"}),"Compared with similar public repos on the printable page."]}):null]})}var E=s=>`$${s.toLocaleString(void 0,{minimumFractionDigits:2,maximumFractionDigits:2})}`;function Pe({c:s}){let a=Object.entries(s.by_runner||{}).sort((i,l)=>l[1]-i[1]).map(([i,l])=>({id:i,runner:i,minutes:l}));return e(_,{title:"What CI costs",description:`Last ${s.days} days, at GitHub's published per-minute prices (read ${s.prices_read}); each job rounded up to a whole minute, as GitHub bills it.`,children:[e(D,{children:[e(o,{label:"Billed minutes",value:s.minutes.toLocaleString(),note:s.self_hosted_minutes?`${s.self_hosted_minutes.toLocaleString()} on self-hosted runners (not priced)`:void 0}),e(o,{label:s.private?"Cost":"Cost (public repo)",value:E(s.usd),note:s.private?void 0:`standard runners are free here; ${E(s.private_usd)} on a private repo`}),e(o,{label:"Spent on flaky re-runs",value:`${s.rerun_minutes.toLocaleString()} min`,note:`${E(s.rerun_usd)}: attempts that failed, then passed when run again`})]}),e(U,{children:[e(g,{title:"By workflow",children:e(m,{name:"cost-workflows",caption:"By workflow",search:!1,rows:s.workflows,columns:[{key:"workflow",label:"Workflow",primary:!0},{key:"minutes",label:"Minutes",num:!0},{key:"usd",label:"Cost",num:!0,render:i=>E(i.usd)}]})}),a.length?e(g,{title:"By runner",note:"Minutes",children:e(m,{name:"cost-runners",caption:"Minutes by runner",search:!1,rows:a,columns:[{key:"runner",label:"Runner",primary:!0},{key:"minutes",label:"Minutes",num:!0,render:i=>i.minutes.toLocaleString()}]})}):null]}),e(g,{title:"Costliest jobs",note:"The ten that cost the most.",children:e(m,{name:"cost-jobs",caption:"Costliest jobs",search:!1,rows:s.jobs.slice(0,10),columns:[{key:"job",label:"Job",primary:!0},{key:"workflow",label:"Workflow"},{key:"minutes",label:"Minutes",num:!0},{key:"usd",label:"Cost",num:!0,render:i=>E(i.usd)}]})}),s.tests?.length?e(g,{title:"What each flaky test cost",note:"The attempts it flaked in, shared when several flaked together.",children:e(m,{name:"cost-tests",caption:"What each flaky test cost",search:!1,rows:s.tests,columns:[{key:"id",label:"Test",primary:!0,render:i=>e("a",{class:"ci-id",href:`#/ci/test/${encodeURIComponent(i.id)}`,children:i.id})},{key:"flakes",label:"Flakes",num:!0},{key:"minutes",label:"Minutes",num:!0},{key:"usd",label:"Cost",num:!0,render:i=>E(i.usd)},{key:"prs",label:"PRs it got in the way of",num:!0}]})}):null]})}export{ze as default};
@@ -0,0 +1 @@
1
+ .blk-facts.cm-side{align-self:start;border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);grid-template-columns:minmax(0,1fr)}.section-body>.cm-figs{border:1px solid var(--line);border-radius:var(--r-lg);background:var(--surface);margin-bottom:18px}.section-body>.blk-tbl,.section-body>.columns+.blk-tbl{margin-top:18px}.section-body>.blk-tbl:first-child{margin-top:0}.blk-panel.is-good{border-top:3px solid var(--good)}.blk-panel.is-bad{border-top:3px solid var(--bad)}.cm-left small{display:block;font-size:13px;color:var(--ink-2)}.blk-facts.is-figures .cm-left{font-size:16px;font-weight:700;letter-spacing:0}[data-design=sheet][data-page=community] .bar-fill.tone-neutral{background:transparent;border:1px solid var(--ink);border-bottom:0}[data-design=sheet][data-page=community] .bar:has(.tone-bad) .bar-label:before{content:"";display:inline-block;width:11px;height:11px;margin-right:5px;vertical-align:-1px;background:var(--mark-bad) center / contain no-repeat}[data-design=sheet][data-page=community] .bar-label{font-size:14px;font-weight:600;color:var(--ink)}[data-design=sheet][data-page=community] .bar-value{font-size:14px}[data-design=sheet][data-page=community] .card:has(>.hero){border:1.5px solid var(--frame)}[data-design=sheet][data-page=community] .hero-name{margin:-18px -18px 14px;padding:9px 18px;min-height:42px;background:var(--strip);border-bottom:1px solid var(--line-strong);color:var(--ink)}[data-design=sheet][data-page=community] :is(.cm-side,.section-body>.cm-figs){border:1.5px solid var(--frame)}[data-design=sheet][data-page=community] .section-body>.blk-tbl{margin-top:24px}[data-design=sheet][data-page=community] .section-body>.blk-tbl:first-child{margin-top:0}[data-design=sheet][data-page=community] .blk-panel.is-good{border-top:5px solid var(--sv-mint)}[data-design=sheet][data-page=community] .blk-panel.is-bad{border-top:5px solid var(--sv-coral)}[data-design=sheet][data-page=community] .blk-facts.is-figures .cm-left{font-family:var(--font);font-size:16px;font-weight:700}[data-design=sheet][data-page=community] .cm-left small{font-size:14px;font-weight:400}@media(max-width:480px){[data-design=sheet][data-page=community] .bar-label{flex:none}[data-design=sheet][data-page=community] .bar-label:before{display:block;margin:0 auto 3px}}.blk-tbl{margin-top:18px;min-width:0}.blk-tbl:first-child{margin-top:0}.blk-tbl-h{display:flex;gap:4px 12px;align-items:baseline;flex-wrap:wrap;padding:10px 14px;background:var(--surface);border:1px solid var(--line);border-bottom:0;border-radius:var(--r-lg) var(--r-lg) 0 0}.blk-tbl-h h3{margin:0;font-size:15.5px;font-weight:700}.blk-tbl-note{font-size:13.5px;color:var(--ink-2);flex:1 1 200px;min-width:0}.blk-tbl-act{margin-left:auto}.blk-tbl .table-wrap{border-top-left-radius:0;border-top-right-radius:0}.blk-tbl>.card:not(.table-wrap),.blk-tbl>.blk-facts{border-top-left-radius:0;border-top-right-radius:0}.blk-tbl>.blk-facts{border:1px solid var(--line);background:var(--surface)}.blk-tbl>.blk-note{margin:0;padding:9px 14px;border:1px solid var(--line);border-top:0;background:var(--surface-2);font-size:13.5px;color:var(--ink-2);border-radius:0 0 var(--r-lg) var(--r-lg);overflow-wrap:anywhere}.blk-tbl:has(>.blk-note) .table-wrap{border-bottom-left-radius:0;border-bottom-right-radius:0}@media(min-width:641px){.blk-tbl .table :is(th,td)+:is(th,td){border-left:1px solid var(--line)}}.blk-panel{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);overflow:hidden;min-width:0;display:flex;flex-direction:column}.blk-panel+.blk-panel,.blk-tbl+.blk-panel,.blk-panel+.blk-tbl{margin-top:18px}.columns>.blk-panel+.blk-panel,.columns>.blk-tbl+.blk-panel,.columns>.blk-panel+.blk-tbl,.columns>.blk-tbl+.blk-tbl{margin-top:0}.blk-panel-h{display:flex;gap:6px 12px;align-items:center;justify-content:space-between;flex-wrap:wrap;padding:10px 16px;background:var(--surface-2);border-bottom:1px solid var(--line)}.blk-panel-h h3{margin:0;font-size:15.5px;font-weight:700}.blk-panel-b{padding:14px 16px;display:flex;flex-direction:column;gap:12px;flex:1;min-width:0}.blk-panel-b>p{margin:0;overflow-wrap:anywhere}.blk-panel-b>.blk-facts{margin:-14px -16px 0;border-bottom:1px solid var(--line)}.blk-panel-b>.blk-facts:last-child{margin-bottom:-14px;border-bottom:0}.blk-bar{display:flex;gap:8px;align-items:center;flex-wrap:wrap;padding:10px 16px;border-top:1px solid var(--line);background:var(--surface-2)}.blk-bar-flat{padding:0;border:0;background:none}.blk-how{margin:0;font-size:13.5px;color:var(--ink-2)}.blk-facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(170px,1fr));margin:0;overflow:hidden}.blk-fact{padding:9px 16px;border-right:1px solid var(--line);border-bottom:1px solid var(--line);margin:0 -1px -1px 0;min-width:0}.blk-fact dt{font-size:13px;font-weight:700;color:var(--ink-2)}.blk-fact dd{margin:2px 0 0;font-size:14.5px;overflow-wrap:anywhere}.blk-fact dd.num,.blk-fact .num{font-variant-numeric:tabular-nums}.blk-facts.is-figures .blk-fact dd{font-size:24px;font-weight:800;letter-spacing:-.01em;line-height:1.2}.blk-facts.is-figures .blk-fact dd small{display:block;font-size:13px;font-weight:400;letter-spacing:0;color:var(--ink-2);line-height:1.4}.blk-form{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));gap:12px 16px;align-items:start}.blk-form .field{margin:0}.blk-out{overflow-wrap:anywhere}@media(max-width:480px){.blk-panel-h,.blk-panel-b,.blk-bar:not(.blk-bar-flat),.blk-tbl-h{padding-left:14px;padding-right:14px}.blk-panel-b>.blk-facts{margin-left:-14px;margin-right:-14px}.blk-facts{grid-template-columns:repeat(2,minmax(0,1fr))}.blk-fact{padding:9px 14px}.blk-facts>.blk-fact:last-child:nth-child(odd){grid-column:span 2}}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl{margin-top:24px}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl:first-child{margin-top:0}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl-h{background:var(--strip);border:1.5px solid var(--frame);border-bottom:1px solid var(--line-strong);padding:10px 14px;min-height:42px}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl-h h3{font-size:16px;font-weight:700}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl-note{font-size:14px}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl .table-wrap,[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl>.card:not(.table-wrap),[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl>.blk-facts{border:1.5px solid var(--frame);border-top:0}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl>.blk-note{border:1.5px solid var(--frame);border-top:1px solid var(--line-strong);background:var(--strip);font-size:14px}@media(min-width:641px){[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl .table :is(th,td)+:is(th,td){border-left:1px solid var(--line-strong)}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl .table td{padding:10px 14px}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-tbl .th-btn{padding:9px 14px}}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-panel{border:1.5px solid var(--frame)}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-panel-h{background:var(--strip);border-bottom-color:var(--line-strong);min-height:42px;padding:9px 16px}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-panel-h h3{font-size:16px;font-weight:700}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-panel-b{font-size:15px}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-panel-b>.blk-facts{border-bottom-color:var(--line-strong)}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-bar:not(.blk-bar-flat){background:var(--strip);border-top-color:var(--line-strong)}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-how{font-size:14px}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-fact{border-color:var(--line-strong);padding:10px 16px}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-fact dt{font-size:14px;font-weight:600;color:var(--ink-2)}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-fact dd{font-size:15px;color:var(--ink)}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-facts.is-figures .blk-fact dd{font-family:var(--display);font-weight:700;font-size:32px;letter-spacing:0;line-height:1.1;margin-top:4px}[data-design=sheet]:is([data-page=deps],[data-page=community]) .blk-facts.is-figures .blk-fact dd small{font-family:var(--font);font-size:14px;font-weight:400;margin-top:2px}.blk-link{background:none;border:0;padding:0;font:inherit;color:var(--accent);cursor:pointer;text-decoration:underline;text-underline-offset:3px}.blk-link:hover{text-decoration-thickness:2px}