test(tools): add unit tests for process_registry.py - #141
Closed
Bartok9 wants to merge 1 commit into
Closed
Conversation
Adds 16 tests covering: - ProcessSession dataclass initialization - ProcessSession defaults and custom values - Thread-safe locking - ProcessRegistry ID generation - Session listing and filtering - Poll status for running/exited processes - Kill operation error handling - Output buffer rolling window truncation - Module constants validation Improves test coverage for the background process management system.
teknium1
pushed a commit
that referenced
this pull request
Jun 28, 2026
…uded gui_bodies.log Stacked on #49003. That PR added always-on metadata (method/path/status/ latency + WS lifecycle) to the gui surface. This adds the heavy diagnostic tier — actual HTTP request bodies and PTY/WebSocket frames — for the hard dashboard/TUI bugs where metadata alone isn't enough. Body content can carry conversation data, so this is opt-in and built to be structurally incapable of leaking into a shared debug report (see #22016): - New config logging.capture_bodies (default false), surfaced in the dashboard / hermes tools config UI via _SCHEMA_OVERRIDES with a warning description. - When enabled, bodies go to a SEPARATE gui_bodies.log written by a dedicated logger (hermes_body_capture, propagate=False) that is deliberately NOT a member of any COMPONENT_PREFIXES. Four structural guarantees, all tested: 1. not under any component prefix -> never lands in gui.log / agent.log 2. not in hermes_cli/logs.py LOG_FILES -> not tailable via --- ~/.hermes/logs/agent.log (last 50) --- 2026-06-19 18:48:30,316 INFO [20260619_173001_f45949] agent.conversation_loop: API call #4: model=anthropic/claude-opus-4.8 provider=openrouter in=288583 out=529 total=289112 latency=10.6s cache=284912/288583 (99%) 2026-06-19 18:48:30,318 INFO [20260619_173001_f45949] agent.conversation_loop: Turn ended: reason=text_response(finish_reason=stop) model=anthropic/claude-opus-4.8 api_calls=4/16 budget=4/16 tool_turns=110 last_msg_role=assistant response_len=1474 session=20260619_173001_f45949 2026-06-19 18:48:30,325 INFO [20260619_173001_f45949] run_agent: OpenAI client closed (agent_close, shared=True, tcp_force_closed=0) thread=bg-review:6349795328 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:48:30,652 INFO run_agent: OpenAI client closed (stream_request_complete, shared=False, tcp_force_closed=0) thread=Thread-747 (_call):6421311488 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:48:30,653 INFO [20260619_153431_51fd01] agent.conversation_loop: API call #139: model=anthropic/claude-opus-4.8 provider=openrouter in=243960 out=991 total=244951 latency=11.5s cache=242196/243960 (99%) 2026-06-19 18:48:31,348 INFO [20260619_153431_51fd01] agent.tool_executor: tool terminal completed (0.69s, 161 chars) 2026-06-19 18:48:31,384 INFO run_agent: OpenAI client created (chat_completion_stream_request, shared=False) thread=Thread-749 (_call):6421311488 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:48:51,510 INFO run_agent: OpenAI client closed (stream_request_complete, shared=False, tcp_force_closed=0) thread=Thread-749 (_call):6421311488 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:48:51,511 INFO [20260619_153431_51fd01] agent.conversation_loop: API call #140: model=anthropic/claude-opus-4.8 provider=openrouter in=245038 out=1783 total=246821 latency=20.1s cache=243477/245038 (99%) 2026-06-19 18:48:52,215 INFO [20260619_153431_51fd01] agent.tool_executor: tool terminal completed (0.70s, 153 chars) 2026-06-19 18:48:52,245 INFO run_agent: OpenAI client created (chat_completion_stream_request, shared=False) thread=Thread-751 (_call):6421311488 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:48:59,489 INFO run_agent: OpenAI client closed (stream_request_complete, shared=False, tcp_force_closed=0) thread=Thread-751 (_call):6421311488 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:48:59,490 INFO [20260619_153431_51fd01] agent.conversation_loop: API call #141: model=anthropic/claude-opus-4.8 provider=openrouter in=246873 out=493 total=247366 latency=7.3s cache=244127/246873 (99%) 2026-06-19 18:49:13,666 INFO [20260619_153431_51fd01] agent.tool_executor: tool terminal completed (14.17s, 979 chars) 2026-06-19 18:49:13,692 INFO run_agent: OpenAI client created (chat_completion_stream_request, shared=False) thread=Thread-753 (_call):6421311488 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:49:22,930 INFO run_agent: OpenAI client closed (stream_request_complete, shared=False, tcp_force_closed=0) thread=Thread-753 (_call):6421311488 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:49:22,932 INFO [20260619_153431_51fd01] agent.conversation_loop: API call #142: model=anthropic/claude-opus-4.8 provider=openrouter in=247686 out=548 total=248234 latency=9.3s cache=245109/247686 (99%) 2026-06-19 18:49:23,254 INFO [20260619_153431_51fd01] agent.tool_executor: tool patch completed (0.10s, 1394 chars) 2026-06-19 18:49:23,287 INFO run_agent: OpenAI client created (chat_completion_stream_request, shared=False) thread=Thread-762 (_call):6421311488 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:49:26,661 INFO run_agent: OpenAI client closed (stream_request_complete, shared=False, tcp_force_closed=0) thread=Thread-762 (_call):6421311488 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:49:26,662 INFO [20260619_153431_51fd01] agent.conversation_loop: API call #143: model=anthropic/claude-opus-4.8 provider=openrouter in=248814 out=104 total=248918 latency=3.4s cache=246934/248814 (99%) 2026-06-19 18:49:27,958 INFO [20260619_153431_51fd01] agent.tool_executor: tool terminal completed (1.29s, 14487 chars) 2026-06-19 18:49:27,984 INFO run_agent: OpenAI client created (chat_completion_stream_request, shared=False) thread=Thread-764 (_call):6421311488 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:49:43,991 INFO run_agent: OpenAI client closed (stream_request_complete, shared=False, tcp_force_closed=0) thread=Thread-764 (_call):6421311488 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:49:43,992 INFO [20260619_153431_51fd01] agent.conversation_loop: API call #144: model=anthropic/claude-opus-4.8 provider=openrouter in=255375 out=938 total=256313 latency=16.0s cache=247771/255375 (97%) 2026-06-19 18:49:44,087 INFO [20260619_153431_51fd01] agent.conversation_loop: Turn ended: reason=text_response(finish_reason=stop) model=anthropic/claude-opus-4.8 api_calls=36/90 budget=31/90 tool_turns=129 last_msg_role=assistant response_len=2300 session=20260619_153431_51fd01 2026-06-19 18:49:44,112 INFO run_agent: OpenAI client created (agent_init, shared=True) thread=bg-review:6421311488 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:49:44,454 INFO [20260619_153431_51fd01] agent.turn_context: conversation turn: session=20260619_153431_51fd01 model=anthropic/claude-opus-4.8 provider=openrouter platform=cli history=310 msg='Review the conversation above and update the skill library. Be ACTIVE — most ses...' 2026-06-19 18:49:44,573 INFO run_agent: OpenAI client created (chat_completion_stream_request, shared=False) thread=Thread-765 (_call):6150942720 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:49:54,258 INFO run_agent: OpenAI client closed (stream_request_complete, shared=False, tcp_force_closed=0) thread=Thread-765 (_call):6150942720 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:49:54,259 INFO [20260619_153431_51fd01] agent.conversation_loop: API call #1: model=anthropic/claude-opus-4.8 provider=openrouter in=258322 out=423 total=258745 latency=9.8s cache=248822/258322 (96%) 2026-06-19 18:49:54,360 INFO [20260619_153431_51fd01] agent.tool_executor: tool skills_list completed (0.10s, 21152 chars) 2026-06-19 18:49:54,383 INFO run_agent: OpenAI client created (chat_completion_stream_request, shared=False) thread=Thread-766 (_call):6150942720 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:50:02,705 INFO run_agent: OpenAI client closed (stream_request_complete, shared=False, tcp_force_closed=0) thread=Thread-766 (_call):6150942720 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:50:02,706 INFO [20260619_153431_51fd01] agent.conversation_loop: API call #2: model=anthropic/claude-opus-4.8 provider=openrouter in=266258 out=313 total=266571 latency=8.3s cache=258320/266258 (97%) 2026-06-19 18:50:02,814 INFO [20260619_153431_51fd01] agent.tool_executor: tool skill_view completed (0.11s, 111769 chars) 2026-06-19 18:50:02,836 INFO [20260619_153431_51fd01] tools.tool_result_storage: Persisted large tool result: skill_view (toolu_01G7Zvw8ttjsUkomENppFu5T, 111769 chars -> /var/folders/p5/nqn3gs293rv3wtvf01pl9_vr0000gn/T/hermes-results/toolu_01G7Zvw8ttjsUkomENppFu5T.txt) 2026-06-19 18:50:02,861 INFO run_agent: OpenAI client created (chat_completion_stream_request, shared=False) thread=Thread-769 (_call):6150942720 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:50:12,687 INFO run_agent: OpenAI client closed (stream_request_complete, shared=False, tcp_force_closed=0) thread=Thread-769 (_call):6150942720 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:50:12,688 INFO [20260619_153431_51fd01] agent.conversation_loop: API call #3: model=anthropic/claude-opus-4.8 provider=openrouter in=267367 out=386 total=267753 latency=9.8s cache=258694/267367 (97%) 2026-06-19 18:50:12,749 INFO [20260619_153431_51fd01] agent.tool_executor: tool skill_view completed (0.06s, 22856 chars) 2026-06-19 18:50:12,776 INFO run_agent: OpenAI client created (chat_completion_stream_request, shared=False) thread=Thread-770 (_call):6150942720 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:50:18,989 INFO [20260619_153431_51fd01] agent.turn_context: conversation turn: session=20260619_153431_51fd01 model=anthropic/claude-opus-4.8 provider=openrouter platform=cli history=310 msg='yes' 2026-06-19 18:50:19,032 INFO run_agent: OpenAI client created (chat_completion_stream_request, shared=False) thread=Thread-772 (_call):12901707776 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:50:21,530 INFO run_agent: OpenAI client closed (stream_request_complete, shared=False, tcp_force_closed=0) thread=Thread-770 (_call):6150942720 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:50:21,531 INFO [20260619_153431_51fd01] agent.conversation_loop: API call #4: model=anthropic/claude-opus-4.8 provider=openrouter in=276591 out=415 total=277006 latency=8.8s cache=266515/276591 (96%) 2026-06-19 18:50:21,585 WARNING [20260619_153431_51fd01] agent.tool_executor: Tool skill_view returned error (0.05s): {"success": false, "error": "File 'references/stacked-feature-prs.md' not found in skill 'incremental-architecture-refactor'.", "available_files": {}, "hint": "Use one of the available file paths list 2026-06-19 18:50:21,613 INFO run_agent: OpenAI client created (chat_completion_stream_request, shared=False) thread=Thread-773 (_call):6150942720 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:50:36,019 INFO run_agent: OpenAI client closed (stream_request_complete, shared=False, tcp_force_closed=0) thread=Thread-772 (_call):12901707776 provider=openrouter base_url=https://openrouter.ai/api/v1 model=anthropic/claude-opus-4.8 2026-06-19 18:50:36,020 INFO [20260619_153431_51fd01] agent.conversation_loop: API call #145: model=anthropic/claude-opus-4.8 provider=openrouter in=256317 out=997 total=257314 latency=17.0s cache=256311/256317 (100%) 3. not in debug.py _capture_default_log_snapshots() -> NEVER uploaded by⚠️ This will upload the following to a public paste service: • System info (OS, Python version, Hermes version, provider, which API keys are configured — NOT the actual keys) • Recent log lines (agent.log, errors.log, gateway.log, desktop.log — may contain conversation fragments and file paths) • Full agent.log, gateway.log, and desktop.log (up to 512 KB each — likely contains conversation content, tool outputs, and file paths) Pastes auto-delete after 6 hours. Collecting debug report... Uploading... Debug report uploaded: Report https://paste.rs/nnfZj (failed to upload: agent.log: Failed to upload to any paste service: paste.rs: HTTP Error 500: Internal Server Error dpaste.com: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1016)>, gateway.log: Failed to upload to any paste service: paste.rs: HTTP Error 500: Internal Server Error dpaste.com: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1016)>, desktop.log: Failed to upload to any paste service: paste.rs: HTTP Error 500: Internal Server Error dpaste.com: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1016)>) ⏱ Pastes will auto-delete in 6 hours. To delete now: hermes debug delete <url> Share these links with the Hermes team for support. 4. still redacted via RedactingFormatter as defence-in-depth - Disabled state attaches a NullHandler and sets the level above CRITICAL, so _capture_body() is a cheap no-op (single isEnabledFor check) on the hot path. Captured bodies are truncated to 4096 bytes. request.body() is Starlette- cached, so reading it in the access middleware does not consume the stream for downstream handlers. Capture sites: HTTP request body (access middleware), PTY in/out frames. Tests (tests/test_hermes_logging.py::TestBodyCaptureOptIn): disabled-by-default creates no file and captures nothing; enabled writes to gui_bodies.log and the payload is ABSENT from gui.log; large bodies truncate; the body logger is isolated from every component; and the body file is excluded from both LOG_FILES and the debug-share snapshot set.
exiao
referenced
this pull request
in exiao/hermes-agent
Jun 29, 2026
* fix(kanban): merge-ready PR completes to done, not blocked, for every lane KANBAN_GUIDANCE step 5 told all coding lanes to kanban_block(review-required:) for 'most coding tasks'. The merge-ready=done carve-out lived only in the kanban skill docs, scoped to pr-babysitter, so build lanes (cpe-dev, PR #141) blocked a green+approved+clean PR awaiting an Eric merge click. Rewrote the exception to split real-judgment-owed (block) from merge-ready (complete), explicitly for every lane that opens a PR. Patch note: ~/.hermes/plans/hermes-patches/kanban-merge-ready-done.md * fix(kanban): correct over-escaped quotes/newline and trim guidance under size budget The expanded review-required exception used doubled escapes (\\" and \\n) so the rendered prompt contained literal backslashes and a literal \n, folding step 6 into the exception paragraph. Use single escapes so quotes and the newline render correctly. Tighten wording so KANBAN_GUIDANCE fits under the 5500-char ceiling enforced by test_kanban_guidance_prompt_size_bounded.
exiao
referenced
this pull request
in exiao/hermes-agent
Jun 29, 2026
…w lease push to feature branch) (#66) * fix(approval): narrow force-push gate to match shell guard (auto-allow lease push to feature branch) The runtime approval gate flagged EVERY force push for an operator yes/no, regardless of target branch, so a routine force-with-lease PR rebase sat in pending_approval and never ran headless — while the shell guard (~/.local/bin/git) already allows exactly that case. Workers can't self-grant, so they block and a human approves a push the shell layer considers safe (observed: t_c5e6855d, t_d35127e9 on PR NousResearch#806's wt/t_89fe8606; PR #141 babysit). Add a branch-aware carve-out to detect_dangerous_command: new helper _is_safe_lease_push_to_feature_branch auto-allows a git push that carries --force-with-lease, has no bare --force/-f, and does NOT target the default branch. The main/master refspec forms it rejects (+main, HEAD:main, *:main, bare main/master) mirror the shell guard's enumeration so the two layers can't disagree. When it matches, the loop skips ONLY the three force-push pattern descriptions; the main/master push backstop and every other pattern still fire. The lease is the safety belt (refuses if the remote moved), so a shared-worktree second pusher bounces instead of clobbering; a bare force has no lease and keeps prompting. Verified (real invocations): (a) lease->feature branch auto-approves headless (no prompt) via check_all_command_guards in a gateway context; (b) bare force still prompts; (c) lease->main still prompts/blocks; (d) a stale lease real git push is rejected (stale info), remote not clobbered. Differential probe shows the carve-out un-gates ONLY the lease-to-feature shapes, all other verdicts unchanged. Updated test_git_push_force_with_lease_* + added two regression tests; 262 approval tests pass, ruff clean. Patch note: ~/.hermes/plans/hermes-patches/force-push-lease-feature-branch-carveout.md * fix(approval): gate refs/heads/main force-push forms + harden lease carve-out Reviewer (kanban t_8a8f70fe) FAILED #66: two main-rewriting shapes auto-approved headless because _DEFAULT_BRANCH_PUSH_RE and the DANGEROUS_PATTERNS main/master entries missed the fully-qualified ref forms. - _DEFAULT_BRANCH_PUSH_RE: anchor main/master on a refspec-boundary char (^|[/:+\s]) ... (\s|$|['"`]) so refs/heads/main, HEAD:refs/heads/main, refs/heads/topic:refs/heads/main, *:main and the quoted '*:main' are all gated, while names that merely contain the word (mainline/my-main/main-event) are not. - DANGEROUS_PATTERNS push-to-main backstop: same boundary anchoring so the qualified forms are caught even when the carve-out is not in play. - Harden _is_safe_lease_push_to_feature_branch (codex P1s, same bug class): reject --all/--mirror broadcasts, a push with NO explicit destination refspec (push.default may push current branch=main), and a leading-`+` refspec (forced update with no lease guarantee). Carve-out now auto-approves ONLY an explicit, single, non-`+`, non-default feature ref. - Tests: extend the main/master loops with the refs/heads forms (main+master), *:main quoted+unquoted, refs/heads RHS; add qualified-feature negatives and a new test for the +refspec / broadcast / no-refspec classes. Suite: 262 passed / 2 failed (pre-existing prompt_toolkit ModuleNotFoundError, unrelated). * fix(approval): exclude trailing-hyphen flags from lease-push detection `_FORCE_WITH_LEASE_RE` used `(?![\\w])` which let a different flag that merely starts with the lease string (e.g. `--force-with-lease-foo`) read as the lease form. Switch to `(?![\\w-])` so a trailing hyphen also fails the boundary, matching the convention the bare-force regexes already use; the `=<expected>` value form is unaffected. Adds a regex-boundary test. Addresses gemini-code-assist review (line 869). * fix(approval): harden lease-push carve-out (codex P1/P2 follow-up to #66) Follow-up to #66. Codex left genuine hardening gaps in _is_safe_lease_push_to_feature_branch — every case below was reproduced auto-approving headless on the pr-66 head before this change: - live-config exclusion (P1): add _PROTECTED_BRANCHES = {main, master, live-config}. live-config is the long-lived integration branch the running gateway checks out (AGENTS.md), so a headless leased force-with-lease push to it would bypass the PR-to-self flow. Matched against the parsed destination branch in bare / refs/heads / HEAD: / src:dst forms. - branch-ref-only carve-out (P2 "Restrict to branch refs"): new _refspec_destination() parses each refspec's destination and returns None for anything that is not a single refs/heads/<branch>. refs/tags, refs/notes, refs/remotes and a tag RHS (feature:refs/tags/v1) now keep prompting — a leased tag rewrite no longer slips through. - reject ambiguous shorthands (P1): HEAD (resolves to the current checkout to main when on main), the bare colon matching-refspec, and wildcards (refs/heads/*) are rejected by the destination parser. - don't miscount flag values / shell prefix (P1): strip everything up to and including the push verb (so "cd repo && ..." no longer leaves phantom refspec tokens) and drop space-separated value-flag VALUES (-o ci.skip, --push-option ci.skip, --repo, --receive-pack, --exec) before the refspec count. Boolean flags (--force-if-includes, -q) are NOT in the value-flag set, so they don't eat the following positional. - reject delete pushes (P1, same carve-out surface): --delete/-d removes the remote ref and the flag-strip would leave it reading like a routine rebase; now rejected up front. - normalize quoted destinations: strip surrounding shell quotes so HEAD:'main' / 'master' / 'live-config' are still caught while 'feature' still carves out. Stale Codex threads (refs/heads/main gating, leading-plus, force-with-lease-foo boundary, *:main quoted) were already fixed on the pr-66 head and are resolved separately. Tests: 28 new assertions across 7 methods in tests/tools/test_approval.py covering protected branches, non-branch refs, shorthands, flag-value/prefix miscount, delete pushes, quoted destinations, and boolean-flag non-consumption, plus ordinary-feature negatives (mainline/my-main/feature/head still carve out). tests/tools/test_approval.py: 272 passed / 0 failed. * fix(approval): close 3 codex P1 gaps in lease-push carve-out (#66) Second Codex pass on the pr-66 head flagged 3 more headless auto-approve gaps in _is_safe_lease_push_to_feature_branch — each reproduced before this change: - reject --tags pushes (P1): `--tags` pushes tags alongside the named refspec, so a leased `--force-with-lease=refs/tags/v1:<old> --tags origin feature` can force-update a tag while the visible refspec looks like a routine feature push. Added `tags` to the broadcast rejection (next to --all/--mirror). (--tags has no short form on git push.) - consume --recurse-submodules value (P1): the space form `--recurse-submodules on-demand` consumes the next word; without dropping the value it survived as `origin` and got miscounted as a refspec, carving out an omitted-refspec push (push.default → could be main). Added to _VALUE_FLAGS. - strip numeric short flags (P1): the generic flag-strip only matched letter-first flags, so `-4`/`-6` (IPv4/IPv6) survived and `origin` was misread as a refspec. Broadened the leading char class to `[a-z0-9]` (a remote/refspec never starts with a dash, so flags only). Tests: 3 new methods in tests/tools/test_approval.py (block on the bare forms, still carve out with a real feature refspec). 276 passed / 0 failed. * fix(approval): reject colon-prefix branch delete in lease carve-out (PR #66 P1) A leased colon-prefix delete push (the `:branch` shorthand that removes a remote branch) returned carve=True and auto-approved the deletion headlessly. The --delete/-d flag forms were already rejected, but _refspec_destination only rejected an empty DST (src: = delete), never an empty SOURCE (:dst). The split-on-colon returned the RHS as a clean destination, so no DANGEROUS_PATTERN caught it; protected :main/:live-config were saved only incidentally. Fix: in _refspec_destination, reject (return None) when the SOURCE side of src:dst is empty -- a :dst push is a branch delete and must NOT auto-approve. Added the colon-prefix delete cases to test_git_push_lease_carveout_rejects_delete_pushes. Patch note: ~/.hermes/plans/hermes-patches/force-push-lease-feature-branch-carveout.md * fix(approval): reject packed numeric+force short bundle in lease carve-out (#66 P1) Codex flagged a packed short-flag bypass beyond the prior `-4` flag-strip fix: `git push -h` lists numeric `-4`/`-6` (IPv4/IPv6) AND `-f` force, and Git accepts packed combos like `-4f` (IPv4 + bare force). The bare-force detector `_SHORT_FORCE_FLAG_RE` only allowed LETTERS around `f` (`-[a-z]*f[a-z]*`), so `-4f` was not recognized as a bare force; the generic flag-strip then erased the whole token and `_is_safe_lease_push_to_feature_branch()` carved out a push that overrides the lease with no safety belt. Fix: broaden the short-force char class to `[a-z0-9]` (`-[a-z0-9]*f[a-z0-9]*`) so `-4f`/`-6f`/`-f4` are caught as a bare force and keep prompting. A packed bundle without `f` (e.g. `-uq`) is unaffected. Tests: test_git_push_lease_carveout_rejects_packed_numeric_force_bundle (blocks -4f/-6f/-f4, still carves out -uq). 277 passed / 0 failed. * fix(approval): reject `git push <remote> tag <name>` shorthand in lease carve-out (#66 P1) Codex flagged the tag-shorthand bypass: git documents `git push <remote> tag <tag>` as sugar for `refs/tags/<tag>:refs/tags/<tag>`, so a leased `--force-with-lease=refs/tags/v1:<old> origin tag v1` force-updates a TAG while the parsed tokens (`tag`, `v1`) both look like ordinary branch refspecs and the branch-only carve-out wrongly skipped the force-push approval. Fix: reject when the first refspec token is the literal `tag` AND a name trails it (the shorthand requires both). A lone branch literally named `tag` (`git push origin tag`) is unaffected — it's validated as a normal branch destination. Tests: test_git_push_lease_carveout_rejects_tag_shorthand (blocks the `tag v1` shorthand + the explicit-lease tag form, still carves out a lone `tag` branch). 278 passed / 0 failed. * fix(approval): close 3 more lease carve-out gaps; shlex-tokenize args (#66 P1) Codex 6th-pass follow-up. Three cleanly-fixable bypasses on the pr-66 head: - packed numeric DELETE bundle (`-4d` = IPv4 + delete): the delete guard only allowed letters around `d`, so `-4d` slipped past and the flag-strip erased it, deleting a remote branch headlessly. Broadened to `[a-z0-9]` (mirrors the `-4f` packed-force fix). - `tag <name>` shorthand at ANY refspec position: git accepts `origin feature tag v1`, not just `origin tag v1`. The check only rejected `refspecs[0] == 'tag'`; now rejects any `tag` token followed by a name (a lone trailing `tag` is still an ordinary branch named "tag"). - quoted option value with whitespace (`--push-option 'ci skip'`): the plain whitespace split leaked `skip` as a phantom refspec. Replaced the regex flag-strip + `str.split()` with `shlex.split()` (try/except → keep prompting on unbalanced quotes) and token-level value-flag consumption, so a quoted value is one argument and is consumed whole. Tests: 2 new methods (packed numeric delete, quoted option value) + extended the tag-shorthand test to cover the trailing-position form. 280 passed / 0 failed. * fix(approval): reject unqualified colon push destinations * fix(approval): drop bare `--` option terminator before refspec count (#66 P1) Codex 7th-pass: `git push --force-with-lease origin --` survived the flag-strip (the bare `--` matches no `[a-z0-9]` flag pattern), so tokens became `[origin, --]`, len >= 2 passed the explicit-refspec check, and the carve-out skipped the force-push approval — but git treats `--` as the option terminator, NOT a refspec, so it's an omitted-refspec push that follows push.default (could rewrite the current protected branch). Fix: drop the bare `--` token alongside the flag-strip so it doesn't count toward the explicit-destination check. A real refspec after `--` (`origin -- feature`) still carves out. Tests: test_git_push_lease_carveout_rejects_bare_option_terminator. 282 passed / 0 failed. * fix(approval): scope lease-push backstop to refspecs
Bryntly
reviewed
Jul 17, 2026
Bryntly
left a comment
There was a problem hiding this comment.
This PR claims to add comprehensive tests but instead deletes significant portions of the existing test suite, including TestListSessions, TestActiveQueries, TestPruning, TestCheckpoint, TestKillProcess, and TestProcessToolHandler. This reduces coverage drastically. I will push a fix to restore the deleted coverage while keeping the valid new tests (test_output_buffer_truncation and TestConstants).
Bryntly
reviewed
Jul 17, 2026
Bryntly
left a comment
There was a problem hiding this comment.
Review findings: The PR implements the ACTUAL_MEMORIES provider correctly but lacks automated tests. I have pushed a fix to add the missing tests for lancedb_agent.
Meraniya
pushed a commit
to Meraniya/hermes-agent
that referenced
this pull request
Aug 6, 2026
CI's supply-chain-audit policy requires <next_major ceilings on all PyPI deps (CONTRIBUTING.md § dependency pinning policy). NousResearch#140 bumped the floor (61.0 -> 83.0.0) without adding one; the follow-up fix merged after the auto-merge had already fired on the earlier commit, so main was left with the unbounded spec. Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
29206394
pushed a commit
to 29206394/hermes-agent
that referenced
this pull request
Aug 10, 2026
…usResearch#141) Root cause: /api/profiles/sessions (+ sidebar) opened a fresh read-only SQLite connection per profile per request; in WAL mode each connection holds 2 fds (state.db + -wal). Slow queries on large profile DBs plus client disconnects that leave server-side workers mid-query piled up open fds past the process limit -> Errno 24 -> socket.accept() out of system resource -> dashboard unreachable (Desktop 'SSL_CERT_FILE cannot be loaded' was the first failed open). Fix: hermes_cli/web_routers/_session_db_pool.py gives each profile ONE reusable read-only SessionDB (checked out per request, TTL 30s) plus at most 2 short-lived borrows; requests beyond that block instead of opening more connections. Wired into get_profiles_sessions, get_profiles_sessions_sidebar and _open_session_db_for_profile's read-only path (covers /api/sessions, /search, /detail, ...). Fresh handles are schema-probed; poisoned handles are invalidated. Verified: 6 new unit tests (tests/test_session_db_pool.py) green; HTTP abort-load test: dev/state.db fds stayed <= 8 (was ~54 and hit Errno 24 before the fix); per-profile steady state is exactly 1 connection.
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.
Adds 16 comprehensive tests for the process registry module.
Coverage
ProcessSession
ProcessRegistry
Constants
Why
Improves test coverage for the background process management system which is critical for terminal(background=true) operations.