ci: remove unused variables (linter)

This commit is contained in:
Maciej Lisiewski committed 2026-10-10 15:20:14 +02:00
1 parent 7ef490ed3b
commit bda3aea496
117 files changed
+84 -185

No files matched your search

@@ -129,7 +129,6 @@ def radon(files):
R = shutil.which("radon") or os.path.join(os.path.dirname(sys.executable), "radon")
import tempfile
out = {}
lst = "\n".join(files)
# radon can't take a list file; run per-directory roots instead
roots = sorted({os.path.relpath(f, TREE).split(os.sep)[0] if os.sep in os.path.relpath(f, TREE) else os.path.relpath(f, TREE) for f in files})
roots = [r for r in roots if r != "tests"]
-1
View File
@@ -428,7 +428,6 @@ def main():
summaries = []
if args.also_uncompacted:
spec = {"ctor": {}, "attrs": {"tail_token_budget": 10**9}}
# control: no compression at all — answer from the full transcript
context_text = serialize_for_exam(messages, char_cap=900_000)
results = []
-3
View File
@@ -61,9 +61,6 @@ class Peer(BaseHTTPRequestHandler):
def do_POST(self):
body = json.loads(self.rfile.read(int(self.headers["Content-Length"])))
requests.append({"path": self.path, "body": body})
users = [
m.get("content") for m in body.get("messages", []) if m["role"] == "user"
]
fail = fault == "http400"
if fail:
data = {
@@ -131,7 +131,6 @@ class _OAStream:
def patched_create(self, *a, **kw):
msgs = kw.get("messages") or []
import re as _re
first = msgs[0] if msgs else {}
sysm = next((m for m in msgs if m.get("role") == "system"), None)
nonsys = [m for m in msgs if m.get("role") != "system"]
f0 = nonsys[0] if nonsys else {}
@@ -28,12 +28,11 @@ def claims(token):
records=[]
class Handler(BaseHTTPRequestHandler):
def do_POST(self):
raw_body=self.rfile.read(int(self.headers.get('Content-Length',0)))
self.rfile.read(int(self.headers.get('Content-Length',0)))
if self.path == '/api/oauth/token':
records.append({'path':self.path,'refresh':True})
raw=json.dumps({'access_token':refresh_reply,'refresh_token':'fixture-rotated','expires_in':3600,'token_type':'Bearer','scope':'inference:invoke'}).encode()
self.send_response(200);self.send_header('Content-Type','application/json');self.send_header('Content-Length',str(len(raw)));self.end_headers();self.wfile.write(raw);return
body=json.loads(raw_body or '{}')
bearer=self.headers.get('Authorization','').removeprefix('Bearer ')
records.append({'path':self.path,'sub':claims(bearer).get('sub') if bearer else None})
if bearer and claims(bearer)['exp'] < time.time():
@@ -124,7 +124,6 @@ def run(out_path: str) -> dict:
cc.context_length = CONTEXT_LENGTH
cc.threshold_tokens = THRESHOLD
compress_calls: list[dict] = []
original = agent._compress_context
def counting(messages, system_message, **kw):
# Real compaction would need a summarizer; drop everything but the last 2 rows like one.
+1 -1
View File
@@ -62,7 +62,7 @@ def run_one(scenario: dict[str, Any], mode: str, rep: int, out_dir: Path) -> dic
hermes_home = base.setup_isolated_home(enabled, listing=("auto" if mode == "listing" else "off"))
os.environ["HERMES_HOME"] = str(hermes_home)
base.reset_module_state()
n_registered = base.register_fake_tools()
base.register_fake_tools()
base.FIXTURE_NOTES.parent.mkdir(parents=True, exist_ok=True)
base.FIXTURE_NOTES.write_text("Hello from the test fixture.\n", encoding="utf-8")
@@ -184,7 +184,6 @@ def run_one(scenario, mode, scale, rep, out_dir: Path):
error = None
final_response = ""
messages_out: list[dict[str, Any]] = []
pm = None
_orig_norm = None
try:
from run_agent import AIAgent
+1 -2
View File
@@ -326,7 +326,6 @@ class _Collector:
"""Claim events on one board, appending delivery dicts to ``deliveries``."""
if not self._board_has_subs(slug):
return
kb = self.kb
try:
conn = _kbc().connect(board=slug)
except Exception as exc:
@@ -791,7 +790,7 @@ class _KanbanNotification:
if self.wake_kinds:
wake_payloads.append((self.synth, self.wake_diagnostic, self.wake_kinds))
self.d = {**self.d, "events": original_events}
wake_kinds, is_push = self.wake_kinds, self.is_push_adapter
is_push = self.is_push_adapter
from gateway.wake import WakeNotAccepted
# A requested wake is required even when its passive ping already landed.
+1 -1
View File
@@ -2537,7 +2537,7 @@ class GatewayTurnMixin:
)
await sender(chat_id=source.chat_id, metadata=_thread_metadata, **{key: media_path})
except Exception as e:
except Exception:
logger.exception("Background task %s failed", task_id)
# Automatic failure diagnostic (the task produced no requested result to deliver).
with suppress(Exception):
+2 -2
View File
@@ -867,7 +867,7 @@ def _start_or_report_running(running_pids: list[int] | None = None) -> None:
if running_pids:
_report_already_running(running_pids)
else:
pid = _spawn_detached()
_spawn_detached()
_report_gateway_start("direct spawn")
@@ -1689,7 +1689,7 @@ def start() -> None:
# Manual starts use the same console-less direct spawn as restart() and install --start-now;
# Scheduled Task / Startup entries are only login persistence.
pid = _spawn_detached()
_spawn_detached()
_report_gateway_start("direct spawn")
@@ -85,7 +85,7 @@ class TestCloudCheckDepsLive:
class TestHealthCheckLive:
def test_health_check_passes(self, cloud_key, capsys):
from health_check import main as health_main
rc = health_main(["--host", "https://cloud.comfy.org", "--api-key", cloud_key])
health_main(["--host", "https://cloud.comfy.org", "--api-key", cloud_key])
captured = capsys.readouterr()
# Should produce JSON
import json
@@ -176,7 +176,6 @@ class TestDuplicateDeduplication:
schema = extract_schema(wf)
params = schema["parameters"]
# Both seeds present with disambiguated names
seed_keys = [k for k in params if "seed" in k]
# Symmetric: both renamed (no bare "seed")
assert "seed" not in params
assert "seed_3" in params and "seed_4" in params
@@ -151,7 +151,6 @@ def render_brief(plan: dict) -> str:
f"| {d.get('format', '?')} | {d.get('resolution', '?')} | "
f"{d.get('notes', '')} |"
)
deliv_table = "\n".join(deliv_rows) if deliv_rows else "_(none)_"
# Replacements (single-pass)
replacements = {
@@ -228,7 +228,6 @@ def cmd_trades(limit: int = 10, market: str | None = None):
size = t.get("size", "?")
outcome = t.get("outcome", "?")
title = t.get("title", "?")[:50]
ts = t.get("timestamp", "")
print(f" {side:4} {price:>7} x{float(size):>8.2f} [{outcome}] {title}")
@@ -116,7 +116,6 @@ def _build_request(url: str, headers: dict | None = None) -> urllib.request.Requ
def fetch_url(url: str, headers: dict | None = None, retries: int = MAX_RETRIES) -> dict | list | None:
"""Fetch a URL, parse JSON, retry on transient errors."""
last_err = None
for attempt in range(retries):
try:
req = _build_request(url, headers)
@@ -124,17 +123,14 @@ def fetch_url(url: str, headers: dict | None = None, retries: int = MAX_RETRIES)
raw = resp.read()
return json.loads(raw.decode("utf-8", errors="replace"))
except urllib.error.HTTPError as e:
last_err = e
if e.code in {404, 400}:
break # no point retrying
wait = BACKOFF_BASE ** attempt
time.sleep(wait)
except urllib.error.URLError as e:
last_err = e
except urllib.error.URLError:
wait = BACKOFF_BASE ** attempt
time.sleep(wait)
except json.JSONDecodeError as e:
last_err = e
except json.JSONDecodeError:
break
return None
@@ -572,7 +568,6 @@ def cmd_compare(symbols: list[str]) -> None:
# 52w performance: (current - 52w_low) / (52w_high - 52w_low)
try:
price_f = float(entry["price"]) if entry["price"] else None
high_f = float(entry["52w_high"]) if entry["52w_high"] else None
low_f = float(entry["52w_low"]) if entry["52w_low"] else None
if price_f and low_f and price_f > 0 and low_f > 0:
perf = ((price_f - low_f) / low_f) * 100
@@ -2364,7 +2364,6 @@ class Migrator:
entries = plugins.get("entries") or {}
for plugin_name, plugin_cfg in entries.items():
if isinstance(plugin_cfg, dict):
env_vars = plugin_cfg.get("env") or {}
api_key = plugin_cfg.get("apiKey")
if api_key and self.migrate_secrets:
env_key = f"PLUGIN_{plugin_name.upper().replace('-', '_')}_API_KEY"
-1
View File
@@ -256,7 +256,6 @@ def _cmd_status(_args: argparse.Namespace) -> int:
# credential-derived strings (keeps cli.py taint-free for CodeQL).
photon_auth.print_credential_summary(print)
node_bin = find_node_executable("node")
sidecar_installed = sidecar_deps_installed()
print(f" node binary : {node_bin or '✗ missing (install Node 18+)'}")
print(f" sidecar deps : {'✓ installed' if sidecar_deps_installed() else '✗ run `hermes photon install-sidecar`'}")
print(f" telemetry : {'on' if _telemetry_enabled() else 'off'} (`hermes photon telemetry on|off`)")
-1
View File
@@ -872,7 +872,6 @@ def drift(*, include_venv: bool = True) -> dict[str, str]:
problems: dict[str, str] = {}
lockfile = _lockfile()
facts = _facts()
store = _store()
target = current_target()
for name in lockfile.names():
try:
+1 -1
View File
@@ -276,7 +276,7 @@ select = [
# "F821",
"F822",
"F823",
# "F841",
"F841",
"F842",
"F901",
"PGH005",
-1
View File
@@ -776,7 +776,6 @@ def scan_file(path: Path, footguns: list[Footgun]) -> list[tuple[int, str, Footg
continue
# Now check for docstring-open in the (possibly after-triple) portion.
# Scan for the first unescaped '''/""" in the current code_for_scan.
stripped = code_for_scan.strip()
for delim in ('"""', "'''"):
if delim in code_for_scan:
# Count occurrences — even count means single-line docstring,
-1
View File
@@ -291,7 +291,6 @@ def render_comment(
info = by_severity.get("info", [])
debug = by_severity.get("debug", [])
has_blocking = any(by_severity.get(s) for s in _BLOCKING_SEVERITIES)
body = f"{MARKER}\n# ૮ >ﻌ< ა ci review\n\n"
-2
View File
@@ -788,7 +788,6 @@ def _regressions(timings: dict, baseline: dict | None) -> str:
rows = []
for _, diff, job, step, cur, bl_d in top:
cls = "slower" if diff > 0 else "faster"
tag = f'<span class="tag {"slow" if diff > 0 else "fast"}">{"+" if diff > 0 else ""}{diff:.1f}s</span>'
rows.append(
f'<tr>'
@@ -860,7 +859,6 @@ def generate_html(timings: dict, baseline: dict | None = None) -> str:
def generate_summary(timings: dict, baseline: dict | None = None) -> str:
stats = compute_stats(timings, baseline)
bl_map = {j["name"]: j for j in (baseline or {}).get("jobs", [])}
lines = ["## CI Timing Summary\n"]
-2
View File
@@ -291,8 +291,6 @@ def collect_salvaged_contributors(since_tag, until="HEAD"):
pr_number = pr.get("number", "?")
# Also credit the PR author
pr_author = pr.get("author", {})
pr_author_login = pr_author.get("login", "") if isinstance(pr_author, dict) else ""
for pattern in SALVAGE_PATTERNS:
for match in pattern.finditer(body):
-1
View File
@@ -61,7 +61,6 @@ def main() -> int:
outbox_directory=scratch / "outbox",
)
today = datetime.now(UTC).date().isoformat()
# The generator only exports COMPLETED periods, so the realistic E2E
# package is yesterday's. It also has to be: the consent gate only
# releases a package once its whole period is confirmed consented, and
-1
View File
@@ -967,7 +967,6 @@ def cmd_check(source: Path, out: Path) -> int:
for i in range(count):
entry = data[6 + i * 16 : 6 + (i + 1) * 16]
w = entry[0] or 256
h = entry[1] or 256
sizes.append(w)
return sorted(set(sizes))
+1 -1
View File
@@ -227,7 +227,7 @@ def read_archive_bytes(key: str) -> bytes:
def advance_stable(env: dict, release: dict, root: Path) -> dict:
"""Advance one published release from its immutable tag-scoped receipts."""
creds, base, bucket = r2.credentials()
store = R2ChannelStore(creds, base, bucket)
R2ChannelStore(creds, base, bucket)
public_base = r2.public_base_url()
key = f"releases/tag/{release['claim_tag']}/release-candidates.json"
digest = hashlib.sha256(read_archive_bytes(key)).hexdigest()
+1 -1
View File
@@ -922,7 +922,7 @@ def _load_durations(repo_root: Path) -> dict[str, float]:
return {}
try:
return json.loads(path.read_text(encoding="utf-8-sig"))
except (json.JSONDecodeError, OSError) as e:
except (json.JSONDecodeError, OSError):
print("[ERROR] Failed to load json durations file! {e}")
return {}
@@ -231,7 +231,7 @@ def test_edit_swap_image(deck, workdir):
"YPj/HwADAgH/p5UronAAAAAASUVORK5CYII=")
blue_path = workdir / "blue.png"
blue_path.write_bytes(blue)
outline = run("pptx_read.py", deck)
run("pptx_read.py", deck)
# find picture shape name via python-pptx directly
sys.path.insert(0, SCRIPTS)
from pptx import Presentation
-1
View File
@@ -383,7 +383,6 @@ def _live_system_guard(request, monkeypatch):
return _GuardedPopen
real_run = _subprocess.run
real_popen = _subprocess.Popen
real_call = _subprocess.call
real_check_call = _subprocess.check_call
real_check_output = _subprocess.check_output
+1 -1
View File
@@ -936,7 +936,7 @@ class TestPreflightCompression:
patch.object(agent, "_save_trajectory"),
patch.object(agent, "_cleanup_task_resources"),
):
result = agent.run_conversation("hello", conversation_history=big_history)
agent.run_conversation("hello", conversation_history=big_history)
mock_compress.assert_not_called()
@@ -444,7 +444,7 @@ class TestStaleAttemptEndToEnd:
finally:
a_done.set()
gen1 = _claim_compressor_attempt(cc)
_claim_compressor_attempt(cc)
with patch("agent.context_compressor.call_llm", side_effect=fake_call_llm):
t = threading.Thread(target=attempt_a, daemon=True)
thread_a[0] = t
+1 -1
View File
@@ -56,7 +56,7 @@ def test_prune_only_pass_takes_no_snapshot_but_still_ages_old_ones_out(env, monk
def test_only_one_process_claims_a_due_pass(env, monkeypatch):
curator, home = env["curator"], env["home"]
curator = env["curator"]
monkeypatch.setattr(curator, "should_run_now", lambda now=None: True)
started, release = threading.Event(), threading.Event()
runs = []
@@ -69,7 +69,7 @@ from agent.agent_runtime_helpers import sanitize_tool_call_arguments
def simulate_compression(msgs):
"""Rewrite the middle of the history with fresh dict copies + a summary."""
head, mid, tail = msgs[:2], msgs[2:-6], msgs[-6:]
head, tail = msgs[:2], msgs[-6:]
summary = {"role": "user", "content": "SUMMARY OF DROPPED CONTEXT " + UNI}
new = [dict(m) if isinstance(m, dict) else m for m in head]
new.append(summary)
@@ -541,7 +541,7 @@ class TestConversationGenerationRotates:
assert resolve_prompt_cache_scope(again) == scope_mine
def test_scope_never_carries_the_raw_key_or_boundary(self, db):
agent = self._keyed(db, "sess-A")
self._keyed(db, "sess-A")
db.end_session("sess-A", "session_reset")
rotated = self._keyed(db, "sess-B")
scope = resolve_prompt_cache_scope(rotated)
@@ -74,7 +74,6 @@ class TestCompressNoOpRegistersIneffective:
# Mock _find_tail_cut_by_tokens to return head_end,
# causing compress_start >= compress_end
original = comp._find_tail_cut_by_tokens
comp._find_tail_cut_by_tokens = lambda msgs, he: he # force no-op
result = comp.compress(messages, current_tokens=73_000)
+1 -2
View File
@@ -222,7 +222,6 @@ class TestInsightsPopulated:
"""A session that switches models mid-flight is split across both
models in the breakdown, not dumped on the initial model (#51607).
"""
now = time.time()
db.create_session(session_id="sw", source="cli",
model="deepseek/deepseek-v4-pro")
# 40k tokens on deepseek, then switch and 50k on opus.
@@ -534,7 +533,7 @@ class TestEdgeCases:
assert report["platforms"][0]["platform"] == "cli"
# Terminal format should NOT show platform section for single platform
text = engine.format_terminal(report)
engine.format_terminal(report)
# (it still shows platforms section if there's only cli and nothing else)
# Actually the condition is > 1 platforms OR non-cli, so single cli won't show
@@ -46,5 +46,5 @@ def test_create_openai_client_preserves_already_suffixed_kimi_url(monkeypatch):
"base_url": "https://api.kimi.com/coding/v1",
}
client = create_openai_client(mock_agent, client_kwargs, reason="test", shared=False)
create_openai_client(mock_agent, client_kwargs, reason="test", shared=False)
assert captured_kwargs.get("base_url") == "https://api.kimi.com/coding/v1"
@@ -98,7 +98,6 @@ class TestQueryLocalContextLengthVllm:
from agent.model_metadata import _query_local_context_length
detail_resp = self._make_resp(200, {"id": "omnicoder-9b", "max_model_len": 100000})
list_resp = self._make_resp(404, {})
client_mock = MagicMock()
client_mock.__enter__ = lambda s: client_mock
@@ -285,7 +284,6 @@ class TestQueryLocalContextLengthModelsList:
"""
from agent.model_metadata import _query_local_context_length
detail_resp = self._make_resp(404, {})
list_resp = self._make_resp(200, {
"data": [
{
@@ -317,7 +315,6 @@ class TestQueryLocalContextLengthModelsList:
since the server can only actually serve the runtime value."""
from agent.model_metadata import _query_local_context_length
detail_resp = self._make_resp(404, {})
list_resp = self._make_resp(200, {
"data": [
{"id": "/app/models/m.gguf", "meta": {"n_ctx": 256000, "n_ctx_train": 262144}}
@@ -381,7 +378,6 @@ class TestQueryLocalContextLengthAnthropicProxy:
def test_models_list_prefers_max_input_tokens(self):
from agent.model_metadata import _query_local_context_length
detail_resp = self._make_resp(404, {})
list_resp = self._make_resp(200, {
"data": [
{
@@ -930,7 +926,6 @@ class TestQueryLocalContextLengthMaxTokensNotContext:
no genuine context-window key is present."""
from agent.model_metadata import _query_local_context_length
detail_resp = self._make_resp(404, {})
list_resp = self._make_resp(200, {
"data": [
{
@@ -1000,7 +995,6 @@ class TestReconcileSelfHealsPoisonedCache:
the poisoned 393216 cache entry with 1048576."""
from agent.model_metadata import _reconcile_local_cached_context_length
detail_resp = self._make_resp(404, {})
list_resp = self._make_resp(200, {
"data": [
{
+1 -1
View File
@@ -725,7 +725,7 @@ class TestAuxiliaryClientProviderPriority:
"scope": "inference:invoke",
}
with patch("agent.auxiliary_client._read_nous_auth", return_value=nous_auth), \
patch("agent.auxiliary_client.OpenAI") as mock, \
patch("agent.auxiliary_client.OpenAI"), \
patch("hermes_cli.models.get_nous_recommended_aux_model", return_value=None):
_client, model = get_text_auxiliary_client()
assert model == _NOUS_MODEL
+1 -1
View File
@@ -833,7 +833,7 @@ def test_logical_close_skips_pop_under_concurrent_turn_scope(relay_turn):
observe_top = lambda: lease.host.run_in_session(
lease.session, relay_runtime._current_top, relay
)
top_before_sibling = observe_top()
observe_top()
# A concurrent turn's live scope sits above ours.
sibling_handle = lease.host.run_in_session(
@@ -124,7 +124,6 @@ def test_xai_responses_ttfb_scaled_for_large_requests(monkeypatch):
# 2. Large request (>50k tokens) scales TTFB timeout to 120s, so it does NOT trip at 0.2s
closes.clear()
large_text = "word " * 65_000
stop_large = {"flag": False}
def fake_stream_delayed(api_kwargs, client=None, on_first_delta=None):
# Sleeps for 0.5s (longer than the initial 0.2s TTFB cutoff)
+1 -1
View File
@@ -5859,7 +5859,7 @@ class TestAnthropicBaseUrlPassthrough:
patch("agent.anthropic_adapter.build_anthropic_client") as mock_build,
):
mock_build.return_value = MagicMock()
a = AIAgent(
AIAgent(
api_key="sk-ant-api03-test1234567890",
base_url="https://llm-proxy.company.com/v1",
api_mode="anthropic_messages",
@@ -1613,7 +1613,7 @@ def test_try_refresh_copilot_client_credentials_falls_back_when_exchange_unavail
def test_preflight_codex_api_kwargs_strips_optional_function_call_id(monkeypatch):
agent = _build_agent(monkeypatch)
_build_agent(monkeypatch)
from agent.codex_responses_adapter import _preflight_codex_api_kwargs
preflight = _preflight_codex_api_kwargs(
{
@@ -1640,7 +1640,7 @@ def test_preflight_codex_api_kwargs_strips_optional_function_call_id(monkeypatch
def test_preflight_codex_api_kwargs_rejects_function_call_output_without_call_id(monkeypatch):
agent = _build_agent(monkeypatch)
_build_agent(monkeypatch)
with pytest.raises(ValueError):
from agent.codex_responses_adapter import _preflight_codex_api_kwargs
@@ -1915,7 +1915,7 @@ def test_codex_incomplete_opaque_state_updated_in_place(monkeypatch):
def test_normalize_codex_response_marks_commentary_only_message_as_incomplete(monkeypatch):
agent = _build_agent(monkeypatch)
_build_agent(monkeypatch)
from agent.codex_responses_adapter import _normalize_codex_response
assistant_message, finish_reason = _normalize_codex_response(
_codex_commentary_message_response("I'll inspect the repository first.")
@@ -1930,7 +1930,7 @@ def test_normalize_codex_response_marks_commentary_only_message_as_incomplete(mo
def test_normalize_codex_response_does_not_fallback_to_output_text_for_commentary_only(monkeypatch):
agent = _build_agent(monkeypatch)
_build_agent(monkeypatch)
from agent.codex_responses_adapter import _normalize_codex_response
response = _codex_commentary_message_response("I’ll call the tool now.")
@@ -2187,7 +2187,7 @@ def test_chat_messages_to_responses_input_reasoning_only_has_following_item(monk
"""When converting a reasoning-only interim message to Responses API input,
the reasoning items must be followed by an assistant message (even if empty)
to satisfy the API's 'required following item' constraint."""
agent = _build_agent(monkeypatch)
_build_agent(monkeypatch)
messages = [
{"role": "user", "content": "think hard"},
{
@@ -2445,7 +2445,6 @@ def test_codex_compaction_only_continuation_gets_nudged_before_budget_runs_out(m
_codex_message_response("Here is the answer."),
]
sent_message_counts: list = []
original_call = agent._interruptible_api_call
def _fake_call(api_kwargs):
sent_message_counts.append(api_kwargs)
-1
View File
@@ -172,7 +172,6 @@ class TestAllowlistOps:
# Touch the script to bump its mtime then re-approve.
import os
import time
new_mtime = original_entry.get("script_mtime_at_approval")
time.sleep(0.01)
os.utime(script, None) # current time
@@ -151,7 +151,7 @@ class TestGatewayAutoLoadScaffold:
return f"{joined}\n\n{user_text}" if user_text else joined
def test_describes_the_typed_request(self, skills):
message = self._auto_load_scaffold(skills_dir := skills, ["work"],
message = self._auto_load_scaffold(skills, ["work"],
user_text="Fix the CI gate before the release")
assert describe_skill_invocation(message) == "Fix the CI gate before the release"
-1
View File
@@ -55,7 +55,6 @@ def test_stderr_is_also_routed_per_thread():
def test_many_concurrent_silenced_and_loud_threads():
"""Stress: interleaved silenced/loud threads keep their respective fates."""
start = threading.Event()
results_lock = threading.Lock()
def silenced(i):
start.wait(timeout=2.0)
@@ -944,7 +944,7 @@ def test_flush_atomic_mixed_repair_and_append_rollback_on_failure(tmp_path, monk
agent = _make_agent()
db_path = tmp_path / "state.db"
session_id = "sess-atomic-rollback"
db = _attach_real_session_db(agent, db_path, session_id)
_attach_real_session_db(agent, db_path, session_id)
messages = [
{"role": "user", "content": "summarize"},
@@ -246,7 +246,7 @@ def test_bounded_fallback_records_kanban_failure_when_interrupted(monkeypatch):
agent = _LimitAgent()
# Budget exhausted (60/60), interrupted, no fallback-eligible exit_reason
result = finalize_turn(
finalize_turn(
agent,
final_response=None,
api_call_count=60,
@@ -286,7 +286,7 @@ def test_bounded_fallback_records_kanban_failure_when_failed(monkeypatch):
monkeypatch.setattr("hermes_cli.kanban_db_dispatch._record_task_failure", record)
agent = _LimitAgent()
result = finalize_turn(
finalize_turn(
agent,
final_response=None,
api_call_count=60,
@@ -319,7 +319,7 @@ def test_bounded_fallback_does_not_fire_without_kanban_task(monkeypatch):
monkeypatch.setattr("hermes_cli.kanban_db_dispatch._record_task_failure", record)
agent = _LimitAgent()
result = finalize_turn(
finalize_turn(
agent,
final_response=None,
api_call_count=60,
@@ -351,7 +351,7 @@ def test_bounded_fallback_does_not_fire_when_budget_not_exhausted(monkeypatch):
agent = _LimitAgent(budget_remaining=60)
# api_call_count=10, max_iterations=60 — budget NOT exhausted
result = finalize_turn(
finalize_turn(
agent,
final_response=None,
api_call_count=10,
-1
View File
@@ -234,7 +234,6 @@ class TestYieldedTickIsAFailedTick:
ticked: list[str] = []
per_home_beats: dict[str, list[bool]] = {home_a: [], home_b: []}
per_home_errors: dict[str, list[str]] = {home_a: [], home_b: []}
state = {"cycles": 0}
stop = threading.Event()
prov = InProcessCronScheduler()
+1 -1
View File
@@ -56,7 +56,7 @@ def _create_job() -> dict:
class TestCreateSurfacesGatewayLiveness:
def test_create_with_gateway_running_has_no_warning(self, hermes_env):
with patch_liveness(provider="builtin", pids=[12345]) as patches:
with patch_liveness(provider="builtin", pids=[12345]):
result = _create_job()
assert result["success"] is True
-1
View File
@@ -1299,7 +1299,6 @@ class TestBadNextRunAtRecovery:
from datetime import timezone, timedelta as td
now = datetime.now(UTC)
past = (now - td(seconds=30)).isoformat()
future = (now + td(days=1)).isoformat()
# Bad record: next_run_at is not a valid ISO string (e.g. from hand-edit or corruption)
# Healthy sibling is past due with good schedule.
+1 -1
View File
@@ -155,7 +155,7 @@ def test_merge_does_not_mutate_caller_list(hermes_env):
not grow as a side effect of save_jobs()."""
from cron.jobs import create_job, save_jobs
job = create_job(
create_job(
prompt=None,
schedule="every 2m",
script="watch.sh",
@@ -105,7 +105,7 @@ class TestPersistedStaleErrorRecovery:
job = J.get_job(job_id)
with mock.patch("cron.jobs.load_jobs", return_value=[job]):
n = S.tick(verbose=False, sync=True)
S.tick(verbose=False, sync=True)
latest = E.latest_execution(job_id)
assert latest is not None, (
+1 -1
View File
@@ -100,7 +100,7 @@ class TestStaleInflightSelfHeal:
# and the dispatch loop re-fires it.
job = J.get_job(job_id)
with mock.patch("cron.jobs.load_jobs", return_value=[job]):
n = S.tick(verbose=False, sync=True)
S.tick(verbose=False, sync=True)
latest = E.latest_execution(job_id)
assert job_id not in S.get_running_job_ids(), "stale claim must be released"
+1 -1
View File
@@ -465,7 +465,7 @@ def _serve(spool: Path) -> None: # pragma: no cover - runs in the child process
async def main() -> None:
nonlocal runner
parent = int(os.environ.get("C12_PARENT_PID") or os.getppid())
watchdog = asyncio.ensure_future(orphan_watchdog(parent))
watchdog = asyncio.ensure_future(orphan_watchdog(parent)) # noqa: F841 -- strong ref: the loop holds tasks weakly
runner = GatewayRunner(config)
ok = await runner.start()
if not ok:
@@ -70,7 +70,7 @@ def test_trace_multisegment_draft_flow():
)
return r3
r3 = loop.run_until_complete(turn())
loop.run_until_complete(turn())
print("\n--- OP TIMELINE ---")
for i, op in enumerate(t.ops):
print(f"{i:2d} {op['op']:<16} final={op.get('final')} draft_id={op.get('draft_id')} "
@@ -80,7 +80,6 @@ def _adapter():
async def _open_turn_draft(a, chat_id="D01", draft_id=7):
"""Open a live draft the way a streaming turn does."""
await a.send_draft(chat_id, draft_id, "streaming partial…")
key = a._draft_key(chat_id, None)
candidates = [k for k in a._open_draft_by_chat if k.startswith(f"{chat_id}:")]
assert candidates, "test setup: draft did not arm interception"
return candidates[0]
-2
View File
@@ -527,8 +527,6 @@ def test_restricted_channels_skipped_during_subscribe():
adapter._channel_state[CHANNEL] = {"chat_type": "group", "last_ts": 0, "seen": {}}
adapter._restricted_channels.add(CHANNEL)
subscriptions = {}
class _CountingWs:
sent = []
@@ -224,9 +224,6 @@ class TestIncomingDocumentHandling:
content1 = b"First file content"
content2 = b"Second file content"
call_count = 0
responses = [content1, content2]
def make_session(_responses):
idx = 0
@@ -943,7 +943,6 @@ async def test_discord_dm_does_not_backfill(adapter, monkeypatch):
adapter.config.extra["history_backfill"] = True
adapter._fetch_channel_context = AsyncMock(return_value="[Recent channel messages]\n[Alice] context")
bot_user = adapter._client.user
dm_channel = SimpleNamespace(
id=999,
name=None,
+1 -1
View File
@@ -221,7 +221,7 @@ class TestConfigMigration:
import hermes_cli.config as cfg_mod
importlib.reload(cfg_mod)
result = cfg_mod.migrate_config(interactive=False, quiet=True)
cfg_mod.migrate_config(interactive=False, quiet=True)
# Re-read config
updated = yaml.safe_load(config_path.read_text(encoding="utf-8"))
platforms = updated.get("display", {}).get("platforms", {})
-1
View File
@@ -171,7 +171,6 @@ class TestFeishuAdapterMessaging(unittest.TestCase):
from plugins.platforms.feishu.adapter import FeishuAdapter
adapter = FeishuAdapter(PlatformConfig())
ws_client = SimpleNamespace()
owned_executor = object()
submitted_executors = []
-1
View File
@@ -134,7 +134,6 @@ class TestIRCAdapterMessageParsing:
# Mock handle_message to capture the event
dispatched = []
original_dispatch = adapter._dispatch_message
async def capture_dispatch(**kwargs):
dispatched.append(kwargs)
+1 -4
View File
@@ -475,7 +475,7 @@ class TestCodeExpiry:
def test_expired_codes_cleaned_up(self, tmp_path):
with patch("gateway.pairing.PAIRING_DIR", tmp_path):
store = PairingStore()
code = store.generate_code("telegram", "user1")
store.generate_code("telegram", "user1")
# Manually expire all pending entries
pending = store._load_json(store._pending_path("telegram"))
@@ -535,15 +535,12 @@ class TestListAndClear:
class TestUnreadablePairingFile:
def test_permission_error_logs_warning_and_returns_empty(self, tmp_path, caplog):
import logging
import builtins
approved_path = tmp_path / "weixin-approved.json"
approved_path.write_text(
'{"o9cq80fake@im.wechat": {"user_name": "x", "approved_at": 0}}'
)
real_open = builtins.open
def fake_read_text(self, *a, **kw):
# Path.read_text uses Path.open internally; raise PermissionError
# to mimic a 0600 file owned by a different uid.
@@ -121,7 +121,7 @@ def test_no_pending_approval_does_not_consume_conversational_yes():
session_key = runner._session_key_for_source(source)
# No approval registered.
handled = asyncio.run(
asyncio.run(
runner._handle_active_session_busy_message(_make_event("yes"), session_key)
)
@@ -337,7 +337,6 @@ from plugins.platforms.mattermost.adapter import MattermostAdapter
class TestMattermostMultiImage:
@pytest.fixture
def adapter(self):
config = PlatformConfig(enabled=True, token="fake")
# Minimal construction via object.__new__ to avoid full setup
a = object.__new__(MattermostAdapter)
a._base_url = "https://mm.example.com"
+2 -2
View File
@@ -240,7 +240,7 @@ class TestSendWithRetryRateLimited:
adapter = _StubAdapter()
flood = SendResult(success=False, error="flood control exceeded, retry in 60 seconds")
adapter._send_results = [flood, flood, flood]
with patch("asyncio.sleep", new_callable=AsyncMock) as mock_sleep:
with patch("asyncio.sleep", new_callable=AsyncMock):
result = await adapter._send_with_retry("chat1", "hello", max_retries=2, base_delay=0)
assert not result.success
# 1 initial + 2 retries = 3 sends; NO delivery-failure notice (4th send)
@@ -277,7 +277,7 @@ class TestSendWithRetryFailureTypeTransitions:
SendResult(success=False, error="Bad Request: can't parse entities"),
# fallback send (auto-succeeds via _next_result)
]
with patch("asyncio.sleep", new_callable=AsyncMock) as mock_sleep:
with patch("asyncio.sleep", new_callable=AsyncMock):
result = await adapter._send_with_retry("chat1", "**bold**", max_retries=3, base_delay=0)
# The formatting error was not retried further: exactly 1 retry then the
# plain-text fallback. 1 initial + 1 retry + 1 fallback = 3 sends.
-1
View File
@@ -86,7 +86,6 @@ async def test_sentinel_placed_before_agent_setup():
concurrent message sees the session as occupied."""
runner = _make_runner()
event = _make_event()
session_key = build_session_key(event.source)
# Patch _handle_message_with_agent to capture state at entry
sentinel_was_set = False
-1
View File
@@ -4292,7 +4292,6 @@ class TestTrackingStructureBounds:
"team_id": "T1",
"response_url": f"https://hooks.slack.com/commands/{i}",
}
respond = AsyncMock()
await adapter._handle_slash_command(command)
assert len(adapter._slash_command_contexts) <= adapter._SLASH_CTX_MAX
# Newest stash survives. Keys are workspace-scoped 3-tuples (#20583)
-1
View File
@@ -53,7 +53,6 @@ class TestPolicyFromExtra:
# Admin lists are scope-specific. DM admin must not auto-promote in groups.
extra = {"allow_admin_from": ["111"]}
dm = policy_from_extra(extra, "dm")
gp = policy_from_extra(extra, "group")
assert dm.is_admin("111") is True
# Group has no admin list set → gating disabled in groups → "111"
# gets unrestricted access, but that's the backward-compat fallback,
@@ -139,7 +139,7 @@ class TestFeishuFallbackThreadRouting:
# Call _send_raw_message with reply_to=None and thread_id in metadata
import json
result = await FeishuAdapter._send_raw_message(
await FeishuAdapter._send_raw_message(
adapter,
chat_id="oc_main_chat",
msg_type="text",
+1 -2
View File
@@ -71,7 +71,6 @@ class TestHandleUpdateCommand:
# Since Path(__file__) resolves to the real gateway/run.py,
# project_root will be the real hermes-agent dir (which HAS .git).
# Patch Path to control this.
original_path = Path
class FakePath(type(Path())):
pass
@@ -141,7 +140,7 @@ class TestHandleUpdateCommand:
patch("hermes_cli.config.detect_install_method", return_value="git"), \
patch("shutil.which", side_effect=lambda x: "/usr/bin/hermes" if x == "hermes" else "/usr/bin/setsid"), \
patch("subprocess.Popen"):
result = await runner._handle_update_command(event)
await runner._handle_update_command(event)
pending_path = hermes_home / ".update_pending.json"
assert pending_path.exists()
-1
View File
@@ -189,7 +189,6 @@ class TestSendText:
# At least 2 POST calls
assert adapter._http_client.post.call_count >= 2
# Second call should NOT have context (only first chunk gets reply_to)
first_call = adapter._http_client.post.call_args_list[0]
second_call = adapter._http_client.post.call_args_list[1]
# No reply_to passed → no context anywhere, but verify structure anyway
assert "context" not in second_call.kwargs["json"]
@@ -145,10 +145,6 @@ class TestWhatsAppCloudAdapterUsesSecretScope:
try:
from gateway.platforms.whatsapp_cloud import WhatsAppCloudAdapter
cfg = type("C", (), {
"extra": {},
"enabled": True,
})()
# Cloud adapter won't fully init without creds, but we can at least
# verify the dm_policy assignment path doesn't crash under scope.
# We test via the mixin's behavior instead.
@@ -205,8 +205,6 @@ class TestP0ChatLockEviction:
adapter._outbound.sender._chat_locks[f"chat_{i}"] = asyncio.Lock()
# Lock the oldest entry
oldest_key = next(iter(adapter._outbound.sender._chat_locks))
oldest_lock = adapter._outbound.sender._chat_locks[oldest_key]
# Simulate a held lock by acquiring it in a non-async way (set _locked)
# asyncio.Lock is not held until actually acquired; so we test the
# method logic by acquiring the first lock manually.
@@ -50,7 +50,7 @@ async def test_do_reconnect_calls_set_active_on_success():
mock_ws.close = AsyncMock()
with (
patch.object(cm, "_cleanup_ws", new_callable=AsyncMock) as mock_cleanup,
patch.object(cm, "_cleanup_ws", new_callable=AsyncMock),
patch(
"gateway.platforms.yuanbao.SignManager.force_refresh",
new_callable=AsyncMock,
@@ -33,7 +33,6 @@ class TestCLISubagentInterrupt(unittest.TestCase):
interrupt_detected = threading.Event()
child_started = threading.Event()
child_api_call_count = 0
# Create a real-enough parent agent
parent = AIAgent.__new__(AIAgent)
@@ -63,7 +62,6 @@ class TestCLISubagentInterrupt(unittest.TestCase):
parent._execution_thread_id = None
# We'll track what happens with _active_children
original_children = parent._active_children
# Mock the child's run_conversation to simulate a slow operation
# that checks _interrupt_requested like the real one does
-2
View File
@@ -743,7 +743,6 @@ class TestLaunchdServiceRecovery:
# _launchd_domain() must return user/<uid>.
gateway_cli._resolved_launchd_domain = None
monkeypatch.setattr(os, "getuid", lambda: 501)
label = gateway_cli.get_launchd_label()
def fake_run(cmd, check=False, **kwargs):
if "print" in cmd and "gui/" in " ".join(cmd):
@@ -1608,7 +1607,6 @@ class TestSystemServiceIdentityRootHandling:
"""When root is explicitly passed via --run-as-user root, allow it."""
root_info = pwd.getpwnam("root")
root_group = grp.getgrgid(root_info.pw_gid).gr_name
username, _group, home, _uid = gateway_cli._system_service_identity(run_as_user="root")
assert username == "root"
+1 -1
View File
@@ -100,7 +100,7 @@ class TestSessionActiveDirectory:
monkeypatch.chdir(launch_dir)
record_session_cwd("gateway:999", str(workspace))
tokens = set_session_vars(session_key="gateway:999")
set_session_vars(session_key="gateway:999")
try:
prompt = build_init_prompt_for_cwd()
assert f"for the project at: {workspace}" in prompt
+1 -1
View File
@@ -177,7 +177,7 @@ def test_dependency_block_with_open_parent_stays_parked_across_dispatch_tick(
with kb.write_txn(conn):
conn.execute("UPDATE tasks SET status='running' WHERE id=?", (child,))
assert kb.block_task(conn, child, reason="wait", kind="dependency")
res = kbd.dispatch_once(conn, spawn_fn=fake_spawn)
kbd.dispatch_once(conn, spawn_fn=fake_spawn)
assert kb.get_task(conn, child).status == "todo"
assert child not in spawns
with kb.write_txn(conn):
@@ -72,7 +72,7 @@ def test_capped_tasks_dispatched_on_subsequent_tick(isolated_kanban_home_with_pr
from hermes_cli import kanban_db_dispatch as kbd
with kbc.connect_closing() as conn:
kb.create_board(slug="default", name="Test")
ids = [kb.create_task(conn, title=f"a{i}", assignee="alpha") for i in range(3)]
[kb.create_task(conn, title=f"a{i}", assignee="alpha") for i in range(3)]
# First tick: cap=1, only 1 alpha dispatched
with kbc.connect_closing() as conn:
-1
View File
@@ -56,7 +56,6 @@ def test_create_swarm_graph_is_atomic_and_rolls_back_partial_build(
writer = kbc.connect(db_path)
reader = kbc.connect(db_path)
original_create = kb.create_task
original_complete = kb.complete_task
calls = 0
def observed_create(*args, **kwargs):
@@ -118,10 +118,6 @@ def test_reap_orphans_kills_only_our_parentless_binaries(tmp_path, monkeypatch):
exe = tmp_path / "llama-server.exe"
exe.write_text("")
orphan = _FakeChild(300)
adopted = _FakeChild(301) # parent alive -> not an orphan
foreign = _FakeChild(302) # different binary -> never touched
def _info(pid, exe_path, ppid):
p = _FakeChild(pid)
p.info = {"exe": exe_path, "ppid": ppid}
+1 -1
View File
@@ -117,7 +117,7 @@ class TestGatewayPersonalityNone:
p1, p2 = self._gateway_env(tmp_path)
with p1, p2:
event = self._make_event("default")
result = await runner._handle_personality_command(event)
await runner._handle_personality_command(event)
saved = yaml.safe_load(config_file.read_text())
assert saved["agent"]["system_prompt"] == "manual forever"
+2 -2
View File
@@ -570,11 +570,11 @@ def test_install_refuses_a_non_https_update_url(monkeypatch, tmp_path, update_ur
def test_install_saves_an_https_update_url_tag(monkeypatch, tmp_path):
from hermes_cli.plugins_cmd import _install_plugin_core
repo, _old, sha = _plugin_repo(tmp_path)
repo, _old, _sha = _plugin_repo(tmp_path)
(repo / "plugin.yaml").write_text(
yaml.safe_dump({"name": "demo", "version": "1.0.0", "update_url": " https://feed.example/f.yml "}),
encoding="utf-8")
sha = _commit(repo, "feed", "feed")
_commit(repo, "feed", "feed")
home = tmp_path / "home"
monkeypatch.setenv("HERMES_HOME", str(home))
@@ -1070,7 +1070,6 @@ def test_unload_cancels_a_deferred_platform_before_module_load():
scope=scope,
)
platform_registry.register_deferred(name, old_loader, scope=scope)
displaced = platform_registry.snapshot_registration(name, scope=scope)
manager = PluginManager(scope_key=scope)
manifest = PluginManifest(
name=f"{name}-platform",
@@ -83,7 +83,7 @@ def test_cli_export_rejects_bad_profile_name_without_traceback(
tmp_path, monkeypatch, capsys, profiles
):
"""A bad name must print a clean error — the helper raises before export."""
main_mod = importlib.import_module("hermes_cli.main")
importlib.import_module("hermes_cli.main")
default_home = tmp_path / ".hermes"
default_home.mkdir()
monkeypatch.setattr(profiles, "_get_default_hermes_home", lambda: default_home)
@@ -104,7 +104,7 @@ def test_cli_export_rejects_bad_profile_name_without_traceback(
def test_cli_export_default_does_not_write_into_the_current_checkout(
tmp_path, monkeypatch, capsys, profiles
):
main_mod = importlib.import_module("hermes_cli.main")
importlib.import_module("hermes_cli.main")
default_home = tmp_path / ".hermes"
default_home.mkdir()
(default_home / "config.yaml").write_text("model: test\n", encoding="utf-8")
-2
View File
@@ -1274,7 +1274,6 @@ class TestRenameProfile:
def test_rename_delegates_identity_migration_to_live_gateway(self, profile_env):
"""Under a live multiplexer the CLI must NOT rewrite the routing DB directly (the gateway holds
it in memory and would clobber the write); it delegates to the control verb instead."""
tmp_path = profile_env
create_profile("oldname", no_alias=True)
with patch("hermes_cli.profiles.check_alias_collision", return_value="skip"), \
@@ -1658,7 +1657,6 @@ class TestEdgeCases:
def test_clone_from_named_profile(self, profile_env):
"""Clone config from a named (non-default) profile."""
tmp_path = profile_env
# Create source profile with config
source_dir = create_profile("source", no_alias=True)
(source_dir / "config.yaml").write_text("model: cloned", encoding="utf-8")
+1 -1
View File
@@ -59,7 +59,7 @@ class TestDoUninstallSkipConfirm:
def test_skip_confirm_bypasses_input(self):
"""With skip_confirm=True, input() should not be called."""
from hermes_cli.skills_hub import do_uninstall
with patch("hermes_cli.skills_hub._console") as mock_console, \
with patch("hermes_cli.skills_hub._console"), \
patch("tools.skills_hub_install.uninstall_skill", return_value=(True, "Removed")) as mock_uninstall, \
patch("builtins.input") as mock_input:
do_uninstall("test-skill", skip_confirm=True)
@@ -576,7 +576,7 @@ class TestRestartMacosLaunchdGateways:
assert restarted == ["ai.hermes.gateway", "ai.hermes.gateway-after"]
def test_sibling_that_never_comes_back_is_failed(self, monkeypatch, tmp_path):
rec = _fleet(
_fleet(
monkeypatch,
tmp_path,
current="ai.hermes.gateway",
@@ -136,7 +136,7 @@ class TestUpdateStatusReadsReceipt:
def test_running_receipt_proves_nothing(self, client, tmp_path, monkeypatch):
"""An unfinished receipt (crashed run / mid-update) must not report
an outcome — clients keep polling."""
receipt = _write_receipt(tmp_path, monkeypatch, outcome="running")
_write_receipt(tmp_path, monkeypatch, outcome="running")
self._clear_registries(monkeypatch, tmp_path)
resp = client.get("/api/actions/hermes-update/status")
@@ -129,7 +129,6 @@ def test_get_status_does_not_block_event_loop():
import httpx
results: dict[str, float] = {}
errors: list[str] = []
async def _run():
transport = httpx.ASGITransport(app=web_server_mod.app)
+1 -1
View File
@@ -416,7 +416,7 @@ class TestConnectionLifecycle:
read_only = SessionDB(db_path=db_path, read_only=True)
try:
assert read_only._fts_enabled is True
matches = read_only.search_messages("wal-race")
read_only.search_messages("wal-race")
finally:
read_only.close()
@@ -129,7 +129,7 @@ def test_read_conn_open_failure_marks_thread(db, monkeypatch, tmp_path):
def test_anchored_view_and_around_use_read_path(db):
msgs = db.get_messages("s1")
anchor = msgs[0]["id"]
acquired = db._lock.acquire()
db._lock.acquire()
try:
done = {}
@@ -166,7 +166,7 @@ def test_session_resume_reads_do_not_take_writer_lock(db):
db.append_message("child1", role="user", content="child turn")
db.append_message("child1", role="assistant", content="child reply")
acquired = db._lock.acquire()
db._lock.acquire()
try:
done = {}
@@ -190,7 +190,7 @@ class TestSlashGateIsolation:
def test_evaluate_slash_channel_gate_per_adapter(self, monkeypatch):
import types
discord_lib = pytest.importorskip(
pytest.importorskip(
"discord", reason="discord.py optional dep not installed"
)
-1
View File
@@ -12,7 +12,6 @@ def test_missing_bundle_tool_is_installed_in_writable_store(pm_env, tmp_path, mo
from pm.install import ensure, env_for, is_installed
import importlib
fixture = pm_env
shipped = tmp_path / "payload" / "tools"
shipped.mkdir(parents=True)
(shipped.parent / "manifest.json").write_text('{"repo":"core","store":"tools","venv":"venv"}')
@@ -205,7 +205,7 @@ class TestModelPickerBaseUrlIntegration:
return_value={"api_key": "sk-test", "base_url": "https://custom.proxy.com"}),
):
from hermes_cli.models import provider_model_ids
result = provider_model_ids("test-provider")
provider_model_ids("test-provider")
# Verify fetch_models was called with base_url
mock_profile.fetch_models.assert_called_once()
call_kwargs = mock_profile.fetch_models.call_args
+6 -6
View File
@@ -433,7 +433,7 @@ def test_slack_settings_migrated(tmp_path: Path):
workspace_target=None, overwrite=False, migrate_secrets=False, output_dir=None,
selected_options={"slack-settings"},
)
report = migrator.migrate()
migrator.migrate()
env_text = (target / ".env").read_text(encoding="utf-8")
assert "SLACK_BOT_TOKEN=xoxb-slack-bot" in env_text
assert "SLACK_APP_TOKEN=xapp-slack-app" in env_text
@@ -464,7 +464,7 @@ def test_model_config_migrated(tmp_path: Path):
workspace_target=None, overwrite=True, migrate_secrets=False, output_dir=None,
selected_options={"model-config"},
)
report = migrator.migrate()
migrator.migrate()
config_text = (target / "config.yaml").read_text(encoding="utf-8")
assert "anthropic/claude-sonnet-4" in config_text
@@ -492,7 +492,7 @@ def test_shared_skills_migrated(tmp_path: Path):
workspace_target=None, overwrite=False, migrate_secrets=False, output_dir=None,
selected_options={"shared-skills"},
)
report = migrator.migrate()
migrator.migrate()
imported = target / "skills" / mod.SKILL_CATEGORY_DIRNAME / "my-shared-skill" / "SKILL.md"
assert imported.exists()
@@ -520,7 +520,7 @@ def test_daily_memory_merged(tmp_path: Path):
workspace_target=None, overwrite=False, migrate_secrets=False, output_dir=None,
selected_options={"daily-memory"},
)
report = migrator.migrate()
migrator.migrate()
mem_path = target / "memories" / "MEMORY.md"
assert mem_path.exists()
content = mem_path.read_text(encoding="utf-8")
@@ -556,7 +556,7 @@ def test_provider_keys_require_migrate_secrets_flag(tmp_path: Path):
workspace_target=None, overwrite=False, migrate_secrets=False, output_dir=None,
selected_options={"provider-keys"},
)
report = migrator.migrate()
migrator.migrate()
env_path = target / ".env"
if env_path.exists():
assert "sk-or-test-key" not in env_path.read_text(encoding="utf-8")
@@ -567,7 +567,7 @@ def test_provider_keys_require_migrate_secrets_flag(tmp_path: Path):
workspace_target=None, overwrite=False, migrate_secrets=True, output_dir=None,
selected_options={"provider-keys"},
)
report2 = migrator2.migrate()
migrator2.migrate()
env_text = (target / ".env").read_text(encoding="utf-8")
assert "OPENROUTER_API_KEY=sk-or-test-key" in env_text
-1
View File
@@ -460,7 +460,6 @@ class TestSetupLogging:
hermes_logging.setup_logging(hermes_home=hermes_home, log_level="WARNING")
root = logging.getLogger()
agent_handlers = [
h for h in hermes_logging._queued_file_handlers
if isinstance(h, RotatingFileHandler)
Loaded 100 of 117 files, more files were not shown because too many files have changed in this diff. Show more