¿Qué hace Refactor Scan?
Detecta, nunca escribe. Propone en qué se podría trabajar a continuación y detecta lo que ya se ha resuelto por sí solo desde la última pasada — nunca crea una incidencia, nunca decide un resultado. refactor-design crea incidencias para lo que se elige; refactor-learn actúa sobre lo que esta habilidad detecta.
Proceso
1. Comprobar condiciones previas
- No hay repositorio git → detener la pasada, informarlo, no proponer nada.
- Cinco o más incidencias abiertas con la etiqueta
refactor:candidate→ detener, no proponer nada nuevo; dejar que el trabajo existente se resuelva primero.
2. Reanudar trabajo pendiente primero
Leer el campo Pending candidates de las Notas de Refactorización (skills/continuous-refactoring/references/refactoring-bookkeeping.md). Si nombra una incidencia, una pasada anterior de refactor-design la creó pero fue interrumpida antes de que le siguiera una solicitud de merge. Proponer exactamente esa incidencia y detenerse — terminar el trabajo pendiente tiene prioridad sobre proponer trabajo nuevo.
3. Detectar estado recordado cerrado/fusionado
Obtener el conjunto recordado — docs/agents/issue-tracker.md nombra un rastreador de etiquetas nativo (GitHub, GitLab) → cada incidencia abierta con etiqueta refactor:candidate, resuelta a su pull request vinculada mediante la referencia cruzada nativa del rastreador (el Closes #<n> que refactor-implement paso 5 ya pone en una MR que entrega); una incidencia sin PR vinculada aún no tiene nada que reconciliar en este paso. De lo contrario → cada entrada en el archivo merge-requests.md de las Notas de Refactorización. Para cada una, comprobar el rastreador externo/git: ¿la MR sigue abierta?, ¿la incidencia sigue abierta?
No hay gh/glab (u otra API/token de forja) disponible → recurrir a reconciliación solo con git en lugar de saltar este paso (nunca intentar instalar gh/glab aquí — tratar su ausencia exactamente como el desencadenante de esta alternativa): skills/refactor-scan/references/git-only-reconciliation.md. Sin remoto (git remote -v vacío) → la variante solo local del mismo archivo — un candidato recordado puede haber sido fusionado o rechazado por un humano trabajando puramente en local, según opening-a-merge-request.md sección "No forge/remote available".
-
Fusionado → un hallazgo: entregado.
-
Cerrado sin fusionar → un hallazgo: rechazado — anotar si los comentarios de cierre dan una razón estructural del mantenedor (material fuera de alcance) o no.
-
Todavía abierto, con actividad del revisor (una revisión o comentario) más reciente que el último commit de la rama → un candidato para reanudar, no un hallazgo — nadie ha decidido nada aún, solo necesita otra revisión. API disponible → leer también el estado real de la revisión junto con la marca de tiempo (
gh pr view <n> --json reviews,reviewDecisiono equivalente) — no detenerse en "algo cambió":CHANGES_REQUESTED, sin abordar (sin commit más reciente que la revisión) → sigue siendo candidato para reanudar, pero pasar adelante los propios comentarios/cuerpo de la revisión junto con él, no solo la rama —refactor-implementpaso 1 lee y aborda esa retroalimentación específica, no solo reexamina la rama desde cero.- Un comentario, o una revisión
APPROVEDsin retroalimentación bloqueante → el manejo genérico de candidato para reanudar existente; nada más que adjuntar.
Pasarlo adelante de la misma manera que el paso 2 pasa adelante una entrada de
Pending candidates: directamente arefactor-implement, saltándoserefactor-prioritize/refactor-design(ya tiene un diseño y una MR abierta; solo aplica el bucle de corrección —refactor-implementpaso 1 ya soporta reanudar una rama existente). Sin API (alternativa solo git) → el estado de revisión no está disponible exactamente como ya documentagit-only-reconciliation.md; este refinamiento no aplica allí. -
Todavía abierto, nada cambió → no es un hallazgo.
También comprobar cada entrada en el archivo out-of-scope/<node>.md de las Notas de Refactorización que nombre una condición **Blocked by:** PHP >= X.Y (tooling_tree.py's detect_nodes() informa esto directamente cuando se ejecuta; manualmente, comparar con el require.php/config.platform.php del composer.json actual del objetivo) — si la versión actual de PHP del objetivo ahora la satisface, un hallazgo: este rechazo se revierte. Sin campo Blocked by:, o condición aún no cumplida → nunca un hallazgo.
Entregar cada hallazgo a refactor-learn — esta habilidad solo detecta; nunca marca nada como done/wontfix y nunca escribe en out-of-scope/ de las Notas de Refactorización.
3b. Detectar candidatos etiquetados externamente
API disponible (no la alternativa solo git) → también consultar incidencias abiertas con etiqueta refactor:candidate que no estén ya contempladas: no la entrada de Pending candidates del paso 2, no el conjunto recordado del paso 3 (una incidencia con un pull request vinculado ya está en curso). Lo que queda es un candidato que un humano (u otro proceso) etiquetó directamente, que este bucle nunca ha diseñado ni implementado — en operación normal los propios candidatos del suite siempre están cubiertos por uno de esos dos, así que no se necesita nada extra para distinguir "nuestros" de "de otros". Pasar cada uno adelante como una propuesta, a la misma lista que se unen las propuestas del árbol de herramientas del paso 4 — refactor-prioritize las clasifica junto a todo lo demás según sus factores habituales; compite por sus propios méritos, nunca se salta la cola solo por existir. Sin API (alternativa solo git) → saltar este paso por completo; git solo no puede enumerar incidencias por etiqueta.
4. Proponer nodos del árbol de herramientas
Saltar si el paso 2 ya propuso un candidato pendiente.
Ejecutar python3 skills/refactor-scan/references/tooling_tree.py <target-repo> y leer el campo next del JSON — el conjunto real, actualmente desbloqueado, nodos rechazados (una entrada existente en out-of-scope/<node>.md de las Notas de Refactorización) ya excluidos, tomar tal cual sin importar cuántas entradas tenga. No roadmap (una simulación hacia adelante, no opciones reales hoy). También leer withheld: nodos que de otro modo estarían en next pero esperan a un padre recomendado no decidido — cada entrada nombra qué padre(s). Sin python3, o no permitido → enviar un subagente con el prompt de skills/refactor-scan/references/tree-walk-prompt.md ({N}=all) — recorre los mismos documentos del árbol a mano (lee primero Fulfilled nodes del bookkeeping.md de las Notas de Refactorización para evitar re-derivar estado cachead, salta cualquier nodo con una entrada out-of-scope); sin mecanismo de subagente → ejecutar sus pasos directamente.
- Nodos de herramientas ordinarios (
loop-config, y nodos de especialización del lenguaje, p. ej.skills/refactor-scan/references/php-tooling-tree.md) — propuestos por su Nombre (nunca el slug crudo); cada uno ya está completamente especificado en su documento de árbol. structural-scan— propuesto una vez que todos los nodos con una aristaresolvedhacia él están resueltos (cumplidos, o rechazados explícitamente bajoout-of-scope/de las Notas de Refactorización):editorconfigen la raíz genérica, más el propio nodo de agregación de la especialización del lenguaje activo (PHP:php-structural-scan), a su vez resuelto una vez que todos sus propios padres resueltos están resueltos (skills/refactor-scan/references/tooling-tree.md). Solostructural-scanse propone de esta manera —php-structural-scanes pura tubería, nunca un candidato. Proponerlo es solo nombrarlo; la exploración del código base ocurre enrefactor-design, solo para el nodo realmente elegido.- Ningún árbol de lenguaje reconocido:
structural-scansigue esperando aeditorconfig, la hoja genérica de la raíz — no es inmediatamente proponible solo porque no aplique ningún árbol específico del lenguaje.
Salida
Entregada por el orquestador, claramente:
- Qué condición previa detuvo la pasada, si es que alguna — nada de lo siguiente aplica en esta pasada.
- Hallazgos (posiblemente vacíos) →
refactor-learn. - Un candidato para reanudar, si se detectó uno → directamente a
refactor-implement, omitiendorefactor-prioritize/refactor-design. - Propuestas — el candidato pendiente solo, o cada Nombre de nodo actualmente desbloqueado (nunca slugs, nunca limitado) más cualquier candidato etiquetado externamente del paso 3b, o ninguno →
refactor-prioritize. Todos los nodos actualmente desbloqueados (padres requeridos cumplidos, no rechazados, cada padre recomendado ya decidido) — nunca un subconjunto truncado por prioridad. Junto a ello, nombrar cada nodowithheldy qué padre(s) está esperando (p. ej. "Rector: Type Coverage Set — esperando a: PHP CS Fixer").
Criterio de finalización
Hallazgos (si los hay) entregados a refactor-learn, un candidato para reanudar (si existe) entregado directamente a refactor-implement, propuestas (si las hay) entregadas a refactor-prioritize — o una condición previa detuvo la pasada y el informe dice cuál. Nunca un nodo junto con entradas más allá de structural-scan en la misma lista.