Skip to content

clean tests: never hand a git call the runner's stdin (#46) - #50

Merged
kikashy merged 1 commit into
mainfrom
clean-test-stdin-46
Oct 26, 2026
Merged

kikashy merged 1 commit into
mainfrom
clean-test-stdin-46

Conversation

@kikashy

@kikashy kikashy commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Closes #46: tests/clean_test.py hung in git mktree when the runner's stdin was an open socket (an agent harness); with < /dev/null it passed.

What changes

  • tests/clean_test.py: the fixture git helper gives every call stdin=subprocess.DEVNULL unless it passes input= explicitly, and a 60 s timeout; git mktree gets its (empty) input through input=''; the sleeping child of the process-evidence test gets stdin=subprocess.DEVNULL too.
  • lib/clean.py: unchanged. Its only subprocess call, run(), already closes stdin; the new test pins that.
  • New test test_git_calls_ignore_an_open_socket_on_stdin: runs the mktree fixture (Cleanup.test_bare_git_refused) in a child whose stdin is one end of a socketpair, under a 60 s timeout, and calls clean.run() on git mktree with fd 0 pointed at the socket.

Mutants (scratch copies):

  • the helper's inherited stdin restored and mktree's input= removed: the new test fails after 60 s (the fixture git calls waited on the open socket on stdin);
  • stdin=subprocess.DEVNULL removed from clean.run(): the new test errors after 30 s (verification unavailable: git).

The whole suite (tests/run.sh) run with a socket on stdin, not /dev/null: exit 0, 439 passed, 0 failed.

Public surface

none (tests only).

Checks

== jpdev check --changed: 1 changed path since origin/main (merge base 250b07a) ==
unmapped path tests/clean_test.py: whole list
== whole list: 8 checks ==
PASS     diff-check, local-paths, agent-docs, bash-n, duplication, json, shellcheck
PASS     tests                    89s
    439 passed, 0 failed
== 8 passed, 0 failed, 0 skipped, 0 not run ==

Sign-off

Every commit carries a Developer Certificate of Origin sign-off that matches its author.

🤖 Generated with Claude Code

tests/clean_test.py ran its fixture git calls with the runner's stdin,
so `git mktree` waited forever when that stdin was an open socket, as
under an agent harness. The fixture helper now gives every git call an
empty stdin unless it passes input explicitly, `git mktree` gets its
input through `input=`, and the helper has a timeout. lib/clean.py's
git calls already closed stdin.

A new test runs the mktree fixture in a child whose stdin is one end of
a socketpair under a timeout, and calls lib/clean.py's run() on
`git mktree` with fd 0 pointed at the socket.

Closes #46

Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kikashy

kikashy commented Oct 26, 2026

Copy link
Copy Markdown
Contributor Author

Narrow review: devkit #49 (issue #48) and #50 (issue #46)

#49 (e779849 on 250b07a): the parity example now moves its checkout out of the clone before any check runs, and an unmapped untracked path gets its own reason; no findings.
#50 (406a87d on 250b07a): the clean tests' git helper closes stdin unless given input, and a socketpair test holds the hang shut; no findings. Same-vendor review, not a cross-vendor claim.

#49 dispositions

Finding Severity Evidence What to do
none - Move step precedes check.sh --setup, the first check.sh call; mv takes the whole directory; an empty untracked directory is not a changed path (selection returned no paths); actionlint on the uncommented example: clean (shellcheck present) -
none - Untracked unmapped path gets the new reason, a staged unmapped path keeps unmapped path ...; a checkout left inside with its own .git is reported as untracked; lib and vendored copies identical after the vendored header line -
none - Mutant "plain reason for every path": 3 failures; reverse mutant "untracked reason for every path": 4 failures -
none - Docs state the rule plainly (outside the clone or in .gitignore, else whole list on every pull request); public text clean -

#50 dispositions

Finding Severity Evidence What to do
none - Helper: stdin DEVNULL unless input=, 60 s timeout, mktree fed input=''; clean.run() already passes stdin=DEVNULL; the only stdin-reading git subcommand in lib/, tests/, scripts/, bin/ is mktree (other git calls inherit stdin but do not read it) -
none - Socketpair test passes (0.2 s); mutant "helper inherits stdin": fails at its 60 s timeout with the named message; mutant "DEVNULL removed from clean.run()": errors with verification unavailable: git at 30 s; no stray process left -
none - tests/run.sh with a socketpair on stdin: exit 0, 439 passed, 0 failed (137 s) -

What I ran

#49 merge: ready, no HIGH, no MEDIUM, no LOW.
#50 merge: ready, no HIGH, no MEDIUM, no LOW.

@kikashy
kikashy merged commit f87a7ee into main Oct 26, 2026
3 checks passed
@kikashy
kikashy deleted the clean-test-stdin-46 branch October 26, 2026 14:56
@github-actions

Copy link
Copy Markdown

🎉 Thank you for your contribution to Judgment Pack!

If you’d like another task, choose a path that interests you:

@kikashy kikashy mentioned this pull request Oct 26, 2026
kikashy added a commit that referenced this pull request Oct 26, 2026
Version 0.9.0 in lib/common.sh and on line 2 of the vendored scripts and
the template; the release notes for v0.9.0 (#49 workflow-created paths
and --changed, #50 test suites and standard input).

Signed-off-by: kikashy <35789537+kikashy@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
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.

tests/clean_test.py hangs in git mktree when stdin is an open socket

1 participant