#72: dashboard — content-filter recent-hits ring#89
Merged
Conversation
content_filter() (bridge/text.py) already has the tier and the matched term in scope at every hit. Keep the last 20 hits in an in-memory ring and expose them at /ui/safety/recent. - bridge/text.py: _cf_recent deque (maxlen 20) + recent_content_filter_hits getter; content_filter appends (ts, tier, rule, 8-char prefix) per hit. In-memory only — never written to disk; no more exposed than the content-filter-hit log line content_filter() already emits. - dashboard.py: GET /ui/safety/recent. - safety_recent.html + a card in dashboard.html. Closes #72. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Implements issue #72 by keeping an in-memory ring buffer of the most recent content-filter hits and surfacing them in the dashboard at /ui/safety/recent, allowing operators to quickly inspect recent tier/rule/prefix details without persisting any filtered text to disk.
Changes:
- Add a 20-entry in-memory deque in
bridge/text.pythat records timestamp, tier, matched term, and an 8-character prefix on eachcontent_filter()hit. - Add
GET /ui/safety/recentinbridge/dashboard.pyto render the recent-hit ring into a dashboard partial. - Add a new
safety_recent.htmlpartial template and a new Safety card section todashboard.htmlthat polls the endpoint every 30s.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| bridge/text.py | Adds recent-hit ring buffer and accessor; appends hit metadata during content_filter() matches. |
| bridge/dashboard.py | Adds /ui/safety/recent route to format and render ring-buffer entries. |
| bridge/templates/safety_recent.html | New partial to display recent content-filter hits in the dashboard card body. |
| bridge/templates/dashboard.html | Adds a new Safety card that polls /ui/safety/recent via htmx. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+895
to
+899
| ts = hit.get("ts") or 0 | ||
| try: | ||
| time_str = datetime.fromtimestamp(ts).astimezone().strftime("%H:%M:%S") | ||
| except Exception: | ||
| time_str = "?" |
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.
#72 — content-filter recent-hits ring
content_filter()(bridge/text.py) already evaluates the severity tierand the matched term at every hit. This keeps the last 20 hits in an
in-memory ring and surfaces them at
/ui/safety/recent._cf_recentdeque (maxlen 20) +recent_content_filter_hits()getter;content_filter()appends(ts, tier, rule, 8-char prefix)per hit. In-memory only — neverwritten to disk, and the matched term recorded is no more exposed than
the
content-filter-hitlog linecontent_filter()already emits.GET /ui/safety/recent.dashboard.html.Completes the dashboard quick-wins batch — #69 / #64 / #74 landed via #87.
Verified with
py_compileand Jinja tag-balance; the bridge test suiteneeds the RPi.
Closes #72.
🤖 Generated with Claude Code