detectkit 0.23.2__tar.gz → 0.24.1__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.
Files changed (126) hide show
  1. {detectkit-0.23.2/detectkit.egg-info → detectkit-0.24.1}/PKG-INFO +1 -1
  2. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/__init__.py +1 -1
  3. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/autotune/config_emitter.py +17 -5
  4. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/autotune/html_labeler.py +9 -5
  5. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/autotune/label_server.py +10 -5
  6. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/assets/claude/rules/autotune.md +4 -2
  7. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/assets/claude/rules/cli.md +8 -7
  8. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/assets/claude/skills/dtk-autotune/SKILL.md +10 -7
  9. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/commands/autotune.py +3 -3
  10. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/commands/init_claude.py +10 -7
  11. {detectkit-0.23.2 → detectkit-0.24.1/detectkit.egg-info}/PKG-INFO +1 -1
  12. {detectkit-0.23.2 → detectkit-0.24.1}/LICENSE +0 -0
  13. {detectkit-0.23.2 → detectkit-0.24.1}/MANIFEST.in +0 -0
  14. {detectkit-0.23.2 → detectkit-0.24.1}/README.md +0 -0
  15. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/alerting/__init__.py +0 -0
  16. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/alerting/channels/__init__.py +0 -0
  17. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/alerting/channels/base.py +0 -0
  18. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/alerting/channels/branding.py +0 -0
  19. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/alerting/channels/email.py +0 -0
  20. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/alerting/channels/factory.py +0 -0
  21. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/alerting/channels/mattermost.py +0 -0
  22. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/alerting/channels/slack.py +0 -0
  23. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/alerting/channels/telegram.py +0 -0
  24. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/alerting/channels/webhook.py +0 -0
  25. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/alerting/orchestrator/__init__.py +0 -0
  26. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/alerting/orchestrator/_base.py +0 -0
  27. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/alerting/orchestrator/_cooldown.py +0 -0
  28. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/alerting/orchestrator/_decision.py +0 -0
  29. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/alerting/orchestrator/_dispatch.py +0 -0
  30. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/alerting/orchestrator/_recovery.py +0 -0
  31. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/alerting/orchestrator/_types.py +0 -0
  32. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/alerting/orchestrator/orchestrator.py +0 -0
  33. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/autotune/__init__.py +0 -0
  34. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/autotune/_base.py +0 -0
  35. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/autotune/_types.py +0 -0
  36. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/autotune/autotuner.py +0 -0
  37. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/autotune/crossval.py +0 -0
  38. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/autotune/detector_select.py +0 -0
  39. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/autotune/distribution.py +0 -0
  40. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/autotune/grid_search.py +0 -0
  41. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/autotune/labels.py +0 -0
  42. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/autotune/result.py +0 -0
  43. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/autotune/scoring.py +0 -0
  44. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/autotune/seasonality_search.py +0 -0
  45. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/autotune/settings.py +0 -0
  46. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/autotune/window_select.py +0 -0
  47. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/__init__.py +0 -0
  48. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/_output.py +0 -0
  49. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/assets/claude/CLAUDE.section.md +0 -0
  50. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/assets/claude/rules/alerting.md +0 -0
  51. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/assets/claude/rules/detectors.md +0 -0
  52. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/assets/claude/rules/metrics.md +0 -0
  53. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/assets/claude/rules/overview.md +0 -0
  54. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/assets/claude/rules/project.md +0 -0
  55. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/assets/claude/skills/dtk-feedback/SKILL.md +0 -0
  56. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/assets/claude/skills/dtk-new-metric/SKILL.md +0 -0
  57. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/assets/claude/skills/dtk-setup-project/SKILL.md +0 -0
  58. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/commands/__init__.py +0 -0
  59. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/commands/clean.py +0 -0
  60. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/commands/init.py +0 -0
  61. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/commands/run.py +0 -0
  62. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/commands/test_alert.py +0 -0
  63. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/commands/unlock.py +0 -0
  64. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/cli/main.py +0 -0
  65. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/config/__init__.py +0 -0
  66. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/config/metric_config.py +0 -0
  67. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/config/profile.py +0 -0
  68. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/config/project_config.py +0 -0
  69. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/config/validator.py +0 -0
  70. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/core/__init__.py +0 -0
  71. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/core/interval.py +0 -0
  72. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/core/models.py +0 -0
  73. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/database/__init__.py +0 -0
  74. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/database/_sql_manager.py +0 -0
  75. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/database/clickhouse_manager.py +0 -0
  76. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/database/internal_tables/__init__.py +0 -0
  77. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/database/internal_tables/_alert_states.py +0 -0
  78. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/database/internal_tables/_autotune_runs.py +0 -0
  79. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/database/internal_tables/_base.py +0 -0
  80. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/database/internal_tables/_datapoints.py +0 -0
  81. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/database/internal_tables/_detections.py +0 -0
  82. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/database/internal_tables/_maintenance.py +0 -0
  83. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/database/internal_tables/_metrics.py +0 -0
  84. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/database/internal_tables/_schema.py +0 -0
  85. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/database/internal_tables/_tasks.py +0 -0
  86. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/database/internal_tables/manager.py +0 -0
  87. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/database/manager.py +0 -0
  88. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/database/mysql_manager.py +0 -0
  89. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/database/postgres_manager.py +0 -0
  90. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/database/tables.py +0 -0
  91. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/detectors/__init__.py +0 -0
  92. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/detectors/base.py +0 -0
  93. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/detectors/factory.py +0 -0
  94. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/detectors/seasonality.py +0 -0
  95. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/detectors/statistical/__init__.py +0 -0
  96. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/detectors/statistical/_windowed.py +0 -0
  97. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/detectors/statistical/iqr.py +0 -0
  98. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/detectors/statistical/mad.py +0 -0
  99. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/detectors/statistical/manual_bounds.py +0 -0
  100. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/detectors/statistical/zscore.py +0 -0
  101. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/loaders/__init__.py +0 -0
  102. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/loaders/metric_loader.py +0 -0
  103. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/loaders/query_template.py +0 -0
  104. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/orchestration/__init__.py +0 -0
  105. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/orchestration/error_dispatch.py +0 -0
  106. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/orchestration/task_manager/__init__.py +0 -0
  107. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/orchestration/task_manager/_alert_step.py +0 -0
  108. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/orchestration/task_manager/_base.py +0 -0
  109. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/orchestration/task_manager/_detect_step.py +0 -0
  110. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/orchestration/task_manager/_load_step.py +0 -0
  111. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/orchestration/task_manager/_types.py +0 -0
  112. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/orchestration/task_manager/manager.py +0 -0
  113. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/utils/__init__.py +0 -0
  114. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/utils/datetime_utils.py +0 -0
  115. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/utils/env_interpolation.py +0 -0
  116. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/utils/json_utils.py +0 -0
  117. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit/utils/stats.py +0 -0
  118. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit.egg-info/SOURCES.txt +0 -0
  119. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit.egg-info/dependency_links.txt +0 -0
  120. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit.egg-info/entry_points.txt +0 -0
  121. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit.egg-info/requires.txt +0 -0
  122. {detectkit-0.23.2 → detectkit-0.24.1}/detectkit.egg-info/top_level.txt +0 -0
  123. {detectkit-0.23.2 → detectkit-0.24.1}/pyproject.toml +0 -0
  124. {detectkit-0.23.2 → detectkit-0.24.1}/requirements.txt +0 -0
  125. {detectkit-0.23.2 → detectkit-0.24.1}/setup.cfg +0 -0
  126. {detectkit-0.23.2 → detectkit-0.24.1}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: detectkit
