[Atlas] Per-hypothesis history viewer with full attribution timeline open

← Atlas
Per-hypothesis event timeline with backfill from audit_chain, live event-bus subscribers, LLM-composed story-arc narrative.

Completion Notes

Restore scidex/agora/cross_disease_analogy.py (do not delete in this commit) Restore migrations/cross_vertical_analogy.py Restore deploy/scidex-cross-disease-analogy.service and deploy/scidex-cross-disease-analogy.timer Restore scripts/run_cross_disease_analogy.py and scidex/agora/prompts/analogy_v1.md Restore docs/planning/specs/q-vert-cross-disease-analogy-engine_spec.md Verify the rebuilt api.py contains BOTH the /analogies endpoint (from 66b240491) and the /senate/disease-priorities endpoint (from e5e7ae715), since the corrupted api.py notes say both must be preserved Keep the legitimate hypothesis-history changes: migrations 138/139, scidex/atlas/hypothesis_history.py, event_bus hookup, tests, and the new spec file Changed files: - .claude/skills/cardio-expert - .claude/skills/cardio-skeptic - .claude/skills/immunology-expert - .claude/skills/immunology-skeptic - .claude/skills/infectious-expert - .claude/skills/infectious-skeptic - .claude/skills/metabolic-expert - .claude/skills/metabolic-skeptic - .claude/skills/oncology-expert - .claude/skills/oncology-skeptic - api.py - api_routes/senate.py - deploy/scidex-cross-disease-analogy.service - deploy/scidex-cross-disease-analogy.timer - docs/planning/specs/q-sand-rate-limit-aware-tools_spec.md - docs/planning/specs/q-time-field-shift-detector_spec.md - docs/planning/specs/q-time-hypothesis-history-viewer_spec.md - docs/planning/specs/q-vert-cross-disease-analogy-engine_spec.md - docs/planning/specs/q-vert-vertical-personas-pack_spec.md - migrations/021_rate_buckets.py - migrations/138_hypothesis_history_events.py - migrations/139_hypothesis_history_backfill.py - migrations/20260427_debate_persona_assignment.sql - migrations/add_field_shift_tables.py - migrations/cross_vertical_analogy.py - personas/cardio-expert/SKILL.md - personas/cardio-skeptic/SKILL.md - personas/immunology-expert/SKILL.md - personas/immunology-skeptic/SKILL.md - personas/infectious-expert/SKILL.md - personas/infectious-skeptic/SKILL.md - personas/metabolic-expert/SKILL.md - personas/metabolic-skeptic/SKILL.md - personas/oncology-expert/SKILL.md - personas/oncology-skeptic/SKILL.md - scidex/agora/cross_disease_analogy.py - scidex/agora/prompts/analogy_v1.md - scidex/agora/vertical_persona_router.py - scidex/atlas/hypothesis_history.py - scidex/core/event_bus.py Diff stat: .claude/skills/cardio-expert | 1 - .claude/skills/cardio-skeptic | 1 - .claude/skills/immunology-expert | 1 - .claude/skills/immunology-skeptic | 1 - .claude/skills/infectious-expert | 1 - .claude/skills/infectious-skeptic | 1 - .claude/skills/metabolic-expert | 1 - .claude/skills/metabolic-skeptic | 1 - .claude/skills/oncology-expert | 1 - .claude/skills/oncology-skeptic | 1 - api.py

Last Error

Review gate REJECT attempt 1/10: Diff deletes the cross-disease analogy engine (7 files, ~1126 lines including 861 lines of scidex/agora/cross_disease_analogy.py, its migration, systemd service+timer, prompt template, runner script, and spec) that was merged 2 commits ago in 66b240491 — completely unrelated to the hypothesis-history-viewer task and reverts shipped work with an active timer.

Git Commits (1)

[Atlas] Per-hypothesis history viewer with full attribution timeline [task:3cca8708-367b-4cf8-997b-ff9f8142141a]2026-04-27
Spec File

Effort: thorough

Goal

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.

Acceptance Criteria

