Phase 0: stop-the-bleeding bugfixes
- 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>
This commit is contained in:
@@ -184,10 +184,16 @@ class FilterEngine:
|
||||
if self.config.is_ai_enabled():
|
||||
self._init_stages()
|
||||
|
||||
# If AI is disabled but filterset requires it, fall back to no_filter
|
||||
# If AI is disabled but the filterset requires it, do NOT silently pass
|
||||
# everything as no_filter. Pass the posts through (so the feed is not
|
||||
# blanked) but mark every result as FAILED with an explicit error so the
|
||||
# degradation is observable, not silent.
|
||||
if not self.config.is_ai_enabled() and filterset_name != 'no_filter':
|
||||
logger.warning(f"AI disabled but '{filterset_name}' requires AI - falling back to 'no_filter'")
|
||||
return self._process_no_filter(posts)
|
||||
logger.warning(
|
||||
f"AI disabled but filterset '{filterset_name}' requires AI - "
|
||||
f"passing posts through unfiltered with FAILED status"
|
||||
)
|
||||
return self._process_ai_disabled(filterset_name, posts)
|
||||
|
||||
# Get pipeline stages for this filterset
|
||||
stage_names = self._get_stages_for_filterset(filterset_name)
|
||||
@@ -218,6 +224,28 @@ class FilterEngine:
|
||||
|
||||
return results
|
||||
|
||||
def _process_ai_disabled(self, filterset_name: str, posts: List[Dict[str, Any]]) -> List[FilterResult]:
|
||||
"""Pass posts through unfiltered when the requested filterset needs AI
|
||||
but AI is disabled. Unlike no_filter, every result is marked FAILED with
|
||||
an explicit error so the degradation is observable rather than silent.
|
||||
"""
|
||||
results = []
|
||||
for post in posts:
|
||||
result = FilterResult(
|
||||
post_uuid=post.get('uuid', ''),
|
||||
passed=True, # do not blank the feed
|
||||
score=0.5, # neutral score
|
||||
categories=[],
|
||||
tags=[],
|
||||
filterset_name=filterset_name,
|
||||
processed_at=datetime.now(),
|
||||
status=ProcessingStatus.FAILED,
|
||||
error=f"AI disabled: filterset '{filterset_name}' requires AI; passed through unfiltered"
|
||||
)
|
||||
results.append(result)
|
||||
|
||||
return results
|
||||
|
||||
def _get_stages_for_filterset(self, filterset_name: str) -> List[str]:
|
||||
"""Get pipeline stages to run for a filterset"""
|
||||
filterset = self.config.get_filterset(filterset_name)
|
||||
|
||||
Reference in New Issue
Block a user