Skip to content

refactor: drop the dead anchor: kwarg + gate the legacy trees as alias-only (ADR-008 step 7) - #229

Merged
pftg merged 1 commit into
masterfrom
refactor/adr8-anchor-kill-and-gate
Aug 22, 2026
Merged

refactor: drop the dead anchor: kwarg + gate the legacy trees as alias-only (ADR-008 step 7)#229
pftg merged 1 commit into
masterfrom
refactor/adr8-anchor-kill-and-gate

Conversation

@pftg

@pftg pftg commented Aug 22, 2026

Copy link
Copy Markdown
Collaborator

Part A — kill the dead anchor: kwarg

SnapDiff::Capture::Viewport.prepare!(expected_window_size, anchor: nil) accepted a kwarg no caller ever passed non-nil — both screenshot_matcher.rb call sites hardcoded anchor: nil — plus a unit test pinning its acceptance and a doc comment promising it to v3.

Adding an optional kwarg later is 100% non-breaking, so reserving it now bought nothing and shipped a test defending dead code. v3's scroll-preservation work will design the real contract; the panel found this seam's shape wrong for that anyway (no session access, no resize responsibility).

Deleted: the kwarg, both call-site args, the @param anchor doc, and the test.

Part B — the mechanical CI gate

test/unit/legacy_tree_is_alias_only_test.rb — a plain unit test, so it runs in rake test:unit and in CI with no new workflow wiring.

What it asserts. Every .rb file under lib/capybara/ and lib/capybara_screenshot_diff/ (plus both root entry files) contains nothing but:

  • require / require_relative / autoload
  • module / class / class << self / end / private / extend / include
  • constant aliases (Foo = SnapDiff::Foo)
  • def_delegator(s)
  • defs whose entire body is a single line delegating into SnapDiff

That last rule is the one that matters: a one-line forwarder is not lost when the file is deleted, real logic is. This is what keeps 3.0 a git rm instead of a refactor.

Mechanism: strip comments/blanks, fold trailing-comma continuations into one logical line, then walk. Failures name the file and the offending line.

The allowlist — the honest state of the tree

One file.

