fix(core): parallelize PreToolUse hooks, backfill extraction, dedupe MAGE mapping - #6297
Merged
Conversation
bug-ops
enabled auto-merge (squash)
July 14, 2026 20:38
…upe MAGE signal mapping Three independent architecture-review findings in the agent turn loop: PreToolUse hooks fired sequentially per tier (mirrors the already-fixed PostToolUse twin, #6128); graph_backfill extracted entities strictly sequentially despite the store's per-entity upsert already making concurrent extraction safe; begin_turn re-derived the MAGE signal type from a raw code instead of matching the already-computed RiskSignal enum. Closes #6259 Closes #6261 Closes #6272
bug-ops
force-pushed
the
fix/6259-agent-tier-arch-fixes
branch
from
July 14, 2026 20:38
cd65bb9 to
210a9a9
Compare
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
Three independent architecture-review findings in
crates/zeph-core/src/agent/, all P2, no dependencies between them, filed by the same review pass and grouped into one PR.build_tier_call_futuresfired each tier'sPreToolUsehooks sequentially, addingN x hook_latencyof purely serial blocking on the agent turn loop before the tier's already-parallelized tool execution even began — the same defect class already fixed for thePostToolUseside (tier_loop: apply_tier_results serializes RuntimeLayer/PostToolUse hooks per tool result within a tier #6128) but never mirrored toPreToolUse. Hooks now fire concurrently (Phase 1, bounded by the tier semaphore) before the sequential gate-check loop (Phase 2), mirroring the tier_loop: apply_tier_results serializes RuntimeLayer/PostToolUse hooks per tool result within a tier #6128 pattern. The per-idx ordering invariant (a call's own hook fires before that call's own gate check) is preserved structurally since Phase 1 fully completes before Phase 2 starts; there is no ordering requirement across different idx values.AgentAccess::graph_backfillextracted entities/edges from each unprocessed message strictly sequentially (one LLM call plus SQLite/Qdrant write at a time), despite the store's DB-levelUNIQUE(canonical_name, entity_type)+ON CONFLICT ... RETURNING idupsert already making concurrent extraction across messages safe. Replaced withfutures::stream::iter(...).buffer_unordered(4), matching the existingsemantic_scan_plugin_addpattern/bound exactly — cuts backfill wall time roughly 4x with no correctness change.Agent::begin_turnre-derived the MAGE(AuditSignalType, Severity)pair from the raw trajectory-signalu8code via an independent hand-rolled match, duplicating the code-to-meaning table already authoritative inRiskSignal::from_code— the two tables were not compiler-coupled and could silently drift. Now matches on the already-computedRiskSignalenum value instead, removing the second magic-number table. Pure refactor, zero behavior change (exhaustively verified againstRiskSignal::from_code's full variant set).Review notes
A review pass caught a factually incorrect safety comment introduced for #6261: the original comment attributed graph_backfill's concurrency safety to
EntityResolver::lock_name, butextract_and_storebuilds a freshEntityResolver(and a fresh, emptyname_locksmap) per call, solock_namenever spans the 4 concurrent calls in a batch. The comment now correctly cites the DB-levelUNIQUEconstraint +ON CONFLICTupsert as the actual safety mechanism.graph_backfillpassesembedding_store=None, so the one non-transactional path (Qdrant fuzzy match) is skipped entirely for backfill.Test plan
pre_tool_use_concurrency_tests.rs(timing-based concurrency proof + per-idx isolation test for PreToolUse hooks fire sequentially per tier, unlike the already-fixed PostToolUse twin (#6128) #6259),graph_backfill_concurrent_extraction_aggregates_stats_without_dropping_results+graph_backfill_concurrent_extraction_survives_real_sqlite_write_contention(real file-backed SQLite, WAL + busy_timeout, 8 messages exceeding the concurrency bound, for graph_backfill processes messages strictly sequentially despite store's per-entity locking making concurrent extraction safe #6261),mage_signal_mapping_tests.rs(pins the exact old-vs-new mapping for begin_turn re-derives MAGE signal type from raw u8 code instead of matching RiskSignal enum #6272).cargo +nightly fmt --check— cleancargo clippy --profile ci --workspace --all-targets --features "desktop,ide,server,chat,pdf,scheduler,testing" -- -D warnings— cleancargo nextest run --config-file .github/nextest.toml --workspace --features "desktop,ide,server,chat,pdf,scheduler" --lib --bins— 13717 passed, 0 failed, 34 skippedRUSTFLAGS="-D warnings" RUSTDOCFLAGS="--deny rustdoc::broken_intra_doc_links" cargo doc --no-deps --workspace --features "desktop,ide,server,chat,pdf,scheduler"— cleangitleaks protect --staged— no leaks.local/testing/coverage-status.mdupdated (existing rows for PreToolUse hooks fire sequentially per tier, unlike the already-fixed PostToolUse twin (#6128) #6259/begin_turn re-derives MAGE signal type from raw u8 code instead of matching RiskSignal enum #6272, new row for graph_backfill processes messages strictly sequentially despite store's per-entity locking making concurrent extraction safe #6261)CHANGELOG.mdupdatedCloses #6259
Closes #6261
Closes #6272