Effort: thorough
A hypothesis on SciDEX is not a static document — its statement,
composite_score, mechanism prose, supporting PMIDs, debate
verdicts, market price, and Elo rank all change over weeks and
months. Today this history is invisible: /hypothesis/{id}
renders only the current state. A researcher cannot answer
"how did this hypothesis evolve?" or "when did it lose support?"
Build a per-hypothesis History Viewer: a timeline of every
change to the hypothesis with attribution (which agent or human
made the change), the diff, and the trigger event (debate,
market settlement, paper ingest, manual edit). The timeline is
the audit trail and the story arc.
hypothesis_history_events with (id, hyp_id,(hyp_id, occurred_at DESC).
migrations/<n>_hypothesis_history_backfill.py:audit_chain rows referencing the hypothesis,debate_sessions linked via hypothesis_id,price_history for the linked market,composite_score_revisions (search the codebase for thescore_history JSONB onhypothesis_papers insert timestampspaper_added. Goal: ≥10 events per top-50 hypothesis.
scidex/atlas/hypothesis_history.py:record_event(hyp_id, event_kind, actor_id, actor_kind,
before, after, trigger_artifact_id, narrative_md) — theget_timeline(hyp_id, limit=200, since: datetime = None)
-> list[dict] — query helper.compose_history_narrative(hyp_id) -> str — LLM-record_event to events emitted byscidex/agora/synthesis_engine.py (verdict update),scidex/exchange/market_dynamics.py (price settlement),scidex/atlas/citation_extraction.py (new PMID added)./hypothesis/{id} titled "History" renderingGET /api/hypothesis/{id}/history JSON endpoint withsince query param.
since query param indexed(hyp_id, occurred_at DESC) partial.
event_dedup_key UNIQUE column to allow re-running).
scidex/core/event_bus.py) — subscribe handlers, don'tbrief_writer.py; prompt: "Given this list of events,scidex.core.event_bus — subscriber substrate.scidex.exchange.market_dynamics — price-settle events.scidex.agora.synthesis_engine — verdict events.q-time-field-time-series (sibling) — uses the same eventCompleted all acceptance criteria:
hypothesis_history_events with (id, hyp_id, occurred_at,(hyp_id, occurred_at DESC), event_kind, occurred_at DESC.migrations/138_hypothesis_history_events.py.migrations/139_hypothesis_history_backfill.py reconstructs eventshypothesis_score_history, price_history, hypothesis_papers,debate_sessions/hypothesis_debates, audit_chain, and hypothesis creationscidex/atlas/hypothesis_history.py with:record_event(...) — idempotent forward write via ON CONFLICT DO NOTHING onevent_dedup_key; invalidates narrative cache on insert.get_timeline(hyp_id, limit, since, before_cursor) — cursor-based pagination,poll_event_bus() before querying.compose_history_narrative(hyp_id) — LLM-composed story arc cached in anarrative_cache event row; regenerated on cache invalidation.poll_event_bus(hyp_id=None) — consumes relevant events fromscidex.core.event_bus for hypothesis_scored, debate_round_completed,analysis_completed, and 3 new event types.hypothesis_score_updated, hypothesis_paper_added,hypothesis_verdict_updated to EVENT_TYPES in scidex/core/event_bus.py.get_events(unconsumed_by='hypothesis_history'))./hypothesis/{id} page: tab button with event countGET /api/hypothesis/{id}/history with limit, since, before_cursor{events, count, next_cursor, narrative}.tests/atlas/test_hypothesis_history.py: 11 tests covering