Repository navigation
fix(harness): bound the scan's first look for a file (TH-9525) - #277
Merged
khushalsonawat merged 2 commits intoNov 11, 2026
Merged
khushalsonawat merged 2 commits into
khushalsonawat merged 2 commits into
Conversation
Before anything else the scan checks that the source holds a regular file. That look was an os.walk with no bound, so a crafted tree with no file in it, such as many empty folders or folders of links only, grew the walk's list of folders still to visit without end. Since os.walk lists a folder whole before the caller sees it, one folder of millions of empty folders was held before anything could stop it. The look is now the scan's own loop. It reads each folder once, entry by entry, depth first in name order. It takes each real subfolder onto its list, and counts what the list holds, at what each path costs as a str. A regular file anywhere in a folder wins, and is the same lstat test as before. Past 64 MiB held, or past the deadline after the source's own folder, with no file found, the scan ends as a walk stopped with nothing found: scan_limit_reached or scan_timed_out, no rows and no model asked. TH-9525 Signed-off-by: khushalsonawat <khushalsonawat@gmail.com>
…loose Both reviewers cleared round 1, finding nothing wrong in the look. This fixes the gaps they found in its tests: - The list watcher reads the look's stack and count by name, so a rename fails rather than checking nothing, and a new test holds the count equal to what the list holds at every entry. - An entry that vanishes before its lstat is passed over, and the clock is first read after the source's own folder, each with its own test. - Names that sort before "/" pin the sort to the folders one folder added, and the clock test that meant the walk's clock has a root file. - The look's stop line is at info, as the walk's is, and has a test. TH-9525 Signed-off-by: khushalsonawat <khushalsonawat@gmail.com>
1 task done
cdileep23
approved these changes
Nov 11, 2026
khushalsonawat
merged commit Nov 11, 2026
0bc9118
into
feat/TH-8804-source-subfolder
11 checks passed
khushalsonawat
added a commit
that referenced
this pull request
Nov 11, 2026
… into TH-9491 - tools.py: _save_contract keeps the single writer (write_contract) and takes #268's new reply text, which names unconfirmed prompt lines. - contract.py: #268's new fields, unconfirmed_prompt_from and model (ModelSource), are marked as kept as written: they hold source lines and the cited model name, read by the build. The test that fails on an unmarked field caught them. Signed-off-by: khushalsonawat <khushalsonawat@gmail.com>
2 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Before anything else, the agent scan checks that the source holds a regular file. That look was an
os.walkwith nobound. A crafted tree with no file in it, such as many empty folders or folders holding only links, grew the walk's
list of folders still to visit without end, and could kill the 4 GB sandbox before the scan wrote anything. Because
os.walklists a folder whole first, even one folder of millions of empty folders was held before anything could stopit.
The look is now the scan's own short loop. It's bounded by what its list holds, and by the scan's clock. Past either
with no file found, the scan ends the way a walk stopped with nothing found already ends.
Type of Change
For the platform (Dileep)
Two rows of the scan's results table are widened. No new rows and no new codes:
agents.json,cost.jsonerror,scan_limit_reachedagents.json,cost.jsonerror,scan_timed_outstatus: "error", no agents,truncated: false, and no model step.feat/TH-8812-agent-layeraccepts both.ScanErrorSerializertakes any^[a-z0-9_]+$code, andscan_limit_reachedis already in its tests.SCAN_TIMED_OUT = "scan_timed_out"inagent_reading_sandbox.pyis afailurecode, for an abandonedworker. It's not this report's
error.code. They share a string, but not a meaning.Changes
agent_scan.py:_holds_a_filebecomes_look, which returns(word, folders, held).when it moves into it.
lstat+S_ISREG, today's test) wins wherever it is in a folder.limit._scan:limitorclocklogs one line of counts, then writes through_resultwith a walk stopped that way.Tests: 18 new in
test_agent_scan.py.scandirwrapper, never past the bound under ASCII, long and wide-character paths.entries.
test_the_clock_ends_in_a_filegains a root file, so it still drives the walk's clock.Testing
One file at a time, on Python 3.12.14 and 3.12.3, all passed:
test_agent_scantest_scan_walktest_scan_folderstest_authoring_entrypoint_find_agentstest_reading_entrytest_harness_architecturePlanted breaks: 17, one per guard, each caught and restored. Both change reviewers ran every one.
Crafted trees, through the real scan, each under a 400-character path so it passes 64 MiB:
scan_limit_reached, in under a secondscan_source_missing, after walking it allscan_limit_reachedscan_source_missingscan_limit_reachedscan_source_missingAt this size the old walk doesn't crash the laptop either, so this shows the bound and the outcome, not a prevented
kill.
Real repositories (the kit, and a monorepo checkout): the look finds a file in its first folder, as before.
Review, on Lane N:
wrong in the code.
What wasn't reviewed
d0b0fbf5fixes the change review's findings without another round. It's tests, plus the stop line's level, now info.Not changed here
A tree with a file found early and a huge empty subtree behind it gets past the look. On this branch, the scan's main
walk then holds that subtree's folders with no bound. #273 bounds that walk.
Related Issues
Refs: TH-9525