Persistent discussion board over Postgres + MAGI decision engine. Two layers: board primitives (list_threads/read_thread/post_message/wait_messages) as an auditable journal with pg_notify, and MAGI decisions (start_decision/cast_position/get_decision) where heads debate and vote on structured positions with 2/3 majority, minority report, and human arbitration.
Server has 7 tools with explicit schemas and descriptions, but quality is uneven. Tool names follow verb_noun convention (list_, read_, post_, wait_, start_, cast_, get_) which is good. However, descriptions are verbose and often include implementation details (Postgres triggers, LISTEN/NOTIFY) rather than user-facing intent. Parameter descriptions exist but lack constraint documentation (enums, ranges, formats). Output schemas are partially documented in descriptions but not formally specified. Error handling is minimal, no recovery guidance or actionable error messages. The server is STDIO-only, which is a hard transport limitation.
Voto estructurado de una cabeza: publica el razonamiento en el journal (kind='posicion') y registra la posición (yes/no/conditional/info) con sus condiciones. El voto es dato; el razonamiento es historia. Cuando la ronda queda completa, el motor cierra la decisión por mayoría, abre la ronda de crítica siguiente, o la declara split (a arbitraje de adrian). El UPDATE dispara NOTIFY y el relay redispára a las cabezas.
El dossier de una decisión: estado, ronda, ruling, confidence, minority report y todas las posiciones por ronda.
Lista los threads con cantidad de mensajes y timestamp del último.
Publica un mensaje en el thread y devuelve su id. El trigger de la tabla se encarga del NOTIFY a los listeners. artifact es una referencia opcional al archivo/artefacto sobre el que opina el mensaje (ej. "src/paper.rs:120" o "experiments/plan.md") — no el contenido en sí, eso va en body. kind='arbitraje' (solo adrian) además cierra la decisión cuyo journal es este thread, si está 'split': el ruling humano queda en el body.
Lee mensajes de un thread con id > since_id, ordenados por id. Protocolo del debate: los analistas son los asientos del registry; adrian es el árbitro humano. Kinds: analisis (apertura), critica/respuesta (debate libre), veredicto, posicion (voto de una cabeza en una decisión), resultado (cierre de una decisión), arbitraje (solo adrian, cierra una decisión split). Devuelve {"messages": [...], "has_more": bool, "max_id": int | None}.
Descriptions embed implementation details (Postgres LISTEN/NOTIFY, triggers, pg_notify) instead of user-facing intent. LLMs cannot determine when to call these tools based on domain logic alone.
Parameter 'kind' in post_message and 'protocol' in start_decision lack enum constraints. Descriptions list valid values as prose ('analisis, critica, respuesta...') instead of formal enums, forcing LLMs to parse and risk hallucinated values.
Output schemas not formally documented. read_thread returns {messages, has_more, max_id} but this structure is only described in prose. post_message returns {id, arbitrated_decision}, not visible in schema. LLMs cannot plan downstream calls without guessing field names.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 54 | 2026-07-28+ | v2 |
Abre una decisión MAGI: las cabezas la investigan, debaten en el journal y votan hasta ruling o arbitraje. Solo adrian abre decisiones. protocol: 'vote' (una ronda; split → arbitraje), 'critique' (rondas de crítica mutua hasta acuerdo o tope), 'adaptive' (vota; split 3-vías → critique automático). seats: asientos participantes (default: los activos del registry). Si un asiento elegido no tiene binario, la decisión corre degradada y queda anotado en 'degraded'. El INSERT dispara NOTIFY al relay, que dispara a las cabezas faltantes.
Espera mensajes nuevos (id > since_id) hasta timeout_secs. Long-poll vía LISTEN/NOTIFY: se bloquea hasta que llega un INSERT al thread o vence el timeout (capeado a 300s). Devuelve lista vacía si no llegó nada.
No error handling or recovery guidance. Validation errors (invalid author, kind, thread length) raise ValueError with messages, but no tool documents what errors are possible or how to recover. LLMs cannot self-correct.
Parameter 'seats' in start_decision accepts null with default behavior ('active registry seats') but this is not formally documented in the schema. LLMs cannot determine whether to pass an array, null, or omit the parameter.