feat: add pipeline failure rate tool for DORA CFR/MTTR #5

Merged
ldraney merged 1 commit from 4-pipeline-failure-data into main 2026-06-14 01:44:33 +00:00
Owner

Summary

Add get_pipeline_failure_rate MCP tool that aggregates pipeline success/failure counts over a configurable time window, enabling CFR and MTTR calculation from the agent side.

Changes

  • src/woodpecker_mcp/tools/dora.py -- new DORA tool module with get_pipeline_failure_rate tool and _fetch_all_pipelines pagination helper
  • src/woodpecker_mcp/tools/__init__.py -- register the dora module
  • tests/test_dora.py -- 11 unit tests with mocked SDK + 2 integration tests

Test Plan

  • 11 unit tests pass covering: basic counts, all-success, no-pipelines, pending exclusion, branch filter, event filter, recent failure limit, timestamp fields, killed-as-failure, pagination, response shape
  • 2 integration tests pass against live Woodpecker API
  • Full test suite (30 passed, 2 pre-existing skips)
  • ruff check and format clean
  • Forgejo issue: #4

🤖 Generated with Claude Code

## Summary Add `get_pipeline_failure_rate` MCP tool that aggregates pipeline success/failure counts over a configurable time window, enabling CFR and MTTR calculation from the agent side. ## Changes - `src/woodpecker_mcp/tools/dora.py` -- new DORA tool module with `get_pipeline_failure_rate` tool and `_fetch_all_pipelines` pagination helper - `src/woodpecker_mcp/tools/__init__.py` -- register the dora module - `tests/test_dora.py` -- 11 unit tests with mocked SDK + 2 integration tests ## Test Plan - [x] 11 unit tests pass covering: basic counts, all-success, no-pipelines, pending exclusion, branch filter, event filter, recent failure limit, timestamp fields, killed-as-failure, pagination, response shape - [x] 2 integration tests pass against live Woodpecker API - [x] Full test suite (30 passed, 2 pre-existing skips) - [x] ruff check and format clean ## Related - Forgejo issue: #4 🤖 Generated with [Claude Code](https://claude.com/claude-code)
New get_pipeline_failure_rate MCP tool that aggregates pipeline
success/failure counts over a configurable time window. Returns
structured data including change failure rate and recent failure
details with timestamps, enabling CFR and MTTR calculation from
the agent side.

Closes #4

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Author
Owner

PR #5 Review

DOMAIN REVIEW

Tech stack: Python / FastMCP / Pydantic / Woodpecker SDK -- same stack as the existing tool modules.

Pattern consistency with existing tools: The new dora.py module follows the established patterns from pipelines.py precisely:

  • Same import line: from ..server import _error_response, _ok, get_client, lookup_repo, mcp
  • Same @mcp.tool() decorator + Annotated[..., Field(...)] parameter style
  • Same try/except with _error_response(exc) error handling
  • Same lookup_repo() -> get_client() flow
  • Alphabetical insertion in __init__.py -- correct

Pagination helper (_fetch_all_pipelines): Well-structured, clean exit conditions (empty batch or partial page). Matches the pagination approach described in the existing codebase.

Client-side filtering (lines 82-85): The code fetches ALL pipelines in the time range and then filters by branch/event in Python, whereas the existing list_pipelines tool passes these filters to the SDK directly. The inline comment explains this is intentional ("keep the pagination logic simple"). For the first iteration this is acceptable, but note that repos with heavy pipeline traffic would fetch significantly more data than necessary.

CFR calculation: Correct. Excludes pending/running/blocked/declined from the denominator (reported as other_count), which is the right approach -- in-progress pipelines are not yet pass/fail.

Failure classification: killed counted as failure alongside failure and error -- reasonable and well-tested.

BLOCKERS

None. The code is correct, follows established patterns, has solid test coverage, and introduces no security risks.