3
- Version: 0.23.2
3
+ Version: 0.24.1
4
4
  Summary: Metric monitoring with automatic anomaly detection
5
5
  Author: detectkit team
6
6
  License: MIT
@@ -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.23.2"
7
+ __version__ = "0.24.1"
8
8
 
9
9
  from detectkit.core.interval import Interval
10
10
  from detectkit.core.models import ColumnDefinition, TableModel
@@ -85,11 +85,23 @@ def _build_body(original: MetricConfig, result: AutoTuneResult, new_name: str) -
85
85
  body["loading_start_time"] = original.loading_start_time
86
86
  body["loading_batch_size"] = original.loading_batch_size
87
87
 
88
- scalar_cols = _flatten_scalar_seasonality(result.chosen_seasonality)
89
- if scalar_cols:
90
- body["seasonality_columns"] = scalar_cols
91
- elif original.seasonality_columns:
92
- body["seasonality_columns"] = original.seasonality_columns
88
+ # The top-level `seasonality_columns` field feeds the *built-in* time-feature
89
+ # extractor and is validated against the built-in allowlist (hour/day_of_week/…).
90
+ # When the metric instead sources seasonality from the query
91
+ # (`query_columns.seasonality`, e.g. a custom `league_day`), the loader ignores
92
+ # `seasonality_columns` entirely and the chosen grouping rides in the detector's
93
+ # `seasonality_components`. So never copy the chosen columns here in that mode —
94
+ # they may be custom names the validator rejects, and they are already declared
95
+ # in the preserved `query_columns`.
96
+ if original.query_columns and original.query_columns.seasonality:
97
+ if original.seasonality_columns:
98
+ body["seasonality_columns"] = original.seasonality_columns
99
+ else:
100
+ scalar_cols = _flatten_scalar_seasonality(result.chosen_seasonality)
101
+ if scalar_cols:
102
+ body["seasonality_columns"] = scalar_cols
103
+ elif original.seasonality_columns:
104
+ body["seasonality_columns"] = original.seasonality_columns
93
105
 
