chore: implement phase 3 - #2781
Merged
Merged
Conversation
|
Tofel
force-pushed
the
dx-5122-alerts-assertion-p3
branch
from
September 1, 2026 11:25
e0da182 to
df1c958
Compare
Tofel
force-pushed
the
dx-5122-alerts-assertion-p2
branch
from
September 1, 2026 11:25
bbd2ebe to
799df17
Compare
Tofel
force-pushed
the
dx-5122-alerts-assertion-p3
branch
from
September 1, 2026 14:18
df1c958 to
96a840d
Compare
Tofel
force-pushed
the
dx-5122-alerts-assertion-p2
branch
from
September 2, 2026 09:45
4159c2f to
3894d9b
Compare
Tofel
force-pushed
the
dx-5122-alerts-assertion-p3
branch
from
September 2, 2026 09:45
74d5473 to
375b1b4
Compare
Tofel
force-pushed
the
dx-5122-alerts-assertion-p2
branch
from
September 4, 2026 15:03
3894d9b to
96ebd78
Compare
Tofel
force-pushed
the
dx-5122-alerts-assertion-p3
branch
from
September 4, 2026 15:03
375b1b4 to
2b8c6c4
Compare
Tofel
force-pushed
the
dx-5122-alerts-assertion-p2
branch
from
September 7, 2026 09:35
96ebd78 to
be0dd37
Compare
Tofel
force-pushed
the
dx-5122-alerts-assertion-p3
branch
from
September 7, 2026 09:35
2b8c6c4 to
f25a091
Compare
Tofel
marked this pull request as ready for review
September 7, 2026 09:36
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The resolver semantics and CLI wiring appear correct and are backed by targeted unit tests covering the main success and error paths.
Pull request overview
Adds the first runnable “phase 3” slice of grafana-alertcheck: a name/UID resolver for alert rule definitions plus a list subcommand that can authenticate to Grafana, validate version support, fetch/parse rule definitions, and print them in a copy-friendly form to support subsequent Resolve() flows.
Changes:
- Implement
gate.Resolve()/resolveOne()to map operator-provided names to unique rule definitions, including UID form support, folder scoping, duplicate-UID collapsing with notes, and structured no-match / ambiguous-match errors with suggestions. - Add CLI command wiring (
main.run) and implementlistto fetch definitions from Grafana and print KIND/FOLDER/GROUP/TITLE/UID. - Add focused unit tests for resolver semantics and CLI
run/listbehavior, plus env var parsing helper.
File summaries
| File | Description |
|---|---|
| grafana-alertcheck/internal/gate/resolve.go | Implements alert-name/UID resolution semantics, ambiguity/no-match surfaces, and supported-kind filtering. |
| grafana-alertcheck/internal/gate/resolve_test.go | Adds comprehensive tests covering resolution forms, errors, unsupported kinds, UID-collapsing notes, and folder scoping. |
| grafana-alertcheck/cmd/grafana-alertcheck/main.go | Implements testable CLI dispatch (run) and routes list with consistent exit-code rules. |
| grafana-alertcheck/cmd/grafana-alertcheck/main_test.go | Tests CLI dispatch: no args, help, unknown subcommand, and list-with-missing-env behavior. |
| grafana-alertcheck/cmd/grafana-alertcheck/list.go | Implements list subcommand: version check, definitions fetch, stable sorting, and tabular output. |
| grafana-alertcheck/cmd/grafana-alertcheck/list_test.go | Tests end-to-end list output and version gating using an httptest Grafana server. |
| grafana-alertcheck/cmd/grafana-alertcheck/env.go | Adds environment-only Grafana URL/token loading with token-safe error behavior. |
Review details
- Files reviewed: 7/7 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Tofel
force-pushed
the
dx-5122-alerts-assertion-p2
branch
from
September 7, 2026 09:46
be0dd37 to
6f29f79
Compare
Tofel
force-pushed
the
dx-5122-alerts-assertion-p3
branch
from
September 7, 2026 09:46
f25a091 to
1b660dd
Compare
Add Resolve() for alert name resolution (uid:/Title/Folder/Title/ Folder/Group/Title forms, UID collapse, no-match suggestions) and the grafana-alertcheck CLI's list subcommand, the first runnable piece of the gate. Incorporates review fixes: reject empty path segments in classifyForm, guard uid: against an empty suffix, scope the no-match rule count and suggestions to supported rule kinds only, and exit 0 on -h/--help.
Tofel
force-pushed
the
dx-5122-alerts-assertion-p3
branch
from
September 7, 2026 15:02
1b660dd to
f674ee0
Compare
Tofel
force-pushed
the
dx-5122-alerts-assertion-p2
branch
from
September 7, 2026 15:02
6f29f79 to
ba93502
Compare
Tofel
removed this pull request from stack #2791
September 9, 2026 09:52
* chore: implement phase 4 Add per-rule poll timings, scheduler, and budget check (P4). - schedule.go: DeriveTimings, Scheduler, CheckBudget (§5) - Address review: add Folder/Title resolve test, rename CheckBudget's minPollEvery to tightestUID * chore: enhance unit tests * chore: address code review comments * chore: implement phase 5 (#2783) * chore: implement phase 5 Add the JSONL evidence log (P5). - log.go: Header/Poll records, reduction, H2 transition markers, §3.2 verification, append-only Writer with flock, ReadLog - flock_unix.go: non-blocking exclusive lock, unix only - schedule.go: DeriveTimingsFromLog — log-mode cadence comes from the header, never from the definitions * chore: remove unix build tags * chore: address code review comments * chore: implement phase 6 (#2784) * chore: implement phase 6 Invariant defended: H2. The one question: can watch return success over a window that nothing is recording? Watch() records the first observation of each non-skipped rule, then detaches a child that polls at the cadence in the header. The parent returns only after the child reports ready on an inherited pipe, and writes the pidfile after that. A clean stop writes the sentinel; a hard error does not. * chore: add a unit test, remove build tags * chore: address code review comments * chore: implement phase 7 (#2785) * chore: implement phase 7 Invariant defended: H3. The one question: can a rule be called alive because it looked alive one poll ago? proveCoverage (grafana-alertcheck/internal/gate/coverage.go) is the pure coverage function: nine checks over one rule's polls — sentinel, from-bounds, heartbeat continuity, health error/nodata, liveness, in-window pause, rule absence, KeepLast. Liveness is absolute, never a delta. Cross-domain comparisons translate by each poll's own skew and widen boundary segments by its skew bound, fail-closed. * chore: enhance unit tests * chore: address code review comments * chore: implement phase 8 (#2786) * chore: implement phase 8 Invariant defended: H6/H7. The one question: can a violation ever outrank an unobservable rule, or can a pass happen without Violations empty and err nil? Adds classify.go: the pure per-instance classifier (outcome table, preexisting policy, BadFor) and decide(), the seam combining proveCoverage with those timelines under one Policy. Consolidates rule-poll filtering and skew translation onto pollsForRule/runnerTime, shared with coverage.go. * chore: rename some vars + add unit tests * chore: address code review comments * chore: implement phase 9 (#2787) * chore: implement phase 9 Invariant defended: H5/H7. The one question: can check report a pass over a window it did not prove? Check() is the I/O shell around the pure decide(). Single-step synthesizes the header and its own sentinel, so no mode flag reaches the pure layer. Log mode stops the recorder before the one full read. The header, not a definition re-resolved after the window closed, is the authority for what was paused when the window opened — it decides `skipped`, the drain set, and the transitionGrace max. The flock, not the pidfile, is the authority for whether a writer still exists. * chore: remove unix build tag * chore: implement phase 10 (#2788) * Wire watch/check subcommands to the gate library, with a table+JSON renderer and H6/H7 exit-code mapping. Extend Result with per-rule/global thresholds and a real skew bound; export SkewHardLimit; reject --states normal. * chore: fix goreleaser.yaml and add version command * chore: implement phase 11 (#2789) * chore: implement phase 11 Add coverage.go's declared-KeepLast check (no_data_state/exec_err_state, not just an observed reason) and close the remaining §22 gaps: newly_bad's no-early-exit clock assertion, a recorder-mode gap right after the deploy, a rule's own coverage gap overriding its own recovery, a genuinely skew-discriminating staleness test, exit-2 consequences on two Reason-only coverage tests, and end-to-end checks for log-name collapse, a truncated log, and the real watch-written state histogram. * chore: address code review comments * chore: more concise comments (#2790) * chore: more concise comments * fix: merge conflict * chore: shorten comments * fix: resolve conflict * chore: use testify's require in tests (#2792) * chore: use testify's require in tests * chore: move remaining assumptions to testify * chore: address code review comments * chore: fix logging and std out printing (#2798) * chore: fix logging and std out printing * chore: truncate to seconds when comparing from time * chore: add centralized docs (#2802) * chore: add centralized docs * chore: further update docs * chore: address code review comments (#2807) * chore: address code review comments * chore: get rid of goreleaser
Tofel
added a commit
that referenced
this pull request
Sep 9, 2026
* chore: implement phase 2 Fix retry-error conflation, measure full poll latency, and harden Source test doubles for concurrency. * chore: enhance unit tests * chore: address code review comments * chore: implement phase 3 (#2781) * chore: implement phase 3 Add Resolve() for alert name resolution (uid:/Title/Folder/Title/ Folder/Group/Title forms, UID collapse, no-match suggestions) and the grafana-alertcheck CLI's list subcommand, the first runnable piece of the gate. Incorporates review fixes: reject empty path segments in classifyForm, guard uid: against an empty suffix, scope the no-match rule count and suggestions to supported rule kinds only, and exit 0 on -h/--help. * chore: enhance unit tests * chore: implement phase 4 (#2782) * chore: implement phase 4 Add per-rule poll timings, scheduler, and budget check (P4). - schedule.go: DeriveTimings, Scheduler, CheckBudget (§5) - Address review: add Folder/Title resolve test, rename CheckBudget's minPollEvery to tightestUID * chore: enhance unit tests * chore: address code review comments * chore: implement phase 5 (#2783) * chore: implement phase 5 Add the JSONL evidence log (P5). - log.go: Header/Poll records, reduction, H2 transition markers, §3.2 verification, append-only Writer with flock, ReadLog - flock_unix.go: non-blocking exclusive lock, unix only - schedule.go: DeriveTimingsFromLog — log-mode cadence comes from the header, never from the definitions * chore: remove unix build tags * chore: address code review comments * chore: implement phase 6 (#2784) * chore: implement phase 6 Invariant defended: H2. The one question: can watch return success over a window that nothing is recording? Watch() records the first observation of each non-skipped rule, then detaches a child that polls at the cadence in the header. The parent returns only after the child reports ready on an inherited pipe, and writes the pidfile after that. A clean stop writes the sentinel; a hard error does not. * chore: add a unit test, remove build tags * chore: address code review comments * chore: implement phase 7 (#2785) * chore: implement phase 7 Invariant defended: H3. The one question: can a rule be called alive because it looked alive one poll ago? proveCoverage (grafana-alertcheck/internal/gate/coverage.go) is the pure coverage function: nine checks over one rule's polls — sentinel, from-bounds, heartbeat continuity, health error/nodata, liveness, in-window pause, rule absence, KeepLast. Liveness is absolute, never a delta. Cross-domain comparisons translate by each poll's own skew and widen boundary segments by its skew bound, fail-closed. * chore: enhance unit tests * chore: address code review comments * chore: implement phase 8 (#2786) * chore: implement phase 8 Invariant defended: H6/H7. The one question: can a violation ever outrank an unobservable rule, or can a pass happen without Violations empty and err nil? Adds classify.go: the pure per-instance classifier (outcome table, preexisting policy, BadFor) and decide(), the seam combining proveCoverage with those timelines under one Policy. Consolidates rule-poll filtering and skew translation onto pollsForRule/runnerTime, shared with coverage.go. * chore: rename some vars + add unit tests * chore: address code review comments * chore: implement phase 9 (#2787) * chore: implement phase 9 Invariant defended: H5/H7. The one question: can check report a pass over a window it did not prove? Check() is the I/O shell around the pure decide(). Single-step synthesizes the header and its own sentinel, so no mode flag reaches the pure layer. Log mode stops the recorder before the one full read. The header, not a definition re-resolved after the window closed, is the authority for what was paused when the window opened — it decides `skipped`, the drain set, and the transitionGrace max. The flock, not the pidfile, is the authority for whether a writer still exists. * chore: remove unix build tag * chore: implement phase 10 (#2788) * Wire watch/check subcommands to the gate library, with a table+JSON renderer and H6/H7 exit-code mapping. Extend Result with per-rule/global thresholds and a real skew bound; export SkewHardLimit; reject --states normal. * chore: fix goreleaser.yaml and add version command * chore: implement phase 11 (#2789) * chore: implement phase 11 Add coverage.go's declared-KeepLast check (no_data_state/exec_err_state, not just an observed reason) and close the remaining §22 gaps: newly_bad's no-early-exit clock assertion, a recorder-mode gap right after the deploy, a rule's own coverage gap overriding its own recovery, a genuinely skew-discriminating staleness test, exit-2 consequences on two Reason-only coverage tests, and end-to-end checks for log-name collapse, a truncated log, and the real watch-written state histogram. * chore: address code review comments * chore: more concise comments (#2790) * chore: more concise comments * fix: merge conflict * chore: shorten comments * fix: resolve conflict * chore: use testify's require in tests (#2792) * chore: use testify's require in tests * chore: move remaining assumptions to testify * chore: address code review comments * chore: fix logging and std out printing (#2798) * chore: fix logging and std out printing * chore: truncate to seconds when comparing from time * chore: add centralized docs (#2802) * chore: add centralized docs * chore: further update docs * chore: address code review comments (#2807) * chore: address code review comments * chore: get rid of goreleaser
Tofel
added a commit
that referenced
this pull request
Sep 9, 2026
* chore: implement phase 1 Strict parsers for the state and ruler endpoints (H1), a Prometheus-style duration parser, and fixtures sliced from real Grafana 13.1.0 payloads covering every required/optional-field and must-error case, including the "Normal (NoData)"/"Normal (Error)" composite reason states found live in the current fleet capture (not in the original plan's vocabulary). * chore: apply code review comments * chore: implement phase 2 (#2780) * chore: implement phase 2 Fix retry-error conflation, measure full poll latency, and harden Source test doubles for concurrency. * chore: enhance unit tests * chore: address code review comments * chore: implement phase 3 (#2781) * chore: implement phase 3 Add Resolve() for alert name resolution (uid:/Title/Folder/Title/ Folder/Group/Title forms, UID collapse, no-match suggestions) and the grafana-alertcheck CLI's list subcommand, the first runnable piece of the gate. Incorporates review fixes: reject empty path segments in classifyForm, guard uid: against an empty suffix, scope the no-match rule count and suggestions to supported rule kinds only, and exit 0 on -h/--help. * chore: enhance unit tests * chore: implement phase 4 (#2782) * chore: implement phase 4 Add per-rule poll timings, scheduler, and budget check (P4). - schedule.go: DeriveTimings, Scheduler, CheckBudget (§5) - Address review: add Folder/Title resolve test, rename CheckBudget's minPollEvery to tightestUID * chore: enhance unit tests * chore: address code review comments * chore: implement phase 5 (#2783) * chore: implement phase 5 Add the JSONL evidence log (P5). - log.go: Header/Poll records, reduction, H2 transition markers, §3.2 verification, append-only Writer with flock, ReadLog - flock_unix.go: non-blocking exclusive lock, unix only - schedule.go: DeriveTimingsFromLog — log-mode cadence comes from the header, never from the definitions * chore: remove unix build tags * chore: address code review comments * chore: implement phase 6 (#2784) * chore: implement phase 6 Invariant defended: H2. The one question: can watch return success over a window that nothing is recording? Watch() records the first observation of each non-skipped rule, then detaches a child that polls at the cadence in the header. The parent returns only after the child reports ready on an inherited pipe, and writes the pidfile after that. A clean stop writes the sentinel; a hard error does not. * chore: add a unit test, remove build tags * chore: address code review comments * chore: implement phase 7 (#2785) * chore: implement phase 7 Invariant defended: H3. The one question: can a rule be called alive because it looked alive one poll ago? proveCoverage (grafana-alertcheck/internal/gate/coverage.go) is the pure coverage function: nine checks over one rule's polls — sentinel, from-bounds, heartbeat continuity, health error/nodata, liveness, in-window pause, rule absence, KeepLast. Liveness is absolute, never a delta. Cross-domain comparisons translate by each poll's own skew and widen boundary segments by its skew bound, fail-closed. * chore: enhance unit tests * chore: address code review comments * chore: implement phase 8 (#2786) * chore: implement phase 8 Invariant defended: H6/H7. The one question: can a violation ever outrank an unobservable rule, or can a pass happen without Violations empty and err nil? Adds classify.go: the pure per-instance classifier (outcome table, preexisting policy, BadFor) and decide(), the seam combining proveCoverage with those timelines under one Policy. Consolidates rule-poll filtering and skew translation onto pollsForRule/runnerTime, shared with coverage.go. * chore: rename some vars + add unit tests * chore: address code review comments * chore: implement phase 9 (#2787) * chore: implement phase 9 Invariant defended: H5/H7. The one question: can check report a pass over a window it did not prove? Check() is the I/O shell around the pure decide(). Single-step synthesizes the header and its own sentinel, so no mode flag reaches the pure layer. Log mode stops the recorder before the one full read. The header, not a definition re-resolved after the window closed, is the authority for what was paused when the window opened — it decides `skipped`, the drain set, and the transitionGrace max. The flock, not the pidfile, is the authority for whether a writer still exists. * chore: remove unix build tag * chore: implement phase 10 (#2788) * Wire watch/check subcommands to the gate library, with a table+JSON renderer and H6/H7 exit-code mapping. Extend Result with per-rule/global thresholds and a real skew bound; export SkewHardLimit; reject --states normal. * chore: fix goreleaser.yaml and add version command * chore: implement phase 11 (#2789) * chore: implement phase 11 Add coverage.go's declared-KeepLast check (no_data_state/exec_err_state, not just an observed reason) and close the remaining §22 gaps: newly_bad's no-early-exit clock assertion, a recorder-mode gap right after the deploy, a rule's own coverage gap overriding its own recovery, a genuinely skew-discriminating staleness test, exit-2 consequences on two Reason-only coverage tests, and end-to-end checks for log-name collapse, a truncated log, and the real watch-written state histogram. * chore: address code review comments * chore: more concise comments (#2790) * chore: more concise comments * fix: merge conflict * chore: shorten comments * fix: resolve conflict * chore: use testify's require in tests (#2792) * chore: use testify's require in tests * chore: move remaining assumptions to testify * chore: address code review comments * chore: fix logging and std out printing (#2798) * chore: fix logging and std out printing * chore: truncate to seconds when comparing from time * chore: add centralized docs (#2802) * chore: add centralized docs * chore: further update docs * chore: address code review comments (#2807) * chore: address code review comments * chore: get rid of goreleaser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds
Resolve()for alert-name resolution (uid:/Title/Folder forms, UID collapse, no-match suggestions) and the CLI'slistsubcommand — the first runnable piece of the gate.Review focus:
resolve.goresolution semantics;cmd/grafana-alertcheck/list.gowiring.