NITS

  1. Unused before parameter in _fetch_all_pipelines (line 17): The function signature accepts before but the caller on line 74 never passes it. The function calculates now internally but only passes after_str. Either pass before from the caller for completeness, or remove it from the helper's signature to avoid misleading readers.

  2. No input validation on days parameter: A caller passing days=0 gets after=now (empty window). days=-1 sets after to the future. Neither is dangerous, but a days < 1 guard with a clear error message would be more user-friendly. Alternatively, Pydantic Field(ge=1) would catch this at the schema level.

  3. Sort key fallback (line 130): lambda f: f.get("created_at") or 0 -- the or 0 also triggers on created_at=0 (Unix epoch). This is practically impossible for real pipeline data, but f.get("created_at", 0) would be more precise and idiomatic.

  4. Missing error-path test: No unit test for what happens when lookup_repo raises an exception. The existing pipelines.py tools rely on the same try/except pattern so it is implicitly tested elsewhere, but a test_error_handling case in this module would make it self-contained.

  5. Missing edge-case test for recent_failure_limit=0: The limit is applied via failures[:0] which returns an empty list -- correct behavior, but worth an explicit test to document the intent.

  6. Performance at scale: The client-side filtering approach means a repo with 10,000 pipelines over 30 days downloads all of them even when filtering for a single branch. For a v1 this is fine, but consider passing branch/event to the SDK in a follow-up when pagination logic is revisited.

SOP COMPLIANCE

  • PR body has ## Summary, ## Changes, ## Test Plan, ## Related
  • No secrets or credentials committed
  • No unnecessary file changes (scope is tight: 3 files, all feature-related)
  • Tests exist: 11 unit tests + 2 integration tests (284 lines of test code for 149 lines of implementation)
  • Ruff clean per PR body

PROCESS OBSERVATIONS

  • Change failure risk: Low. New additive tool, no changes to existing code paths. The only modification to existing code is a 1-line import addition in __init__.py.
  • Documentation: The docstring on get_pipeline_failure_rate clearly explains the status categorization and DORA metric intent. Good for agent consumption.
  • Test quality: Tests are well-structured with clear names and assertions. The pagination test using side_effect for multi-page responses is particularly solid. Integration tests appropriately use pytest.skip for API errors.

VERDICT: APPROVED

Clean implementation that follows established patterns. Nits are all non-blocking improvements. Ship it.

## PR #5 Review ### DOMAIN REVIEW **Tech stack:** Python / FastMCP / Pydantic / Woodpecker SDK -- same stack as the existing tool modules. **Pattern consistency with existing tools:** The new `dora.py` module follows the established patterns from `pipelines.py` precisely: - Same import line: `from ..server import _error_response, _ok, get_client, lookup_repo, mcp` - Same `@mcp.tool()` decorator + `Annotated[..., Field(...)]` parameter style - Same try/except with `_error_response(exc)` error handling - Same `lookup_repo()` -> `get_client()` flow - Alphabetical insertion in `__init__.py` -- correct **Pagination helper (`_fetch_all_pipelines`):** Well-structured, clean exit conditions (empty batch or partial page). Matches the pagination approach described in the existing codebase. **Client-side filtering (lines 82-85):** The code fetches ALL pipelines in the time range and then filters by branch/event in Python, whereas the existing `list_pipelines` tool passes these filters to the SDK directly. The inline comment explains this is intentional ("keep the pagination logic simple"). For the first iteration this is acceptable, but note that repos with heavy pipeline traffic would fetch significantly more data than necessary. **CFR calculation:** Correct. Excludes pending/running/blocked/declined from the denominator (reported as `other_count`), which is the right approach -- in-progress pipelines are not yet pass/fail. **Failure classification:** `killed` counted as failure alongside `failure` and `error` -- reasonable and well-tested. ### BLOCKERS None. The code is correct, follows established patterns, has solid test coverage, and introduces no security risks. ### NITS 1. **Unused `before` parameter in `_fetch_all_pipelines` (line 17):** The function signature accepts `before` but the caller on line 74 never passes it. The function calculates `now` internally but only passes `after_str`. Either pass `before` from the caller for completeness, or remove it from the helper's signature to avoid misleading readers. 2. **No input validation on `days` parameter:** A caller passing `days=0` gets `after=now` (empty window). `days=-1` sets `after` to the future. Neither is dangerous, but a `days < 1` guard with a clear error message would be more user-friendly. Alternatively, Pydantic `Field(ge=1)` would catch this at the schema level. 3. **Sort key fallback (line 130):** `lambda f: f.get("created_at") or 0` -- the `or 0` also triggers on `created_at=0` (Unix epoch). This is practically impossible for real pipeline data, but `f.get("created_at", 0)` would be more precise and idiomatic. 4. **Missing error-path test:** No unit test for what happens when `lookup_repo` raises an exception. The existing `pipelines.py` tools rely on the same try/except pattern so it is implicitly tested elsewhere, but a `test_error_handling` case in this module would make it self-contained. 5. **Missing edge-case test for `recent_failure_limit=0`:** The limit is applied via `failures[:0]` which returns an empty list -- correct behavior, but worth an explicit test to document the intent. 6. **Performance at scale:** The client-side filtering approach means a repo with 10,000 pipelines over 30 days downloads all of them even when filtering for a single branch. For a v1 this is fine, but consider passing `branch`/`event` to the SDK in a follow-up when pagination logic is revisited. ### SOP COMPLIANCE - [x] PR body has ## Summary, ## Changes, ## Test Plan, ## Related - [x] No secrets or credentials committed - [x] No unnecessary file changes (scope is tight: 3 files, all feature-related) - [x] Tests exist: 11 unit tests + 2 integration tests (284 lines of test code for 149 lines of implementation) - [x] Ruff clean per PR body ### PROCESS OBSERVATIONS - **Change failure risk:** Low. New additive tool, no changes to existing code paths. The only modification to existing code is a 1-line import addition in `__init__.py`. - **Documentation:** The docstring on `get_pipeline_failure_rate` clearly explains the status categorization and DORA metric intent. Good for agent consumption. - **Test quality:** Tests are well-structured with clear names and assertions. The pagination test using `side_effect` for multi-page responses is particularly solid. Integration tests appropriately use `pytest.skip` for API errors. ### VERDICT: APPROVED Clean implementation that follows established patterns. Nits are all non-blocking improvements. Ship it.
Author
Owner

