feat(fix): T4 OWN001/OWN014 fixer — the one fixer no tool ships - #2
Conversation
own-check's subscription/region-escape rules are ours, so nothing off-the-shelf fixes them. This adds OwnFixApplier behind the same Applier interface as the mass appliers, so it inherits dry-run, the no-new-findings gate, and rollback. OWN is tier T4 -> every result is queued-for-review, never auto. This slice fixes the named-handler subscription shape by inserting a teardown detach (Window -> Closed, FrameworkElement -> Unloaded). It refuses the inline- lambda shape (own-check: 'no -= handle, could never be detached') as suggest-only and surfaces it in applier.skipped rather than emitting a fake fix. - fixarm/own_fix.py classify + plan_file + OwnFixApplier (build-free, structural) - fixtures real STS sites: AmountWindow (OWN001), KTSGoods2 (OWN014), DatabaseOptimizationWindow (OWN001 lambda, suggest-only) - cli.py --applier own drive the real fixer through the wrapper - tests 5/5 incl. classifier boundary + OWN-fixer revert-on-reject Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LLHz4cevQyXHKZnjzCqhoa
📝 WalkthroughWalkthroughAdds an OWN fixer implementation, CLI selection between replay and own appliers, fixture-based tests for OWN001/OWN014 and inline-lambda handling, and updated docs for the new fixer slice. ChangesOWN fixer flow
Sequence Diagram(s)sequenceDiagram
participant CLI
participant run_fix
participant OwnFixApplier
participant ReplayApplier
alt --applier own
CLI->>OwnFixApplier: build from filtered findings
CLI->>run_fix: invoke with OwnFixApplier
run_fix->>OwnFixApplier: dry_run/apply/revert via Applier
else --applier replay
CLI->>ReplayApplier: build from fixture directory
CLI->>run_fix: invoke with ReplayApplier
run_fix->>ReplayApplier: dry_run/apply/revert via Applier
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit:1b1ed58c97
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| skipped.append((f, "no-safe-teardown")) | ||
| continue | ||
| indent = re.match(r"\s*", lines[idx]).group(0) | ||
| inserts.append((idx, f"{indent}this.{ev} += (s, e) => {src_event} -= {handler};\n")) |
There was a problem hiding this comment.
Skip unbraced conditional subscriptions
When the reported subscription is the single statement under an unbraced guard, e.g. if (source != null) source.Event += Handler;, this inserts the teardown registration as a separate unconditional statement. In that scenario the Closed/Unloaded lambda is still registered when the guarded subscription was skipped, so closing/unloading can dereference the same null (or otherwise detach from an object that was never subscribed); with an else immediately following, the insertion also splits the if/else. Please detect and skip these unbraced control-flow bodies or insert inside a braced block.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
@codex Fixed in 7d38af3. Added _in_unbraced_control_flow: a subscription that is the single statement of an unbraced if/else/while/for/foreach/using/lock body — inline guard, a header on the line above with no {, or an else immediately after — is routed to suggest-only (unbraced-control-flow) and never patched, rather than emitting an unconditional teardown that could detach on a skipped subscribe or split an if/else. Test: test_unbraced_if_guard_is_skipped.
Generated by Claude Code
There was a problem hiding this comment.
To use Codex here, create an environment for this repo.
| ap.add_argument("--applier", choices=("replay", "own"), default="replay", | ||
| help="replay = recorded after/ tree; own = the real OWN001/OWN014 fixer") |
There was a problem hiding this comment.
Reject replay on OWN fixtures without an after tree
Because replay remains the default, running the CLI on the new OWN fixtures without --applier own treats the missing after/ source tree as an empty tree; for example own001-sub-window prints an OK patch that deletes Broker/AmountWindow.xaml.cs because its after.findings.json is empty. Please default OWN rules to the OWN applier or fail fast when replay is selected for a fixture that has no after/ tree.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
@codex Fixed in 7d38af3. OWN* rules now default to the OWN applier, and --applier replay on a fixture with no after/ tree fails fast (exit 2) instead of reading the absent tree as file deletions. Tests: test_cli_replay_on_own_fixture_fails_fast, test_cli_defaults_own_rule_to_own_applier.
Generated by Claude Code
There was a problem hiding this comment.
To use Codex here, create an environment for this repo.
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
fix/tests/test_own_fix.py (1)
78-78: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAvoid unused unpacked
applierin context manager tuples.Line 78 and Line 96 unpack
applierbut never use it. Rename to_applier(or_) to silence RUF059 warnings.Also applies to: 96-96
🤖 Prompt for 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. In `@fix/tests/test_own_fix.py` at line 78, The tuple unpacking in the _wrapped context manager usage is binding an unused applier value, which triggers RUF059. Update the unpacking at the call sites in test_own_fix.py to use _applier or _ in place of applier while keeping the existing res and wd bindings, and apply the same change wherever the _wrapped helper is used with the unused third value.Source: Linters/SAST tools
🤖 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 `@fix/fixarm/own_fix.py`:
- Around line 84-104: The teardown insertion loop in the function that builds
`inserts` from `findings` can emit duplicate detach lines when multiple findings
resolve to the same subscription site. Deduplicate the pending inserts by the
full `(idx, text)` pair before the bottom-up `lines.insert` pass, so repeated
OWN001 matches on the same `src_event`/`handler` only produce one `this.<ev> +=
...` line and one `applied` entry per unique insertion.
- Around line 76-81: Reject unsafe finding paths before any workdir join: in
own_fix.py, make _plan(), dry_run(), apply(), and revert() validate each
Finding.path from orchestrate.py so absolute paths and any `..` traversal are
normalized and confirmed to stay inside workdir before use. Centralize the check
around the path-handling logic in plan_file()/the caller flow so only safe
relative paths are joined and processed.
In `@fix/tests/test_own_fix.py`:
- Line 84: Rename the ambiguous loop variable used in test_own_fix.py so Ruff
E741 no longer flags it; update both occurrences in the generator expressions
that search through lines, replacing the single-letter identifier in the
`next(...)` / `enumerate(...)` expressions with a clearer name (for example in
the `sub` lookup and the later matching lookup) while keeping the same logic in
`test_own_fix`.
---
Nitpick comments:
In `@fix/tests/test_own_fix.py`:
- Line 78: The tuple unpacking in the _wrapped context manager usage is binding
an unused applier value, which triggers RUF059. Update the unpacking at the call
sites in test_own_fix.py to use _applier or _ in place of applier while keeping
the existing res and wd bindings, and apply the same change wherever the
_wrapped helper is used with the unused third value.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 1630a675-ba70-435b-8915-8c9b6abe88ab
📒 Files selected for processing (13)
docs/fix-arm.mdfix/README.mdfix/fixarm/cli.pyfix/fixarm/own_fix.pyfix/fixtures/own001-lambda/before.findings.jsonfix/fixtures/own001-lambda/before/Broker/DatabaseOptimizationWindow.xaml.csfix/fixtures/own001-sub-window/after.findings.jsonfix/fixtures/own001-sub-window/before.findings.jsonfix/fixtures/own001-sub-window/before/Broker/AmountWindow.xaml.csfix/fixtures/own014-region-escape/after.findings.jsonfix/fixtures/own014-region-escape/before.findings.jsonfix/fixtures/own014-region-escape/before/Broker/KTS/KTSGoods2.xaml.csfix/tests/test_own_fix.py
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- skip unbraced control-flow subscriptions (if/else/while/... single-statement
bodies): inserting an unconditional teardown there would register the detach
even when the guarded subscribe was skipped, or split an if/else. Routed to
suggest-only ('unbraced-control-flow'), never patched. (Codex)
- cli: OWN* rules default to the OWN applier; replay on a fixture with no after/
tree now fails fast instead of reading the absent tree as file deletions. (Codex)
- dedupe planned inserts by (idx, text): repeated findings on one += line produce
one detach + one applied entry. (CodeRabbit)
- reject absolute / '..' finding paths before any workdir join via _safe_join,
across plan/dry_run/apply/revert. (CodeRabbit security)
- tests: rename ambiguous 'l' (E741) and unused 'applier' -> '_applier' (RUF059);
add docstrings; +unbraced-skip, +dedup, +path-traversal, +cli fail-fast/default.
10/10 own + 7/7 wrapper on bare python3.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LLHz4cevQyXHKZnjzCqhoaThere was a problem hiding this comment.
Actionable comments posted: 3
🤖 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 `@fix/fixarm/cli.py`:
- Around line 49-61: The OWN path in cli.main still assumes an
after.findings.json exists and later crashes in ReplayReaudit, so add a
validation check alongside the existing after/ tree guard before building the
applier. In the main flow where kind is selected and ReplayApplier/OwnFixApplier
are prepared, verify that args.fixture contains after.findings.json as well as
after/, and if it is missing print a clear stderr error and return 2 instead of
continuing.
In `@fix/fixarm/own_fix.py`:
- Around line 147-152: The duplicate-site guard in own_fix.py is silently
dropping findings because the seen-check continues before the finding is
recorded anywhere. Update the insert-planning logic around the ins/seen handling
so that a repeated `(idx, text)` is added to the skipped ledger instead of being
ignored, while still avoiding duplicate inserts. Make the change in the loop
that builds inserts/applied/skipped so the function’s no-silent-drops contract
is preserved.
In `@fix/tests/test_own_fix.py`:
- Around line 198-205: The path traversal test in OwnFixApplier should not rely
on assert False as the failure guard, because it disappears under optimized
Python. Update the test in test_own_fix.py to use an explicit failure path such
as pytest.fail or a direct raise inside the try block after applier.apply(wd,
"OWN001"), keeping the ValueError handling around OwnFixApplier.apply and the
Finding-based setup intact.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: c6cedca4-5e29-422a-96de-c296f0902bc0
📒 Files selected for processing (3)
fix/fixarm/cli.pyfix/fixarm/own_fix.pyfix/tests/test_own_fix.py
| # OWN* rules default to the OWN fixer; replay needs a recorded after/ tree, so | ||
| # refuse it on a fixture that has none (else it reads the absent tree as deletions). | ||
| kind = args.applier or ("own" if args.rule.startswith("OWN") else "replay") | ||
| if kind == "replay" and not os.path.isdir(os.path.join(args.fixture, "after")): | ||
| print(f"error: fixture {args.fixture!r} has no after/ tree; replay would read it as " | ||
| f"file deletions. Use --applier own.", file=sys.stderr) | ||
| return 2 | ||
| before = load_findings(os.path.join(args.fixture, "before.findings.json")) | ||
| wd = _seed_workdir(os.path.join(args.fixture, "before")) | ||
| try: | ||
| applier = (OwnFixApplier([f for f in before if f.rule == args.rule]) | ||
| if kind == "own" else ReplayApplier(args.fixture)) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash# List fixture dirs and check for after.findings.json presence.
fd -t d . fix/fixtures --max-depth 1 |whileread -r dir;doif [ -f"$dir/after.findings.json" ];thenecho"OK $dir"elseecho"MISS $dir (no after.findings.json — ReplayReaudit would fail)"fidoneRepository: PhysShell/OwnAudit
Length of output: 388
🏁 Script executed:
#!/bin/bashset -euo pipefail
echo"== fix/fixarm/cli.py =="
ast-grep outline fix/fixarm/cli.py --view expanded ||trueecho
sed -n '1,120p' fix/fixarm/cli.py
echoecho"== references to ReplayReaudit and after.findings.json =="
rg -n "ReplayReaudit|after\.findings\.json|run_fix\(" fix -SRepository: PhysShell/OwnAudit
Length of output: 5637
🏁 Script executed:
#!/bin/bashset -euo pipefail
# Inspect the fixture that is missing after.findings.json and its siblings.forfin fix/fixtures/own001-lambda fix/fixtures/own001-sub-window fix/fixtures/idisp001-clean fix/fixtures/idisp001-regress fix/fixtures/own014-region-escape;doecho"== $f =="
find "$f" -maxdepth 1 -type f | sort
echodoneecho"== any code paths that special-case own001-lambda =="
rg -n "own001-lambda|own001-sub-window|idisp001-clean|idisp001-regress|own014-region-escape" -S .Repository: PhysShell/OwnAudit
Length of output: 2454
Validate after.findings.json tooown001-lambda has no after.findings.json, but main always constructs ReplayReaudit(.../after.findings.json), so the OWN path still fails later with a stack trace. Check for that file alongside after/ and return a clear error.
🤖 Prompt for 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.
In `@fix/fixarm/cli.py` around lines 49 - 61, The OWN path in cli.main still
assumes an after.findings.json exists and later crashes in ReplayReaudit, so add
a validation check alongside the existing after/ tree guard before building the
applier. In the main flow where kind is selected and ReplayApplier/OwnFixApplier
are prepared, verify that args.fixture contains after.findings.json as well as
after/, and if it is missing print a clear stderr error and return 2 instead of
continuing.
| ins = (idx, f"{indent}this.{ev} += (s, e) => {src_event} -= {handler};\n") | ||
| if ins in seen: | ||
| continue # same detach already planned for this site | ||
| seen.add(ins) | ||
| inserts.append(ins) | ||
| applied.append((f, ev)) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Duplicate-site continue silently drops the finding from the ledger.
When a second finding resolves to an already-planned (idx, text), the continue skips both applied and skipped, so that finding appears in neither list. This contradicts the function's own contract: "no silent drops (docs/fix-arm.md §8)". Record it as skipped instead of dropping it.
♻️ Proposed fix
ins = (idx, f"{indent}this.{ev} += (s, e) => {src_event} -= {handler};\n")
if ins in seen:
- continue # same detach already planned for this site+ skipped.append((f, "duplicate-site")) # detach already planned; keep ledger complete+ continue
seen.add(ins)
inserts.append(ins)
applied.append((f, ev))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ins= (idx, f"{indent}this.{ev} += (s, e) => {src_event} -= {handler};\n") | |
| ifinsinseen: | |
| continue# same detach already planned for this site | |
| seen.add(ins) | |
| inserts.append(ins) | |
| applied.append((f, ev)) | |
| ins= (idx, f"{indent}this.{ev} += (s, e) => {src_event} -= {handler};\n") | |
| ifinsinseen: | |
| skipped.append((f, "duplicate-site")) # detach already planned; keep ledger complete | |
| continue | |
| seen.add(ins) | |
| inserts.append(ins) | |
| applied.append((f, ev)) |
🤖 Prompt for 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.
In `@fix/fixarm/own_fix.py` around lines 147 - 152, The duplicate-site guard in
own_fix.py is silently dropping findings because the seen-check continues before
the finding is recorded anywhere. Update the insert-planning logic around the
ins/seen handling so that a repeated `(idx, text)` is added to the skipped
ledger instead of being ignored, while still avoiding duplicate inserts. Make
the change in the loop that builds inserts/applied/skipped so the function’s
no-silent-drops contract is preserved.
| for bad in ("../escape.cs", "/etc/passwd"): | ||
| applier = OwnFixApplier([Finding("OWN001", bad, 1, tool="own-check", | ||
| message="event 'a.E' is subscribed (handler 'H')")]) | ||
| try: | ||
| applier.apply(wd, "OWN001") | ||
| assert False, f"expected ValueError for {bad!r}" | ||
| except ValueError: | ||
| pass |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Avoid assert False; it’s stripped under python -O.
If apply does not raise, the assert False guard is removed when running with -O, so a regression where path traversal stops being rejected would pass silently. Use an explicit raise (or pytest.fail).
Suggested patch
try:
applier.apply(wd, "OWN001")
- assert False, f"expected ValueError for {bad!r}"+ raise AssertionError(f"expected ValueError for {bad!r}")
except ValueError:
pass📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| forbadin ("../escape.cs", "/etc/passwd"): | |
| applier=OwnFixApplier([Finding("OWN001", bad, 1, tool="own-check", | |
| message="event 'a.E' is subscribed (handler 'H')")]) | |
| try: | |
| applier.apply(wd, "OWN001") | |
| assertFalse, f"expected ValueError for {bad!r}" | |
| exceptValueError: | |
| pass | |
| forbadin ("../escape.cs", "/etc/passwd"): | |
| applier=OwnFixApplier([Finding("OWN001", bad, 1, tool="own-check", | |
| message="event 'a.E' is subscribed (handler 'H')")]) | |
| try: | |
| applier.apply(wd, "OWN001") | |
| raiseAssertionError(f"expected ValueError for {bad!r}") | |
| exceptValueError: | |
| pass |
🧰 Tools
🪛 Ruff (0.15.18)
[warning] 203-203: Do not assert False (python -O removes these calls), raise AssertionError()
Replace assert False
(B011)
🤖 Prompt for 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.
In `@fix/tests/test_own_fix.py` around lines 198 - 205, The path traversal test in
OwnFixApplier should not rely on assert False as the failure guard, because it
disappears under optimized Python. Update the test in test_own_fix.py to use an
explicit failure path such as pytest.fail or a direct raise inside the try block
after applier.apply(wd, "OWN001"), keeping the ValueError handling around
OwnFixApplier.apply and the Finding-based setup intact.
Source: Linters/SAST tools
Uh oh!
There was an error while loading. Please reload this page.
- own_fix: duplicate-site dedupe now records the dropped finding as 'duplicate-site' in the skipped ledger (preserves the no-silent-drops contract) instead of silently continuing. - cli: validate after.findings.json exists (the CLI re-audits via it for both appliers); fail fast with a clear error instead of crashing later in ReplayReaudit on suggest-only fixtures like own001-lambda. - tests: replace 'assert False' with 'raise AssertionError' (B011, survives -O); assert the duplicate finding lands in the skipped ledger. 10/10 + 7/7, incl -O. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LLHz4cevQyXHKZnjzCqhoa
Extends the OWN fixer (PR #2) to a second leak shape: an IDisposable field that is never disposed (Timer, CancellationTokenSource, ...). On a WPF owner it disposes the field on the teardown event, reusing the same Window->Closed / FrameworkElement-> Unloaded selection as the subscription detach: public ShareWindow() { InitializeComponent(); + this.Closed += (s, e) => _timer?.Dispose(); The dispose hook is anchored after the ctor's InitializeComponent() (in scope for 'this'). Honesty boundary kept: disposable-LOCAL stays suggest-only (needs a scoped using), as does the inline-lambda subscription. classify() now also returns the field shape; plan_file dispatches per shape to the right anchor + teardown statement. Fixture: ShareWindow._timer (real STS site). +2 tests; 13/13 own + 7/7 wrapper, incl -O. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LLHz4cevQyXHKZnjzCqhoa
…ntion) Killer feature #2 — the original "STS Runtime Analysis", grounded. Correlates static leak findings with a heap dump captured after a scenario, turning a suspicion into a confirmed leak with a confidence. Same split as graph.json: the .NET/CLR heap-dump collector runs on the stand and emits runtime.json (contract: docs/runtime-contract.md); correlation is pure stdlib, CI-testable. - runtime/correlate.py: three-way split — confirmed (static finding AND retention agree; high when held by a static-event delegate or excess >= high_count), static_only (suspect FP / unexercised path), runtime_only (the analyzer's blind spot). gate() for a PR ratchet on confirmed confidence. - runtime/cli.py: --findings + --runtime -> runtime-findings.json (findings.json shape, tool own-runtime) + runtime-report.md (3 sections). --gate-level fails (exit 2) on a confirmed leak at/above a confidence; report-only otherwise. - runtime/config.json: leak categories + thresholds. - report/sarif.py: runtime-confirmed-leak -> error, runtime-only-leak -> warning. - docs: runtime-contract.md (runtime.json schema + stand-side ClrMD collector sketch); phase-5 section marked engine-ready. - ci.yml: runtime suite added (normal + -O). - tests: runtime/tests/test_runtime.py (12/12, -O safe) — confirmed/rooted/medium/ noise/static-only/runtime-only/gate/CLI. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LLHz4cevQyXHKZnjzCqhoa
Arm 3 (Fix) — T4
OWN001/OWN014fixerThe one fixer no off-the-shelf tool covers: own-check's subscription / region-escape rules are ours, so nothing (
roslynator fix,dotnet format, OpenRewrite, Copilot Autofix) knows them. This addsOwnFixApplierbehind the sameApplierinterface as the mass appliers, so it inherits dry-run, the no-new-findings regression gate, and rollback for free. OWN is tier T4 → every result isqueued-for-review, never auto (lifetime-correct teardown placement is a judgement call).What this slice fixes
The named-handler subscription shape — insert a teardown detach, owner-type aware:
fGoods.PropertyChanged += new PropertyChangedEventHandler(GoodsPropertyChanged); + this.Closed += (s, e) => fGoods.PropertyChanged -= new PropertyChangedEventHandler(GoodsPropertyChanged); // WindowfThis.PropertyChanged += data_PropertyChanged; + this.Unloaded += (s, e) => fThis.PropertyChanged -= data_PropertyChanged; // FrameworkElement (OWN014)Honesty boundary
The inline-lambda shape is refused, not faked — own-check itself flags it has "no
-=handle, so it could never be detached". It's classified suggest-only and surfaced inapplier.skipped; a lambda needs extraction to a named handler first.Contents
fix/fixarm/own_fix.py—classify+plan_file+OwnFixApplier(build-free, structural)AmountWindow(OWN001),KTSGoods2(OWN014),DatabaseOptimizationWindow(OWN001 lambda → suggest-only)cli.py --applier own— drive the real fixer through the wrapperTest plan
Still to build (OWN fixer)
disposable-field/local shapes · lambda extraction then detach · fold into an existing
OnClosed/Dispose· Windows-bound fix-spike (roslynator fixload ofBroker.sln).🤖 Generated with Claude Code
Generated by Claude Code
Summary by CodeRabbit