☐ New table hypothesis_history_events with (id, hyp_id,
occurred_at, event_kind, actor_id, actor_kind: Literal[
'agent', 'human', 'system'], before JSONB, after JSONB,
trigger_artifact_id, narrative_md)
and an index on
(hyp_id, occurred_at DESC).
☐ Backfill migration migrations/<n>_hypothesis_history_backfill.py:
reconstructs history events from existing audit sources —
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 the
table; if absent, reconstruct from score_history JSONB on
the hypothesis row), hypothesis_papers insert timestamps
paper_added. Goal: ≥10 events per top-50 hypothesis.
☐ New module scidex/atlas/hypothesis_history.py:
- record_event(hyp_id, event_kind, actor_id, actor_kind,
before, after, trigger_artifact_id, narrative_md)
— the
forward write path; called by the live event-bus
listeners.
- get_timeline(hyp_id, limit=200, since: datetime = None)
-> list[dict]
— query helper.
- compose_history_narrative(hyp_id) -> str — LLM-
composed 200-word "story so far" summarizing the
timeline; cached, regenerated on new event.
☐ Live wiring: subscribe record_event to events emitted by
scidex/agora/synthesis_engine.py (verdict update),
scidex/exchange/market_dynamics.py (price settlement),
scidex/atlas/citation_extraction.py (new PMID added).
Any composite_score recompute also writes an event.
☐ UI: new tab on /hypothesis/{id} titled "History" rendering
a vertical timeline with one event per row showing
timestamp, actor avatar, event kind icon, 1-line summary,
expandable diff. Header of the History tab shows the
LLM-composed "Story so far" narrative.
GET /api/hypothesis/{id}/history JSON endpoint with
pagination + since query param.
☐ Performance: timeline render uses cursor pagination (no
LIMIT/OFFSET large scans); since query param indexed
via the (hyp_id, occurred_at DESC) partial.
☐ Pytest: seed a hypothesis with 5 backfilled events;
assert timeline returns them ordered correctly; record a
new event via the live API; assert the narrative cache
invalidates.