94
106
  body["detectors"] = [
95
107
  {"type": result.chosen_detector_type, "params": result.chosen_detector_params}
@@ -7,7 +7,8 @@ file in the canonical schema, fed back via
7
7
  ``dtk autotune --select <metric> --incidents <file-or-dir>``.
8
8
 
9
9
  The page is offline-only — a browser cannot write to the project, so Export
10
- downloads a **versioned** file (``<metric>-<UTC-stamp>.yml``); drop it into
10
+ downloads a **versioned** file named after the metric, with the optional set name
11
+ folded in as a suffix (``<metric>[-<name>]-<UTC-stamp>.yml``); drop it into
11
12
  ``incidents/<metric>/`` to keep every labeling round (``--incidents`` accepts
12
13
  that directory and uses the newest version).
13
14
 
@@ -135,7 +136,7 @@ _TEMPLATE = """<!doctype html>
135
136
  <div id="empty" class="empty">No incidents marked yet — drag across a span on the chart above.</div>
136
137
  <ul id="list"></ul>
137
138
  <footer>All times UTC · self-contained, nothing leaves your browser · re-label any time —
138
- exports are versioned (<code>__METRIC__-&lt;timestamp&gt;.yml</code>), so keep every round in
139
+ exports are versioned (<code>__METRIC__[-&lt;name&gt;]-&lt;timestamp&gt;.yml</code>), so keep every round in
139
140
  <code>incidents/__METRIC__/</code>. Generated by <code>dtk autotune --label</code>.</footer>
140
141
  </div>
141
142
  <script>
@@ -451,10 +452,13 @@ exportBtn.onclick = () => {
451
452
  const d=new Date();
452
453
  const stamp=d.getUTCFullYear()+pad2(d.getUTCMonth()+1)+pad2(d.getUTCDate())+'T'
453
454
  +pad2(d.getUTCHours())+pad2(d.getUTCMinutes())+pad2(d.getUTCSeconds())+'Z';
454
- const base = name.trim() ? slug(name) : '__METRIC__';
455
+ // Always name the file after the metric; the optional set name is a suffix.
456
+ const s = name.trim() ? slug(name) : '';
457
+ const suffix = (s && s !== '__METRIC__') ? '-' + s : '';
458
+ const fname = '__METRIC__' + suffix + '-' + stamp + '.yml';
455
459
  const blob=new Blob([y], {type:'text/yaml'}); const a=document.createElement('a');
456
- a.href=URL.createObjectURL(blob); a.download=base+'-'+stamp+'.yml'; a.click();
457
- setMsg('Downloaded ' + base + '-' + stamp + '.yml — move it into incidents/__METRIC__/ and re-run.', 'info');
460
+ a.href=URL.createObjectURL(blob); a.download=fname; a.click();
461
+ setMsg('Downloaded ' + fname + ' — move it into incidents/__METRIC__/ and re-run.', 'info');
458
462
  }
459
463
  };
460
464
 
@@ -31,9 +31,12 @@ _MAX_BODY = 5_000_000 # generous cap on the posted labels payload
31
31
 
32
32
 
33
33
  def _sanitize(name: str) -> str:
34
- """Filesystem-safe slug for a label-set name; falls back to ``incidents``."""
35
- slug = _NAME_RE.sub("-", name.strip().lower()).strip("-")
36
- return slug or "incidents"
34
+ """Filesystem-safe slug for a label-set name; ``""`` when blank.
35
+
36
+ Used as an optional *suffix* appended to the metric-named filename, so a blank
37
+ name simply yields no suffix (the file is named after the metric alone).
38
+ """
39
+ return _NAME_RE.sub("-", name.strip().lower()).strip("-")
37
40
 
38
41
 
39
42
  def _stamp() -> str:
@@ -87,14 +90,16 @@ class _Handler(BaseHTTPRequestHandler):
87
90
 
88
91
  payload = json.loads(self.rfile.read(length).decode("utf-8"))
89
92
  yaml_text = str(payload.get("yaml", ""))
90
- set_name = _sanitize(str(payload.get("name", "")))
93
+ suffix = _sanitize(str(payload.get("name", "")))
91
94
  raw = _yaml.safe_load(yaml_text)
92
95
  # validate against the canonical schema before writing anything
93
96
  parse_incident_labels(
94
97
  raw, interval_seconds=srv.interval_seconds, metric_name=srv.metric
95
98
  )
96
99
  srv.incidents_dir.mkdir(parents=True, exist_ok=True)
97
- out = srv.incidents_dir / f"{set_name}-{_stamp()}.yml"
100
+ # Always name the file after the metric; the optional set name is a suffix.
101
+ stem = f"{srv.metric}-{suffix}" if suffix else srv.metric
102
+ out = srv.incidents_dir / f"{stem}-{_stamp()}.yml"
98
103
  out.write_text(yaml_text, encoding="utf-8")
99
104
  srv.saved_path = out
100
105
  except Exception as exc:
@@ -53,7 +53,8 @@ dtk autotune --select <sel> [--incidents FILE] [--label] [--scoring METRIC] \
53
53
  non-interactively) tunes **unsupervised**.
54
54
  - `--label` — open the interactive labeler (zoom/pan, edit incident edges,
55
55
  per-incident descriptions, named sets). **Default:** a local 127.0.0.1 server +
56
- browser; **Save & tune** writes `incidents/<name>/<set>-<UTC>.yml` and the run
56
+ browser; **Save & tune** writes `incidents/<metric>/<metric>[-<set>]-<UTC>.yml`
57
+ (named after the metric, optional set name as a suffix) and the run
57
58
  **continues into tuning on it**. `--no-serve` writes a static
58
59
  `metrics/<name>__labeler.html` (Export downloads the file) and exits; `--no-open`
59
60
  prints the URL instead of launching a browser.
@@ -91,7 +92,8 @@ user to recall timestamps** — it is the easiest, most reliable path:
91
92
  2. The user marks incidents on the chart (scroll to zoom, drag the navigator to
92
93
  move, click-drag to mark, drag an incident's edges to adjust, add a
93
94
  description, optionally name the set), then clicks **Save & tune**.
94
- 3. That writes `incidents/<name>/<set>-<UTC>.yml` automatically (versioned —
95
+ 3. That writes `incidents/<metric>/<metric>[-<set>]-<UTC>.yml` automatically
96
+ (named after the metric, optional set name as a suffix; versioned —
95
97
  re-labeling never overwrites) and the **same command continues into the tuning
96
98
  run** on it. No manual file moving.
97
99
  4. To re-tune later on saved sets, point `--incidents` at the folder
@@ -20,15 +20,16 @@ Run all commands from a project directory (the one containing
20
20
 
21
21
  Used by `run`, `unlock`, and `clean` (drift mode). Three forms:
22
22
 
23
- - **Metric name** — `--select cpu_usage`. Searches the root `metrics/` dir only.
24
- Do **not** add `.yml` (it is appended). This matches the metric **file**, but
25
- every operation is keyed by the metric `name` inside the YAML.
23
+ - **Metric name** — `--select cpu_usage`. Resolves to `metrics/cpu_usage.yml` at
24
+ the root, then falls back to a recursive search by the YAML `name:` field in any
25
+ subdirectory. Do **not** add `.yml` (it is appended). This matches the metric by
26
+ **name**, and every operation is keyed by that `name` inside the YAML.
26
27
  - **Path / glob** — `--select "metrics/critical/*.yml"`, `--select "api_*"`,
27
28
  `--select "metrics/**/*.yml"`. Searches recursively via glob; keep `.yml`.
28
29
  - **Tag** — `--select tag:critical`. Searches recursively for metrics whose
29
30
  `tags:` list contains that tag.
30
31
 
31
- `--select "*"` selects everything. `--exclude / -e` removes matches
32
+ `--select "*"` selects everything. `--exclude / -e` (on `dtk run`) removes matches
32
33
  (`--select "*" --exclude "metrics/staging/*"`). Metric names must be unique
33
34
  across the project; duplicates raise an error listing the conflicting files.
34
35
 
@@ -39,9 +40,9 @@ dtk run --select <sel> [--steps load,detect,alert] [--from DATE] [--to DATE] \
39
40
  [--full-refresh] [--force] [--profile NAME]
40
41
  ```
41
42
 
42
- - `--steps` — subset/order of `load`, `detect`, `alert` (default all). Examples:
43
- `--steps load` (verify the query), `--steps detect` (rerun detection only),
44
- `--steps detect,alert` (skip load).
43
+ - `--steps` — which of `load`, `detect`, `alert` to run (default all); they always
44
+ execute in `load → detect → alert` order. Examples: `--steps load` (verify the
45
+ query), `--steps detect` (rerun detection only), `--steps detect,alert` (skip load).
45
46
  - `--from DATE` / `--to DATE` — `YYYY-MM-DD` or `YYYY-MM-DD HH:MM:SS`, UTC.
46
47
  Affects only the `load` step. `--from` overrides the metric's
47
48
  `loading_start_time`; `--to` defaults to now.
@@ -96,8 +96,9 @@ user through it:
96
96
  with an optional **description**). Adjust one by dragging its **edges**, or its
97
97
  **middle** to move it; optionally **name the set**; *remove* / *Clear all* fix
98
98
  mistakes.
99
- 3. Click **Save & tune**. The server writes `incidents/<name>/<set>-<UTC>.yml`
100
- automatically (versioned re-labeling never overwrites) and the **same command
99
+ 3. Click **Save & tune**. The server writes `incidents/<metric>/<metric>[-<set>]-<UTC>.yml`
100
+ automatically (named after the metric, with the optional set name as a suffix;
101
+ versioned — re-labeling never overwrites) and the **same command
101
102
  continues into the tuning run on it**. No manual file moving.
102
103
 
103
104
  (`--no-serve` is the offline fallback: it writes a static
@@ -157,8 +158,10 @@ dtk autotune --select <name>
157
158
  none yet, load first: `dtk run --select <name> --steps load` (optionally
158
159
  `--from <date>` to backfill history — more history tunes better). The default
159
160
  **supervised** scoring metric is **MCC** (robust to rare anomalies). Override
160
- only with reason: `--scoring recall` when a miss is worse than a false page,
161
- `--scoring f1`, etc. Run `dtk autotune --help` to confirm the live flags. Use
161
+ only with reason: `--scoring f_beta` (with a higher `beta:` in the `autotune:`
162
+ block) when a miss is worse than a false page, `--scoring f1` for a balanced
163
+ trade-off, etc. The valid values are `mcc`, `f1`, `f_beta`, `balanced_accuracy`,
164
+ `roc_auc`, `pr_auc`. Run `dtk autotune --help` to confirm the live flags. Use
162
165
  `--dry-run` to search without writing anything.
163
166
 
164
167
  **Unsupervised runs** (no labels) do **not** optimize MCC or any labeled metric —
@@ -190,9 +193,9 @@ The `#` comment header walks the whole decision; summarize for the user:
190
193
  `Objective : unsupervised (band-fit + flag-budget) = …` (it never claims an
191
194
  `mcc =` score it didn't compute). Read off the value + the per-fold CV spread.
192
195
 
193
- Offer alternatives: a re-run with a different `--scoring` (e.g. precision vs
194
- recall trade-off) or a nudged parameter. See `autotune.md` for the
195
- `_dtk_autotune_runs` audit table.
196
+ Offer alternatives: a re-run with a different `--scoring` (e.g. `f_beta` with a
197
+ higher `beta` to favor recall, or `f1` for balance) or a nudged parameter. See
198
+ `autotune.md` for the `_dtk_autotune_runs` audit table.
196
199
 
197
200
  ## Step 5 — Show how the monitoring behaves (DB inspection query)
198
201
 
@@ -100,9 +100,9 @@ _LABELS_GLOBS = ("*.yml", "*.yaml", "*.json")
100
100
  def _labels_files(directory: Path) -> list[Path]:
101
101
  """All labels files in *directory*, oldest→newest.
102
102
 
103
- The labeler saves versioned, ISO-stamped names (``<set>-<UTC>.yml``) which sort
104
- chronologically, so name order is chronological (tie-broken by mtime). This
105
- lets ``incidents/<metric>/`` keep every labeling round on disk.
103
+ The labeler saves versioned, ISO-stamped names (``<metric>[-<set>]-<UTC>.yml``)
104
+ which sort chronologically, so name order is chronological (tie-broken by mtime).
105
+ This lets ``incidents/<metric>/`` keep every labeling round on disk.
106
106
  """
107
107
  files: list[Path] = []
108
108
  for pattern in _LABELS_GLOBS:
@@ -10,9 +10,9 @@ It writes three things into the target directory:
10
10
  - ``CLAUDE.md`` — created if absent, otherwise a managed detectkit block is
11
11
  injected/refreshed between HTML-comment markers (existing content is kept).
12
12
  - ``.claude/rules/detectkit/`` — the reference docs the assistant reads on
13
- demand (overview, cli, project, metrics, detectors, alerting).
13
+ demand (overview, cli, project, metrics, detectors, alerting, autotune).
14
14
  - ``.claude/skills/`` — user-facing skills (``dtk-setup-project``,
15
- ``dtk-new-metric``, ``dtk-feedback``).
15
+ ``dtk-new-metric``, ``dtk-autotune``, ``dtk-feedback``).
16
16
 
17
17
  The source of truth for all of the above lives in ``detectkit/cli/assets/claude``
18
18
  and ships with the package, so re-running this command after upgrading detectkit
@@ -38,12 +38,15 @@ if TYPE_CHECKING:
38
38
  # strings, so it is never evaluated at runtime — safe on 3.10).
39
39
  from importlib.resources.abc import Traversable
40
40
 
41
- # Region in CLAUDE.md owned by this command. The BEGIN marker carries the
42
- # detectkit version (informational); the regex below matches any version so a
43
- # refresh replaces the whole region in place rather than appending a duplicate.
41
+ # Region in CLAUDE.md owned by this command. The BEGIN marker is intentionally
42
+ # version-less: stamping the version here made the block churn on every release
43
+ # (a no-op upgrade still rewrote the marker), pushing users to re-run for nothing.
44
+ # The block now changes only when its content actually changes, so a refresh is a
45
+ # true no-op otherwise. The regex still matches the old *versioned* markers
46
+ # (`<!-- BEGIN detectkit v0.23.2 ... -->`), so refreshing an existing file
47
+ # replaces the whole region in place rather than appending a duplicate.
44
48
  _BEGIN = (
45
- f"<!-- BEGIN detectkit v{__version__} "
46
- "(managed by `dtk init-claude` — do not edit between these markers) -->"
49
+ "<!-- BEGIN detectkit (managed by `dtk init-claude` — do not edit between these markers) -->"
47
50
  )
48
51
  _END = "<!-- END detectkit -->"
49
52
  _BLOCK_RE = re.compile(r"<!-- BEGIN detectkit.*?-->.*?<!-- END detectkit -->", re.DOTALL)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: detectkit
3
- Version: 0.23.2
3
+ Version: 0.24.1
4
4
  Summary: Metric monitoring with automatic anomaly detection
5
5
  Author: detectkit team
6
6
  License: MIT
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes