Close the question arm of percolation: when a comment classified as
question is posted on any artifact, the system mints a new
open_question artifact (the artifact_type already used by the open-question
quest b307ad54-a95), seeded with the comment text, attributed to the
comment author, and linked back to the host artifact via a typed
artifact_link. The new open_question then enters the per-field Elo ranking
that quest already owns. Today every question buried in a discussion
disappears; this emitter promotes them to first-class artifacts that compete
for attention.
scidex/senate/question_emitter.py withscan_candidates, extract_question_text, emit_open_question,run_once mirroring the action / refutation emitter shape.
artifact_comments withcomment_type_labels::jsonb @> '["question"]' ANDspawned_open_question_id IS NULL. No consensus required (questionsmigrations/20260428_question_emitter.sql:ALTER TABLE artifact_comments ADD COLUMN IF NOT EXISTS
spawned_open_question_id text + partial index.CREATE TABLE comment_question_emitter_runs ... mirroring*_emitter_runs tables.
extract_question_text(comment_content) -> str | None: returns the? if the comment text contains one;emit_open_question(...): creates an artifact viascidex.atlas.artifact_registry.register_artifact withartifact_type='open_question',title=extracted_question_text,created_by=comment.author_id,metadata={"source_comment_id":..., "host_artifact_id":...,
"field": host_artifact.metadata.get("field")}.field so the per-field Elo fromscidex/agora/open_question_tournament.py (ENTITY_TYPE='open_question')artifact_provenance row withaction_kind='spawn_proposal' (the closest existing kind in the'spawn_proposal').artifact_link with link_type='derives_from',source_artifact_id=open_question_id,target_artifact_id=host_artifact_id, lifecycle='confirmed'.
spawned_open_question_id.POST /api/senate/question_emitter/run andGET /api/senate/question_emitter/status.
Atlas/feat] Wiki TODO/Open-Question section parser is the patternq-perc-comment-classifier-v1 — supplies comment_type_labels.b2d85e76 per the wikiq-perc-comment-trace-ui — surfaces "your question is now tracked openorchestra/task/4320d55a-question-emitter-question-comments-spawnorigin/main (e9ab5b9aa).action_emitter.py, refutation_emitter.py, andopen_question_miner_wiki.py. Used register_artifact + create_link fromscidex.atlas.artifact_registry (same pattern as wiki miner). Reusedspawn_proposal for action_kind (already in CHECK constraint, avoids migration).derives_from for link_type (already allowed per chk_link_type).migrations/20260428_question_emitter.sql:ALTER TABLE artifact_comments ADD COLUMN IF NOT EXISTS spawned_open_question_id textidx_ac_spawned_open_question_id.CREATE TABLE comment_question_emitter_runs (...) + index.scidex/senate/question_emitter.py: implemented all four functionsscan_candidates, extract_question_text, emit_open_question, run_once) plusget_audit_stats and CLI. Key design decisions:extract_question_text: returns first ?-ending sentence, else first 280 chars.title_hash: SHA1(normalized_lower) → 12-char hex, used for field-scoped dedupemit_open_question: checks dedup via _find_existing_by_hash before creating.host_artifact_metadata.field_tag or metadata.field orapi_routes/senate.py:POST /api/senate/question_emitter/run — delegates to _qe.run_onceGET /api/senate/question_emitter/status — delegates to _qe.get_audit_statstests/test_question_emitter.py: 12 tests covering:extract_question_text: first ? sentence, no ?, long text, empty inputtitle_hash: case insensitivity, whitespace, 12-char hex formatemit_open_question: dry_run flag, empty content errorscan_candidates: column shape via mock DBrun_once(dry_run=True) against live DB: 1 candidate found.