Skip to content

fix(telemetry): Service counters read true across pods and repos - #3680

Merged
dan-petty merged 17 commits into
mainfrom
fix/3495-service-counters-zero-series-repo-label
Nov 5, 2026
Merged

dan-petty merged 17 commits into
mainfrom
fix/3495-service-counters-zero-series-repo-label

Conversation

@dan-petty

Copy link
Copy Markdown
Owner

The Service counters gave a false picture. A label set had no series until its first count, so increase() lost that count on each new pod, and the failure counter mixed all repositories. The Service now creates each bounded label set of its counters at zero when it starts. The failure counter has the label repo, and the job counter has the label row and counts each row. RoadmapServiceJobsFailing groups and joins on the row, and a row that the stop of the Service ends counts under stopped. The webhook alerts lose their #2505 workaround terms. The documents state the scrape behavior and the labels, and the action of RoadmapServiceGitHubRequestsHigh no longer says to lower the budget. A round that fails before its first row stays out of this change: #3666 holds it.

Closes #3495

Change: openspec/changes/archive/2026-11-05-3495-service-counters-zero-series-repo-label/

Plan the zero-start label sets, the repo label of the failure counter, the row label of the job counter and the promtool cases as one change. Change: openspec/changes/3495-service-counters-zero-series-repo-label
The Service creates each failure class for each repository at zero when it starts, so increase() reads the first count of each pod. The failure counter adds the label repo, the round passes its repository down to the report.

Change: openspec/changes/3495-service-counters-zero-series-repo-label
The roadmap run counts each row of a lane with its repository, lane, row and result, and a scoped update counts its job. The worker no longer counts a whole lane batch. The Service primes the job and job seconds counters at zero for each repository and lane.

Change: openspec/changes/3495-service-counters-zero-series-repo-label
The Service creates each declared event of each provider, with other, and each outcome of the webhook deliveries counter at zero when its routes are built. The server test reads the series that count, not the series at zero.

Change: openspec/changes/3495-service-counters-zero-series-repo-label
The Service starts the webhook counter at zero, so the rejected and self delivery rules sum increase() without the #2505 offset terms. The promtool cases start the rejected series at zero and prove the true total of the failure counter over a pod change. The OTLP terms stay.

Change: openspec/changes/3495-service-counters-zero-series-repo-label
The telemetry document and the Service document state the zero series, the labels of the failure counter and the job counter, and the gap of a count before the first scrape. The action of the GitHub requests alert no longer says to lower the budget.

Change: openspec/changes/3495-service-counters-zero-series-repo-label
Archive the change folder and write the capability roadmap-service-counters into the main specs.

Change: openspec/changes/archive/2026-11-05-3495-service-counters-zero-series-repo-label
This reverts commit 923a0753b8cbce6ff4db154c29088470f4fa1d0b.
The trigger counter and the chat command counter had no series until
their first count. The Service now creates each label set of both
counters at zero when it starts.

The result values of the chat command are named constants. The chat
route and the priming use the same constants.

The spec lists these label sets in their own requirement. The identity
counter stays out of the list, as design.md records.
…at zero

The generator text of docs/TELEMETRY.md and the k8s README said that
each counter of the Service starts its label sets at zero. The identity
counter does not, so both texts now name it as the exception. The
telemetry page is regenerated from the generator.
The change folder moves to the archive, and its requirements merge
into the main spec of roadmap-service-counters. The main spec has a
Purpose that states the scrape behavior.
This reverts commit 58c06b28c22a41a0203f4c2c8a1089b8711bd814.
… failed row

A round now counts each of its rows. So a join of RoadmapServiceJobsFailing on the lane hides a row that fails while another row of its lane succeeds: the alert now groups and joins on the row. A row or a scoped update that the stop of the Service ends counts under result stopped, as a round did before, so a rollout does not fire the alert. The budget hold counter also starts at zero.
This reverts commit 5a26c38a746a40122171d4b27d3bb5dd2ff05aa3.
…ds them

k8s/README.md gave a false count of classes, and docs/ROADMAP_WORKFLOW.md did not name the repo label. The records of the change now name the true owner of the scoped count, the true priming site, the real promtool case and the window of the query. The tables that only counter_series.py reads get private names.
@dan-petty dan-petty added type/bug Defect, regression, or incorrect runtime behavior scope/telemetry OpenTelemetry tracing spans, Prometheus metrics, and Jaeger labels Nov 5, 2026
@dan-petty

Copy link
Copy Markdown
Owner Author

Results

Fail-to-pass (local). uv run devops ci fail-to-pass --base origin/main: 26 tests failed on the base (26 failed and 229 passed in tests/prometheus/test_k8s_monitoring_alerting.py, tests/roadmap/test_roadmap_implement_submit.py, tests/roadmap/test_roadmap_run.py, tests/server/test_server_service.py, tests/telemetry/test_service_counter_series.py; 0 errored).

