calm-artist-7206
08/18/2026, 12:07 PMfresh-machine-37269
08/18/2026, 12:07 PMfresh-machine-37269
08/18/2026, 12:18 PM_install_chromium() removal is correct β zero callers on the branch; subprocess and sanitized_env imports are genuinely orphaned after removal (verified nothing else references them). shutil is still used, so that import correctly stays.
β’ execution_status setter removal is benign β ConversationStateProtocol only declares the getter; the setter just raised NotImplementedError. Assignment now raises the standard AttributeError. The PR description now calls this out (the thread was correctly resolved).
β’ The removed tests are genuinely low-value β Pydantic serialization tautologies, mock-wiring of a private helper, a joserfc import test (testing the dependency, not OpenHands), source-text scans, and a literal Codex allowlist. The test_openai_codex_models rewrite is the right kind of replacement: it asserts the maintained invariant (OPENAI_CODEX_MODELS supersets every model the Codex ACP provider exposes) instead of hard-coding model names that drift.
Two notes, neither blocking:
1. unresolved-review-threads is the one red check. The 7 open threads are your own observations (mostly "this test tests Pydantic works π"). Worth resolving/thread-closing so the merge gate goes green.
2. On the two invariants the maintainer flagged (agent_state reassignment β autosave, and the telemetry flock lock-ordering): I agree with your follow-up framing. Those AST/source-scan guards were brittle, but the underlying invariants are real β I confirmed FileSettingsStore.load() still does not take the _file_lock while update() does, so the behavior they pinned holds today. A behavior-based replacement (or linting rule) in a follow-up would keep the safety net without the brittleness. Not required for this PR.
No bugs, security issues, or design flaws found. Nice cleanup.calm-artist-7206
08/18/2026, 12:26 PMfresh-machine-37269
08/18/2026, 12:28 PMscripts/check_import_rules.py and check_tool_registration.py β so most of these are cheap to bolt on.
1. Dead private code β vulture
Add vulture as a pre-commit hook. It's a dead-code detector that would have flagged _install_chromium() (and the now-orphaned imports) automatically, so "private helper pinned only by mock tests" dies at the source instead of surviving a test sweep. It's the direct replacement for the execution_status "test-only setter" class of problem too.
2. "Tests peeking at private internals" β Ruff SLF (flake8-self)
This is the single most relevant off-the-shelf rule. It flags private-member access (_queue, _drain_task, _response_schema_json_cache, _encoding_cache, _install_chromium, `BrowserToolExecutor.__new__`β¦). Enable "SLF" in [tool.ruff.lint] select. The handful of tests that legitimately need internals (e.g. the retained bounded-queue test touching sink._queue) get an explicit # noqa: SLF001 or a narrow per-file-ignores β which forces deliberate, reviewed opt-in rather than silent mirroring. That's exactly the discipline you want.
3. agent_state reassignment invariant β custom AST check (or Semgrep)
A deterministic script (mirroring your check_import_rules.py) that flags in-place mutation of `agent_state`:
β’ state.agent_state[...] = β¦ / +=
β’ state.agent_state.update(...), .setdefault(...), .pop(...), .clear()
β’ and requires the state.agent_state = {**state.agent_state, ...} reassignment form.
This is the same AST logic the deleted test used, but as a lint (deterministic, no inspect.getsource, no false "runtime coverage" pretense). A Semgrep rule is equivalent:
rules:
- id: agent-state-inplace-mutation
message: "Reassign agent_state instead of mutating in place (autosave)"
severity: ERROR
patterns:
- pattern-either:
- pattern: $STATE.agent_state[...] = ...
- pattern: $STATE.agent_state.update(...)
- pattern: $STATE.agent_state.setdefault(...)
languages: [python]
4. flock non-reentrancy / lock-ordering invariant β Semgrep rules
Two rules:
β’ FileSettingsStore.load() must not reference _file_lock (pin that load stays lock-free).
β’ No store.update(...) / store.load(...) / get_settings_store(...) inside a with _file_lock(...) block (pin that nothing re-enters the store while holding the non-reentrant lock).
This encodes the real invariant (deadlock avoidance) instead of inspect.getsource string-scanning.
5. "Testing Pydantic/framework" anti-patterns β Semgrep "behavior-not-implementation" ruleset
Target the tell-tale signatures of the removed tests, restricted to `tests/**`:
β’ inspect.getsource(...) and ast.parse(...) / ast.NodeVisitor
β’ importlib.reload(...) in tests
β’ .__dict__ and hasattr(x, "_...") assertions
β’ model_dump() / model_validate() / model_dump_json() used as the assertion subject (as opposed to incidental serialization)
This turns the new AGENTS.md sentence into something machine-checkable, and it would have caught most of this PR's removals before they were written.
Optional, lower priority
β’ Ruff PT (flake8-pytest-style) for test hygiene, and full B (bugbear) beyond the three you already enable.
β’ Ruff B027? no β but TRY (tryceratops) would have flagged `_install_chromium`'s except (TimeoutExpired, FileNotFoundError, Exception) bare-ish catch.
My recommendation for the follow-up, in order of value: vulture + SLF + a Semgrep config (items 3β5 in one semgrep-rules/ dir wired into pre-commit). Those three together recover every invariant the sweep dropped, as deterministic lint rather than brittle tests.calm-artist-7206
08/18/2026, 12:55 PMfresh-machine-37269
08/18/2026, 1:03 PMagent = Agent(llm=llm, tools=[]) # no explicit include_default_tools
agent_dict = json.loads(agent.model_dump_json())
assert set(agent_dict["include_default_tools"]) == {"FinishTool", "ThinkTool"}
It pins two real, user-visible invariants that are now untested:
1. The exact default value of the field. default_factory=lambda: [tool.__name__ for tool in BUILT_IN_TOOLS]. After this deletion, no test asserts the plain-Agent default equals ["FinishTool", "ThinkTool"]. The only remaining assertions are behavioral ("finish" in tools_map / "think" in tools_map in test_agent_tool_init.py), which resolve through base.py and wouldn't catch a wrong-but-consistent default name.
2. That the field is serialized at all. It's not marked Field(exclude=True), so it lands in model_dump_json(). If someone later excludes it (or changes the default factory), base_state.json would silently drop the tool set and restore would fall back to defaults. The retained test_agent_type_annotation_on_basemodel_works_json round-trips a default agent, but only asserts equality, never the specific string values β it would pass even if the default became ["FooTool"].
Note base_state.json fixtures and the restore/state-serialization tests all use explicit lists, so they never exercise this default-serialization path. This is a genuine, low-churn gap. Keep it.
2. test_include_default_tools_deserialization_roundtrip() β line 466 β safe to remove agent_json = agent.model_dump_json()
deserialized = AgentBase.model_validate_json(agent_json)
assert isinstance(deserialized, Agent)
assert deserialized.include_default_tools == ["FinishTool"]
This one (and its siblings _empty, _all_tools, _from_dict, _serialization_empty, _serialization_partial) is Pydantic mechanics. Two already-retained tests cover the substance:
β’ test_agent_type_annotation_on_basemodel_works_json (still in the file) does a full model_dump_json() β model_validate_json() round-trip and asserts the discriminated dispatch back to Agent.
β’ tests/sdk/conversation/local/test_state_serialization.py and tests/cross/test_conversation_restore_behavior.py serialize/deserialize agents with explicit include_default_tools and assert the value survives (and drives verify() correctly).
So the deserialization path is covered. Engel's "isn't that Pydantic mechanics?" is correct here β no loss.
3. test_metrics_diff_current_usage_not_none() β line 709 β safe to remove (duplicate) "why deleting this?" β because the exact branch is still covered. It exercises Metrics.diff() with both current_usage and base_usage non-None (the subtraction path). The retained test_metrics_diff_method() does the same:
metrics1.add_token_usage(500, 250, ...) # current
metrics2.add_token_usage(200, 100, ...) # baseline
diff = metrics1.diff(metrics2) # both not None β subtracts
assert diff.accumulated_token_usage.prompt_tokens == 300
I confirmed all three branches of `diff()`'s token-usage logic remain covered:
So the deleted test was a duplicate with different numbers (20/10 vs 500/200). No coverage lost.
The remaining inline threads (no coverage concern) β’ remote_conversation.py:585 (bot β resolved) β execution_status setter removal. Already verified benign; the getter-only protocol is unchanged, and the description now documents the NotImplementedError β AttributeError change. β
β’ remote_conversation.py:592 (enyst) β author's own "this is why this PR exists" comment about the test-only setter. Commentary, not a coverage ask.
β’ test_schema_immutability.py:86,89 (enyst) β "testing that Pydantic works" / frozen-Schema tautology. Correct: the replacement test_schema_subclasses_are_frozen keeps the one meaningful immutability assertion.
β’ test_tool.py:102 (enyst) β the examples self-referential docstring test. Correctly deleted (tested its own premise).
β’ test_tool_name_consistency.py:56 (enyst) β "test tests its own premise." Correctly deleted; name resolution is still covered by tool registration checks (scripts/check_tool_registration.py) and real tool tests.
β’ test_agent_immutability.py:21 (enyst) β frozen-Agent design constraint. Reasonable to move to a lint/AGENTS rule (the retained test_agent_is_frozen still asserts the ValidationError: Instance is frozen behavior).
β’ test_agent_serialization.py:447 (enyst) β "these test that Pydantic works." Correct for the bulk of them β except the serialization_default case above.
Bottom line One thing worth bringing back: test_include_default_tools_serialization_default(). It's the only test pinning the default built-in tool set and its serialized names. Everything else Vasco flagged (the deserialization variants, the metrics diff test) is genuinely redundant β I traced each to a surviving test that exercises the same code path.
Want me to draft the exact restoration (just that one function, possibly tightened to drop the redundant set() comparison if you prefer a stricter order/equality check)?, with interactive elementscalm-artist-7206
08/18/2026, 1:08 PMfresh-machine-37269
08/18/2026, 1:13 PMtest_include_default_tools_serialization_default() (line 437), confirming it's worth keeping and explaining why: it pins the exact default value (["FinishTool", "ThinkTool"]) and that the field is serialized into model_dump_json() at all β two things the remaining behavioral tests don't catch.
2. Restored the test and committed it:
23c888e2 test: restore include_default_tools default serialization coverage
The test passed (1 passed), and I pushed the commit to chore/test-sweep-2026-08-13, updating PR #4484.calm-artist-7206
08/18/2026, 1:29 PM# Make sure you're on the PR branch and have the base branch available
git fetch origin main
# --- Coverage on PR branch (current HEAD) ---
pytest --cov=YOUR_PACKAGE --cov-report=term-missing --cov-report=json:coverage-pr.json -q
PR_COV=$(python -c "import json; print(json.load(open('coverage-pr.json'))['totals']['percent_covered_display'])")
# --- Coverage on main branch ---
git stash -u # optional, keeps your work safe
git checkout origin/main
pytest --cov=YOUR_PACKAGE --cov-report=json:coverage-main.json -q
MAIN_COV=$(python -c "import json; print(json.load(open('coverage-main.json'))['totals']['percent_covered_display'])")
git checkout - # back to the PR branch
git stash pop # optional
# --- Post the comment ---
gh pr comment --body "## Coverage comparison
| Branch | Coverage |
|--------|----------|
| **main** | ${MAIN_COV}% |
| **PR** | ${PR_COV}% |
$(python -c "
main = float('${MAIN_COV}'.rstrip('%') or 0)
pr = float('${PR_COV}'.rstrip('%') or 0)
diff = pr - main
sign = '+' if diff >= 0 else ''
print(f'**Delta:** {sign}{diff:.2f}%')
")"fresh-machine-37269
08/18/2026, 1:57 PMcalm-artist-7206
08/18/2026, 2:36 PMfresh-machine-37269
08/18/2026, 2:36 PMfresh-machine-37269
08/18/2026, 2:36 PMcalm-artist-7206
08/18/2026, 2:50 PM# Make sure you're on the PR branch and have the base branch available
git fetch origin main
# --- Coverage on PR branch (current HEAD) ---
pytest --cov=YOUR_PACKAGE --cov-report=term-missing --cov-report=json:coverage-pr.json -q
PR_COV=$(python -c "import json; print(json.load(open('coverage-pr.json'))['totals']['percent_covered_display'])")
# --- Coverage on main branch ---
git stash -u # optional, keeps your work safe
git checkout origin/main
pytest --cov=YOUR_PACKAGE --cov-report=json:coverage-main.json -q
MAIN_COV=$(python -c "import json; print(json.load(open('coverage-main.json'))['totals']['percent_covered_display'])")
git checkout - # back to the PR branch
git stash pop # optional
# --- Post the comment ---
gh pr comment --body "## Coverage comparison
| Branch | Coverage |
|--------|----------|
| **main** | ${MAIN_COV}% |
| **PR** | ${PR_COV}% |
$(python -c "
main = float('${MAIN_COV}'.rstrip('%') or 0)
pr = float('${PR_COV}'.rstrip('%') or 0)
diff = pr - main
sign = '+' if diff >= 0 else ''
print(f'**Delta:** {sign}{diff:.2f}%')
")"fresh-machine-37269
08/18/2026, 3:19 PMcalm-artist-7206
08/18/2026, 3:32 PMfresh-machine-37269
08/18/2026, 3:32 PMmain and the PR branch, and found no meaningful regression (SDK flat at ~88.64%, tools β0.06% rounding noise). Couldn't post the comment to the PR because the GitHub token is rate-limited, but the comparison is done.
What would you like me to do next?calm-artist-7206
08/18/2026, 3:33 PMfresh-machine-37269
08/18/2026, 3:33 PMtmux missing in this environment), so the delta is still a clean apples-to-apples comparison.
I also couldn't post the comment straight onto the PR β the GitHub token here is rate-limited (403). Want me to retry posting it, or is that good enough?, with interactive elementscalm-artist-7206
08/18/2026, 3:34 PMfresh-machine-37269
08/18/2026, 3:35 PM_install_chromium() function from browser_use/impl.py, which was 17 statements and 100% covered (17/17).
That's the whole story: removing a fully-covered block of code lowers the package's overall percentage, because that block (100%) was above the package's ~77.7% average. When you drop above-average coverage out of the denominator, the weighted average ticks downward slightly. No live line lost coverage β the 1316 uncovered lines are unchanged.
So the honest phrasing is:
β’ At integer display precision (percent_covered_display), both branches read 78% β identical. That's the only sense in which it's "rounding."
β’ At higher precision it's a real β0.0645 pp change, fully explained by deleting 17 fully-covered lines of dead code, not by any test losing coverage.
Good instinct to push on that β my "rounding noise" was misleading, and the above is the correct explanation.