Extracted _restore_fingerprint_tracking() helper and flattened early-return
guards to reduce nesting depth from 5 to 3. Seedgo deep_nesting now passes.
370 tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
_fire_to_handlers now tracks consecutive failures per handler. After 5
consecutive failures, the handler is auto-disabled (skipped) and a CRITICAL
log is emitted. Success resets the failure count. Disabled handlers re-enable
on process restart (in-memory tracking). Prevents broken handlers from
flooding logs forever. 370 tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Moved CB state from trigger_config.json to dedicated trigger_cb_state.json.
Now persists full state: recent_errors timestamps, half_open_allow, summary_sent,
and per-fingerprint dispatch tracking (last_dispatch + count). Restored on
startup. record_dispatch() now triggers persist. Uses atomic_write_json +
json_file_lock. Module-level dict init moved before CB load so fingerprint
data restores correctly. 370 tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Added json_file_lock() context manager to config.py using fcntl.flock
with .lock sidecar files. Wrapped all read-modify-write cycles:
- error_registry.py: report(), _save/_clear_circuit_breaker_state()
- medic_state.py: set_enabled(), mute_branch(), unmute_branch()
- log_watcher.py: _save_seen_hashes(), _save_log_positions()
Combined with existing atomic_write_json for both concurrency and crash safety.
370 tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
error_logged.py handler stripped to monitor-only (log event, no email/dispatch).
Centralized watcher (watchers/log_watcher.py) now calls registry_report() +
fires error_detected for ERROR-level lines, routing through full Medic v2
pipeline (count threshold, circuit breaker, fingerprint backoff). Falls back
to error_logged (monitor-only) if registry unavailable. 370 tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
report_error() and log_watcher primary path both had a gate that only fired
error_detected at count==1 (new) and count==2 (exact match). After count
passed 2, the event never fired again, so the handler's backoff logic never
got a chance to re-dispatch. Fix: always fire the event and let the
error_detected handler's Medic v2 gating (circuit breaker, backoff, rate
limiting) decide. 370 tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The fallback path in log_watcher._process_log_line() used _is_duplicate_error()
to skip repeated errors entirely. This prevented registry count from incrementing
past 1, so dispatch (which requires count>=2) never fired. Fix: retry lazy
registry import in fallback path; if truly unavailable, track count locally
and fire event with count so error_detected handler can apply threshold.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
After delivering error notification email, now calls wake_branch() to spawn
an agent in the target branch immediately. Changes reply_to from @trigger
to @devpulse so resolution reports go to devpulse. 370 tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
After a real API key leaked through test files with realistic hex patterns,
this adds automated secret detection: .gitleaks.toml with custom rules for
OpenRouter/OpenAI/Anthropic/Google keys, and .pre-commit-config.yaml wiring
gitleaks as a pre-commit hook. Test keys locally updated to use FAKE-/NOTREAL
conventions that pass the allowlist.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
3 CRITICAL security fixes: (1) get_cache_stats() no longer exposes raw API keys, (2) .env created with 0o600 perms + dir with 0o700, (3) google_creds.json same restricted permissions. 3 BUG fixes: validation rules aligned between keys.py and provider.py, cleanup return type (0 is success not failure), show_stats() now aggregates across all callers vs show_session() for current session. 290/290 tests pass, seedgo 99%.
Co-Authored-By: @api <api@aipass>
All 11 JSON write locations across 7 files now use write-to-tmp + os.replace
pattern via shared atomic_write_json() in config.py. Prevents file corruption
if the process crashes mid-write. Updated 8 test files to provide the real
atomic_write_json on mocked config modules. 370 tests passing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>