PR #5 Review

DOMAIN REVIEW

Tech stack: Python / FastMCP (MCP server) / Pydantic / pytest

New get_pipeline_failure_rate tool in src/woodpecker_mcp/tools/dora.py with supporting pagination helper and comprehensive test suite in tests/test_dora.py.

Pattern compliance -- PASS. The new tool follows every established codebase convention:

  • @mcp.tool() decorator with -> str return type
  • Annotated[type, Field(description="...")] parameter annotations
  • from ..server import _error_response, _ok, get_client, lookup_repo, mcp import line
  • from __future__ import annotations at module top
  • try/except wrapping with _error_response(exc) on failure, _ok(data) on success
  • Module docstring present and follows convention
  • Registration in __init__.py in alphabetical order with # noqa: F401
  • Response data is curated (not raw SDK dump) with clear field selection

Response structure quality -- GOOD. The output is well-designed for agent consumption:

  • counts.total, counts.success, counts.failure, counts.other -- clean aggregates
  • change_failure_rate -- pre-calculated CFR (saves agent from doing arithmetic)
  • recent_failures with created_at, started_at, finished_at timestamps -- enables MTTR calculation
  • time_range and filters echoed back -- self-documenting response
  • Zero-division guard on empty pipeline sets (returns 0.0, not an error)

Status categorization -- CORRECT. success={success}, failure={failure, error, killed}, excluded={pending, running, blocked, declined}. The killed -> failure mapping is the right call for DORA metrics.

Pagination helper _fetch_all_pipelines -- clean implementation, consistent with patterns elsewhere in the codebase.

Client-side filtering note: Branch/event filters are applied after fetching all pipelines. The inline comment explains the rationale (keeps pagination simple). This is fine for typical DORA windows (30-90 days). For repos with thousands of daily pipelines this could become slow, but that is a future optimization concern, not a blocker.

BLOCKERS

None.

NITS

  1. Module docstring word count: The docstring says "pipeline failure rate and recent failures for CFR/MTTR calculation" which accurately describes the single tool. If more DORA tools land in this module later (e.g., deployment frequency, lead time), the docstring will need updating. No action needed now.

  2. Sorting by created_at with fallback to 0: Line failures.sort(key=lambda f: f.get("created_at") or 0, reverse=True) -- the or 0 fallback means pipelines with created_at=None sort to the end. This is correct behavior but worth noting: if Woodpecker ever returns None for created_at, those entries will cluster at the tail of the list rather than raising. Acceptable.

SOP COMPLIANCE

  • PR body has ## Summary, ## Changes, ## Test Plan, ## Related
  • Tests exist: 11 unit tests + 2 integration tests (13 total)
  • No secrets, .env files, or credentials committed
  • No unnecessary file changes (3 files, all on-topic)
  • Commit messages reference the issue number
  • ruff check and format clean (per Test Plan)

PROCESS OBSERVATIONS

  • DORA enablement: This tool directly enables CFR (Change Failure Rate) calculation, one of the four DORA metrics. The structured output with timestamps also supports MTTR (Mean Time to Recovery) analysis from the agent layer.
  • Test coverage ratio: 434 lines added, 284 of which are tests (65% test-to-code ratio). Strong coverage with edge cases (zero pipelines, all success, killed status, pagination boundary).
  • Incremental scope: Pure addition, zero deletions, no breaking changes to existing tools. Clean additive change.

