Skip to content

fix: install summary reflects actual changes on no-op runs (closes #1557)#1569

Open
danielmeppiel wants to merge 2 commits into
mainfrom
bbs/fix-1557-noop-summary
Open

fix: install summary reflects actual changes on no-op runs (closes #1557)#1569
danielmeppiel wants to merge 2 commits into
mainfrom
bbs/fix-1557-noop-summary

Conversation

@danielmeppiel
Copy link
Copy Markdown
Collaborator

fix(install): report no-op installs as unchanged

TL;DR

This PR makes the final apm install summary reflect actual file mutations instead of cached dependency visits. A second install with identical deployed files now reports No changes -- install state already up to date rather than Installed 1 APM dependency. This closes #1557 and preserves the named-package --update selection surface by limiting the change to install-summary counters and rendering.

Note

Closes #1557. The regression test exercises the observable CLI summary, not an internal counter.

Problem (WHY)

  • A no-op reinstall printed (files unchanged) in the package detail but still ended with Installed 1 APM dependency, which contradicted the observable work performed.
  • Cached package materialization counted as installed even when no primitives were written, so the headline measured integration passes rather than mutations.
  • [!] The misleading headline weakens the DevX promise that install output should be trustworthy and scannable.

Why this matters: this is a CLI-output trust bug. The project skill guidance says, "Before reporting changes, check if anything actually changed. Don't report no-ops." The user-facing regression in #1557 was exactly that mismatch.

Approach (WHAT)

# Fix
1 Treat cached dependencies as installed only when they were fetched in this run or when integration writes actual primitives.
2 Treat root .apm/ integration as installed only when local primitives were deployed.
3 Add a zero-change final summary path that says the install state is already up to date.
4 Add an end-to-end observable CLI regression test for the second no-op install.

Implementation (HOW)

File Intent
src/apm_cli/install/sources.py Cached sources now seed installed from fetched_this_run, avoiding false positives when the package was only reused from cache.
src/apm_cli/install/template.py The integration template promotes a dependency to installed only when primitive counters show actual file writes.
src/apm_cli/install/phases/integrate.py Root project primitive integration now contributes to the installed count only when local content changed.
src/apm_cli/core/command_logger.py InstallLogger.install_summary() renders an explicit no-change summary when APM, MCP, and error counts are all zero.
tests/unit/install/test_noop_summary.py Adds a regression-trap CLI test that installs once, reinstalls without mutation, and asserts the rendered second summary is no-op text.

Diagrams

Legend: the changed path is the cached reinstall branch; the new summary depends on primitive mutation counts instead of dependency traversal.

flowchart LR
    subgraph Acquire[Acquire]
        A1[CachedDependencySource]
        A2[fetched_this_run flag]
    end
    subgraph Integrate[Integrate]
        I1[run_integration_template]
        I2[primitive mutation counters]
    end
    subgraph Render[Render]
        R1[InstallLogger.install_summary]
        R2[No changes summary]
    end
    A1 --> A2
    A2 --> I1
    I1 --> I2
    I2 --> R1
    R1 --> R2
    classDef new stroke-dasharray: 5 5;
    class A2,I2,R2 new;
Loading

Trade-offs

  • Counter semantics over contract expansion. Chose to reuse the existing installed delta field; rejected a new result contract to avoid coupling with concurrent install work.
  • No-op wording is generic. Chose No changes -- install state already up to date; rejected dependency-count wording because the current summary API does not receive the verified dependency total.
  • Unit-level end-to-end coverage. Chose a hermetic CliRunner test with a downloader stub; rejected a live GitHub dependency because tests must avoid network calls.

Benefits

  1. A no-op second install no longer emits Installed 1 APM dependency.
  2. The package detail (files unchanged) and final summary now agree.
  3. The regression trap fails if cached source counting is reverted to per-dependency traversal.
  4. Existing install summary, command logger, and install unit suites remain green.

Validation

Mutation-break gate:

$ uv run --locked --extra dev pytest tests/unit/install/test_noop_summary.py -x
FAILED tests/unit/install/test_noop_summary.py::test_second_noop_install_reports_no_changes_in_summary
AssertionError: assert 'Installed 1 APM dependency' not in ...

After restore:

$ uv run --locked --extra dev pytest tests/unit/install/test_noop_summary.py -x
1 passed in 3.15s
Relevant suite and lint output
$ uv run --locked --extra dev pytest tests/unit/install tests/unit/test_install_command.py -x
1587 passed in 14.79s

$ uv run --locked --extra dev ruff check --quiet src/ tests/

$ uv run --locked --extra dev ruff format --check --quiet src/ tests/

$ uv run --locked --extra dev python -m pylint --disable=all --enable=R0801 --min-similarity-lines=10 --fail-on=R0801 src/apm_cli/
Your code has been rated at 10.00/10 (previous run: 10.00/10, +0.00)

$ bash scripts/lint-auth-signals.sh
[*] Rule A: get_bearer_provider boundary (any reference)
[*] Rule B: git ls-remote auth-delegated annotation
[+] auth-signal lint clean

Scenario Evidence

# Scenario (user promise) Principle(s) Test(s) proving it Type
1 Run apm install twice with no second-run file mutations; the second summary reports no changes instead of a fresh install DevX (pragmatic as npm) tests/unit/install/test_noop_summary.py::test_second_noop_install_reports_no_changes_in_summary (regression-trap for #1557) unit

How to test

  • Run uv run --locked --extra dev pytest tests/unit/install/test_noop_summary.py -x and observe the regression test pass.
  • Run uv run --locked --extra dev pytest tests/unit/install tests/unit/test_install_command.py -x and observe the install unit suites pass.
  • Run uv run --locked --extra dev ruff check --quiet src/ tests/ and observe no output.
  • Run uv run --locked --extra dev ruff format --check --quiet src/ tests/ and observe no output.
  • Run bash scripts/lint-auth-signals.sh and observe [+] auth-signal lint clean.

Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 1, 2026 09:21
@danielmeppiel danielmeppiel added the status/shepherding Actively being driven by an APM shepherd run label Jun 1, 2026
@danielmeppiel danielmeppiel self-assigned this Jun 1, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adjusts apm install summary counting so the final headline reflects actual on-disk mutations (and not merely cached dependency traversal), and adds a regression test for the no-op reinstall case (closes #1557).

Changes:

  • Make cached dependencies contribute to the “installed” count only when fetched in this run or when integration actually writes primitives.
  • Make root-project .apm/ integration contribute to the “installed” count only when local primitives were deployed.
  • Render an explicit “No changes -- install state already up to date” final summary when nothing was installed and there were no errors, and add a CLI-facing regression test.
Show a summary per file
File Description
src/apm_cli/install/sources.py Seeds cached-source installed delta from fetched_this_run to avoid false-positive “installed” counts on cache reuse.
src/apm_cli/install/template.py Promotes a dependency to installed only when mutation counters indicate actual primitive writes.
src/apm_cli/install/phases/integrate.py Counts root-project integration as installed only when local primitives were deployed.
src/apm_cli/core/command_logger.py Adds a no-op final summary branch when APM/MCP/error counts are all zero.
tests/unit/install/test_noop_summary.py Adds a regression test asserting the second, no-op install does not claim a dependency was installed.

Copilot's findings

  • Files reviewed: 5/5 changed files
  • Comments generated: 1

Comment on lines +815 to +819
else:
_rich_info(
f"No changes -- install state already up to date{timing_suffix}.",
symbol="info",
)
Document why source-level installed deltas are promoted only when primitive counters report actual file mutations. This folds the panel follow-up on the install summary counter boundary.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@danielmeppiel
Copy link
Copy Markdown
Collaborator Author

APM Review Panel: ship_now

Fixes misleading install summary on no-op runs so repeat apm install reports zero changes instead of echoing stale dependency counts.

cc @danielmeppiel @sergio-sisternes-epam -- a fresh advisory pass is ready for your review.

All panel lenses converge: this is a straightforward correctness fix with no security, auth, or docs regression. The counter semantics now distinguish source traversal from real primitive mutation, and the regression test exercises the user-visible no-op reinstall path through the CLI.

Aligned with: pragmatic_as_npm: install output now mirrors observed filesystem state; oss_community_driven: closes community-filed #1557 with a regression test.

Panel summary

Persona B R N Takeaway
Python Architect 0 0 1 Counter semantics are sound; the data flow now distinguishes source traversal from primitive mutation.
CLI Logging Expert 0 0 1 The no-op summary is clear, ASCII-safe, and consistent with trustworthy CLI output.
DevX UX Expert 0 0 1 The fix aligns CLI output with the user's observed state on repeat installs.
Supply Chain Security Expert 0 0 0 The diff changes display counters only; no supply-chain integrity, token, or download trust boundary regresses.
OSS Growth Hacker 0 0 0 This is a trust-building CLI correctness fix with no launch or README impact.
Test Coverage Expert 0 0 1 The PR includes a strong CliRunner regression test for the no-op install path.

B = blocking-severity findings, R = recommended, N = nits.
Counts are signal strength, not gates. The maintainer ships.

Folded in this run

  1. [panel/python-architect] Added a one-line comment in src/apm_cli/install/template.py explaining that source-level install deltas are promoted only when primitive counters report actual changes. Resolved in cd205fcc279d1cba53ffce622b4ecddde783b7b1.

Copilot signals reviewed

  1. [NOT-LEGIT] Review 4399981809: Copilot posted an overview only and no inline actionable comments were present; no code change required.

Recommendation

Merge as-is. The regression test locks the user promise, the panel follow-up was folded, CI is green, and no security or breaking-change surface is touched.

Lint evidence

  • uv run --extra dev ruff check src/ tests/ -- passed with no diagnostics.
  • uv run --extra dev ruff format --check src/ tests/ -- passed; 1155 files already formatted.
  • uv run --extra dev python -m pylint --disable=all --enable=R0801 --min-similarity-lines=10 --fail-on=R0801 src/apm_cli/ -- 10.00/10.
  • bash scripts/lint-auth-signals.sh -- auth-signal lint clean.

CI evidence

  • GitHub status checks on cd205fcc279d1cba53ffce622b4ecddde783b7b1: 13/13 completed successfully.

Mergeability status

PR Head CEO stance Iterations Folds Deferrals Copilot rounds CI Mergeable Merge state Notes
#1569 cd205fc ship_now 1 1 0 2 green MERGEABLE BLOCKED pending required review

Full per-persona findings

Python Architect

  • [nit] Add a short comment near installed delta promotion at src/apm_cli/install/template.py:116
    The precedence between source-level installed deltas and integration-level mutation promotion spans sources.py and template.py; a one-line comment makes the intent easier to scan. Folded in cd205fcc279d1cba53ffce622b4ecddde783b7b1.

CLI Logging Expert

DevX UX Expert

  • [nit] Consider leading with already-up-to-date wording at src/apm_cli/core/command_logger.py:817
    Current wording is understandable and ASCII-safe; no fold needed because the exact phrasing already matches the PR body, tests, and issue scope.

Supply Chain Security Expert

No findings.

OSS Growth Hacker

No findings.

Auth Expert -- inactive

PR touches command_logger.py, integrate.py, sources.py, template.py, and a test file solely for install summary display logic; no authentication behavior, token management, credential resolution, or remote-host fallback semantics are affected.

Doc Writer -- inactive

All changes land in src/apm_cli/ and tests/unit/install/test_noop_summary.py; existing docs show first-install outputs that remain accurate and no no-op install output is documented.

Test Coverage Expert

  • [nit] Direct unit coverage for the zero-count install_summary branch is absent at tests/unit/test_command_logger.py
    The new no-op branch is exercised through the CliRunner install path, which is the right user-level trap. Additional unit coverage would be branch-local defense-in-depth, not necessary for this scoped bug fix.
    Proof (passed): tests/unit/install/test_noop_summary.py::test_second_noop_install_reports_no_changes_in_summary -- proves: a no-op reinstall reports no changes rather than claiming dependencies were installed.

This panel is advisory. It does not block merge. Re-apply the panel-review label after addressing feedback to re-run.

@danielmeppiel danielmeppiel removed the status/shepherding Actively being driven by an APM shepherd run label Jun 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] apm install summary reports "Installed N APM dependencies" even when no files changed

2 participants