- Defer polling_service.start() and FilterEngine init to a one-shot
before_request hook so importing app.py no longer spawns a scheduler
thread (also fixes migrate_*.py import side effects).
- Fix migrate_bookmarks.py: init_db returns None, so use the shared
`db` instead of assigning its None return.
- Enforce MIN_PASSWORD_LENGTH (8) in the password-reset route for
consistency with signup (was hardcoded 6).
- post_detail.html: replace undefined moment(...).fromNow() (always
"Recently") with a new timeago Jinja filter + data-timestamp attrs
that also drive the existing JS updater; make nl2br escape-then-Markup
and drop | safe from comment/post content to close the XSS hole.
- filter_pipeline: when AI is disabled but a filterset requires it,
pass posts through with status=FAILED + explicit error instead of
silently degrading to no_filter.
No source-file mojibake found; content-encoding ingest is a Phase 3 concern.
Co-Authored-By: Claude <noreply@anthropic.com>
Implements plugin-based content filtering system with multi-level caching:
Core Components:
- FilterEngine: Main orchestrator for content filtering
- FilterCache: 3-level caching (memory, AI results, filterset results)
- FilterConfig: Configuration loader for filter_config.json & filtersets.json
- FilterResult & AIAnalysisResult: Data models for filter results
Architecture:
- BaseStage: Abstract class for pipeline stages
- BaseFilterPlugin: Abstract class for filter plugins
- Multi-threaded parallel processing support
- Content-hash based AI result caching (cost savings)
- Filterset result caching (fast filterset switching)
Configuration:
- filter_config.json: AI models, caching, parallel workers
- Using only Llama 70B for cost efficiency
- Compatible with existing filtersets.json
Integration:
- apply_filterset() API compatible with user preferences
- process_batch() for batch post processing
- Lazy-loaded stages to avoid import errors when AI disabled
Related to issue #8 (filtering engine implementation)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>