VERDICT: APPROVED

## PR #5 Review ### DOMAIN REVIEW **Tech stack:** Python / FastMCP (MCP server) / Pydantic / pytest New `get_pipeline_failure_rate` tool in `src/woodpecker_mcp/tools/dora.py` with supporting pagination helper and comprehensive test suite in `tests/test_dora.py`. **Pattern compliance -- PASS.** The new tool follows every established codebase convention: - `@mcp.tool()` decorator with `-> str` return type - `Annotated[type, Field(description="...")]` parameter annotations - `from ..server import _error_response, _ok, get_client, lookup_repo, mcp` import line - `from __future__ import annotations` at module top - try/except wrapping with `_error_response(exc)` on failure, `_ok(data)` on success - Module docstring present and follows convention - Registration in `__init__.py` in alphabetical order with `# noqa: F401` - Response data is curated (not raw SDK dump) with clear field selection **Response structure quality -- GOOD.** The output is well-designed for agent consumption: - `counts.total`, `counts.success`, `counts.failure`, `counts.other` -- clean aggregates - `change_failure_rate` -- pre-calculated CFR (saves agent from doing arithmetic) - `recent_failures` with `created_at`, `started_at`, `finished_at` timestamps -- enables MTTR calculation - `time_range` and `filters` echoed back -- self-documenting response - Zero-division guard on empty pipeline sets (returns 0.0, not an error) **Status categorization -- CORRECT.** success={success}, failure={failure, error, killed}, excluded={pending, running, blocked, declined}. The `killed` -> failure mapping is the right call for DORA metrics. **Pagination helper `_fetch_all_pipelines`** -- clean implementation, consistent with patterns elsewhere in the codebase. **Client-side filtering note:** Branch/event filters are applied after fetching all pipelines. The inline comment explains the rationale (keeps pagination simple). This is fine for typical DORA windows (30-90 days). For repos with thousands of daily pipelines this could become slow, but that is a future optimization concern, not a blocker. ### BLOCKERS None. ### NITS 1. **Module docstring word count:** The docstring says "pipeline failure rate and recent failures for CFR/MTTR calculation" which accurately describes the single tool. If more DORA tools land in this module later (e.g., deployment frequency, lead time), the docstring will need updating. No action needed now. 2. **Sorting by `created_at` with fallback to `0`:** Line `failures.sort(key=lambda f: f.get("created_at") or 0, reverse=True)` -- the `or 0` fallback means pipelines with `created_at=None` sort to the end. This is correct behavior but worth noting: if Woodpecker ever returns `None` for `created_at`, those entries will cluster at the tail of the list rather than raising. Acceptable. ### SOP COMPLIANCE - [x] PR body has ## Summary, ## Changes, ## Test Plan, ## Related - [x] Tests exist: 11 unit tests + 2 integration tests (13 total) - [x] No secrets, .env files, or credentials committed - [x] No unnecessary file changes (3 files, all on-topic) - [x] Commit messages reference the issue number - [x] ruff check and format clean (per Test Plan) ### PROCESS OBSERVATIONS - **DORA enablement:** This tool directly enables CFR (Change Failure Rate) calculation, one of the four DORA metrics. The structured output with timestamps also supports MTTR (Mean Time to Recovery) analysis from the agent layer. - **Test coverage ratio:** 434 lines added, 284 of which are tests (65% test-to-code ratio). Strong coverage with edge cases (zero pipelines, all success, killed status, pagination boundary). - **Incremental scope:** Pure addition, zero deletions, no breaking changes to existing tools. Clean additive change. ### VERDICT: APPROVED
ldraney deleted branch 4-pipeline-failure-data 2026-06-14 01:44:33 +00:00
Author
Owner

PR #5 Review

Post-merge review of feat: add pipeline failure rate tool for DORA CFR/MTTR.

DOMAIN REVIEW

Stack: Python / FastMCP / Woodpecker SDK / pytest

Pattern Conformance -- STRONG

The new dora.py module follows the established codebase patterns precisely:

  • Module docstring present and descriptive
  • from __future__ import annotations import
  • Correct imports from ..server: _error_response, _ok, get_client, lookup_repo, mcp
  • @mcp.tool() decorator with Annotated[type, Field(description="...")] parameter annotations
  • Return type str (JSON-formatted)
  • try/except Exception wrapping with _error_response(exc) for error handling
  • lookup_repo() used correctly for repo resolution
  • Curated response fields (not raw API passthrough)
  • Registered in __init__.py in alphabetical order