Approach

  • Backfill is a one-time job — keep it idempotent (use an
  • event_dedup_key UNIQUE column to allow re-running).
  • The forward path uses the existing event_bus
  • (scidex/core/event_bus.py) — subscribe handlers, don't
    couple writers.
  • Diff rendering is JSON-diff in the UI (vanilla, no
  • library) — the API returns before/after JSONB and the
    client computes the diff display.
  • Narrative LLM call uses the same wrapper as
  • brief_writer.py; prompt: "Given this list of events,
    write a 200-word story arc explaining how this hypothesis
    evolved."
  • Timeline is also exposed as an embedded mini-chart on the
  • hypothesis hero showing composite_score over time.

    Dependencies

    • 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 event
    ledger as a feed.

    Work Log

    Payload JSON
    {
      "_gate_retry_count": 1,
      "_gate_last_decision": "REJECT",
      "_gate_last_reason": "Diff deletes the cross-disease analogy engine (7 files, ~1126 lines including 861 lines of scidex/agora/cross_disease_analogy.py, its migration, systemd service+timer, prompt template, runner script, and spec) that was merged 2 commits ago in 66b240491 \u2014 completely unrelated to the hypothesis-history-viewer task and reverts shipped work with an active timer.",
      "_gate_judge_used": "max_outlook1:claude-auto",
      "_gate_last_instructions": "Restore scidex/agora/cross_disease_analogy.py (do not delete in this commit)\nRestore migrations/cross_vertical_analogy.py\nRestore deploy/scidex-cross-disease-analogy.service and deploy/scidex-cross-disease-analogy.timer\nRestore scripts/run_cross_disease_analogy.py and scidex/agora/prompts/analogy_v1.md\nRestore docs/planning/specs/q-vert-cross-disease-analogy-engine_spec.md\nVerify the rebuilt api.py contains BOTH the /analogies endpoint (from 66b240491) and the /senate/disease-priorities endpoint (from e5e7ae715), since the corrupted api.py notes say both must be preserved\nKeep the legitimate hypothesis-history changes: migrations 138/139, scidex/atlas/hypothesis_history.py, event_bus hookup, tests, and the new spec file",
      "_gate_branch": "orchestra/task/3cca8708-per-hypothesis-history-viewer-with-full",
      "_gate_changed_files": [
        ".claude/skills/cardio-expert",
        ".claude/skills/cardio-skeptic",
        ".claude/skills/immunology-expert",
        ".claude/skills/immunology-skeptic",
        ".claude/skills/infectious-expert",
        ".claude/skills/infectious-skeptic",
        ".claude/skills/metabolic-expert",
        ".claude/skills/metabolic-skeptic",
        ".claude/skills/oncology-expert",
        ".claude/skills/oncology-skeptic",
        "api.py",
        "api_routes/senate.py",
        "deploy/scidex-cross-disease-analogy.service",
        "deploy/scidex-cross-disease-analogy.timer",
        "docs/planning/specs/q-sand-rate-limit-aware-tools_spec.md",
        "docs/planning/specs/q-time-field-shift-detector_spec.md",
        "docs/planning/specs/q-time-hypothesis-history-viewer_spec.md",
        "docs/planning/specs/q-vert-cross-disease-analogy-engine_spec.md",
        "docs/planning/specs/q-vert-vertical-personas-pack_spec.md",
        "migrations/021_rate_buckets.py",
        "migrations/138_hypothesis_history_events.py",
        "migrations/139_hypothesis_history_backfill.py",
        "migrations/20260427_debate_persona_assignment.sql",
        "migrations/add_field_shift_tables.py",
        "migrations/cross_vertical_analogy.py",
        "personas/cardio-expert/SKILL.md",
        "personas/cardio-skeptic/SKILL.md",
        "personas/immunology-expert/SKILL.md",
        "personas/immunology-skeptic/SKILL.md",
        "personas/infectious-expert/SKILL.md",
        "personas/infectious-skeptic/SKILL.md",
        "personas/metabolic-expert/SKILL.md",
        "personas/metabolic-skeptic/SKILL.md",
        "personas/oncology-expert/SKILL.md",
        "personas/oncology-skeptic/SKILL.md",
        "scidex/agora/cross_disease_analogy.py",
        "scidex/agora/prompts/analogy_v1.md",
        "scidex/agora/vertical_persona_router.py",
        "scidex/atlas/hypothesis_history.py",
        "scidex/core/event_bus.py",
        "scidex/forge/rate_limiter.py",
        "scidex/forge/rate_limits.yaml",
        "scidex/forge/tools.py",
        "scidex/senate/field_shift_detector.py",
        "scidex/senate/scheduled_tasks.py",
        "scripts/run_cross_disease_analogy.py",
        "tests/atlas/test_hypothesis_history.py",
        "tests/test_field_shift_detector.py",
        "tests/test_vertical_persona_routing.py"
      ],
      "_gate_diff_stat": ".claude/skills/cardio-expert                       |    1 -\n .claude/skills/cardio-skeptic                      |    1 -\n .claude/skills/immunology-expert                   |    1 -\n .claude/skills/immunology-skeptic                  |    1 -\n .claude/skills/infectious-expert                   |    1 -\n .claude/skills/infectious-skeptic                  |    1 -\n .claude/skills/metabolic-expert                    |    1 -\n .claude/skills/metabolic-skeptic                   |    1 -\n .claude/skills/oncology-expert                     |    1 -\n .claude/skills/oncology-skeptic                    |    1 -\n api.py                                             | 1718 ++++++++++++++++++--\n api_routes/senate.py                               |   42 -\n deploy/scidex-cross-disease-analogy.service        |   14 -\n deploy/scidex-cross-disease-analogy.timer          |   10 -\n .../specs/q-sand-rate-limit-aware-tools_spec.md    |   32 -\n .../specs/q-time-field-shift-detector_spec.md      |   16 -\n .../specs/q-time-hypothesis-history-viewer_spec.md |   43 +\n .../q-vert-cross-disease-analogy-engine_spec.md    |   49 -\n .../specs/q-vert-vertical-personas-pack_spec.md    |   48 -\n migrations/021_rate_buckets.py                     |   35 -\n migrations/138_hypothesis_history_events.py        |   45 +\n migrations/139_hypothesis_history_backfill.py      |  330 ++++\n migrations/20260427_debate_persona_assignment.sql  |   37 -\n migrations/add_field_shift_tables.py               |  129 --\n migrations/cross_vertical_analogy.py               |   79 -\n personas/cardio-expert/SKILL.md                    |   76 -\n personas/cardio-skeptic/SKILL.md                   |   74 -\n personas/immunology-expert/SKILL.md                |   85 -\n personas/immunology-skeptic/SKILL.md               |   90 -\n personas/infectious-expert/SKILL.md                |   77 -\n personas/infectious-skeptic/SKILL.md               |   86 -\n personas/metabolic-expert/SKILL.md                 |   83 -\n personas/metabolic-skepti",
      "_gate_history": [
        {
          "ts": "2026-04-27 17:14:33",
          "decision": "REJECT",
          "reason": "Diff deletes the cross-disease analogy engine (7 files, ~1126 lines including 861 lines of scidex/agora/cross_disease_analogy.py, its migration, systemd service+timer, prompt template, runner script, and spec) that was merged 2 commits ago in 66b240491 \u2014 completely unrelated to the hypothesis-history-viewer task and reverts shipped work with an active timer.",
          "instructions": "Restore scidex/agora/cross_disease_analogy.py (do not delete in this commit)\nRestore migrations/cross_vertical_analogy.py\nRestore deploy/scidex-cross-disease-analogy.service and deploy/scidex-cross-disease-analogy.timer\nRestore scripts/run_cross_disease_analogy.py and scidex/agora/prompts/analogy_v1.md\nRestore docs/planning/specs/q-vert-cross-disease-analogy-engine_spec.md\nVerify the rebuilt api.py contains BOTH the /analogies endpoint (from 66b240491) and the /senate/disease-priorities endpoint (from e5e7ae715), since the corrupted api.py notes say both must be preserved\nKeep the legitimate hypothesis-history changes: migrations 138/139, scidex/atlas/hypothesis_history.py, event_bus hookup, tests, and the new spec file",
          "judge_used": "max_outlook1:claude-auto",
          "actor": "opus-4.7:40",
          "retry_count": 1
        }
      ]
    }

    Sibling Tasks in Quest (Atlas) ↗