Skip to content

feat: persist autonomous alerts and surface them in session briefing (closes #161)#171

Open
luceinaltis wants to merge 2 commits into
mainfrom
feat/161-autonomous-monitoring-completion
Open

feat: persist autonomous alerts and surface them in session briefing (closes #161)#171
luceinaltis wants to merge 2 commits into
mainfrom
feat/161-autonomous-monitoring-completion

Conversation

@luceinaltis
Copy link
Copy Markdown
Owner

Summary

Closes #161.

Earlier work wired AutonomousMonitor into the Server tick loop and routed triggered alerts to the notification registry, but overnight findings evaporated when the process exited, the session briefing had no way to surface them, and autonomous.check_interval_seconds (called out in docs/autonomous-mode.md) was hardcoded. This PR closes that loop.

What changed

  • qracer/autonomous.py — new AutonomousAlertStore: file-backed persistence for AutonomousAlert records (~/.qracer/autonomous_alerts.json). Bounded at 500 entries, tolerant of missing / malformed files, and reloads cross-process on mtime change — same pattern as AlertStore. Exposes save, get_since(dt), clear, alerts, and __len__.
  • qracer/server.py — accepts an optional autonomous_alert_store kwarg; every alert produced by AutonomousMonitor.check() is saved before the NotificationRegistry dispatch. A try/except around the save means a disk failure can't block alert delivery.
  • qracer/conversation/quickpath.pygenerate_briefing() gains an autonomous_alert_store kwarg and a new "Overnight Autonomous Findings" section. Alerts are filtered by the previous session's mtime, capped at 10 entries with a trailing ... and N more, and the header shows the total pre-truncation count (matching the Pending Tasks pattern).
  • qracer/cli.py — both qracer repl and qracer serve instantiate AutonomousAlertStore(~/.qracer/autonomous_alerts.json) and thread it through. qracer serve now uses app_cfg.autonomous_check_interval_seconds for the autonomous scan cadence (formerly the CLI's shared --check-interval).
  • qracer/config/models.py + qracer/config/schema/config.toml — new autonomous_check_interval_seconds: int = 60 on AppConfig.
  • docs/autonomous-mode.md — replaces the "구현 예정" banner with an accurate status note (what's live vs. what's still roadmap).

Scope mapping (#161)

Scope item Status
Connect AutonomousMonitor to Server._tick() ✅ already in place
Route triggered events to notification providers (Telegram) ✅ already in place
Session briefing includes overnight autonomous findings via FactStore ✅ via AutonomousAlertStore (simpler than a FactStore column for this payload shape)
autonomous.enabled config key ✅ already in place as autonomous_enabled
autonomous.check_interval_seconds config key ✅ new in this PR
autonomous.significance_threshold ⚠️ covered by the existing price_move_threshold_pct + hardcoded 0.7 news-sentiment floor; a dedicated single-knob key is a follow-up
Integration tests with mocked price feeds ⚠️ store + briefing + server-wiring tests added; full mocked-feed integration tests remain future work

Items marked ⚠️ are intentionally left to follow-ups so this PR stays focused on persistence + briefing — the two user-visible gaps.

Test plan

  • uv run pytest tests/test_autonomous.py tests/test_server.py tests/conversation/test_quickpath.py109 passed (9 new TestAutonomousAlertStore cases + 3 new TestServerAutonomousPersistence cases + 3 new TestGenerateBriefing autonomous cases).
  • uv run pytest full suite — 793 passed, 14 skipped.
  • uv run ruff check on changed files — all checks passed.
  • uv run pyright on changed files — 0 errors.

New tests cover

  • AutonomousAlertStore: save + cross-process roundtrip; get_since filters by timestamp and orders newest-first; malformed created_at skipped; the 500-alert cap evicts oldest; clear persisted; missing file, malformed JSON, and partially-malformed entries all tolerated.
  • Server: alert is saved via store.save(alert) during _tick; no store → still notifies (backward compatible); store raising RuntimeError does not prevent the notification from firing.
  • generate_briefing: autonomous section appears with the correct total count, filters stale alerts (>1 session old), truncates at 10 with ... and N more and shows the pre-truncation total, omits the section entirely when the store is empty.

Manual verification

from pathlib import Path
from datetime import datetime, timezone, timedelta
from qracer.autonomous import AutonomousAlert, AutonomousAlertStore, Severity, TriggerType

store = AutonomousAlertStore(Path("/tmp/auto.json"))
store.save(AutonomousAlert(
    ticker="AAPL",
    trigger_type=TriggerType.PRICE_MOVE,
    summary="AAPL moved up 5.0% ($200.00 -> $210.00)",
    severity=Severity.CRITICAL,
))
# Reload across "processes" — alert survives.
assert len(AutonomousAlertStore(Path("/tmp/auto.json"))) == 1
store.get_since(datetime.now(timezone.utc) - timedelta(hours=1))  # -> [alert]

claude added 2 commits April 15, 2026 04:14
Closes #161.

`AutonomousMonitor` was wired into the `Server` tick loop and routing to
notification providers in earlier work, but overnight findings evaporated
when the process exited and the `autonomous.check_interval_seconds` knob
hinted at in the docs was hardcoded. This commit closes the loop:

- `AutonomousAlertStore` — file-backed persistence for triggered
  `AutonomousAlert` records (`~/.qracer/autonomous_alerts.json`).
  Bounded at 500 entries, tolerant of missing/corrupt files, and reloads
  cross-process on mtime change (matching `AlertStore`).
- `Server` now saves every autonomous alert to the store before
  dispatching to `NotificationRegistry`. Persistence failures are
  swallowed so they can't block alert delivery.
- `generate_briefing()` learned an `autonomous_alert_store` argument
  and a new "Overnight Autonomous Findings" section that filters alerts
  by the previous session's mtime, caps output at 10 entries, and shows
  the total count in the header.
- `qracer repl` and `qracer serve` both instantiate the store and thread
  it into their respective loops.
- `app.autonomous_check_interval_seconds` (default 60s) added to
  `AppConfig` + `config.toml` schema; `qracer serve` now uses it instead
  of the CLI `--check-interval` for autonomous scans.
- Docs updated to reflect the implemented status.

Scope items from #161 not addressed here (roadmap): volume-spike
trigger, `significance_threshold` beyond the existing price-move
percentage, and integration tests with mocked price feeds beyond the
unit-level fakes already in `tests/test_autonomous.py`.
`ruff check` passes individually but `ruff format --check .` flagged
four files for line-collapse/wrap differences. Auto-formatted.
No logic changes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: complete autonomous market monitoring during trading hours

2 participants