Skip to content

fix(memory): wire MAGE soft-escalation gate and fix community.rs newline injection#6135

Merged
bug-ops merged 1 commit into
mainfrom
fix/5956-mage-escalation-injection
Jul 12, 2026
Merged

fix(memory): wire MAGE soft-escalation gate and fix community.rs newline injection#6135
bug-ops merged 1 commit into
mainfrom
fix/5956-mage-escalation-injection

Conversation

@bug-ops

@bug-ops bug-ops commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Summary

  • Wires TrajectoryRiskAccumulator::should_escalate()/record_escalation() (spec 004-16 FR-006, MAGE shadow-memory soft-warning tier) into the tool-dispatch loop. Previously only the hard-block tier (is_blocked()) gated tool execution — the graduated warn-before-block design was inert. When cumulative trajectory risk lands in [escalation_threshold, risk_threshold), the agent now requires a single batch-level human confirmation, then dispatches through the unmodified tier execution loop so check_trust/PermissionPolicy and the shadow-probe safety gate still apply per call exactly as without escalation.
  • Adds config-load validation rejecting an inverted or collapsed escalation_threshold/risk_threshold band.
  • Replaces whitespace-preserving strip_format_chars with strip_control_chars for entity_names/intra_facts in classify_communities, closing a newline/tab prompt-injection vector reintroduced by the fix(security): consolidate duplicated sanitization and redaction logic #6091 sanitization consolidation.

Closes #5956
Closes #6093

Review history

This PR went through the full team-develop bug-fix chain (developer → tester → adversarial critic → developer → critic re-verify → code reviewer):

  • An earlier version of the bug(memory): TrajectoryRiskAccumulator's should_escalate()/record_escalation() soft-warning tier never wired into the agent loop — only the hard is_blocked() gate fires #5956 fix synthesized ToolError::ConfirmationRequired per call and dispatched approved calls through execute_tool_call_confirmed_erased, which intentionally bypasses check_trust for already-approved calls. That would have let a policy-Deny tool execute under MAGE escalation — including unattended under auto-approve/-y/--bare/non-TTY CLI modes — precisely when accumulated risk signals made that the worst possible moment to drop the gate. Caught by adversarial review before merge; fixed by gating on one up-front batch confirmation and falling through to the unmodified, fully-gated tier execution loop. Independently re-verified closed by both the critic and the code reviewer.
  • Tester and critic also drove: exact-boundary tests at escalation_threshold/risk_threshold, a tightened hard-block-vs-escalation precedence test, and 3 regression tests using a real TrustGateExecutor+PermissionPolicy stack (not a permissive mock) proving a policy-Deny tool never executes on approval, including under auto-approve.

Test plan

  • cargo +nightly fmt --check
  • cargo clippy --profile ci --workspace --all-targets --features "desktop,ide,server,chat,pdf,scheduler,testing" -- -D warnings
  • cargo nextest run --config-file .github/nextest.toml --workspace --features "desktop,ide,server,chat,pdf,scheduler" --lib --bins (13021 passed, 0 failed)
  • Rustdoc gate (RUSTFLAGS="-D warnings" RUSTDOCFLAGS="--deny rustdoc::broken_intra_doc_links" cargo doc --no-deps --workspace --features "desktop,ide,server,chat,pdf,scheduler")
  • 23 new tests: 7 in zeph-memory/src/shadow/mod.rs, 7 in new zeph-core/.../tests/mage_escalation_tests.rs, 1 in zeph-memory/src/graph/community.rs, 3 config-validation tests in zeph-config/src/loader.rs, plus 5 more spread across the above per the review history
  • .local/testing/playbooks/memory.md and memory-graph.md updated with new scenarios
  • .local/testing/coverage-status.md updated in place

@github-actions github-actions Bot added bug Something isn't working size/XL Extra large PR (500+ lines) documentation Improvements or additions to documentation memory zeph-memory crate (SQLite) rust Rust code changes core zeph-core crate and removed size/XL Extra large PR (500+ lines) labels Jul 12, 2026
…ine injection

Wire TrajectoryRiskAccumulator's should_escalate()/record_escalation()
soft-warning tier into the tool-dispatch loop: risk in
[escalation_threshold, risk_threshold) now requests a batch-level
human confirmation before dispatch, then falls through to the
unmodified tier execution loop so per-call check_trust/PermissionPolicy
and shadow-probe gates still apply. Previously only the hard-block
tier fired, leaving the graduated warn-before-block design inert.

Also add config-load validation rejecting an inverted or collapsed
escalation_threshold/risk_threshold band.

Replace whitespace-preserving strip_format_chars with
strip_control_chars for entity_names/intra_facts in
classify_communities, closing a newline/tab prompt-injection vector
reintroduced by the #6091 sanitization consolidation.
@bug-ops
bug-ops enabled auto-merge (squash) July 12, 2026 14:08
@bug-ops
bug-ops force-pushed the fix/5956-mage-escalation-injection branch from 217c759 to 303cde8 Compare July 12, 2026 14:08
@github-actions github-actions Bot added the size/XL Extra large PR (500+ lines) label Jul 12, 2026
@bug-ops
bug-ops merged commit 09a3aaf into main Jul 12, 2026
43 checks passed
@bug-ops
bug-ops deleted the fix/5956-mage-escalation-injection branch July 12, 2026 14:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working core zeph-core crate documentation Improvements or additions to documentation memory zeph-memory crate (SQLite) rust Rust code changes size/XL Extra large PR (500+ lines)

Projects

None yet

1 participant