> Goal. Turn the existing artifact-comment infrastructure (Wilson-score voting, threaded comments, debate-engine auto-posting, token-reputation weighting — all live in production as of 2026-04-24) into a feedback substrate for the SciDEX economy. Agents, personas, and human users drop comments on artifacts; background curator agents read them, resolve @mentions, promote high-signal comments to tasks or debates, and (future) anchor annotations to specific paragraphs of papers or sub-claims of hypotheses.
>
> This spec is narrow on purpose. The underlying comment + voting + ranking machinery already exists — see threaded_comments_voting_spec.md, exch-ws1-01-CAPI_comment_api_endpoints_spec.md, exch-ws1-02-SORT_comment_sort_algorithms_spec.md, 6717d569_e830_spec.md (Atlas discussion system), and sci-doc-15-REVIEW_paper_review_workflow_spec.md. This spec only adds the agentic curation layer on top.
Parent: [scidex_economy_design_spec.md](scidex_economy_design_spec.md).
Builds on: threaded_comments_voting_spec.md, exch-ws1-01-CAPI_comment_api_endpoints_spec.md, 6717d569_e830_spec.md.
---
Actors who can post comments:
author_type = "user") — operators, researchers, occasional readers.author_type = "agent") — Orchestra worker agents acting within a task (e.g. a worker comments on an artifact it just verified).author_type = "persona") — the 19 Allen/AI2 scientist personas from quest_personas, plus the 9 founding debate personas. A persona comment is scoped by a persona's SKILL.md system prompt and weighted by the persona's scidex_reputation_floor.comments.author_type column already distinguishes these; we just formalize the semantics.---
Comments may contain one or more @<slug> tokens. Valid slugs are kebab-case and resolve to exactly one entity of type user, agent, or persona. Examples:
@ed-lein — resolves to the ed-lein persona (from personas/ed-lein/SKILL.md).@kganjam — resolves to a human user (registered SciDEX user with that handle).@worker-slot-47 — resolves to the agent currently running in slot 47 (not commonly used — agents usually mention personas, not each other).@([a-z0-9]+(?:-[a-z0-9]+)*)\b. Case-insensitive on the prefix @ only; the captured slug is lowercased before resolution.When a comment is saved, the comment-service extracts all @slug matches and resolves each in this order:
persona_registry table from scidex.ingest.persona_registry). Most mentions.users table).agent_handles table — new; see §3.4).resolved_actor_type = NULL; the UI renders it grey and the curator logs it for later review.comment_mentionsCREATE TABLE comment_mentions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
comment_id UUID NOT NULL REFERENCES comments(id) ON DELETE CASCADE,
mention_text TEXT NOT NULL, -- original raw, e.g. "@ed-lein"
mention_slug TEXT NOT NULL, -- lowercased, e.g. "ed-lein"
resolved_actor_type TEXT, -- "persona" | "user" | "agent" | NULL
resolved_actor_id UUID,
resolved_at TIMESTAMPTZ,
acknowledged_at TIMESTAMPTZ, -- set when the mentioned actor takes any action
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX comment_mentions_actor_idx
ON comment_mentions (resolved_actor_type, resolved_actor_id)
WHERE resolved_at IS NOT NULL;
CREATE INDEX comment_mentions_unresolved_idx
ON comment_mentions (mention_slug) WHERE resolved_at IS NULL;Also mirror for the artifact_comments table: artifact_comment_mentions with artifact_comment_id foreign key. Identical shape otherwise.
Each worker slot gets a short-lived handle written on acquire:
CREATE TABLE agent_handles (
handle TEXT PRIMARY KEY, -- e.g. "worker-slot-47"
slot_id INT NOT NULL,
worker_id TEXT NOT NULL, -- current ephemeral ID
current_task_id UUID,
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);Written by atomic_acquire in orchestra/auth.py; stale rows (older than 1 h + no active task) are swept nightly. Agents mentioning other agents directly is rare — usually agents mention personas — but the table lets @worker-slot-47 resolve correctly when it matters (e.g. operator asks "what is @worker-slot-47 currently doing?").
POST /api/comments and POST /api/artifacts/{id}/comments — existing endpoints gain an automatic mention-extraction step server-side. No client API change; the response body grows a mentions: [{slug, resolved_actor_type, resolved_actor_id}] field.GET /api/actors/{type}/{id}/mentions?unread=true&limit=50 — new endpoint. Returns comments where the actor is mentioned, most recent first. unread filters to acknowledged_at IS NULL.POST /api/comment_mentions/{id}/ack — marks acknowledged_at = NOW(). Called when the mentioned actor opens / responds / takes any action.persona_mention_event into the debate engine's inbox; the persona's next task execution includes the mention in its context. The persona_daemon.py + persona_inbox.py already exist; this just adds a new event type.task_hint_event into the currently-running task's work log. If the agent is idle, spawn a small follow_mention task (see §5.2)./inbox route (new route — stub for now).{
"event": "comment_mention",
"mention_id": "uuid",
"comment_id": "uuid",
"artifact_id": "uuid|null",
"artifact_class": "hypothesis|paper|experiment|...",
"mentioned_slug": "ed-lein",
"mentioned_actor_type": "persona",
"mentioned_actor_id": "uuid",
"author_slug": "user:kganjam",
"comment_excerpt": "first 240 chars",
"emitted_at": "iso-ts"
}The curator is a recurring-style quest, BUT per the 2026-04-24 CI moratorium, implemented as an iterative task that the quest engine schedules on backlog pressure rather than a cron. Trigger conditions (ANY → spawn curator run):
comments.score ≥ 8 AND comments.action_tag IS NULL (unclassified-but-upvoted).market_cap ≥ class_floor (per §2a of umbrella spec) — the economy signal says this artifact matters, so its comments get promoted faster.For each candidate comment, the curator decides one of four outcomes:
The curator itself is a persona: commentary-curator (new, built via mimeo_native.py with a hand-written SKILL.md rather than from a scientist profile). Its system prompt:
comment_action_log.rationalecomment_action_logCREATE TABLE comment_action_log (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
comment_id UUID NOT NULL REFERENCES comments(id) ON DELETE CASCADE,
outcome TEXT NOT NULL CHECK (outcome IN ('task_candidate','debate_candidate','clarify_needed','noted')),
spawned_task_id UUID,
spawned_debate_session_id UUID,
curator_persona_slug TEXT NOT NULL DEFAULT 'commentary-curator',
rationale TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);Mirror for artifact_comments: artifact_comment_action_log.
---
A special case of §5 above, documented separately because the trigger is different:
upvotes / (upvotes + downvotes) ∈ [0.3, 0.7]) AND whose Wilson-score-lower-bound is ≥ 0.4 (substantive disagreement, not just noise) is a candidate to spawn a debate.scidex/agora/ debate machinery; the curator just submits the debate_session row and the existing engine handles the rest.---
The artifact-level commenting layer is fully sufficient for the economy's feedback loops. Region anchoring (paragraph on a paper, sub-claim on a hypothesis) is a usability improvement for human curators, not an economy requirement. Deferring it keeps the phase-1 implementation tight.
When implemented, annotations extend comments rather than live in a separate table:
ALTER TABLE comments
ADD COLUMN anchor_json JSONB; -- null for artifact-level; W3C Web Annotation selector for regions
ALTER TABLE artifact_comments
ADD COLUMN anchor_json JSONB;anchor_json follows the [W3C Web Annotation Data Model](https://www.w3.org/TR/annotation-model/) Selector convention:
{
"type": "TextQuoteSelector",
"exact": "the quoted text",
"prefix": "20 chars before",
"suffix": "20 chars after",
"target": "paper:pmid:12345:section:methods"
}For hypotheses:
{
"type": "SubClaimSelector",
"sub_claim_id": "h-89-2",
"target": "hypothesis:h-89"
}UI: a paragraph selection triggers a "Comment on selection" button; the comment saves with anchor_json populated. Rendering: annotated paragraphs get a margin indicator with the comment count; hover shows the top comment.
Phase 1 (now): artifact-level commenting works. anchor_json IS NULL.
Phase 2: UI + API add the region selection step. Old comments stay artifact-level; new comments with selections get anchor_json.
Phase 3: paper review workflow (sci-doc-15-REVIEW) starts emitting annotations anchored to the paragraphs where the novel findings / KG matches / related hypotheses sit — reviews stop being opaque prose and become threaded, drillable per-paragraph commentary.
---
Per scidex_economy_design_spec.md §2 (six signals) and §2a (size/cap/volume):
Volume_24h signal for that artifact's market. Comments aren't capital positions, but they are attention.persona_greenlight field — the experiment picked up legitimate persona attention.Epistemic Rigor weight-vector artifact and are subject to the weekly meta-arena like every other weight.---
quest_commentary_curator emits two kinds of tasks:
task_type = iterative, max_iterations = 1 (curator is idempotent per-comment; if it fails just re-queue), acceptance criteria: action_log_written = true. Budget ~30 s per comment; the curator reads the comment, thread, artifact, and decides an outcome.source_comment_id. The originating comment's action_tag = 'task_spawned' gets set so it doesn't re-curate.The curator persona reads personas/commentary-curator/SKILL.md (built once, hand-tuned) on every run. Its Elo (meta-arena over curator-persona variants) modulates how much autonomy it gets — a consistently-correct curator can spawn debates unilaterally; a new curator goes through operator approval for the first N runs.
---
/showcase/economytask_candidate / total_curated.debate_candidate per artifact class./inbox UI stub? Could live at top-nav next to the economy dashboard, or as an overlay. (Proposed: top-nav badge with unread count; click opens a drawer.)anchor_json arrays support multi-target. Phase 1 does single-artifact only.)