Fix #815: deduplicate SessionConnector::watch() emissions with debouncing#858
Fix #815: deduplicate SessionConnector::watch() emissions with debouncing#858AlexMikhalev wants to merge 2266 commits intomainfrom
Conversation
… + AutoMerge enqueue' (#717) from task/roc-v1-step-f-verdict-polling into main
…Refs adf-fleet#35
…impl — Refs adf-fleet#35
…ostMergeTestGate enqueue + [ADF] issue on failure) — Refs adf-fleet#35
…andle_auto_merge — Refs adf-fleet#35
…ath — Refs adf-fleet#35 Add 5 integration tests driving handle_auto_merge_for_project with an in-memory RecordingExecutor (no mock frameworks, async-trait impl): * auto_merge_success_enqueues_post_merge_gate * auto_merge_skipped_when_head_sha_changed * auto_merge_failure_opens_adf_issue * auto_merge_marks_dedupe_set_on_success * auto_merge_skipped_when_pr_already_closed Expose pub auto_merge_enqueued() accessor on AgentOrchestrator so tests can verify the (project, pr_number, head_sha) dedupe set is populated after a successful merge.
… from task/roc-v1-step-g-auto-merge-handler into main
…ait + config — Refs adf-fleet#36 Adds crates/terraphim_orchestrator/src/post_merge_gate.rs with: - CommandRunner async trait + TokioCommandRunner real impl (streams stdout/stderr tails via bounded ring buffer so long test runs do not OOM; kills child on wall-time timeout). - run_workspace_tests / classify_failure / revert_merge helpers. - ScriptedRunner test double (no mock library — trait impl only). - 9 inline unit tests covering runner behaviour, failure parsing, and revert paths (green, timeout, io-error, test-failure parse, harness detection, timeout classification, revert no-push, revert with push, revert all-paths-failed). Adds PostMergeGateConfig to OrchestratorConfig (optional; defaults to 10 minute budget, push revert to origin main). Back-fills the new field on every existing OrchestratorConfig initializer across lib.rs test fixtures and integration tests.
…k - Refs adf-fleet#36 Replaces the Step B log-only stub for DispatchTask::PostMergeTestGate with a real handler that: - Resolves project working_dir as repo_root - Runs post_merge_gate::run_workspace_tests with GateConfig built from orchestrator.toml [post_merge_gate] overrides (default 10 min budget) - On green: logs post_merge_gate_verified at info - On red: classifies failure, calls post_merge_gate::revert_merge to create revert commit + push to remote, opens [ADF] post-merge test gate reverted issue on the project's Gitea repo with merge_sha, revert_sha, top failing tests, stderr tail, wall_time - Logs post_merge_gate_reverted at warn handle_post_merge_test_gate_with_runner<R: CommandRunner + ?Sized> is parametrised so integration tests can inject ScriptedRunner without spawning cargo. Adds truncate_for_issue helper for char-boundary-safe trimming of stderr tails when opening the [ADF] issue (avoids corrupting UTF-8 mid-sequence). All orchestrator tests pass; clippy clean; fmt clean.
…/merge — Refs adf-fleet#37 Add OrchestratorEvent enum (PrReviewed, PrAutoMerged, PrAutoMergedVerified, PrAutoReverted) to quickwit.rs with emit_event helper on QuickwitFleetSink. All emits are gated behind the quickwit feature and tolerate Quickwit down (warn log, business logic unblocked). Wire emit sites: - PrReviewed in poll_pending_reviews_for_project after verdict parse - PrAutoMerged in handle_auto_merge_for_project after merge_pr succeeds - PrAutoMergedVerified in handle_post_merge_test_gate_with_runner on green - PrAutoReverted in handle_post_merge_test_gate_with_runner on revert Replace the two quickwit_event_placeholder log lines from Step H with the typed emits above. Add tests/quickwit_events_tests.rs: 4 field-mapping tests + 1 tolerance test (all gated behind quickwit feature). 501 tests pass; clippy clean; fmt clean. Co-Authored-By: Terraphim AI <noreply@terraphim.cloud>
…e flow (ROC v1 Step I)' (#726) from task/roc-v1-step-i-quickwit-events into main
…reviewers event-driven) — Refs adf-fleet#39 No template change needed: implementation-swarm is not yet in scripts/adf-setup/agents/. Developer cron bump to */20 applies to live conf.d/*.toml during Step L rollout.
…ump (ROC v1 Step K)' (#727) from task/roc-v1-step-k-cron-extension into main
…lo -> terraphim -- Refs adf-fleet#40 Co-Authored-By: Terraphim AI <noreply@terraphim.cloud>
…728) from task/roc-v1-step-l-rollout-runbook into main
Updates the adf.rs provider registration from Kimi K2.5 to K2.6 so the ProviderHealthMap probe + KeywordRouter fallback target the current model. KG routing tables (planning_tier.md) and conf.d/digital-twins.toml already reference kimi-for-coding/k2p6; this aligns register_providers. Tests unchanged (test fixtures use synthetic k2p5 strings that remain valid — provider_key_for_model is generic and C1 allow-list checks the kimi-for-coding prefix, not the specific model string).
…tion to k2p6' (#734) from task/kimi-k2p6-provider into main
…rand - Patch rustls-webpki from 0.103.10 to 0.103.12 (fixes name constraints bypass) - Add RUSTSEC-2026-0098/0099 to deny.toml (serenity/discord-feature-only) - Replace rand with WASM-compatible fastrand in terraphim_multi_agent and terraphim_kg_agents - Remove two direct rand dependencies from workspace crates Refs #630
…on (#818) * feat(cli): add evaluate subcommand for automata ground-truth evaluation Wire the existing evaluate() function to a CLI subcommand in terraphim_cli. Changes: - Add Evaluate command with --ground-truth and --thesaurus flags - Add handle_evaluate() function using terraphim_automata::evaluate() - Add 4 integration tests for evaluate command - Wire Evaluate match arm in command dispatcher The core evaluation logic was already implemented in terraphim_automata::evaluation (~613 lines, 13 unit tests). This adds CLI integration for automation use. Example usage: terraphim-cli evaluate --ground-truth gt.json --thesaurus th.json Part of: Gitea #576 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(clippy): replace sort_by with sort_by_key for Rust 1.95 compatibility Rust 1.95 promotes clippy::unnecessary_sort_by to hard error under -D warnings. Convert all sort_by calls to sort_by_key across 3 crates: - terraphim-markdown-parser: 1 change (descending sort with Reverse) - terraphim_router: 1 change (descending sort with Reverse) - terraphim-session-analyzer: 13 changes (ascending + descending) Line 548 in reporter.rs retains sort_by with #[allow] due to fallible string parsing in the key function. Refs #576 * fix(clippy): fix remaining sort_by in session-analyzer main.rs and submodules Missed in previous commit: session-analyzer has duplicated logic in main.rs (binary target) and submodules (kg/search, patterns/loader) that also use sort_by. Convert to sort_by_key where possible, add #[allow] for float comparisons using partial_cmp. Refs #576 * fix(clippy): workspace-wide sort_by -> sort_by_key for Rust 1.95 compatibility Convert all remaining sort_by calls across 40 files to either sort_by_key or #[allow(clippy::unnecessary_sort_by)] for cases with non-Copy types, multi-line closures, or partial_cmp on floats. Covers: terraphim_agent, terraphim_automata, terraphim_orchestrator, terraphim_service, terraphim_persistence, terraphim_update, terraphim_usage, terraphim_sessions, terraphim_cli, terraphim_mcp_server, terraphim_types, terraphim_symphony, terraphim_tinyclaw, terraphim_multi_agent, terraphim_agent_evolution, terraphim_agent_registry, terraphim_goal_alignment Refs #576 * fix(clippy): fix Rust 1.95 lints in task_decomposition and rolegraph examples - Remove unnecessary .into_iter() in extend() call (useless_conversion lint) - Collapse if guards into match arms (collapsible_match lint) - Allow explicit_counter_loop in rolegraph examples Refs #576 * fix(clippy): allow collapsible_match lint in middleware and agent_evolution Rust 1.95 clippy promotes collapsible_match to hard error under -D warnings. Add #![allow] at file level for ripgrep.rs, orchestrator_workers.rs, and parallelization.rs where collapsing the match arms would reduce readability. Refs #576 * fix(clippy): allow collapsible_match in goal_alignment/goals.rs Refs #576 * fix(ci): pin Rust toolchain to 1.94.0 to match local dev dtolnay/rust-toolchain@stable installs latest (1.95.0) which has new clippy lints (collapsible_match, unnecessary_sort_by, useless_conversion) not present in 1.94. Pin all ci-pr.yml jobs to 1.94.0 and update rust-toolchain.toml accordingly. Refs #576 --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Cherry-pick from local main (0115f06f). Refs #612 Co-authored-by: Test User <test@test.com>
Pre-build at script line 98 ran cargo build --workspace --all-targets without --features zlob. fff-search build.rs panics under CI when zlob isn't enabled (intentional gate). Clippy step at line 112 already had the flag; pre-build needed it too. Unblocks lint-and-format CI for PR #818 and any future PR. Refs #818 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
openai/* via the OpenAI Plus/Pro/Team plan is subscription-gated, not pay-per-use. PR #631 (the P0 C1 fix) removed openai from the allow-list under the mistaken belief that all openai/* was pay-per-use; this commit restores it. Session-token exhaustion (the temporary state that motivated #631 in the first place) is already handled correctly by the orchestrator's existing primitives: - ProviderHealthMap probes every probe_ttl_secs (1800s / 30 min) and marks exhausted providers unhealthy. Routing falls through to the next KG route (anthropic/opus -> kimi-for-coding/k2p6 -> openai/gpt-5.4 -> zai-coding-plan/glm-5). - CircuitBreaker opens on 5 consecutive failures, half-opens after cooldown, closes on successful probe. - ProviderBudgetTracker hour+day tumbling UTC windows; force_exhaust() on throttle classification pushes past cap so routing drops until the next window rolls. - error_signatures.rs classifies stderr as Throttle / Flake / Unknown per provider with configurable regex. Changes: - config.rs: add "openai" to ALLOWED_PROVIDER_PREFIXES. - config.rs tests: include openai/gpt-5.4 and openai/gpt-5.3-codex in test_is_allowed_provider_c1_allowed_prefixes. - provider_gate_tests.rs: rename probe_gate_rejects_openai_provider -> probe_gate_allows_openai_subscription_provider; invert assertion and document the session-token-tracking rationale. - docs/taxonomy/routing_scenarios/adf/planning_tier.md: retain anthropic/opus as primary route, add kimi-for-coding/k2p6 and openai/gpt-5.4 as alternate routes; remove the previously-present legacy openai/gpt-5.4 entry that had been stripped as a C1 violation. Tests: all 497 unit tests + 16 integration tests pass. Clippy + fmt clean.
…st + KG routing' (#737) from task/kg-planning-tier-k2p6 into main
Replace the in-memory DeviceStorage backend with MarkdownLearningStore for durable persistence of shared learnings across process restarts. Changes: - Expand markdown frontmatter to serialize full SharedLearning state - Add backwards-compatible parsing for old sparse frontmatter files - Replace DeviceStorage with MarkdownLearningStore in SharedLearningStore - Implement real load_all() that hydrates index from persisted markdown - Add startup deduplication by learning.id (canonical over shared copies) - Update StoreConfig to include MarkdownStoreConfig - Align default paths with ProjectDirs recommendation - Add integration tests for restart durability and deduplication - Fix unreachable match arm for SharedLearningSub::Inject Refs #6
Handle missing learnings root as an empty store on first run and make startup deduplication deterministic by preferring canonical agent copies over shared replicas when IDs collide. Refs #6
Adds the agent template that the Phase 2 fan-out loop spawns when `pr-security-sentinel` appears in `[pr_dispatch.agents_on_pr_open]`. Mirrors the pr-spec-validator skeleton: - sonnet (subscription-only) — security-audit needs deeper reasoning than a structural diff scan to spot data-flow vulnerabilities and unsafe-code subtleties; haiku is the wrong tier - skill_chain = ["security-audit"] - 2-hour idempotency check via the existing comment-marker pattern (uses agent-scoped marker "Last security-audited commit: <sha>") - best-effort POST_STATUS helper mirroring build-runner.toml Path filter (early-exit happy path) — implemented in this script, NOT in the orchestrator: - skips the LLM call when no `Cargo.toml`, `Cargo.lock`, `crates/**/src/**`, or `**/secrets/**` paths changed - posts `success` (not no-post) with description "n/a — no security-relevant changes" because `adf/security` becomes a required check on `main` post-deploy; a never-resolved `pending` would block docs-only PRs forever Verdict parsing prefers an explicit `Verdict:` line; falls back to a `Risk:` line (critical/high → fail, medium → concerns, low → pass); finally falls back to `state=success` "audit posted; manual gate" when neither parses — so the commit status is never left in `pending` even on transient agent failures. The cron-scheduled `security-sentinel` agent stays in tree for full-repo audits; this PR-event variant is additive and diff-scoped. Operator follow-up after merge: 1. Rebuild + restart the orchestrator on bigbox so the new dispatch arm goes live. 2. PATCH branch_protections/main to add `adf/security` to required contexts. Refs #953
…2c) Refs #953 Rebased onto main after Phase 2b/d/e merges.
…open with path filter' (#1028) from task/953-adf-phase-2c-security-sentinel into main
…eMeta Refs #1026' (#1029) from sync/1026-query-role-meta into main
…eports for #1026' (#1030) from sync/vv2-1026 into main
…ams SDK tests Refs #1034
… #1012 - Intercept args before clap parsing to apply typo correction, alias expansion, and case-insensitive matching - Add build_cli_forgiving_parser() with actual CLI subcommands - Add apply_forgiving_parsing() heuristic for subcommand detection - Print correction notifications to stderr - Add 3 integration tests: auto-correction, alias expansion, case-insensitive matching
…PI Refs #1011 - Add Robot variant to Command enum with subcommands: capabilities, schemas, examples - Add RobotFormat enum (json, table, minimal) for --format flag - Add handle_robot_command() and format_robot_output() helpers - Wire Robot command in main() before catch-all server/offline dispatch - Add unreachable! arms in run_offline_command and run_server_command to satisfy exhaustive match (Robot is handled in main) - Add 4 integration tests: capabilities JSON, schemas JSON, examples text, all formats honoured - Fix pre-existing compilation errors in terraphim_orchestrator (config clone + concurrency_permit field)
- Add [Unreleased] section with commits since 1.17.0 - Generate doc gap report: 771 undocumented items across 46 crates - Generate API reference snippets from documented public APIs Refs #1046
- Change ContentBlock::ToolResult from is_error: bool to exit_code: i32 with backward-compatible deserialization for old JSON containing is_error - Add --include-pinned flag to search subcommand in terraphim_agent - Add --pinned flag to graph subcommand as kg list --pinned equivalent - Fix learn query to call query_all_entries when semantic=false, matching spec - Add pinned_node_ids to RoleGraphResponseDto for server/agent parity - Add get_role_graph_pinned to TuiService for offline pinned retrieval
- Add spec gaps fix entry to CHANGELOG (Refs #1040) - Generate documentation gap report for workspace crates - Generate API reference snippets for key public items - Update existing Gitea issue #1046 with recurrence findings Part of: #1046
…Refs #451 - Add hook_manager field to TerraphimAgent with HookManager initialized in ::new() - Add execute_llm_with_hooks() helper that wraps LLM calls with run_pre_llm/run_post_llm - Wire hooks into 5 agent command handlers: generate, answer, analyze, create, review - Wire hooks into specialized agents: ChatAgent and SummarizationAgent - Add unit tests verifying hook invocation (pre_llm, post_llm, block, modify decisions) - All existing tests pass, clippy clean, fmt clean Implements requirement from spec-validator remediation #442 to wire LLM hooks that were previously defined but not connected.
- Update [Unreleased] with 20 entries from 2026-04-27 to 2026-04-28 - Scan 58 crates: ~2,647 pub items missing documentation - Generate gap report at .docs/reports/documentation-gap-report-20260428.md - Recurrence comment on Gitea issue #1046 Refs #1046
Clippy fixes (blocking ADF build-runner on all PRs): - Remove needless borrows in exit_codes_integration_test.rs - Replace useless format! with .to_string() in offline_mode_tests.rs - Remove unused LearningStore import in learning.rs Test helper fixes (pre-existing failures): - Update review_pr_config_with_spec_fanout to push pr-spec-validator into pr_dispatch_per_project['alpha'] (per-project config takes precedence over top-level pr_dispatch) - Same fix for review_pr_config_with_security_checklist_fanout and review_pr_config_with_test_fanout Refs #238
…d handlers' (#1056) from task/451-llm-hooks-wiring into main
… test helpers' (#1058) from task/fix-clippy-and-test-helpers into main
…ebouncing Refs #815 - Track (path -> messages_len, Instant) per file in shared HashMap - Only emit when messages.len grew AND debounce window (~250ms) elapsed - Document dedup contract on SessionConnector::watch trait method - Fix clippy collapsible-if in emission guard - Integration test: 3 incremental writes produce at most 5 emissions
…ound assertion Refs #815
security_checklist SummaryPR #815 ( Risk: lowVerdict: passFindingsLow — Mutex poison propagates as silent task failure let mut guard = last_seen.lock().unwrap();
The critical section cannot be triggered by external input — paths originate from the Recommendation: replace Informational — Unbounded HashMap accumulation
Informational — Path extension check without canonicalisation if path.extension().is_some_and(|ext| ext == "jsonl")The Dependency ChangesNone. Scope of ChangesThe remaining diff is purely additive documentation (rustdoc on enum variants,
Last security_checklist-audited commit: 971b3e0 |
Security Audit SummaryPR #858 introduces debounce/dedup logic to Risk: lowVerdict: passFindings[LOW] Mutex poison propagation in production watch loop let mut guard = last_seen.lock().unwrap();
Recommendation: recover from poisoned state: let mut guard = last_seen.lock().unwrap_or_else(|e| e.into_inner());[INFORMATIONAL] Valid — the runtime handle is accessible from blocking threads. Lock guard is dropped before any Dependency ChangesNone. Cargo.toml and Cargo.lock unchanged. Scope not affected
Last security-audited commit: 971b3e0 |
Requirements Traceability SummaryPR #858 — Fix #815: deduplicate Branch: Scope: Verdict: passTraceability Matrix
Gaps
Last spec-validated commit: 971b3e0 |
Requirements Traceability SummaryPR #858 ( Spec sources: PR #858 description, commit messages Verdict: concernsCore requirement implemented and verified. Three follow-up items noted — none are release blockers, but the rustdoc/code mismatch and the missing shutdown test are worth tracking. Traceability Matrix
Gaps
Recommendation: align rustdoc to "≈200 ms" or extract a named constant and reference it in the doc. The Recommendation: add a test that drops
The debounce map grows proportionally with unique paths observed and is never pruned. Benign for typical Terraphim AI project directories (tens to hundreds of files), but worth a bounded structure (LRU-1000) for correctness at scale. Recommendation: open a follow-up issue; not a blocker for this PR. ℹ️ Note: Two duplicate commits carry identical messages SHAs Last spec-validated commit: 971b3e0 |
Requirements Traceability SummaryPR #858 fixes a session-watch deduplication defect in Referenced issue cross-System confusion: PR title cites Verdict: concernsTraceability Matrix
Gapsconcerns REQ-003 — Debounce constant inTerraphim Graph Embeddings: Learning Agent Guide concerns NFR-001 — No explicit receiver-drop test concerns NFR-002 — No multi-instance isolation test note — Issue cross-System mismatch Last spec-validated commit: HEAD (PR #858 task/815-session-connector-dedup) |
Requirements Traceability SummaryPR: #858 — Fix #815: deduplicate SessionConnector::watch() emissions with debouncing This PR is tightly scoped to two files: The change replaces an unbounded event-loop with a debounced, closed-channel-aware loop that tracks Verdict: concernsThe core dedup invariant is correctly implemented and the integration test Traceability Matrix
Gaps
No design note captures this trade-off. Recommend a one-paragraph comment in ℹ️ Referenced issue #815 is a merged PR, not a dedup issue
Last spec-validated commit: pr-858 |
Summary
HashMap<PathBuf, (usize, Instant)>tracking inNativeTerraphim AIConnector::watch()messages.lengrew AND 250 ms debounce window elapsed since last emissionSessionConnector::watchtrait methodcollapsible_ifin the emission guardTest plan
cargo test -p terraphim_sessions— all 54 tests pass includingtest_watch_dedup_on_incremental_writescargo clippy -p terraphim_sessions— cleancargo fmt -p terraphim_sessions -- --check— cleanRefs #815
🤖 Generated with Terraphim AI