detectkit 0.25.0__tar.gz → 0.26.0__tar.gz
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.
- {detectkit-0.25.0/detectkit.egg-info → detectkit-0.26.0}/PKG-INFO +1 -1
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/__init__.py +1 -1
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/autotune/html_labeler.py +57 -15
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/assets/claude/rules/autotune.md +4 -2
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/assets/claude/skills/dtk-autotune/SKILL.md +3 -0
- {detectkit-0.25.0 → detectkit-0.26.0/detectkit.egg-info}/PKG-INFO +1 -1
- {detectkit-0.25.0 → detectkit-0.26.0}/LICENSE +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/MANIFEST.in +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/README.md +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/alerting/__init__.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/alerting/channels/__init__.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/alerting/channels/base.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/alerting/channels/branding.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/alerting/channels/email.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/alerting/channels/factory.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/alerting/channels/mattermost.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/alerting/channels/slack.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/alerting/channels/telegram.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/alerting/channels/webhook.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/alerting/orchestrator/__init__.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/alerting/orchestrator/_base.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/alerting/orchestrator/_cooldown.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/alerting/orchestrator/_decision.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/alerting/orchestrator/_dispatch.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/alerting/orchestrator/_recovery.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/alerting/orchestrator/_types.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/alerting/orchestrator/orchestrator.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/autotune/__init__.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/autotune/_base.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/autotune/_types.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/autotune/autotuner.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/autotune/config_emitter.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/autotune/crossval.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/autotune/detector_select.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/autotune/distribution.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/autotune/grid_search.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/autotune/label_server.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/autotune/labels.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/autotune/result.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/autotune/scoring.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/autotune/seasonality_search.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/autotune/settings.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/autotune/window_select.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/__init__.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/_output.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/assets/claude/CLAUDE.section.md +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/assets/claude/rules/alerting.md +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/assets/claude/rules/cli.md +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/assets/claude/rules/detectors.md +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/assets/claude/rules/metrics.md +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/assets/claude/rules/overview.md +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/assets/claude/rules/project.md +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/assets/claude/skills/dtk-feedback/SKILL.md +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/assets/claude/skills/dtk-new-metric/SKILL.md +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/assets/claude/skills/dtk-setup-project/SKILL.md +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/commands/__init__.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/commands/autotune.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/commands/clean.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/commands/init.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/commands/init_claude.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/commands/run.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/commands/test_alert.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/commands/unlock.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/main.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/config/__init__.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/config/metric_config.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/config/profile.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/config/project_config.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/config/validator.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/core/__init__.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/core/interval.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/core/models.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/database/__init__.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/database/_sql_manager.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/database/clickhouse_manager.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/database/internal_tables/__init__.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/database/internal_tables/_alert_states.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/database/internal_tables/_autotune_runs.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/database/internal_tables/_base.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/database/internal_tables/_datapoints.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/database/internal_tables/_detections.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/database/internal_tables/_maintenance.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/database/internal_tables/_metrics.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/database/internal_tables/_schema.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/database/internal_tables/_tasks.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/database/internal_tables/manager.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/database/manager.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/database/mysql_manager.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/database/postgres_manager.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/database/tables.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/detectors/__init__.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/detectors/base.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/detectors/factory.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/detectors/seasonality.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/detectors/statistical/__init__.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/detectors/statistical/_windowed.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/detectors/statistical/iqr.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/detectors/statistical/mad.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/detectors/statistical/manual_bounds.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/detectors/statistical/zscore.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/loaders/__init__.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/loaders/metric_loader.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/loaders/query_template.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/orchestration/__init__.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/orchestration/error_dispatch.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/orchestration/task_manager/__init__.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/orchestration/task_manager/_alert_step.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/orchestration/task_manager/_base.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/orchestration/task_manager/_detect_step.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/orchestration/task_manager/_load_step.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/orchestration/task_manager/_types.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/orchestration/task_manager/manager.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/utils/__init__.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/utils/datetime_utils.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/utils/env_interpolation.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/utils/json_utils.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit/utils/stats.py +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit.egg-info/SOURCES.txt +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit.egg-info/dependency_links.txt +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit.egg-info/entry_points.txt +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit.egg-info/requires.txt +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/detectkit.egg-info/top_level.txt +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/pyproject.toml +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/requirements.txt +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/setup.cfg +0 -0
- {detectkit-0.25.0 → detectkit-0.26.0}/setup.py +0 -0
|
@@ -4,7 +4,7 @@ detectk - Anomaly Detection for Time-Series Metrics
|
|
|
4
4
|
A Python library for data analysts and engineers to monitor metrics with automatic anomaly detection.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
__version__ = "0.
|
|
7
|
+
__version__ = "0.26.0"
|
|
8
8
|
|
|
9
9
|
from detectkit.core.interval import Interval
|
|
10
10
|
from detectkit.core.models import ColumnDefinition, TableModel
|
|
@@ -177,6 +177,7 @@ _TEMPLATE = """<!doctype html>
|
|
|
177
177
|
<label>bridge gaps ≤
|
|
178
178
|
<input id="thgap" class="num" type="number" min="0" step="1" value="0" /> intervals
|
|
179
179
|
</label>
|
|
180
|
+
<button id="thwin" class="ghost" style="display:none" title="capture across the whole current view again">↺ whole view</button>
|
|
180
181
|
<button id="thadd" class="primary" disabled>Add 0 spans</button>
|
|
181
182
|
<button id="thdone" class="ghost">Done</button>
|
|
182
183
|
</div>
|
|
@@ -189,8 +190,10 @@ _TEMPLATE = """<!doctype html>
|
|
|
189
190
|
<canvas id="ov" aria-label="navigator — drag the window to pan, its edges to stretch the view"></canvas>
|
|
190
191
|
<div class="navhint">Drag on an empty area to mark an incident · drag an existing incident's edges to
|
|
191
192
|
adjust it, or its middle to move it · click its <b>✕</b> (or select it and press Delete) to remove it ·
|
|
192
|
-
use <b>Threshold capture</b> to grab every span past a horizontal line at once
|
|
193
|
-
the chart to it
|
|
193
|
+
use <b>Threshold capture</b> to grab every span past a horizontal line at once (click sets the line; drag
|
|
194
|
+
across the chart to limit it to a time window — handy when the metric behaves differently across periods) ·
|
|
195
|
+
<b>focus</b> a row to jump the chart to it · scroll to zoom, double-click to reset · drag the navigator
|
|
196
|
+
window below to pan.</div>
|
|
194
197
|
<div id="empty" class="empty">No incidents marked yet — drag across a span on the chart above.</div>
|
|
195
198
|
<ul id="list"></ul>
|
|
196
199
|
<footer>All times UTC · self-contained, nothing leaves your browser · re-label any time —
|
|
@@ -232,6 +235,9 @@ let dpr = 1, hover = null, dragging = null, ovAct = null;
|
|
|
232
235
|
// selObj: the currently selected incident (object ref, survives re-sorting);
|
|
233
236
|
// hoverRow/hoverDel: list-row / ✕-handle hover targets; threshold-capture state.
|
|
234
237
|
let selObj = null, hoverRow = -1, hoverDel = -1, thMode = false, thHover = null;
|
|
238
|
+
// Threshold-capture window: thDown tracks a press, thDragWin a live drag, capWin
|
|
239
|
+
// the committed custom window (null → capture within the current view).
|
|
240
|
+
let thDown = null, thDragWin = null, capWin = null;
|
|
235
241
|
|
|
236
242
|
const clamp = (x,a,b) => Math.max(a, Math.min(b, x));
|
|
237
243
|
const vspan = () => viewMax - viewMin;
|
|
@@ -304,11 +310,20 @@ const vAt = clientY => { const r=c.getBoundingClientRect();
|
|
|
304
310
|
// The active threshold value: the locked input wins, else the live cursor value.
|
|
305
311
|
function thEff() { const s=thvalEl.value.trim();
|
|
306
312
|
return (s!=='' && !isNaN(Number(s))) ? Number(s) : thHover; }
|
|
307
|
-
//
|
|
313
|
+
// The active capture window: a live/committed custom window, else the current
|
|
314
|
+
// view — so the threshold only grabs the period you're looking at (or painted),
|
|
315
|
+
// not the whole series. Returns [lo, hi] in ms.
|
|
316
|
+
function capRange() {
|
|
317
|
+
const w = thDragWin || capWin;
|
|
318
|
+
if (w) return [Math.min(w.a,w.b), Math.max(w.a,w.b)];
|
|
319
|
+
return [viewMin, viewMax]; }
|
|
320
|
+
// Contiguous runs of points on the chosen side of the line within the capture
|
|
321
|
+
// window, bridging short gaps.
|
|
308
322
|
function thRuns() { const val=thEff(); if (val===null) return [];
|
|
309
323
|
const dir=thdirEl.value, gapMax=Math.max(0, parseInt(thgapEl.value)||0);
|
|
324
|
+
const win=capRange(), lo=win[0], hi=win[1];
|
|
310
325
|
const runs=[]; let s=null, e=null, gap=0;
|
|
311
|
-
for (let i=0;i<N;i++) { const p=pts[i];
|
|
326
|
+
for (let i=0;i<N;i++) { const p=pts[i]; if (p.ts<lo || p.ts>hi) continue;
|
|
312
327
|
const q = p.v!==null && (dir==='above' ? p.v>val : p.v<val);
|
|
313
328
|
if (q) { if (s===null) s=p.ts; e=p.ts; gap=0; }
|
|
314
329
|
else if (s!==null) { gap++; if (gap>gapMax) { runs.push([s,e]); s=null; gap=0; } } }
|
|
@@ -328,8 +343,8 @@ function addCaptured(a,b) {
|
|
|
328
343
|
if (host===null) incidents.push({a, b, label:''}); }
|
|
329
344
|
function toggleTh(on) { thMode = (on===undefined) ? !thMode : !!on;
|
|
330
345
|
thbtnEl.classList.toggle('active', thMode); thbarEl.style.display = thMode ? 'flex' : 'none';
|
|
331
|
-
if (thMode) { hover=null; } else { thHover=null; }
|
|
332
|
-
c.style.cursor = thMode ? '
|
|
346
|
+
if (thMode) { hover=null; } else { thHover=null; thDown=null; thDragWin=null; capWin=null; updateThWin(); }
|
|
347
|
+
c.style.cursor = thMode ? 'crosshair' : 'crosshair'; if (thMode) { updateThWin(); thCount(); } draw(); }
|
|
333
348
|
|
|
334
349
|
function rr(g,x,y,w,h,r) { g.beginPath();
|
|
335
350
|
g.moveTo(x+r,y); g.arcTo(x+w,y,x+w,y+h,r); g.arcTo(x+w,y+h,x,y+h,r);
|
|
@@ -376,9 +391,20 @@ function draw() {
|
|
|
376
391
|
if (dragging && dragging.mode==='new') { const x0=px(dragging.a), x1=px(dragging.b);
|
|
377
392
|
ctx.fillStyle='rgba(240,173,78,0.28)'; ctx.fillRect(Math.min(x0,x1), M.t*dpr, Math.abs(x1-x0), plotH()); }
|
|
378
393
|
drawSeries(ctx, px, py, viewMin, viewMax, M.l*dpr, plotW(), '#d15b36', 1.5);
|
|
379
|
-
if (thMode) {
|
|
380
|
-
|
|
381
|
-
|
|
394
|
+
if (thMode) {
|
|
395
|
+
const win=capRange(), narrow = !!(thDragWin || capWin);
|
|
396
|
+
const xlo=clamp(px(win[0]), M.l*dpr, c.width-M.r*dpr), xhi=clamp(px(win[1]), M.l*dpr, c.width-M.r*dpr);
|
|
397
|
+
if (narrow) { // dim everything outside the capture window
|
|
398
|
+
ctx.fillStyle='rgba(27,25,22,0.5)';
|
|
399
|
+
ctx.fillRect(M.l*dpr, M.t*dpr, xlo-M.l*dpr, plotH());
|
|
400
|
+
ctx.fillRect(xhi, M.t*dpr, (c.width-M.r*dpr)-xhi, plotH());
|
|
401
|
+
ctx.strokeStyle='rgba(240,173,78,0.7)'; ctx.lineWidth=1*dpr; ctx.setLineDash([3*dpr,3*dpr]);
|
|
402
|
+
ctx.beginPath(); ctx.moveTo(xlo,M.t*dpr); ctx.lineTo(xlo,c.height-M.b*dpr);
|
|
403
|
+
ctx.moveTo(xhi,M.t*dpr); ctx.lineTo(xhi,c.height-M.b*dpr); ctx.stroke(); ctx.setLineDash([]); }
|
|
404
|
+
const val=thEff();
|
|
405
|
+
if (val!==null && val>=vmin && val<=vmax) { const yy=py(val);
|
|
406
|
+
ctx.strokeStyle='#f0ad4e'; ctx.lineWidth=1.5*dpr; ctx.setLineDash([6*dpr,4*dpr]);
|
|
407
|
+
ctx.beginPath(); ctx.moveTo(xlo, yy); ctx.lineTo(xhi, yy); ctx.stroke(); ctx.setLineDash([]); } }
|
|
382
408
|
ctx.restore();
|
|
383
409
|
if (thMode) drawThLabel();
|
|
384
410
|
else if (dragging && !ovAct) drawDragLabel();
|
|
@@ -389,8 +415,9 @@ function draw() {
|
|
|
389
415
|
// would be captured.
|
|
390
416
|
function drawThLabel() {
|
|
391
417
|
const val=thEff(); if (val===null) return;
|
|
392
|
-
const n=thRuns().length;
|
|
393
|
-
const text='line '+fmtVal(val)+' · '+thdirEl.value+' · '+n+' span'+(n===1?'':'s')
|
|
418
|
+
const n=thRuns().length, win=capRange(), narrow = !!(thDragWin || capWin);
|
|
419
|
+
const text='line '+fmtVal(val)+' · '+thdirEl.value+' · '+n+' span'+(n===1?'':'s')
|
|
420
|
+
+ (narrow ? (' · '+fmtDur(win[1]-win[0])+' window') : '');
|
|
394
421
|
ctx.font=(11*dpr)+'px ui-monospace, monospace';
|
|
395
422
|
const tw=ctx.measureText(text).width, bw=tw+14*dpr, bh=22*dpr, bx=M.l*dpr+6*dpr, by=M.t*dpr+2;
|
|
396
423
|
ctx.fillStyle='rgba(27,25,22,0.96)'; ctx.strokeStyle='#f0ad4e'; ctx.lineWidth=1*dpr;
|
|
@@ -491,7 +518,9 @@ function hitIncident(clientX, clientY) {
|
|
|
491
518
|
return null;
|
|
492
519
|
}
|
|
493
520
|
c.addEventListener('mousedown', e => {
|
|
494
|
-
|
|
521
|
+
// In threshold mode a press either sets the line (a click) or paints a capture
|
|
522
|
+
// window (a horizontal drag) — resolved on mouseup by how far it moved.
|
|
523
|
+
if (thMode) { thDown = {x:e.clientX, ts:tsAt(e.clientX)}; thHover = vAt(e.clientY); thCount(); draw(); return; }
|
|
495
524
|
const hit = hitIncident(e.clientX, e.clientY), t = tsAt(e.clientX);
|
|
496
525
|
if (hit && hit.edge==='del') { removeIncident(incidents[hit.i]); return; }
|
|
497
526
|
if (hit && hit.edge==='move') { const iv=incidents[hit.i]; selObj=iv;
|
|
@@ -501,7 +530,10 @@ c.addEventListener('mousedown', e => {
|
|
|
501
530
|
else { selObj=null; dragging={mode:'new', a:t, b:t, sx:e.clientX, cx:e.clientX}; draw(); }
|
|
502
531
|
});
|
|
503
532
|
c.addEventListener('mousemove', e => { if (ovAct) return;
|
|
504
|
-
if (thMode && !dragging) {
|
|
533
|
+
if (thMode && !dragging) {
|
|
534
|
+
if (thDown && Math.abs(e.clientX - thDown.x) > 6) { thDragWin = {a: thDown.ts, b: tsAt(e.clientX)}; }
|
|
535
|
+
else { if (thDown) thDragWin = null; thHover = vAt(e.clientY); }
|
|
536
|
+
thCount(); draw(); return; }
|
|
505
537
|
if (dragging) {
|
|
506
538
|
dragging.cx=e.clientX; const t=tsAt(e.clientX);
|
|
507
539
|
if (dragging.mode==='new') { dragging.b=t; }
|
|
@@ -539,8 +571,16 @@ window.addEventListener('mousemove', e => { if (!ovAct) return; const t=ovTsAtCs
|
|
|
539
571
|
if (ovAct.type==='l') setView(Math.min(t, viewMax-minSpan), viewMax);
|
|
540
572
|
else if (ovAct.type==='r') setView(viewMin, Math.max(t, viewMin+minSpan));
|
|
541
573
|
else { const d=t-ovAct.grab; setView(ovAct.vMin+d, ovAct.vMax+d); } });
|
|
542
|
-
window.addEventListener('mouseup',
|
|
574
|
+
window.addEventListener('mouseup', e => {
|
|
543
575
|
if (ovAct) { ovAct=null; return; }
|
|
576
|
+
if (thMode && thDown) {
|
|
577
|
+
if (Math.abs(e.clientX - thDown.x) > 6) { // a drag → set the capture window
|
|
578
|
+
const a=thDown.ts, b=tsAt(e.clientX); capWin = {a:Math.min(a,b), b:Math.max(a,b)};
|
|
579
|
+
} else { // a click → set the threshold line value
|
|
580
|
+
thvalEl.value = String(Math.round(vAt(e.clientY)*1000)/1000);
|
|
581
|
+
}
|
|
582
|
+
thDown=null; thDragWin=null; updateThWin(); thCount(); draw(); return;
|
|
583
|
+
}
|
|
544
584
|
if (!dragging) return;
|
|
545
585
|
if (dragging.mode==='new') {
|
|
546
586
|
if (Math.abs(dragging.cx-dragging.sx) > 4) {
|
|
@@ -574,7 +614,9 @@ window.focusInc = i => { const iv=incidents[i]; if (!iv) return; selObj=iv;
|
|
|
574
614
|
const thbtnEl=document.getElementById('thbtn'), thbarEl=document.getElementById('thbar');
|
|
575
615
|
const thvalEl=document.getElementById('thval'), thdirEl=document.getElementById('thdir');
|
|
576
616
|
const thgapEl=document.getElementById('thgap'), thaddEl=document.getElementById('thadd');
|
|
577
|
-
const thdoneEl=document.getElementById('thdone');
|
|
617
|
+
const thdoneEl=document.getElementById('thdone'), thwinEl=document.getElementById('thwin');
|
|
618
|
+
function updateThWin() { thwinEl.style.display = capWin ? '' : 'none'; }
|
|
619
|
+
thwinEl.onclick = () => { capWin=null; updateThWin(); thCount(); draw(); };
|
|
578
620
|
thbtnEl.onclick = () => toggleTh();
|
|
579
621
|
thdoneEl.onclick = () => toggleTh(false);
|
|
580
622
|
thdirEl.onchange = () => { thCount(); draw(); };
|
|
@@ -99,8 +99,10 @@ user to recall timestamps** — it is the easiest, most reliable path:
|
|
|
99
99
|
move, click-drag to mark, drag an incident's edges to adjust, add a
|
|
100
100
|
description, optionally name the set), then clicks **Save & tune**. For many
|
|
101
101
|
clear outliers, **Threshold capture** grabs every span past a horizontal line
|
|
102
|
-
at once (above/below, with an optional gap-bridge);
|
|
103
|
-
|
|
102
|
+
at once (above/below, with an optional gap-bridge); it captures within the
|
|
103
|
+
current view by default, and dragging across the chart limits it to a time
|
|
104
|
+
window (different boundary per period). Each band's ✕ (or select + Delete)
|
|
105
|
+
removes one, and **focus** on a list row jumps the chart to it.
|
|
104
106
|
3. That writes `incidents/<metric>/<metric>[-<set>]-<UTC>.yml` automatically
|
|
105
107
|
(named after the metric, optional set name as a suffix; versioned —
|
|
106
108
|
re-labeling never overwrites) and the **same command continues into the tuning
|
{detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/assets/claude/skills/dtk-autotune/SKILL.md
RENAMED
|
@@ -101,6 +101,9 @@ user through it:
|
|
|
101
101
|
3. When many outliers are obvious, use **Threshold capture**: set a horizontal
|
|
102
102
|
line (hover the chart or type a value), pick **above/below**, optionally bridge
|
|
103
103
|
small gaps, and **Add N spans** marks them all at once — then tidy with the ✕.
|
|
104
|
+
It captures within the current view by default; **drag across the chart** to
|
|
105
|
+
limit it to a time window (so a metric that behaves differently across periods
|
|
106
|
+
can take a different boundary in each). **↺ whole view** clears the window.
|
|
104
107
|
4. Click **Save & tune**. The server writes `incidents/<metric>/<metric>[-<set>]-<UTC>.yml`
|
|
105
108
|
automatically (named after the metric, with the optional set name as a suffix;
|
|
106
109
|
versioned — re-labeling never overwrites) and the **same command
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/assets/claude/skills/dtk-feedback/SKILL.md
RENAMED
|
File without changes
|
{detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/assets/claude/skills/dtk-new-metric/SKILL.md
RENAMED
|
File without changes
|
{detectkit-0.25.0 → detectkit-0.26.0}/detectkit/cli/assets/claude/skills/dtk-setup-project/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|