@saulwade/swl-ses 2.5.3 → 2.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +9 -9
- package/README.md +37 -37
- package/agentes/_intent-spec.md +73 -73
- package/agentes/_propose-step.md +90 -90
- package/agentes/accesibilidad-wcag-swl.md +690 -690
- package/agentes/arquitecto-swl.md +267 -267
- package/agentes/auto-evolucion-swl.md +932 -908
- package/agentes/backend-csharp-swl.md +420 -420
- package/agentes/backend-go-swl.md +390 -390
- package/agentes/backend-java-swl.md +281 -281
- package/agentes/backend-rust-swl.md +364 -364
- package/agentes/backend-workers-swl.md +482 -482
- package/agentes/cloud-infra-swl.md +509 -509
- package/agentes/consolidador-swl.md +541 -541
- package/agentes/depurador-swl.md +352 -352
- package/agentes/devops-ci-swl.md +400 -400
- package/agentes/disenador-ui-swl.md +569 -569
- package/agentes/documentador-swl.md +345 -345
- package/agentes/frontend-angular-swl.md +621 -621
- package/agentes/frontend-css-swl.md +716 -716
- package/agentes/frontend-react-swl.md +692 -692
- package/agentes/frontend-swl.md +496 -496
- package/agentes/frontend-tailwind-swl.md +826 -826
- package/agentes/investigador-swl.md +432 -432
- package/agentes/investigador-ux-swl.md +505 -505
- package/agentes/migrador-swl.md +442 -442
- package/agentes/mobile-android-swl.md +511 -511
- package/agentes/mobile-cross-swl.md +541 -541
- package/agentes/mobile-ios-swl.md +502 -502
- package/agentes/mobile-testing-swl.md +302 -302
- package/agentes/nemesis-auditor-swl.md +285 -285
- package/agentes/observabilidad-swl.md +438 -438
- package/agentes/pagos-swl.md +310 -310
- package/agentes/perfilador-usuario-swl.md +321 -321
- package/agentes/planificador-swl.md +399 -399
- package/agentes/producto-prd-swl.md +589 -589
- package/agentes/red-team-swl.md +218 -218
- package/agentes/release-manager-swl.md +590 -590
- package/agentes/rendimiento-swl.md +713 -713
- package/agentes/revisor-angular-swl.md +278 -278
- package/agentes/revisor-csharp-swl.md +264 -264
- package/agentes/revisor-go-swl.md +259 -259
- package/agentes/revisor-java-swl.md +257 -257
- package/agentes/revisor-kotlin-swl.md +273 -273
- package/agentes/revisor-nextjs-swl.md +281 -281
- package/agentes/revisor-php-swl.md +271 -271
- package/agentes/revisor-react-swl.md +278 -278
- package/agentes/revisor-rust-swl.md +346 -346
- package/agentes/revisor-seguridad-swl.md +399 -399
- package/agentes/revisor-swift-swl.md +268 -268
- package/agentes/revisor-typescript-swl.md +346 -346
- package/agentes/tdd-qa-swl.md +393 -393
- package/bin/swl-ses.js +32 -7
- package/comandos/swl/actualizar.md +3 -3
- package/comandos/swl/aprender.md +13 -0
- package/comandos/swl/deuda-codigo.md +97 -97
- package/comandos/swl/evaluar-skill.md +18 -3
- package/comandos/swl/evolucion-continua.md +73 -0
- package/comandos/swl/evolucionar.md +13 -0
- package/comandos/swl/instalar.md +4 -4
- package/comandos/swl/notificaciones.md +1 -1
- package/comandos/swl/status.md +2 -2
- package/gateway/cron/jobs.example.json +12 -0
- package/habilidades/auto-evolucion-protocolo/SKILL.md +19 -1
- package/habilidades/autoresearch/SKILL.md +3 -2
- package/habilidades/backend-async-postgres-testing/SKILL.md +2 -1
- package/habilidades/benchmark-memoria/SKILL.md +7 -7
- package/habilidades/changelog-generator/SKILL.md +1 -1
- package/habilidades/changelog-generator/scripts/parse-commits.js +2 -1
- package/habilidades/checkpoints-verificacion/SKILL.md +6 -0
- package/habilidades/compactacion-contexto/SKILL.md +2 -1
- package/habilidades/contenedores-docker/SKILL.md +4 -2
- package/habilidades/context-builder/SKILL.md +4 -0
- package/habilidades/doubt-driven-review/SKILL.md +17 -1
- package/habilidades/drift-detection/SKILL.md +6 -1
- package/habilidades/ejecutar-fase/SKILL.md +6 -6
- package/habilidades/ejecutar-task-iterativo/SKILL.md +278 -278
- package/habilidades/eval-framework/SKILL.md +8 -3
- package/habilidades/extractor-de-aprendizajes/SKILL.md +8 -2
- package/habilidades/git-worktrees-paralelo/SKILL.md +19 -1
- package/habilidades/harness-claude-code/SKILL.md +7 -3
- package/habilidades/infra-github-actions/SKILL.md +4 -3
- package/habilidades/instalar-sistema/SKILL.md +5 -1
- package/habilidades/memoria-busqueda/SKILL.md +31 -39
- package/habilidades/planear-fase/SKILL.md +9 -1
- package/habilidades/prevencion-sobreingenieria/recursos/soluciones-nativas.md +166 -166
- package/habilidades/prevencion-sobreingenieria/recursos/variables-residuales-post-refactor.md +85 -85
- package/habilidades/proceso-ddia-fundamentos/SKILL.md +3 -2
- package/habilidades/proceso-ingenieria-requerimientos/SKILL.md +147 -147
- package/habilidades/release-semver/SKILL.md +2 -2
- package/habilidades/swl-claudemd/SKILL.md +6 -7
- package/habilidades/swl-dashboard/SKILL.md +11 -43
- package/habilidades/tdd-workflow/SKILL.md +12 -7
- package/habilidades/validacion-ci-sistema/SKILL.md +1 -1
- package/hooks/agente-lifecycle.js +2 -1
- package/hooks/aiisms-detector.js +13 -4
- package/hooks/audit-trail.js +2 -1
- package/hooks/auto-consolidacion.js +2 -1
- package/hooks/captura-acciones-post.js +2 -1
- package/hooks/captura-acciones-session.js +2 -1
- package/hooks/captura-feedback-usuario.js +3 -2
- package/hooks/claudemd-bloat-detector.js +12 -3
- package/hooks/claudemd-duplicacion-detector.js +13 -3
- package/hooks/contexto-iteracion.js +2 -1
- package/hooks/contexto-subagente.js +68 -68
- package/hooks/degradacion-instintos.js +2 -1
- package/hooks/extraccion-aprendizajes.js +109 -15
- package/hooks/grafo-contexto.js +2 -1
- package/hooks/guardrail-modelo.js +2 -1
- package/hooks/inbox-aviso.js +2 -1
- package/hooks/inyeccion-contexto.js +2 -1
- package/hooks/lib/agent-matcher.js +2 -1
- package/hooks/lib/agent-routing.js +2 -1
- package/hooks/lib/autonomia.js +5 -3
- package/hooks/lib/captura-acciones.js +2 -1
- package/hooks/lib/consolidation-lock.js +21 -10
- package/hooks/lib/etapa-auto-evolucion.js +10 -4
- package/hooks/lib/etapa-metricas.js +2 -1
- package/hooks/lib/etapa-perfil-usuario.js +20 -4
- package/hooks/lib/evolution-tracker.js +2 -1
- package/hooks/lib/gateway-notify.js +17 -3
- package/hooks/lib/loop-telemetry.js +5 -4
- package/hooks/lib/mcp-health.js +2 -1
- package/hooks/lib/memory-search.js +4 -0
- package/hooks/lib/merkle-audit.js +58 -6
- package/hooks/lib/notificacion-formato.js +58 -0
- package/hooks/lib/nudge-tracker.js +2 -1
- package/hooks/lib/otlp-exporter.js +2 -1
- package/hooks/lib/propose-step.js +3 -2
- package/hooks/lib/raiz-proyecto.js +127 -0
- package/hooks/lib/run-log.js +2 -1
- package/hooks/lib/singleton-guard.js +225 -27
- package/hooks/lib/telegram-cliente.js +28 -11
- package/hooks/notificacion-telegram.js +13 -3
- package/hooks/preservar-estado-pre-compact.js +2 -1
- package/hooks/proteccion-rutas.js +59 -3
- package/hooks/registro-turnos.js +2 -1
- package/hooks/resumen-sesion.js +2 -1
- package/hooks/risk-scoring.js +2 -1
- package/hooks/rotar-audit-auto.js +46 -20
- package/hooks/session-briefing.js +127 -1
- package/hooks/spec-gate.js +2 -1
- package/hooks/sugerir-contribuir.js +6 -3
- package/hooks/sugerir-regenerar-inventario.js +3 -2
- package/hooks/tdd-gate.js +2 -1
- package/hooks/telemetria-agentes.js +2 -1
- package/hooks/telemetria-skill-routing.js +2 -1
- package/hooks/tracking-costos.js +4 -3
- package/hooks/validar-formato-post-subagente.js +2 -1
- package/hooks/validar-intent-spec.js +2 -1
- package/hooks/validar-memoria-hook.js +13 -3
- package/hooks/validar-planning-paths.js +2 -1
- package/instintos/perfil-usuario.yaml +506 -3
- package/instintos/proyecto.yaml +78 -0
- package/llms.txt +29 -29
- package/manifiestos/canonical-hashes.json +5588 -4925
- package/manifiestos/hooks-config.json +469 -469
- package/manifiestos/invariantes-criticos.json +30 -30
- package/manifiestos/modulos.json +1429 -1423
- package/manifiestos/planning-paths.json +1 -0
- package/manifiestos/skills-lock.json +1275 -1275
- package/package.json +94 -95
- package/plugin.json +369 -369
- package/scripts/actualizar.js +3 -0
- package/scripts/auditar-clases-conocidas.js +134 -106
- package/scripts/benchmark-memoria.js +1 -0
- package/scripts/bootstrap-instintos.js +85 -14
- package/scripts/canario-hooks.js +166 -166
- package/scripts/cli/autonomia.js +23 -0
- package/scripts/cli/benchmark-memoria.js +37 -0
- package/scripts/cli/ciclo-autonomo.js +73 -0
- package/scripts/cli/ciclo-fase-b.js +102 -0
- package/scripts/cli/guardrail-metrics.js +39 -0
- package/scripts/cli/loop-telemetry.js +4 -2
- package/scripts/cli/memoria-search.js +69 -0
- package/scripts/cli/nudge-accionar.js +39 -0
- package/scripts/cli/run-eval.js +38 -0
- package/scripts/cli/run-skill-evals.js +13 -2
- package/scripts/derivar-feature-list.js +15 -14
- package/scripts/desinstalar.js +11 -0
- package/scripts/doctor.js +50 -13
- package/scripts/evidencia-valor.js +101 -101
- package/scripts/field-report.js +16 -16
- package/scripts/instalador.js +98 -7
- package/scripts/lib/activar-hooks-proyecto.js +116 -104
- package/scripts/lib/auditar-invocaciones-comandos.js +96 -6
- package/scripts/lib/ciclo-autonomo/candidatos.js +174 -0
- package/scripts/lib/ciclo-autonomo/config.js +165 -0
- package/scripts/lib/ciclo-autonomo/drenador-feedback.js +174 -0
- package/scripts/lib/ciclo-autonomo/fallback.js +77 -0
- package/scripts/lib/ciclo-autonomo/guard-convivencia.js +139 -0
- package/scripts/lib/ciclo-autonomo/higiene-nudges.js +112 -0
- package/scripts/lib/ciclo-autonomo/index.js +301 -0
- package/scripts/lib/ciclo-autonomo/lock.js +124 -0
- package/scripts/lib/ciclo-autonomo/presupuesto.js +122 -0
- package/scripts/lib/ciclo-autonomo/puente-degradacion.js +240 -0
- package/scripts/lib/ciclo-autonomo/runner-fase-b.js +248 -0
- package/scripts/lib/ciclo-autonomo/writer-instintos.js +190 -0
- package/scripts/lib/ciclo-autonomo/yaml-instintos.js +535 -0
- package/scripts/lib/estado.js +9 -0
- package/scripts/lib/evidencia-valor.js +228 -228
- package/scripts/lib/expandir-targets.js +71 -71
- package/scripts/lib/gitignore-manifest.js +8 -1
- package/scripts/lib/hooks-settings.js +45 -0
- package/scripts/lib/limpiar-basura-global.js +161 -0
- package/scripts/lib/toml-merge.js +204 -204
- package/scripts/mcp-server/auth.js +105 -105
- package/scripts/mcp-server/cache.js +106 -106
- package/scripts/rotar-audit-logs.js +48 -2
- package/scripts/run-eval.js +1 -0
- package/scripts/run-skill-evals.js +287 -8
- package/scripts/smoke-test.js +16 -8
- package/scripts/tui/pantallas/install-wizard.js +69 -13
- package/scripts/validar.js +40 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: git-worktrees-paralelo
|
|
3
3
|
description: Uso de git worktrees para ejecución paralela de agentes. Crear, usar y limpiar worktrees de forma segura para que múltiples agentes trabajen sin conflictos.
|
|
4
|
-
version: "1.0.
|
|
4
|
+
version: "1.0.1"
|
|
5
5
|
herramientasPermitidas: [Read, Bash]
|
|
6
6
|
evolvable: true # default para skill estandar
|
|
7
7
|
exclusiones:
|
|
@@ -268,3 +268,21 @@ git worktree remove --force ../mi-app-agente-1
|
|
|
268
268
|
**`git stash` en un worktree guarda el stash en el stash global del repositorio compartido, no en el worktree**: si el Agente 1 hace `git stash` en `mi-app-agente-1/` y luego el Agente 2 hace `git stash pop` en `mi-app-agente-2/`, aplica el stash del Agente 1 en la rama incorrecta. Causa: el stash de Git es una referencia global en `.git/refs/stash`, no por worktree. Fix: NUNCA usar `git stash` en flujos con múltiples worktrees. En su lugar, hacer un commit temporal con mensaje `WIP:` y usar `git reset HEAD~1` para deshacer cuando se necesite.
|
|
269
269
|
|
|
270
270
|
**Eliminar un worktree con `git worktree remove --force` mientras hay un agente activo escribiendo archivos en ese directorio produce corrupción del objeto store de Git**: si el agente tiene archivos en proceso de escritura (commit parcial, `git add` pendiente) y otro proceso ejecuta `remove --force`, Git puede quedar con objetos huérfanos o referencias rotas. Causa: `--force` no verifica si hay procesos con file handles abiertos en el worktree. Fix: el orquestador debe verificar que cada agente emitió su commit final antes de ejecutar `git worktree remove`. Usar `git worktree list --porcelain` para confirmar que la rama del worktree tiene el commit esperado.
|
|
271
|
+
|
|
272
|
+
**Un worktree creado con `isolation: "worktree"` (Agent tool) puede branchear desde una base desactualizada — no la rama de trabajo activa — si pasó tiempo o hubo compactación de contexto entre sesiones** [CONFIRMADO]: si el mecanismo que crea el worktree resuelve el punto de partida contra una referencia que quedó "congelada" (ej. `main` publicado, 100+ commits detrás de la rama de feature real en la que se trabaja), el agente produce commits legítimos, pero al intentar fusionarlos aparecen decenas de conflictos espurios en archivos que el agente ni siquiera tocó — el diff real es contra una base vieja, no contra el trabajo del agente. Caso real (sesión de 4 agentes en paralelo): 2 de 4 worktrees branchearon desde `main` (un PR ya squash-mergeado, 182 commits detrás de la rama de trabajo activa) en vez de la rama de feature; el merge directo habría arrastrado cientos de líneas de diffs no relacionados, y de hecho generó colisión real de números consecutivos de migración. Causa raíz: el punto de partida del worktree se fijó en un momento donde apuntaba a la rama correcta, pero la rama activa siguió avanzando mientras el worktree quedó anclado a su snapshot original — cuanto más dura la sesión (o si hay compactación de por medio), mayor la ventana de divergencia. Diagnóstico ANTES de fusionar (barato, dos comandos):
|
|
273
|
+
```bash
|
|
274
|
+
git merge-base --is-ancestor <rama-actual> <rama-worktree> && echo "base correcta" || echo "requiere investigar"
|
|
275
|
+
git merge-base <rama-actual> <rama-worktree> # compara contra el HEAD conocido de cuando se lanzó el agente
|
|
276
|
+
```
|
|
277
|
+
Si la base real está muy por detrás de lo esperado: **no usar `git merge` directo** (arrastraría el diff completo vs. la base vieja) — usar `git cherry-pick` de los commits propios del agente, uno por uno, sobre la punta actual de la rama de trabajo (`git log --reverse --oneline <base-vieja>..<rama-worktree>` para la lista en orden). Los conflictos del cherry-pick son reales y acotados al cambio de ESE commit, no ruido de la divergencia de base.
|
|
278
|
+
|
|
279
|
+
**Un conflicto de merge/cherry-pick en un archivo SQL con múltiples `CREATE PROCEDURE`/`CREATE FUNCTION` consecutivos y boilerplate similar (`LANGUAGE plpgsql SECURITY INVOKER AS $$ DECLARE ... END; $$;`) puede hacer que el diff de línea de Git "interleave" el contenido de dos objetos distintos** [CONFIRMADO x2]: cuando ambos lados de un conflicto agregan objetos PL/pgSQL nuevos en la misma posición del archivo, git empareja las líneas de cierre genéricas (`)`, `LANGUAGE plpgsql`, `SECURITY INVOKER`, `AS $$`, `DECLARE`) como si fueran contexto compartido sin cambios — el bloque `<<<<<<< / ======= / >>>>>>>` alterna entre la firma de un objeto de un lado y el cuerpo de OTRO objeto del otro lado. Resolver aceptando los hunks tal cual (o con merge automático) borra o duplica el cuerpo de uno de los dos procedimientos/funciones. Diagnóstico antes de resolver: contar cuántos `CREATE OR REPLACE (PROCEDURE|FUNCTION)` hay en cada lado del rango conflictuado (`grep -n "CREATE OR REPLACE"` entre las líneas del `<<<<<<<` y `>>>>>>>`) — si el conteo no cuadra con objetos completos y autocontenidos, hay interleaving. Fix: NO editar los hunks en el sitio; reconstruir manualmente objeto-por-objeto, concatenando cada `CREATE ... COMMENT ON ...` completo de un lado seguido del completo del otro lado, en cualquier orden (son independientes). Verificar al final: `grep -c '\$\$' archivo.sql` debe dar un número PAR, y el conteo de `CREATE OR REPLACE PROCEDURE/FUNCTION` debe coincidir con la suma real esperada de ambos lados sin duplicados.
|
|
280
|
+
|
|
281
|
+
**Una rama que sigue recibiendo commits DESPUÉS de que su propio PR se squash-mergeó a `main` genera un conflicto espurio de 20+ archivos en el SIGUIENTE PR de esa misma rama** [CONFIRMADO — variante del gotcha anterior]: el caso de "worktree con base desactualizada" cubre una rama que *branchea desde* una base squasheada; este es el caso hermano donde la MISMA rama continúa avanzando tras su propio squash-merge. El historial de commits de la rama nunca queda como descendiente del commit squash en `main` — git no tiene ancestro común que refleje el squash, así que el siguiente PR desde esa rama muestra `mergeStateStatus: CONFLICTING` aunque semánticamente nada choque. Diagnóstico y resolución seguros — **barrido "ours vs theirs" por shortstat ANTES de elegir estrategia**, nunca a ciegas:
|
|
282
|
+
```bash
|
|
283
|
+
git log --oneline -3 main # confirmar que main NO avanzó desde el squash
|
|
284
|
+
git diff --name-only <rama-local> main # lista de archivos en conflicto
|
|
285
|
+
# Para CADA archivo conflictuado, comparar tamaño de cambio de cada lado:
|
|
286
|
+
git diff --shortstat :2:<archivo> :3:<archivo> # :2 = ours/HEAD, :3 = theirs/main
|
|
287
|
+
```
|
|
288
|
+
Si TODOS los archivos muestran que "theirs" (main, congelado en el punto del squash) es subconjunto estricto de líneas de "ours" (la rama que sigue avanzando), y `main` no avanzó desde el squash (paso 1), `git checkout --ours <archivo>` para cada uno es seguro — no se pierde contenido independiente de main. Si algún archivo muestra líneas exclusivas del lado "theirs" que "ours" no tiene, DETENERSE y resolver ese archivo manualmente (no aplica el atajo). Referencia cruzada: `~/.claude/rules/sesiones-paralelas.md`.
|
|
@@ -10,7 +10,7 @@ description: >
|
|
|
10
10
|
Cargar cuando el usuario reporte "se acabó la cuota", se prepare una
|
|
11
11
|
sesión Opus larga (>2h), se planifique adopción de MCP servers, o se
|
|
12
12
|
detecte context-rot recurrente.
|
|
13
|
-
version: "1.0.
|
|
13
|
+
version: "1.0.6"
|
|
14
14
|
evolved: false
|
|
15
15
|
herramientasPermitidas: [Read]
|
|
16
16
|
exclusiones:
|
|
@@ -264,8 +264,12 @@ Sin observar la métrica, no puedes optimizarla.
|
|
|
264
264
|
|
|
265
265
|
---
|
|
266
266
|
|
|
267
|
-
## Gotchas / Errores comunes no obvios
|
|
268
|
-
|
|
267
|
+
## Gotchas / Errores comunes no obvios
|
|
268
|
+
|
|
269
|
+
- **La frontera de escritura/seguridad de un hook es la raíz del repo git, no el CWD de la sesión** [doble caso real 2026-07-10]: (1) `proteccion-rutas` bloqueó la escritura de `.planning/audit/` en SIGM porque la sesión corría en `sigm/backend/` — el destino estaba dentro del proyecto pero un nivel arriba del CWD; (2) en sistema-verificacion-oic, los hooks de telemetría anclados a `process.cwd()` crearon TRES árboles `.planning/` (raíz + backend + frontend) fragmentando la memoria del proyecto. Causa común: tratar el CWD de arranque como frontera del proyecto — en monorepos la sesión se abre en subdirectorios rutinariamente. Fix de clase: ascender hasta `.git` (directorio o archivo — worktrees) y usar esa raíz como frontera de permisos y como ancla de escritura; si la detección falla → conservador (bloquear / caer al CWD). Aplicado a `proteccion-rutas` con boundary checks (repos ajenos y siblings `repo-evil` siguen bloqueados); el resto de hooks en DT-HOOKS-RAIZ-GIT.
|
|
270
|
+
|
|
271
|
+
- **`node --test <directorio>` en Windows intenta ejecutar el directorio como test y falla en milisegundos sin correr nada** [caso real 2026-07-10: `node --test tests/ciclo-autonomo/` reportó 1 fail en 42ms cuando el glob equivalente corría 21 tests]: la duración es la delatora — <100ms para una suite que debería tardar segundos significa que el runner no encontró tests, no que fallaron. Fix: glob explícito entre comillas — `node --test "tests/ciclo-autonomo/*.test.js"`.
|
|
272
|
+
|
|
269
273
|
- **El exit code de un pipeline es el del ÚLTIMO comando — verificar suites con `cmd | grep` enmascara fallos** [caso real 2026-07-09: `npm run test:all | grep ℹ | head` reportó "verde" durante horas sobre una suite que fallaba; el único runner sin máscara fue el `prepublishOnly` del usuario, que tumbó el publish]: en bash, `a | b` sale con el código de `b` (grep=0 si matcheó algo). Patrón correcto para verificar comandos largos: `cmd > /tmp/x.log 2>&1; echo "exit=$?"` y decidir por el exit explícito; el grep va DESPUÉS, sobre el log. Alternativa: `set -o pipefail` al inicio del script. Aplica a todo: suites, builds, linters, gates.
|
|
270
274
|
|
|
271
275
|
- **Matriz de canales de hooks — stderr con exit 0 es un canal INVISIBLE** [origen: check-update 2026-07-08, el aviso de nuevas versiones se emitió al vacío desde su creación]: en Claude Code, el canal correcto depende del exit code. **exit 0 (éxito)**: solo el stdout llega — como contexto del turno en UserPromptSubmit/SessionStart (o `hookSpecificOutput.additionalContext`); el stderr no lo lee nadie. **exit 2 (bloqueo)**: solo el stderr llega al modelo con la razón del bloqueo; stdout con exit 2 = bloqueo ciego (regla ya conocida, L1 #4 de APRENDIZAJES). Anti-patrón resultante: "hook que funciona perfecto y nadie ve" — todo aviso al usuario desde un hook exitoso DEBE ir a stdout, idealmente con instrucción explícita ("INFORMA AL USUARIO...") para que el modelo lo retransmita. Verificación: correr el hook a mano con un flag de forzado y confirmar en qué canal aparece el mensaje.
|
|
@@ -82,14 +82,15 @@ gh secret set CLAUDE_API_KEY
|
|
|
82
82
|
|
|
83
83
|
## Branch protection
|
|
84
84
|
|
|
85
|
-
Aplicar después de tener CI funcionando. El
|
|
85
|
+
Aplicar después de tener CI funcionando. El subcomando del CLI (resuelve
|
|
86
|
+
cross-scope: repo madre → bin en PATH → `npx -y @saulwade/swl-ses@latest`):
|
|
86
87
|
|
|
87
88
|
```bash
|
|
88
89
|
# Ver qué haría sin aplicar cambios
|
|
89
|
-
|
|
90
|
+
swl-ses configure-branch-protection --dry-run
|
|
90
91
|
|
|
91
92
|
# Aplicar
|
|
92
|
-
|
|
93
|
+
swl-ses configure-branch-protection
|
|
93
94
|
```
|
|
94
95
|
|
|
95
96
|
Requiere `gh` CLI autenticado y permisos de admin en el repositorio.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: instalar-sistema
|
|
3
3
|
description: "Instala o actualiza el sistema SWL en el proyecto actual. Usar PROACTIVAMENTE cuando: (1) el usuario pide instalar SWL, (2) se detecta que el proyecto no tiene .claude/agents/ ni .claude/skills/ con componentes SWL, (3) el usuario menciona perfiles de instalación o pide agregar agentes/skills al proyecto. NO usar cuando SWL ya está instalado y funcionando correctamente."
|
|
4
4
|
user-invocable: false
|
|
5
|
-
version: "1.0.
|
|
5
|
+
version: "1.0.3"
|
|
6
6
|
herramientasPermitidas: [Read, Write, Edit, Bash, Glob, Grep]
|
|
7
7
|
exclusiones:
|
|
8
8
|
- "No cargar si SWL ya está instalado y funcionando (`doctor` pasa sin errores); no reinstalar para resolver problemas de uso del sistema."
|
|
@@ -176,6 +176,10 @@ cat .claude/settings.json 2>/dev/null | grep -c "hooks/"
|
|
|
176
176
|
|
|
177
177
|
## Gotchas / Errores comunes no obvios
|
|
178
178
|
|
|
179
|
+
- **Todo mecanismo que ENCIENDE escritores debe sembrar el ignore de sus outputs en el mismo acto** [caso real 2026-07-10: `install --solo-hooks` activaba la telemetría (los hooks escriben METRICAS.md, audit*.jsonl, evolution/nudges.jsonl, runs/…) sin aplicar `gitignore-manifest` — 19 archivos runtime aparecieron como cambios pendientes en el editor del usuario en sistema-verificacion-oic]: activar hooks/telemetría/watchers es un contrato de DOS partes — registrar el escritor Y declarar sus outputs como runtime ignorado. Fix: `activar-hooks-proyecto` aplica `actualizarGitignore()` tras registrar (no bloqueante, reporta la ruta). Patrón general: al agregar cualquier writer nuevo al sistema, preguntarse en el mismo commit "¿quién ignora lo que este escribe en el destino?".
|
|
180
|
+
|
|
181
|
+
- **Todo rename de path runtime necesita shim de migración en el manifest de ignorados** [caso real 2026-07-10: instalaciones pre-ADR-0031 escriben `.planning/evolucion/` (español) — el manifest solo ignoraba `evolution/` (inglés), así que los proyectos con hooks viejos ensuciaban su working tree hasta correr `update`]: al renombrar un directorio runtime, el path viejo sigue VIVO en el campo mientras existan instalaciones sin actualizar; el manifest debe cubrir ambas variantes (con comentario de vigencia) hasta que la base instalada migre. Señal en reportes de campo: un path "que ya no existe en el fuente" apareciendo en capturas del usuario delata instalación vieja — pedir `update`, no buscar el writer en el código actual.
|
|
182
|
+
|
|
179
183
|
- **Un generador de archivos de instrucciones NUNCA sobrescribe un archivo sin su marcador de procedencia** [caso real 2026-07-08: `install --target opencode` pisó el AGENTS.md canónico del repo madre — 420 líneas de documentación curada reemplazadas por el stub del transformador]: la rama "clásica" de `generarArchivoInstrucciones` escribía a ciegas. Regla: antes de sobrescribir AGENTS.md/GEMINI.md/etc. en el destino, verificar que el archivo existente contenga el marcador `Generado por swl-ses`; sin marcador = contenido del usuario o doc canónica del proyecto → preservar y avisar ("borra el archivo y reinstala si quieres el generado"). El guard vive en `scripts/instalador.js` (rama clásica). Patrón general: todo writer de archivos compartidos necesita prueba de ownership antes de escribir — el mismo principio del merge-no-overwrite (ADR-0040) aplicado a archivos generados.
|
|
180
184
|
|
|
181
185
|
- **Instalación con perfil `completo` en proyecto Python/TypeScript instala reglas de lenguaje innecesarias**: si no se usa la detección automática de stack, el perfil `completo` instala 5 reglas Java + 5 Go + 5 Rust aunque el proyecto no use esos lenguajes, incrementando el contexto base en ~700 líneas por sesión. Causa: no se activó la detección automática de stack. Solución: dejar que el instalador detecte automáticamente o usar `--all-langs` solo si se trabaja con todos los lenguajes; verificar con `ls .claude/rules/` post-instalación.
|
|
@@ -71,29 +71,26 @@ sobre el ranking RRF si lo desea. Si `rrf-fusion.js` no está disponible,
|
|
|
71
71
|
|
|
72
72
|
**Cuándo usar**: Siempre primero. Retorna solo id, tipo, título, fecha y relevancia.
|
|
73
73
|
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
// Con filtros
|
|
85
|
-
const soloAprendizajes = search(baseDir, "autenticación JWT", {
|
|
86
|
-
tipo: 'aprendizaje',
|
|
87
|
-
fecha_desde: '2026-01-01',
|
|
88
|
-
limit: 10,
|
|
89
|
-
});
|
|
74
|
+
```bash
|
|
75
|
+
# Búsqueda básica (JSON a stdout)
|
|
76
|
+
swl-ses memoria-search search --query="error build classpath"
|
|
77
|
+
# → [
|
|
78
|
+
# { "id": "apr-5", "tipo": "aprendizaje", "titulo": "Fix classpath Maven en CI", "fecha": "2026-03-10", "relevancia": 0.9 },
|
|
79
|
+
# { "id": "ses-20260310-142000", "tipo": "sesion", "titulo": "Investigacion build Java", "fecha": "2026-03-10", "relevancia": 0.6 }
|
|
80
|
+
# ]
|
|
81
|
+
|
|
82
|
+
# Con filtros
|
|
83
|
+
swl-ses memoria-search search --query="autenticación JWT" --tipo=aprendizaje --desde=2026-01-01 --limit=10
|
|
90
84
|
```
|
|
91
85
|
|
|
86
|
+
(El CLI resuelve cross-scope; en el repo madre el módulo equivalente es
|
|
87
|
+
`hooks/lib/memory-search.js` — `search(baseDir, query, filtros)`.)
|
|
88
|
+
|
|
92
89
|
**Filtros disponibles**:
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
96
|
-
-
|
|
90
|
+
- `--tipo`: `aprendizaje` | `sesion` | `instinto` (sin filtro = todas las fuentes)
|
|
91
|
+
- `--desde`: YYYY-MM-DD — excluye resultados anteriores
|
|
92
|
+
- `--hasta`: YYYY-MM-DD — excluye resultados posteriores
|
|
93
|
+
- `--limit`: número máximo de resultados (default: 20)
|
|
97
94
|
|
|
98
95
|
**Costo por resultado**: ~50-100 tokens. Seguro pedir hasta 20 resultados.
|
|
99
96
|
|
|
@@ -104,17 +101,14 @@ const soloAprendizajes = search(baseDir, "autenticación JWT", {
|
|
|
104
101
|
**Cuándo usar**: Después de search(), para los 3-7 resultados más relevantes.
|
|
105
102
|
Agrega contexto resumido sin traer el contenido completo.
|
|
106
103
|
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
// { id: "ses-...", tipo: "sesion", contenido: "Modo: dev | Tool calls: 47 | Archivos: build.gradle | ...", fecha: "2026-03-10" },
|
|
116
|
-
// ]
|
|
117
|
-
// Ordenado descendente por fecha (más reciente primero).
|
|
104
|
+
```bash
|
|
105
|
+
# Tomar los 3-7 ids más relevantes de search()
|
|
106
|
+
swl-ses memoria-search timeline --ids=apr-5,ses-20260310-142000
|
|
107
|
+
# → [
|
|
108
|
+
# { "id": "apr-5", "tipo": "aprendizaje", "titulo": "...", "contenido": "<primeros 500 chars>", "fecha": "2026-03-10" },
|
|
109
|
+
# { "id": "ses-...", "tipo": "sesion", "contenido": "Modo: dev | Tool calls: 47 | Archivos: build.gradle | ...", "fecha": "2026-03-10" }
|
|
110
|
+
# ]
|
|
111
|
+
# Ordenado descendente por fecha (más reciente primero).
|
|
118
112
|
```
|
|
119
113
|
|
|
120
114
|
**Costo por entrada**: ~200-500 tokens. Seguro para 5-7 entradas simultáneas.
|
|
@@ -126,15 +120,13 @@ const ctx = timeline(baseDir, ids);
|
|
|
126
120
|
**Cuándo usar**: Solo para los 1-3 resultados definitivamente relevantes.
|
|
127
121
|
Retorna el contenido completo sin truncar.
|
|
128
122
|
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
const detalle = fetch(baseDir, "apr-5");
|
|
134
|
-
// → { id: "apr-5", tipo: "aprendizaje", contenido: "<bloque completo del APRENDIZAJES.md>" }
|
|
123
|
+
```bash
|
|
124
|
+
# Solo para el resultado más relevante
|
|
125
|
+
swl-ses memoria-search fetch --id=apr-5
|
|
126
|
+
# → { "id": "apr-5", "tipo": "aprendizaje", "contenido": "<bloque completo del APRENDIZAJES.md>" }
|
|
135
127
|
|
|
136
|
-
|
|
137
|
-
|
|
128
|
+
swl-ses memoria-search fetch --id=ses-20260310-142000
|
|
129
|
+
# → { "id": "ses-...", "tipo": "sesion", "contenido": { ...objeto JSON completo... } }
|
|
138
130
|
```
|
|
139
131
|
|
|
140
132
|
**Costo**: ~500-1,000 tokens por resultado. NO hacer fetch de todos los resultados.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: planear-fase
|
|
3
3
|
description: Crea el PLAN.md ejecutable para una fase de desarrollo. Descompone la fase en tareas atómicas con dependencias explícitas, las agrupa en oleadas de ejecución paralela cuando es posible, y aplica verificación goal-backward para garantizar que el plan completo satisface los criterios de éxito definidos en CONTEXTO.md.
|
|
4
|
-
version: "1.3.
|
|
4
|
+
version: "1.3.3"
|
|
5
5
|
herramientasPermitidas: [Read, Write, Edit, Bash, Glob, Grep]
|
|
6
6
|
exclusiones:
|
|
7
7
|
- "No cargar si no existe CONTEXTO.md para la fase — ejecutar `discutir-fase` primero."
|
|
@@ -283,6 +283,14 @@ Todo REQ del CONTEXTO tiene ≥1 tarea. ✓
|
|
|
283
283
|
- **Plan sin oleada de verificación final**: el plan puede estar completo pero
|
|
284
284
|
los criterios de éxito sin satisfacer
|
|
285
285
|
- **Mezclar implementación y tests en una sola tarea**: deben ser tareas separadas
|
|
286
|
+
- **Prosa narrativa que contradice el criterio de verificación de la tarea**:
|
|
287
|
+
cuando la descripción dice una cosa y el criterio verificable dice otra,
|
|
288
|
+
**el criterio manda** — es la autoridad más específica y comprobable [caso
|
|
289
|
+
real F22·T-06, 2026-07-10: la prosa decía "un no-op puede spawnear" y el
|
|
290
|
+
criterio exigía "no-op → detector NO corre"; el implementador resolvió a
|
|
291
|
+
favor del criterio y documentó la ambigüedad]. El autor del plan debe
|
|
292
|
+
auto-revisar esta consistencia; el ejecutor que la detecte resuelve por el
|
|
293
|
+
criterio y registra la desviación — nunca elige en silencio
|
|
286
294
|
|
|
287
295
|
---
|
|
288
296
|
|
|
@@ -1,166 +1,166 @@
|
|
|
1
|
-
# Soluciones nativas de plataforma
|
|
2
|
-
|
|
3
|
-
Recurso de `prevencion-sobreingenieria` — peldaño 4 de la escalera de decisión.
|
|
4
|
-
La primera pregunta del ingeniero perezoso-eficiente: *¿la plataforma ya hace
|
|
5
|
-
esto?* Antes de instalar un paquete o escribir un componente, revisar aquí.
|
|
6
|
-
La plataforma viaja gratis con la app, no se rompe en updates, y la escribió
|
|
7
|
-
gente cuyo trabajo es exactamente ese problema.
|
|
8
|
-
|
|
9
|
-
Adaptado de [ponytail](https://github.com/DietrichGebert/ponytail)
|
|
10
|
-
`docs/platform-native.md` (MIT, DietrichGebert).
|
|
11
|
-
|
|
12
|
-
---
|
|
13
|
-
|
|
14
|
-
## Elementos HTML
|
|
15
|
-
|
|
16
|
-
| Crees que necesitas | La plataforma ya tiene |
|
|
17
|
-
|---|---|
|
|
18
|
-
| Lib de date picker | `<input type="date">` |
|
|
19
|
-
| Lib de time picker | `<input type="time">` |
|
|
20
|
-
| Lib de color picker | `<input type="color">` |
|
|
21
|
-
| Lib de range slider | `<input type="range">` |
|
|
22
|
-
| Componente de progress bar | `<progress value="70" max="100">` |
|
|
23
|
-
| Componente meter/gauge | `<meter value="0.7">` |
|
|
24
|
-
| Lib de modal/diálogo | `<dialog>` + `dialog.showModal()` |
|
|
25
|
-
| Componente acordeón/FAQ | `<details><summary>Título</summary>…</details>` |
|
|
26
|
-
| Lib de tooltips | atributo `title` + CSS `::before`/`::after` |
|
|
27
|
-
| Dropdown con búsqueda | `<input list="id"> <datalist id="id">` |
|
|
28
|
-
| Textarea auto-crecible | `field-sizing: content` (CSS) |
|
|
29
|
-
| Header sticky | `position: sticky; top: 0` (CSS) |
|
|
30
|
-
|
|
31
|
-
## Capacidades CSS (donde se suele usar JS)
|
|
32
|
-
|
|
33
|
-
| Crees que necesitas JS para | CSS ya tiene |
|
|
34
|
-
|---|---|
|
|
35
|
-
| Font size responsivo | `font-size: clamp(1rem, 2.5vw, 2rem)` |
|
|
36
|
-
| Espaciado fluido | `padding: clamp(1rem, 5vw, 3rem)` |
|
|
37
|
-
| Dark mode | `@media (prefers-color-scheme: dark)` |
|
|
38
|
-
| Reduced motion | `@media (prefers-reduced-motion: reduce)` |
|
|
39
|
-
| Layout responsivo sin breakpoints | `grid-template-columns: repeat(auto-fill, minmax(250px, 1fr))` |
|
|
40
|
-
| Responsive a nivel componente | `@container` queries |
|
|
41
|
-
| Design tokens / theming global | Custom properties (`--color-primario: #7c3aed`) |
|
|
42
|
-
| Smooth scroll | `scroll-behavior: smooth` |
|
|
43
|
-
| Carrusel con scroll-snap | `scroll-snap-type: x mandatory` + `scroll-snap-align: start` |
|
|
44
|
-
| Aspect ratio | `aspect-ratio: 16 / 9` |
|
|
45
|
-
| Truncar texto con elipsis | `overflow: hidden; text-overflow: ellipsis; white-space: nowrap` |
|
|
46
|
-
| Clamp multilínea | `-webkit-line-clamp: 3` |
|
|
47
|
-
| Aislamiento de estilos por capas | `@layer base, components, utilities` |
|
|
48
|
-
| Selectores anidados | CSS nesting nativo (sin preprocesador) |
|
|
49
|
-
| Selector de padre | `:has(input:checked)` |
|
|
50
|
-
|
|
51
|
-
## JavaScript / APIs del navegador
|
|
52
|
-
|
|
53
|
-
| Crees que necesitas | La plataforma ya tiene |
|
|
54
|
-
|---|---|
|
|
55
|
-
| `query-string` / `qs` | `new URLSearchParams(location.search)` |
|
|
56
|
-
| `lodash.clonedeep` | `structuredClone(obj)` |
|
|
57
|
-
| `lodash.groupby` | `Object.groupBy(arr, fn)` |
|
|
58
|
-
| `numeral` / `accounting` | `new Intl.NumberFormat("es-MX", { style: "currency", currency: "MXN" })` |
|
|
59
|
-
| `date-fns` format | `new Intl.DateTimeFormat("es-MX", { dateStyle: "long" }).format(fecha)` |
|
|
60
|
-
| Tiempo relativo | `new Intl.RelativeTimeFormat("es", { numeric: "auto" }).format(-3, "day")` |
|
|
61
|
-
| Plurales i18n | `new Intl.PluralRules("es-MX").select(cantidad)` |
|
|
62
|
-
| `clipboard.js` | `navigator.clipboard.writeText(texto)` |
|
|
63
|
-
| `uuid` (v4) | `crypto.randomUUID()` |
|
|
64
|
-
| Lib de infinite scroll | `new IntersectionObserver(cb).observe(sentinela)` |
|
|
65
|
-
| Lib de resize listener | `new ResizeObserver(cb).observe(elemento)` |
|
|
66
|
-
| Watcher de mutaciones DOM | `new MutationObserver(cb).observe(el, opciones)` |
|
|
67
|
-
| Check de conectividad | `navigator.onLine` + eventos `online`/`offline` |
|
|
68
|
-
| Lib de share sheet | `navigator.share({ title, text, url })` |
|
|
69
|
-
| `store.js` / `localForage` (caso simple) | `localStorage.setItem(clave, JSON.stringify(valor))` |
|
|
70
|
-
| Abortar fetch por timeout | `AbortSignal.timeout(5000)` pasado a `fetch` |
|
|
71
|
-
| Event bus custom | `new EventTarget()` / `dispatchEvent(new CustomEvent("x", { detail }))` |
|
|
72
|
-
|
|
73
|
-
**Debounce en 3 líneas** (sin dependencia):
|
|
74
|
-
|
|
75
|
-
```js
|
|
76
|
-
// simplificado: debounce de una sola instancia; lib si se necesitan N independientes
|
|
77
|
-
let t;
|
|
78
|
-
const debounce = (fn, ms) => (...args) => { clearTimeout(t); t = setTimeout(() => fn(...args), ms); };
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## Swift / SwiftUI
|
|
82
|
-
|
|
83
|
-
| Crees que necesitas | La plataforma ya tiene |
|
|
84
|
-
|---|---|
|
|
85
|
-
| Lib de date/time picker | `DatePicker` |
|
|
86
|
-
| Lib de color picker | `ColorPicker` |
|
|
87
|
-
| Search bar + filtrado | `.searchable(text:)` |
|
|
88
|
-
| Lib de pull-to-refresh | `.refreshable { }` |
|
|
89
|
-
| Swipe-to-delete / acciones de fila | `.swipeActions { }` |
|
|
90
|
-
| Carga async de imágenes + caché | `AsyncImage` |
|
|
91
|
-
| Lib de gráficas | Swift Charts (`import Charts`) |
|
|
92
|
-
| Render de Markdown | `Text(...)` markdown / `AttributedString(markdown:)` |
|
|
93
|
-
| Share sheet | `ShareLink` |
|
|
94
|
-
| Spinner de carga | `ProgressView()` |
|
|
95
|
-
| Photo picker | `PhotosPicker` |
|
|
96
|
-
| JSON (SwiftyJSON) | `Codable` + `JSONDecoder`/`JSONEncoder` |
|
|
97
|
-
| HTTP simple (Alamofire) | `URLSession` async/await |
|
|
98
|
-
| Crypto (CryptoSwift) | `CryptoKit` |
|
|
99
|
-
| Persistencia / ORM | `SwiftData`, o `@AppStorage` para key-values |
|
|
100
|
-
| Logging | `Logger` (`os.log`) |
|
|
101
|
-
|
|
102
|
-
## Stdlib de Node.js
|
|
103
|
-
|
|
104
|
-
| Crees que necesitas | Node ya tiene |
|
|
105
|
-
|---|---|
|
|
106
|
-
| `mkdirp` / `make-dir` | `fs.mkdirSync(ruta, { recursive: true })` |
|
|
107
|
-
| `rimraf` | `fs.rmSync(ruta, { recursive: true, force: true })` |
|
|
108
|
-
| `uuid` (v4) | `crypto.randomUUID()` |
|
|
109
|
-
| `object-assign` | `Object.assign()` / spread |
|
|
110
|
-
| `array-uniq` | `[...new Set(arr)]` |
|
|
111
|
-
| `array-flatten` / `flat` | `arr.flat(profundidad)` |
|
|
112
|
-
| `path-exists` | `fs.existsSync(ruta)` |
|
|
113
|
-
| `load-json-file` | `JSON.parse(fs.readFileSync(ruta, "utf8"))` |
|
|
114
|
-
| `write-json-file` | `fs.writeFileSync(ruta, JSON.stringify(obj, null, 2))` |
|
|
115
|
-
| `pkg-dir` | `path.resolve(__dirname, "..")` / `import.meta.dirname` |
|
|
116
|
-
|
|
117
|
-
## Stdlib de Python
|
|
118
|
-
|
|
119
|
-
| Crees que necesitas | Python ya tiene |
|
|
120
|
-
|---|---|
|
|
121
|
-
| `python-dateutil` (parsing básico) | `datetime.fromisoformat()` (3.7+) |
|
|
122
|
-
| `pytz` | `zoneinfo.ZoneInfo("America/Mexico_City")` (3.9+) |
|
|
123
|
-
| `attrs` (data classes simples) | `@dataclass` |
|
|
124
|
-
| `pathlib2` | `pathlib.Path` (3.4+) |
|
|
125
|
-
| `simplejson` (uso básico) | `json` (stdlib) |
|
|
126
|
-
| `requests` (GET simple) | `urllib.request.urlopen(url)`; `requests` para algo real |
|
|
127
|
-
| `click` (comando único) | `argparse` (stdlib) |
|
|
128
|
-
| `mergedeep` | `dict \| otro_dict` (3.9+) |
|
|
129
|
-
| `more-itertools` (básico) | `itertools`: `chain`, `islice`, `groupby`, `product` |
|
|
130
|
-
| `toolz` (básico) | `functools`: `lru_cache`, `partial`, `reduce` |
|
|
131
|
-
|
|
132
|
-
## Base de datos
|
|
133
|
-
|
|
134
|
-
| Crees que necesitas código de app para | La BD ya tiene |
|
|
135
|
-
|---|---|
|
|
136
|
-
| Paginación | `LIMIT 20 OFFSET 40` |
|
|
137
|
-
| Totales acumulados | `SUM(...) OVER (ORDER BY fecha)` (window function) |
|
|
138
|
-
| Rank por grupo | `RANK() OVER (PARTITION BY categoria ORDER BY score DESC)` |
|
|
139
|
-
| Deduplicación | `SELECT DISTINCT` / `ON CONFLICT DO NOTHING` |
|
|
140
|
-
| Recorrido de árboles | CTE recursivo (`WITH RECURSIVE`) |
|
|
141
|
-
| Full-text search (básico) | `tsvector` / `MATCH AGAINST` / `FTS5` |
|
|
142
|
-
| Almacenar + consultar JSON | `jsonb` (Postgres) / `JSON_EXTRACT` (SQLite/MySQL) |
|
|
143
|
-
| Generación de UUID | `gen_random_uuid()` (Postgres) / `UUID()` (MySQL) |
|
|
144
|
-
| Timestamps de insert/update | `DEFAULT now()` + trigger / `ON UPDATE CURRENT_TIMESTAMP` |
|
|
145
|
-
| Unicidad | constraint `UNIQUE`, no checks en la app |
|
|
146
|
-
| Integridad referencial | `FOREIGN KEY`, no checks en la app |
|
|
147
|
-
| Rangos de valores | `CHECK (precio > 0)`, no validación en la app |
|
|
148
|
-
|
|
149
|
-
---
|
|
150
|
-
|
|
151
|
-
## El patrón
|
|
152
|
-
|
|
153
|
-
En todas las capas es el mismo ciclo:
|
|
154
|
-
|
|
155
|
-
```
|
|
156
|
-
El equipo de la plataforma pasa años resolviendo el problema.
|
|
157
|
-
Alguien lo envuelve en un paquete.
|
|
158
|
-
Instalas el wrapper.
|
|
159
|
-
El wrapper queda sin mantenimiento.
|
|
160
|
-
Terminas debugueando el wrapper.
|
|
161
|
-
```
|
|
162
|
-
|
|
163
|
-
Sáltate el wrapper. Cuando la solución nativa es genuinamente insuficiente
|
|
164
|
-
(soporte de navegadores viejos, edge cases que no cubre, ergonomía que importa
|
|
165
|
-
a escala), la librería se gana su lugar — instálala entonces, no antes
|
|
166
|
-
(y verifica su vigencia con Context7, regla `usar-context7.md`).
|
|
1
|
+
# Soluciones nativas de plataforma
|
|
2
|
+
|
|
3
|
+
Recurso de `prevencion-sobreingenieria` — peldaño 4 de la escalera de decisión.
|
|
4
|
+
La primera pregunta del ingeniero perezoso-eficiente: *¿la plataforma ya hace
|
|
5
|
+
esto?* Antes de instalar un paquete o escribir un componente, revisar aquí.
|
|
6
|
+
La plataforma viaja gratis con la app, no se rompe en updates, y la escribió
|
|
7
|
+
gente cuyo trabajo es exactamente ese problema.
|
|
8
|
+
|
|
9
|
+
Adaptado de [ponytail](https://github.com/DietrichGebert/ponytail)
|
|
10
|
+
`docs/platform-native.md` (MIT, DietrichGebert).
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## Elementos HTML
|
|
15
|
+
|
|
16
|
+
| Crees que necesitas | La plataforma ya tiene |
|
|
17
|
+
|---|---|
|
|
18
|
+
| Lib de date picker | `<input type="date">` |
|
|
19
|
+
| Lib de time picker | `<input type="time">` |
|
|
20
|
+
| Lib de color picker | `<input type="color">` |
|
|
21
|
+
| Lib de range slider | `<input type="range">` |
|
|
22
|
+
| Componente de progress bar | `<progress value="70" max="100">` |
|
|
23
|
+
| Componente meter/gauge | `<meter value="0.7">` |
|
|
24
|
+
| Lib de modal/diálogo | `<dialog>` + `dialog.showModal()` |
|
|
25
|
+
| Componente acordeón/FAQ | `<details><summary>Título</summary>…</details>` |
|
|
26
|
+
| Lib de tooltips | atributo `title` + CSS `::before`/`::after` |
|
|
27
|
+
| Dropdown con búsqueda | `<input list="id"> <datalist id="id">` |
|
|
28
|
+
| Textarea auto-crecible | `field-sizing: content` (CSS) |
|
|
29
|
+
| Header sticky | `position: sticky; top: 0` (CSS) |
|
|
30
|
+
|
|
31
|
+
## Capacidades CSS (donde se suele usar JS)
|
|
32
|
+
|
|
33
|
+
| Crees que necesitas JS para | CSS ya tiene |
|
|
34
|
+
|---|---|
|
|
35
|
+
| Font size responsivo | `font-size: clamp(1rem, 2.5vw, 2rem)` |
|
|
36
|
+
| Espaciado fluido | `padding: clamp(1rem, 5vw, 3rem)` |
|
|
37
|
+
| Dark mode | `@media (prefers-color-scheme: dark)` |
|
|
38
|
+
| Reduced motion | `@media (prefers-reduced-motion: reduce)` |
|
|
39
|
+
| Layout responsivo sin breakpoints | `grid-template-columns: repeat(auto-fill, minmax(250px, 1fr))` |
|
|
40
|
+
| Responsive a nivel componente | `@container` queries |
|
|
41
|
+
| Design tokens / theming global | Custom properties (`--color-primario: #7c3aed`) |
|
|
42
|
+
| Smooth scroll | `scroll-behavior: smooth` |
|
|
43
|
+
| Carrusel con scroll-snap | `scroll-snap-type: x mandatory` + `scroll-snap-align: start` |
|
|
44
|
+
| Aspect ratio | `aspect-ratio: 16 / 9` |
|
|
45
|
+
| Truncar texto con elipsis | `overflow: hidden; text-overflow: ellipsis; white-space: nowrap` |
|
|
46
|
+
| Clamp multilínea | `-webkit-line-clamp: 3` |
|
|
47
|
+
| Aislamiento de estilos por capas | `@layer base, components, utilities` |
|
|
48
|
+
| Selectores anidados | CSS nesting nativo (sin preprocesador) |
|
|
49
|
+
| Selector de padre | `:has(input:checked)` |
|
|
50
|
+
|
|
51
|
+
## JavaScript / APIs del navegador
|
|
52
|
+
|
|
53
|
+
| Crees que necesitas | La plataforma ya tiene |
|
|
54
|
+
|---|---|
|
|
55
|
+
| `query-string` / `qs` | `new URLSearchParams(location.search)` |
|
|
56
|
+
| `lodash.clonedeep` | `structuredClone(obj)` |
|
|
57
|
+
| `lodash.groupby` | `Object.groupBy(arr, fn)` |
|
|
58
|
+
| `numeral` / `accounting` | `new Intl.NumberFormat("es-MX", { style: "currency", currency: "MXN" })` |
|
|
59
|
+
| `date-fns` format | `new Intl.DateTimeFormat("es-MX", { dateStyle: "long" }).format(fecha)` |
|
|
60
|
+
| Tiempo relativo | `new Intl.RelativeTimeFormat("es", { numeric: "auto" }).format(-3, "day")` |
|
|
61
|
+
| Plurales i18n | `new Intl.PluralRules("es-MX").select(cantidad)` |
|
|
62
|
+
| `clipboard.js` | `navigator.clipboard.writeText(texto)` |
|
|
63
|
+
| `uuid` (v4) | `crypto.randomUUID()` |
|
|
64
|
+
| Lib de infinite scroll | `new IntersectionObserver(cb).observe(sentinela)` |
|
|
65
|
+
| Lib de resize listener | `new ResizeObserver(cb).observe(elemento)` |
|
|
66
|
+
| Watcher de mutaciones DOM | `new MutationObserver(cb).observe(el, opciones)` |
|
|
67
|
+
| Check de conectividad | `navigator.onLine` + eventos `online`/`offline` |
|
|
68
|
+
| Lib de share sheet | `navigator.share({ title, text, url })` |
|
|
69
|
+
| `store.js` / `localForage` (caso simple) | `localStorage.setItem(clave, JSON.stringify(valor))` |
|
|
70
|
+
| Abortar fetch por timeout | `AbortSignal.timeout(5000)` pasado a `fetch` |
|
|
71
|
+
| Event bus custom | `new EventTarget()` / `dispatchEvent(new CustomEvent("x", { detail }))` |
|
|
72
|
+
|
|
73
|
+
**Debounce en 3 líneas** (sin dependencia):
|
|
74
|
+
|
|
75
|
+
```js
|
|
76
|
+
// simplificado: debounce de una sola instancia; lib si se necesitan N independientes
|
|
77
|
+
let t;
|
|
78
|
+
const debounce = (fn, ms) => (...args) => { clearTimeout(t); t = setTimeout(() => fn(...args), ms); };
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Swift / SwiftUI
|
|
82
|
+
|
|
83
|
+
| Crees que necesitas | La plataforma ya tiene |
|
|
84
|
+
|---|---|
|
|
85
|
+
| Lib de date/time picker | `DatePicker` |
|
|
86
|
+
| Lib de color picker | `ColorPicker` |
|
|
87
|
+
| Search bar + filtrado | `.searchable(text:)` |
|
|
88
|
+
| Lib de pull-to-refresh | `.refreshable { }` |
|
|
89
|
+
| Swipe-to-delete / acciones de fila | `.swipeActions { }` |
|
|
90
|
+
| Carga async de imágenes + caché | `AsyncImage` |
|
|
91
|
+
| Lib de gráficas | Swift Charts (`import Charts`) |
|
|
92
|
+
| Render de Markdown | `Text(...)` markdown / `AttributedString(markdown:)` |
|
|
93
|
+
| Share sheet | `ShareLink` |
|
|
94
|
+
| Spinner de carga | `ProgressView()` |
|
|
95
|
+
| Photo picker | `PhotosPicker` |
|
|
96
|
+
| JSON (SwiftyJSON) | `Codable` + `JSONDecoder`/`JSONEncoder` |
|
|
97
|
+
| HTTP simple (Alamofire) | `URLSession` async/await |
|
|
98
|
+
| Crypto (CryptoSwift) | `CryptoKit` |
|
|
99
|
+
| Persistencia / ORM | `SwiftData`, o `@AppStorage` para key-values |
|
|
100
|
+
| Logging | `Logger` (`os.log`) |
|
|
101
|
+
|
|
102
|
+
## Stdlib de Node.js
|
|
103
|
+
|
|
104
|
+
| Crees que necesitas | Node ya tiene |
|
|
105
|
+
|---|---|
|
|
106
|
+
| `mkdirp` / `make-dir` | `fs.mkdirSync(ruta, { recursive: true })` |
|
|
107
|
+
| `rimraf` | `fs.rmSync(ruta, { recursive: true, force: true })` |
|
|
108
|
+
| `uuid` (v4) | `crypto.randomUUID()` |
|
|
109
|
+
| `object-assign` | `Object.assign()` / spread |
|
|
110
|
+
| `array-uniq` | `[...new Set(arr)]` |
|
|
111
|
+
| `array-flatten` / `flat` | `arr.flat(profundidad)` |
|
|
112
|
+
| `path-exists` | `fs.existsSync(ruta)` |
|
|
113
|
+
| `load-json-file` | `JSON.parse(fs.readFileSync(ruta, "utf8"))` |
|
|
114
|
+
| `write-json-file` | `fs.writeFileSync(ruta, JSON.stringify(obj, null, 2))` |
|
|
115
|
+
| `pkg-dir` | `path.resolve(__dirname, "..")` / `import.meta.dirname` |
|
|
116
|
+
|
|
117
|
+
## Stdlib de Python
|
|
118
|
+
|
|
119
|
+
| Crees que necesitas | Python ya tiene |
|
|
120
|
+
|---|---|
|
|
121
|
+
| `python-dateutil` (parsing básico) | `datetime.fromisoformat()` (3.7+) |
|
|
122
|
+
| `pytz` | `zoneinfo.ZoneInfo("America/Mexico_City")` (3.9+) |
|
|
123
|
+
| `attrs` (data classes simples) | `@dataclass` |
|
|
124
|
+
| `pathlib2` | `pathlib.Path` (3.4+) |
|
|
125
|
+
| `simplejson` (uso básico) | `json` (stdlib) |
|
|
126
|
+
| `requests` (GET simple) | `urllib.request.urlopen(url)`; `requests` para algo real |
|
|
127
|
+
| `click` (comando único) | `argparse` (stdlib) |
|
|
128
|
+
| `mergedeep` | `dict \| otro_dict` (3.9+) |
|
|
129
|
+
| `more-itertools` (básico) | `itertools`: `chain`, `islice`, `groupby`, `product` |
|
|
130
|
+
| `toolz` (básico) | `functools`: `lru_cache`, `partial`, `reduce` |
|
|
131
|
+
|
|
132
|
+
## Base de datos
|
|
133
|
+
|
|
134
|
+
| Crees que necesitas código de app para | La BD ya tiene |
|
|
135
|
+
|---|---|
|
|
136
|
+
| Paginación | `LIMIT 20 OFFSET 40` |
|
|
137
|
+
| Totales acumulados | `SUM(...) OVER (ORDER BY fecha)` (window function) |
|
|
138
|
+
| Rank por grupo | `RANK() OVER (PARTITION BY categoria ORDER BY score DESC)` |
|
|
139
|
+
| Deduplicación | `SELECT DISTINCT` / `ON CONFLICT DO NOTHING` |
|
|
140
|
+
| Recorrido de árboles | CTE recursivo (`WITH RECURSIVE`) |
|
|
141
|
+
| Full-text search (básico) | `tsvector` / `MATCH AGAINST` / `FTS5` |
|
|
142
|
+
| Almacenar + consultar JSON | `jsonb` (Postgres) / `JSON_EXTRACT` (SQLite/MySQL) |
|
|
143
|
+
| Generación de UUID | `gen_random_uuid()` (Postgres) / `UUID()` (MySQL) |
|
|
144
|
+
| Timestamps de insert/update | `DEFAULT now()` + trigger / `ON UPDATE CURRENT_TIMESTAMP` |
|
|
145
|
+
| Unicidad | constraint `UNIQUE`, no checks en la app |
|
|
146
|
+
| Integridad referencial | `FOREIGN KEY`, no checks en la app |
|
|
147
|
+
| Rangos de valores | `CHECK (precio > 0)`, no validación en la app |
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## El patrón
|
|
152
|
+
|
|
153
|
+
En todas las capas es el mismo ciclo:
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
El equipo de la plataforma pasa años resolviendo el problema.
|
|
157
|
+
Alguien lo envuelve en un paquete.
|
|
158
|
+
Instalas el wrapper.
|
|
159
|
+
El wrapper queda sin mantenimiento.
|
|
160
|
+
Terminas debugueando el wrapper.
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Sáltate el wrapper. Cuando la solución nativa es genuinamente insuficiente
|
|
164
|
+
(soporte de navegadores viejos, edge cases que no cubre, ergonomía que importa
|
|
165
|
+
a escala), la librería se gana su lugar — instálala entonces, no antes
|
|
166
|
+
(y verifica su vigencia con Context7, regla `usar-context7.md`).
|