Skip to content

kimicli hardening, safe tar extraction, classmethod fixtures - #19

Merged
Liebmann5 merged 1 commit into
mainfrom
work/small-fixes
Oct 28, 2026
Merged

Liebmann5 merged 1 commit into
mainfrom
work/small-fixes

Conversation

@Liebmann5

@Liebmann5 Liebmann5 commented Oct 28, 2026 •

Copy link
Copy Markdown
Owner
  • kimicli now checks a turn together with the earlier turns of its session before calling it not applicable.
    • It offers the free --skip route before a paid repair.
    • The installer extracts uv's archive with Python's "data" filter, with manual checks on older Pythons.
    • The four class-scoped browser fixtures are now classmethods.
    • Windows: 2421 passed / 5 skipped / 1 xfailed, no warnings. kimicli selftest 55/55.

Summary by Sourcery

Harden kimicli turn staging and installer archive extraction while modernizing browser fixtures.

New Features:

  • Improve kimicli staging to evaluate dependent turns together and offer a free skip-based application path when only some files fail.

Bug Fixes:

  • Prevent unsafe uv archive extraction from writing outside the installation destination across supported Python versions.

Enhancements:

  • Update class-scoped browser fixtures to use class methods.

Tests:

  • Add coverage for session-level kimicli applicability, skip-route selection, and tar extraction safety.

@sourcery-ai

sourcery-ai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Reviewer's Guide

This PR hardens installation archive handling, improves kimicli’s session-aware staging and recovery guidance, and updates class-scoped browser fixtures to classmethods, with targeted regression coverage.

Sequence diagram for session-aware kimicli staging

sequenceDiagram
    participant CLI as kimicli
    participant Session
    participant Applier
    participant User

    CLI->>Session: stage_turn
    CLI->>Applier: validate current turn
    Applier-->>CLI: plans and problems
    alt turn has file problems and no parse errors
        CLI->>Applier: session_applies_cleanly
        Applier-->>CLI: merged_ok
        alt earlier turns make the session apply cleanly
            CLI-->>User: offer --apply-fixes session
        else session still fails
            CLI->>CLI: free_skip_command
            alt some files apply cleanly
                CLI-->>User: offer --skip route
            else no safe partial apply
                CLI-->>User: offer paid repair
            end
        end
    else parse errors or clean turn
        CLI-->>User: show staging result
    end
Loading

Flow diagram for safe uv archive extraction

flowchart TD
    A[Open uv archive] --> B{Python supports data_filter}
    B -->|Yes| C[extractall with data filter]
    B -->|No| D[Resolve each archive target]
    D --> E{Path stays under destination}
    E -->|No| F[Raise InstallError]
    E -->|Yes| G{Entry is link or device}
    G -->|Yes| F
    G -->|No| H[extractall after manual checks]
    C --> I[Locate uv binary]
    H --> I
Loading

File-Level Changes

Change Details Files
Make kimicli staging aware of dependencies across turns and expose a no-cost partial-apply path before paid repair.
  • Track whether the latest-emission session validates cleanly as a whole when the current turn fails in isolation.
  • Suppress repair prompts when earlier turns make the combined session applicable, and explain the combined apply route.
  • Generate a --skip command when only some files fail and the response has no parse errors.
  • Add self-tests covering cross-turn validation and safe --skip recommendations.
kimicli.py
Harden uv tar extraction against path traversal and unsafe archive members across Python versions.
  • Use Python's PEP 706 data filter where available.
  • Manually reject escaping paths, links, and device files on older Python versions.
  • Add regression tests for traversal rejection and explicit filtered extraction without deprecation warnings.
packages/auto_apply/src/auto_apply/application/services/install/engine.py
packages/auto_apply/tests/application/test_install_engine.py
Update class-scoped Selenium fixtures to use classmethod-compatible declarations.
  • Convert four browser driver fixtures to classmethods while retaining class scope.
