fix(cron): stop lifecycle guard false-positives and crashes on .py/binary scripts - #77332
Merged
kshitijk4poor merged 2 commits intoAug 3, 2026
Conversation
…nary scripts The gateway lifecycle guard (cron/lifecycle_guard.py) applied shell-style tokenization and script-reference resolution to non-shell content, with two regressions: NousResearch#77131 - every .py cron script using pathlib division was hard-blocked: Path.home() / ".hermes" / ".env" tokenizes the bare "/" operator as an executable path, which resolves to the filesystem root; the regular-file check then fails closed as unsafe. Since Python runs under the interpreter, never through a POSIX shell, the shell-script reference walk is a false-positive generator on Python sources. check_gateway_lifecycle now skips the walk for *.py scripts (the direct command regex still scans the full text), and _iter_referenced_shell_scripts skips pure-separator tokens. NousResearch#76762 - terminal commands invoking a binary by absolute path (e.g. /usr/bin/python3) crashed the guard with ValueError: embedded null byte: the walk read the binary's bytes, decoded them as text, and re-tokenized machine code; the recursion then hit Path.resolve() on a NUL-bearing path while only OSError was caught. _read_referenced_script now skips NUL-containing files (binaries are not referenced shell scripts) and resolve() tolerates ValueError. Shell scripts (.sh/.bash/.zsh) keep the full deep scan; literal lifecycle commands in .py scripts are still blocked by the direct regex. New tests cover all four behaviors.
kshitijk4poor
enabled auto-merge (rebase)
August 3, 2026 04:26
On Linux, /usr/bin/python3 is >1MB, so the size check fired before the NUL check could run — the binary was returned as unsafe=True (blocked) instead of (None, False) (skip). Reorder: read the bounded chunk first, check for NUL bytes (binary → skip), then check size (oversized text → fail closed).
kshitijk4poor
force-pushed
the
salvage/cron-lifecycle-guard-77201
branch
from
August 3, 2026 04:37
640224d to
39dac43
Compare
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.
Summary
Python cron scripts using pathlib division (e.g.
Path.home() / ".hermes") are no longer false-positive blocked by the lifecycle guard, and binary scripts invoked by absolute path no longer crash it.Root cause:
_iter_referenced_shell_scriptstreated pathlib's bare/operator as an executable path, resolved it to the filesystem root, and failed closed on every.pyscript containingPath(...) / "...". Separately, binary executables with NUL bytes crashedPath.resolve()withValueError: embedded null byte(#76762).Changes
cron/lifecycle_guard.py: skip the shell-script reference walk for.pyscripts (they run via the Python interpreter, never through a shell); preserve direct regex scanning for lifecycle commands in their text. Pure-separator/tokens are filtered in_iter_referenced_shell_scriptsso shell-script chains are still caught. Binary files with NUL bytes return(None, False)instead of crashing, andPath.resolve()catchesValueErroralongsideOSError.tests/hermes_cli/test_gateway_restart_loop.py: 4 new tests — pathlib division not blocked, literal lifecycle command in.pystill blocked, binary path doesn't crash, shell-script reference walk still catches nested lifecycle commands.Validation
.pywithPath.home() / ".hermes".pywithhermes gateway restart/usr/bin/python3 -c "print(1)"in prompt.shchain →hermes gateway restarttest_gateway_restart_loop.pypassCredit
Cherry-picked from #77201 by @criptogus. Closes #77131. Also fixes #76762.
Competing PRs #77137 and #77230 addressed the same issue but with less coverage — #77137 removes
"/" in executableentirely (weakens shell-chain detection), #77230 has no tests and includes unrelated changes.