fix(api)+chore(sonar): re-raise CancelledError + sync coverage exclusions#13
Merged
Conversation
…ions Two changes that together flip the SonarCloud gate from ERROR to OK: 1. src/runtime/api.py:889 — re-raise asyncio.CancelledError after cleanup in the SSE _stream() generator instead of returning silently. Suppressing CancelledError breaks asyncio's cancellation propagation contract; Sonar python:S7497 caught it. The single MAJOR bug was driving new_reliability_rating from A to C. 2. sonar-project.properties — sync sonar.coverage.exclusions with pyproject.toml's [tool.coverage.run].omit list (ui.py, __main__.py, checkpointer_postgres.py, triggers/transports/plugin.py) and add examples/** since pytest's --cov=src/runtime never instruments the reference apps. Without this, Sonar's new-code coverage was 71.7% (gate threshold 80%) while local CI saw 87.21% on the same suite — pure config drift between two gates that should agree. Verification: ruff check src/ tests/ — passed pyright src/runtime — 0 errors / 0 warnings pytest -x — 1265 passed / 8 skipped pytest --cov=src/runtime --cov-fail-under=85 — 87.21% build_single_file.py — dist/* regenerated Projected SonarCloud gate after merge: new_reliability_rating 3 (C) -> 1 (A) ✓ new_security_hotspots_reviewed 0% -> 100% ✓ (already done via API) new_coverage 71.7% -> ~87.2% ✓
7 tasks
Sonar python:S2737 caught the ``except CancelledError: raise`` from the previous commit as a code smell — the catch-and-rethrow does nothing observable; Python propagates CancelledError automatically without any try/except in the SSE _stream() generator. Removing the wrapper achieves the same correct cancellation semantics S7497 was asking for, without adding the dead handler S2737 then flagged. Comment retains the rationale so a future reader doesn't re-add a suppressing handler. Verification: ruff check src/ — passed pyright src/runtime — 0 errors / 0 warnings pytest -x — 1265 passed / 8 skipped build_single_file.py — dist/* regenerated Projected SonarCloud after this commit (PR #13): new_maintainability_rating 3 (C) -> 1 (A)
aksOps
added a commit
that referenced
this pull request
May 15, 2026
Targeted coverage uplift across the framework core's pure helpers and
two integration boundaries. Local src/runtime coverage rises from
87.21% to 88.90% (+1.69%); 97 previously-uncovered lines now exercised.
New test files (7):
* tests/test_llm_stub_structured_output.py
StubChatModel.with_structured_output happy path
(llm.py:141-160, 171-177). The defensive model_validate fallback
(lines 161-169) is excluded with a documented justification:
pydantic v2's model_validate calls __init__, so any schema whose
constructor raises also fails the fallback.
* tests/test_envelope_recovery.py
_try_recover_envelope_from_raw (graph.py:583-610). Table-driven
across the three candidate-substring strategies (raw, fenced,
greedy first-{...-last-}) and every failure path.
* tests/test_orchestrator_extract_last_error.py
Orchestrator._extract_last_error (orchestrator.py:945-998).
Pure mapping from a failed-AgentRun summary string to a
representative typed exception. Table-driven across
EnvelopeMissingError, ValidationError, TimeoutError,
OSError, RuntimeError fallback, plus reversed-iteration ordering.
* tests/test_handle_agent_failure.py
_handle_agent_failure (graph.py:613-644). Both the happy path
(reload + append + status='error') and the FileNotFoundError
fallback path (use caller's in-memory session). Plus a
partial-tool-write preservation regression test.
* tests/test_retry_session_locked_post_policy.py
Orchestrator._retry_session_locked post-policy execution path
(orchestrator.py:1552-1587). Stub orchestrator pulls in the
real method body and substitutes the surrounding integration
points (graph, finalize, pause). Covers the failed-AgentRun
filter, retry_count + active_thread_id pinning, and the
pause-vs-finalize fork.
* tests/test_service_run_exception_branches.py
OrchestratorService.start_session._run exception branches
(service.py:541-568). Three classes get distinct treatment:
CancelledError (propagate as-is), GraphInterrupt (propagate
WITHOUT marking registry status='error' -- HITL pause is not a
failure), generic Exception (mark error then propagate).
* tests/test_sse_tail_loop.py
SSE _stream tail-poll loop (api.py:879-890), including the
CancelledError re-raise from PR #13. Two tests: one drives the
tail to deliver a post-drain event, one forces sleep to raise
CancelledError and asserts it propagates (pinning the bug fix).
Verification:
ruff check src/ tests/ passed
pyright src/runtime 0 errors / 0 warnings
pytest -x 1310 passed / 8 skipped (was 1265/8)
pytest --cov=src/runtime --cov-fail-under=85 88.90% (was 87.21%)
build_single_file.py dist unchanged (tests only)
Projected SonarCloud impact (after the Phase 1 PR #13 exclusion sync):
new_coverage ~87.2% -> ~89-90%
|
aksOps
added a commit
that referenced
this pull request
May 15, 2026
Targeted coverage uplift across the framework core's pure helpers and
two integration boundaries. Local src/runtime coverage rises from
87.21% to 88.90% (+1.69%); 97 previously-uncovered lines now exercised.
New test files (7):
* tests/test_llm_stub_structured_output.py
StubChatModel.with_structured_output happy path
(llm.py:141-160, 171-177). The defensive model_validate fallback
(lines 161-169) is excluded with a documented justification:
pydantic v2's model_validate calls __init__, so any schema whose
constructor raises also fails the fallback.
* tests/test_envelope_recovery.py
_try_recover_envelope_from_raw (graph.py:583-610). Table-driven
across the three candidate-substring strategies (raw, fenced,
greedy first-{...-last-}) and every failure path.
* tests/test_orchestrator_extract_last_error.py
Orchestrator._extract_last_error (orchestrator.py:945-998).
Pure mapping from a failed-AgentRun summary string to a
representative typed exception. Table-driven across
EnvelopeMissingError, ValidationError, TimeoutError,
OSError, RuntimeError fallback, plus reversed-iteration ordering.
* tests/test_handle_agent_failure.py
_handle_agent_failure (graph.py:613-644). Both the happy path
(reload + append + status='error') and the FileNotFoundError
fallback path (use caller's in-memory session). Plus a
partial-tool-write preservation regression test.
* tests/test_retry_session_locked_post_policy.py
Orchestrator._retry_session_locked post-policy execution path
(orchestrator.py:1552-1587). Stub orchestrator pulls in the
real method body and substitutes the surrounding integration
points (graph, finalize, pause). Covers the failed-AgentRun
filter, retry_count + active_thread_id pinning, and the
pause-vs-finalize fork.
* tests/test_service_run_exception_branches.py
OrchestratorService.start_session._run exception branches
(service.py:541-568). Three classes get distinct treatment:
CancelledError (propagate as-is), GraphInterrupt (propagate
WITHOUT marking registry status='error' -- HITL pause is not a
failure), generic Exception (mark error then propagate).
* tests/test_sse_tail_loop.py
SSE _stream tail-poll loop (api.py:879-890), including the
CancelledError re-raise from PR #13. Two tests: one drives the
tail to deliver a post-drain event, one forces sleep to raise
CancelledError and asserts it propagates (pinning the bug fix).
Verification:
ruff check src/ tests/ passed
pyright src/runtime 0 errors / 0 warnings
pytest -x 1310 passed / 8 skipped (was 1265/8)
pytest --cov=src/runtime --cov-fail-under=85 88.90% (was 87.21%)
build_single_file.py dist unchanged (tests only)
Projected SonarCloud impact (after the Phase 1 PR #13 exclusion sync):
new_coverage ~87.2% -> ~89-90%
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Two surgical changes that flip the SonarCloud quality gate from ERROR → OK without writing any new tests.
1. Bug fix —
src/runtime/api.py:889(1 line)The SSE
_stream()generator caughtasyncio.CancelledErrorand silentlyreturned instead of re-raising. SuppressingCancelledErrorbreaks asyncio's cancellation-propagation contract; Sonar'spython:S7497rule caught it. This was the single MAJOR bug pushingnew_reliability_ratingfrom A to C.2. Sonar config sync —
sonar-project.propertiessonar.coverage.exclusionsnow mirrorspyproject.toml's[tool.coverage.run].omitlist and addsexamples/**:src/runtime/ui.pysrc/runtime/__main__.pysrc/runtime/checkpointer_postgres.pysrc/runtime/triggers/transports/plugin.pyexamples/**--cov=src/runtimedoesn't instrument them, but they have integration tests undertests/This was pure config drift — local CI saw 87.21% on the same test suite while SonarCloud saw 71.7%, all because the two gates disagreed about which files to measure.
Bonus (already done via API)
The 2 open security hotspots (
python:S4790non-cryptographic SHA-1 in example MCP server_seed()helpers) were marked SAFE in SonarCloud earlier in this session, with audit-trail justification.Projected SonarCloud gate after merge
new_reliability_ratingnew_security_hotspots_reviewednew_coveragenew_security_ratingnew_maintainability_ratingnew_duplicated_lines_densityTest plan
uv run ruff check src/ tests/— passeduv run pyright src/runtime— 0 errors / 0 warningsuv run pytest -x— 1265 passed / 8 skippeduv run pytest --cov=src/runtime --cov-fail-under=85 -x— 87.21%uv run python scripts/build_single_file.py—dist/*regenerated and committed🤖 Generated with Claude Code