Full CI (local). uv run devops ci ran on the commit before the archive, on top of a base one commit older than this head. The test row had 4 failures and 1 error, all in the known host failures that also fail on origin/main: tests/crypto/test_ssh.py, tests/telemetry/test_config_env_overrides.py, tests/commands/test_devcontainer_install_tools_dry_run.py, tests/ai/test_tool_executor_contract.py (#3585). 18894 tests passed. The test row took 7m 27s, over the 5m budget (#3593). The coverage row failed only because the test run failed: total coverage is 94.53%, above the 85.0% floor. All other rows passed.

After the archive and the last rebase. uv run devops openspec check passed, and tests/telemetry/test_service_counter_series.py with tests/prometheus/test_k8s_monitoring_alerting.py passed (58). The full CI did not run again on this head locally. The rebase brought in #3669, which touches none of the files of this change except text in docs/ROADMAP_WORKFLOW.md and openspec/specs/roadmap-implement-job/spec.md.

Review findings

All 6 findings are correct, and the branch fixes them.

  1. k8s/README.md, step 12: len(FailureClass) is 18, not 19. The sentence now gives no count and links to the table of classes, so it cannot go stale. The records of the change now say 18.
  2. docs/ROADMAP_WORKFLOW.md: the sentence now gives the labels class and repo. After the rebase it is at line 143.
  3. tasks.md: task 3.2 now names create_webhook_router in src/devops_cli/server/routes/webhooks.py, and task 4.1 gives the real name of the promtool case.
  4. design.md: the decision now says that run_scoped_update counts each scoped update, and that the worker counts no job.
  5. roadmap-service-counters spec: the requirement now uses [1h], the window of the promtool case, and the scenario gives the 5 counts plus the extrapolation to the window edges, about 5.03. The delta spec and the main spec agree.
  6. src/devops_cli/server/counter_series.py: the three tables are now _SERVICE_JOB_RESULTS, _SERVICE_TRIGGER_SOURCES and _SERVICE_CHAT_RESULTS.

Check after the deploy

Not run. The series change only after the deploy, so no live Prometheus query proves this change before the merge. Run these queries after the deploy:

# 18 for each repository of service.repos, in each pod.
count by (pod, repo) (devops_cli_service_implement_failures_total{job="roadmap-service"})

# Only values of service.repos.
count by (repo) (devops_cli_service_implement_failures_total{job="roadmap-service"})

# Each row of each lane, with the results success, error and stopped.
count by (lane, row) (devops_cli_service_jobs_total{job="roadmap-service"})

# The split of the failures by repository. Compare it with the stop comments over the same window.
sum by (repo, class) (increase(devops_cli_service_implement_failures_total{job="roadmap-service"}[24h]))

devops_cli_service_identity_refused_total has no series until the first refusal. This is its design, and it is not a failure of this check.

@dan-petty
dan-petty marked this pull request as ready for review November 5, 2026 18:45
@dan-petty

Copy link
Copy Markdown
Owner Author

Fail-to-pass check run of the head 6fb8ac7b: https://github.com/dan-petty/devops-cli/actions/runs/42086262362/job/127452066715. On this head, 21 check runs passed and 2 were skipped, so the full suite passed on the GitHub runner.

@dan-petty
dan-petty merged commit 05544ab into main Nov 5, 2026
23 checks passed
@dan-petty
dan-petty deleted the fix/3495-service-counters-zero-series-repo-label branch November 5, 2026 18:47
dan-petty pushed a commit that referenced this pull request Nov 5, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.63.0](v0.62.7...v0.63.0)
(2026-11-05)


### Features

* **roadmap:** let a negative search verify check more than one name
([#3678](#3678))
([5a4364c](5a4364c))
* **telemetry:** count the tool calls of the stage planner by outcome
([#3679](#3679))
([45da6dd](45da6dd))


### Bug Fixes

* **ci:** count tests and specs as hardening guardrails
([#3684](#3684))
([1d75506](1d75506))
* **security:** mask the user name of a URL with no password
([#3676](#3676))
([bc1cd9c](bc1cd9c))
* **service:** count each GitHub request with its HTTP status
([#3686](#3686))
([b39dcba](b39dcba))
* **telemetry:** Service counters read true across pods and repos
([#3680](#3680))
([05544ab](05544ab))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: dan-petty-release[bot] <346998822+dan-petty-release[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

scope/telemetry OpenTelemetry tracing spans, Prometheus metrics, and Jaeger type/bug Defect, regression, or incorrect runtime behavior

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(telemetry): Service counters read true across pods and repos

1 participant