Plan Canvas
Bucle de revisión para planes y artefactos visuales: tú escribes el artefacto, el humano lo revisa en el navegador — anotando el elemento exacto al que se refiere, chateando y emitiendo un veredicto de Aprobar plan / Solicitar cambios — mientras te bloqueas en una única llamada CLI que devuelve su retroalimentación como JSON.
Inspirado por lavish-axi; reconstruido nativo de ECC alrededor de la puerta de confirmación /plan, con cero dependencias.
Cuándo Usarlo
- Acabas de escribir un artefacto de plan (
.claude/plans/*.plan.mdde/plan) y necesitas la decisión CONFIRMAR/aprobar — el veredicto del lienzo reemplaza un "sí/proceder" escrito. - El usuario debe señalar qué cambiar: revisando diseños, comparaciones, informes o cualquier artefacto local
.md/.html. - El usuario solicita
/plan-canvas, una revisión visual, o "ábrelo en el navegador".
NO usar para: revisión de código de diffs (/code-review), aplicaciones web en ejecución o URLs remotas. El lienzo solo sirve archivos de artefactos locales.
Cómo Funciona
Invoca el CLI como ecc-plan-canvas — el binario distribuido por el paquete ecc-universal (en el PATH después de una instalación global/plugin; node "$CLAUDE_PLUGIN_ROOT/scripts/plan-canvas.js" también funciona para instalaciones plugin). Ejecútalo desde el proyecto que estás revisando; funciona desde cualquier directorio de trabajo. Gestiona un servidor loopback separado (127.0.0.1:4517) compartido por todas las sesiones, indexado por la ruta del artefacto — sin IDs de sesión que rastrear.
El flujo de trabajo es un bucle simple de CLI más JSON, por lo que es independiente del modelo y del entorno: cualquier agente que pueda ejecutar un comando de shell y leer la salida estándar lo maneja de la misma manera (Claude Code, Codex, Cursor, Gemini, OpenCode, Copilot). Actívalo como tu entorno exponga las habilidades — p. ej. /plan-canvas en Claude Code, $plan-canvas en Codex — o simplemente ejecuta los comandos ecc-plan-canvas directamente.
# 1. Abre el artefacto en el navegador del usuario (regresa inmediatamente)
ecc-plan-canvas open .claude/plans/feature.plan.md
# 2. Bloquéate hasta que el humano responda. Déjalo ejecutándose; vuelve a ejecutar si se interrumpe:
# la retroalimentación en cola nunca se pierde.
ecc-plan-canvas await .claude/plans/feature.plan.md
Mantente a la escucha, o el humano habla con una silla vacía
La retroalimentación solo te llega mientras un await está realmente estacionado en la sesión. Si tu turno termina sin que nada esté escuchando, el mensaje se queda en la cola y, desde el lado del humano del cristal, enviar parece no hacer nada.
Por lo tanto, ejecuta await como una tarea en segundo plano cuando tu entorno lo admita (en Claude Code, una llamada Bash con run_in_background: true). Sale en el momento en que llega la retroalimentación y el entorno te entrega el JSON, lo que mantiene el bucle vivo a través de los turnos en lugar de morir con la llamada en primer plano. Un await en primer plano también funciona, pero solo hasta que el entorno lo limite por tiempo.
Existen dos salvaguardas, y ninguna es una excusa para omitir lo anterior:
ecc-plan-canvas pendinglista la retroalimentación en cola sin oyente. Verifícalo cada vez que no estés seguro de si te perdiste algo.- El hook
stop:plan-canvas-pendingbloquea que tu turno termine mientras la retroalimentación del lienzo no se haya entregado, y te entrega los mensajes. Si estás leyendo la retroalimentación de ese hook, dejaste de escuchar demasiado pronto.
await imprime JSON cuando el humano actúa:
{
"status": "feedback",
"items": [
{ "kind": "annotation", "text": "Divide esto en dos fases",
"anchor": { "selector": "h2:nth-of-type(3)", "tag": "h2", "snippet": "Fase 2: Migración" } },
{ "kind": "verdict", "verdict": "request-changes" }
]
}
kind: "chat"— mensaje de forma libre; responde en el lienzo, no en la terminal.kind: "annotation"— retroalimentación anclada a un elemento (anchor.selector,anchor.snippetmuestran a qué señalaron;anchor.textRange.textcuando resaltaron un pasaje).kind: "verdict"—approvesignifica que el plan está CONFIRMADO: deja de sondear, finaliza la sesión y comienza la implementación.request-changessignifica revisar el artefacto (el lienzo lo recarga en vivo) y continuar el bucle.
3. Responde siempre en el lienzo, luego sigue escuchando. Un comando hace ambas cosas:
ecc-plan-canvas await <archivo> --reply "Divido la Fase 2 como se solicitó. Échale un vistazo."
Cada mensaje humano recibe una respuesta en el lienzo, incluso una línea como "En ello, reescribiendo la tabla de riesgos ahora." El silencio en el panel de chat es indistinguible de un lienzo roto, que es exactamente el fallo que este bucle existe para prevenir. Responde allí, no solo en la terminal.
Mientras trabajas, mantén el chat honesto con el indicador de actividad:
# burbuja animada "el agente está pensando..."; refréscala durante trabajo largo
ecc-plan-canvas typing <archivo> --state thinking
# cambia a "el agente está escribiendo..." justo antes de que llegue una respuesta
ecc-plan-canvas typing <archivo> --state typing
await establece thinking por ti en el momento en que te entrega un lote, y --reply lo limpia. Ambos estados se autodestruyen, por lo que un agente bloqueado se degrada a un "en cola" honesto en lugar de dejar al humano viendo puntos para siempre. Refresca thinking si una revisión toma más de un minuto.
4. Termina cuando la revisión concluya: ecc-plan-canvas end <archivo>.
Diagramas (Mermaid)
Cuando parte del plan sea un flujo, arquitectura, secuencia, máquina de estados, modelo ER o gráfico de dependencias, escríbelo como un bloque delimitado ```mermaid en lugar de arte ASCII o un muro de prosa — el lienzo lo renderiza como un diagrama temático que el humano puede señalar. Recurre a ello cuando una imagen se lea más rápido que un párrafo; omítelo para listas o tablas simples.
```mermaid
flowchart LR
A[Mercado resuelve] --> B{¿Observadores?}
B -->|sí| C[Encolar trabajos] --> D[Trabajador fan-out]
```
Los diagramas se renderizan en el tema oscuro de ECC con la paleta de acentos. Mermaid se carga en el navegador desde un CDN fijo; si no está disponible (sin conexión), el bloque se degrada mostrando su fuente, por lo que la revisión nunca se bloquea. Apunta un mirror local a ECC_PLAN_CANVAS_MERMAID_URL para entornos desconectados.
Reglas
- Los artefactos Markdown se renderizan en la plantilla de plan de ECC (incluyendo bloques Mermaid); los artefactos
.htmlse renderizan tal cual con la capa de anotación inyectada. Para orientación sobre autoría HTML, usa las habilidadesfrontend-design-directionyartifact-design. - Edita el archivo del artefacto para revisar — el lienzo se recarga en vivo al guardar. Nunca vuelvas a ejecutar
openpara refrescar. {"status": "ended", "endedBy": "user"}(osessionEnded: trueen un lote de retroalimentación) significa que el usuario cerró la revisión: deja de sondear, entrega las actualizaciones restantes en el chat y no reabras. Unopensimple en esa sesión es rechazado; pasa--reopensolo cuando el usuario solicite reanudar.- Los activos hermanos (imágenes, CSS) deben estar junto al artefacto y ser referenciados por ruta relativa.
- El servidor es solo loopback y sale después de 30 minutos de inactividad (
ECC_PLAN_CANVAS_IDLE_MS);stoplo apaga explícitamente. El estado reside en~/.claude/plan-canvas/(ECC_PLAN_CANVAS_STATE_DIR).
Ejemplos
Flujo de aprobación de plan — /plan escribe .claude/plans/notifications.plan.md y debe ESPERAR confirmación:
ecc-plan-canvas open .claude/plans/notifications.plan.md
ecc-plan-canvas await .claude/plans/notifications.plan.md
# → {"status":"feedback","items":[{"kind":"verdict","verdict":"approve"}]}
ecc-plan-canvas end .claude/plans/notifications.plan.md
# el plan está confirmado — comienza la implementación
Bucle de revisión — llega retroalimentación, editas el archivo, respondes, sigues escuchando:
# await devolvió anotaciones → edita el .plan.md (el lienzo se recarga en vivo)
ecc-plan-canvas await <archivo> --reply "Rehice la tabla de riesgos."
# → se bloquea de nuevo hasta la siguiente respuesta
Antipatrones
- Sondear con
--timeout-msen un bucle. Existe para pruebas. Deja elawaitsimple ejecutándose en su lugar. - Terminar tu turno sin que ningún
awaitesté escuchando mientras la revisión sigue abierta. Ese es el único fallo que el humano experimenta como "Envié un mensaje y no pasó nada". - Leer la retroalimentación pero responder solo en la terminal. El humano está mirando el lienzo.
- Reabrir después de un fin iniciado por el usuario "solo para mostrar" algo.
- Pegar todo el plan en el chat y abrir un lienzo — elige el lienzo y mantén el resumen de terminal en una línea.
- Analizar el chat del lienzo desde los archivos de estado — todo lo que necesitas llega a través de
await.
Notas de diseño y origen: docs/design/plan-canvas.md.