diff --git a/.github/workflows/backend-guardrails.yml b/.github/workflows/backend-guardrails.yml
new file mode 100644
index 000000000..f97174982
--- /dev/null
+++ b/.github/workflows/backend-guardrails.yml
@@ -0,0 +1,26 @@
+name: Backend Guardrails
+
+on:
+ pull_request:
+ paths:
+ - "src-tauri/**"
+ - ".github/workflows/backend-guardrails.yml"
+ workflow_dispatch:
+
+permissions:
+ contents: read
+
+jobs:
+ banned-patterns:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v7
+
+ - name: No Handle::refresh calls in feat
+ run: '! grep -rn "Handle::refresh" src-tauri/src/feat'
+
+ - name: No notice_message outside core/notify
+ run: '! grep -rn "notice_message(" src-tauri/src --include="*.rs" | grep -v "core/notify"'
+
+ - name: No manual update-flag chains
+ run: '! grep -rnE "determine_update_flags|UpdateFlags" src-tauri/src'
diff --git a/src-tauri/AGENTS.md b/src-tauri/AGENTS.md
new file mode 100644
index 000000000..a9356e9fc
--- /dev/null
+++ b/src-tauri/AGENTS.md
@@ -0,0 +1,24 @@
+# Backend Architecture Guardrails
+
+These rules supplement the repository's root `AGENTS.md`.
+
+- **Configuration ownership.** `config` owns source configuration, drafts and
+ persistence. The effects layer owns patch orchestration. Rollbackable
+ transactions publish state after persistence and commit; already applied
+ runtime and independently committed state remain authoritative even when a
+ later step fails. Preserve that state, its notifications and the error.
+- **Effect ownership.** The declarative registry classifies every Verge patch field,
+ including fields without effects and platform-specific fields. Effects
+ reconcile toward the requested state with explicit dependencies and ordering.
+ Configuration rollback does not imply reversal of OS or lifecycle effects.
+- **Shared wire contract.** Notifications are typed. Event names, status values
+ and payload shapes stay compatible with frontend producers and consumers.
+- **Synchronization ownership.** Configuration-write serialization precedes
+ configuration-update ownership, which precedes Core lifecycle ownership.
+ When both are held, the lifecycle lock precedes the DNS lock. Independent
+ transactions retain their ownership boundaries; asynchronous work does not
+ hold synchronous data guards across suspension.
+- **Retry ownership.** Bounded retries share backend policy while preserving
+ attempts, delays and terminal errors. Background monitors own their lifetime
+ and cancellation separately; waiting for a condition or traversing distinct
+ candidates is not a retry policy.
diff --git a/src-tauri/src/cmd/clash.rs b/src-tauri/src/cmd/clash.rs
index 76f059516..2d5a567db 100644
--- a/src-tauri/src/cmd/clash.rs
+++ b/src-tauri/src/cmd/clash.rs
@@ -1,4 +1,5 @@
use super::{CmdResult, CommandFailure, WithErrorCode as _, proxy_aware_coded_error, proxy_aware_error};
+use crate::core::notify::NoticeStatus;
use crate::feat;
use crate::utils::{dirs, yaml_emitter};
use crate::{
@@ -56,14 +57,14 @@ pub async fn change_clash_core(clash_core: String) -> CmdResult