Skip to content

fix(async-audit): Phase 3 cleanup — eliminate sync I/O in async routes#247

Merged
pcalnon merged 1 commit intomainfrom
fix/async-route-audit-phase-3-cleanup
May 6, 2026
Merged

fix(async-audit): Phase 3 cleanup — eliminate sync I/O in async routes#247
pcalnon merged 1 commit intomainfrom
fix/async-route-audit-phase-3-cleanup

Conversation

@pcalnon
Copy link
Copy Markdown
Owner

@pcalnon pcalnon commented May 6, 2026

Summary

Phase 3 of the async-route audit migration (see juniper-ml ASYNC_ROUTE_AUDIT_HOOK_MIGRATION_PLAN.md). Clears the remaining 5 ASYNC* violations in this repo so we can flip the hook from soft-fail to enforcement in Phase 4.

After this lands, ruff check --select ASYNC src/ reports All checks passed!

Changes

src/main.py

  • ASYNC240 — Snapshot detail route (GET /api/v1/snapshots/{snapshot_id}) now calls a new _find_snapshot_file helper via asyncio.to_thread. Previously did path.exists() + path.iterdir() + snapshot_file.stat() inline (three sync stat-bound calls on the event loop); now bundled into a single thread hop.
  • ASYNC240 — Snapshot create route (POST /api/v1/snapshots) wraps Path(_snapshots_dir).mkdir(parents=True, exist_ok=True) in asyncio.to_thread so a slow disk can't stall the loop.
  • ASYNC109api_remote_stop_workers's timeout: int = 10 parameter is a passthrough to backend._adapter.stop_remote_workers, not a deadline for this handler. Silenced with # noqa: ASYNC109 and a justification comment.

src/backend/cascor_service_adapter.py

  • ASYNC110 — Control-stream supervisor's while not self._shutdown and self.is_connected: await asyncio.sleep(1) keeps its 1s poll cadence with # noqa: ASYNC110. Rewriting around asyncio.Event would require touching the stream callback chain we don't own, and 1s latency is well below any reconnect we care about.

New helpers in src/main.py (added in a prior commit on this branch):

  • _load_snapshot_history(history_file) — reads snapshot_history.jsonl; tolerates missing files and malformed lines.
  • _find_snapshot_file(snapshots_dir, snapshot_id) — bundles exists + iterdir + stat for callers using asyncio.to_thread.

Verification

  • ruff check --select ASYNC src/ exits clean
  • pre-commit suite green on both files (black, isort, flake8, mypy, bandit, async-audit)
  • Helper smoke test: missing-file → [], malformed JSON tolerated, snapshot lookup hit/miss/missing-dir all behave correctly
  • CI pre-commit + tests
  • Local pytest currently blocked by an unrelated torch ImportError in the JuniperCanopy 3.14t conda env (see juniper-ml memory: "JuniperCascor torch ImportError"); not introduced by this PR

Test plan

  • Pre-commit + flake8 + mypy + bandit pass in CI
  • Async-audit job is now an unconditional pass (Phase 2 was soft-fail; Phase 4 will flip --exit-zero and continue-on-error off after this lands)
  • Unit suite green in CI environment with working torch

🤖 Generated with Claude Code

Wrap the remaining ASYNC* violations in src/ so the async-route audit
hook reaches a clean state ahead of Phase 4 enforcement.

src/main.py
- ASYNC240 — snapshot detail route now calls a new ``_find_snapshot_file``
  helper via ``asyncio.to_thread``, bundling ``exists`` + ``iterdir`` +
  ``stat`` into a single thread hop instead of three event-loop blocks.
- ASYNC240 — snapshot create route runs ``Path.mkdir(parents=True,
  exist_ok=True)`` via ``asyncio.to_thread`` so a slow disk can't stall
  the loop.
- ASYNC109 — ``api_remote_stop_workers`` keeps its ``timeout`` query
  parameter (it's a passthrough to the adapter, not a deadline for this
  handler) with a ``# noqa: ASYNC109`` and justification.

src/backend/cascor_service_adapter.py
- ASYNC110 — control-stream supervisor's ``while ...: await sleep(1)``
  poll keeps its 1-second cadence with a ``# noqa: ASYNC110``;
  rewriting around ``asyncio.Event`` would require touching the stream
  callback chain we don't own and the 1s latency is well below any
  reconnect we care about.

Verification: ``ruff check --select ASYNC src/`` reports "All checks
passed!" The full pre-commit suite (black, isort, flake8, mypy, bandit,
async-audit) is green on both files. Helper functions exercised with
ad-hoc fixtures covering missing-dir, malformed-jsonl, hit, and
miss paths.

Refs: notes/ASYNC_ROUTE_AUDIT_HOOK_MIGRATION_PLAN.md (Phase 3)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@pcalnon pcalnon self-assigned this May 6, 2026
Copy link
Copy Markdown
Owner Author

@pcalnon pcalnon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

approved

@pcalnon pcalnon merged commit 2e4674f into main May 6, 2026
30 of 42 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant