detectkit 0.16.1__tar.gz → 0.16.3__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.16.1/detectkit.egg-info → detectkit-0.16.3}/PKG-INFO +1 -1
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/__init__.py +1 -1
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/channels/email.py +27 -6
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/channels/telegram.py +4 -4
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/channels/webhook.py +26 -6
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/rules/alerting.md +9 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/rules/cli.md +3 -2
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/commands/test_alert.py +18 -2
- {detectkit-0.16.1 → detectkit-0.16.3/detectkit.egg-info}/PKG-INFO +1 -1
- {detectkit-0.16.1 → detectkit-0.16.3}/LICENSE +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/MANIFEST.in +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/README.md +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/__init__.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/channels/__init__.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/channels/base.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/channels/branding.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/channels/factory.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/channels/mattermost.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/channels/slack.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/orchestrator/__init__.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/orchestrator/_base.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/orchestrator/_cooldown.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/orchestrator/_decision.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/orchestrator/_dispatch.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/orchestrator/_recovery.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/orchestrator/_types.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/alerting/orchestrator/orchestrator.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/__init__.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/_output.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/CLAUDE.section.md +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/rules/detectors.md +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/rules/metrics.md +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/rules/overview.md +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/rules/project.md +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/skills/dtk-feedback/SKILL.md +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/skills/dtk-new-metric/SKILL.md +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/skills/dtk-setup-project/SKILL.md +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/commands/__init__.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/commands/clean.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/commands/init.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/commands/init_claude.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/commands/run.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/commands/unlock.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/main.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/config/__init__.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/config/metric_config.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/config/profile.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/config/project_config.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/config/validator.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/core/__init__.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/core/interval.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/core/models.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/__init__.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/_sql_manager.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/clickhouse_manager.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/__init__.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/_alert_states.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/_base.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/_datapoints.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/_detections.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/_maintenance.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/_metrics.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/_schema.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/_tasks.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/internal_tables/manager.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/manager.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/mysql_manager.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/postgres_manager.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/database/tables.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/__init__.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/base.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/factory.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/seasonality.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/statistical/__init__.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/statistical/_windowed.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/statistical/iqr.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/statistical/mad.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/statistical/manual_bounds.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/detectors/statistical/zscore.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/loaders/__init__.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/loaders/metric_loader.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/loaders/query_template.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/orchestration/__init__.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/orchestration/error_dispatch.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/orchestration/task_manager/__init__.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/orchestration/task_manager/_alert_step.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/orchestration/task_manager/_base.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/orchestration/task_manager/_detect_step.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/orchestration/task_manager/_load_step.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/orchestration/task_manager/_types.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/orchestration/task_manager/manager.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/utils/__init__.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/utils/datetime_utils.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/utils/env_interpolation.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/utils/json_utils.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit/utils/stats.py +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit.egg-info/SOURCES.txt +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit.egg-info/dependency_links.txt +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit.egg-info/entry_points.txt +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit.egg-info/requires.txt +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/detectkit.egg-info/top_level.txt +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/pyproject.toml +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/requirements.txt +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/setup.cfg +0 -0
- {detectkit-0.16.1 → detectkit-0.16.3}/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.16.
|
|
7
|
+
__version__ = "0.16.3"
|
|
8
8
|
|
|
9
9
|
from detectkit.core.interval import Interval
|
|
10
10
|
from detectkit.core.models import ColumnDefinition, TableModel
|
|
@@ -315,13 +315,13 @@ class EmailChannel(BaseAlertChannel):
|
|
|
315
315
|
parts.append(self._lead_html(ctx["description"]))
|
|
316
316
|
|
|
317
317
|
if kind == "anomaly":
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
318
|
+
parts.append(self._rule_html(ctx))
|
|
319
|
+
parts.append(
|
|
320
|
+
self._lead_html(
|
|
321
|
+
f"Latest {ctx['consecutive_count']}/{ctx['consecutive_required']} "
|
|
322
|
+
"consecutive points met the quorum."
|
|
323
|
+
)
|
|
323
324
|
)
|
|
324
|
-
parts.append(self._lead_html(lead))
|
|
325
325
|
parts.append(
|
|
326
326
|
self._stat_grid(
|
|
327
327
|
[
|
|
@@ -340,6 +340,7 @@ class EmailChannel(BaseAlertChannel):
|
|
|
340
340
|
"expected bounds."
|
|
341
341
|
)
|
|
342
342
|
parts.append(self._lead_html(lead))
|
|
343
|
+
parts.append(self._rule_html(ctx))
|
|
343
344
|
parts.append(
|
|
344
345
|
self._stat_grid(
|
|
345
346
|
[
|
|
@@ -380,6 +381,26 @@ class EmailChannel(BaseAlertChannel):
|
|
|
380
381
|
f"{html.escape(text)}</td></tr>"
|
|
381
382
|
)
|
|
382
383
|
|
|
384
|
+
def _rule_html(self, ctx: dict[str, Any]) -> str:
|
|
385
|
+
"""The configured firing rule: a bold ``Rule`` label + a monospace chip.
|
|
386
|
+
|
|
387
|
+
Mirrors the inline-code "Rule chip" the webhook/Telegram channels render,
|
|
388
|
+
so the same firing rule reads the same way in every channel.
|
|
389
|
+
"""
|
|
390
|
+
expr = (
|
|
391
|
+
f"min_detectors={ctx['min_detectors']} · "
|
|
392
|
+
f"direction={html.escape(str(ctx['direction_policy']))} · "
|
|
393
|
+
f"consecutive={ctx['consecutive_required']}"
|
|
394
|
+
)
|
|
395
|
+
return (
|
|
396
|
+
f'<tr><td style="padding:0 24px 14px 24px;font-family:{_SANS};font-size:13px;'
|
|
397
|
+
f'color:{_MUTED};mso-line-height-rule:exactly;line-height:22px;">'
|
|
398
|
+
f'<strong style="color:{_INK};">Rule</strong> '
|
|
399
|
+
f'<code style="font-family:{_MONO};font-size:12px;background-color:{_PAPER};'
|
|
400
|
+
f'border:1px solid {_BORDER};border-radius:4px;padding:2px 6px;color:{_INK};">'
|
|
401
|
+
f"{expr}</code></td></tr>"
|
|
402
|
+
)
|
|
403
|
+
|
|
383
404
|
def _stat_grid(self, pairs: list[tuple[str, str]]) -> str:
|
|
384
405
|
"""A 2-column grid of (label, value) cells (table-based)."""
|
|
385
406
|
cells = ""
|
|
@@ -167,9 +167,9 @@ class TelegramChannel(BaseAlertChannel):
|
|
|
167
167
|
f"<b>{ctx['consecutive_count']}/{ctx['consecutive_required']}</b> consecutive"
|
|
168
168
|
)
|
|
169
169
|
lines.append(
|
|
170
|
-
f"<
|
|
170
|
+
f"<b>Rule</b> <code>min_detectors={ctx['min_detectors']} · "
|
|
171
171
|
f"direction={esc(ctx['direction_policy'])} · "
|
|
172
|
-
f"consecutive={ctx['consecutive_required']}</
|
|
172
|
+
f"consecutive={ctx['consecutive_required']}</code>"
|
|
173
173
|
)
|
|
174
174
|
lines.append("")
|
|
175
175
|
lines.append(
|
|
@@ -188,9 +188,9 @@ class TelegramChannel(BaseAlertChannel):
|
|
|
188
188
|
"expected bounds."
|
|
189
189
|
)
|
|
190
190
|
lines.append(
|
|
191
|
-
f"<
|
|
191
|
+
f"<b>Rule</b> <code>min_detectors={ctx['min_detectors']} · "
|
|
192
192
|
f"direction={esc(ctx['direction_policy'])} · "
|
|
193
|
-
f"consecutive={ctx['consecutive_required']}</
|
|
193
|
+
f"consecutive={ctx['consecutive_required']}</code>"
|
|
194
194
|
)
|
|
195
195
|
lines.append("")
|
|
196
196
|
lines.append(
|
|
@@ -256,11 +256,19 @@ class WebhookChannel(BaseAlertChannel):
|
|
|
256
256
|
def code(s: str) -> str:
|
|
257
257
|
return f"`{s}`" if s else ""
|
|
258
258
|
|
|
259
|
+
# The configured firing rule, set apart as a bold "Rule" label + an
|
|
260
|
+
# inline-code chip so it reads as "this is the config that fired" at a
|
|
261
|
+
# glance. Backticks render identically on Slack and Mattermost; the bold
|
|
262
|
+
# label is platform-aware (see ``_bold``).
|
|
263
|
+
rule_chip = f"{self._bold('Rule')} " + code(
|
|
264
|
+
f"min_detectors={ctx['min_detectors']} · "
|
|
265
|
+
f"direction={ctx['direction_policy']} · "
|
|
266
|
+
f"consecutive={ctx['consecutive_required']}"
|
|
267
|
+
)
|
|
268
|
+
|
|
259
269
|
if kind == "anomaly":
|
|
260
270
|
lead = (
|
|
261
|
-
f"
|
|
262
|
-
f"direction={ctx['direction_policy']} · "
|
|
263
|
-
f"consecutive={ctx['consecutive_required']}. "
|
|
271
|
+
f"{rule_chip}\n"
|
|
264
272
|
f"Latest {ctx['consecutive_count']}/{ctx['consecutive_required']} "
|
|
265
273
|
"consecutive points met the quorum."
|
|
266
274
|
)
|
|
@@ -275,9 +283,7 @@ class WebhookChannel(BaseAlertChannel):
|
|
|
275
283
|
elif kind == "recovery":
|
|
276
284
|
lead = (
|
|
277
285
|
"The alert condition no longer holds — the metric is back within "
|
|
278
|
-
f"expected bounds
|
|
279
|
-
f"direction={ctx['direction_policy']} · "
|
|
280
|
-
f"consecutive={ctx['consecutive_required']}."
|
|
286
|
+
f"expected bounds.\n{rule_chip}"
|
|
281
287
|
)
|
|
282
288
|
short("Value", code(ctx["value_display"]))
|
|
283
289
|
short("Expected", code(ctx["expected_range"]))
|
|
@@ -329,6 +335,20 @@ class WebhookChannel(BaseAlertChannel):
|
|
|
329
335
|
"mrkdwn_in": ["text", "fields"],
|
|
330
336
|
}
|
|
331
337
|
|
|
338
|
+
def _bold(self, text: str) -> str:
|
|
339
|
+
"""Render *text* bold in the target platform's markdown.
|
|
340
|
+
|
|
341
|
+
Slack mrkdwn uses ``*bold*``; Mattermost and generic webhooks use
|
|
342
|
+
CommonMark ``**bold**`` (where ``*x*`` would render as italic). Mirrors
|
|
343
|
+
the platform branch in :meth:`_link_markup`, detecting Slack from the
|
|
344
|
+
webhook host.
|
|
345
|
+
"""
|
|
346
|
+
if not text:
|
|
347
|
+
return ""
|
|
348
|
+
if "hooks.slack.com" in self.webhook_url:
|
|
349
|
+
return f"*{text}*"
|
|
350
|
+
return f"**{text}**"
|
|
351
|
+
|
|
332
352
|
def _link_markup(self, url: str, label: str) -> str:
|
|
333
353
|
"""Render *label* as a clickable link in the target platform's syntax.
|
|
334
354
|
|
|
@@ -213,6 +213,15 @@ leads with a colored **status circle** — 🔴 anomaly, 🟢 recovery, 🟡 no-
|
|
|
213
213
|
table, a monospace params box, an optional "Open dashboard" button, and a
|
|
214
214
|
footer. The plain-text body remains the multipart fallback.
|
|
215
215
|
|
|
216
|
+
On anomaly **and** recovery alerts the **firing rule is set apart uniformly** in
|
|
217
|
+
every default-rendered channel: a bold **Rule** label + an inline-code chip
|
|
218
|
+
(`min_detectors=… · direction=… · consecutive=…`), with the quorum explanation
|
|
219
|
+
on its own line, so the rule reads as "this is the config that fired" at a
|
|
220
|
+
glance. Bold is platform-aware (`*Rule*` on Slack, `**Rule**` on
|
|
221
|
+
Mattermost/generic; `<b>Rule</b>` on Telegram; `<strong>` in email), while the
|
|
222
|
+
code chip is identical everywhere. Custom templates and the plain-text fallbacks
|
|
223
|
+
are unchanged.
|
|
224
|
+
|
|
216
225
|
## Project label (multi-project channels)
|
|
217
226
|
|
|
218
227
|
The bot keeps the **detectkit brand** name + avatar by default (so users rarely
|
|
@@ -55,8 +55,9 @@ dtk run --select <sel> [--steps load,detect,alert] [--from DATE] [--to DATE] \
|
|
|
55
55
|
|
|
56
56
|
Sends a mock alert (fake value/CI/severity) through the metric's configured
|
|
57
57
|
channels, using that alert config's own rule (`min_detectors` / `direction` /
|
|
58
|
-
`consecutive_anomalies`)
|
|
59
|
-
verify webhook URLs, channel permissions, and
|
|
58
|
+
`consecutive_anomalies`) and the project-name `[name]` prefix, so the preview
|
|
59
|
+
matches a real firing. Use it to verify webhook URLs, channel permissions, and
|
|
60
|
+
custom templates.
|
|
60
61
|
|
|
61
62
|
## `dtk unlock --select <sel>`
|
|
62
63
|
|
|
@@ -23,6 +23,7 @@ def create_mock_alert_data(
|
|
|
23
23
|
alerting_config,
|
|
24
24
|
timezone_display: str = "UTC",
|
|
25
25
|
help_url: str | None = None,
|
|
26
|
+
project_name: str | None = None,
|
|
26
27
|
) -> AlertData:
|
|
27
28
|
"""
|
|
28
29
|
Create realistic mock AlertData for testing.
|
|
@@ -36,6 +37,10 @@ def create_mock_alert_data(
|
|
|
36
37
|
timezone_display: Timezone for display
|
|
37
38
|
help_url: Resolved "how to read this alert" link to preview (the
|
|
38
39
|
project's ``alert_help_url``); ``None`` renders no help link.
|
|
40
|
+
project_name: Project name from ``detectkit_project.yml`` — stamped so
|
|
41
|
+
the preview carries the same ``[name]`` prefix a real ``dtk run``
|
|
42
|
+
renders (the run pipeline sets it in ``_alert_step.py``); ``None``
|
|
43
|
+
renders no prefix.
|
|
39
44
|
|
|
40
45
|
Returns:
|
|
41
46
|
AlertData with mock anomaly data
|
|
@@ -90,6 +95,7 @@ def create_mock_alert_data(
|
|
|
90
95
|
},
|
|
91
96
|
consecutive_count=consecutive_required,
|
|
92
97
|
mentions=mentions,
|
|
98
|
+
project_name=project_name,
|
|
93
99
|
dashboard_url=getattr(alerting_config, "dashboard_url", None),
|
|
94
100
|
links=dict(getattr(alerting_config, "links", {}) or {}),
|
|
95
101
|
help_url=help_url,
|
|
@@ -123,7 +129,13 @@ def run_test_alert(metric_name: str, profile: str | None = None):
|
|
|
123
129
|
with open(project_config_path) as f:
|
|
124
130
|
project_data = yaml.safe_load(f)
|
|
125
131
|
|
|
126
|
-
|
|
132
|
+
# Project name drives the [name] prefix a real `dtk run` stamps on every
|
|
133
|
+
# alert (see _alert_step.py); thread it so the preview matches a real firing.
|
|
134
|
+
project_name = project_data.get("name")
|
|
135
|
+
|
|
136
|
+
# Metrics dir lives under `paths.metrics` (default "metrics"); the old
|
|
137
|
+
# top-level `metrics_path` key is deprecated and ignored by ProjectConfig.
|
|
138
|
+
metrics_dir_name = (project_data.get("paths") or {}).get("metrics", "metrics")
|
|
127
139
|
|
|
128
140
|
# Resolve the "how to read this alert" link so the preview matches what real
|
|
129
141
|
# alerts would carry (brand default, a custom URL, or hidden via false).
|
|
@@ -187,7 +199,11 @@ def run_test_alert(metric_name: str, profile: str | None = None):
|
|
|
187
199
|
print(f" Channels: {', '.join(alerting_config.channels)}\n")
|
|
188
200
|
|
|
189
201
|
alert_data = create_mock_alert_data(
|
|
190
|
-
metric_config,
|
|
202
|
+
metric_config,
|
|
203
|
+
alerting_config,
|
|
204
|
+
timezone_display,
|
|
205
|
+
help_url=help_url,
|
|
206
|
+
project_name=project_name,
|
|
191
207
|
)
|
|
192
208
|
|
|
193
209
|
success_count = 0
|
|
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.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/skills/dtk-feedback/SKILL.md
RENAMED
|
File without changes
|
{detectkit-0.16.1 → detectkit-0.16.3}/detectkit/cli/assets/claude/skills/dtk-new-metric/SKILL.md
RENAMED
|
File without changes
|
{detectkit-0.16.1 → detectkit-0.16.3}/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
|