Skip to content

Bundle: three race fixes and the gitpython, virtualenv and filelock security bumps - #482

Merged
hellices merged 8 commits into
mainfrom
fix/bundle-races-and-security-bumps
Oct 25, 2026
Merged

hellices merged 8 commits into
mainfrom
fix/bundle-races-and-security-bumps

Conversation

@hellices

@hellices hellices commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Closes #472
Closes #476
Closes #478
Closes #480

Summary

main requires a branch to be up to date before it merges, so every merge sent the other open pull requests back through a rebase and a full CI run. This branch carries everything that was open, so it needs one CI run and one merge.

Commits From Change
0b1a74f1, 96a56bd6 #475 (#472) Drop asyncio's relay child-watcher race warning from the stdio server's stderr checks. Emit it on every forced-relay run, so each check proves it filters it.
d64ad2b9 #477 (#476) Drop agent events that arrive while the app shuts down (product fix).
b72b03a3 #479 (#478) Wait for an approval dialog to compose before the eval driver reads it.
48929054 Dependabot #473 gitpython 3.1.62 → 3.2.1 (dev): GHSA-83vg-56qc-22m7.
293d7a85 Dependabot #474 virtualenv 21.7.13 → 21.14.9 (dev), with packaging 26.2 → 26.3.
cd253a2f Relock run 40155755520 (#480) filelock 3.32.0 → 4.2.4.
f243a0ab Relock run 40158406513 virtualenv 21.14.9 → 21.14.13.

The four fix commits are unchanged from their reviewed pull requests. The two Dependabot commits are cherry-picked with -x. The net uv.lock change against main is four version bumps: filelock 4.2.4, gitpython 3.2.1, packaging 26.3 and virtualenv 21.14.13.

Why the dependency bumps go together.

The relocked locks.

  • The Relock workflow resolved and verified both relocks on this branch. Each time, its propose job then could not branch from a base whose uv.lock differs from main's (Relock: propose fails when the base branch's uv.lock differs from main's #481).
  • Each commit is the workflow's relocked-lock artifact, matched to the SHA-256 its relock job recorded before any resolved code ran (493ce1c4…4f96 and 1de17a61…95da), and re-checked with main's scripts/check_lock_hosts.py.
  • uv 0.10.9, which the workflow pins, writes lock revision 3 and keeps two dependency markers the Dependabot commits had dropped. So the branch's net uv.lock change is just the four version bumps.

Merging. Please use Rebase and merge, or a merge commit, rather than squash. That keeps the eight commits, two of them Dependabot's, separate in history, so each can be reverted on its own. A squash would fold four dependency bumps into one test-fix commit.

Superseded. #475, #477 and #479 are closed in favour of this branch. Dependabot should close #473 and #474 itself once those packages are at or past their versions on main; otherwise they can be closed by hand.

Verification

Run locally on macOS with Python 3.13, with gitpython 3.2.1, virtualenv 21.14.9, packaging 26.3 and filelock 4.2.1 installed. This machine's package mirror does not have filelock 4.2.4 or virtualenv 21.14.13 yet, so the local runs used the nearest versions it has; the Relock gates and this PR's CI use the locked ones. Each step ran directly with the project's environment rather than through make, because this machine cannot sync from PyPI:

Command Result
python scripts/check_lock_hosts.py uv.lock uv.lock resolves from PyPI only
pytest -p no:tach tests/test_lockfile.py (final lock) 74 passed
python scripts/check_source_size.py passed
ruff check src/ tests/ and ruff format --check src/ tests/ passed; 659 files already formatted
SKIP=mypy,source-size pre-commit run --from-ref origin/main --to-ref HEAD passed, including "No private index URLs in uv.lock"
mypy no issues in 659 source files
tach check all modules validated
pytest -n 8 -q 13,755 passed, 25 skipped

This branch's final CI run (f243a0ab, run on hosted runners with four xdist workers) passed every required check: pre-commit, security, dependency-review, analyze, and:

Job Result
test (3.11) passed, 11m58s
test (3.12) passed, 13m10s
test (3.13) passed, 10m34s
windows-test passed, 8m0s

Checklist

  • This closes an accepted issue in this repository that is assigned to me.
  • Tests cover the change, written to fail first where behavior changed.
  • make check passes locally.
  • User-facing docs and the README match the change, or nothing user-facing changed.
  • No security invariant is weakened: writes still need a keystroke approval
    and a fail-closed audit entry, and sensitive reads are still masked.

hellices and others added 7 commits October 25, 2026 11:43
test_stdout_noise_is_diverted_and_wire_is_restored[True] failed on 3.12
because the stdio server's stderr carried "child process pid 3098 exit
status already read:  will report returncode 255" (#472). relay=True
forces the Windows-only relays onto POSIX. When the stdin relay exits on
its own as the test closes stdin, and _relay_stdin's cleanup runs before
the event loop has processed that exit, process.kill() goes through
Popen.send_signal, which polls first (bpo-38630). That waitpid reaps the
relay, and asyncio's child watcher then finds it gone, reports 255 and
logs the warning. On Windows, where the relays run in production, nothing
reaps.

The three forced-relay tests that compare the server's stderr exactly now
read it through _server_stderr, which drops whole lines matching either
watcher's warning (pidfd: "exit status already read", threaded: "Unknown
child process pid") and keeps everything else. Two tests pin both
directions: both warnings are dropped with the surrounding lines intact,
and a line that only contains one is kept.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The race is rare on its own, so the first fix only covered the three
exact stderr checks it was seen against. Under four xdist workers on
korvid-runners it hit a fourth on 3.12, twice:
test_stdio_delivers_large_utf8_output_without_truncation[True] expected
an empty stderr log and got "child process pid ... exit status already
read:  will report returncode 255".

The forced-relay entrypoint now logs that exact asyncio warning at
startup, so every relay=True test that reads the server's stderr has to
filter it on every run instead of passing on a lucky one. With the line
emitted, exactly that test failed; it now reads its stderr log through
_server_stderr, which accepts text as well as bytes, and test_stdio.py
passes in full.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Textual's App._shutdown clears _running, removes every screen and widget,
and only then sends the App its Unmount, which is where KorvidApp marks
the agent session down. A turn the user interrupted just before quitting
can settle in between. _finish_interrupted_turn then still sees
_shutting_down as False and hands its TurnInterrupted to the panel port,
which looks the panel up on a pruned screen. The resulting NoMatches
fails the turn task and escapes app shutdown through wait_for_turn
(#476). On #471's CI it surfaced as "No nodes match '#agent-status' on
AgentPanel()".

AppAgentPanel.apply_event, the one place agent events reach the panel,
now drops an event once the app is no longer running. The new test
reproduces the ordering deterministically: the interrupted turn's
cleanup waits for the panel's Unmount, so it settles after Textual
pruned the panel and before the App's Unmount. Without the guard it
fails with NoMatches every run.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The operation-journey driver read `.confirm-title` as soon as app.screen
was a ConfirmScreen. Textual makes a pushed screen current before it
composes the screen's widgets, so on a loaded runner the poll landed in
between and the read raised "No nodes match '.confirm-title' on
ConfirmScreen()". That failed two journeys on main's 3.11 (#478); the
second NoMatches in that log came from the journey tearing down after
the first.

handle() now first waits, through _await_dialog_composed, until the
current dialog has its title or is no longer current; a dialog that
closes first falls through to the existing closed-before-a-decision
path. The new test delays ConfirmScreen's compose by 0.3 seconds and
still expects one matched dialog_observed and no hard failures; without
the wait it fails every run with CI's exact pair of errors.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Bumps [gitpython](https://github.com/gitpython-developers/GitPython) from 3.1.62 to 3.2.1.
- [Release notes](https://github.com/gitpython-developers/GitPython/releases)
- [Changelog](https://github.com/gitpython-developers/GitPython/blob/main/doc/source/changes.rst)
- [Commits](gitpython-developers/GitPython@3.1.62...3.2.1)

---
updated-dependencies:
- dependency-name: gitpython
  dependency-version: 3.2.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 3861683)
Bumps [virtualenv](https://github.com/pypa/virtualenv) from 21.7.13 to 21.14.9.
- [Release notes](https://github.com/pypa/virtualenv/releases)
- [Changelog](https://github.com/pypa/virtualenv/blob/main/docs/changelog.rst)
- [Commits](pypa/virtualenv@21.7.13...21.14.9)

---
updated-dependencies:
- dependency-name: virtualenv
  dependency-version: 21.14.9
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
(cherry picked from commit 5e10f14)
Since 2026-10-23 pip-audit fails every branch on filelock 3.32.0:
GHSA-v4x8-3wr5-3wq8 (high), GHSA-9vhv-jxww-xfm5 and GHSA-567q-5qvw-pf92,
all fixed in 4.2.1 (#480). The locked virtualenv 21.7.13 required
filelock<4; the virtualenv 21.14.9 bump in this branch allows <5, so
filelock can now move to 4.2.4.

These are the bytes the Relock workflow produced and verified on this
branch: run 40155755520, package=filelock, base 293d7a8. Its relock job
resolved the lock on a runner with direct PyPI access, confirmed every
url, registry and index names PyPI, and ran uv sync --locked, ruff, the
format check, mypy, tach and the test suite (13,753 passed). Its propose
job then could not open a branch, because the base's uv.lock differs from
main's (#481), so the relocked-lock artifact was committed by hand after
matching its SHA-256 to the digest the relock job recorded before any
resolved code ran (493ce1c4...4f96) and re-checking it with main's
scripts/check_lock_hosts.py.

uv 0.10.9, which the workflow pins, writes lock revision 3 and keeps the
httpcore2 and secretstorage markers, so this also returns those lines to
main's form: the branch's net lock change is the four version bumps.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI 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.

🟡 Changes recommended

The locked virtualenv 21.14.9 has a high-severity command-injection vulnerability fixed in 21.14.13.

No open issues 🎉

What changed in this PR

Bundles three race fixes with dependency security updates for korvid’s Kubernetes TUI and test infrastructure, consolidating previously separate PRs.

Changes:

  • Drop agent-panel events during app shutdown and add a regression test.
  • Stabilize forced-relay stderr checks and wait for approval dialogs to compose before evaluation.
  • Update filelock, GitPython, virtualenv, and packaging in the lockfile.
File Description
uv.lock Updates four dependencies and their locked artifacts.
tests/​ui/​test_agent_interrupt.py Covers an interrupted turn settling after panel removal.
tests/​mcp/​test_stdio.py Filters known relay warnings and verifies filtering behavior.
tests/​evals/​test_operation_journeys.py Adds delayed-dialog composition coverage.
tests/​evals/​operation_app.py Waits for dialog widgets before reading approval details.
src/​korvid/​ui/​app_surfaces.py Drops agent events once the app stops running.

Review effort: Balanced

@my-reviewer-agent my-reviewer-agent 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.

검토 범위에서 보고할 이슈가 없습니다.

리뷰 범위: 변경사항 일부만 검토했습니다.

virtualenv 21.14.9, the version Dependabot proposed in #474, introduced
GHSA-cx6f-vjhg-g3cc / CVE-2026-115586 (high): the Bash activator leaves
the --prompt value unescaped in PS1 when promptvars is off, so prompt
expansion later executes it. The advisory was published on 2026-10-23,
after #474 passed its checks, and this branch's dependency-review check
failed on it. It is fixed in 21.14.13, which still allows filelock<5 and
needs packaging>=26.3, both already locked here.

These are the bytes the Relock workflow produced and verified on this
branch: run 40158406513, package=virtualenv, base cd253a2. Its relock
job resolved on a runner with direct PyPI access, confirmed every url,
registry and index names PyPI, and ran uv sync --locked, ruff, the format
check, mypy, tach and the test suite (13,753 passed). Its propose job hit
#481 again, so the relocked-lock artifact was committed by hand after
matching its SHA-256 to the digest the relock job recorded
(1de17a61...95da) and re-checking it with main's
scripts/check_lock_hosts.py. Only the virtualenv block changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@hellices

Copy link
Copy Markdown
Owner Author

Re the review on cd253a2 ("the locked virtualenv 21.14.9 has a high-severity command-injection vulnerability fixed in 21.14.13"): fixed in f243a0a. The Relock workflow moved virtualenv to 21.14.13 on this branch (run 40158406513: only the virtualenv block changes, PyPI-only, and its full gate passed with 13,753 tests). The commit is its verified artifact, digest-matched and host-checked as described in the PR body. 21.14.13 still allows filelock<5 and needs packaging>=26.3, both already locked. OSV now reports no known advisories for any of the four bumped packages.

Copilot AI 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.

🔵 Needs a closer look

The available validation evidence leaves required Python and Windows checks incomplete for the final combined changes.

No open issues 🎉

Review effort: Balanced

Copilot AI 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.

🟢 Approval recommended

No unresolved issues were identified, the race fixes have regression coverage, and the reported required CI checks passed.

No open issues 🎉

Review effort: Balanced

@hellices
hellices merged commit b3ad503 into main Oct 25, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants