feat(ml): schedule presentation auto-hide sweep + retention (#141 step 6)
CI / lint (push) Successful in 2s
CI / frontend-build (push) Successful in 18s
CI / backend-lint-and-test (push) Successful in 27s
CI / integration (push) Successful in 3m41s

scheduled_presentation_auto_apply (daily beat) runs presentation_auto_apply_sweep
— idempotent, so an interrupted run just re-runs next cycle (that's the recovery),
wall-clock bounded by soft/hard task time limits. prune_presentation_reviews
(daily beat) drops RESOLVED review flags older than 30 days (rule 89 retention).
Tests run both tasks via a monkeypatched session factory. Milestone 141 complete:
the presentation-chrome auto-hide + conflict-flagged review is now live end-to-end.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CDgx8bQS5YrGRK76v8HUnM
This commit is contained in:
2026-07-06 23:46:09 -04:00
parent 9726d6f4b5
commit 2bcaa20b22
3 changed files with 147 additions and 0 deletions
+9
View File
@@ -152,6 +152,15 @@ def make_celery() -> Celery:
"schedule": 86400.0, # soft auto-apply: drop auto-tags now below
# their threshold (m139); no-op unless the auto-apply switch is on
},
"presentation-auto-apply-daily": {
"task": "backend.app.tasks.ml.scheduled_presentation_auto_apply",
"schedule": 86400.0, # auto-hide banner/editor chrome (#141);
# no-op unless presentation_auto_apply_enabled
},
"prune-presentation-reviews-daily": {
"task": "backend.app.tasks.ml.prune_presentation_reviews",
"schedule": 86400.0, # retention: drop resolved review flags >30d
},
"snapshot-head-metrics-daily": {
"task": "backend.app.tasks.maintenance.snapshot_head_metrics",
"schedule": 86400.0,
+40
View File
@@ -594,6 +594,46 @@ def scheduled_ccip_auto_apply() -> str:
return f"applied={applied}"
@celery.task(
name="backend.app.tasks.ml.scheduled_presentation_auto_apply",
soft_time_limit=1800, time_limit=2100,
)
def scheduled_presentation_auto_apply() -> str:
"""Auto-hide presentation chrome (banner / editor screenshot) on a daily
passive sweep (#141). No-op unless presentation_auto_apply_enabled. Idempotent
— already-hidden images are skipped — so an interrupted run simply re-runs next
cycle (that IS the recovery). Wall-clock bounded by the task time limits."""
from ..services.ml.heads import presentation_auto_apply_sweep
SessionLocal = _sync_session_factory()
with SessionLocal() as session:
result = presentation_auto_apply_sweep(session)
return f"applied={result['n_applied']} flagged={result['n_flagged']}"
@celery.task(name="backend.app.tasks.ml.prune_presentation_reviews")
def prune_presentation_reviews() -> str:
"""Retention (rule 89): drop RESOLVED presentation-review flags older than 30
days — the operator has acted on them, so they're just history (#141)."""
from datetime import UTC, datetime, timedelta
from sqlalchemy import delete as sa_delete
from ..models import PresentationReview
cutoff = datetime.now(UTC) - timedelta(days=30)
SessionLocal = _sync_session_factory()
with SessionLocal() as session:
res = session.execute(
sa_delete(PresentationReview).where(
PresentationReview.resolved_at.is_not(None),
PresentationReview.resolved_at < cutoff,
)
)
session.commit()
return f"pruned={res.rowcount}"
@celery.task(
name="backend.app.tasks.ml.scheduled_retract_auto_tags",
soft_time_limit=1800, time_limit=2100,