API Design -- SOLID

  • _fetch_all_pipelines pagination helper is clean: terminates on empty batch or partial page
  • Client-side filtering for branch/event is pragmatic (avoids complicating pagination logic with SDK filter params)
  • other_count for pending/running/blocked/declined is a smart design choice -- keeps CFR denominator clean
  • killed classified as failure is the correct DORA interpretation
  • Pre-computed change_failure_rate with round(cfr, 4) saves agents from doing math
  • recent_failures with timestamps (created_at, started_at, finished_at) enables MTTR calculation downstream

Test Coverage -- COMPREHENSIVE (11 unit + 2 integration)

Unit tests cover:

  • Basic success/failure counting
  • All-success edge case (CFR = 0.0)
  • No-pipelines edge case (CFR = 0.0, no div-by-zero)
  • Pending/running exclusion from rate denominator
  • Branch filtering
  • Event filtering
  • recent_failure_limit cap
  • Timestamp field presence on failure records
  • killed status classification
  • Pagination across multiple pages
  • Response schema shape validation

Integration tests:

  • Live API call with real repo
  • Branch filter against live data
  • Proper pytest.skip on API errors (graceful degradation)

BLOCKERS

None.

NITS

  1. Sorting by integer timestamps: failures.sort(key=lambda f: f.get("created_at") or 0, reverse=True) -- created_at from the Woodpecker API is a unix timestamp (int). The or 0 fallback handles None, but pipelines with created_at = 0 would sort unpredictably among nulls. This is unlikely in practice but a (f.get("created_at") or 0) could be (f.get("created_at") or -1) to push nulls to the end. Purely cosmetic.

  2. No before parameter passed to API: _fetch_all_pipelines(repo_id, after=after_str) is called without before, so technically it fetches everything after the start date including pipelines created after the now snapshot. For a tool called once this is fine, but if the tool were called in a tight loop with overlapping windows, the upper bound is slightly fuzzy. Not a real issue for DORA use cases.

  3. Module docstring specificity: Existing modules enumerate tool count (e.g., "Pipeline tools -- trigger, status, logs, restart, cancel, list (6 tools)"). The new module says "DORA tools -- pipeline failure rate and recent failures for CFR/MTTR calculation." -- this is descriptive but doesn't follow the (N tools) suffix convention. Consider: "DORA tools -- pipeline failure rate (1 tool).".

  4. Unit tests use @patch decorator stacking: The unit tests correctly mock both get_client and lookup_repo, but every test method repeats the same two @patch decorators. A class-level @patch or a pytest fixture could reduce duplication. Not a blocker -- the current approach is clear and explicit.

SOP COMPLIANCE

  • PR body follows template (Summary, Changes, Test Plan, Related present)
  • Related references issue #4
  • No secrets, .env files, or credentials committed
  • No unnecessary file changes (3 files, all on-target: new tool module, registration, tests)
  • Commit message is descriptive
  • Closes #4 not present in PR body (PR says Forgejo issue: #4 but does not use the Closes keyword for auto-close)
  • Branch name convention not verifiable from merged PR metadata (head_branch shows refs/pull/5/head)
  • Related Notes section does not reference a plan slug (no plan note exists for this work)

PROCESS OBSERVATIONS

  • Deployment frequency: This tool is foundational infrastructure for DORA measurement. Having pipeline failure data available via MCP enables automated CFR/MTTR dashboarding.
  • Change failure risk: LOW. Pure addition (434 lines added, 0 deleted). No changes to existing tools or server core. The only touch point is the import registration in __init__.py.
  • Documentation: The tool's docstring is thorough and explains the status categorization logic clearly. No separate docs needed for an MCP tool.
  • Test quality: The mock-based unit tests are well-isolated and cover edge cases that would be hard to test against a live API (zero pipelines, all success, pagination boundaries). Integration tests provide confidence the real API contract is met.

VERDICT: APPROVED

