fix: install summary reflects actual changes on no-op runs (closes #1557)#1569
fix: install summary reflects actual changes on no-op runs (closes #1557)#1569danielmeppiel wants to merge 2 commits into
Conversation
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
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
| 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>
APM Review Panel:
|
| 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
- [panel/python-architect] Added a one-line comment in
src/apm_cli/install/template.pyexplaining that source-level install deltas are promoted only when primitive counters report actual changes. Resolved incd205fcc279d1cba53ffce622b4ecddde783b7b1.
Copilot signals reviewed
- [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 incd205fcc279d1cba53ffce622b4ecddde783b7b1.
CLI Logging Expert
- [nit] The regression test over-asserts on detail wording at
tests/unit/install/test_noop_summary.py:128
The summary contract is the no-change headline; asserting on(files unchanged)couples this test to detail wording. The shepherd left this unchanged because the package-detail line is also observable evidence for [BUG]apm installsummary reports "Installed N APM dependencies" even when no files changed #1557 and does not weaken the summary trap.
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.
fix(install): report no-op installs as unchanged
TL;DR
This PR makes the final
apm installsummary reflect actual file mutations instead of cached dependency visits. A second install with identical deployed files now reportsNo changes -- install state already up to daterather thanInstalled 1 APM dependency. This closes #1557 and preserves the named-package--updateselection 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)
(files unchanged)in the package detail but still ended withInstalled 1 APM dependency, which contradicted the observable work performed.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)
.apm/integration as installed only when local primitives were deployed.Implementation (HOW)
src/apm_cli/install/sources.pyinstalledfromfetched_this_run, avoiding false positives when the package was only reused from cache.src/apm_cli/install/template.pyinstalledonly when primitive counters show actual file writes.src/apm_cli/install/phases/integrate.pysrc/apm_cli/core/command_logger.pyInstallLogger.install_summary()renders an explicit no-change summary when APM, MCP, and error counts are all zero.tests/unit/install/test_noop_summary.pyDiagrams
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;Trade-offs
installeddelta field; rejected a new result contract to avoid coupling with concurrent install work.No changes -- install state already up to date; rejected dependency-count wording because the current summary API does not receive the verified dependency total.CliRunnertest with a downloader stub; rejected a live GitHub dependency because tests must avoid network calls.Benefits
Installed 1 APM dependency.(files unchanged)and final summary now agree.Validation
Mutation-break gate:
After restore:
Relevant suite and lint output
Scenario Evidence
apm installtwice with no second-run file mutations; the second summary reports no changes instead of a fresh installtests/unit/install/test_noop_summary.py::test_second_noop_install_reports_no_changes_in_summary(regression-trap for #1557)How to test
uv run --locked --extra dev pytest tests/unit/install/test_noop_summary.py -xand observe the regression test pass.uv run --locked --extra dev pytest tests/unit/install tests/unit/test_install_command.py -xand observe the install unit suites pass.uv run --locked --extra dev ruff check --quiet src/ tests/and observe no output.uv run --locked --extra dev ruff format --check --quiet src/ tests/and observe no output.bash scripts/lint-auth-signals.shand observe[+] auth-signal lint clean.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com