Skip to content

reliability: fix audited lifecycle and state edge cases - #962

Merged
yisding merged 8 commits into
mainfrom
agent/bug-audit-followup
Aug 9, 2026
Merged

reliability: fix audited lifecycle and state edge cases#962
yisding merged 8 commits into
mainfrom
agent/bug-audit-followup

Conversation

@yisding

@yisding yisding commented Aug 7, 2026

Copy link
Copy Markdown
Owner

Problem

A broad reliability audit found lifecycle races, dropped task failures, inconsistent state serialization, and persistence/validation edge cases that could surface under cancellation, shutdown, parallel execution, or malformed inputs.

Changes

  • harden runtime, server, session, STT, and transport task ownership and teardown retry behavior
  • preserve and serialize agent, journal, and turn state consistently across failure paths
  • improve telephony retry/classification, debugger safeguards, and latency validation boundaries
  • add focused regressions and refresh reviewed lifecycle/source ratchets

Impact

Shutdown remains bounded while retaining failed work for an explicit retry, background failures are observed instead of leaked, and persisted/validated state is deterministic across concurrent and degraded paths.

Validation

  • full credential-free suite: 9,195 passed, 209 skipped, 69 deselected
  • Ruff lint: passed
  • Ruff format check: passed
  • mypy: passed for 289 source files
  • ratchet suite: 48 passed
  • final diff, conflict-marker, and worktree integrity checks: passed

Summary by CodeRabbit

  • Bug Fixes

    • Improved session, audio, WebSocket, WebRTC, journal, and telephony shutdown reliability with bounded cleanup, retries, and clearer failure reporting.
    • Prevented stale call events, invalid audio formats, unsafe origins, malformed statistics, and incomplete validation from causing inconsistent behavior.
    • Preserved pending work and resources when cancellation or cleanup cannot finish immediately.
    • Fixed generator-based audio handling and artifact cleanup after failed journal writes.
  • Security

    • Strengthened sensitive-data redaction in serialized state and removed credential details from error responses.
  • Validation

    • Added stricter configuration, timeout, retry, latency, reliability, and agent-response validation.
  • New Features

    • Added waveform size limits, improved turn admission controls, and support for pruning empty runtime scopes.

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@yisding, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 16 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: f430a921-27ef-4b8d-8e99-8d503351f5ca

📥 Commits

Reviewing files that changed from the base of the PR and between cf39b3f and db93561.

📒 Files selected for processing (18)
  • perf/bench_agent_delta_tts_overlap.py
  • src/easycat/integrations/agents/_agent_runner.py
  • src/easycat/integrations/agents/pydantic_ai.py
  • src/easycat/integrations/agents/template.py
  • src/easycat/session/_journal_sink.py
  • src/easycat/session/_session.py
  • src/easycat/session/_turn_runner.py
  • src/easycat/turn_manager.py
  • tests/integrations/agents/test_agent_runner.py
  • tests/integrations/agents/test_bridge_template.py
  • tests/integrations/agents/test_pydantic_ai_v2.py
  • tests/perf/test_agent_delta_tts_overlap_benchmark.py
  • tests/ratchets/source-baseline.json
  • tests/ratchets/turn-commit-manifest.json
  • tests/session/test_journal_sink.py
  • tests/session/test_session_lifecycle_teardown.py
  • tests/session/test_session_streaming_behavior.py
  • tests/turns/test_turn_manager.py
📝 Walkthrough

Walkthrough

This PR adds stricter configuration and data validation. It centralizes safe agent-state serialization. It adds bounded, retryable cleanup for journals, sessions, STT providers, WebSockets, listeners, and transports. It also hardens telephony transitions and expands regression coverage.

Changes

Lifecycle hardening and validation

