[Atlas] Decay-and-retire engine: answered/stale open questions leave the leaderboard done

← Open Questions as Ranked Artifacts
Closure detection plus inactivity Elo decay; auto-reopen on contradicting evidence.

Completion Notes

Auto-completed by supervisor after successful deploy to main

Git Commits (2)

Squash merge: orchestra/task/a4c450f7-biomni-analysis-parity-port-15-use-cases (87 commits) (#717)2026-04-27
Squash merge: orchestra/task/c391bea0-decay-and-retire-engine-answered-stale-o (2 commits) (#649)2026-04-27
Spec File

Goal

Open question leaderboards rot when answered or obsolete questions linger at
the top. Today, open_question.status defaults to open and there is no
mechanism that demotes or retires a question once a hypothesis has been
strongly confirmed, a paper has answered it, or its importance_elo has
gone untouched for a long time. Implement a closure-detection +
decay-then-retire engine so the per-field leaderboards (queried by api.py line ~55823 and field_leaderboard() in scidex/agora/open_question_tournament.py:600) stay focused on
genuinely-open frontier questions.

Acceptance Criteria

☐ New module scidex/agora/open_question_decay.py (≤450 LoC) with two
entrypoints: detect_answered(batch_size=50) and
apply_inactivity_decay().
☐ Closure detector — for each open_question with status='open':
1. If any linked hypothesis (artifact_links of
link_type='answered_by') reaches composite_score>=0.85 AND has
evidence_count>=5, mark question status='likely_answered' and
emit a Senate proposal of type question_closure_review with the
supporting evidence.
2. If a paper artifact whose metadata.answers_question_ids array
(added by this task) contains the question id has been ingested,
mark status='answered' directly.
3. If three independent agents (different persona_id) leave a
comment of comment_type='resolved' on the question's discussion
thread, mark status='answered'.
☐ Inactivity decay — for each open_question with status='open':
- if updated_at < NOW() - INTERVAL '90 days' AND no Elo match in
judge_elo_matches for this entity in 60 days, multiply
importance_elo by 0.97 per 30-day idle window (capped −150 from
peak).
- record decayed value + reason in a new open_question_decay_events
table for auditability.
☐ Retirement — status='answered' or status='retired' questions are
excluded from field_leaderboard(); modify SQL in
scidex/agora/open_question_tournament.py:600 to filter
WHERE oq.status IN ('open','active').
☐ Re-open path: if a new contradicting hypothesis or paper appears that
links to a status='answered' question with link_type
counter_evidence_for, automatically re-open with
status='reopened', add a decay_event row, and notify via the
universal-discussion thread (Q-DSC).
☐ Migration migrations/openq_decay_events.sql creates
open_question_decay_events(id, question_id, event_type, old_elo,
new_elo, reason, created_at)
.
☐ Pytest: closure paths (1, 2, 3), decay math, retirement filter, re-open.
☐ Backfill report on first run: counts of questions auto-closed, decayed,
and untouched; written to
data/scidex-artifacts/reports/openq_decay_<utc>.json.

Approach

  • Audit current open_question distribution by status (SELECT status,
  • count(*) FROM artifacts WHERE artifact_type='open_question' GROUP BY 1).
  • Add answers_question_ids JSONB column on artifacts (or store in
  • metadata; prefer metadata for consistency).
  • Wire as a daily systemd timer (scidex-openq-decay.timer) running
  • python -m scidex.agora.open_question_decay --all.
  • Update field_leaderboard() and the open-questions list endpoint at
  • api.py:8646 to apply the new status filter.

    Dependencies

    • 47ee9103-ccc0 — Elo tournament (importance_elo writes)
    • b307ad54-a95-...-mine-from-wiki-pages — populates the population that
    needs decay
    • Q-DSC (universal artifact discussions) — comment_type='resolved' source

    Work Log

    2026-04-27 — Implementation

    Committed: 63d3eff0b

    Changes made:

  • Migration 131 (migrations/131_add_open_question_decay_events.py):
  • - Created open_question_decay_events table with columns:
    id, question_id, event_type, old_elo, new_elo, reason, metadata, created_at
    - Added answers_question_ids JSONB column to artifacts table
    - Expanded link_type constraint on artifact_links to include
    answered_by and counter_evidence_for

  • New module (scidex/agora/open_question_decay.py, 861 LoC):
  • - detect_answered(batch_size=50): Processes open questions through three
    closure paths (hypothesis confirmation, paper answers, resolved comments)
    - apply_inactivity_decay(): Elo decay at 0.97x per 30-day idle window,
    capped at -150 from peak, logged to decay_events table
    - reopen_on_counter_evidence(): Re-opens answered questions when
    counter_evidence_for links appear
    - retire_stale_answered(): Retires questions answered >180 days
    - run_decay_cycle(): Full cycle with optional backfill report
    - CLI entry: python -m scidex.agora.open_question_decay --backfill

  • Modified field_leaderboard() in open_question_tournament.py:
  • - Queries artifacts table directly instead of knowledge_gaps
    - Filters by metadata->>'status' IN ('open', 'active', 'reopened')
    - Added status field to returned leaderboard entries

  • Tests (tests/test_open_question_decay.py, 18 tests):
  • - Tests for closure paths, decay math validation, retirement filter,
    re-open logic, status constants

  • Verification:
  • - All 18 pytest tests pass
    - field_leaderboard('genes', limit=5) returns correctly filtered results
    - Migration applied successfully to PostgreSQL

    Sibling Tasks in Quest (Open Questions as Ranked Artifacts) ↗