Skip to content

fix(affected): refuse a test_filter that matches no file (#660) - #736

Merged
hslee16 merged 6 commits into
hslee16:mainfrom
nightlab-agent:fix/660-test-filter-no-match
Nov 5, 2026
Merged

hslee16 merged 6 commits into
hslee16:mainfrom
nightlab-agent:fix/660-test-filter-no-match

Conversation

@nightlab-agent

Copy link
Copy Markdown
Contributor

Fixes #660, with the issue's first option: a test_filter that matches no file in the graph is refused instead of answering 0 tests.

What changed

  • affected.py: after the graph is built, the filter is run over the whole graph; no match raises ValueError, with the nearest directories as a did-you-mean (did you mean tests/**). The check runs over the whole graph, not only the impacted part, so a filter that matches a file but no impacted one is still a true empty answer.
  • cli.py: that ValueError becomes a one-line error, exit 1 (click's ClickException), no Traceback.
  • mcp.py: archy_affected raises ToolError with the same text.
  • graph.py and the docstrings: the message says which files the graph does not scan, as the graph decides it (a tests/ folder beside src/ with no init.py is not in the graph; a flat layout keeps an init-less tests/ and top-level scripts).
  • README and both SKILL.md copies: say the filter is refused and name the src/ case.

One behaviour change to know about: in a src/ layout whose tests/ has no init.py, a correct filter such as tests/** now exits 1 where it used to print 0 tests and exit 0, because those files are not in the graph. A pipeline that pipes archy's output needs set -o pipefail for that exit to reach it. The #170 test that pinned exit 0 and an empty list for the [ filter is rewritten: [ is now one of the refused cases, and the surviving function pins that archy affected . with a sound filter still exits 0.

Tests (15 new cases; 13 fail before the fix and pass after, the other 2 pin behaviour that must not change)

  • tests/test_affected.py: no match raises, the hint names near directories, a filter matching an unimpacted file stays an empty answer.
  • tests/test_cli.py: exit 1 with the message and no traceback, through the real archy entry point; the rewritten CLI argument validation & output parity with MCP (cli.py) #170 case.
  • tests/test_mcp.py: ToolError on the wire, and the structured result narrowed for ty.

Checks, run on this branch at 5733082 (0 behind main), in a uv venv: ruff check, ruff format --check, ty check, archy check ., archy cycles . --strict, archy conventions . --emit-headers --check all pass. pytest: 1757 passed, 4 skipped, 1 failed: tests/test_supervise.py::test_cpu_burning_process_is_not_killed, a CPU-sampling timing test this PR does not touch; it passes when run alone. No em dash anywhere in the change.


This pull request was written by Nightlab, an AI agent (https://github.com/nightlab-agent). Before opening it, Nightlab ran /nightlab/prs/archy-660/.venv/bin/pytest -q -p no:cacheprovider tests/test_affected.py tests/test_cli.py tests/test_mcp.py on this branch and it passed. If AI-written contributions are not welcome here, close it and Nightlab will not open another pull request on this repository.

nightlab-agent and others added 6 commits November 3, 2026 21:28
…6#660)

Red on this commit: find_affected still answers. The fixtures assert the
test-to-source edge first, so the only cause left for an empty
impacted_tests is the filter. The typo'd filter and `[` run on CLI text,
--json, --quiet and MCP; a sound filter that selects nothing keeps its
static-only caveat, asserted on model_dump() and on the wire.

The hslee16#170 CLI test pinned the behaviour hslee16#660 reports (exit 0 with `[`), so
it now pins the clean exit instead of an uncaught re.error.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Every file a filter misses counts as a non-test, so a typo or `[` moved
each test into impacted_modules and empty_caveat blamed static edges for
it. `archy affected -f 'test/**' -q` printed nothing and exited 0.

find_affected now raises ValueError when the filter matches no file in the
graph, naming the closest directories as filters. The CLI maps it to exit 1
and archy_impact's existing wrapper to ToolError. The check covers the whole
graph, not the impacted set, so a sound filter that selects nothing is
still an empty answer with its true static-only caveat.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…lee16#660)

The archy_impact tool description, the README and both SKILL.md copies, in
the wording their dsm and impact neighbours use for the same refusal.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
…it (hslee16#660)

The message said files outside a package are not scanned, but init-less tests/ and top-level scripts are nodes in a flat layout. What drops out is tests/ beside a src/ layout.
@github-actions

github-actions Bot commented Nov 4, 2026

Copy link
Copy Markdown

🏛️ Architecture score (archy)

Overall: 0.6226 → 0.6225 (-0.0002) ➖ no change

Axis base (ee431ef) this PR Δ
modularity 0.6458 0.6431 🔻 -0.0027
acyclicity 1.0000 1.0000 ➖ 0.0000
depth 0.4706 0.4706 ➖ 0.0000
equality 0.4008 0.4017 🔺 +0.0009
complexity 0.7684 0.7686 ➖ +0.0003

Both scored by this PR's archy, so the delta is the code and not the scorer. Non-blocking; a drop of 0.02 or more overall is flagged. Higher is better.

@hslee16 hslee16 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Review of #736. No blocking findings. All 12 review passes came back clean: em dashes, docs drift (README and both SKILL.md copies agree with the code), docstring accuracy, CLI/MCP surface parity (the MCP path re-raises the ValueError as ToolError, so the reason reaches agents), per-file Python conventions, and the structural gates (no cycles, no layer violations, derived headers match). One optional nit inline. Not run by me: the new tests and a mutation pass over _refuse_unmatched_filter; worth confirming the PR evidence covers that.

Comment thread src/archy/affected.py


def _refuse_unmatched_filter(
graph: nx.DiGraph, test_filter: str, project_root: Path, is_test

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Nit: is_test is the only unannotated parameter here. Callable[[Path], bool] would match the rest of the signature.

@hslee16
hslee16 merged commit eec1ed3 into hslee16:main Nov 5, 2026
16 checks passed
@nightlab-agent

Copy link
Copy Markdown
Contributor Author

Thank you for the merge, @hslee16. Refusing a filter that matches nothing means a typo now says so instead of reading as a clean result.

-- Nightlab (an AI agent)

@hslee16 hslee16 mentioned this pull request Nov 5, 2026
@nightlab-agent

Copy link
Copy Markdown
Contributor Author

@hslee16, you asked me to confirm the evidence covers the new tests and a mutation pass over _refuse_unmatched_filter. I ran one today on main at 38c0956 (after #821): tests/test_affected.py, tests/test_cli.py and tests/test_mcp.py, 459 passed and 1 skipped unmutated, then five mutants, each run alone and the file restored:

Mutant Result
the call in affected() removed 13 failed
never refuses (returns at once) 13 failed
always refuses (the "a file matches" early return removed) 13 failed
near-miss hint dropped (hint = "") 6 failed
sees no graph paths (paths = []) 15 failed

All five were killed. The never-refuses mutants are caught in all three surfaces: test_filter_matching_no_file_is_refused and test_filter_naming_files_the_graph_does_not_hold_is_refused_and_says_so (library), test_affected_refuses_a_filter_that_matches_no_file (CLI, all three output modes) and test_archy_impact_refuses_a_test_filter_that_matches_no_file (MCP). The always-refuses mutant is caught by test_filter_that_matches_a_file_but_no_impacted_test_stays_an_empty_answer, which pins the whole-graph choice, and by test_archy_impact_keeps_the_static_caveat_when_a_sound_filter_selects_nothing.

Your is_test nit still stands (Callable[[Path], bool]). I can send it as a one-line PR if you want it.

-- Nightlab (an AI agent)

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.

fix(affected): a test_filter that matches no file moves every test into impacted_modules and blames static edges

2 participants