Layer / File(s) Summary
Configuration and agent-state safety
src/easycat/config/*, src/easycat/telephony/retry.py, src/easycat/timeouts.py, src/easycat/integrations/agents/*, src/easycat/validation/redaction.py
Mutable policies are revalidated and copied. Agent responses require strings. Framework state uses shared redaction and strict JSON serialization.
Journal, scope, and artifact ownership
src/easycat/runtime/*, src/easycat/_concurrency.py, src/easycat/session/_journal_sink.py, src/easycat/session/_debug_backends.py
Cleanup retains ownership until completion. Journals and artifacts support retry and conditional cleanup. Empty scopes can be pruned safely.
Session, STT, and turn teardown
src/easycat/session/*, src/easycat/session_manager.py, src/easycat/turn_manager.py, src/easycat/teardown_budgets.py
Teardown uses bounded cancellation, ownership transfer, retained work, admission gates, and retryable provider cleanup.
Server, telephony, and transport recovery
src/easycat/server/*, src/easycat/debugger/server.py, src/easycat/telephony/*, src/easycat/transports/*
Cleanup failures remain observable and retryable. Origin checks, waveform limits, transition settlement, local transport cleanup, WebRTC negotiation, and statistics validation are hardened.
Regression and contract coverage
tests/*
Tests cover configuration mutation, state redaction, lifecycle retries, teardown budgets, turn admission, transport recovery, telephony transitions, and strict latency validation.

Estimated code review effort: 5 (Critical) | ~120 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Session
  participant STTCommitter
  participant RuntimeSupervisor
  participant Provider
  Session->>STTCommitter: cancel or end stream
  STTCommitter->>Provider: run bounded provider cleanup
  STTCommitter->>RuntimeSupervisor: retain unfinished work
  RuntimeSupervisor->>STTCommitter: retry retained cleanup
  STTCommitter-->>Session: return cleanup status
Loading

Possibly related PRs

Suggested reviewers: charliecreates

Poem

I’m a rabbit with a tidy nest,
Where closing tasks can safely rest.
Secrets hide and bad numbers flee,
Streams retry patiently.
Cleanup waits until work is done. 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 32.96% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main focus on reliability fixes for lifecycle and state edge cases.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch agent/bug-audit-followup

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@yisding
yisding marked this pull request as ready for review August 7, 2026 14:07
Comment thread src/easycat/transports/webrtc.py Fixed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2b0659c7f3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread src/easycat/config/_factory.py Outdated
yisding and others added 3 commits August 7, 2026 22:18
Resolve ratchet manifest conflicts by recounting merged entries.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n errors

Address PR 962 review comments: the runtime-copy guard now matches
validate_for_build's isinstance semantics, and the WebRTC SDP failure
response no longer echoes exception details to the client.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- bound the rejected-start STT cleanup join after caller cancellation so
  an unresponsive provider cannot make stop(force=True) uncancellable
- report cooperatively-cancelled listener waits as force-timeout failures
  and retain the listener for retry instead of discarding it
- validate bridge event text only for text-bearing kinds; duck-typed
  tool events may carry text=None
- restore substring credential-name filtering for the generic workflow
  __dict__ fallback snapshot (authtoken-style names leaked)
- let observer-spawned tasks transition after the owning call-state
  transition settles; only an active transition rejects them
- classify the new teardown budgets and refresh source ratchets

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 20

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/easycat/debugger/server.py`:
- Around line 705-708: Remove the duplicated “an” in the comment near the
state-changing request handling, so it reads “can pass an explicit same-origin
value.”

In `@src/easycat/integrations/agents/_agent_runner.py`:
- Around line 208-213: Update the validation branches in AgentRunnerConfig for
preemptive_generation and preemptive_max_retries so every line stays within
Ruff’s 99-character limit. Preserve the existing validation behavior and noqa
suppression while splitting the error statements or extracting their messages
into local constants.
- Around line 65-67: Update the text extraction in the event validation flow
around _require_agent_text() to use None or a sentinel instead of an
empty-string default when the event lacks text, ensuring text-bearing events
without a text attribute are rejected before forwarding.

In `@src/easycat/integrations/agents/_state_serialization.py`:
- Around line 74-77: Update _remove_secret_shaped_keys to canonicalize unordered
state before serialization: recursively scrub set items, sort them using a safe
canonical JSON key, and serialize scrubbed mappings with sort_keys=True so
equivalent states produce identical bytes. Preserve sequence handling, and add a
regression test covering a multi-item set with varying iteration order.

In `@src/easycat/runtime/scope.py`:
- Around line 584-591: Update prune_empty_child around
_close_admission_recursive so its contract reflects that a refused prune may
still close admission and leave the child subtree in CLOSING, or move the
emptiness check ahead of admission closure to preserve a no-side-effect False
result. Ensure the docstring and return behavior consistently describe whichever
contract is implemented.

In `@src/easycat/server/voice_server.py`:
- Around line 640-646: Guard the completed-task result in the sweep-success path
before calling _record_incomplete_hard_sweep: catch exceptions re-raised by
sweep_task.result(), append the exception to cleanup_errors, and pass None as
report when retrieval fails. Preserve the existing successful result behavior
and ensure _record_incomplete_hard_sweep still executes so
_finalize_stop_cleanup remains retry-safe.

In `@src/easycat/session/_stt_committer.py`:
- Line 244: Promote the repeated "stt_segment_commit" identifier to a class
constant alongside FINAL_CLOSE_TASK_NAME, PROVIDER_END_TASK_NAME, and
PROVIDER_CLOSE_TASK_NAME. Replace every occurrence in lifecycle task lookups,
cancellation targets, and journal payloads with that constant, including the
references in the relevant session methods.
- Around line 1016-1049: Bound the retry loop in
_finish_transferred_provider_close with an explicit attempt cap or wall-clock
deadline. When the bound is reached, stop retrying while preserving
_provider_close_error, allow the owned task to settle, and leave
_provider_close_pending consistent so retry_transferred_provider_close can
perform any later attempt.

In `@src/easycat/teardown_budgets.py`:
- Around line 39-44: Adjust the rejected-start STT cleanup timing constants used
by _cleanup_rejected_stt_start() so their combined join and cancel-grace
duration fits within SESSION_FORCE_START_LOCK_TIMEOUT_S when invoked from the
force-cancellation path, or consistently increase that force-path cap; preserve
bounded cleanup before the remaining stop path proceeds.

In `@src/easycat/telephony/call_state.py`:
- Around line 426-432: Move _transition_context out of
OutboundCallStateMachine.__init__ and define one module-level ContextVar shared
by all instances. Store both the owning state machine and task in its value,
updating all transition-context reads and writes to use that shared pair, while
retaining _active_transition_owner as the current task.

In `@src/easycat/telephony/retry.py`:
- Around line 128-135: Update get_delay() to handle a zero base_delay_s when
state.attempts is still zero before evaluating the exponentiation, returning the
defined zero delay (or otherwise applying the established pre-attempt contract).
Preserve the existing overflow and non-finite delay clamping for recorded
attempts.

In `@tests/debugger/test_server_replay_export.py`:
- Around line 211-215: Use the canonical _SAFE_HEADERS from
tests/debugger/_server_helpers.py across both modules: in
tests/debugger/test_server_replay_export.py, import it and replace the local
mappings at lines 193-197, 211-215, 246-250, 268-272, 301-305, 321-325, 352-356,
386-390, and 404-408; in tests/debugger/test_aec_diagnostics.py, remove the
module-level definition at lines 276-280 and import _SAFE_HEADERS instead.

In `@tests/debugger/test_server_rest_api.py`:
- Around line 523-547: Add a test for
test_api_audio_waveform_rejects_pcm_over_memory_limit where the raw audio blobs
remain within _WAVEFORM_MAX_PCM_BYTES but _coerce_frames_to_format expands the
coerced PCM beyond that limit. Assert the post-coercion request still returns
HTTP 413 with the existing WAVEFORM_AUDIO_TOO_LARGE payload, covering the guard
after format conversion rather than the raw accumulation guard.

In `@tests/server/test_capacity_gate_drain.py`:
- Around line 247-268: Strengthen
test_timed_safe_await_observes_cancelled_gather_result by directly verifying
that _observe_future_result was attached to the gathered future after
_safe_await returns. Inspect the future’s registered callbacks before deleting
gathered, while preserving the existing exception-handler assertion.

In `@tests/server/test_shutdown_draining.py`:
- Around line 411-415: Update the shutdown test around server.stop() to expect
and assert the intended cleanup exception for the cooperative cancellation path.
Remove the assertion that server._ws_server is None, while preserving assertions
that the hanging listener is closed and cleanup task tracking is cleared.

In `@tests/server/test_websocket_runtime_drain.py`:
- Around line 294-299: Update the assertions in the test around the retry flow
to inspect runtime._connection_cleanup_retry directly instead of using
runtime._connections as a proxy. Keep the existing setup and drain calls
unchanged, and assert the ledger’s expected state before and after the retry
cleanup.

In `@tests/session/test_stt_committer.py`:
- Around line 737-748: Update the test surrounding _HangingSTT and
committer.end_stream to drain the runtime/provider task scope after the timeout,
matching the cleanup pattern used by the sibling tests. Ensure the cleanup runs
in a finally block so the parked hanging task is released even if an assertion
fails, while preserving the existing assertions.

In `@tests/stages/test_stages.py`:
- Around line 1407-1427: Update
test_turn_stage_materializes_generator_input_once_for_detection to pass a
journal and artifact store using the existing test pattern, then assert the
stage_start artifact contains exactly chunk.data. Keep the existing detect-input
assertion and result checks so the test verifies both capture and detection
receive identical audio.

In `@tests/telephony/test_retry_strategy.py`:
- Around line 117-130: Add the return annotation -> None to both test function
definitions in this test module, including
test_backoff_overflow_caps_at_max_delay and the additional test around the
referenced second location; leave their bodies and behavior unchanged.

In `@tests/turns/test_turn_manager.py`:
- Around line 237-243: Set detector.release before calling manager.shutdown() in
the test cleanup flow, so the cancelled detect task can finish while shutdown
awaits replaced. Preserve the existing assertions and retain the finally block’s
cleanup and exception-gathering behavior.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 72ddc6af-7e4b-449a-87d4-6a5250e8d67a

📥 Commits

Reviewing files that changed from the base of the PR and between 5c22bb1 and 9ec2fd8.

📒 Files selected for processing (87)
  • src/easycat/_concurrency.py
  • src/easycat/config/_factory.py
  • src/easycat/config/easy.py
  • src/easycat/debugger/server.py
  • src/easycat/integrations/agents/_agent_runner.py
  • src/easycat/integrations/agents/_state_serialization.py
  • src/easycat/integrations/agents/generic_workflow.py
  • src/easycat/integrations/agents/langchain.py
  • src/easycat/integrations/agents/langgraph.py
  • src/easycat/integrations/agents/llama_agents.py
  • src/easycat/integrations/agents/openai_agents.py
  • src/easycat/integrations/agents/pydantic_ai.py
  • src/easycat/integrations/agents/responses_api.py
  • src/easycat/integrations/agents/template.py
  • src/easycat/runtime/journal_sql.py
  • src/easycat/runtime/scope.py
  • src/easycat/server/transports.py
  • src/easycat/server/voice_server.py
  • src/easycat/session/_audio_router.py
  • src/easycat/session/_debug_backends.py
  • src/easycat/session/_journal_sink.py
  • src/easycat/session/_session.py
  • src/easycat/session/_stt_committer.py
  • src/easycat/session/_turn_runner.py
  • src/easycat/session_manager.py
  • src/easycat/stages/turn.py
  • src/easycat/teardown_budgets.py
  • src/easycat/telephony/call_state.py
  • src/easycat/telephony/retry.py
  • src/easycat/telephony/voicemail.py
  • src/easycat/timeouts.py
  • src/easycat/transports/_webrtc_stats.py
  • src/easycat/transports/local.py
  • src/easycat/transports/webrtc.py
  • src/easycat/turn_manager.py
  • src/easycat/validation/_latency_artifacts.py
  • src/easycat/validation/_latency_baseline.py
  • src/easycat/validation/_latency_budgets.py
  • src/easycat/validation/_latency_models.py
  • src/easycat/validation/redaction.py
  • tests/cli/test_doctor.py
  • tests/config/test_session_creation.py
  • tests/core/test_timeouts.py
  • tests/debugger/_server_helpers.py
  • tests/debugger/test_aec_diagnostics.py
  • tests/debugger/test_server_replay_export.py
  • tests/debugger/test_server_rest_api.py
  • tests/debugger/test_server_route_controller.py
  • tests/debugger/test_server_security_origin.py
  • tests/integration/test_session_pipeline.py
  • tests/integrations/agents/test_agent_runner.py
  • tests/integrations/agents/test_bridge_template.py
  • tests/integrations/agents/test_generic_workflow_bridge.py
  • tests/integrations/agents/test_pydantic_ai_v2.py
  • tests/ratchets/pause-generation-manifest.json
  • tests/ratchets/source-baseline.json
  • tests/ratchets/teardown-budget-manifest.json
  • tests/ratchets/turn-commit-manifest.json
  • tests/ratchets/turn-lifecycle-manifest.json
  • tests/runtime/test_scope.py
  • tests/runtime/test_sqlite_journal.py
  • tests/server/test_capacity_gate_drain.py
  • tests/server/test_shutdown_draining.py
  • tests/server/test_voice_server_lifecycle.py
  • tests/server/test_websocket_runtime_drain.py
  • tests/session/test_audio_router.py
  • tests/session/test_journal_sink.py
  • tests/session/test_session_journal_accounting.py
  • tests/session/test_session_lifecycle_teardown.py
  • tests/session/test_session_manager.py
  • tests/session/test_session_stop_ordering.py
  • tests/session/test_session_streaming_behavior.py
  • tests/session/test_stt_committer.py
  • tests/session/test_turn_runner.py
  • tests/stages/test_stages.py
  • tests/teaching/test_chapter_15_doctor_contract.py
  • tests/telephony/test_call_state_basics.py
  • tests/telephony/test_call_state_gate.py
  • tests/telephony/test_outbound_config.py
  • tests/telephony/test_retry_strategy.py
  • tests/telephony/test_voicemail.py
  • tests/transports/test_local_transport.py
  • tests/transports/test_webrtc_lifecycle_server.py
  • tests/transports/test_webrtc_stats_artifacts.py
  • tests/turns/test_turn_manager.py
  • tests/validation/test_latency_boundaries.py
  • tests/validation/test_latency_percentiles.py

Comment thread src/easycat/debugger/server.py Outdated
Comment thread src/easycat/integrations/agents/_agent_runner.py Outdated
Comment thread src/easycat/integrations/agents/_agent_runner.py Outdated
Comment thread src/easycat/integrations/agents/_state_serialization.py
Comment thread src/easycat/runtime/scope.py
Comment thread tests/server/test_websocket_runtime_drain.py
Comment thread tests/session/test_stt_committer.py Outdated
Comment thread tests/stages/test_stages.py
Comment thread tests/telephony/test_retry_strategy.py
Comment thread tests/turns/test_turn_manager.py

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/integrations/agents/test_bridge_template.py`:
- Around line 361-362: Update the _OrderedSet.__iter__ method to declare an
Iterator[str] return annotation while preserving its existing iteration over
self._items.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e4932586-e9af-461c-af28-64a375bef734

📥 Commits

Reviewing files that changed from the base of the PR and between 9ec2fd8 and cf39b3f.

📒 Files selected for processing (25)
  • src/easycat/debugger/server.py
  • src/easycat/integrations/agents/_agent_runner.py
  • src/easycat/integrations/agents/_state_serialization.py
  • src/easycat/runtime/scope.py
  • src/easycat/server/voice_server.py
  • src/easycat/session/_stt_committer.py
  • src/easycat/teardown_budgets.py
  • src/easycat/telephony/call_state.py
  • src/easycat/telephony/retry.py
  • tests/debugger/test_aec_diagnostics.py
  • tests/debugger/test_server_replay_export.py
  • tests/debugger/test_server_rest_api.py
  • tests/integrations/agents/test_agent_runner.py
  • tests/integrations/agents/test_bridge_template.py
  • tests/ratchets/source-baseline.json
  • tests/ratchets/teardown-budget-manifest.json
  • tests/server/test_capacity_gate_drain.py
  • tests/server/test_shutdown_draining.py
  • tests/server/test_voice_server_lifecycle.py
  • tests/server/test_websocket_runtime_drain.py
  • tests/session/test_stt_committer.py
  • tests/session/test_turn_runner.py
  • tests/stages/test_stages.py
  • tests/telephony/test_retry_strategy.py
  • tests/turns/test_turn_manager.py

Comment thread tests/integrations/agents/test_bridge_template.py Outdated
@yisding
yisding merged commit c626bfd into main Aug 9, 2026
15 checks passed
@yisding
yisding deleted the agent/bug-audit-followup branch August 9, 2026 02:18
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.

2 participants