packages/auto_apply/tests/integration/test_form_filling.py
packages/auto_apply/tests/integration/test_multipage_live_run.py
packages/auto_apply/tests/integration/test_probe_real_browser.py

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

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 5 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="kimicli.py" line_range="3269-3273" />
<code_context>
+    with --skip and fix the failing files by hand. None when skipping cannot
+    help - a parse error (a cut-off reply, a malformed block) or no file that
+    applies."""
+    pr = s.parse
+    if any(p.severity == "error" for p in pr.problems + s.group_problems):
+        return None
+    failing = [pl for pl in s.plans if pl.problems]
+    fine = [pl for pl in s.plans if not pl.problems and pl.action != "identical"]
+    if not failing or not fine:
+        return None
</code_context>
<issue_to_address>
**Session command omits earlier proposals**

When an earlier turn has a selected failing proposal or needs creation permissions that are absent from the current turn's plans, `free_skip_command` derives skips and creation flags from the current turn, but `--apply-fixes` loads proposals from every turn; an earlier failing proposal remains selected or its required creation permission is omitted, so the suggested dry run rejects the batch.

Build the skip list and creation flags from the full set of proposals that `--apply-fixes` will load.

Also at `kimicli.py:3276-3279`, `kimicli.py:3362`.
</issue_to_address>

### Comment 2
<location path="kimicli.py" line_range="3362" />
<code_context>
+        print("\n  These blocks fail on their own but the session applies cleanly as a whole:")
+        print("  they rely on an earlier turn of this session, and --apply-fixes <session>")
+        print("  applies every turn together (latest emission per file). Next:")
+        print(f"\n    python kimicli.py --apply-fixes {session.id} --dry-run --allow-new-files")
+    elif s.repair_path:
         print("\n  NOT APPLICABLE AS-IS. Nothing will be written until this is fixed.")
</code_context>
<issue_to_address>
**Merged-session command rejects new directories**

When a merged proposal creates a file beneath a directory that does not yet exist, `session_applies_cleanly` validates with `allow_new_dirs=True`, but the suggested merged-session command omits `--allow-new-dirs`, so `run_apply` rejects the session instead of confirming it applies cleanly.

Include `--allow-new-dirs` in the suggested merged-session command.

Also at `kimicli.py:3276-3278`.
</issue_to_address>

### Comment 3
<location path="kimicli.py" line_range="3236-3240" />
<code_context>
     errors = [p for p in all_parse if p.severity == "error"]
-    if errors or any(pl.problems for pl in plans):
+    if any(pl.problems for pl in plans) and not errors:
+        summary.merged_ok = session_applies_cleanly(session, turn)
+    if errors or (any(pl.problems for pl in plans) and not summary.merged_ok):
         body = build_repair_prompt([pl for pl in plans if pl.problems], errors, turn)
</code_context>
<issue_to_address>
**Concurrent turns hide failed proposals**

When two processes stage different turns of the same session concurrently, `session_applies_cleanly` validates a live session snapshot, while concurrent `stage_turn` calls can replace each other’s `proposals.json` updates; it can therefore report a clean merge and suppress the repair even though the completed session contains a failing proposal.

Serialize staging and proposal-index updates per session, and validate a consistent snapshot before suppressing the repair.
</issue_to_address>

### Comment 4
<location path="kimicli.py" line_range="3273" />
<code_context>
+    if any(p.severity == "error" for p in pr.problems + s.group_problems):
+        return None
+    failing = [pl for pl in s.plans if pl.problems]
+    fine = [pl for pl in s.plans if not pl.problems and pl.action != "identical"]
+    if not failing or not fine:
+        return None
</code_context>
<issue_to_address>
**Skipping one failure invalidates another plan**

When a failing Python module edit has a syntax error and a clean plan imports a name that edit adds, `free_skip_command` assumes every non-failing plan remains applicable after the failing plans are skipped. When a failing Python edit has a syntax error but introduces a name imported by a clean plan, validation can accept that import while considering the staged module; applying the suggested skip removes that module from the batch, and revalidation rejects the remaining plan.

Validate the proposal set after excluding the failing files before offering the skip command, and include any newly failing dependent plans in the skips.
</issue_to_address>

### Comment 5
<location path="kimicli.py" line_range="5196-5197" />
<code_context>
+            check("when only some files fail, the free --skip route is offered before a paid repair; "
+                  "never when the reply itself is broken",
+                  sh.repair_path is not None and free is not None and "--skip notes.md" in free
+                  and "pkg/lib.py" not in free.split("--dry-run", 1)[1]
+                  and free_skip_command(broken, "x") is None, str(free))
         finally:
             g.update(saved_paths)
</code_context>
<issue_to_address>
**Invalid skip command passes**

When `free_skip_command` returns a dry-run string containing `--skip notes.md` but omitting or misidentifying the session, `selftest_h` only checks for `--skip notes.md` and `--dry-run`; a command that omits `--apply-fixes selftest_h` still passes, even though it cannot apply this session’s other files.

Assert the returned command includes `--apply-fixes selftest_h` as well as the expected skip and dry-run arguments.
</issue_to_address>

Sourcery assessment

Approval pending. 5 findings to address first.

Blocking findings: kimicli.py:3273, kimicli.py:3362, kimicli.py:3240, kimicli.py:3273, kimicli.py:5197


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread kimicli.py
Comment on lines +3269 to +3273
pr = s.parse
if any(p.severity == "error" for p in pr.problems + s.group_problems):
return None
failing = [pl for pl in s.plans if pl.problems]
fine = [pl for pl in s.plans if not pl.problems and pl.action != "identical"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Session command omits earlier proposals

When an earlier turn has a selected failing proposal or needs creation permissions that are absent from the current turn's plans, free_skip_command derives skips and creation flags from the current turn, but --apply-fixes loads proposals from every turn; an earlier failing proposal remains selected or its required creation permission is omitted, so the suggested dry run rejects the batch.

Build the skip list and creation flags from the full set of proposals that --apply-fixes will load.

Also at kimicli.py:3276-3279, kimicli.py:3362.

Prompt for AI agents
In `kimicli.py` at lines 3269-3273:

**Session command omits earlier proposals**

When an earlier turn has a selected failing proposal or needs creation permissions that are absent from the current turn's plans, `free_skip_command` derives skips and creation flags from the current turn, but `--apply-fixes` loads proposals from every turn; an earlier failing proposal remains selected or its required creation permission is omitted, so the suggested dry run rejects the batch.

Build the skip list and creation flags from the full set of proposals that `--apply-fixes` will load.

Also at `kimicli.py:3276-3279`, `kimicli.py:3362`.

Comment thread kimicli.py
print("\n These blocks fail on their own but the session applies cleanly as a whole:")
print(" they rely on an earlier turn of this session, and --apply-fixes <session>")
print(" applies every turn together (latest emission per file). Next:")
print(f"\n python kimicli.py --apply-fixes {session.id} --dry-run --allow-new-files")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Merged-session command rejects new directories

When a merged proposal creates a file beneath a directory that does not yet exist, session_applies_cleanly validates with allow_new_dirs=True, but the suggested merged-session command omits --allow-new-dirs, so run_apply rejects the session instead of confirming it applies cleanly.

Include --allow-new-dirs in the suggested merged-session command.

Also at kimicli.py:3276-3278.

Prompt for AI agents
In `kimicli.py` at line 3362:

**Merged-session command rejects new directories**

When a merged proposal creates a file beneath a directory that does not yet exist, `session_applies_cleanly` validates with `allow_new_dirs=True`, but the suggested merged-session command omits `--allow-new-dirs`, so `run_apply` rejects the session instead of confirming it applies cleanly.

Include `--allow-new-dirs` in the suggested merged-session command.

Also at `kimicli.py:3276-3278`.

Comment thread kimicli.py
Comment on lines +3236 to 3240
summary.merged_ok = session_applies_cleanly(session, turn)
if errors or (any(pl.problems for pl in plans) and not summary.merged_ok):
body = build_repair_prompt([pl for pl in plans if pl.problems], errors, turn)
if body:
summary.repair_path = session.dir / f"repair_turn{turn}.md"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Concurrent turns hide failed proposals

When two processes stage different turns of the same session concurrently, session_applies_cleanly validates a live session snapshot, while concurrent stage_turn calls can replace each other’s proposals.json updates; it can therefore report a clean merge and suppress the repair even though the completed session contains a failing proposal.

Serialize staging and proposal-index updates per session, and validate a consistent snapshot before suppressing the repair.

Prompt for AI agents
In `kimicli.py` at lines 3236-3258:

**Concurrent turns hide failed proposals**

When two processes stage different turns of the same session concurrently, `session_applies_cleanly` validates a live session snapshot, while concurrent `stage_turn` calls can replace each other’s `proposals.json` updates; it can therefore report a clean merge and suppress the repair even though the completed session contains a failing proposal.

Serialize staging and proposal-index updates per session, and validate a consistent snapshot before suppressing the repair.

Comment thread kimicli.py
if any(p.severity == "error" for p in pr.problems + s.group_problems):
return None
failing = [pl for pl in s.plans if pl.problems]
fine = [pl for pl in s.plans if not pl.problems and pl.action != "identical"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Skipping one failure invalidates another plan

When a failing Python module edit has a syntax error and a clean plan imports a name that edit adds, free_skip_command assumes every non-failing plan remains applicable after the failing plans are skipped. When a failing Python edit has a syntax error but introduces a name imported by a clean plan, validation can accept that import while considering the staged module; applying the suggested skip removes that module from the batch, and revalidation rejects the remaining plan.

Validate the proposal set after excluding the failing files before offering the skip command, and include any newly failing dependent plans in the skips.

Prompt for AI agents
In `kimicli.py` at line 3273:

**Skipping one failure invalidates another plan**

When a failing Python module edit has a syntax error and a clean plan imports a name that edit adds, `free_skip_command` assumes every non-failing plan remains applicable after the failing plans are skipped. When a failing Python edit has a syntax error but introduces a name imported by a clean plan, validation can accept that import while considering the staged module; applying the suggested skip removes that module from the batch, and revalidation rejects the remaining plan.

Validate the proposal set after excluding the failing files before offering the skip command, and include any newly failing dependent plans in the skips.

Comment thread kimicli.py
Comment on lines +5196 to +5197
and "pkg/lib.py" not in free.split("--dry-run", 1)[1]
and free_skip_command(broken, "x") is None, str(free))

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Medium · Invalid skip command passes

When free_skip_command returns a dry-run string containing --skip notes.md but omitting or misidentifying the session, selftest_h only checks for --skip notes.md and --dry-run; a command that omits --apply-fixes selftest_h still passes, even though it cannot apply this session’s other files.

Assert the returned command includes --apply-fixes selftest_h as well as the expected skip and dry-run arguments.

Prompt for AI agents
In `kimicli.py` at lines 5196-5197:

**Invalid skip command passes**

When `free_skip_command` returns a dry-run string containing `--skip notes.md` but omitting or misidentifying the session, `selftest_h` only checks for `--skip notes.md` and `--dry-run`; a command that omits `--apply-fixes selftest_h` still passes, even though it cannot apply this session’s other files.

Assert the returned command includes `--apply-fixes selftest_h` as well as the expected skip and dry-run arguments.

@Liebmann5
Liebmann5 merged commit 80b38d2 into main Oct 28, 2026
19 checks passed
@Liebmann5
Liebmann5 deleted the work/small-fixes branch October 28, 2026 23:12
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