Skip to content

Count publish index-lookup downgrade events (#68) - #133

Merged
leynos merged 17 commits into
mainfrom
issue-68-downgrade-metrics
Jun 16, 2026
Merged

Count publish index-lookup downgrade events (#68)#133
leynos merged 17 commits into
mainfrom
issue-68-downgrade-metrics

Conversation

@leynos

@leynos leynos commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #68

  • New lading/utils/metrics.py: a process-local metrics accumulator with labelled counters, flushed as one structured JSON log line at interpreter exit (atexit); quiet runs emit nothing.
  • Backend choice (documented in the module docstring and docs/developers-guide.md): a lading invocation is a short-lived CLI process whose logs are already aggregated, so the summary log line is the operational boundary — prometheus_client/statsd would add a runtime dependency and a network target without a scraper. The in-process registry doubles as a deterministic test seam (counter_value, snapshot, reset).
  • The downgrade success path in _handle_index_missing_version increments publish.index_lookup_downgrade with subcommand and missing_crate labels.
  • Metric name and labels documented in the developers' guide.

Testing

  • Unit tests for the accumulator (per-label accumulation, label-order normalisation, snapshot/reset isolation, structured summary payload, silence without metrics).
  • tests/unit/publish/test_downgrade_metrics.py verifies the counter increments exactly once on the downgrade path and never on the raise paths (flag disabled; out-of-plan dependency).
  • make check-fmt, make lint, make typecheck, and make test (568 passed) all green after rebasing onto current main.
  • coderabbit review --agent: 0 findings.

🤖 Generated with Claude Code

Summary by Sourcery

Introduce a process-local metrics accumulator and track publish index-lookup downgrade events.

New Features:

  • Add an in-process metrics accumulator module that records labelled counters and emits a structured JSON summary log line at process exit.
  • Record a publish.index_lookup_downgrade counter when index-lookup failures are downgraded to warnings in the publish flow.

Documentation:

  • Document the in-process metrics system and the publish.index_lookup_downgrade metric in the developers guide.

Tests:

  • Add unit tests for the metrics accumulator’s behaviour and logging output.
  • Add publish-flow tests verifying that the downgrade path increments the counter and error paths do not.

@sourcery-ai

sourcery-ai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds a lightweight in-process metrics accumulator and uses it to track publish index-lookup downgrade events, documenting the behavior and covering it with targeted unit tests.

Sequence diagram for publish index-lookup downgrade metrics recording

sequenceDiagram
    participant PublishIndexCheck as publish_index_check
    participant Metrics as metrics
    participant Atexit as atexit
    participant Logger as logging

    PublishIndexCheck->>Metrics: increment_counter(INDEX_LOOKUP_DOWNGRADE_METRIC, subcommand, missing_crate)
    Note over PublishIndexCheck,Metrics: Called in _handle_index_missing_version downgrade path

    Atexit-->>Metrics: emit_summary()
    Metrics-->>Logger: _LOGGER.info("lading metrics summary: %s", json.dumps(rendered))
Loading

File-Level Changes

Change Details Files
Introduce a process-local metrics accumulator with labelled counters and structured summary logging.
  • Add a thread-safe in-process counter registry keyed by metric name and sorted label tuples
  • Provide helper APIs to increment counters, read specific counter values, snapshot and reset the registry
  • Log a single JSON-formatted metrics summary line at interpreter exit only when metrics are present, and export these functions as the public interface
lading/utils/metrics.py
Instrument the publish index-missing-version downgrade path to emit a labelled counter and document the metric.
  • Define a constant metric name for index-lookup downgrade events in the publish index check command module
  • Increment the downgrade counter with subcommand and missing_crate labels on the successful downgrade path in the index-missing handler
  • Extend the developers guide to describe the in-process metrics backend and the publish.index_lookup_downgrade metric and its labels
lading/commands/publish_index_check.py
docs/developers-guide.md
Add unit tests for the metrics accumulator and the index-lookup downgrade metric behavior.
  • Add tests verifying per-label accumulation, label-order normalization, snapshot/reset behavior, and structured summary logging with silence on empty metrics
  • Add tests that drive the index-missing handler to ensure the downgrade path increments the labelled counter once and that error paths do not increment it
tests/unit/utils/test_metrics.py
tests/unit/publish/test_downgrade_metrics.py

Assessment against linked issues

Issue Objective Addressed Explanation
#68 Introduce a lightweight metrics backend suitable for the CLI (e.g., in-process accumulator) and integrate it into the codebase.
#68 Emit a counter increment each time _handle_index_missing_version downgrades an index-lookup failure to a warning, labelling the metric with the cargo subcommand and the missing crate name, and document the metric.
#68 Add unit tests verifying that the downgrade counter increments on the downgrade path and does not increment on the raise paths.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Add a process-local, thread-safe metrics accumulator with support for counters and duration observations; register a one-time atexit emitter in the CLI; instrument publish index-lookup downgrade success and lockfile operations to increment labelled counters; and add comprehensive unit, property and end-to-end tests, snapshots and documentation covering the accumulator and instrumentation points.

Changes

Observability metrics for publish index-lookup downgrades and lockfile operations

Layer / File(s) Summary
Metrics module: counter and duration accumulator
lading/utils/metrics.py
Implement a thread-safe, process-local counter and duration registry with deterministic label-based keying, increment_counter and counter_value for counters, observe_duration and duration_stats for durations, snapshot and reset for test isolation, emit_summary for structured JSON exit-time logging, and register_summary_atexit for idempotent hook registration.
Metrics test coverage: unit, property, and snapshot
tests/unit/utils/test_metrics.py, tests/unit/utils/test_metrics_properties.py, tests/unit/utils/__snapshots__/test_metrics.ambr
Add unit tests for per-label-set accumulation and label-order normalisation; Hypothesis property tests for label-permutation invariance, accumulation-summation correctness, and snapshot isolation; log-capture tests for structured summary emission and silence when empty; update snapshot for emitted JSON payload.
Publish downgrade: metric instrumentation and tests
lading/commands/publish_index_check.py, tests/unit/publish/test_downgrade_metrics.py
Define INDEX_LOOKUP_DOWNGRADE_METRIC constant, refactor _validate_dependency_placement to return structured _DependencyPlacement tuple, extract downgrade-success handling into _emit_downgrade_success helper that increments counter with subcommand and missing_crate labels and emits logs. Add unit tests asserting counter increments on downgrade path only, and end-to-end test verifying emitted summary payload.
Lockfile operations: discovery, refresh, and validation metrics
lading/commands/lockfile.py, tests/unit/test_lockfile.py
Add metric name constants and instrument discover_tracked_lockfiles to count discoveries, refresh_lockfile to record duration and outcome (success/failure), and validate_lockfile_freshness to record duration and outcome (fresh/stale/failed). Add unit tests verifying counter and duration observations for all outcomes.
CLI bootstrap and registration
lading/cli.py
Import metrics module and call register_summary_atexit() during main() initialisation to emit accumulated metrics summary at process exit.
Architecture decision and documentation updates
docs/adr/004-in-process-metrics-backend.md, docs/contents.md, docs/developers-guide.md, docs/users-guide.md
Add ADR-004 documenting the in-process metrics accumulator design, registration contract, test seams, and label semantics. Update contents index. Rewrap linting workflow and downgrade descriptions for readability in developer guide. Add "In-process metrics" section documenting accumulator, test seams and available metrics. Extend user guide with observability note and new "Observability" section defining publish.index_lookup_downgrade metric with increment semantics and labels.
Build configuration: ruff version pinning across toolchains
pyproject.toml, .github/workflows/ci.yml, Makefile
Pin ruff==0.15.12 in dev dependencies, CI workflow, and Makefile to prevent lint-rule version skew between local and CI environments. Wire Makefile targets to invoke ruff via uv tool run with the pinned version.

Sequence Diagram(s)

sequenceDiagram
  participant PublishFlow as publish.run
  participant IndexCheck as _handle_index_missing_version
  participant Metrics as increment_counter
  participant Registry as _counters
  participant Exit as process exit
  PublishFlow->>IndexCheck: CargoIndexLookupFailure (beta crate)
  IndexCheck->>Metrics: increment_counter("publish.index_lookup_downgrade", subcommand="package", missing_crate="beta")
  Metrics->>Registry: update counter under lock
  PublishFlow->>PublishFlow: complete publish run
  Exit->>Metrics: emit_summary()
  Metrics->>Registry: read counters and durations
  Metrics->>Exit: log "lading metrics summary" JSON with downgrade count
Loading

Possibly related issues

Possibly related PRs

  • leynos/cuprum#167 — Overlaps on Makefile/CI/dev-guide updates that pin and invoke Ruff via locked uv run environment wiring.

Suggested reviewers

  • codescene-delta-analysis

Poem

A counter wakes as code departs,
Labelled whispers, in metric parts.
When index fails but plans allow,
Lockfiles turn and validate now—
At exit they sing one JSON line: observability, refined. 📊

🚥 Pre-merge checks | ✅ 19 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Developer Documentation ⚠️ Warning The new RUFF_VERSION Makefile variable introduced to pin Ruff across dev/CI/local tooling is undocumented in the developers guide; similar variables like PYLINT_PYPY_SHIM_REF are documented in... Add RUFF_VERSION to the Makefile variables list in docs/developers-guide.md with a description matching the inline Makefile comment explaining version pinning rationale.
✅ Passed checks (19 passed)
Check name Status Explanation
Title check ✅ Passed The PR title directly references issue #68 and accurately summarizes the main change: introducing metrics counting for publish index-lookup downgrade events.
Description check ✅ Passed The PR description comprehensively explains the metrics accumulator implementation, backend rationale, downgrade path instrumentation, and testing strategy, all aligned with the changeset.
Linked Issues check ✅ Passed The PR fully satisfies issue #68 requirements: implements an in-process metrics backend, increments a counter on downgrade success with subcommand and missing_crate labels, and includes unit tests verifying counter behaviour on all paths.
Out of Scope Changes check ✅ Passed All changes align with issue #68 scope: metrics system implementation, publish flow instrumentation, comprehensive testing, documentation, and ruff version pinning to eliminate lint-rule skew across tooling.
Docstring Coverage ✅ Passed Docstring coverage is 92.45% which is sufficient. The required threshold is 80.00%.
Testing (Overall) ✅ Passed Tests are substantive and rigorous. Unit tests for the metrics module verify counter accumulation, label-order normalisation, snapshot isolation, zero-amount guards, and proper JSON serialisation....
User-Facing Documentation ✅ Passed New user-facing metrics functionality is comprehensively documented in docs/users-guide.md with an Observability section explaining the JSON summary output, metric name, labels, and conditions; doc...
Module-Level Documentation ✅ Passed All 7 modules (4 production, 3 test) carry module-level docstrings clearly explaining purpose, utility, relationships, and design rationale. Examples include design decisions (metrics backend choic...
Testing (Unit And Behavioural) ✅ Passed Test coverage comprehensively validates unit behaviour, edge cases, error paths, invariants, and end-to-end workflows. 13 unit tests verify metrics accumulation, label-order normalisation, zero-amo...
Testing (Property / Proof) ✅ Passed The PR introduces substantive property-based tests via Hypothesis validating three non-obvious invariants: label-order invariance (permutations address same counter), accumulation correctness (inte...
Testing (Compile-Time / Ui) ✅ Passed Snapshot tests for structured JSON metrics output are well-designed: 2 focused syrupy tests with meaningful docstrings, deterministic serialisation via sorted labels/counters, and small semantic as...
Unit Architecture ✅ Passed Metrics module maintains clear separation: infallible, thread-safe accumulator with deterministic test seams; explicit CLI bootstrap registration; properly documented side-effects in workflow comma...
Domain Architecture ✅ Passed PR maintains strict domain architecture: metrics module (infrastructure) has zero domain imports; domain models (CargoIndexLookupFailure, PublishPlan) have zero metrics imports; metrics calls occur...
Observability ✅ Passed PR introduces bounded in-process metrics with decision-point logging, documented architecture (ADR-004), clear metric names/labels, and comprehensive test coverage; "quiet runs stay quiet" contract...
Security And Privacy ✅ Passed Metrics collect only non-sensitive operational data (crate names from manifests, Cargo subcommands, timing, outcome enums); no secrets, environment variables, command output, or user input leak int...
Performance And Resource Use ✅ Passed Metrics implementation avoids algorithmic regressions, unbounded memory growth, and blocking work on hot paths. Memory bounded by ~500 max distinct metric keys; CPU overhead negligible (~1-10 µs pe...
Concurrency And State ✅ Passed Concurrency model is explicit and well-tested. Shared state (_COUNTERS, _DURATIONS) protected by single threading.Lock(); all 8 public API functions acquire lock with narrow critical sections (1-13...
Architectural Complexity And Maintainability ✅ Passed The abstraction reduces net complexity by eliminating duplication (label sorting, quiet-run logic, JSON serialisation) across multiple call sites and has a credible, already-demonstrated reuse stra...
Rust Compiler Lint Integrity ✅ Passed The check is not applicable. This PR modifies only Python source, tests, documentation, and configuration—no Rust source files (.rs) are changed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-68-downgrade-metrics

Comment @coderabbitai help to get the list of available commands and usage tips.

codescene-delta-analysis[bot]

This comment was marked as outdated.

codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos

leynos commented Jun 10, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai Please suggest a fix for this issue and supply a prompt for an AI coding agent to enable it to apply the fix. Include the file and symbol names indicated in the issue at the head of your response.

lading/commands/publish_index_check.py

Comment on lines +329 to +333

    metrics.increment_counter(
        INDEX_LOOKUP_DOWNGRADE_METRIC,
        subcommand=failure.subcommand,
        missing_crate=missing_name,
    )

❌ New issue: Large Method
_handle_index_missing_version has 70 lines, threshold = 70

@coderabbitai

This comment was marked as resolved.

codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos
leynos marked this pull request as ready for review June 10, 2026 18:06
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @leynos, you have reached your weekly rate limit of 2500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai coderabbitai Bot added the Issue label Jun 10, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lading/utils/metrics.py`:
- Line 32: Replace the assignment-style type alias for _CounterKey with the PEP
695 form: change the current "_CounterKey = tuple[str, tuple[tuple[str, str],
...]]" alias to use the new "type" keyword (i.e., declare the alias using "type
_CounterKey = ...") so it targets Python 3.13 and improves IDE/runtime support;
update any imports or annotations that reference _CounterKey if necessary.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 2890e4ef-580a-43a0-8627-e900f30db5aa

📥 Commits

Reviewing files that changed from the base of the PR and between 8537f34 and 37a56e9.

📒 Files selected for processing (5)
  • docs/developers-guide.md
  • lading/commands/publish_index_check.py
  • lading/utils/metrics.py
  • tests/unit/publish/test_downgrade_metrics.py
  • tests/unit/utils/test_metrics.py

Comment thread lading/utils/metrics.py Outdated
codescene-delta-analysis[bot]

This comment was marked as outdated.

@leynos

This comment was marked as resolved.

@leynos

leynos commented Jun 10, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews resumed.

@coderabbitai

This comment was marked as resolved.

@leynos

leynos commented Jun 10, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai pause

@coderabbitai

coderabbitai Bot commented Jun 10, 2026

Copy link
Copy Markdown
✅ Action performed

Reviews paused.

@lodyai

lodyai Bot commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai resume

codescene-delta-analysis[bot]

This comment was marked as outdated.

codescene-delta-analysis[bot]

This comment was marked as outdated.

@coderabbitai

This comment was marked as resolved.

@leynos

leynos commented Jun 11, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

coderabbitai[bot]

This comment was marked as resolved.

The downgrade decision in _handle_index_missing_version (a crates.io
index-lookup failure downgraded to a warning because the missing
dependency is part of the publish plan) was visible only in logs, so
operators could not track how often releases rely on the override.

Add lading.utils.metrics: a process-local accumulator with labelled
counters flushed as one structured JSON log line at interpreter exit.
The deliberate backend choice over prometheus_client or statsd is
documented in the module docstring and developers' guide: a lading run
is a short-lived CLI process whose logs are already aggregated, so the
summary line is the operational boundary and the registry gives tests
a deterministic seam.

The downgrade success path increments
publish.index_lookup_downgrade with subcommand and missing_crate
labels. Unit tests cover the accumulator itself and verify the counter
increments exactly once on the downgrade path and never on the raise
paths.

Closes #68
@leynos

leynos commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai resume

@coderabbitai

This comment was marked as resolved.

- users-guide.md: the Observability lead-in scoped the metrics summary to
  `lading publish`, but register_summary_atexit is wired from CLI bootstrap and
  flushes process-wide for any command. Reword to "When `lading` runs" and
  describe a process-wide flush, keeping publish.index_lookup_downgrade as a
  concrete example.

- lockfile.py: guard the discovered-lockfiles counter with `if lockfiles:`. A
  zero-amount increment created a 0-valued counter key, which made an otherwise
  silent run emit an exit summary; quiet runs now stay quiet. Add an assertion
  to test_discover_tracked_lockfiles_returns_empty_result confirming no metric
  is recorded on empty discovery.

- test_downgrade_metrics.py: add `match=` patterns to the parametrised
  pytest.raises so the flag-disabled and out-of-plan paths assert their distinct
  error messages, not just the exception type.

- test_metrics.py: remove the three @given properties duplicated from the
  canonical suite in test_metrics_properties.py (label-order invariance,
  accumulation, snapshot isolation); example-based coverage of these invariants
  remains in this file. Drop the now-unused hypothesis/operator imports.

- test_metrics.py: add a syrupy snapshot test covering counters and duration
  aggregates together, locking the combined exit-summary serialisation.

The "register under concurrency with ThreadPoolExecutor" suggestion is already
covered by test_register_summary_atexit_registers_once_under_concurrency, which
releases eight threads on a barrier and asserts a single registration; not
duplicated. missing_crate cardinality is intentionally left verbatim per
ADR-004.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
codescene-delta-analysis[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as resolved.

@leynos

leynos commented Jun 14, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai resume

@coderabbitai

This comment was marked as resolved.

Address documentation review findings (all verified against the current code):

- docs/adr/004-in-process-metrics-backend.md: the ADR template in
  documentation-style-guide.md requires sections in the order Status, Date
  (YYYY-MM-DD), Context and problem statement. Add the missing `## Date`
  (2026-06-14, the ADR's creation date) and rename `## Context` to
  `## Context and problem statement`.

- docs/developers-guide.md: add `duration_stats` to the lading.utils.metrics
  test-seam list (it is a public, query-only function in __all__ alongside
  counter_value, snapshot, and reset).

- Makefile: add a comment above RUFF_VERSION explaining that the pin must stay
  in sync with the ruff== dev dependency in pyproject.toml and the
  uv tool install ruff== step in CI, and that all three must be bumped together
  to avoid version-skew lint failures.

Skipped: the same Date/heading deviations exist in ADR-003 but are pre-existing
and outside this review's scope, so ADR-003 is left unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
codescene-delta-analysis[bot]

This comment was marked as outdated.

coderabbitai[bot]

This comment was marked as resolved.

Make "quiet runs stay quiet" robust at the source: increment_counter now
returns early when amount == 0, so a zero increment never records a 0-valued
key (a Counter with a 0-valued entry is truthy, which would otherwise make
emit_summary surface a spurious metric). This generalises the call-site guard
already present in discover_tracked_lockfiles to every caller. Add a regression
test asserting a zero-amount increment records nothing.

Other findings in the review were verified and skipped:
- users-guide.md `#### publish.index_lookup_downgrade`: already correctly nested
  one level under `### Observability` (h3 -> h4); markdownlint MD001 passes.
  Demoting to `###` would make it a sibling of Observability and break the
  nesting, so it is left as-is.
- docs/adr/004: the `## Date` and `## Context and problem statement` headings
  were already added in a previous commit, so no change is needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
codescene-delta-analysis[bot]

This comment was marked as outdated.

@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai Have the following now been resolved?

lading/utils/metrics.py (1)

54-63: ⚠️ Potential issue | 🟡 Minor
Skip zero-amount increments to preserve quiet runs.
When increment_counter is called with amount=0, the entry is still recorded in _COUNTERS with a zero value. Since collections.Counter instances with zero-valued entries are not falsy, the check in emit_summary() (if not _COUNTERS) evaluates to True, and the zero-valued metric surfaces in the output—breaking the "quiet runs stay quiet" contract. Return early before acquiring the lock when amount == 0, and add a regression test to lock this invariant.

Proposed patch
def increment_counter(name: str, *, amount: int = 1, **labels: str) -> None:
     """Increment the counter ``name`` for the supplied label values.
 
     Examples
     --------
     >>> increment_counter("demo.total", subcommand="package")
     """
+    if amount == 0:
+        return
     with _LOCK:
         _COUNTERS[_counter_key(name, labels)] += amount
🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @lading/utils/metrics.py around lines 54 - 63, The increment_counter
function records zero-valued entries in _COUNTERS which prevents
emit_summary() from correctly detecting quiet runs. Add an early return check
at the beginning of the increment_counter function (before the with _LOCK:
statement) that returns immediately when amount == 0, so zero-valued metrics
are never recorded in _COUNTERS. Additionally, add a regression test to verify
that calling increment_counter with amount=0 does not create entries in the
counters dictionary and that quiet runs remain quiet.

docs/users-guide.md (1)

311-319: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win
Demote the metric subheading to level 3.
Change #### publish.index_lookup_downgrade to ### publish.index_lookup_downgrade so the Markdown heading hierarchy advances by one level only.
As per coding guidelines, Markdown headings must increase or decrease by no more than one level at a time. Triage: [type:syntax/md]

Suggested fix
-#### publish.index_lookup_downgrade
+### publish.index_lookup_downgrade
🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @docs/users-guide.md around lines 311 - 319, The Markdown heading for the
metric publish.index_lookup_downgrade currently uses four hash marks (####),
creating a level 4 heading, which violates the rule that heading levels must
increase or decrease by no more than one level at a time. Change the heading
from #### publish.index_lookup_downgrade to ### publish.index_lookup_downgrade to demote it to a level 3 heading and maintain
proper Markdown heading hierarchy.

Source: Coding guidelines

docs/adr/004-in-process-metrics-backend.md (1)

3-7: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win
Restore the ADR template metadata.
Add a ## Date section after ## Status and rename ## Context to ## Context and problem statement so the ADR matches the repository template.
As per coding guidelines, accepted ADRs must include a Date section and the full Context and problem statement heading. Triage: [type:docstyle]

Suggested fix
## Status
 
 Accepted.
 
-## Context
+## Date
+
+2026-06-14
+
+## Context and problem statement
🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @docs/adr/004-in-process-metrics-backend.md around lines 3 - 7, The ADR file
is missing required template metadata sections. After the ## Status section
with the "Accepted." content, add a new ## Date section (with an appropriate
date value). Additionally, rename the existing ## Context heading to ## Context and problem statement to align with the repository's ADR template
standard.

Source: Coding guidelines

@pandalump

Copy link
Copy Markdown
Collaborator

@coderabbitai Have the following now been resolved?

docs/developers-guide.md (1)

615-622: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win
Document duration_stats alongside the counter seams.
The seam list names counter_value, snapshot, and reset, but omits duration_stats, which is part of the same test contract.
Triage: [type:docstyle]
As per coding guidelines, maintainer-facing API docs should stay aligned with the documented metrics helpers.

🤖 Detailed instructions

Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @docs/developers-guide.md around lines 615 - 622, The documentation for the
lading.utils.metrics module in the developers guide incompletely describes the
available test seams. Currently it lists counter_value, snapshot, and reset, but
omits duration_stats which is part of the same test contract. Update the seams
list in the paragraph describing lading.utils.metrics to include duration_stats
alongside the other seams (counter_value, snapshot, and reset) to keep the
documentation aligned with the actual API contract.

Source: Coding guidelines

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
echo "=== docs/developers-guide.md lines 600-635 ==="
sed -n '600,635p' docs/developers-guide.md

echo ""
echo "=== Search for duration_stats in developers-guide.md ==="
rg -n "duration_stats\|counter_value\|snapshot\|reset" docs/developers-guide.md | head -30

Length of output: 3392



@pandalump Yes, this is resolved. The relevant sentence in docs/developers-guide.md currently reads:

Tests use counter_value, duration_stats, snapshot, and reset as deterministic seams.

duration_stats is already present alongside the other three seams. No further work is required.

@coderabbitai

This comment was marked as resolved.

Lock the "quiet runs stay quiet" invariant for increment_counter(amount=0):
- test_zero_amount_increment_is_a_noop: a zero-amount increment creates no
  registry entry (counter_value is 0, snapshot is empty).
- test_zero_amount_increment_does_not_break_quiet_run: emit_summary stays
  silent when only zero-amount increments occurred.

These replace the earlier test_increment_counter_ignores_zero_amount, which
asserted the same no-op invariant; consolidating avoids duplicate coverage and
keeps the canonical test names and placement requested in review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
codescene-delta-analysis[bot]

This comment was marked as outdated.

The RUFF_VERSION and RUFF Makefile variables that pin Ruff were undocumented in
the developers guide, unlike the comparable PYLINT_* variables. Add them to the
"relevant Makefile variables" list, mirroring the inline Makefile comment:
RUFF_VERSION is the pinned version (kept in sync with the pyproject.toml dev
dependency and the CI install, bumped together to avoid version-skew lint
failures), and RUFF is the uv-tool-run invocation the fmt, check-fmt, and lint
targets use.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@leynos
leynos merged commit 94fcd31 into main Jun 16, 2026
3 checks passed
@leynos
leynos deleted the issue-68-downgrade-metrics branch June 16, 2026 18:55

@codescene-delta-analysis codescene-delta-analysis Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gates Passed
6 Quality Gates Passed

See analysis details in CodeScene

Absence of Expected Change Pattern

  • lading/lading/cli.py is usually changed with: lading/tests/unit/test_cli.py

Quality Gate Profile: Pay Down Tech Debt
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.

lodyai Bot pushed a commit that referenced this pull request Jun 16, 2026
Rebasing onto origin/main auto-merged the metrics atexit registration
(#133) into cli.main, adjacent to this branch's argument-declaration
extraction. The combined hunks left fewer than two blank lines before the
following module-level `bump` command definition; ruff format restores the
spacing. Whitespace only, no behaviour change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lodyai Bot pushed a commit that referenced this pull request Jul 7, 2026
Rebasing onto origin/main auto-merged the metrics atexit registration
(#133) into cli.main, adjacent to this branch's argument-declaration
extraction. The combined hunks left fewer than two blank lines before the
following module-level `bump` command definition; ruff format restores the
spacing. Whitespace only, no behaviour change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add observability metrics for publish index-lookup downgrade events

2 participants