Close the refutation arm of the percolation loop: when a comment classified
as refutation lands on a hypothesis or analysis artifact and the comment
either supplies a counter-citation (PMID / DOI / artifact UUID) or has at
least one other refutation reply agreeing with it, automatically spawn an
artifact-debate (Theorist vs Skeptic vs Expert vs Synthesizer) targeting the
host artifact, with full provenance back to the originating comment.
This is the analog of the existing action_emitter (action-tagged →
Orchestra task) and edit_emitter (proposal-tagged → version bump), but for
the refutation label produced by q-perc-comment-classifier-v1. Without
this emitter a refutation comment is just text; with it the system reroutes
into the debate engine that already exists in scidex/agora/.
scidex/senate/refutation_emitter.py with the same publicscidex/senate/action_emitter.py:scan_candidates(db) -> list[dict], check_consensus(db, comment) -> bool,emit_debate(db, comment, artifact) -> str (returns debate_id),run_once(dry_run=False) -> dict.
artifact_comments rows wherecomment_type_labels::jsonb @> '["refutation"]',parent_comment_id IS NULL, host artifact_type IN
('hypothesis', 'analysis', 'analysis_proposal'), age < 14d,spawned_debate_id IS NULL.
ACTION_EMITTER_CONSENSUS_THRESHOLD=3):\bPMID[: ]\d+\b or DOI 10\.\d{4,9}/\S+ ormigrations/20260427_refutation_emitter.sql:ALTER TABLE artifact_comments ADD COLUMN IF NOT EXISTS
spawned_debate_id text + index on it (WHERE NOT NULL).CREATE TABLE comment_refutation_emitter_runs (...) with the samecomment_action_emitter_runs.
/api/agora/debate/create (find via grep — likelyscidex/agora/debate.py:create_debate), passingtopic = host_artifact.title, position_a = host_artifact_claim,position_b = refutation_comment.content[:500],seed_evidence = extracted_citations.
artifact_provenance row withaction_kind='spawn_debate' (extend the CHECK constraint to includesource_comment_id,target_artifact_id = host_artifact.id, actor_id = "refutation_emitter".'comment','edit','version_bump','spawn_task',
'spawn_proposal','spawn_link','snapshot','gate_decision' (see\d artifact_provenance).
artifact_link withlink_type='refuted_by_debate', source_artifact_id=host,target_artifact_id=debate_artifact_id,lifecycle='confirmed' (auto-confirmed because debate is structuredroot_comment_id never spawns more than one debatespawned_debate_id).
POST /api/senate/refutation_emitter/run andGET /api/senate/refutation_emitter/status mirroring the actiontests/test_refutation_emitter.py: citation regex extractionscidex/senate/action_emitter.py end-to-end and copy its skeleton.def create_debate\|/agora/debate/createscidex/agora and api.py; use it as a function (not HTTP) so thisscidex/senate/citation_extract.py helper that returns a list of{kind, value} dicts; add unit tests.
q-perc-comment-classifier-v1 — supplies comment_type_labels.scidex/agora/ — invoked to create the debate.q-perc-sla-dashboard — measures comment-to-debate latency.q-perc-comment-trace-ui — surfaces "this comment spawned debate X" tob05ac4aa-refutation-emitter-refutation-comments-s was squash-merged togit fetch origin main && git rebase origin/main — branch is now cleane47ecfb20 (PR #610, 2026-04-27 09:24 UTC)git fetch origin main && git rebase origin/main → zero net diff in task files.orchestra-slot.json modified locally (slot reservation, not part of task)e47ecfb20 (PR #610) is the authoritative landing SHAe47ecfb20 (PR #610, squash-merged 2026-04-27 09:24 UTC).git diff origin/main..HEAD returns zero diff for all task files.{
"completion_shas": [
"e47ecfb20"
],
"completion_shas_checked_at": ""
}