## PR #5 Review Post-merge review of `feat: add pipeline failure rate tool for DORA CFR/MTTR`. ### DOMAIN REVIEW **Stack:** Python / FastMCP / Woodpecker SDK / pytest **Pattern Conformance -- STRONG** The new `dora.py` module follows the established codebase patterns precisely: - Module docstring present and descriptive - `from __future__ import annotations` import - Correct imports from `..server`: `_error_response`, `_ok`, `get_client`, `lookup_repo`, `mcp` - `@mcp.tool()` decorator with `Annotated[type, Field(description="...")]` parameter annotations - Return type `str` (JSON-formatted) - `try/except Exception` wrapping with `_error_response(exc)` for error handling - `lookup_repo()` used correctly for repo resolution - Curated response fields (not raw API passthrough) - Registered in `__init__.py` in alphabetical order **API Design -- SOLID** - `_fetch_all_pipelines` pagination helper is clean: terminates on empty batch or partial page - Client-side filtering for branch/event is pragmatic (avoids complicating pagination logic with SDK filter params) - `other_count` for pending/running/blocked/declined is a smart design choice -- keeps CFR denominator clean - `killed` classified as failure is the correct DORA interpretation - Pre-computed `change_failure_rate` with `round(cfr, 4)` saves agents from doing math - `recent_failures` with timestamps (`created_at`, `started_at`, `finished_at`) enables MTTR calculation downstream **Test Coverage -- COMPREHENSIVE (11 unit + 2 integration)** Unit tests cover: - Basic success/failure counting - All-success edge case (CFR = 0.0) - No-pipelines edge case (CFR = 0.0, no div-by-zero) - Pending/running exclusion from rate denominator - Branch filtering - Event filtering - `recent_failure_limit` cap - Timestamp field presence on failure records - `killed` status classification - Pagination across multiple pages - Response schema shape validation Integration tests: - Live API call with real repo - Branch filter against live data - Proper `pytest.skip` on API errors (graceful degradation) ### BLOCKERS None. ### NITS 1. **Sorting by integer timestamps**: `failures.sort(key=lambda f: f.get("created_at") or 0, reverse=True)` -- `created_at` from the Woodpecker API is a unix timestamp (int). The `or 0` fallback handles None, but pipelines with `created_at = 0` would sort unpredictably among nulls. This is unlikely in practice but a `(f.get("created_at") or 0)` could be `(f.get("created_at") or -1)` to push nulls to the end. Purely cosmetic. 2. **No `before` parameter passed to API**: `_fetch_all_pipelines(repo_id, after=after_str)` is called without `before`, so technically it fetches everything after the start date including pipelines created after the `now` snapshot. For a tool called once this is fine, but if the tool were called in a tight loop with overlapping windows, the upper bound is slightly fuzzy. Not a real issue for DORA use cases. 3. **Module docstring specificity**: Existing modules enumerate tool count (e.g., `"Pipeline tools -- trigger, status, logs, restart, cancel, list (6 tools)"`). The new module says `"DORA tools -- pipeline failure rate and recent failures for CFR/MTTR calculation."` -- this is descriptive but doesn't follow the `(N tools)` suffix convention. Consider: `"DORA tools -- pipeline failure rate (1 tool)."`. 4. **Unit tests use `@patch` decorator stacking**: The unit tests correctly mock both `get_client` and `lookup_repo`, but every test method repeats the same two `@patch` decorators. A class-level `@patch` or a pytest fixture could reduce duplication. Not a blocker -- the current approach is clear and explicit. ### SOP COMPLIANCE - [x] PR body follows template (Summary, Changes, Test Plan, Related present) - [x] Related references issue #4 - [x] No secrets, .env files, or credentials committed - [x] No unnecessary file changes (3 files, all on-target: new tool module, registration, tests) - [x] Commit message is descriptive - [ ] `Closes #4` not present in PR body (PR says `Forgejo issue: #4` but does not use the `Closes` keyword for auto-close) - [ ] Branch name convention not verifiable from merged PR metadata (head_branch shows `refs/pull/5/head`) - [ ] Related Notes section does not reference a plan slug (no plan note exists for this work) ### PROCESS OBSERVATIONS - **Deployment frequency**: This tool is foundational infrastructure for DORA measurement. Having pipeline failure data available via MCP enables automated CFR/MTTR dashboarding. - **Change failure risk**: LOW. Pure addition (434 lines added, 0 deleted). No changes to existing tools or server core. The only touch point is the import registration in `__init__.py`. - **Documentation**: The tool's docstring is thorough and explains the status categorization logic clearly. No separate docs needed for an MCP tool. - **Test quality**: The mock-based unit tests are well-isolated and cover edge cases that would be hard to test against a live API (zero pipelines, all success, pagination boundaries). Integration tests provide confidence the real API contract is met. ### VERDICT: APPROVED
Sign in to join this conversation.
No description provided.