File Reason
lib/capybara/screenshot/diff/config_legacy.rb Generates the old accessor names from SnapDiff::Config::MAPPING via define_method (ADR-008 step 1 — it is delegation, but contains def/define_method). It also still holds derived config logic that never moved: .active? precedence, .screenshot_area path assembly, and .default_options — which carries one literal default, the vips tolerance 0.001. AVAILABLE_DRIVERS also lives here on purpose (#227: test_helper reads it at boot, image_compare_test stubs it) — the gate does not push it out.

Narrowed with a second cheap assertion: config_legacy.rb's method inventory is pinned to exactly active?, screenshot_area, screenshot_area_abs, self.compare, self.configure, self.default_options. Any new method there reds the gate.

Findings (files that did NOT need allowlisting, contrary to the brief)

  • lib/capybara_screenshot_diff.rb needs no exception. Its error-class eager aliases are plain constant assignments and it hosts no defs at all — the session forwarders actually live in lib/capybara_screenshot_diff/screenshot_assertion.rb.
  • screenshot_assertion.rb and static.rb need no exception either. Every method in them is a one-line SnapDiff.* forwarder, so the shape rule accepts them without a per-file carve-out.
  • The real finding is config_legacy.rb. ADR-008 step 1 moved config storage to SnapDiff::Config but left derived config logic behind. ADR-008's "alias-only" claim holds for the whole tree except this file. Worth amending the ADR to say so.

Gate-check evidence

Three mutation probes, each reverted with a targeted edit (no git checkout):

1. One-line def in a non-allowlisted file (lib/capybara_screenshot_diff/static.rb):

capybara_screenshot_diff/static.rb: `def probe_me; end` is not a one-line forwarder into SnapDiff
2 runs, 5 assertions, 1 failures

2. Realistic multi-line method (lib/capybara/screenshot/diff/region.rb):

capybara/screenshot/diff/region.rb: `def probe_me(other)` is not a one-line forwarder into SnapDiff
2 runs, 5 assertions, 1 failures

3. New method in the ALLOWLISTED file (config_legacy.rb) — proves the allowlist is not a blanket pass:

-["active?", "screenshot_area", "screenshot_area_abs", "self.compare", "self.configure", "self.default_options"]
+["active?", "probe_me", "screenshot_area", ...]
2 runs, 5 assertions, 1 failures

After all three reverts (git diff --stat clean of probes): 2 runs, 5 assertions, 0 failures on the untouched tree. The gate also refute_emptys the file glob, so it cannot pass vacuously.

Test numbers

Baseline (923f588) This branch
rake test:unit 528 runs, 1486 assertions, 0 failures 529 runs, 1490 assertions, 0 failures
rake test 562 runs, 1535 assertions, 0 failures, 6 skips
standardrb lib test 140 files, no offenses

529 = 528 − 1 (deleted anchor test) + 2 (gate tests).

No CHANGELOG or version.rb changes.

🤖 Generated with Claude Code

Summary by Sourcery

Remove the dead viewport anchor seam and enforce that legacy compatibility code remains removable alias-only plumbing.

Enhancements:

  • Remove the unused viewport anchor: parameter and its associated test and call-site arguments.
  • Add a unit-test gate that enforces the legacy compatibility trees remain alias-only, while explicitly tracking the remaining derived configuration logic.

Tests:

  • Add coverage for legacy-tree shape enforcement and the allowed configuration method inventory.

…s-only (ADR-008 step 7)

Part A -- kill `anchor:`. Viewport.prepare! accepted an `anchor:` kwarg no
caller ever passed non-nil (both ScreenshotMatcher call sites hardcoded
`anchor: nil`), plus a test defending its acceptance. Adding an optional
kwarg later is 100% non-breaking, so reserving it now bought nothing and
shipped a test guarding dead code. v3's scroll-preservation work designs
the real contract.

Part B -- the CI gate. New unit test asserts every .rb file under
lib/capybara/ and lib/capybara_screenshot_diff/ is nothing but requires,
namespace reopening, constant aliases and one-line forwarders into
SnapDiff. That is what keeps 3.0 a `git rm` instead of a refactor. One
allowlisted file (config_legacy.rb, with a written reason), narrowed by
pinning its method inventory so new logic there still reds.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Sorry @pftg, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@sourcery-ai

sourcery-ai Bot commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Removes an unused anchor: keyword argument from the viewport preparation seam and introduces a unit test gate that enforces the legacy capybara/capybara_screenshot_diff trees remain alias-only shims around SnapDiff, with a narrowly allowlisted config file.

Sequence diagram for viewport preparation without the dead anchor keyword

sequenceDiagram
    participant Matcher as SnapDiff::ScreenshotMatcher
    participant Viewport as SnapDiff::Capture::Viewport
    participant Browser as BrowserHelpers

    Matcher->>Viewport: prepare!(expected_window_size)
    Viewport->>Browser: window_size_is_wrong?(expected_window_size)
    Browser-->>Viewport: size mismatch or valid
    Viewport-->>Matcher: return or raise WindowSizeMismatchError
Loading

Flow diagram for the legacy tree alias-only CI gate

flowchart TD
    Start["rake test:unit"] --> Discover["Discover legacy .rb files and root entry files"]
    Discover --> Parse["Strip comments/blanks and fold continuations"]
    Parse --> Validate["Validate allowed declarations and SnapDiff forwarders"]
    Validate --> Inventory["Check config_legacy.rb method inventory"]
    Inventory --> Result{Pass?}
    Result -->|Yes| Green["Unit suite passes"]
    Result -->|No| Failure["Report file and offending line"]
Loading

File-Level Changes

Change Details Files
Remove the dead anchor: keyword argument from viewport preparation and its supporting test coverage.
  • Change Viewport.prepare! to accept only expected_window_size and drop the anchor: keyword parameter.
  • Update both ScreenshotMatcher call sites to stop passing anchor: nil when preparing the viewport.
  • Delete the unit test that asserted prepare! accepts the anchor parameter without using it.
  • Simplify the viewport doc comment to describe only the window-size guard behavior.
lib/snap_diff/capture/viewport.rb
lib/snap_diff/screenshot_matcher.rb
test/unit/capture/viewport_test.rb
Add a CI-enforced unit test gate ensuring legacy capybara trees contain only aliasing and forwarding code, with a narrowly scoped allowlist for known remaining logic.
  • Add a unit test that glob-loads all legacy lib/capybara/*.rb and lib/capybara_screenshot_diff/**/*.rb files and asserts they contain only requires, namespace declarations, constant aliases, delegators, and one-line SnapDiff forwarder methods.
  • Implement a line-shape analyzer that strips comments/blank lines, folds trailing-comma continuations, and flags any non-allowed constructs or multi-line method bodies as offences.
  • Introduce an allowlist for config_legacy.rb with a documented reason, while separately pinning its exact set of defined methods so new logic there fails the gate.
  • Ensure the gate test fails loudly with file/line details when real logic appears in legacy trees and cannot pass vacuously by asserting the glob is non-empty.
test/unit/legacy_tree_is_alias_only_test.rb

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

@pftg
pftg merged commit 9c53009 into master Aug 22, 2026
5 checks passed
@pftg
pftg deleted the refactor/adr8-anchor-kill-and-gate branch August 22, 2026 23:32
@coderabbitai

coderabbitai Bot commented Aug 22, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@pftg, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 3 minutes

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

Wait for the limit to reset, then comment @coderabbitai review or push new commits to the PR.

An organization admin can change what happens after included review limits in Billing.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fca45cb8-34d3-497b-9bb4-0b69875f86db

📥 Commits

Reviewing files that changed from the base of the PR and between 923f588 and da62601.

📒 Files selected for processing (4)
  • lib/snap_diff/capture/viewport.rb
  • lib/snap_diff/screenshot_matcher.rb
  • test/unit/capture/viewport_test.rb
  • test/unit/legacy_tree_is_alias_only_test.rb

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

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.

1 participant